commit 2648440f22a5528d238736ca26a3ae65825dc322 Author: xinxin6623 Date: Thu Jun 11 12:18:53 2026 +0800 init autotrain project diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..51d7701 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +__pycache__/ +*.pyc +.DS_Store +config.local.json +archive/ +data/input/ +data/mail/ +data/attachments/ +output/ +work/ diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..f3b33d2 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,112 @@ +--- +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. 找到目标模块目录,**先读它的本地文档**(如 `/README.md` 或 `/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: + +| 脚本 | 角色 | 入口形式 | +|---|---|---| +| `invoice_tool.py` | 主 CLI,~1700 行,argparse 多子命令。覆盖:台账 / 名单 / 邮件归集 / 网页辅助页 / Android ADB 自动化 / 视觉驱动批量。所有路径常量、`run_adb`、`Config` 都在这里 | `python3 scripts/invoice_tool.py ` | +| `web_ui.py` | 本地 HTTP 服务(端口 8765),`import invoice_tool` 复用其常量和 `run_adb`,把批处理包成子进程跑 | `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///`,用于"人工演示 → 反推脚本" | `python3 scripts/record_step.py "label" [--new] [--list]` | + +`web_ui.py` 当前调用的是旧路径 `android-page-run`(XML/文字匹配),**没有接** `android-vision-page-run`。如果改 Web UI 的"开始运行"按钮,注意切换的是 `invoice_tool.py` 的子命令名。 + +### 两条 Android 自动化路径并存 + +12306 App 是脱敏姓名(`张*岩`)+ 输入身份证后 8 位的开票流程。代码里有两套实现,**都保留**: + +1. **旧:`android-page-run` / `android-page-step`** — 走 `uiautomator dump` 读 XML,按 text / bounds 匹配。问题是 12306 列表页很多控件没有 text,全靠坐标。 +2. **新:`android-vision-page-run`** — 截屏交给 Qwen3-VL,模型返回归一化 bbox(**0-1000 坐标系**,不是像素),脚本再 denormalize 到屏幕像素后 tap。流程:截图 → `vision_fallback.list-rows` 识所有可见行 → anchor 去重防滑动后重复 tap → 子流程(核验 / 发票管理 / 成功页)走旧的状态机。 + +### 配置与外部依赖 + +- **ADB 路径硬编码**:`invoice_tool.py:39` 写死 `/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` 段**:`base_url` / `api_key` / `model`(当前用 SiliconFlow + `Qwen/Qwen3-VL-8B-Instruct`,详见 memory `reference_siliconflow_qwen_vl.md`)。`vision_fallback.load_vision_config` 缺任一键直接抛错。 +- `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` | +| `replay//` | 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`。 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..40cf009 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,173 @@ +## 2026-06-11 #fix scope:android-h5-automation - 下载后自动退出发票预览页 + +- Why: 下载确认后会停在预览页,需要稳定回到成功页/列表继续下一人 +- 详见: scripts/invoice_tool.py:1004, scripts/invoice_tool.py:1892 + +## 2026-06-11 #fix scope:android-h5-automation - 下载发票文件名带真实姓名 + +- Why: 仅用脱敏名无法区分和归档,本地 PDF 需要直接体现发票归属人 +- 详见: scripts/invoice_tool.py:1919 + +## 2026-06-11 #fix scope:android-h5-automation - 处理发票下载完成确认弹窗 + +- Why: PDF 已保存后卡在下载进度 100% 弹窗,导致无法自动回到列表 +- 详见: scripts/invoice_tool.py:1004, scripts/invoice_tool.py:1886 + +## 2026-06-11 #fix scope:web-ui - 保存目录输入不再被自动刷新覆盖 + +- Why: UI 状态轮询会重置正在编辑的目录,导致用户无法自定义保存位置 +- 详见: scripts/web_ui.py:481, scripts/web_ui.py:696 + +## 2026-06-11 #feat scope:android-h5-automation scope:web-ui - 成功页支持下载发票到本地 + +- Why: 后续需要保留邮箱流程,同时默认把发票文件直接沉淀到电脑目录 +- 详见: scripts/invoice_tool.py:959, scripts/web_ui.py:212 + +## 2026-06-11 #fix #perf scope:android-h5-automation scope:web-ui - 修复核验同名歧义、优化续跑锚点、优化图库选码 + +- Why: UI 跑流程经常报错,需先验证 CLI、再固化续跑断点、再优化重试顺序 +- 详见: scripts/invoice_tool.py:402, scripts/invoice_tool.py:1485, scripts/web_ui.py:899 + +# 铁路系统发票 · CHANGELOG + +> 每次动了什么记一条。详细记录写在各自模块目录下,根目录 CHANGELOG 是**强标签化的检索索引**。 +> +> **如本项目下有子项目**(子目录里也有 AGENTS/INDEX/CHANGELOG 三件套):本 CHANGELOG **只记录跨多个子项目的同时操作**;单一子项目操作记在该子项目自己的 CHANGELOG 里。详见 `../docs/trio-protocol.md`。 + +## 格式规范(严格) + +```text +## YYYY-MM-DD # scope: [#...] - <一句话主题> + +- Why: <一句话动机,不复述 what> +- 详见: +``` + +**硬约束**: +- 日期必须 ISO 格式 `YYYY-MM-DD` +- 类型标签必须以 `#` 开头,从下面字典选一个为主标签 +- 作用域必须 `scope:` 形式,name 用 kebab-case;多模块改动用多个 `scope:` +- Why 一行不超过 80 字符 +- **不贴 diff、不复述 what**,那些进 commit 或模块自己的文档 + +## 类型标签字典 + +| 标签 | 含义 | +|---|---| +| `#feat` | 新功能 | +| `#fix` | bug 修复 | +| `#refactor` | 重构(无行为变化) | +| `#perf` | 性能优化 | +| `#docs` | 文档变更 | +| `#test` | 测试相关 | +| `#chore` | 构建/依赖/工具链/初始化 | +| `#archive` | 归档/弃用 | +| `#breaking` | 破坏性变更(叠加) | +| `#deprecated` | 标记弃用(叠加) | +| `#wip` | 进行中(叠加) | + +## 检索示例 + +```bash +grep -E "^## .* #feat .* scope:invoice" CHANGELOG.md +grep "#breaking" CHANGELOG.md +grep "^## 2026-06" CHANGELOG.md +``` + +--- + +## 2026-06-09 #feat scope:android-automation #scope:vision-fallback - 视觉驱动批量开票路径打通 + +- Why: 用户改路线,手动扫码进列表 → 视觉抓行 → anchor 去重 → 按页批次开票 +- 详见: scripts/invoice_tool.py 新增 `android-vision-page-run` 命令(并行存在,不动老路径);scripts/vision_fallback.py 新增 `list-rows` 子命令 + 0-1000 归一化反推 +- docs/debug-trace-2026-06-09.md 全程实测时间线 + +## 2026-06-09 #chore scope:vision-fallback - 换用硅基流动 Qwen3-VL-8B-Instruct + +- Why: 豆包 Doubao-Seed-2.0-mini OCR 准但 grounding 失败(返坐标完全猜的);Qwen3-VL 系列做过 grounding 训练,实测 bbox 误差 <25px +- 详见: config.local.json provider 改 siliconflow,model Qwen/Qwen3-VL-8B-Instruct;费用估算 21-25 次/全程 ≈ ¥0.05-0.1 + +## 2026-06-09 #fix scope:android-automation - 兜底 back 退过头 + 2 字脱敏名正则失配 + +- Why: no_match 名 tap 后核验弹窗解析失败,旧 back+back 兜底把列表页一起退到 12306 首页 +- 详见: invoice_tool.py 核验弹窗正则 `{1,3}`→`{0,3}` 兼容澳*/吴* 类两字名;safe_back_until_list 加首页检测 + 单次 back 后 1.5s 等待,外层 abort 闸门避免误操作 + +## 2026-06-09 #feat scope:replay - 录制工具 record_step.py + +- Why: 视觉模型坐标精度够但子流程边界(2 字名 / 已开过 / 已退 H5)边界 case 多,人工示范+录制比盲调更快 +- 详见: scripts/record_step.py — `python3 scripts/record_step.py "label"` 当前 session 累加;`--new` 开新 session + +## 2026-06-09 #feat scope:vision-fallback - 接入火山豆包视觉模型作为 adb dump 兜底 + +- Why: adb 节点解析对 H5 内嵌 UI / 弹窗状态不稳,加视觉 LLM 二次确认 +- 详见: scripts/vision_fallback.py (detect-page / ask / ping),config.local.json vision 段;模型 doubao-seed-1-6-flash-250615 + +## 2026-06-09 #feat scope:web-ui - 开始运行流程改成"先回顶 + 截屏建队列 + 才操作" + +- Why: 原流程开始即翻页,用户没看到本页识别结果就直接 swipe,体验跟"卡死"无异 +- 详见: scripts/web_ui.py start_runner 前置同步两步骤;新 UI 复选框"开始前回顶"默认勾选 + +## 2026-06-09 #feat scope:android-automation - 翻页到底 fingerprint 检测 + 回顶命令 + +- Why: swipe 后页面没变化以前会无限滑;新加 android-page-top 让人能从底部回到列表顶端开干 +- 详见: scripts/invoice_tool.py android_page_top / android_page_step _last_done_fp 状态比对 + +## 2026-06-09 #fix scope:android-automation - 运行时异常软失败,不整体崩 runner + +- Why: 单个人开票流程出错以前会让整批停掉,无人值守语义不成立 +- 详见: scripts/invoice_tool.py mark_clicked_as_runtime_error / android_page_run try-except + +## 2026-06-09 #feat scope:android-automation - 本页 retry 一次 + 身份证首末位增强匹配 + +- Why: 用户要求整页跑完后失败者再走一遍才翻页;同名歧义用脱敏身份证首末位解掉 +- 详见: scripts/invoice_tool.py build_visible_page_queue / match_roster / RETRYABLE_FAILURES + +## 2026-06-09 #fix scope:web-ui - 启动命令默认带 --auto-next 避免整页 skip 时空转 + +- Why: 当前页全 skip_progress / no_match 时不上滑下一页,20 步连续 page_done 无进展 +- 详见: scripts/web_ui.py start_runner + +## 2026-06-08 #feat scope:web-ui - 增加本地批处理进度页面 + +- Why: 50 人名单批处理需要可视化上传入口、实时状态和逐人结果表 +- 详见: scripts/web_ui.py / scripts/invoice_tool.py / docs/usage.md + +## 2026-06-08 #feat scope:android-automation - 增加扫码列表页队列和翻页处理 + +- Why: 列表页开具按钮不是固定坐标,需要按当前页姓名和 bounds 生成队列逐个处理 +- 详见: scripts/invoice_tool.py / docs/usage.md + +## 2026-06-08 #feat scope:android-automation - 增加 12306 单步开票状态机 + +- Why: 截图逐页校准成本高,需要用 XML 状态识别减少人工判断和 token 消耗 +- 详见: scripts/invoice_tool.py / docs/usage.md + +## 2026-06-08 #feat scope:android-automation - 增加 Android 端身份证后 8 位自动填入 + +- Why: 12306 App 扫码后需按脱敏姓名匹配名单并填写身份证后 8 位,人工逐个填写太慢 +- 详见: scripts/invoice_tool.py / templates/android_flow.example.json / docs/usage.md + +## 2026-06-08 #fix scope:batch-invoice - 识别 PNG 扫码开票单为 App 扫码流程 + +- Why: 真实铁路扫码开票单截图要求 12306App 扫码,不能误判为网页自动开票 +- 详见: scripts/invoice_tool.py / work/invoice_tasks.csv / docs/usage.md + +## 2026-06-08 #feat scope:batch-invoice - 落地铁路发票批量处理本地工具 + +- Why: 普通代购票缺统一批量开票入口,需要用官方页面配合本地台账和收票归集降本 +- 详见: scripts/invoice_tool.py / docs/usage.md / INDEX.md + +## 2026-06-10 #feat scope:h5-automation scope:web-ui - H5扫码开票单视觉驱动批量 + 优雅退出 + UI模式切换 + +- Why: H5单页挂N个乘客,back重置scroll;手机被占用(微信来电)时常发生,需优雅退出而非崩溃 +- 详见: scripts/invoice_tool.py android-h5-page-run + _h5_scroll_down;scripts/web_ui.py mode=h5/h5+模式选择器;三处优雅退出(PREECHECK_FAILED/PAGE_AWAY/sub-flow无法回列表) + +## 2026-06-10 #feat scope:web-ui scope:qr-decode - 粘贴二维码截图自动解码 + ADB 在手机打开 + +- Why: 用户拿到开票二维码截图后不用再手动扫,贴到 UI 自动解码→在手机打开对应页面 +- 详见: scripts/invoice_tool.py decode_qr_image(OpenCV QRCodeDetector);scripts/web_ui.py /api/decode-qr /api/open-on-phone + +## 2026-06-08 #chore scope:init - 项目初始化 + +- Why: 新专项需要独立工作区和三件套入口,便于后续发票资料处理与核对 +- 详见: AGENTS.md / INDEX.md / 本文件 diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 0000000..47dc3e3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file diff --git a/INDEX.md b/INDEX.md new file mode 100644 index 0000000..edc015a --- /dev/null +++ b/INDEX.md @@ -0,0 +1,123 @@ +# 铁路系统发票 + +铁路系统发票专项工作区,用于沉淀铁路系统相关发票资料、处理脚本、核对记录和衍生产出。 + +> 🤖 Agent 上手先读 [`AGENTS.md`](./AGENTS.md) 的操作守则(通用协议在 [`../docs/trio-protocol.md`](../docs/trio-protocol.md));改动后记得追加 [`CHANGELOG.md`](./CHANGELOG.md)(强标签格式见文件顶部)。 + +