From 12e9aa85915a6e417e5cbb7e461cdf0fa28b6fb3 Mon Sep 17 00:00:00 2001 From: QiuSW Date: Tue, 15 Sep 2026 11:12:17 +0800 Subject: [PATCH] =?UTF-8?q?docs(#278):=20=E5=90=8C=E6=AD=A5=20SYB=20?= =?UTF-8?q?=E5=95=86=E5=93=81=20PDD=20=E5=9B=BE=E6=90=9C=E9=87=87=E9=9B=86?= =?UTF-8?q?=E5=A5=91=E7=BA=A6=E9=95=9C=E5=83=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 线上 Wiki 页面 Android-Agent-API-Contract 已更新并回读, revision 4d1ba4423229;本次提交为 harness sync 生成的本地镜像。 新增章节记录:批量创建接口与去重、批次上限 50(作用于去重后条数、 不可 env 覆盖及其原因)、设备能力 pdd.image-search.v1、任务载荷只下发 imageUrl/mediaType/sizeBytes/sha256、复用 identify 回填身份、自动关联的 CAS 谓词与跨币种拒绝、image_search_linked 标记只读出现在采购视图且不参与 门禁、采集 > 图搜的 ORDER BY 及其必须落在服务端的原因、管理端与 Agent 两侧错误码表、Agent 执行边界(禁 OCR/VLM、订单确认页只返回、相册最新项 须跨图片与视频比较),以及 2026-09-15 真机核对的 PDD 界面判据与已知偏差。 Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01NTDbDcwbDw1TSAcE6wfh2F --- docs/08-agent-api-contract.md | 160 ++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) diff --git a/docs/08-agent-api-contract.md b/docs/08-agent-api-contract.md index 10c0564..fdce6ce 100644 --- a/docs/08-agent-api-contract.md +++ b/docs/08-agent-api-contract.md @@ -1,3 +1,11 @@ + +generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件) +wiki_page: Android-Agent-API-Contract +wiki_url: https://git.ilapage.cn/OPC/goauto/wiki/Android-Agent-API-Contract.- +wiki_revision: 4d1ba4423229a611fa7ddabc3a4553c14433036d +synchronized_at: 2026-09-15T03:10:13Z + + generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件) wiki_page: Android-Agent-API-Contract @@ -819,6 +827,158 @@ Content-Type: application/json - Admin 的 BatchRetry 接口和行为不变。相同 `requestId` 重放返回同一新任务;不同 requestId 再点由最新任务门禁拒绝。 +## SYB 商品 PDD 图搜采集(#275~#280) + +图搜任务**就是采集任务**:同一张 `collection_task` 表、`source = 'image_search'`、同一个 +`/api/agent/v1/tasks/*` 端点。与 `agent_current_page` 的唯一区别是**谁打开那个商品页**—— +后者由采购员手动打开,图搜由 Agent 用 PDD 的拍照搜索找到并打开。打开之后的链路 +(分享链接 → goods_id → 身份回填 → 采集 → 提交结果)完全复用,没有新的采集接口。 + +### 管理端:批量创建 + +```http +POST /api/admin/v1/collection-tasks/image-search/batch +Content-Type: application/json + +{"requestId":"","sybProductIds":[1,2,3],"ruleId":3,"deviceId":null,"overwriteLinked":false} +``` + +- **按 `shopee_product` 去重**:多条共用同一张参考图的 SYB 明细合并为一个任务。请求最多 + 100 个 `sybProductIds`。 +- **批次上限作用于去重后的任务数**,不是入参明细数,上限 50(约 17~33 分钟设备占用, + 按单次图搜 20~40 秒估算)。超限整批拒绝,不部分创建。该值是服务端常量,**不可由环境 + 变量覆盖**:确认对话框必须在提交前禁用按钮,前端持有同一个数字,可覆盖只会让两侧静默 + 不一致。改动时两侧必须同改。 +- `deviceId` 可空,为空由具备能力的空闲设备领取。`overwriteLinked` 为 false 时已关联 PDD + 的蝦皮商品返回 `IMAGE_SEARCH_ALREADY_LINKED` 并跳过。 +- 响应逐条返回 `shopeeProductId` / `sybProductIds` / `taskId` 或 `code` + `message`。 + +### 设备能力 + +图搜任务要求设备声明 `pdd.image-search.v1`。不具备该能力的设备在领取阶段跳过图搜任务, +不会失败。 + +### 任务下发 + +`GET /api/agent/v1/tasks/next` 与 `claim` 返回的载荷结构对所有来源一致,图搜任务额外携带 +`imageSearch`: + +```json +{"imageUrl":"https://...","mediaType":"image/jpeg","sizeBytes":81234,"sha256":"<64 位小写 hex>"} +``` + +- **只下发这四个字段。** 关联关系、参考价、价格倍数、去重明细等快照留在服务端的 + `image_search_snapshot`,不下发给 Agent。 +- 服务端不下发图片字节;Agent 自行下载 `imageUrl` 并按 `sizeBytes` 与 `sha256` 校验。 + 参考图上限 10MB。 +- 身份回填前 `pddProductId` 为 `null`,`urlSnapshot` 与 `goodsIdSnapshot` 为空字符串, + 与 `agent_current_page` 相同。 + +### 身份回填 + +图搜复用 `POST /api/agent/v1/current-page-collection-tasks/{taskId}/identify`,该接口已同时 +接受 `agent_current_page` 与 `image_search` 两种来源。 + +- `[已核对]` 该接口要求任务已绑定 `device_id`。批量创建允许 `deviceId` 为空,但 + `Claim` 在事务中写入 `device_id = <领取设备>`,因此 Agent 调用 identify 时设备必定已绑定。 +- 回填之后的结果提交、失败上报、reset 重采与 `agent_current_page` 完全一致。 + **`ruleSnapshot` 结构与普通采集任务同构**,图搜不引入第二种规则形态。 + +### 结果自动关联与价格兜底 + +任务完成后服务端尝试建立 shopee↔PDD 关联,条件全部满足才写入: + +- 任务 `source = 'image_search'`、状态 `completed`、已回填 `pddProductId`; +- 目标蝦皮商品 `pdd_product_id IS NULL AND image_search_linked = false`(CAS 谓词:Agent + 执行期间发生的人工改动胜出,不被覆盖); +- 蝦皮商品币种为 CNY。**非 CNY 一律不自动关联**,快照记 `priceGuardSkipped` 与 + `CROSS_CURRENCY_TWD_PDD_CNY`,留给人工复核; +- 候选价与 `syb_product.unit_price_cent` 的偏离在 `maxPriceRatio`(1~100 的整数)之内。 + +自动建立的关联置 `shopee_product.image_search_linked = true`,含义是**「机器找的,没人核过」**。 +采购员在虾皮商品页手动关联(`LinkPDD`)时置回 false。该标记只读地出现在批量采购预览 +(`BatchPreviewItem.imageSearchLinked`)与采购任务列表,**不参与任何采购门禁、资格判定或 +下单条件**。 + +### 调度:采集 > 图搜 + +图搜任务在领取队列中垫底,由 `task.Service.Next` 的 `ORDER BY` 实现: + +```sql +-- 共享候选池 +CASE WHEN device_id IS NULL THEN 1 ELSE 0 END, +CASE WHEN source = 'image_search' THEN 1 ELSE 0 END, +created_at ASC, id ASC +``` + +- `[必须]` 该排序只能落在服务端。图搜任务是同一端点下发的普通采集任务,Agent 调用 + `next` 时服务端已经选好了行,客户端无从推翻。 +- `[必须]` 来源降级排在 `device_id` **之后**:指派设备是人的显式选择,必须继续优先于来源 + 降级,否则未指派的采集任务会抢在运营明确路由到本机的图搜任务前面。 +- 采购任务不需要此处排序:它们在独立表、独立端点,Agent 已在检查采集之前处理。 + +### 错误码 + +管理端(批量创建): + +| 错误码 | 含义 | +|---|---| +| `IMAGE_SEARCH_BATCH_TOO_LARGE` | 去重后任务数超过单批上限 | +| `IMAGE_SEARCH_ALREADY_LINKED` | 蝦皮商品已关联 PDD,逐条跳过 | +| `IMAGE_SEARCH_TASK_ACTIVE` | 该蝦皮商品已有未结束的图搜任务 | +| `IMAGE_SEARCH_IMAGE_INVALID` | 参考图下载失败、格式不支持或超过限制 | +| `IMAGE_SEARCH_INPUT_CHANGED` | 关联、参考图、价格或币种在请求期间变化 | +| `IMAGE_SEARCH_REQUEST_CONFLICT` | 同一 `requestId` 的参数已变化 | +| `IMAGE_SEARCH_CONFIG_INVALID` | 价格倍数配置不是 1~100 的整数 | +| `IMAGE_SEARCH_SNAPSHOT_INVALID` | 任务快照无效 | + +Agent 上报(`POST /api/agent/v1/tasks/{taskId}/fail`): + +| 错误码 | 含义 | +|---|---| +| `IMAGE_SEARCH_PERMISSION_REQUIRED` | 未授予相册权限 | +| `IMAGE_SEARCH_ASSET_INVALID` | 参考图下载、校验或写入相册失败 | +| `IMAGE_SEARCH_ASSET_NOT_LATEST` | 相册中存在更新的图片或视频,无法确认会选中我们准备的图 | +| `IMAGE_SEARCH_ENTRY_NOT_FOUND` | 找不到唯一可点的拍照搜索入口,或归位失败 | +| `IMAGE_SEARCH_NO_CANDIDATES` | 结果页没有可用候选(含全部为广告) | +| `IMAGE_SEARCH_AUTOMATION_UNAVAILABLE` | Agent 版本尚未接入图搜自动化 | + +### Agent 执行边界 + +- 只使用无障碍树,**禁止 OCR / VLM**。 +- 相册权限:Android 13+ `READ_MEDIA_IMAGES`,10~12 `READ_EXTERNAL_STORAGE` + (`maxSdkVersion=32`)。这是本功能新增的权限,既有设备升级后需重新授权。 +- 参考图写入设备相册后,Agent 先确认它是相册中**最新的一项(图片与视频一起比较)**, + 再点击网格第一格;不是最新一项时以 `IMAGE_SEARCH_ASSET_NOT_LATEST` 明确失败, + 不往后找、不猜测。任务结束(成功或失败)都删除本次写入的图片。 +- 归位状态机可从商品详情、规格弹层、订单确认页、结果页、重试弹窗回到图搜入口,上限 + 50 次动作。**订单确认页只执行返回**;支付与下单文案仅作只读识别信号,任何情况下都不是 + 点击目标。 +- 结果页默认打开第一个候选,跳过带「广告」标记的卡片。 +- 「重新采集」对图搜任务的语义是**重采同一个商品**,不重新搜索;搜错了由采购员在管理端 + 手动改关联(该动作同时清除图搜标记)。 + +### PDD 界面判据(2026-09-15 真机核对,设备 1080×2354) + +常量集中在 `PinduoduoImageSearchCriteria`。以下为核对结论,PDD 版本更新后需要重新 dump: + +| 位置 | 判据 | 状态 | +|---|---|---| +| 首页入口 | `content-desc='拍照搜索'` 且 **clickable** 的唯一节点,实测 `[941,161][1080,228]` | 命中 | +| 图搜页 | `我的相册` + `最近搜索` + `历史浏览` + `点击拍照` | 命中 | +| 选图网格 | `最近项目` 锚点下方 4 列等宽网格,每格 267px,屏宽 1080,容差 ±24 | 命中 | +| 结果页 | `搜图片同款` 且排序控件 ≥ 3(实测 综合/销量/价格/品牌) | 命中 | +| 重试弹窗 | `请对准商品或码,保持手机稳定` + `取消` + `再试一次` | **未验证** | + +`[必须]` 入口判定必须同时要求 clickable:个人中心 tab 的**根节点**也带 +`content-desc='拍照搜索'`,覆盖全屏且不可点,而 `visibleTexts()` 会把 `contentDescription` +当文本收集。只按文案匹配会把个人中心判成首页,随后几何兜底会点到该页顶部最靠右的可点 +节点——实测那是「设置」按钮。 + +已知偏差:取景提示真机为「对准商品/条形码/二维码,自动识别」,历史常量 +`即可进行自动识别` 不命中,靠 `点击拍照` 兜住。相册网格同时包含视频(实测见到带时长的 +格子),这是上面「最新一项须跨图片与视频比较」的由来。 + ## 采集颜色图片上传(#133) 结构化采集结果成功提交后,Agent 可为任务已采集的颜色逐张调用: