- add scripts/annotate_roster_invoices.py: match passenger names to invoice PDFs, write status + relative encoded hyperlinks into roster xlsx - add scripts/add_invoice_passenger_name.py, scripts/calibrate.py - add .opencode/skills/invoice-wrapup skill - improve invoice_tool.py / web_ui.py / vision_fallback.py automation - add device/screen/windows adaptation docs - ignore out/ (delivered invoice PDFs contain sensitive data)
131 lines
9.5 KiB
Markdown
131 lines
9.5 KiB
Markdown
---
|
||
trio: standard-v2
|
||
trio-initialized: 2026-06-08
|
||
---
|
||
|
||
# 铁路系统发票 · Agent 操作守则
|
||
|
||
> **上来先读这份**,再看 [`INDEX.md`](./INDEX.md) 找模块和导航。
|
||
>
|
||
> **通用三件套协议**见 [`../docs/trio-protocol.md`](../docs/trio-protocol.md)(文档维护节奏 / Handoff 写入 / 子项目嵌套 / 记忆三条线边界 / 语言规则 / 跨项目反例)。**本文件只列本项目专属守则**。
|
||
>
|
||
> **`trio: standard-v2`** = 本项目按当前标准维护三件套。
|
||
|
||
## 这是什么项目
|
||
|
||
铁路系统发票专项工作区,用于沉淀铁路系统相关发票资料、处理脚本、核对记录和衍生产出。
|
||
|
||
### 核心愿景(Core Vision)
|
||
|
||
**最终目标**:用户在 Web UI 上操作,脚本全自动完成开票、状态记录、结果文档输出。
|
||
|
||
**技术路线**:
|
||
- **截屏驱动**:ADB 截屏 → 视觉 LLM(Qwen3-VL via SiliconFlow)识别页面元素、脱敏姓名、按钮位置
|
||
- **脚本编排**:视觉 LLM 返回归一化 bbox(0-1000 坐标系)→ 脚本判断当前页面状态 → 决定下一步动作(tap / scroll / back)
|
||
- **UI 配合**:Web UI 负责触发、监控进度、展示结果;脚本在子进程中运行,通过 CSV 文件与 UI 交换状态
|
||
- **对话调试**:James 和 Claude Code 通过实际运行观察手机行为,持续修正脚本的判断逻辑和边界条件
|
||
|
||
**当前阶段**:H5 扫码开票单页面自动化。页面结构:单个开票单下挂 N 个乘客(脱敏姓名 + 身份证类型 + 脱敏身份证号 + 「开具」按钮),需要逐人点击「开具」走完子流程,记录每个乘客的开票结果。
|
||
|
||
**已完成的路径**(保留不动):
|
||
1. 原生列表页视觉驱动批量(`android-vision-page-run`):截图 → list-rows 识行 → anchor 去重 → 点击 → 核验/发票管理子流程
|
||
2. 原生列表页 XML 驱动(`android-page-run`):uiautomator dump → 解析 XML → 匹配 text/bounds
|
||
|
||
## 上手三步
|
||
|
||
1. 读 [`INDEX.md`](./INDEX.md),看项目结构和子模块导航。
|
||
2. 找到目标模块目录,**先读它的本地文档**(如 `<module>/README.md` 或 `<module>/AGENTS.md` 若存在)。
|
||
3. 看根目录或模块里有没有脚本入口、配置文件、环境变量样例。
|
||
|
||
## 项目专属硬规则
|
||
|
||
- **不要随手改 `.env` / 凭证 / `settings.json`**:敏感配置由项目所有者维护。
|
||
- **不要主动删除文件**:废弃 / 旧版本 / 半成品请移动到 `archive/` 或 `不加载/` 这类约定目录,不要 `rm`。
|
||
- **不要重命名公共接口、路由、对外 API 字段**:除非明确授权,这些是契约。
|
||
- **涉及发票、金额、税号、单位名称等字段时先保留原始值**:规范化和清洗产物另存,不覆盖来源资料。
|
||
- **改动前确认是否有依赖你正在改的代码的其他模块**:先 `grep` 引用再下手。
|
||
|
||
## 目录命名约定
|
||
|
||
| 子目录 | 用途 |
|
||
|---|---|
|
||
| `data/` | 本项目自有数据与导入材料 |
|
||
| `scripts/` | 可执行脚本 |
|
||
| `src/` 或 `lib/` | 主代码 |
|
||
| `tests/` | 测试 |
|
||
| `docs/` | 详细文档 |
|
||
| `assets/` | 静态素材 |
|
||
| `templates/` | 模板文件 |
|
||
| `archive/` 或 `不加载/` | 归档区,不参与构建 |
|
||
|
||
## 项目专属"不要做的事"
|
||
|
||
- ❌ 删除文件(应该 `mv` 到归档目录)
|
||
- ❌ 自动提交 secrets / 凭证
|
||
- ❌ 覆盖原始发票资料或人工核对记录
|
||
- ❌ 未经确认修改发票字段含义、金额口径、税率口径
|
||
|
||
## 架构与关键依赖
|
||
|
||
> 本节是给 Claude Code 看的"big picture"。具体命令清单在 `INDEX.md` 的"常用操作"段;详细用法在 `docs/usage.md`。这里只写**读多个文件才能拼出来的事**。
|
||
|
||
### 脚本分工
|
||
|
||
`scripts/` 下没有 `src/lib`,所有逻辑都在独立 Python 文件里,彼此通过文件(CSV / 配置 / 截图)耦合,而不是 import(图谱里 IMPORTS 边仅 1 条,可印证):
|
||
|
||
| 脚本 | 角色 | 入口形式 |
|
||
|---|---|---|
|
||
| `invoice_tool.py` | 主 CLI,~3200 行,argparse 多子命令。覆盖:台账 / 名单 / 邮件归集 / 网页辅助页 / Android ADB 自动化(3 条路径)/ 视觉驱动批量。所有路径常量、`run_adb`、`Config`、CSV header 都在这里 | `python3 scripts/invoice_tool.py <subcmd>` |
|
||
| `web_ui.py` | 本地 HTTP 服务(端口 8765),`import invoice_tool` 复用其常量和 `run_adb`,把批处理包成子进程跑;日常启动优先用稳定重启脚本 | `./scripts/restart_web_ui.sh`(推荐)/ `python3 scripts/web_ui.py`(前台调试) |
|
||
| `vision_fallback.py` | 视觉调用层:adb 截屏 -> base64 -> 调 SiliconFlow Qwen3-VL grounding API -> 解析归一化 bbox | `python3 scripts/vision_fallback.py {ping,detect-page,ask,list-rows}` |
|
||
| `record_step.py` | 录制工具:`adb uiautomator dump` + `screencap` 落盘到 `work/replay/<session>/<NN_label>/`,用于"人工演示 -> 反推脚本" | `python3 scripts/record_step.py "label" [--new] [--list]` |
|
||
| `add_invoice_passenger_name.py` | 从发票 PDF 抽取乘客姓名并补进文件名(`pypdf` 硬依赖)。处理 `out/` 下已交付 PDF | `python3 scripts/add_invoice_passenger_name.py <dir>` |
|
||
|
||
**Web UI 的运行模式**(`start_runner` in `web_ui.py:294`):前端 `<select id="runMode">` 选模式,POST `/api/start` 带 `mode` 字段:
|
||
- `h5`(默认)→ 子命令 `android-h5-page-run`(H5 扫码开票单,当前主线)
|
||
- `native` → 子命令 `android-vision-page-run`(原生列表页视觉)
|
||
|
||
**Web UI 永远不会走旧的 `android-page-run`(XML 路径)**。改"开始运行"按钮行为时,改的是 mode 映射,不是子命令名硬编码。
|
||
|
||
### 三条 Android 自动化路径并存
|
||
|
||
12306 App 是脱敏姓名(`张*岩`)+ 输入身份证后 8 位的开票流程。代码里有三套实现,**都保留**:
|
||
|
||
1. **旧:`android-page-run` / `android-page-step`** — 走 `uiautomator dump` 读 XML,按 text / bounds 匹配。问题是 12306 列表页很多控件没有 text,全靠坐标。**Web UI 已不走此路径**,仅 CLI 可达。
|
||
2. **原生列表视觉:`android-vision-page-run`** — 截屏交给 Qwen3-VL,模型返回归一化 bbox(**0-1000 坐标系**,不是像素),脚本再 denormalize 到屏幕像素后 tap。流程:截图 → `vision_fallback.list-rows` 识所有可见行 → anchor 去重防滑动后重复 tap → 子流程(核验 / 发票管理 / 成功页)走旧的状态机。
|
||
3. **当前主线:`android-h5-page-run`** - H5 扫码开票单页面。与原生列表的区别(见 `android_h5_page_run` docstring at `invoice_tool.py:1939`):单页内滚动(不是翻页,scroll 用 `--h5-scroll-*`);back 后 scroll 重置到顶部,每轮重新截屏;不用 anchor 去重(back 重置后无重叠行)。precheck 不在列表页时会尝试 `auto_scan_qr_to_list` 自动扫码进入。
|
||
|
||
### 配置与外部依赖
|
||
|
||
- **ADB 路径硬编码**:`invoice_tool.py:43` 写死 `/opt/homebrew/share/android-commandlinetools/platform-tools/adb`(Apple Silicon Homebrew)。换机器或换 Intel Mac 要改这里。
|
||
- **`config.local.json`**:除 `company_title / tax_id / receiver_email`(`config.example.json` 已示范)外,**视觉路径还需要一个 `vision` 段**:`provider` / `base_url` / `api_key` / `model`(当前用 SiliconFlow + `Qwen/Qwen3-VL-8B-Instruct`,详见 memory `reference_siliconflow_qwen_vl.md`)。`vision_fallback.load_vision_config` 缺任一键直接抛错。
|
||
- **Python 依赖(无 `requirements.txt`)**:除标准库外依赖两个三方包,需手动装:`openpyxl`(`import-roster` 读 xlsx 用,`invoice_tool.py` 顶部 try/except 守护,缺了只是 xlsx 子命令不可用);`pypdf`(`add_invoice_passenger_name.py` 硬 import,缺了该脚本直接起不来)。
|
||
- `config.local.json`、`work/`、`data/input|mail|attachments/`、`output/` 全在 `.gitignore` 里。
|
||
|
||
### 关键工作文件(都在 `work/`,CSV,UTF-8-BOM)
|
||
|
||
| 文件 | 内容 | 写入者 |
|
||
|---|---|---|
|
||
| `passenger_roster.csv` | 名单:姓名 / 身份证号 / 后 8 位 / 来源 | `import-roster` |
|
||
| `invoice_tasks.csv` | 开票码主台账 | `import-codes` |
|
||
| `android_invoice_progress.csv` | 逐人处理结果(masked_name / real_name / id_last8 / status) | Android 子命令 + Web UI 读 |
|
||
| `android_page_queue.csv` | 当前可见页的待点击队列 | `android-build-page-queue` |
|
||
| `qr_page_order.csv` | 二维码扫描顺序 | `import-codes` / H5 流程 |
|
||
| `last_qr_url.txt` / `last_qr_image.png` | 上次扫码的 URL 与二维码图 | `android-h5-*` precheck |
|
||
| `agent_id_last8.txt` | 代理人证件后 8 位(核验/扫码备用) | Web UI / CLI |
|
||
| `replay/<session>/` | record_step.py 的演示快照(ui.xml + screen.png + texts.txt + meta.json) | `record_step.py` |
|
||
|
||
CSV header 常量都集中在 `invoice_tool.py` 顶部(`TASK_HEADERS` / `ROSTER_HEADERS` / `PROGRESS_HEADERS` / `PAGE_QUEUE_HEADERS`)。改字段要同步改这里 + 所有读它的子命令。
|
||
|
||
### 测试
|
||
|
||
`tests/` 当前是空目录。没有单测 / 集成测试框架;验证靠 `--dry-run` 子命令 + 真机跑 + `work/replay/` 录制比对。新增功能不强求加测试,但如果改了 CSV schema 或 ADB 命令组合,至少跑一次 `--dry-run --max-actions 1`。
|
||
|
||
<!-- codebase-memory-codemap -->
|
||
## 代码图谱查询(codebase-memory)
|
||
|
||
本项目已由 codebase-memory 索引,结构画像见根目录 [`CODEMAP.md`](./CODEMAP.md)。
|
||
优先用图谱查询代替逐文件 grep(省 ~120x token):连 MCP 的 agent 直接调工具;
|
||
未连走 CLI `~/.local/bin/codebase-memory-mcp cli <tool> '<json>'`。
|
||
常用工具:`get_architecture` / `search_graph` / `trace_path` / `query_graph`。
|