Files
group_fqcd_jr/docs/04-开发文档评审报告.md
T

32 KiB
Raw Blame History

开发文档评审报告

评审对象:docs/01-通用Agent平台开发设计.md、docs/02-数据库建表设计.md、docs/03-平台端到端流程文档.md
评审基准:docs 目录 2026-09-08 版三份文档(01 标称 v2.1;02、03 未标注版本)
评审方式:逐行通读 + 三份文档交叉引用核对 + 按文档内容推演可实施性
评审结论:三件套综合 80/100(B+),可作为实施基线;P0 项闭环前不建议派发给编码 Agent

复审状态:本文 §1-§6 与附录 A/B 针对 v2.1 版本,行号引用以 v2.1 为准。团队已完成修订,v3.0 复审结果见 附录 C;v3.0 综合评分 90/100(A-),MVC+S 架构合规性 92/100。


1. 总评分

文档 得分 等级 结论
01-通用Agent平台开发设计.md 86 / 100 A- 架构与契约达到可直接编码水准,存在 3 处自相矛盾
02-数据库建表设计.md 77 / 100 B+ 6 张专项表 DDL 质量高,基础表基线不自包含,缺关键表
03-平台端到端流程文档.md 76 / 100 B+ 流程与职责边界清晰,落点与 02 冲突,缺幂等细节
三件套综合(01 占 40%,02/03 各占 30%) 80 / 100 B+ 可进入实施,须先闭环 5 项 P0

1.1 分维度得分

维度 权重 01 02 03
完整性与自包含性 20 16 13 14
内部一致性与准确性 20 16 15 15
架构与技术合理性 25 23 20 19
安全 / 合规 / 风控 15 14 13 13
可测试性与可验收性 10 9 8 8
工程可执行性 / 可维护性 10 8 8 7
合计 100 86 77 76

1.2 等级标准

等级 分数 含义
A 90-100 优秀,可直接落地
B 75-89 良好,修订后可落地
C 60-74 需较大重构
D < 60 需重写

2. 总体评价

2.1 值得肯定的部分

  1. 架构分层可执行。01 §3.1 的分层职责表明确列出每层"不允许承担的职责",比只写"职责"的文档强一个档次;§3.3 给出 Controller/Service 的最小代码约束。
  2. 模板方法约束到位。run_stream() 标记 @final,业务子类只实现 handle(),§15.3 用契约测试扫描受保护方法(01:1296-1313),把"不许重写骨架"从口头约定变成可执行检查。
  3. 契约设计专业。全部跨层对象使用 frozen dataclass,集合用 tuple/frozenset,依赖用 Protocol 倒置,Repository 与外部客户端全部隐藏在 Service 之后(01:491)。
  4. 配置权威链清晰。01 §7.3 的 5 级优先级(安全硬约束 > 代码上限 > 数据库配置 > 代码默认值 > 环境变量),并明确"数据库只能缩小不能扩大",工具取三层交集(01:1050-1054)。
  5. 降级矩阵完整。01 §12 与 03 §13 对 Redis/Milvus/Neo4j/模型/工具/审计/事件逐项给出处理策略,且区分"降级"与"阻断"。
  6. 编码 Agent 交接模板可直接使用。01 §15.5 把任务正文、允许/禁止修改范围、必测项、验收命令、完成回报格式全部固化,这是同类文档中罕见的工程化细节,可当任务单直接派发。
  7. 专项表 DDL 规范。02 §7 的 6 张表统一带 CHECK 约束、版本列、审核人、created_at/updated_at、审计语义,索引设计考虑了查询路径(队列索引 (status, priority, created_at)、前缀索引 + phrase_hash 规避长文本误判)。
  8. 职责边界写得好。03 §2 的"禁止事项"列和 §10.3「Agent 不能确认、关闭或升级预警」把 AI 与人的决策边界钉死,这是金融场景的必备约束。

2.2 结构性问题(一句话概括)

01 是权威源,02 和 03 承接它的能力,但各自都漏了一个 01 已经承诺的持久化载体——澄清轮次、通用领域事件、工具调用记录。这是"设计已完成、数据模型没跟上"的典型缺口,越晚补代价越大。


3. 分文档详评

3.1 01-通用Agent平台开发设计.md(86 / A-)

扣分明细

