Files
group_fqcd_jr/docs/20-第一版到当前版本变更与迁移指南.md
lzf_0626 6516ccb385 feat: 第二版——接口契约对齐 docs/05,修复静默故障与数据库基线
相对第一版 46fc976 的完整变更。组员迁移对照表见 docs/20。

一、对外契约对齐 docs/05(破坏性,共 4 处,组员需按 docs/20 调整)
1) 配置发布端点改为文档规定的复数资源名:submit→validations、
   approve→reviews(需 body decision)、activate→activations、
   rollback→rollbacks;第一版这 4 个动词式路径 docs/05 从未定义过。
2) 错误码由 8 个笼统码改为 15 个具体语义码(FORBIDDEN→AGENT_PERMISSION_DENIED、
   UNAUTHORIZED→AUTHENTICATION_REQUIRED、CONFLICT→RESOURCE_VERSION_CONFLICT、
   RESOURCE_NOT_FOUND→RUN_NOT_FOUND/SESSION_NOT_FOUND 等),
   输入类错误状态码 400→422。
3) POST /api/v1/agent-runs 与 GET /api/v1/agent-runs/{run_id} 统一为
   {data, meta} 信封(data 内字段名与语义未变)。
4) 错误响应体统一为 {error:{code,message,retryable,field_errors}, meta:{trace_id}},
   不再返回 FastAPI 默认的 {"detail": ...}。

二、数据库基线与约束
新增 39 张表的基线迁移(链根)与联合唯一键纠偏(4 张表、删 8 增 4,幂等收敛);
撤下 config_release 的双人复核 CHECK(应用层已允许自审,审核节点保留,
自审如实写入 reviewer_id);记忆 active key 生成列与唯一键;
activate 开始记录 supersedes_release_id 使版本链可追溯。
docs/00 基线未修改,未重命名或删除任何表与字段。

三、修复会静默出错或无报错的缺陷
- 跑完集成测试后平台会静默失去生效配置:清理只删自己创建的版本,却没有恢复被它
  顶成 superseded 的原生效版本,且审计一并删除因而完全无痕,表现为所有工具被拒
  但没有任何报错。已修清理逻辑并加恢复。
- Worker 单轮异常导致进程退出;记忆抽取调用方的“事务已开始”异常;
  召回缓存丢失 degraded 标记;连接时区未生效导致 created_at/updated_at 差 8 小时;
  .env 与 os.getenv 密钥来源分裂导致“没有可用的已批准模型端点”。
- 记忆信号识别漏判与跨键误命中;SSE 未带 Accept 的协商行为。

四、功能补齐
记忆链路 P1/P2/P3(抽取、受控词表、召回与缓存、生命周期级联及投影事件)、
fin_* 场内交易只读 ORM 层、agent_intent_config 状态流转并在运行期真正生效、
限流(Redis 固定窗口、故障一律放行)、游标校验、trace_id 中间件、
示例业务 Agent fund_query_demo 与一键端到端验证脚本,以及审计/指纹/迁移状态工具。

五、文档与验证
新增 docs/19(业务 Agent 接入实操)、docs/20(第一版迁移指南)与 docs/evidence 证据;
docs/01/02/06/08/09/17 同步实现现状。

验证结果:ruff 通过、mypy 103 文件无错、unit+contract 447 passed、
integration 29 passed、acceptance_check --production 7 PASS、
demo_agent_e2e 9/9 PASS(含失败关闭反证)。
2026-09-10 15:55:54 +08:00

6.8 KiB
Raw Permalink Blame History

第一版 → 当前版本:变更说明与迁移指南

面向对象:正在使用「第一版」的组员。 第一版 = git 提交 46fc976(Sep 9 23:40,feat: add shared fund quote capability)。

0. 先说结论

第一版能跑,但它的接口实现和权威文档 docs/05-接口文档.md 有多处不一致。 这一轮的工作就是把这些实现改成文档规定的样子,同时修掉一批会导致"静默错误"的缺陷。

所以会出现两种情况:

  • 如果你的代码是照着第一版实际的返回值/路径写的 → 需要按 §1 改 4 个地方(都是字符串常量,约半小时)。
  • 如果你当初是照着 docs/05 写的 → 那你以前应该是跑不通的(比如错误码永远对不上),现在才对上文档。

当前状态:这些改动已提交到分支 qyqy_develop,尚未合并回 develop。 第一版完整保存在 46fc976(develop 分支上),随时可以回退或对比。


1. 必须改代码的 4 处(不改就会出问题)

1.1 配置发布接口的路径变了 —— 不改会 404

docs/05 §9.1 与附录 A004–A007 规定的是复数资源名,第一版实现用的是动词。现已按文档改正:

旧(第一版) 新(当前) 说明
POST /api/v1/admin/config-releases/{id}/submit POST .../{id}/validations 提交复核
POST /api/v1/admin/config-releases/{id}/approve POST .../{id}/reviews 审核,需要 body {"decision":"approved"|"rejected","comment":"..."}
POST /api/v1/admin/config-releases/{id}/activate POST .../{id}/activations 激活
POST /api/v1/admin/config-releases/{id}/rollback POST .../{id}/rollbacks 回滚

这 4 个接口都要带 If-Match(ETag 从 GET .../{id} 取)和 Idempotency-Key。 完整可跑通的调用顺序见 docs/19-业务Agent接入实操(示例验证版).md 或 tools/demo_agent_e2e.py。

