实现 M002 V3 入站解析与路由基线

This commit is contained in:
andy
2026-07-11 14:11:18 +08:00
parent c5e72078e5
commit 3589bd99c7
22 changed files with 2306 additions and 72 deletions

View File

@@ -4,9 +4,9 @@
| 项目 | 内容 |
| --- | --- |
| 文档版本 | 0.5 |
| 日期 | 2026-07-10 |
| 状态 | 已增加 S000/S999 特殊入口结果处理,并完成单酒店阶段 hotel_id 后端解析收口 |
| 文档版本 | 0.7 |
| 日期 | 2026-07-11 |
| 状态 | 当前代码契约已支持 V2 `ai_task_results[]` 兼容、结构化 S10/S99、V3 业务根基础解析、旧 S000/S999 兼容和单酒店 hotel_id 后端解析 |
| 适用范围 | SuperAgent 调用本系统查询上下文、查询邮件会话、提交 AI 任务结果 |
| 主要读者 | SuperAgent 对接方、后端、测试、运维 |
@@ -88,7 +88,7 @@ X-TH-Hotel-SuperAgent-Signature: sha256=<lowercase-hex-signature>
6. 使用共享 secret 重新计算 HMAC。
7. 常量时间比较签名。
8. 校验并记录 `client_id + nonce`,防止重放。
9. 鉴权和协议校验通过后再解析业务 JSON 或 S000/S999 文本结果。
9. 鉴权和协议校验通过后再解析业务 JSON、结构化 S10/S99、V3 业务根或 S000/S999 文本结果。
## 3. SourceMessage ID 口径
@@ -105,6 +105,25 @@ SuperAgent 不应知道或依赖内部 SourceMessage Inbox ID。任务结果通
查询接口 3、4 面向已经入库的邮件会话:缺省酒店由后端解析,`external_conversation_id` 最终仍按 `hotel_id + source_provider + source_channel + external_conversation_id` 查询;`source_message_id` 表示外部来源消息 ID可作为锚点反查该邮件所属会话。
## 3.1 M002 V3 迁移提醒
2026-07-11 起,项目需求基线已确认采用 `docs/project/requirements/M002-order-task-workflow-v3.md`
- 新入口结果将从旧文本 `S000/S999` 迁移为结构化 `S10/S99`
- 新业务输出将从旧 `ai_task_results[]` 迁移为 `source_message + message_events[] + case_candidates[] + extraction_warnings[] + unhandled_current_intents[]`
- 后端会完整保存 AI 三元组、`route_code` 和系统处理分类;`S10/S99` 仍以只读源邮件通知卡展示,任务列表可见,订单列表不可见。
-`S000/S999` 数据继续兼容展示,语义上分别映射到 `S10/S99`
当前后端已完成 M002 V3 CP1-CP2
- 已建立 42 条 P0 路由枚举 / 稳定配置。
- 已支持结构化 `S10/S99` 入站,创建只读 `SOURCE_MESSAGE_ONLY` 任务。
- 已支持 V3 业务根 `source_message + message_events[]` 的基础解析;可派生到现有任务模型的 event 会创建业务任务,无法派生、显式契约错误或基础 manual_review / parent split 结构不完整的 event 只落 `adapter_contract_error` transition不创建业务任务。
- 已支持 `unhandled_current_intents[]` 最小落库:只写 `UNHANDLED_CURRENT_INTENT` transition不创建业务任务也不按 adapter 契约错误返回。
- 已在 `workflow_reservation_ai_transition` 保存 `route_code``system_process_category``adapter_error_code``adapter_error_message`
尚未完成type-known manual review 同卡解阻、`manual_review.missing_fields[]` 到任务卡可编辑字段白名单的完整映射校验、typed `infrastructure_input_error` 响应、复核场景订单归属确认、真实 OPERA / OHIP、普通任务切换订单、任务列表 / 详情接口完整透出所有 V3 路由字段。
## 4. 接口 1查询订单上下文
### 4.1 请求
@@ -433,9 +452,100 @@ SuperAgent 不应知道或依赖内部 SourceMessage Inbox ID。任务结果通
| URL | `{TH_HOTEL_API_BASE_URL}/api/integrations/superagent/task-results` |
| request_path | `/api/integrations/superagent/task-results` |
| Content-Type | `application/json``text/plain` |
| 业务动作 | 接收 AI 任务结果JSON 写入业务订单和任务,S000/S999 创建只读特殊任务 |
| 业务动作 | 接收 AI 任务结果;V3 JSON 支持结构化 S10/S99 和业务根V2 JSON 继续兼容,旧 S000/S999 文本创建只读特殊任务 |
### 8.2 JSON 请求体
### 8.2 V3 S10/S99 结构化请求体
S10 示例:
```json
{
"source_message": {
"source_message_id": "mail-20260708-0001",
"subject": null,
"from": null,
"cc": [],
"received_at": null,
"source_channel": "Email"
},
"route_code": "S10",
"handler_type": "main_agent_outcome",
"result_type": "source_message_review_notification",
"current_or_history": "current",
"agent_assessment": {
"status": "no_booking_action_detected",
"reason_code": "no_booking_action_detected",
"automation_action": "none"
},
"notification": {
"required": true,
"notification_type": "source_message_review",
"show_source_message": true,
"requires_user_decision": true
},
"manual_review": null
}
```
S99 与 S10 使用相同结构,但 `route_code=S99``agent_assessment.status=material_package_unavailable`,且 `manual_review` 必须是完整入口复核对象。
### 8.3 V3 业务根请求体
```json
{
"source_message": {
"source_message_id": "mail-20260708-0002",
"subject": "New booking",
"from": null,
"cc": [],
"received_at": null,
"source_channel": "Email"
},
"message_events": [
{
"event_type": "New Booking",
"source_event_index": "E1",
"current_or_history": "current",
"case_keys": {
"group_code": null,
"confirmation_number": "CNF-001"
},
"extracted_fields": {
"booking_object_type": "FIT Reservation"
},
"manual_review": null
}
],
"case_candidates": [],
"extraction_warnings": [],
"unhandled_current_intents": []
}
```
V3 字段说明:
| 字段 | 是否必填 | 中文说明 |
| --- | --- | --- |
| `source_message.source_message_id` | 是 | 外部来源消息 ID对应 SourceMessage Inbox 的 `external_message_id`;缺失时返回技术错误且不落库 |
| `route_code` | S10/S99 必填 | 只允许 `S10``S99`,用于区分入口通知结果 |
| `result_type` | S10/S99 必填 | 固定为 `source_message_review_notification` |
| `message_events[]` | 业务根必填 | SuperAgent 最终业务事件列表,本系统逐 event 派生路由 |
| `message_events[].event_type` | 是 | V3 active event 或 `Need Manual Review` |
| `message_events[].source_event_index` | 建议必填 | 可为 `E1` 或数字;后端会归一为数字序号 |
| `message_events[].case_keys` | 否 | 订单关联候选键 |
| `message_events[].extracted_fields` | 否 | 业务字段主体和 subtype 判别字段 |
| `message_events[].manual_review` | 否 | `null` 表示普通任务;对象表示 type-known manual review |
| `unhandled_current_intents[]` | 否 | 第一版只保存 `UNHANDLED_CURRENT_INTENT` transition不自动创建业务任务 |
当前已支持的 V3 行为:
- 42 条 P0 路由进入后端枚举 / 稳定配置。
- 结构化 `S10/S99` 创建只读 `SOURCE_MESSAGE_ONLY` 任务,任务列表可见,订单列表不可见。
- 业务 event 能派生到稳定路由时,复用现有订单 / 任务 / 任务卡创建链路。
- event 判别字段不完整、显式携带 `contract_errors`、根 `missing_fields`、不完整 `manual_review` 或不完整 parent split 候选时,写入 `adapter_contract_error` transition不创建订单和任务同一邮件其他 sibling event 继续处理。
- `unhandled_current_intents[]` 写入 `UNHANDLED_CURRENT_INTENT` transition不返回 `adapter_error_code`
### 8.4 V2 JSON 兼容请求体
```json
{
@@ -481,7 +591,7 @@ SuperAgent 不应知道或依赖内部 SourceMessage Inbox ID。任务结果通
| `ai_task_results[].source_event_index` | 是 | AI current 事件序号 |
| `ai_task_results[].catalog_code` | 是 | Skill 目录代码 |
| `ai_task_results[].skill_id` | 是 | Skill 标识 |
| `ai_task_results[].result_type` | 是 | 新入口只接受 `normal_task``manual_review``informational_message` 仅历史兼容 |
| `ai_task_results[].result_type` | 是 | 当前代码契约只接受 `normal_task``manual_review``informational_message` 仅历史兼容 |
| `ai_task_results[].task_type` | 是 | AI 原始任务类型 |
| `ai_task_results[].task_subtype` | 否 | 业务动作 subtype |
| `ai_task_results[].case_keys` | 否 | 订单关联候选键 |
@@ -489,9 +599,9 @@ SuperAgent 不应知道或依赖内部 SourceMessage Inbox ID。任务结果通
正式联调时SuperAgent 不需要传 `hotel_id`。后端通过系统酒店 `hotel_id + source_provider(默认 AGENTBUS) + source_channel(默认 EMAIL) + source_message_id` 查找 `platform_source_message_inbox.external_message_id`。如果没有找到,返回 `SOURCE_MESSAGE_NOT_FOUND`。本地旧夹具允许在缺少 `hotel_id` 时使用内部数字 SourceMessage ID但该兼容路径不作为 SuperAgent 正式契约。
`informational_message` 结构化任务仅用于历史兼容。新入口如果是纯信息类邮件或无法形成业务素材包,不要提交空数组,也不要生成 `informational_message`;应使用下面的 S000/S999 文本请求体。
`informational_message` 结构化任务仅用于历史兼容。新数据如果是纯信息类邮件或无法形成业务素材包,应优先使用 V3 结构化 `S10/S99`;旧联调或兼容场景仍可使用下面的 `S000/S999` 文本请求体。
### 8.3 S000/S999 文本请求体
### 8.5 S000/S999 文本请求体
纯信息类邮件:
@@ -513,9 +623,9 @@ S999,mail-20260708-0001
| `S999` | 入口阶段无法形成业务素材包,不需要进入业务执行。 |
| `mail-20260708-0001` | 外部来源消息 ID对应 SourceMessage Inbox 的 `external_message_id`。 |
第一版 S000/S999 不在 body 里传 `hotel_id`,后端使用平台酒店表唯一 `ACTIVE` 酒店查询 SourceMessage Inbox。命中后创建 `SOURCE_MESSAGE_ONLY` 只读特殊任务:任务列表可见,订单列表不可见,不允许编辑、确认、转换订单、执行 OPERA 或重试 OPERA也不参与同订单任务执行顺序阻塞。
S000/S999 不在 body 里传 `hotel_id`,后端使用平台酒店表唯一 `ACTIVE` 酒店查询 SourceMessage Inbox。命中后创建 `SOURCE_MESSAGE_ONLY` 只读特殊任务:任务列表可见,订单列表不可见,不允许编辑、确认、转换订单、执行 OPERA 或重试 OPERA也不参与同订单任务执行顺序阻塞。该文本格式仅为兼容路径,新数据优先使用结构化 `S10/S99`
### 8.4 成功响应
### 8.6 成功响应
```json
{
@@ -529,6 +639,9 @@ S999,mail-20260708-0001
"source_event_index": 1,
"array_index": 1,
"ai_transition_id": "1900000000000000250",
"route_code": "R01_NEW_FIT_RESERVATION_NORMAL",
"system_process_category": "BUSINESS_TASK",
"adapter_error_code": null,
"order_id": "1900000000000000300",
"task_id": "1900000000000000400",
"system_task_type": "NEW_BOOKING",
@@ -556,6 +669,9 @@ S000/S999 成功响应示例:
"source_event_index": 1,
"array_index": 1,
"ai_transition_id": "1900000000000000550",
"route_code": "S10",
"system_process_category": "SOURCE_MESSAGE_NOTIFICATION",
"adapter_error_code": null,
"order_id": "1900000000000000600",
"task_id": "1900000000000000700",
"system_task_type": "SOURCE_MESSAGE_ONLY",
@@ -569,6 +685,26 @@ S000/S999 成功响应示例:
}
```
V3 `adapter_contract_error` 响应中的 `items[]` 不会包含 `order_id` / `task_id`
```json
{
"source_event_index": 2,
"array_index": 1,
"ai_transition_id": "1900000000000000800",
"route_code": null,
"system_process_category": "ADAPTER_CONTRACT_ERROR",
"adapter_error_code": "EVENT_ROUTE_UNSUPPORTED",
"order_id": null,
"task_id": null,
"system_task_type": "ADAPTER_CONTRACT_ERROR",
"task_card_type": "ADAPTER_CONTRACT_ERROR",
"task_status": null,
"order_status": null,
"execution_order": null
}
```
## 9. 错误响应
### 9.1 查询接口错误响应