编号 问题 位置 严重度
A1 clarification_round 无持久化载体,澄清/转人工逻辑无法实现 01:245、744 P0
A2 通用领域事件要求 Outbox,但 02 无对应表 01:1125 P0
A3 allowed_tools 按意图配置(02)与单集合消费(01)冲突 01:273、1261 P0
A4 合规策略来源不一致:主流程用 config,降级用 definition 01:583 vs 816 P1
A5 CoreResult 互斥语义矛盾:正文"不能同时为空",实现"恰好一个非空" 01:368 vs 770 P1
A6 目录树缺 bootstrap.py、AgentAuthorizer,但 §7.1/§15.3 引用 01:139-152 vs 996、1292 P1
A7 伪流式与"首个安全 Token 延迟"指标冲突 01:579-636、907、1131 P1
A8 save_user_message 在记忆召回前,与七步描述不符;异常路径遗留孤儿用户消息 01:573 vs 925 P1
A9 @final 仅静态约束,契约测试用 __dict__ 扫描可被 setattr 绕过 01:1312 P2
A10 "核心公共逻辑覆盖率 ≥80%"未界定范围 01:1167 P2
A11 意图集合能否被数据库扩展未明确(§7.3 只列角色/入口/工具为代码上限) 01:1040 P2
A12 幂等键仅提要求,无幂等表 / Redis key 规范 01:936 P2
A13 Prompt 与模型参数"必须版本化并写入审计"无落地载体 01:1087 P2
A14 示例代码 asdict 在第 794 行使用,导入语句在第 853 行 01:794、853 文案

详细说明(关键三项)

  • A4 合规策略来源不一致:主流程 guard_output(raw_reply, config.compliance_policy, context)(01:583)使用解析后的策略;降级分支 fallback_service.reply_for(error.code, self.definition.compliance_policy)(01:816)使用代码默认值。若数据库配置收紧过策略,降级路径会绕过该收紧。建议:统一改为 config.compliance_policy,或在 _persist_degraded_result 签名中传入 config。
  • A5 互斥语义矛盾:01:368 写"两者不能同时为空",01:770 的实现是 if has_direct == has_material: raise,即同时禁止两者同时非空。建议:正文改为"direct_reply 与 generation_material 必须恰好提供一个",与实现对齐。
  • A7 伪流式:当前顺序为「全量生成 → 合规校验 → 落库 → 审计 → 发事件 → 按 256 字符切片发 delta」(01:579-636)。SSE 的价值退化为分块传输,delta 并非 token 级增量,首字延迟 ≈ 全量生成 + 合规 + 落库时间。建议:要么在 §13 删除"首个安全 Token 延迟"指标并明确"合规优先、非真流式"的取舍;要么设计"完整句子缓冲 + 句级合规"方案并单独评估泄漏风险。

3.2 02-数据库建表设计.md(77 / B+)

扣分明细

编号 问题 位置 严重度
B1 基础 33 张表以外部《合并后数据库表设计last.md》为基线,该文件不在仓库,§12 验收无法执行 02:5 P0
B2 无会话主表,session_id 仅为逻辑外键,澄清轮次与会话状态无处存储 02:97 P0
B3 无通用领域事件 Outbox 表,仅 memory_sync_outbox 02:74 P0
B4 conversation_message 缺 tool_calls 列,与 03:138 冲突 02:120-126 P0
B5 fin_knowledge_meta 有效期过滤用 CURRENT_DATE,与"统一 UTC"约定冲突 02:172-173 vs 36 P1
B6 uk_feedback_message_customer(message_id, customer_id):MySQL 中 NULL 不参与唯一性,匿名反馈可重复刷票 02:334 P1
B7 "同一时刻仅一个有效版本"仅靠服务层保证,缺数据库级加固 02:385、489 P1
B8 无归档 / 分区 / 容量策略;conversation_message、interaction_audit、fin_market_price、memory_sync_outbox 只增不减 02 全文 P1
B9 02、03 无版本号与变更记录(仅 01 标 v2.1) 02 / 03 头部 P1
B10 ALTER TABLE ... AFTER trace_id 未说明前置列存在性检查 02:132 P2

