32 KiB
开发文档评审报告
评审对象:
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 值得肯定的部分
- 架构分层可执行。01 §3.1 的分层职责表明确列出每层"不允许承担的职责",比只写"职责"的文档强一个档次;§3.3 给出 Controller/Service 的最小代码约束。
- 模板方法约束到位。
run_stream()标记@final,业务子类只实现handle(),§15.3 用契约测试扫描受保护方法(01:1296-1313),把"不许重写骨架"从口头约定变成可执行检查。 - 契约设计专业。全部跨层对象使用 frozen dataclass,集合用
tuple/frozenset,依赖用Protocol倒置,Repository 与外部客户端全部隐藏在 Service 之后(01:491)。 - 配置权威链清晰。01 §7.3 的 5 级优先级(安全硬约束 > 代码上限 > 数据库配置 > 代码默认值 > 环境变量),并明确"数据库只能缩小不能扩大",工具取三层交集(01:1050-1054)。
- 降级矩阵完整。01 §12 与 03 §13 对 Redis/Milvus/Neo4j/模型/工具/审计/事件逐项给出处理策略,且区分"降级"与"阻断"。
- 编码 Agent 交接模板可直接使用。01 §15.5 把任务正文、允许/禁止修改范围、必测项、验收命令、完成回报格式全部固化,这是同类文档中罕见的工程化细节,可当任务单直接派发。
- 专项表 DDL 规范。02 §7 的 6 张表统一带 CHECK 约束、版本列、审核人、
created_at/updated_at、审计语义,索引设计考虑了查询路径(队列索引(status, priority, created_at)、前缀索引 +phrase_hash规避长文本误判)。 - 职责边界写得好。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 建议动作顺序
- 补表:执行 §5.1 的 4 组 DDL 补丁,更新 02 的表总览(39 → 42 张)。
- 修契约:按 §5.2 修正 01 的 9 处代码级不一致。
- 对齐流程:按 §5.3 补强 03 的异常矩阵与关键路径。
- 重跑核对:以附录 A 为检查表,逐行核对三份文档的交叉引用。
- 派发:使用 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 仅增加字段和新表,不重命名、删除或改变任何已有表名及已有字段定义。