docs(tasks): keep procurement execution on device
This commit is contained in:
+86
-96
@@ -305,6 +305,8 @@ SHA-256,响应和日志都不回显原 token 或 hash。
|
||||
"sku": "BLACK-20L",
|
||||
"description": "容量约20L,外观接近参考图",
|
||||
"image_asset_id": "2bbc1bf2-30f7-497e-a52b-bcb4c61d57c3",
|
||||
"reference_image_sha256": "64-char-lowercase-hex",
|
||||
"task_content_sha256": "64-char-lowercase-hex",
|
||||
"reference_image_url": "/api/v1/tasks/37c9c715-9b51-4ed5-984e-66dad2710c71/reference-image?claim_generation=1",
|
||||
"quantity": 2,
|
||||
"max_budget": "200.00",
|
||||
@@ -319,6 +321,10 @@ SHA-256,响应和日志都不回显原 token 或 hash。
|
||||
}
|
||||
```
|
||||
|
||||
`task_content_sha256` 是服务端对不可变任务内容生成的版本化摘要,客户端视为 opaque
|
||||
并在候选和终态结果中原样回显;服务端拒绝与当前 execution 快照不一致的摘要。
|
||||
App 下载参考图后必须独立校验 `reference_image_sha256`。
|
||||
|
||||
当前设备自己有已过期 `CLAIMED` 时优先回收该任务,避免设备唯一归属冲突;否则按
|
||||
`created_at ASC, id ASC` 选择 `PENDING` 或已过期 `CLAIMED`。没有任务返回 `204`;
|
||||
同 key 的无任务重放始终保持 `204`。同 key、同 token 的活跃 claim 重放返回同一
|
||||
@@ -347,7 +353,9 @@ generation/token 且租约未过期的活跃任务可以读取。成功返回匿
|
||||
|
||||
同 key、同请求重放返回同一 execution;错误用户/设备/token 返回 `403`,过期租约、
|
||||
状态或版本冲突返回 `409`。成功响应包含更新后的 `task`、`execution`、`replayed`
|
||||
和 `server_time`,运行租约使用配置的 running lease。
|
||||
和 `server_time`。T-206 将默认 running lease 从当前 90 秒改为 30 分钟(允许配置
|
||||
5 至 120 分钟),并显式返回 `execution_expires_at`。该时间是 App 可以离线继续
|
||||
自动化的上限,不是后台自动重分配时间。
|
||||
|
||||
### `POST /api/v1/tasks/{task_id}/heartbeat`
|
||||
|
||||
@@ -368,12 +376,13 @@ generation/token 且租约未过期的活跃任务可以读取。成功返回匿
|
||||
"status": "RUNNING",
|
||||
"version": 4,
|
||||
"claim_generation": 1,
|
||||
"claim_expires_at": "2026-07-25T08:35:00Z"
|
||||
"claim_expires_at": "2026-07-25T09:03:30Z"
|
||||
},
|
||||
"execution": {
|
||||
"id": "e3190742-a24b-441e-b5c1-c7ed10ed342f",
|
||||
"current_step": "SCAN_RESULTS",
|
||||
"order_submitted": false
|
||||
"order_submitted": false,
|
||||
"execution_expires_at": "2026-07-25T09:03:30Z"
|
||||
},
|
||||
"cancel_requested": false,
|
||||
"server_time": "2026-07-25T08:33:30Z"
|
||||
@@ -381,8 +390,9 @@ generation/token 且租约未过期的活跃任务可以读取。成功返回匿
|
||||
```
|
||||
|
||||
只接受 1 至 64 字节的大写 ASCII step。heartbeat 使用服务端 UTC 更新 execution、
|
||||
设备最近在线时间、任务 version 和租约,不写高频任务事件;过期运行租约拒绝续租,
|
||||
任务保持原非终态且绝不回到领取队列。
|
||||
设备最近在线时间、任务 version 和 `execution_expires_at`,不写高频任务事件。
|
||||
App 每 30 秒 best-effort 调用;网络失败时可执行到上一次服务端截止时间。截止时间
|
||||
过期后拒绝续作,任务保持原非终态且绝不回到领取队列。
|
||||
|
||||
### `POST /api/v1/tasks/{task_id}/release`
|
||||
|
||||
@@ -402,104 +412,34 @@ App 收到取消请求并在安全检查点停止后调用:
|
||||
}
|
||||
```
|
||||
|
||||
请求带 `X-Claim-Token` 和 `Idempotency-Key`。只有匹配的未结束 execution、有效
|
||||
运行租约和已存在的管理取消请求可以确认;成功把任务置为 `CANCELED`、结束
|
||||
execution、清除 claim 秘密并追加带用户/设备 actor 的事件。同 key 重放不产生第二
|
||||
个事件。
|
||||
请求带 `X-Claim-Token` 和 `Idempotency-Key`。只有匹配的未结束 execution、设备
|
||||
归属和已存在的管理取消请求可以确认;App 已在安全检查点停止时,即使离线授权刚
|
||||
过期也允许原设备补交确认。成功把任务置为 `CANCELED`、结束 execution、清除 claim
|
||||
秘密并追加带用户/设备 actor 的事件。同 key 重放不产生第二个事件。
|
||||
|
||||
## AI 合约
|
||||
## App 本地 AI 边界
|
||||
|
||||
生产 App 不接收 provider、Base URL、model、timeout、retry 或 API Key,也不能通过
|
||||
AI 请求覆盖这些字段。后端在 execution 第一次 AI 调用时绑定 ADMIN 配置的默认
|
||||
provider version,后续调用使用同一 provenance。管理 Web 的 `/settings/vlm` 使用
|
||||
ADMIN session + CSRF 管理追加配置版本;Key 是 write-only,任何 GET/HTML/JSON 均
|
||||
只返回 `has_secret`。连接测试使用内置脱敏样本,不读取任务内容。
|
||||
MVP 后端不实现 `/api/v1/tasks/{task_id}/ai/*`,不保存 VLM 配置或 Key,也不代理
|
||||
第三方模型。App 使用本机配置的 OpenAI 兼容 adapter 完成需求提取和候选评估;后台
|
||||
任务 payload 不能携带或覆盖 provider、Base URL、model、prompt 或 API Key。
|
||||
|
||||
第一版 provider adapter 只支持 OpenAI 兼容 `/v1/chat/completions` 多模态请求。
|
||||
远程地址必须是受 SSRF/DNS rebinding 防护的 HTTPS 端点且不跟随重定向。App 看到的
|
||||
错误只包含稳定 code、可读 message 和 request ID,不包含供应商正文。
|
||||
App 支持 `MANUAL_FIRST` 和 `AI_ASSISTED`。execution 结果必须记录实际模式;
|
||||
`AI_ASSISTED` 还要记录 provider ID、model、prompt/schema version、reference/
|
||||
candidate evidence SHA-256 和结构化模型判断。结果不得包含 Key、Authorization、
|
||||
完整 endpoint、订单号、店铺名或供应商原始响应正文。
|
||||
|
||||
### `POST /api/v1/tasks/{task_id}/ai/extract-requirements`
|
||||
|
||||
只允许当前 execution 调用。后端从任务读取原始图片和文字,客户端不能替换硬约束。
|
||||
|
||||
```json
|
||||
{
|
||||
"execution_id": "e3190742-a24b-441e-b5c1-c7ed10ed342f"
|
||||
}
|
||||
```
|
||||
|
||||
响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"schema_version": 1,
|
||||
"search_query": "黑色 20L 双肩包",
|
||||
"category": "双肩包",
|
||||
"attributes": [
|
||||
{"name": "color", "value": "black", "source": "text"},
|
||||
{"name": "capacity", "value": "about 20L", "source": "text"}
|
||||
],
|
||||
"sku": "BLACK-20L",
|
||||
"quantity": 2,
|
||||
"max_budget": "200.00",
|
||||
"confidence": 0.86,
|
||||
"warnings": [],
|
||||
"manual_review": {
|
||||
"required": false,
|
||||
"reasons": []
|
||||
},
|
||||
"provenance": {
|
||||
"provider_id": "configured-provider",
|
||||
"model": "configured-model",
|
||||
"prompt_version": "requirement-extraction-v1",
|
||||
"reference_image_sha256": "64-char-lowercase-hex"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`sku`、`quantity` 和 `max_budget` 必须来自原始任务,不能采用模型返回值。第一层
|
||||
`ProbeTask` 尚无预算字段,因此 T-103 输出 `max_budget: null` 并追加
|
||||
`MAX_BUDGET_NOT_PROVIDED` 警告。属性 `source` 的 API 表示使用小写
|
||||
`title/image/both`;供应商响应在 adapter 内规范化后才进入此合约。
|
||||
|
||||
### `POST /api/v1/tasks/{task_id}/ai/evaluate-candidate`
|
||||
|
||||
```json
|
||||
{
|
||||
"execution_id": "e3190742-a24b-441e-b5c1-c7ed10ed342f",
|
||||
"candidate_index": 1,
|
||||
"screenshot_asset_id": "7b733922-f90f-4bc4-a9ad-3e8ec4769122",
|
||||
"observed": {
|
||||
"title": "页面可见标题",
|
||||
"price": "189.00"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"schema_version": 1,
|
||||
"candidate_index": 1,
|
||||
"decision": "REVIEW",
|
||||
"score": 0.82,
|
||||
"matched": ["颜色接近", "价格未超预算"],
|
||||
"missing_or_uncertain": ["容量无法从当前页面确认"],
|
||||
"rejection_reasons": [],
|
||||
"confidence": 0.78
|
||||
}
|
||||
```
|
||||
|
||||
`candidate_index` 必须原样回显当前候选 ordinal;`decision` 只允许 `REVIEW`、
|
||||
`REJECT`、`MANUAL_REQUIRED`。App 按 ordinal 串行评估,每个候选最多调用一次、整批
|
||||
最多 5 次,并由本地确定性规则产生建议项。模型不能返回页面动作、建议 ordinal、
|
||||
人工确认状态或订单授权,也没有“提交订单”权限;原任务未提供预算时,任何价格或
|
||||
预算匹配声明都视为无效输出。
|
||||
`sku`、`quantity` 和 `max_budget` 始终来自原任务,模型不能覆盖。App 按 ordinal
|
||||
串行评估,每个 execution 最多一次需求提取、最多 5 次候选评估,并由本地确定性规则
|
||||
产生建议。模型不能返回页面动作、建议 ordinal、人工确认状态或订单授权;低置信度、
|
||||
无效 schema、证据不足或预算不确定时转人工。
|
||||
|
||||
## 执行事件与结果
|
||||
|
||||
App 使用加密 outbox 按“事件 -> evidence asset -> 候选 -> 终态”顺序提交。所有写接口
|
||||
重新校验 BUYER/device/task/execution/claim 和幂等键。原设备可以在
|
||||
`execution_expires_at` 后补报授权内已经产生的结果;后端记录
|
||||
`received_after_execution_expiry=true`,但这不允许 App 在过期后继续自动化。
|
||||
|
||||
### `POST /api/v1/tasks/{task_id}/events`
|
||||
|
||||
批量追加事件,必须带 `Idempotency-Key`:
|
||||
@@ -521,6 +461,53 @@ ADMIN session + CSRF 管理追加配置版本;Key 是 write-only,任何 GET/
|
||||
|
||||
`message` 不能包含凭证或完整个人敏感信息;同一 `event_id` 重放不重复插入。
|
||||
|
||||
### `POST /api/v1/tasks/{task_id}/candidates`
|
||||
|
||||
批量保存当前 execution 实际检查的最多 5 个候选,必须带 `Idempotency-Key`:
|
||||
|
||||
```json
|
||||
{
|
||||
"execution_id": "e3190742-a24b-441e-b5c1-c7ed10ed342f",
|
||||
"task_content_sha256": "64-char-lowercase-hex",
|
||||
"execution_mode": "AI_ASSISTED",
|
||||
"search_query": "黑色 20L 双肩包",
|
||||
"provenance": {
|
||||
"provider_id": "device-configured-provider",
|
||||
"model": "device-configured-model",
|
||||
"prompt_version": "candidate-evaluation-v1",
|
||||
"schema_version": 1
|
||||
},
|
||||
"candidates": [
|
||||
{
|
||||
"ordinal": 1,
|
||||
"title": "页面可见标题",
|
||||
"sku_text": "黑色 20L",
|
||||
"price": "189.00",
|
||||
"product_url": "https://mobile.yangkeduo.com/goods.html?goods_id=example",
|
||||
"image_url": "https://example.invalid/short-lived-image",
|
||||
"evidence_asset_ids": ["7b733922-f90f-4bc4-a9ad-3e8ec4769122"],
|
||||
"evaluation": {
|
||||
"decision": "REVIEW",
|
||||
"score": 0.82,
|
||||
"matched": ["颜色接近"],
|
||||
"missing_or_uncertain": ["容量需人工确认"],
|
||||
"rejection_reasons": [],
|
||||
"confidence": 0.78
|
||||
}
|
||||
}
|
||||
],
|
||||
"recommendation": {
|
||||
"candidate_ordinal": 1,
|
||||
"policy_version": "local-recommendation-v1",
|
||||
"reasons": ["当前证据下匹配分最高"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`MANUAL_FIRST` 时 `provenance` 和 `evaluation` 为空,但候选观察、搜索词和人工结果仍
|
||||
可提交。后端校验 ordinal 连续唯一、最多 5 个和任务内容哈希;不请求 `product_url`
|
||||
或 `image_url`,主要证据必须是已鉴权 asset。
|
||||
|
||||
### `POST /api/v1/tasks/{task_id}/complete`
|
||||
|
||||
人员完成确认后调用,必须带 `Idempotency-Key`:
|
||||
@@ -528,9 +515,12 @@ ADMIN session + CSRF 管理追加配置版本;Key 是 write-only,任何 GET/
|
||||
```json
|
||||
{
|
||||
"execution_id": "e3190742-a24b-441e-b5c1-c7ed10ed342f",
|
||||
"task_content_sha256": "64-char-lowercase-hex",
|
||||
"execution_mode": "AI_ASSISTED",
|
||||
"outcome": "CANDIDATE_ACCEPTED",
|
||||
"operator_reason": "款式和预算符合验证要求",
|
||||
"candidate": {
|
||||
"ordinal": 1,
|
||||
"title": "页面可见标题",
|
||||
"price": "189.00",
|
||||
"evidence_asset_ids": ["7b733922-f90f-4bc4-a9ad-3e8ec4769122"]
|
||||
|
||||
Reference in New Issue
Block a user