详细说明(关键两项)

  • B6 NULL 唯一性陷阱:MySQL 唯一索引中多个 NULL 视为互不相等,因此 customer_id IS NULL 的匿名反馈可无限重复插入,uk_feedback_message_customer 对其完全失效。文档虽要求"必须由 API 限流"(02:346),但把约束责任从数据库移到应用层。建议:customer_id 使用 0 作为匿名哨兵值,或增加 client_fingerprint 列参与唯一键。
  • B7 有效版本唯一性:agent_reply_template 与 agent_intent_config 的"同一时刻仅一个有效版本"依赖发布服务事务保证(02:385、489),并发发布存在窗口。建议:用生成列 + 唯一索引在数据库层强制(见 §5.1 补丁)。

3.3 03-平台端到端流程文档.md(76 / B+)

扣分明细

编号 问题 位置 严重度
C1 要求落库 tool_calls,02 无该列 03:138 P0
C2 澄清轮次存储未定义,§5.3 "每轮只追问一个关键槽位"无法实现 03:112 P0
C3 异常矩阵缺"事件发布失败"行,与 01 §12 不对齐 03:361-372 vs 01:1125 P1
C4 无版本号与变更记录 03 头部 P1
C5 缺幂等、超时预算、重试上限的端到端数值 03 §4、§5 P2
C6 全程纯文本流程,跨 4 张表事务(§9)缺时序图 / 状态图 03 §9、§10 P2
C7 Redis 实时行情不可用时交易流程如何处理未说明 03:248 P2

说明:03 的职责边界、异常矩阵、数据落点表(§14)质量高,主要问题是从 01/02 继承的落点缺口,而非自身叙述缺陷。


4. 问题清单汇总

4.1 P0:不闭环不能开工(5 项)

# 问题 涉及文档 建议修复
P0-1 clarification_round 无持久化载体 01:245、744 / 02:97 / 03:112 新增 svc_conversation_session 表(见 §5.1)
P0-2 通用领域事件无 Outbox 表 01:1125 / 02:74 新增 domain_event_outbox 表(见 §5.1)
P0-3 tool_calls 落点缺失,01/02/03 不一致 03:138 / 02:120-126 conversation_message 增补 tool_calls JSON(见 §5.1)
P0-4 allowed_tools 按意图配置 vs 单集合消费 02:402 / 01:273、1261 修改 ResolvedAgentConfig 契约(见 §5.2)
P0-5 数据库基线不自包含,验收无法执行 02:5 基线 DDL 并入 02 或作为附录提交

4.2 P1:编码前应修正(8 项)

# 问题 位置
P1-1 合规策略来源不一致(config vs definition) 01:583 vs 816
P1-2 CoreResult 互斥语义矛盾 01:368 vs 770
P1-3 目录树缺 bootstrap.py、AgentAuthorizer 01:139-152 vs 996、1292
P1-4 伪流式与首字延迟指标冲突 01:579-636、907、1131
P1-5 用户消息落库顺序与七步描述不符;异常路径孤儿消息 01:573 vs 925
P1-6 CURRENT_DATE 与 UTC 约定冲突 02:172-173 vs 36
P1-7 匿名反馈唯一索引因 NULL 失效 02:334
P1-8 无归档 / 分区策略;02、03 无版本号 02 全文、03 头部

4.3 P2:建议增强(6 项)

# 问题 位置
P2-1 幂等键无表 / Redis key 规范 01:936 / 03:4.2
P2-2 Prompt 版本化无落地载体 01:1087
P2-3 意图集合能否被数据库扩展未明确 01:1040
P2-4 @final 仅静态约束,契约测试可绕过 01:1312
P2-5 覆盖率范围未界定;缺超时预算与 SLA 数值 01:1167 / 03 §4-5
P2-6 缺时序图 / 状态图;Redis 行情故障路径未定义 03 §9、§10

5. 建议修复方案

5.1 P0 表结构补丁 DDL

以下 DDL 与 02 §7 风格一致,需通过 Alembic 生成迁移,并在空库、旧版本库、回滚路径各演练一次。

5.1.1 svc_conversation_session(修复 P0-1)

承载会话状态与澄清轮次,使 01:744 的低置信澄清/转人工逻辑可实现。

