docs(tasks): keep procurement execution on device

This commit is contained in:
QiuSW
2026-07-27 09:43:04 +08:00
parent 45d1436033
commit 9e698c1b6c
15 changed files with 368 additions and 337 deletions
+43 -38
View File
@@ -11,12 +11,12 @@
管理 Web(服务端渲染)
|
v
统一 Backend API ---------------------+
| |
+--> SQLite / 文件存储 +--> VLM Provider
统一 Backend API
|
+--> SQLite / 文件存储
^
|
Android 采购 App
Android 采购 App --------------------> VLM Provider
|
+--> AccessibilityService
|
@@ -25,9 +25,10 @@ Android 采购 App
```
- 管理 Web 与 API 同属 `backend-api/`,但页面层不能直接访问数据库。
- Android App 位于 `android-buyer/`,通过 API 领取任务、调用 AI 能力和回传证据。
- Android App 位于 `android-buyer/`,通过 API 领取任务和回传证据,在本地调用 AI。
- 拼多多是第三方受控边界,只能由 Android 设备在已登录会话中操作。
- VLM 密钥的正式路径保留在后端;技术探针如需端上临时调用,只能使用本地忽略配置。
- VLM 配置和 Key 位于手机,Key 使用 Android Keystore 包装的加密存储;管理后端不
保存、下发或代理模型调用。
## 二、模块职责
@@ -41,7 +42,7 @@ internal/transport/http/ # Gin 路由、中间件、页面和请求/响应 DT
internal/usecase/ # 创建、领取、状态迁移和结果归档
internal/domain/ # 实体、状态机、权限和确定性约束
internal/repository/sqlite/ # database/sql 仓储实现
internal/platform/ # 配置、日志、文件存储和 VLM 适配器
internal/platform/ # 配置、日志和文件存储
```
职责:
@@ -50,7 +51,6 @@ internal/platform/ # 配置、日志、文件存储和 VLM 适配器
- 创建任务并保存原始输入,不让模型结果覆盖原始事实。
- 原子领取下一条任务,签发有期限的 claim lease。
- 校验任务状态迁移、幂等键和设备归属。
- 代理 VLM 调用并把供应商响应转换为领域结构。
- 保存候选、事件、截图元数据和结果。
- 为管理 Web 提供任务列表、详情和取消能力。
@@ -88,7 +88,7 @@ taskclient/ # 后端 API、认证、幂等和重试
tasksource/ # 统一任务源;本地探针与后端 API 实现
workflow/ # ProcurementWorkflow 状态机
automation/ # Accessibility 节点、截图、点击、输入、滑动
ai/ # 结构化 AI 请求/响应,不含供应商业务类型
ai/ # 结构化 AI、OpenAI 兼容适配器和本地安全配置
evidence/ # 截图、步骤日志、脱敏和上传
```
@@ -97,8 +97,10 @@ evidence/ # 截图、步骤日志、脱敏和上传
- 检查无障碍、拼多多安装/登录可用性、网络和当前任务状态。
- 由用户点击后领取任务;一台设备一次只运行一条。
- 执行有界步骤:解析 -> 搜索 -> 浏览不超过 5 个候选 -> 判断 -> 停止确认。
- 直接调用用户在本机明确配置的 VLM;没有可用模型时允许显式人工优先模式。
- 显示当前步骤、停止原因和人工接管入口。
- 用前台服务承载执行中任务,进程重启后从服务端状态恢复或安全失败。
- 用前台服务承载执行中任务,以服务端授权截止时间支持有限离线;进程重启后从加密
本地状态与服务端核对恢复或安全失败。
- 回传步骤事件、候选、截图和最终结果。
不得:
@@ -115,22 +117,22 @@ evidence/ # 截图、步骤日志、脱敏和上传
1. **需求提取**:参考图 + 标题 + SKU -> 搜索词、类目、属性、置信度、警告。
2. **候选评估**:候选截图/文本 + 原始约束 -> 匹配项、缺失项、拒绝原因、建议分。
正式数据流固定为 `Android App -> 肉包后端 -> VLM Provider`。生产采购 App 不选择
provider、不保存供应商 API Key,也不直连模型地址;它只调用 task-scoped AI API。
ADMIN 在管理 Web 维护一个供新 execution 使用的默认 OpenAI 兼容 provider 版本。
第一版只支持 `/v1/chat/completions` 多模态协议。
正式数据流固定为 `Android App -> VLM Provider`。管理后端只下发原始任务并接收
结构化结果,不保存/下发 provider 配置或 Key,也不实现 `/tasks/{id}/ai/*` 代理。
后台任务不能携带或覆盖本机 provider、Base URL、model、prompt 或 API Key。
provider 配置采用追加版本:保存名称、HTTPS Base URL、model、timeout、retry 上限、
启用状态和非秘密审计字段。API Key 使用环境提供的 256 bit master key,通过 AEAD
随机 nonce 加密;AAD 绑定 provider/version。读取接口只返回 `has_secret`,永不返回
明文或密文。execution 在第一次 AI 调用时绑定 config/provider/model/prompt version,
后续调用不随默认配置变化。master key 缺失或解密失败时真实调用安全失败,但管理任务、
领取和非 AI 功能继续可用。
App 支持两个显式模式:
后端 provider transport 禁止重定向,并在解析和连接时阻断 loopback、私网、链路
本地、组播、云 metadata 和 DNS rebinding;生产只允许 HTTPS。无 Key 的 loopback
HTTP 只属于显式 Debug mock,不能从生产管理 Web 创建。连接测试只使用内置脱敏文字
和测试图片,不读取订单或任务资产。
- `MANUAL_FIRST`:从原始标题/SKU 产生有界搜索词,由人员判断候选,不要求 VLM。
- `AI_ASSISTED`:使用 App 本地配置的 OpenAI 兼容 provider 做需求提取和候选评估。
模式在 execution 开始时固定并写入结果;AI 失败后只能由人员明确切换,不能静默降级。
App 同时固定 provider ID、model、prompt/schema version 和证据 SHA-256,作为非秘密
provenance 回传。Key、Authorization、完整 endpoint 和供应商原始响应正文不回传。
provider Key 从旧设置迁移到 Android Keystore 包装的加密存储;迁移或解密失败时
拒绝真实调用。每台设备使用独立、可撤销、有限额度的 Key。Keystore 只降低静态泄露
风险,不能宣称可抵抗已 Root 或完全受控设备。
模型输出是不可信建议,必须通过 schema 和确定性校验:
@@ -154,19 +156,18 @@ T-103 的模型响应只允许
输出并转人工。最终领域结果再由确定性代码补回原 SKU、数量、空预算、人工复核原因和
`provider/model/prompt_version/reference_image_sha256`。
技术探针每次点击最多发出一次 VLM 请求,不沿用通用 Agent 的重试循环。T-207 后端
默认不重试,管理员最多允许一次仅限确定未收到响应的临时网络失败;schema、4xx 和
安全错误绝不重试。该结构化 HTTP 客户端关闭连接自动重试和 HTTP/HTTPS 重定向,
整体调用超时为 75 秒;协程取消
技术探针和后台 execution 的每个逻辑步骤最多发出一次 VLM 请求,不沿用通用 Agent
的重试循环。该结构化 HTTP 客户端关闭连接自动重试和 HTTP/HTTPS 重定向,整体调用
超时为 75 秒;协程取消
会取消底层 HTTP call,成功响应正文上限为 64 KiB,非成功响应不读取正文。
远程 provider 只允许 HTTPS;HTTP 仅允许 Debug 下无 API Key 的 `localhost`、
Release 远程 provider 只允许 HTTPS;HTTP 仅允许 Debug 下无 API Key 的 `localhost`、
`127.0.0.1` 或 `::1` 本机 mock。端点不得包含 userinfo、query 或 fragment。标题
和 SKU 在进入
prompt 前分别限制为 2048 和 512 个 UTF-8 字节。JPEG 在调用前复核媒体类型、20 MiB
上限、魔数和 SHA-256,按声明长度一次分配并精确读取,Android 解码后最长边限制为
2048 px。请求和响应正文、Base64、API Key 不写普通日志;加密密钥存储不可用且存在
密钥时拒绝调用。
2048 px。请求和响应正文、Base64、API Key 不写普通日志;Keystore-backed 密钥存储
不可用且存在 Key 时拒绝调用。
## 三、核心数据流
@@ -274,11 +275,12 @@ T-102/T-104 在搜索结果后追加一个有界候选步骤:
管理员创建 PENDING 任务
-> 采购员在 App 点击“获取任务”
-> 后端事务内选取并置为 CLAIMED
-> App 确认后置为 RUNNING
-> 定期 heartbeat 续租并上报步骤
-> 需求提取和拼多多自动化
-> App 确认后置为 RUNNING,取得有限离线授权
-> best-effort heartbeat 同步步骤和取消
-> App 本地需求提取和拼多多自动化
-> WAITING_CONFIRMATION
-> 采购员接受/拒绝候选
-> 加密 outbox 幂等回传结果和证据
-> SUCCEEDED 或 FAILED/CANCELED
-> 管理端展示结果
```
@@ -312,16 +314,19 @@ CLAIMED/RUNNING/WAITING_CONFIRMATION
规则:
- `SUCCEEDED` 是验证结果成功,不代表已下单;`order_submitted=false`。
- `CLAIMED` 默认租约为 10 分钟;`RUNNING/WAITING_CONFIRMATION` 默认租约为
90 秒,App 计划每 30 秒 heartbeat 续租。三个时长均由有上下界的环境变量配置。
- `CLAIMED` 默认租约为 10 分钟;T-206 将 `RUNNING/WAITING_CONFIRMATION` 默认
授权从当前 90 秒扩展为 30 分钟,允许范围 5 至 120 分钟。App 每 30 秒
best-effort heartbeat,成功后按服务端时间滑动续期。
- 只有当前设备和有效 claim token 能启动、续租、上报或结束任务。
- 终态不可回退;重试创建新的 execution attempt,不篡改历史证据。
- App 在 claim 前生成并安全保存 256 bit Raw URL token;后端只存 SHA-256,
token 与 `Idempotency-Key` 相互独立。
- 过期 `CLAIMED` 可以原子回收;过期 `RUNNING/WAITING_CONFIRMATION` 保持原状态,
不自动回队列。
不自动回队列。App 到达服务端授权截止时间必须安全停止,但原设备可以在重连后
补报已产生的终态结果并留下过期补报审计标志。
- 管理取消 `PENDING/CLAIMED` 立即终态;执行中只设置停止请求,App 在下一个安全
检查点停止并调用 `cancel-ack` 后才进入 `CANCELED`。
检查点停止并调用 `cancel-ack` 后才进入 `CANCELED`。离线期间不能保证即时取消,
App 恢复连接后必须先同步取消状态再继续页面动作。
### 4.2 Android 工作流状态