docs: 文档全面对齐 MCP 架构,并脱敏公开操作指南
接口与入门: - 新增 docs/MCP.md:注册方法(DSH patch + 通用 client)、返回信封、会话目录布局、标准流程、 硬约束与未实现清单;工具参考段由**运行时 input_schema 自动生成**,与代码同源不会漂移 - 删除 docs/API.md(856 行 Flask 路由/SSE 文档,描述的端点已不存在) - 重写根 README.md:为什么放弃自带前端、职责分工表、快速开始、注册配置、流程链、结构与风险 - 重写 AGENTS.md:命令表换成 mcp/mcp-smoke/upload;mypy 目标改 src;新增「提交纪律」 (pre-commit 的 stash 窗口会回滚未暂存改动)、COM 与 Playwright 线程约束、多进程共存提醒 包文档:src/README.md 与各包 README 按现状重写;新增 src/mcp/README.md(写工具的规矩、 宿主侧四个坑)与 src/upload/README.md(路由表与安全边界);tests/scripts/config 三处索引更新。 提示词迁移:src/infra/llm/ 整个删除,三份系统提示词迁到 .agents/skills/reimbursement/references/ (invoice-fields / travel-info-fields / normal-info-fields),删掉 can_submit 与 suggestion (save_info 也会主动剥离),「严格只输出 JSON」改为「交给对应工具落盘」;新增 SKILL.md 作为 宿主侧操作纪律(已被 DSH 热加载为 reimbursement 技能)。 作废处理:删除 .agents/docs/guides/系统事件流全景图.md(SSE 与 file_events/agent_events 通道已整体 移除,现在没有任何事件流);给项目架构全景图与架构分析-2026-06-15 加「已过时」横幅保留历史; config.example.json 去 consumable_storage 加 upload_pin;.env.example 去 LLM 段并说明已不再自动加载 .env。 安全:docs/报销操作指南.md 的示例配置里原来写着**真实姓名、16 位公务卡号、内网 LLM 地址与工号** (自 9fe5d70/2026-05-25 起在版本历史里),本次全部换成占位符。历史清洗与令牌轮换另议。 门禁(改动含 src/infra/llm 删除,故重跑全部门禁): ruff check PASS | ruff format --check PASS(42) | mypy src PASS(34) | deptry PASS(36) | pytest 107 passed | scripts/mcp_smoke.py PASS(17 工具 / instructions 1449 字节 / case_list ok) 申购单在依赖清理后另已实机复测:2.4s 生成,产物单元格读回正确。
This commit is contained in:
@@ -251,6 +251,10 @@ P0 是唯一可能推翻已定决策的阶段,所以它只做验证、不做
|
||||
|
||||
验证脚手架留在 `cases/_p0-gate/` 与 `cases/20261019-P0闸门自测/`(不入库),P1 收尾时删除。
|
||||
|
||||
### 10.5 日期更正
|
||||
|
||||
本文初版误按 `AGENTS.md` 的 `last_reviewed: 2026-07-02` 推断为 2026-07-23 并以此命名,实际系统日期 2026-10-19,文件已改名。
|
||||
|
||||
### 10.6 第二批工具(2026-10-19)
|
||||
|
||||
新增 `mcp__arb__match_payments` / `save_info` / `validate_info` / `rules_get`(共 14 个工具),并新增 `src/core/matching/runner.py`(从缓存匹配,无 LLM);`match_result.json` 的写入从 extractor 私有函数上移为 `cache.save_match_result`,消除两份实现。`tests/test_mcp_analysis.py` 补 8 个用例(一对多凑额、违反业务约束、can_submit 剥离、draft 不落盘、规则形状、非法 info_type)。
|
||||
@@ -264,10 +268,6 @@ P0 是唯一可能推翻已定决策的阶段,所以它只做验证、不做
|
||||
|
||||
遗留观察(进 P1):`validation_rules.json` 里 `remark` 之类的可选字段写成 `required: true, check_empty: false`,含义变成"键必须存在",模型输出省略空备注时会被判缺失。应把这类字段改 `required: false`,否则每次都要为备注来回补数据。
|
||||
|
||||
### 10.5 日期更正
|
||||
|
||||
本文初版误按 `AGENTS.md` 的 `last_reviewed: 2026-07-02` 推断为 2026-07-23 并以此命名,实际系统日期 2026-10-19,文件已改名。
|
||||
|
||||
### 10.7 重连实验与进程模型(2026-10-19)
|
||||
|
||||
按约定做了 kill 验证:`taskkill /PID <旧服务进程> /F` 之后,DSH 的 mcp-client 监督器**自动重新拉起子进程并重同步工具列表**——工具数从 10 变成 15,新增的 `info_skeleton` / `match_payments` / `save_info` / `validate_info` / `rules_get` 当场可调用,且全部通过真实 MCP 线路验证(`info_skeleton` 出的骨架填值后 `save_info` 校验 valid=true、置信度 1.0)。**不需要重启 dsh 就能上新工具**,只要让旧子进程退出。
|
||||
@@ -279,3 +279,84 @@ P0 是唯一可能推翻已定决策的阶段,所以它只做验证、不做
|
||||
3. 同理,**任何端口/单例资源都不能靠「进程只有一个」假设**(收件器已是独立进程,不受影响)。
|
||||
|
||||
小遗留:`case_status` 在 travel_info 与 normal_info 同时存在时只报 `info: travel_info`。P1 改成显式提示冲突(混合发票只能走普通报销),避免误按纯差旅提交。
|
||||
|
||||
## 11. P1 落地记录(2026-10-19 晚)
|
||||
|
||||
### 11.1 提交与体量
|
||||
|
||||
| 提交 | 内容 |
|
||||
|------|------|
|
||||
| `cffa4cc` | MCP 服务器 + 上传收件器 + `cases/` 会话模型 + 内核拆分(+1975/-1272 行的前半) |
|
||||
| `0c54dc8` | 删除 `src/agent/`(878 行)与 Web 的 5 个 Agent 端点(824→491 行、18→13 端点) |
|
||||
| `5f57400` | 2a:`com_thread.py` 单线程 actor、`doc_requisition`、`case_patch_extraction`(工具 15→18) |
|
||||
| `53b1abe` | 2b:删除 `src/web/`、CLI、内置 LLM、出库单功能与 `llama-index*`+`python-dotenv`(工具 18→17) |
|
||||
|
||||
改造后:`src/mcp` 808 行 / 9 文件,`src/core` 1481 / 9,`src/infra` 1456 / 11,`src/upload` 343 / 3,`tests` 1333 / 6。
|
||||
整个 `src` 从约 6900 行降到约 4100 行;依赖 6 个(mcp、segno、flask、playwright、PyMuPDF、pywin32)。
|
||||
|
||||
### 11.2 验证
|
||||
|
||||
- `scripts/mcp_smoke.py`(新增):真起 stdio 子进程握手 → **17 个工具、instructions 1449 字节、`case_list` 正常**
|
||||
- 申购单实机:合成 2 张普通发票 + 1 笔 464 元支付 → 匹配(一对多、`review_required` 如期提示)→
|
||||
`exclude=['万用表']` → 5.0s 生成 19456 字节 .xls;依赖清理后复测 2.4s,产物单元格读回正确
|
||||
(`xlrd` 随 llama-index 一起消失,但申购单走 Excel COM,不依赖它——这是复测的直接动因)
|
||||
- `case_patch_extraction` 语义验证:回显 `from`/`to`、未知键告警、与现值相同不写
|
||||
- 门禁:`ruff check` + `format --check`(44 文件)+ `mypy src`(36 文件)+ `deptry` + `pytest 107 passed`
|
||||
|
||||
### 11.3 事故:pre-commit 的 stash 窗口把工作区回滚了
|
||||
|
||||
`git commit` 触发 pre-commit,它会 **stash 未暂存改动** 跑完再还原;我的执行程序在 120s 上限被杀,
|
||||
还原步骤没跑完,`AGENTS.md`、`src/web/routes.py`、`requisition.py`、`tests/test_requisition.py` 四个文件
|
||||
被回滚成 HEAD 版本。`git fsck --unreachable --dangling` 显示 **0 个游离对象**,说明 pre-commit 没有留下可捞的
|
||||
stash,只能按锚点重放。重放时我又因「往上找分隔注释」找过头,把 `_validate_session` 与 `_build_web_config`
|
||||
一起删了——靠**审计删除行清单**(`git diff` 里出现 `-def _validate_session`)才发现,从 HEAD 取回并跑 Web 冒烟确认。
|
||||
|
||||
顺带清掉两个我造成的污染:`git add -A --intent-to-add` 误把 `.mnemon/`(DSH 本地记忆库)放进暂存区;
|
||||
钩子在我给的清空 `HOME` 环境里把缓存写成了仓库内的 `~/` 目录(里面是它 clone 的 ruff-pre-commit 仓库和 venv)。
|
||||
两者都已处理,`.mnemon/` 进 `.gitignore`。
|
||||
|
||||
结论已写进 AGENTS.md「提交纪律」:门禁退出码单独确认;提交前工作区 `git add` 干净,否则用 `--no-verify`
|
||||
并手动跑等价检查。本次 P1 之后的提交都用了 `--no-verify`,等价的 ruff/format/mypy/deptry/pytest 全部手跑。
|
||||
另一个自己制造的疏漏:2b 第一次提交时 `mcp_smoke.py` 还带 2 个可自动修复的 lint 错误就进了 commit,
|
||||
已修复并 `git commit --amend`(现 HEAD `53b1abe`),HEAD 是干净的。
|
||||
|
||||
### 11.4 出库单功能下线(用户决定)
|
||||
|
||||
做 2a 时 `doc_consumable` 无法实测,追出一个真实缺陷:模板 `易耗品、出库单.doc` 在提交 `63b97c6`
|
||||
(2026-08-31,同一提交新增了 Excel 申购单)被删除,此后 Word 路径必然 `FileNotFoundError`;
|
||||
而 Web 的 `_try_fill_consumable_doc` 把异常吞成一条日志——**普通发票提交有 7 周在静默跳过出库单生成**。
|
||||
差旅发票本来就 skip,所以一直没暴露。
|
||||
|
||||
从历史导出模板做了 PII 扫描:内容是空白校用表单(「阜阳师范大学科研类低值易耗品入、出库单」),
|
||||
无姓名、无卡号、无任何 9 位以上数字串。用户选择直接删除该功能,于是删掉 `consumable.py`、
|
||||
`doc_consumable` 工具、`consumable_storage` 配置项,文档里的出库单说法全部改成申购单。
|
||||
|
||||
教训(适用于本项目所有产物代码):**失败必须冒出来**。返回 `doc_ok=false` 而不报错,等于把 7 周的
|
||||
产物缺失藏进日志。MCP 工具统一用 `ok:false + reason + next_action` 就是这个原因。
|
||||
|
||||
### 11.5 遗留债务(按优先级)
|
||||
|
||||
1. **P3 浏览器分步工具**:`BrowserActor` 单线程 + `.browser.lock`(PID+心跳,防多代进程抢)+
|
||||
`browser_open/login/new_form/fill(step)/page_state/close`,截图进 `cases/<id>/screenshots/`,扫码登录返回 `need_human`
|
||||
2. **P4 提交护栏**:`submit_preview`(快照+摘要+token)与 `submit_execute`(一次性、摘要变更后失效),默认有头
|
||||
3. **申购单模板选择**:现在是「项目根目录 `低值易耗品申购*.xls` 里名字最大者」,应该改成固定模板路径
|
||||
(`templates/` 或配置项),避免将来根目录出现产物就选错
|
||||
4. **文本层行重排**:用 `get_text("words")` 的坐标把标签和值配对回去,并在与读图冲突时输出 warning,
|
||||
而不是让宿主自己猜哪一路对(§10.3 的实测证据)
|
||||
5. `case_delete` / `case_rename`:宿主需要清理 scratch 会话;反斜杠路径建议在服务端归一化而不是只写文档
|
||||
6. `case_status` 应标出「同时存在 travel_info 与 normal_info」的会话(当前两个都存着不报错)
|
||||
7. `cases/` 下的 scratch 会话(`_p0-gate`、`20261019-P0闸门自测` 等)是 gitignore 的本地产物,可随手删
|
||||
|
||||
### 11.6 文档同步(P1 附带完成)
|
||||
|
||||
- 新增 `docs/MCP.md`(工具参考由运行时 schema 生成,与代码同源);删除 `docs/API.md`(Flask/SSE 文档)
|
||||
- 重写根 `README.md`(为什么改成 MCP、快速开始、注册配置、流程图、结构与风险)与 `AGENTS.md`
|
||||
(含「提交纪律」与多进程共存、COM/Playwright 线程约束)
|
||||
- 各包 `README.md` 全部按现状重写;新增 `src/mcp/README.md`、`src/upload/README.md`
|
||||
- 提示词 `src/infra/llm/prompts/*.md` → `.agents/skills/reimbursement/references/*.md`,
|
||||
删除其中 `can_submit`/`suggestion`(`save_info` 也会主动剥离),「只输出 JSON」改成「交给对应工具落盘」;
|
||||
新增 `.agents/skills/reimbursement/SKILL.md`(宿主侧操作纪律,已被 DSH 热加载为 `reimbursement` 技能)
|
||||
- 删除作废的 `.agents/docs/guides/系统事件流全景图.md`(事件流通道已整体移除),给
|
||||
`项目架构全景图.md` 与 `架构分析-2026-06-15.md` 加「已过时」横幅保留历史
|
||||
- `config.example.json` 去掉 `consumable_storage` 加 `upload_pin`;`.env.example` 去掉 LLM 段并说明
|
||||
**已不再自动加载 .env**(python-dotenv 移除),凭据只走 `scripts/data/config.json`
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
|
||||
> **已过时(2026-10-19 MCP 化改造)**:本文描述的是改造前的架构,保留作为历史与决策背景。
|
||||
> 现状请看 `docs/MCP.md`(工具契约)、`AGENTS.md`(项目操作指南)与
|
||||
> `.agents/docs/plans/MCP化改造方案-2026-10-19.md`(决策与实测记录)。
|
||||
> 该分析提出的「灵活性差」问题正是 MCP 化改造的动因,方案与落地结果见
|
||||
> `MCP化改造方案-2026-10-19.md`(同目录)。
|
||||
|
||||
---
|
||||
|
||||
# 项目架构分析与重构建议
|
||||
|
||||
## 一、当前架构总览
|
||||
@@ -222,4 +231,4 @@ agent/
|
||||
|
||||
---
|
||||
|
||||
*生成时间: 2026-06-15*
|
||||
*生成时间: 2026-06-15*
|
||||
Reference in New Issue
Block a user