CREATE TABLE svc_conversation_session (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  session_id VARCHAR(64) NOT NULL,
  user_id BIGINT UNSIGNED NOT NULL,
  portal VARCHAR(32) NOT NULL,
  agent_type VARCHAR(32) NULL,
  status VARCHAR(16) NOT NULL DEFAULT 'active',
  clarification_round TINYINT UNSIGNED NOT NULL DEFAULT 0,
  message_count INT UNSIGNED NOT NULL DEFAULT 0,
  last_intent VARCHAR(32) NULL,
  started_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
  last_active_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
  ended_at DATETIME(6) NULL,
  created_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
  updated_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6)
    ON UPDATE CURRENT_TIMESTAMP(6),
  PRIMARY KEY (id),
  UNIQUE KEY uk_session_id (session_id),
  KEY idx_session_user (user_id, last_active_at),
  KEY idx_session_status (status, last_active_at),
  CONSTRAINT fk_session_user FOREIGN KEY (user_id) REFERENCES sys_user(id),
  CONSTRAINT chk_session_status
    CHECK (status IN ('active', 'ended', 'transferred', 'expired')),
  CONSTRAINT chk_session_clarification
    CHECK (clarification_round <= 10)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
  COMMENT='Agent会话状态与澄清轮次';

配套约定:

  • RequestContextBuilder 每次请求读取该行,将 clarification_round 写入 RequestContext。
  • 澄清轮次递增使用条件更新:UPDATE ... SET clarification_round = clarification_round + 1 WHERE session_id = ? AND clarification_round = ?,避免并发重复澄清。
  • Redis 作为热缓存,MySQL 为权威来源;Redis 丢失可从本表恢复。
  • ended_at 仅在 end_session = true 或超时过期时写入。

5.1.2 domain_event_outbox(修复 P0-2)

承载 01 §5.4 EventPublisher 与 01:882 build_domain_events() 产出的通用领域事件,与 memory_sync_outbox 职责分离。

CREATE TABLE domain_event_outbox (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  event_id CHAR(36) NOT NULL,
  event_type VARCHAR(64) NOT NULL,
  aggregate_type VARCHAR(32) NOT NULL,
  aggregate_id VARCHAR(64) NOT NULL,
  trace_id VARCHAR(64) NOT NULL,
  payload JSON NOT NULL,
  status VARCHAR(16) NOT NULL DEFAULT 'pending',
  retry_count INT UNSIGNED NOT NULL DEFAULT 0,
  next_retry_at DATETIME(6) NULL,
  last_error VARCHAR(500) NULL,
  occurred_at DATETIME(6) NOT NULL,
  published_at DATETIME(6) NULL,
  created_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
  updated_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6)
    ON UPDATE CURRENT_TIMESTAMP(6),
  PRIMARY KEY (id),
  UNIQUE KEY uk_domain_event_id (event_id),
  KEY idx_domain_outbox_pending (status, next_retry_at, id),
  KEY idx_domain_outbox_aggregate (aggregate_type, aggregate_id),
  KEY idx_domain_outbox_trace (trace_id),
  CONSTRAINT chk_domain_outbox_status
    CHECK (status IN ('pending', 'published', 'failed', 'dead'))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
  COMMENT='通用领域事件Outbox';

配套约定:

  • 事件与业务写入(conversation_message、interaction_audit)在同一事务提交,满足 02 §10 的原子性要求。
  • uk_domain_event_id 保证消费者幂等,重复投递不会重复处理。
  • 重试采用指数退避写 next_retry_at,超过阈值置 dead 并告警。

5.1.3 conversation_message 增补 tool_calls(修复 P0-3)

ALTER TABLE conversation_message
  ADD COLUMN tool_calls JSON NULL
    COMMENT '脱敏后的工具调用摘要数组' AFTER source_references;

同步修正 03 §5.6 与 01 §6.2:AgentResult.tool_calls 序列化后写入该列,字段结构与 SSE tools 事件(01:628 serialize_tool_calls)保持一致,禁止写入未脱敏参数。

5.1.4 有效版本唯一性加固(修复 P1-7 的同类问题)

ALTER TABLE agent_reply_template
  ADD COLUMN active_key VARCHAR(96) GENERATED ALWAYS AS
    (IF(status = 'active', CONCAT(template_code, ':', locale), NULL)) STORED,
  ADD UNIQUE KEY uk_reply_template_active_one (active_key);

ALTER TABLE agent_intent_config
  ADD COLUMN active_key VARCHAR(128) GENERATED ALWAYS AS
    (IF(status = 'active', CONCAT(agent_type, ':', intent_code), NULL)) STORED,
  ADD UNIQUE KEY uk_intent_config_active_one (active_key);

