522 lines
32 KiB
Markdown
522 lines
32 KiB
Markdown
# 开发文档评审报告
|
||
|
||
> 评审对象:`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 的低置信澄清/转人工逻辑可实现。
|
||
|
||
```sql
|
||
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` 职责分离。
|
||
|
||
```sql
|
||
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)
|
||
|
||
```sql
|
||
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 的同类问题)
|
||
|
||
```sql
|
||
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"冲突。修正为:
|
||
|
||
```sql
|
||
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 仅增加字段和新表,不重命名、删除或改变任何已有表名及已有字段定义。
|