相对第一版 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(含失败关闭反证)。
6.8 KiB
第一版 → 当前版本:变更说明与迁移指南
面向对象:正在使用「第一版」的组员。 第一版 = 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. 遗留项(需要底座负责人决定)
RUN_LEASE_LOST目前归到RUN_NOT_CANCELLABLE,语义未必贴合;docs/05§3.6 的码表里没有租约类错误码。- 列表接口缺
next_cursor/has_more:§3.3 通则要求,但 §7.3/§9.6 的具体接口没写这两个字段,实现也一直没有。 docs/05§9.5 未收录agent-intent-configs的GET详情路径(接口实际存在,用于取 ETag)。- 游标在 §3.8 被描述为"不透明字符串",实现是裸记录 ID(可用子集,客户端只需回传)。