利用"唯一索引中 NULL 不参与比较"的特性,非 active 行 active_key 为 NULL 可无限共存,active 行则强制唯一,把服务层约定升级为数据库级约束。

5.1.5 时间过滤修正(修复 P1-6)

02:172-173 的 CURRENT_DATE 依赖会话时区,与 02:36"统一使用 UTC"冲突。修正为:

review_status = 'published'
AND status = 'active'
AND (effective_date IS NULL OR effective_date <= UTC_DATE())
AND (expire_date IS NULL OR expire_date > UTC_DATE())

或在仓储层显式传入已计算的 UTC 日期参数,避免 SQL 依赖会话时区。

5.2 代码契约修正清单(01)

编号 修正内容 位置
F1 _persist_degraded_result 使用 config.compliance_policy,签名增加 config 参数 01:802-817
F2 CoreResult 注释改为"direct_reply 与 generation_material 必须恰好提供一个" 01:368
F3 目录树补 service/agent/bootstrap.py、service/agent/authorizer.py,并定义 AgentAuthorizer 协议 01:139-152
F4 ResolvedAgentConfig.allowed_tools 改为 Mapping[str, frozenset[str]](按意图),或在文档中明确"Agent 级并集 + 工具内二次校验" 01:273
F5 §13 明确"合规优先、非真流式",删除或重新定义"首个安全 Token 延迟" 01:1131
F6 §6.3 第 1 步补充"保存用户消息",与 §6.2 实际顺序一致;明确 AgentRequestError 后孤儿用户消息的处理策略 01:920-926
F7 §7.3 第 2 条补充 supported_intents 是否为代码级上限 01:1040
F8 契约测试增加运行期守卫或 CI AST 检查,替代仅依赖 __dict__ 扫描 01:1312
F9 §14.1 界定"核心公共逻辑"的具体模块范围 01:1167

5.3 流程文档补强(03)

编号 修正内容
G1 §13 异常矩阵增加"事件发布失败 → Outbox 异步重试,不丢失关键事件"
G2 §5.3 补充澄清轮次的读取与递增路径,指向 svc_conversation_session
G3 §9 补充 Redis 实时行情不可用时的处理(拒绝下单 / 降级为最近收盘价并明确提示,二选一)
G4 增加会话生命周期时序图与交易事务时序图
G5 头部补充版本号、修订记录与关联文档索引

6. 放行条件与后续动作

6.1 放行条件(全部满足才可派发编码)

  • P0-1 ~ P0-5 全部闭环,且 02 的基线 DDL 已并入或附于文档内
  • 三份文档的交叉引用一致性核对通过(见附录 A)
  • P1 项已修正或已登记为有责任人的待办
  • 02、03 补齐版本号与修订记录

6.2 建议动作顺序

  1. 补表:执行 §5.1 的 4 组 DDL 补丁,更新 02 的表总览(39 → 42 张)。
  2. 修契约:按 §5.2 修正 01 的 9 处代码级不一致。
  3. 对齐流程:按 §5.3 补强 03 的异常矩阵与关键路径。
  4. 重跑核对:以附录 A 为检查表,逐行核对三份文档的交叉引用。
  5. 派发:使用 01 §15.5 的交接模板,附本次补丁后的表结构与契约。

6.3 工作量估算

阶段 内容 预估
补表 + 迁移脚本 §5.1 四组 DDL 及 Alembic 版本 0.5 - 1 人日
契约修正 §5.2 九项,以文档修改为主 0.5 人日
流程补强 §5.3 五项,含两张时序图 0.5 人日
交叉核对 附录 A 全表 0.5 人日
合计 约 2 人日

附录 A:交叉引用一致性核对表

核对项 01 02 03 状态
Agent 类型命名(customer_service / advisor / risk / operations) §5.1 权限表 §6.1 ✅ 一致
客服五类意图(faq/product_inquiry/policy_explain/chitchat/transfer_human) — 02:429 §6.1 ✅ 一致
conversation_message 字段(intent/confidence/source_references) §6.3 02:120-126 §5.6 ⚠️ 缺 tool_calls
转人工工单与风险工单分离 — 02:111、220 §6.4、§10.2 ✅ 一致
知识有效过滤条件(published + active + 有效期) §12 02:170-174 §6.2、§7 ⚠️ CURRENT_DATE 时区
记忆提取触发条件 §6.2 — §12.2 ✅ 一致
降级策略矩阵 §12 — §13 ⚠️ 03 缺事件发布失败
Outbox 可靠投递 §12 仅 memory_sync_outbox §5.7 ❌ 缺通用事件表
会话状态与澄清轮次 §5.2、§6.2 ❌ 无表 §5.3 ❌ 缺表
工具白名单来源 §7.3 按意图配置 — ❌ 语义冲突
适当性校验(C1-C5/R1-R5) §10 fin_risk_assessment §8 ✅ 一致
交易范围限定(场内模拟,不建场外表) §1 §2 §9、§11 ✅ 一致