1.2 错误码全换了 —— 旧判断会静默落空

第一版只用了 8 个笼统码,且和 docs/05 §3.6 的码表不一致。现在已对齐文档:

旧(第一版) 新(当前) 备注
UNAUTHORIZED AUTHENTICATION_REQUIRED 401
FORBIDDEN AGENT_PERMISSION_DENIED 403
VALIDATION_ERROR AGENT_INPUT_INVALID 状态码 400 → 422
CONFLICT RESOURCE_VERSION_CONFLICT / RUN_NOT_CANCELLABLE / IDEMPOTENCY_CONFLICT 409,按场景细分
RESOURCE_NOT_FOUND RUN_NOT_FOUND / SESSION_NOT_FOUND / SESSION_NOT_ACCESSIBLE 404
RECOVERABLE_ERROR DEPENDENCY_UNAVAILABLE(503) / UPSTREAM_TIMEOUT(504)
AGENT_ERROR AGENT_INTERNAL_ERROR 500
RUN_LEASE_LOST RUN_NOT_CANCELLABLE 见 §5 遗留项
AGENT_TYPE_NOT_FOUND 不变
IDEMPOTENCY_CONFLICT 不变

错误响应体结构也变了(第一版是 FastAPI 默认结构):

// 第一版
{ "detail": "Unauthorized" }

// 当前(docs/05 §3.4)
{
  "error": { "code": "AUTHENTICATION_REQUIRED", "message": "...", "retryable": false, "field_errors": [] },
  "meta":  { "trace_id": "..." }
}

1.3 运行接口的响应多了一层 data

docs/05 §3.3 规定业务接口统一 {data, meta} 信封。第一版两个接口是平铺的:

# 第一版
run_id = resp.json()["run_id"]                    # POST /api/v1/agent-runs
status = resp.json()["status"]                    # GET  /api/v1/agent-runs/{run_id}

# 当前
run_id = resp.json()["data"]["run_id"]
status = resp.json()["data"]["status"]

data 里面的字段名和含义完全没变,只是多读一层。

1.4 新增了限流

受保护接口现在会返回 429 RATE_LIMITED,并带 Retry-After 头。 默认阈值是每个用户、每个接口、60 秒 600 次(很宽松,正常调用碰不到)。Redis 不可用时自动放行,不会因为缓存故障拒掉请求。


2. 行为变了,但代码不用改(要知道)

变化 影响
取消双人复核 单管理员可直接自审,reviewer_id 如实写成自己;但"审核"节点不可跳过(草稿必须先提交审核)
agent_intent_config 真正生效 第一版运行期完全忽略该表;现在意图名/描述/示例/阈值会参与分类。如果你们库里配过意图配置,分类结果可能变化
时间统一按 UTC 存储 连接层 SET time_zone='+00:00'。展示时区由 TIMEZONE(默认 Asia/Shanghai)决定,直连数据库看 created_at 会比北京时间少 8 小时
只剩一个常驻 admin 9003;此前用过的临时 9004 等身份已清除
模型端点已配置激活 deepseek-flash 已激活且实测可调用——第一版需要自己配

3. 内部修复(调用方无感)

数据库联合唯一键纠偏(4 张表)、基线 Alembic 迁移、记忆链路 P1/P2/P3 闭环、fin_* 只读 ORM 层、 记忆信号识别缺陷修复、召回缓存降级标记、知识/适当性/健康探针真实实现、SSE 取消契约、 审计与指纹工具、示例 Agent(fund_query_demo)与一键端到端脚本。

其中与你们直接相关的一条:activate 现在会记录 supersedes_release_id, 且测试清理会把被顶掉的生效版本恢复——此前存在"跑完集成测试后平台静默失去生效配置"的故障 (表现为所有工具被拒,但没有任何报错)。


4. 升级方式

建议:把当前工作区提交为一个新版本(第二版),组员按 §1 改 4 处后切换。 迁移成本集中在字符串常量,不涉及业务逻辑。

验证是否改对(组员自己就能跑):

D:\conda\envs\jr_py313\python.exe tools\acceptance_check.py --production   # 期望 7 PASS
D:\conda\envs\jr_py313\python.exe tools\demo_agent_e2e.py                  # 期望 9/9 PASS

注意:跑验收前必须先停常驻 Worker(它和脚本共享 agent_run 队列,会抢走任务导致假失败)。

回退:develop 分支仍停在第一版 46fc976,不合并 qyqy_develop 就等于没有升级,组员不受任何影响。 想逐文件查看差异:git diff 46fc976 qyqy_develop --stat;想试用某一处改动可以单独 cherry-pick。


5. 遗留项(需要底座负责人决定)

  1. RUN_LEASE_LOST 目前归到 RUN_NOT_CANCELLABLE,语义未必贴合;docs/05 §3.6 的码表里没有租约类错误码。
  2. 列表接口缺 next_cursor / has_more:§3.3 通则要求,但 §7.3/§9.6 的具体接口没写这两个字段,实现也一直没有。
  3. docs/05 §9.5 未收录 agent-intent-configs 的 GET 详情路径(接口实际存在,用于取 ETag)。
  4. 游标在 §3.8 被描述为"不透明字符串",实现是裸记录 ID(可用子集,客户端只需回传)。