feat(compare): 外卖比价 2 端点透传到 pricebot-backend (food MVP)

仿 coupon_step 在 app/api/v1/compare.py 加纯 body 透传 2 端点(MVP 不鉴权,只读
device_id/trace_id/step 打日志,不做 schema 校验)。client /api/v1/{intent/recognize,
price/step} → backend 去掉 /v1 转发到 PRICEBOT_BASE_URL,接通外卖比价
client→server→pricebot-backend 链路。

- compare.py: _passthrough helper + intent/recognize + price/step
- config.py: PRICEBOT_COMPARE_TIMEOUT_SEC=60(意图识别大上下文 LLM、price/step
  每帧 LLM,比领券 30s 长;对齐客户端 agent ApiClient 的 60s 读超时)
- main.py: 注册 compare_router
- tests/test_compare_proxy.py: 2 端点参数化测试(透传/5xx→502/不可达→502/坏 JSON→400)
- docs/api/{compare-intent-recognize,compare-price-step}.md + 更新 README 索引
- 待办与技术债.md P2 外卖 2 端点标 (2026-05-27);电商 2 个待接(compare.py
  加两行即可)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-28 10:29:20 +08:00
parent 24faaf9d47
commit aa34ad7d0b
8 changed files with 267 additions and 13 deletions
+4 -2
View File
@@ -18,13 +18,15 @@
| 5 | `POST /api/v1/auth/refresh` | 无 | [详情](./auth-refresh.md) |
| 6 | `GET /api/v1/auth/me` | Bearer | [详情](./auth-me.md) |
| 7 | `POST /api/v1/auth/logout` | Bearer | [详情](./auth-logout.md) |
| 8 | `POST /api/v1/coupon/step` | Bearer | [详情](./coupon-step.md) |
| 8 | `POST /api/v1/coupon/step` | | [详情](./coupon-step.md) |
| 9 | `POST /api/v1/meituan/coupons` | 无 | [详情](./meituan-coupons.md) |
| 10 | `POST /api/v1/meituan/feed` | 无 | [详情](./meituan-feed.md) |
| 11 | `POST /api/v1/meituan/referral-link` | 无 | [详情](./meituan-referral-link.md) |
| 12 | `POST /api/v1/intent/recognize` | 无 | [详情](./compare-intent-recognize.md) |
| 13 | `POST /api/v1/price/step` | 无 | [详情](./compare-price-step.md) |
> ⚠️ 美团三个接口当前**无鉴权**,且 `referral-link` 的 `sid` 允许客户端传值覆盖默认渠道——见各接口"备注"。
> `coupon/step` 是后端**唯一需要 JWT 鉴权的业务接口**
> `coupon/step` 及外卖比价的 `intent/recognize` / `price/step` 都透传到 pricebot-backend,**MVP 阶段均不鉴权**(device_id 透传,待补 JWT——见各接口详情)。当前**所有业务接口均无鉴权**,只有 `/me`、`/logout` 需 Bearer
---
+25
View File
@@ -0,0 +1,25 @@
# POST /api/v1/intent/recognize — 外卖比价 Phase 1 意图识别(透传到 pricebot
> 所属:Compare 组(前缀 `/api/v1`,外卖比价) | 鉴权:**无(MVP 阶段不鉴权)** | [← 返回 API 索引](./README.md)
## 入参
任意 JSON body,**不做 schema 校验**,原样透传给上游。后端仅从中读 `device_id``trace_id``step` 用于日志。
客户端实际传源平台购物车页的无障碍树采集结果(pricebot 协议里的 `screens`:`cart_page_1` / `cart_page_2`)。
## 出参
pricebot-backend 的响应**原样返回**JSON object)。典型含 `result`(店名)、`calibration`(含 `source_platform_id` / `items` / `price`),客户端在 `step=0` 把它透传进 `/price/step`
## 错误码
- `400` body 不是合法 JSON
- `502` pricebot 上游不可达(网络错误)或返回 5xx
## 说明
把请求体原样转发到 `PRICEBOT_BASE_URL``/api/intent/recognize`(去掉 `/v1`,async httpx)。**一次比价只调一次**。真正的识别逻辑(剪枝 + 索引 + LLM)在 **pricebot-backend(另一个 repo,GoalEngine**,本接口只是"透传壳"。
外卖比价由客户端无障碍引擎在源平台(淘宝闪购 / 美团 / 京东外卖)购物车页点悬浮球触发 → 调本接口拿 `query` + `calibration` → 进入 `/price/step` 循环。
⚠️ **MVP 阶段不鉴权**(同 `coupon/step`:`device_id` 透传给 pricebot 区分设备,后端拿不到 `user_id` → 行为暂绑不到登录用户。待补 JWT,见 [待办与技术债.md](../待办与技术债.md) P1。
**相关配置**:
- `PRICEBOT_BASE_URL`(默认 `http://localhost:8000`
- `PRICEBOT_COMPARE_TIMEOUT_SEC`(默认 60s,意图识别是大上下文 LLM,比领券的 30s 长)
+23
View File
@@ -0,0 +1,23 @@
# POST /api/v1/price/step — 外卖比价 Phase 2 步进(透传到 pricebot
> 所属:Compare 组(前缀 `/api/v1`,外卖比价) | 鉴权:**无(MVP 阶段不鉴权)** | [← 返回 API 索引](./README.md)
## 入参
任意 JSON body,**不做 schema 校验**,原样透传给上游。后端仅从中读 `device_id``trace_id``step` 用于日志。
客户端逐帧上报 `screen_state` + 上一步 `action_result`;`step=0` 还带 `query` + `calibration`(来自 Phase 1)。
## 出参
pricebot-backend 的响应**原样返回**JSON object)。含 `action`tap / set_text / launch / wait / done…)、`continue``status`;最终 `done` 帧带 `comparison_results`(源 + 各目标平台到手价,按价升序)。
## 错误码
- `400` body 不是合法 JSON
- `502` pricebot 上游不可达(网络错误)或返回 5xx
## 说明
把请求体原样转发到 `PRICEBOT_BASE_URL``/api/price/step`(去掉 `/v1`,async httpx)。**多轮循环**:客户端按返回的 `action` 操作手机、再上报下一帧,直到 `continue=false`。真正的目标驱动比价逻辑(多目标平台串行复现订单、读到手价、聚合排序)在 **pricebot-backend**,本接口只是"透传壳"。
⚠️ **MVP 阶段不鉴权**(同 `coupon/step`)。
**相关配置**:
- `PRICEBOT_BASE_URL`(默认 `http://localhost:8000`;生产部署应与 pricebot-backend 同内网——比价一单 30~80 步、逐帧多一跳,走公网延迟会累积)
- `PRICEBOT_COMPARE_TIMEOUT_SEC`(默认 60s,price/step 每帧都是 LLM