附录 B:评审发现统计

严重度 数量 分布
P0(阻断实施) 5 01: 3 项、02: 4 项、03: 2 项(含交叉项)
P1(编码前修正) 8 01: 5 项、02: 3 项、03: 1 项
P2(建议增强) 6 01: 4 项、03: 2 项
文案级 1 01: 1 项

注:单项问题可能跨多份文档,故分文档计数之和大于总数。


附录 C:v3.0 复审结果

复审对象:01(v3.0,1575 行)、02(v3.0,812 行)、03(v3.0,462 行)、新增 docs/00-新数据库基线设计.md(1137 行)与 AGENTS.md
复审方式:四份文档逐行通读 + 基线逐字段比对 + MVC+S 分层约束逐项核对
复审结论:综合 90/100(A-),MVC+S 架构合规性 92/100(A);上一轮 P0 项 5/5 全部闭环

C.1 评分变化

文档 v2.1 v3.0 变化 等级
01-通用Agent平台开发设计.md 86 93 +7 A
02-数据库建表设计.md 77 88 +11 B+
03-平台端到端流程文档.md 76 89 +13 A-
三件套综合 80 90 +10 A-
MVC+S 架构合规性 未评 92 — A

C.2 MVC+S 架构合规性检查表

# 检查项 判据 结论
1 四层职责边界明确 01 §3.1 同时给出职责与"不允许承担的职责" ✅ 符合
2 Controller 薄层,无业务判断 01 §3.3 示例仅路由 + 调 Service + 返回 View ✅ 符合
3 Controller 不访问 Model/ORM 01 §3.1、02 §3 ✅ 符合
4 Service 编排业务,不解析 HTTP 细节 01 §3.1、§573 ✅ 符合
5 Model 仅做实体映射与持久化 01 §3.1、02 §3 ✅ 符合
6 View 仅做格式转换,无业务判断与 DB 访问 01 §3.1、§131 ✅ 符合
7 依赖方向 Controller → Service → Repository → Model 01 §227 ⚠️ 方向正确,但"Service -> View"一句表述有歧义
8 Agent 属于 Service 层,非第五层 01 §59、AGENTS.md 第 6 条 ✅ 符合
9 业务 Agent 不得直连 MySQL/Redis/Milvus/Neo4j 01 §227、§541 ✅ 符合
10 业务 Agent 必须经 AgentFactory 创建,不绕过鉴权/记忆/合规/审计/事件 01 §6.1、§16.3 契约测试、AGENTS.md 第 7 条 ✅ 符合
11 事务边界收敛在 Service 层 01 §5.4 AgentPersistenceService、02 §11 ✅ 符合
12 新增 8 张底座表未破坏分层 02 §8,均由 Service 经 Repository 访问 ✅ 符合
13 新增 6 个服务(Session/Idempotency/ConfigCenter/ModelRouter/Relationship/Persistence)均在 app/service/ 下 01 §4 ✅ 符合
14 横切关注点(合规、审计、事件)统一在底座,未下放业务 Agent 01 §11、§13、§16.1 ✅ 符合
15 场外运营流程独立,不写入场内交易表 01 §1、02 §2、03 §11、AGENTS.md 第 8 条 ✅ 符合

架构层面待改进(不影响合规判定)

# 项 说明
A-1 01 §227 表述歧义 "Service -> View 仅返回中立结果,由 Controller 选择 JSON 或 SSE View" 中箭头方向与后半句矛盾,应改为"Service 不依赖 View;Controller 选择 View"
A-2 Service 层粒度膨胀 app/service/ 下已有 15 个子包,应用层编排与基础设施适配器混放;建议在文档中区分 service/application(编排)与 service/infrastructure(模型网关、工具、存储适配)
A-3 AgentRequest 定位 HTTP 请求模型定义在 Service 层 contracts.py,Controller 与 Service 共用同一对象;严格 MVC+S 下 Controller 应持有 API DTO,Service 持有命令对象,建议补充设计说明
A-4 app/model/dto/ 归属模糊 DTO 位于 Model 层,可能造成 Model 被上层反向依赖,文档未说明其用途与依赖方向

