01-W1-findings-response.md 主线整合 12 项 Neo 反馈:
- 直接同意 7 项 (F1-1/1-5 + F2-1 + F3-1/3/5 等)
- 修正 2 项: F1-2 降级 P1 UX (admin-web 无 site_admin 登录),
F1-4 撤销 (前提错误)
- 评估 1 项: F1-3 改良为 Hot DB + Cold Parquet 按月分区
- 简化 1 项: F2-2 基于 Neo 前提 5 分钟方案 (删 .gitignore + 入仓)
- 联网搜 1 项: F3-2 DashScope Qwen3-Max-Preview $1.20/$6.00 per 1M
+ Qwen3 切词 1000 字符 ≈ 500 tokens + SCD2 配置表方案
- 答疑 1 项: F3-4 沙箱越界 422 拒绝
F2-1-openapi-history.md (234 行) 真相:
- 抓取脚本 scripts/ops/_export_openapi.py 12 行曾存在
- 2026-04-06 00:39 commit 779b2f6 批量清理 1155 个废弃文件时
被 Claude Opus 4.6 误归档到 _DEL/_DEL/scripts/ops/
- 36 分钟前同一天还跑过最后一次抓取
- 28 天内无人发现, 9/10 缺失端点是工具消失后新加 router
- 脚本本身无 bug, 推荐恢复 + 加 hook 防御
14 KiB
F2-1 OpenAPI 与代码不同步 历史调研
日期:2026-05-04 / 触发:Neo 担忧"有历史原因被忽略" / Wave 1 W1-T7 P1-7 批 1 后续
一、backend-api.json 历史
文件路径:docs/contracts/openapi/backend-api.json(16327 行)。
git 全历史只有 4 次入仓,且每次都是综合性"批量"提交,没有专门的"重抓 OpenAPI"提交:
| commit | 时间 | message 概要 | 文件规模变化 |
|---|---|---|---|
ded6dfb |
2026-02-15 14:58 | init: 项目初始提交 | 首次入仓,仅 35 行(占位) |
6e20987 |
2026-03-09 01:19 | 微信小程序页面迁移校验之前 P5 任务处理之前 | 增量更新 |
79f9a0e |
2026-03-20 01:43 | feat: batch update gift card breakdown spec, backend APIs ... | 增量更新 |
6f8f123 |
2026-04-06 00:03 | feat: 累积功能变更 — 聊天集成、租户管理、小程序更新、ETL 增强、迁移脚本 | +14315 / -X 行,最后一次更新 |
关键事实:backend-api.json 自 2026-04-06 后再未入仓,距今(2026-05-04)整整 28 天。
二、抓取脚本现状(已被归档)
2.1 当前现状:仓库内不存在抓取脚本
scripts/ops/下:Glob scripts/**/_export_openapi*→ 0 命中tools/下:无 OpenAPI 抓取脚本Glob **/_export_openapi*→ 仅命中_DEL/_DEL/scripts/ops/_export_openapi.py
2.2 历史脚本:scripts/ops/_export_openapi.py(已归档到 _DEL/)
源文件 _DEL/_DEL/scripts/ops/_export_openapi.py(12 行,极简):
"""从运行中的 FastAPI app 导出 OpenAPI spec 到 docs/contracts/openapi/backend-api.json"""
import json, pathlib, sys
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[2] / "apps" / "backend"))
from app.main import app
spec = app.openapi()
out = pathlib.Path(r"C:\Project\NeoZQYY\docs\contracts\openapi\backend-api.json")
out.write_text(json.dumps(spec, ensure_ascii=False, indent=2), encoding="utf-8")
print(f"Done: {len(spec['paths'])} paths, {len(spec['components']['schemas'])} schemas")
实现思路:直接 import app.main,调 app.openapi(),写文件。无 run 服务、无 router 排除条件 — 只要 app.main 能 import 成功,所有已 include_router 的路由都会被囊括。
2.3 抓取脚本 git 时间线
| commit | 时间 | 动作 |
|---|---|---|
6e20987 |
2026-03-09 01:19 | 首次创建(A) |
779b2f6 |
2026-04-06 00:39 | 删除(D),归档到 _DEL/ |
66c9ae8 |
2026-04-10 06:45 | 仅路径迁移 C:\NeoZQYY → C:\Project\NeoZQYY |
779b2f6 commit message 节选:
chore: v1 整理 — 清理历史文件、DDL 合并、文档归档
- 清理 1155 个已删除的历史文件(废弃 prompt_logs、tmp、旧 ops 脚本)
- export/ 数据文件从 git 移除(已在 .gitignore)
_export_openapi.py 与 export_v4_report.py、export_v5_report.py 等"v4/v5/v6/v8 报告导出"脚本一起被当作"旧 ops 脚本"误删。这是一次"误伤" — 该脚本与 v4-v8 报告脚本名称都以 export_ 开头,在批量整理时未被分别审视。
2.4 抓取脚本与最后一次 backend-api.json 更新的时间关系
6f8f123 (2026-04-06 00:03) 最后一次 backend-api.json 入仓 (含手抓产物)
↓ 36 分钟后 ↓
779b2f6 (2026-04-06 00:39) 抓取脚本 _export_openapi.py 被归档删除
完美吻合:最后一次手动跑脚本抓 OpenAPI(6f8f123),当晚把脚本归档清理(779b2f6)。从此抓取手段被"撤掉脚手架"却忘了搭新的 — 而 backend-api.json 也再没人手动重抓过。
三、缺失 10 端点的 router 首次注册时间(timeline 对照)
backend-api.json 上次更新:2026-04-06 00:03(commit 6f8f123)。
| 端点 | router 文件 | router 首次入仓 commit | 时间 | 是否晚于 backend-api.json? |
|---|---|---|---|---|
/api/admin/runtime-context/*(5 个) |
apps/backend/app/routers/admin_runtime_context.py |
caf179a |
2026-05-04 02:30 | 是,晚 28 天 |
/api/admin/triggers/unified |
apps/backend/app/routers/admin_triggers.py |
6f8f123 |
2026-04-06 00:03 | 同 commit(见下) |
/api/admin/ai/run/{app_type} |
apps/backend/app/routers/admin_ai.py(后期扩展) |
caf179a |
2026-05-04 02:30 | 是,晚 28 天 |
/api/admin/ai/triggers GET&PATCH |
admin_ai.py(后期扩展) |
caf179a |
2026-05-04 02:30 | 是,晚 28 天 |
/api/admin/ai/prewarm/progress |
admin_ai.py(后期扩展) |
caf179a |
2026-05-04 02:30 | 是,晚 28 天 |
/api/admin/ai/trigger-event |
admin_ai.py(后期扩展) |
caf179a |
2026-05-04 02:30 | 是,晚 28 天 |
注:
admin_triggers.py与admin_ai.py都首次出现在 6f8f123(2026-04-06),但6f8f123commit message 显示"缓 14315 行 backend-api.json,已含 admin_ai 早期 13 个端点"。grep 验证 backend-api.json 中已含/api/admin/ai/dashboard、/api/admin/ai/trigger-jobs、/api/admin/ai/cache/invalidate、/api/admin/ai/budget、/api/admin/ai/batch-run、/api/admin/ai/alerts/...等 13 个端点,不含/api/admin/triggers/unified。即6f8f123当天导出时:admin_triggers.py已合并但app.include_router(admin_triggers.router)顺序在app.include_router(admin_ai.router)之后(diff 显示+app.include_router(admin_ai.router)与+app.include_router(admin_triggers.router)同 hunk 出现)。最可能的原因:人工手抓时,服务先以"未含 admin_triggers"的旧分支启动跑了导出,然后才在同 commit 把 admin_triggers 合并进来。
两种 stale 性质同时存在:
- 5 月新增的 5+4=9 端点(runtime-context 全部 + admin_ai 后期扩展):纯粹"忘抓" — 28 天没人重跑
app.openapi(),而脚本本身已不在仓库。 /api/admin/triggers/unified1 端点:同 commit 内的并发问题 — 4 月 6 日抓取流程不稳健,导出时 router 还没 include。
四、回答 Neo 的"历史原因"
Q1:backend-api.json 上次更新?
2026-04-06 00:03,commit 6f8f123(message"feat: 累积功能变更 — 聊天集成、租户管理、小程序更新、ETL 增强、迁移脚本")。距今 28 天。
Q2:OpenAPI 抓取脚本在哪?
当前不存在。曾经存在 scripts/ops/_export_openapi.py(12 行,2026-03-09 创建),已于 2026-04-06 00:39 commit 779b2f6 删除归档至 _DEL/_DEL/scripts/ops/_export_openapi.py。
实现极简:from app.main import app; spec = app.openapi()。无 router 排除逻辑、无服务启动条件 — 只要 import 成功,所有 include_router 路径都会被收录。脚本本身没 bug。
backend-api.json 当前的"维护方式":事实上无任何自动化 — 仅 .claude/commands/spec-close.md 第 45 行提到"新增/修改 API 端点时手工同步 backend-api.json",但没指明用什么工具。
Q3:缺失 10 端点 router 首次注册时间?(timeline 判定)
- 9 个端点的 router 都是 2026-05-04 02:30
caf179a— 远晚于 backend-api.json 上次更新(2026-04-06)。 - 1 个端点(
/api/admin/triggers/unified)是 2026-04-06 同 commit — 抓取与合并并行竞态。
判定:9/10 是"忘了重抓",1/10 是"抓取流程不稳健 + 脚本即将被删,无后续补救"。抓取脚本本身没有 bug,bug 在流程 — 脚本被删后,无人发现"维护手段已消失",于是 W1-T7 才在 28 天后撞上 stale。
Q4:是否有历史决策刻意排除某些 router?
没有。docs/audit/changes/ 35 份提及 OpenAPI 的审计记录中,无任何"排除特定 router"的决策。反而:
- 2026-02-26 audit 记录强调"接口代码已变更但 OpenAPI spec 可能未同步,待手动操作:运行
python scripts/ops/_export_openapi.py"。 - 2026-02-27 audit 记录"接口代码已变更,OpenAPI spec 已重新导出(59 paths, 59 schemas)"— 工作流是清晰的"改 router → 跑脚本 → 入仓"。
- 2026-03-04 commit 显示 backend-api.json
+5096 行,即"全量重抓"是常态,无排除清单。 - 2026-03-22 P16 spec 收尾文档明文"更新 OpenAPI spec,补充 P16 相关的接口定义和 Schema 字段"。
结论:无刻意排除,完全是工程链路被切断。
Q5:.mcp.json 中 openapi(awslabs.openapi-mcp-server)与 backend-api.json 关系?
MCP 是单向消费方,不反向生成。.mcp.json:51 配置:
"--api-url", "http://127.0.0.1:8000",
"--spec-path", "C:\\Project\\NeoZQYY\\docs\\contracts\\openapi\\backend-api.json",
awslabs.openapi-mcp-server 把 backend-api.json(spec-path)转成 MCP 工具供 Claude/Cursor 调用,它读 spec、不写 spec。意味着:
- spec stale → MCP 看到的就是 stale 接口集 → AI 写代码可能引用不存在的端点(实际不存在)或忽略已存在的端点(本次 W1-T7 的暴露场景)。
- MCP 没"拉 /openapi.json 自动更新"模式(api-url 仅作为调用 base url,spec 始终来自 spec-path)。
真相判定
主因:抓取脚本"误删"导致维护手段消失,且无人发现。
具体链路:
- 2026-03-09:抓取脚本入仓,工作流"改 router → 跑脚本 → 提交"运转良好(2-3 月份多次 audit 记录验证)。
- 2026-04-06 00:03:最后一次成功抓取并提交(commit 6f8f123,加 14315 行)。
- 2026-04-06 00:39:36 分钟后,脚本与 v4-v8 报告脚本一起被批量归档(commit 779b2f6,message 写"清理废弃 prompt_logs、tmp、旧 ops 脚本")— 该归档动作由 Claude Opus 4.6 协助完成,未识别
_export_openapi.py是"活的工具"而非"废脚本"。 - 2026-04-15 ~ 2026-05-04:
admin_runtime_context、admin_ai后期扩展等大量端点新增,无任何人手动同步 backend-api.json(28 天空窗)。 - 2026-05-04(W1-T7 P1-7 批 1):Neo 用 backend-api.json 作为 PRD 权威源,撞上 stale,发现 30%+ 端点缺失。
被忽略的历史原因(根本原因):"AI 一键清理废弃文件"时把"活的工具"当"废脚本"误删,且未走逐文件审议。该归档 commit 的 message 描述是"清理 1155 个已删除的历史文件",数量级巨大,人工逐个审议不现实 — 这暴露了大批量归档时工具识别不足的工程治理风险。
第二次"被忽略"则是流程层:spec-close.md 第 45 行的"新增/修改 API 端点时手工同步 backend-api.json"是唯一的同步约定,但它没指明用什么命令,且无 hook 强制 — 当抓取脚本被删后,没有任何机制告警"工具已消失"。
五、修复路径推荐(基于真相)
选项 A:从 _DEL/ 恢复脚本 + 跑一次重抓 + 入仓
操作:
cp _DEL/_DEL/scripts/ops/_export_openapi.py scripts/ops/_export_openapi.py(脚本就 12 行,无需修改)。cd apps/backend && python ../../scripts/ops/_export_openapi.py(脚本自带sys.path.insert,可从仓库根直接跑,但更稳是在apps/backend下激活 venv 跑)。git add scripts/ops/_export_openapi.py docs/contracts/openapi/backend-api.json && git commit。
优:成本最低、行为可预期(脚本本身没 bug)、立即解锁 Wave 1 W1-T7 后续批次。 劣:无防再次断裂的护栏;下次再有人"清理 ops 脚本"还可能误删。
选项 B:选项 A + 加 hook / pre-commit 防再断
在 .claude/hooks/ 加一个 PostToolUse hook(matcher = Edit|Write,匹配 apps/backend/app/routers/*.py),提醒"router 改动需重抓 OpenAPI"。或加 pre-commit 钩子:检测到 apps/backend/app/routers/ 改动但 docs/contracts/openapi/backend-api.json 未变 → 警告。
优:堵漏;与已有 hook 体系(post_edit_db_doc_sync.py、post_edit_rls_dual_schema.py)一致风格。
劣:hook 只能提醒,不能强制重抓;开发人员仍可"忽略告警"提交。
选项 C:废弃静态 backend-api.json,改运行时拉 /openapi.json
让 .mcp.json 把 spec 直接读 http://127.0.0.1:8000/openapi.json(MCP 支持 url 形式 spec 源),并把 backend-api.json 改成"快照归档"(每 N 周或 release 时手动 dump 一份,标注 release 版本)。
优:根本上消除"代码改 vs 文档改"的同步问题。 劣:依赖后端服务运行;离线开发(无后端跑)时 MCP 不可用;PRD 工作流(P1-7)需要静态 spec 文件作引文锚点,改成动态后引文不稳。
推荐组合:A + B
- A 立即恢复脚本 + 重抓,不晚于 Wave 2 撰写前(批 2-5 还要继续依赖 OpenAPI)。
- B 加一个轻量 hook(提醒级,不阻断),复用现有 hook 模式;或在 spec-close.md 第 45 行明文写出脚本路径与调用命令,弥补"约定无锚点"问题。
C 留作 Wave 5 治理收尾时的远期方案,本轮不动。
六、给 Neo 的决策清单
-
是否同意"恢复脚本 + 重抓"(选项 A 主路径,5 分钟可完成)?
- 若同意:子代理可在 W1-T7 批 2 撰写前,把
_DEL/_DEL/scripts/ops/_export_openapi.py复制回scripts/ops/,然后由 Neo(或 main agent)跑一次 + 入仓。不在本调研子代理职责内(本任务约定"只调研不改代码")。
- 若同意:子代理可在 W1-T7 批 2 撰写前,把
-
是否要加 hook 防再断(选项 B)?
- 推荐内容:PostToolUse 匹配
apps/backend/app/routers/*.py的 Edit/Write,提醒"router 已改,记得跑python scripts/ops/_export_openapi.py同步 OpenAPI"。 - 是否加在本轮 W1-T7 完成前,还是单独立一个 Wave 2 任务?
- 推荐内容:PostToolUse 匹配
-
是否要在
spec-close.md第 45 行补脚本路径?- 现在仅"新增/修改 API 端点时 →
docs/contracts/openapi/backend-api.json"。建议补成"docs/contracts/openapi/backend-api.json(运行python scripts/ops/_export_openapi.py重抓)"。
- 现在仅"新增/修改 API 端点时 →
-
是否要留一份这次"AI 清理误删"的教训记录?
- 建议在
docs/audit/changes/加一份"2026-05-04__openapi-script-recovery.md"(若选项 A 执行后),把"批量清理时把活工具当废脚本"作为治理教训登记,避免后续再误删tools/scripts/ops/中的活工具。
- 建议在