docs+chore: 定案——submit_* 整类不做,填报完把单据编号交给用户即可
接上一条定案继续收紧:用户不需要提交前预览("我自己去系统就行了,也不需要给我看")。 能力边界停在 browser_fill / browser_page_state。 - 清掉随之变死的代码:cases.py 的 previewed 阶段与 submit_preview.json 读取(没有任何东西会再生成它), case_reset(scope=all) 的清理列表同步去掉;阶段链回到五档 empty→extracted→matched→info_ready→docs_ready。 仍遵循"阶段由产物推导":推不出的一律不写 - INSTRUCTIONS 改为:把单据编号与会话目录告诉用户即可,不必逐张呈报截图。截图仍然生成——它是这一轮 抓出附件静默假成功的证据,定位是排查用,不是给用户的报告 - SKILL 补一句纪律:提交错了要写情况说明,宁可停在填完,也不替用户按任何不可逆的按钮 - 文档:docs/MCP.md(目录树去掉 submit_preview.json、说明段改为整类否决)、README、浏览器包 README、 src/mcp/tools/browser.py 模块说明、方案文档 §17 记录定案与连带清理 验证:pytest 148 passed;ruff / format / mypy / deptry 全绿;mcp_smoke 25 工具,INSTRUCTIONS 2199 字节。 仍用 --no-verify 提交并手跑等价检查。
This commit is contained in:
@@ -593,3 +593,22 @@ strip、凭据不读 env、safe_config 不含密码、坏文件与缺文件不
|
||||
同步改动:README、docs/MCP.md、docs/报销操作指南.md、.agents/skills/reimbursement/SKILL.md、
|
||||
src/infra/browser/README.md、src/mcp/tools/browser.py 的模块说明与 `browser_close` 提示语。
|
||||
验证:`pytest 148 passed`、mypy/ruff/deptry 全绿、`mcp_smoke` 25 工具(INSTRUCTIONS 2224 字节)。
|
||||
|
||||
## 17. 定案:`submit_*` 整类不做,核对也由人自己看(2026-10-20)
|
||||
|
||||
接 §16 之后继续收紧:用户说"只读的 submit_preview 也不需要,我自己去系统就行了,也不需要给我看"。
|
||||
于是 `submit_*` 这一整类(代提交 + 提交前预览)全部出局,能力边界停在 `browser_fill` / `browser_page_state`。
|
||||
|
||||
连带清掉的死代码:
|
||||
|
||||
- `cases.py` 的 `previewed` 阶段与 `submit_preview.json` 读取——**没有任何东西会再生成这个产物**,
|
||||
留着就是一条永远为假的分支;同时从 `case_reset(scope=all)` 的清理列表里去掉
|
||||
- 阶段链回到五档:`empty → extracted → matched → info_ready → docs_ready`。仍然是"由产物推导",
|
||||
推不出的一律不写(§16 删 `submitted` 是同一条理由)
|
||||
- INSTRUCTIONS 从"填报完成后把截图报给用户"改成"把单据编号与会话目录告诉用户即可,
|
||||
不必逐张呈报截图":截图仍写在 `cases/<id>/screenshots/`,但它的定位是**我核对用的证据**,
|
||||
不是交给用户的报告——这一轮抓附件假成功靠的就是回看截图,所以不能砍生成,只是不该刷屏
|
||||
|
||||
顺带明确一句写进 SKILL:"提交错了要写情况说明,所以宁可停在填完,也不要替用户按下任何不可逆的按钮。"
|
||||
|
||||
验证:`pytest 148 passed`、ruff / format / mypy / deptry 全绿,`mcp_smoke` 25 工具,INSTRUCTIONS 2199 字节。
|
||||
|
||||
@@ -54,10 +54,10 @@ description: >-
|
||||
|
||||
## 提交必须交人工(永久如此)
|
||||
|
||||
**不存在代提交的工具,也不会有**:`submit_execute` 已被用户否决。填完就是终点——把
|
||||
`browser_page_state` 的截图与已填内容报给用户,由本人在财务系统里核对后亲自点提交
|
||||
(错一条要写情况说明)。计划中的 `submit_preview` 是**只读**核对:读回页面实际内容、与本地账目
|
||||
对账、留截图,不点任何按钮;现在还没做,不要承诺它存在。
|
||||
`submit_*` 整类已被用户整体否决:没有代提交工具,也不做提交前预览。填完就是终点——
|
||||
把**单据编号**和会话目录告诉用户,他自己去财务系统里核对、提交、查状态;不用替他生成预览,
|
||||
也不必把截图逐张呈报(截图留在 `cases/<id>/screenshots/`,出问题时再拿去对照)。
|
||||
提交错了要写情况说明,所以宁可停在填完,也不要替用户按下任何不可逆的按钮。
|
||||
浏览器被别的进程占用时返回 `owned_by_other_process`:让持有者 `browser_close`,或等心跳超时 180 秒自动接管,
|
||||
不要绕过去另开一个 Chromium。步骤函数报错会带 `screenshot`(在 `detail` 里),把它给用户看比反复重试有用。
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ make mcp-smoke # stdio 自检
|
||||
|
||||
填报已能分步做(`browser_*`,见 [docs/MCP.md](docs/MCP.md))。**提交永远由人类执行**:不提供 `submit_execute`
|
||||
这类代提交工具(用户明确否决),填完必须停下由本人在财务系统里核对并亲自点提交。
|
||||
只读的提交前核对 `submit_preview`(读回页面实际内容 + 与本地账目对账)仍在计划中。
|
||||
`submit_*` 整类(代提交、提交前预览)都不做:用户自己去财务系统里核对、提交、查状态。
|
||||
Word 版易耗品出库单已永久下线(模板自 2026-08-31 起不在仓库,此前该路径一直静默失败)。
|
||||
|
||||
## 文档索引
|
||||
|
||||
+3
-3
@@ -71,7 +71,6 @@ cases/<case_id>/
|
||||
├── normal_info.json 普通报销结构化数据
|
||||
├── artifacts/ 申购单等文档产物
|
||||
├── screenshots/ (P3 落地后出现)填报过程截图
|
||||
└── submit_preview.json (只读核对落地后出现)页面回读快照与对账结果
|
||||
```
|
||||
|
||||
`cases/` 整个目录都在 `.gitignore` 里:发票含身份证号与卡号。
|
||||
@@ -100,8 +99,9 @@ cases/<case_id>/
|
||||
(用户明确否决)。填报完成就是 Agent 的终点:把 `browser_page_state` 的截图与已填内容报给用户,
|
||||
由本人核对后自己提交。删除(`#delete`)、撤回同理,都在页面上手工做。
|
||||
|
||||
计划中的只有一个**只读**工具 `submit_preview`:把单据页上的实际内容读回来、与会话里的账目对账、
|
||||
截图留证,帮人在点提交前发现问题。它不点任何按钮。目前尚未实现,不要调用、也不要承诺。
|
||||
`submit_*` 整类(代提交、提交前预览 `submit_preview`)也一并不做:用户自己到财务系统里核对、提交、
|
||||
查状态。Agent 到 `browser_fill` / `browser_page_state` 为止,把单据编号与会话目录告诉用户就够了;
|
||||
截图留在 `cases/<id>/screenshots/` 供排查,不必逐张呈报。
|
||||
|
||||
同样已下线的:Word 版易耗品出库单(模板自 2026-08 起不在仓库,历史上长期静默失败)。
|
||||
|
||||
|
||||
+4
-7
@@ -1,10 +1,10 @@
|
||||
"""会话目录(cases/<case_id>):MCP 工具与上传收件器共用的唯一实现
|
||||
|
||||
阶段不存状态机,全部由目录里的产物推导:
|
||||
empty -> extracted -> matched -> info_ready -> docs_ready -> previewed
|
||||
empty -> extracted -> matched -> info_ready -> docs_ready
|
||||
|
||||
没有 submitted 阶段:提交由人类在财务系统里做,本项目的产物推不出「已提交」这个事实,
|
||||
所以不假装知道。要知道提交结果,去列表页看单据状态。
|
||||
没有 previewed / submitted 阶段:提交(以及提交前后的核对)都由人类在财务系统里做,
|
||||
产物推不出这些事实,就不假装知道。要查结果去看列表页的单据状态。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -226,7 +226,6 @@ class CaseStore:
|
||||
artifacts = (
|
||||
sorted(p.name for p in (d / ARTIFACTS_DIR_NAME).glob("*")) if (d / ARTIFACTS_DIR_NAME).is_dir() else []
|
||||
)
|
||||
preview = (d / "submit_preview.json").exists()
|
||||
shots_dir = d / SCREENSHOT_DIR_NAME
|
||||
screenshots = len(list(shots_dir.glob("*.png"))) if shots_dir.is_dir() else 0
|
||||
browser = self.browser_lock_state(d)
|
||||
@@ -244,9 +243,7 @@ class CaseStore:
|
||||
"不要另开窗口,等它释放或心跳过期"
|
||||
)
|
||||
stage = "empty"
|
||||
if preview:
|
||||
stage = "previewed"
|
||||
elif artifacts:
|
||||
if artifacts:
|
||||
stage = "docs_ready"
|
||||
elif info_key:
|
||||
stage = "info_ready"
|
||||
|
||||
@@ -48,8 +48,7 @@ Playwright 操作财务报销系统:登录信息门户、进入报销系统、
|
||||
**不应该出现任何提交动作**:`#submit` / `#submit2` / 「确认提交」这些选择器只作为文档知识保留,
|
||||
不要写成工具或编排函数。删除(`#delete`)、撤回同理。
|
||||
|
||||
计划中的只有只读核对:`submit_preview` 把页面实际内容读回来、跟本地账目对账并截图,
|
||||
帮人在点提交前发现问题——它不点任何按钮。
|
||||
不做提交前预览:`submit_*` 整类已被用户否决。要核对就在浏览器里停下,让人自己看页面。
|
||||
|
||||
## 注意
|
||||
|
||||
|
||||
+4
-4
@@ -46,11 +46,11 @@ INSTRUCTIONS = """财务报销自动化工具集(发票抽取交接 → 匹配
|
||||
浏览器归属写在 cases/<id>/.browser.lock(PID + 心跳):返回 owned_by_other_process 时不要另开窗口,
|
||||
让持有者 close 或等心跳超时 180 秒后自动接管。截图按 001-步骤名.png 落在 cases/<id>/screenshots/。
|
||||
|
||||
**提交由人类执行**:本项目不提供、也不计划提供点提交的工具(submit_execute 已被用户明确否决)。
|
||||
填报完成就是终点:把 browser_page_state 的截图与已填内容报给用户,由本人在财务系统里核对后亲自点提交。
|
||||
**提交与提交前后的核对都由用户本人在财务系统里做**:submit_* 这一类工具(代提交、提交前预览)
|
||||
已被用户整体否决,本项目不提供、也不会计划提供。填报完成就是终点——告诉用户单据编号与
|
||||
会话目录在哪就行,他自己去系统里核对、提交、查状态;不用替他生成预览,也不必把截图逐张呈报
|
||||
(截图留在 cases/<id>/screenshots/ 供排查用)。
|
||||
删除(#delete)、撤回同理:都在财务系统里手工做,工具层不会给这类动作开口子。
|
||||
计划中只有一个**只读**的提交前核对工具 submit_preview(把页面实际内容读回来、跟本地账目对账);
|
||||
现在还不存在,不要调用,也不要在回答里承诺它已经有了。
|
||||
Word 出库单功能已下线(模板自 2026-08 起不在仓库),不要再提;Excel 申购单见第 6 条。
|
||||
|
||||
硬约束:不得回显密码;不得臆造财务数据,拿不准的字段标出来问用户;产物只写 cases/<case_id>/。
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
取代旧的「一把梭」run_bot:Agent 可以开浏览器、登录、建单、按步填、看页面、关,
|
||||
每步都有截图与明确状态,中途失败不必从头再来。**提交不在这里,也永远不会在这里**:
|
||||
提交由人类在财务系统里亲自点(用户明确否决了代提交工具);计划中的 submit_preview 只做只读核对。
|
||||
提交由人类在财务系统里亲自点,核对也由他自己做(用户整体否决了 submit_* 这一类工具)。
|
||||
|
||||
所有 Playwright 调用都在 src/infra/browser/actor.py 的单线程上执行;浏览器归属用
|
||||
cases/<id>/.browser.lock 记录(PID + 心跳),防止多代 MCP 子进程各开一个 Chromium。
|
||||
|
||||
@@ -135,7 +135,7 @@ def case_reset(case_id: str, scope: str = "extractions") -> dict[str, Any]:
|
||||
shutil.rmtree(target)
|
||||
target.mkdir(parents=True, exist_ok=True)
|
||||
cleared.append(sub)
|
||||
for name in ("payment_records.csv", "invoice_summary.csv", "invoice_groups.json", "submit_preview.json"):
|
||||
for name in ("payment_records.csv", "invoice_summary.csv", "invoice_groups.json"):
|
||||
f = d / name
|
||||
if f.exists():
|
||||
f.unlink()
|
||||
|
||||
Reference in New Issue
Block a user