C.3 上一轮问题修复验证

P0(5 项):全部闭环 ✅

编号 问题 修复证据
P0-1 澄清轮次无持久化载体 新增 svc_conversation_session(02 §8.1);01 §6.4 规定"从会话表读取、旧值条件原子递增、Redis 只做缓存";03 §4.2、§5.3 同步
P0-2 通用领域事件无 Outbox 新增 domain_event_outbox(02 §8.2);01 §5.4 新增 AgentPersistenceService.complete_run 同事务写 Outbox;03 §5.6、§5.7
P0-3 tool_calls 落点缺失 澄清基线已含 tool_calls(00:853),02 §6.1 明确沿用既有定义,01 AgentResult 保留该字段
P0-4 allowed_tools 按意图 vs 单集合 ResolvedAgentConfig.allowed_tools_by_intent: Mapping[str, frozenset[str]](01:307);§7.3 明确"按当前意图求交集,禁止先求并集";RiskAgent 示例已同步(01:1401)
P0-5 基线不自包含 新增 docs/00-新数据库基线设计.md 纳入项目,AGENTS.md 第 1 条固化为不可变基线

P1(8 项):修复 5、部分 1、未修 2

编号 问题 状态 说明
P1-1 合规策略来源不一致 ✅ 已修 _persist_degraded_result 增加 config 参数并优先使用解析结果(01:862、870、896)
P1-2 CoreResult 互斥语义矛盾 ✅ 已修 01:408 改为"必须恰好提供一个"
P1-3 目录树缺 bootstrap.py/authorizer ✅ 已修 01:157-158 已补
P1-4 伪流式与首字延迟指标冲突 ✅ 已修 01 §12 明示"合规优先、不承诺 Token 级真流式";指标改为"首个安全文本片段延迟"(01:1264)
P1-5 用户消息先于记忆召回落库、孤儿消息 ⚠️ 部分 顺序未调整;AgentRequestError 后遗留用户消息仍未定义处理策略
P1-6 CURRENT_DATE 与 UTC 冲突 ✅ 已修 02:199-200 改为 UTC_DATE()
P1-7 匿名反馈唯一索引 NULL 失效 ❌ 未修 02:361 仍是 (message_id, customer_id),02:373 仍依赖 API 限流
P1-8 无归档/分区策略 ❌ 未修 02 全文仍无分区、冷归档或容量规划

P2(6 项):修复 4、部分 1、未修 2

编号 问题 状态
P2-1 幂等无落地载体 ✅ 新增 request_idempotency(02 §8.3)+ 01 §6.4 幂等范围定义
P2-2 Prompt 版本化无载体 ✅ 新增 prompt_template_version(02 §8.8)
P2-3 意图集合可否扩展未明确 ✅ 01:1106 明确"数据库不能新增 supported_intents 之外的意图"
P2-4 @final 可被绕过 ❌ 仍为 __dict__ 扫描(01:1455)
P2-5 覆盖率范围未界定;缺 SLA 数值 ⚠️ 部分:已补模型超时 15s、重试 1 次、最多 3 端点(01:1168-1170);覆盖率范围仍未界定
P2-6 缺时序图;Redis 行情故障未定义 ⚠️ 部分:03:293 已补行情不可用拒绝下单;时序图仍未补

C.4 v3.0 新发现的问题

编号 问题 位置 严重度
N1 fin_knowledge_meta.content_text 偏离基线:基线定义为 MEDIUMTEXT NOT NULL,02 实施为 NULL 并声明"后续不得更改",属对基线字段定义的单方面变更,未登记偏差 00:875、1028 vs 02:181、192 P1
N2 DomainEvent 缺 aggregate_type:Outbox 表要求该列 NOT NULL,但 01 的事件契约无此字段,填充规则未定义 02:503 vs 01:484-489 P1
N3 03 §3 流程顺序与 §5.6 矛盾:总流程把"View 生成结果"排在"保存 conversation_message"之前,而 §5.6 与 01:963 要求持久化成功后才发送最终 SSE 03:42-46 vs 03:153 P1
N4 02 关系图与 DDL 不一致:关系图声明 svc_conversation_session 1 --- N request_idempotency,但 request_idempotency 表无 session_id 字段 02:132 vs 02:530-554 P2
N5 AgentRequest.metadata: dict[str, Any] 与 §5.1"不传递无结构的顶层 dict"自相矛盾 01:237 vs 259 P2
N6 agent_reply_template / agent_intent_config 的"同一时刻仅一个有效版本"仍靠服务层保证,未采用 config_release 已有的生成列唯一约束方案 02:412、02:575 P2
N7 model_routing_rule.fallback_endpoint_ids 用 JSON 数组存储端点 ID,无法用外键保证端点有效性,应用层校验规则未定义 02:669 P2
N8 02 §13 验证清单口径与基线不一致:"原 39 张表"包含 02 自建的 6 张专项表,而基线仅有 33 张表 02:793 vs 00 §4 P2

C.5 v3.0 剩余待办

优先级 事项
P1 N1 在 02 中登记基线偏差或提请修订基线;N2 为 DomainEvent 补 aggregate_type 或定义推导规则;N3 修正 03 §3 流程顺序;P1-5 明确孤儿用户消息策略
P2 P1-7 匿名反馈唯一性、P1-8 归档分区、P2-4 契约测试加固、N4-N8
架构 A-1 修正 §227 表述;A-2 划分 Service 层应用/基础设施边界;A-3 说明 AgentRequest 定位;A-4 明确 model/dto 依赖方向

C.6 复审结论

v3.0 把上一轮的核心缺口全部补齐:会话状态、幂等、通用 Outbox、配置发布中心、多模型路由、Prompt 版本、Neo4j 受控关系视图均有了持久化载体与流程定义,并且新增的 8 张表全部是新增表、表名与基线无冲突,符合 AGENTS.md 的不可变基线约束。

架构判定:符合 MVC+S。四层职责、依赖方向、Agent 归属、横切关注点收敛均满足约束,业务 Agent 的越权路径在文档层面已被封堵(契约测试 + 工厂 + @final + 依赖注入)。

放行判定:可作为实施基线,建议先修 N1-N3 三项 P1(约 0.5 人日)再派发编码 Agent。

附录 D:v3.1 整改记录

整改日期:2026-09-09
决策依据:项目负责人对附录 C 问题逐项确认;本附录只记录 v3.1 的处理范围,不改写 v2.1/v3.0 的原始评审结论。

D.1 已处理项

对应意见 v3.1 整改结果
记忆提取事件事务一致性 complete_run() 接收提取判定,并在保存助手消息、审计和幂等完成状态的同一事务内写入 memory.extraction_requested Outbox;提交后由 Worker 消费。
N2 DomainEvent 补齐 event_id、aggregate_type 和 occurred_at,所有示例事件均给出完整字段。
N3 03 总流程调整为先提交消息、审计、幂等状态和 Outbox,再发送最终 JSON 或 SSE;仅 start 可以提前发送。
N4 request_idempotency 增加 session_id、索引及到 svc_conversation_session 的外键,DDL 与关系图一致。
N5 AgentRequest.metadata 改为禁止额外键的结构化模型,只允许 locale、client_version 和 ui_entry。
N6 回复模板和意图配置增加 active_key 生成列及唯一索引,由数据库保证每个业务组合最多一个 active 版本。
N7 新增 model_routing_fallback 权威关系表,对备用端点建立外键和顺序唯一约束;原 JSON 字段仅保留为兼容快照。
可恢复运行实体 根据接口设计新增 agent_run,保存运行状态、Worker 租约、结果定位和安全重试信息;run_id 与 trace_id 分离。

D.2 明确暂不处理项

对应意见 决策
缺少 33 张基础表的可执行 DDL 本轮不处理。
N1 content_text 基线差异 本轮不处理。
N8 及表数口径文字问题 不专项处理;仅因新增一张平台表将实际总数同步为 48。
评分与等级不一致 本轮不处理,保留原评审记录。

D.3 v3.1 数据库规模

数据库设计现由 33 张基础业务表、6 张智能客服专项表和 10 张平台底座增量表组成,共 49 张。v3.1 仅增加字段和新表,不重命名、删除或改变任何已有表名及已有字段定义。