987 lines
47 KiB
Markdown
987 lines
47 KiB
Markdown
# TH Hotel SuperAgent API 对接契约
|
||
|
||
## 文档信息
|
||
|
||
| 项目 | 内容 |
|
||
| --- | --- |
|
||
| 文档版本 | 0.10 |
|
||
| 日期 | 2026-07-20 |
|
||
| 状态 | 当前代码契约已支持 V4 订单任务 + 多卡入站、V4 S10/S99 来源通知、V2 `ai_task_results[]` 兼容、V3 业务根兼容、旧 S000/S999 兼容、M011 Booking Excel 调 SuperAgent 前预处理增强和单酒店 hotel_id 后端解析 |
|
||
| 适用范围 | SuperAgent 调用本系统查询上下文、查询邮件会话、提交 AI 任务结果 |
|
||
| 主要读者 | SuperAgent 对接方、后端、测试、运维 |
|
||
|
||
## 联调参数区
|
||
|
||
以下参数供 SuperAgent 联调时手动修改。该区块只用于 dev/test 联调,不作为生产 Secret 管理方式。
|
||
|
||
| 参数 | 当前联调值 | 中文说明 |
|
||
| --- | --- | --- |
|
||
| `TH_HOTEL_API_BASE_URL` | `http://8.138.234.141:18087` | 本系统后端基础地址;本地联调用 8080,部署环境改为实际网关或服务地址。 |
|
||
| `SYSTEM_HOTEL` | 后端平台酒店表唯一 `ACTIVE` 酒店 | SuperAgent 不需要配置或传入 `hotel_id`;单酒店阶段由 TH Hotel 后端从 `platform_hotel` 解析。 |
|
||
| `SUPERAGENT_CLIENT_ID` | `superagent-debug` | SuperAgent 调用方 ID,对应 Header `X-TH-Hotel-SuperAgent-Client-Id`。 |
|
||
| `SUPERAGENT_HMAC_SECRET` | `th-hotel-superagent-debug-20260709-change-before-prod` | dev/test 联调临时 HMAC 密钥;生产上线前必须更换为新的高强度随机密钥。 |
|
||
|
||
生产注意:
|
||
|
||
- 生产环境必须更换 `SUPERAGENT_HMAC_SECRET`,不得继续使用上述联调临时密钥。
|
||
- 生产密钥不得写入 SuperAgent skill 文件、仓库文档、前端代码、镜像或普通日志,只能通过部署 Secret / 环境变量注入。
|
||
- 本系统后端按 profile 优先读取环境专属密钥:dev 使用 `SUPERAGENT_DEV_TASK_RESULT_HMAC_SECRET`,test 使用 `SUPERAGENT_TEST_TASK_RESULT_HMAC_SECRET`,prod 使用 `SUPERAGENT_PROD_TASK_RESULT_HMAC_SECRET`;旧通用变量 `SUPERAGENT_TASK_RESULT_HMAC_SECRET` 仅作为兼容兜底。当前查询接口和任务结果通知接口共用同一个 HMAC secret。
|
||
|
||
## 1. 基础约定
|
||
|
||
请求地址先使用占位符:
|
||
|
||
```text
|
||
{TH_HOTEL_API_BASE_URL}
|
||
```
|
||
|
||
上线或联调时由环境提供实际域名,例如 UAT、生产内网域名或 API Gateway 地址。本文所有接口均为 SuperAgent 到本系统的服务到服务调用,不给前端直接调用。
|
||
|
||
## 2. 通用 HMAC 鉴权
|
||
|
||
查询接口和任务结果通知接口使用同一套 HMAC-SHA256 规则。
|
||
|
||
### 2.1 通用 Header
|
||
|
||
| Header | 是否必填 | 中文说明 |
|
||
| --- | --- | --- |
|
||
| `Content-Type` | 是 | 查询接口固定 `application/json`;任务结果通知接口支持 `application/json` 或 `text/plain` |
|
||
| `X-TH-Hotel-SuperAgent-Client-Id` | 是 | SuperAgent 调用方客户端 ID |
|
||
| `X-TH-Hotel-SuperAgent-Timestamp` | 是 | UTC ISO-8601 时间,例如 `2026-07-08T01:30:00Z` |
|
||
| `X-TH-Hotel-SuperAgent-Nonce` | 是 | 每次请求唯一随机值,用于防重放 |
|
||
| `X-TH-Hotel-SuperAgent-Signature` | 是 | HMAC 签名,格式 `sha256=<lowercase-hex>` |
|
||
| `X-TH-Hotel-Request-Id` | 否 | 调用方请求 ID,用于日志串联 |
|
||
| `X-TH-Hotel-AI-Trace-Id` | 否 | AI 运行链路 ID,查询接口会原样带回 `trace_id` |
|
||
|
||
### 2.2 签名串
|
||
|
||
签名串使用接口 path,不包含域名、query string 或 fragment。
|
||
|
||
```text
|
||
POST
|
||
<request_path>
|
||
<X-TH-Hotel-SuperAgent-Timestamp>
|
||
<X-TH-Hotel-SuperAgent-Nonce>
|
||
<X-TH-Hotel-SuperAgent-Client-Id>
|
||
<lowercase-hex-sha256-of-raw-body>
|
||
```
|
||
|
||
签名算法:
|
||
|
||
```text
|
||
signature = HMAC_SHA256(SUPERAGENT_HMAC_SECRET, canonical_string)
|
||
```
|
||
|
||
Header 写法:
|
||
|
||
```text
|
||
X-TH-Hotel-SuperAgent-Signature: sha256=<lowercase-hex-signature>
|
||
```
|
||
|
||
### 2.3 服务端校验顺序
|
||
|
||
1. 校验请求体大小。
|
||
2. 查询接口校验 `Content-Type` 是否为 `application/json`;任务结果通知接口校验是否为 `application/json` 或 `text/plain`,不支持的媒体类型不消耗 nonce。
|
||
3. 校验 HMAC 相关 Header 是否存在。
|
||
4. 校验 timestamp 是否在允许时间窗口内。
|
||
5. 计算原始请求体 SHA-256。
|
||
6. 使用共享 secret 重新计算 HMAC。
|
||
7. 常量时间比较签名。
|
||
8. 校验并记录 `client_id + nonce`,防止重放。
|
||
9. 鉴权和协议校验通过后再解析业务 JSON、V4 包、结构化 S10/S99、V3 业务根或 S000/S999 文本结果。
|
||
|
||
## 3. SourceMessage ID 口径
|
||
|
||
本系统存在两个容易混淆的 ID:
|
||
|
||
| 名称 | 中文说明 | 使用位置 |
|
||
| --- | --- | --- |
|
||
| 外部来源消息 ID | AgentBus 邮件 payload 中的 `source.external_message_id`,SuperAgent / Main Agent 在最终 JSON 中原样带回为 `source_message_id` | SuperAgent 任务结果通知接口入参和响应回显 |
|
||
| 内部 SourceMessage Inbox ID | `platform_source_message_inbox.id`,本系统数据库内部主键 | `workflow_*` 表的 `source_message_id` 外键、前端和运维排查 |
|
||
|
||
SuperAgent 不应知道或依赖内部 SourceMessage Inbox ID,也不需要为任务结果通知传数据库层 provider/channel。任务结果通知接口收到外部 `source_message_id` 后,后端先解析系统酒店,再使用 `hotel_id + external_message_id` 反查唯一 Inbox 记录,最后用内部 ID 写入业务表;真实 provider/channel 以 SourceMessage Inbox 入库值为准。
|
||
|
||
查询接口 1、2 在 SuperAgent 查询阶段不依赖当前邮件是否已经入库。若请求体兼容旧契约传入 `source_message_id` 或 `source_event_index`,第一版后端会接收但忽略,不校验它们的格式,也不把它们作为查询边界。
|
||
|
||
查询接口 3、4 面向已经入库的邮件会话:缺省酒店由后端解析,`external_conversation_id` 最终仍按 `hotel_id + source_provider + source_channel + external_conversation_id` 查询;`source_message_id` 表示外部来源消息 ID,可作为锚点反查该邮件所属会话。
|
||
|
||
## 3.1 M002 V3 / V4 迁移提醒
|
||
|
||
2026-07-11 起,项目需求基线已确认采用 `docs/project/requirements/M002-order-task-workflow-v3.md`;2026-07-12 起,Parent Group / Allotment 路由采用 `docs/project/requirements/M002-v3-p0.1-parent-group-routing-update.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`。
|
||
- 完整 Parent split 的父事件必须使用 `Cancel Allotment + cancel_allotment_control_block`;`relationship_type=linked_parent_release_after_child_split` 只用于关联和 Preflight,不再作为独立任务 subtype。
|
||
- 当前新入站不接受 `Cancel Booking + linked_parent_release_after_child_split` 作为合法业务任务;该组合仅允许历史数据只读兼容。
|
||
|
||
2026-07-18 起,M002 V4 以 `docs/project/requirements/M002-v4-agent-callback-field-contract.md` 和 `docs/project/requirements/M002-v4-order-task-card-domain-model-cp2.md` 为当前有效业务入站契约:
|
||
|
||
- V4 普通业务包使用 `route_code=null`、`source_message`、`order_contexts[]` 和 `message_events[]`。
|
||
- `source_message.source_message_id` 是外部来源消息 ID,对应 SourceMessage Inbox 的 `external_message_id`,不是本系统内部数据库 ID。
|
||
- 普通业务按 `source_message + order_ref` 创建 V4 订单任务,并在订单任务下创建来源消息只读卡、Basic Information 卡和业务事件卡;V4 后新业务主线不再创建旧 `workflow_reservation_task`。
|
||
- Basic Information 必须先确认;业务卡逐卡确认或复核解阻,确认后永久锁定;V4 第一版不提供前端草稿。
|
||
- `route_code=S10/S99` 使用 V4 来源通知模型,不创建隐藏技术订单,不进入订单详情时间线,不阻塞普通订单;前端只展示和 ack。
|
||
- Account / Market / Source、Room Type、Rate Code 以本系统数据库目录稳定代码为准;SuperAgent 不应输出显示文案作为业务判断依据。
|
||
- Rate Code 下一阶段按当前 `order_ref` 的 `basic_information.account_code` + event `target_order.booking_type`(GROUP / FIT)限定候选;SuperAgent 仍只输出稳定 `rate_code`,不输出价格、显示名或目录对象。
|
||
- Room Information 卡的 Nights、Breakfast、Group Booking Status、当前值 / 最终值 / 差异摘要由本系统后端展示模型提供;SuperAgent 不输出这些展示派生字段。
|
||
|
||
开发阶段不维护 V2/V3 旧任务兼容,测试数据可重建;该策略仅限开发 / 测试阶段,不代表生产迁移方案。生产数据迁移策略不在当前 checkpoint 处理,后续上线前另开迁移方案。
|
||
|
||
当前后端仍兼容 M002 V3 CP1-CP6 历史入口代码路径,但不作为开发阶段新业务验收重点:
|
||
|
||
- 已建立 40 条 P0.1 路由枚举 / 稳定配置;`route_code` 保持历史稳定,不按总数连续重编号,`R41/R42` 仍可能出现在响应和历史 transition 中。
|
||
- 已支持结构化 `S10/S99` 历史入站兼容;当前 V4 新入站使用来源通知模型。
|
||
- 已支持 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 同卡解阻、当前订单归属确认、P0 fixtures 回归测试和 V3 typed `infrastructure_input_error` 响应。
|
||
|
||
尚未完成:真实 OPERA / OHIP、普通任务切换订单、历史旧 Parent Cancel Booking payload 批量迁移、真实 PMS / OPERA / OHIP 目录同步和 SuperAgent 目录机器接口。
|
||
|
||
## 3.2 M011 Booking Excel 预处理输入增强
|
||
|
||
M011 已在 Debug EML 和 AgentBus 自动分发链路中接入 Booking Excel 附件预处理。该能力发生在 TH Hotel 后端调用 SuperAgent Open API 前,不属于 SuperAgent 调本系统的 `task-results` 请求体字段,但会影响 SuperAgent 实际看到的邮件 payload。
|
||
|
||
Debug EML 是人工调试入口,不进入 AgentBus 生产 dispatch。M002 V4 smoke 下,Debug EML 默认使用实时 AgentBus V4 Open API subject,期望 SuperAgent 后续按本契约调用 `task-results` 或 MCP 写入 V4 业务结果;历史 Debug V2/V3 profile 只能显式配置用于旧链路排查,不得作为 V4 smoke 默认入口。
|
||
|
||
处理边界:
|
||
|
||
- 后端只处理邮件附件中的 `.xls` / `.xlsx`,识别并排除 `PASSENGER_ROSTER` 人员名单类 Excel。
|
||
- 对 `BOOKING_UPDATE` 和 `BOOKING_SURCHARGE` 类 Excel,按最近 6 个月候选窗口选择文件内实际存在的最新 3 个业务月,并抽取有背景色标记的业务行。
|
||
- 非空结果追加到 AgentBus Outlook-like payload 的 `attachment_extractions[]` 字段,供 SuperAgent 作为证据输入。
|
||
- `attachment_extractions[]` 只增强 SuperAgent 判断上下文,不直接创建订单、订单任务、任务卡、客户回复或 OPERA / OHIP 操作。
|
||
- 测试机 AgentBus 增强已开启;生产 AgentBus 增强默认关闭。只有 `reservation.booking-excel-extraction.enabled` 和 AgentBus dispatch include 开关同时开启时才会追加该字段。
|
||
|
||
字段契约、抽取规则和安全边界以 `docs/project/requirements/M011-booking-excel-pre-superagent-enrichment-v1.md` 为准。SuperAgent 生成最终 V4 结果时,仍必须按本文第 8 节的 `source_message + order_contexts[] + message_events[]` 契约回调本系统。
|
||
|
||
## 4. 接口 1:查询订单上下文
|
||
|
||
### 4.1 请求
|
||
|
||
| 项目 | 内容 |
|
||
| --- | --- |
|
||
| Method | `POST` |
|
||
| URL | `{TH_HOTEL_API_BASE_URL}/api/ai-query/v1/case-context` |
|
||
| request_path | `/api/ai-query/v1/case-context` |
|
||
| Content-Type | `application/json` |
|
||
| 业务动作 | 只读查询,不创建任务、不修改订单、不写 OPERA |
|
||
|
||
### 4.2 请求体
|
||
|
||
```json
|
||
{
|
||
"group_code": "GRP-001",
|
||
"confirmation_number": null,
|
||
"reservation_no": null,
|
||
"object_type_hint": "group_block",
|
||
"target_key_source": "body_current",
|
||
"body_thread_used_only_as_evidence": false
|
||
}
|
||
```
|
||
|
||
字段说明:
|
||
|
||
| 字段 | 是否必填 | 中文说明 |
|
||
| --- | --- | --- |
|
||
| `hotel_id` | 否 | 酒店上下文 ID;SuperAgent 默认不传,后端按平台酒店表唯一 `ACTIVE` 酒店解析。若兼容旧契约传入,单酒店阶段必须与系统酒店一致。 |
|
||
| `group_code` | 条件必填 | Group / Allotment 查询 key |
|
||
| `confirmation_number` | 条件必填 | FIT Confirmation Number 查询 key |
|
||
| `reservation_no` | 条件必填 | OPERA reservation no;当前系统无可靠表源,只传该字段时会返回人工复核原因 |
|
||
| `object_type_hint` | 否 | 调用方推测的对象类型,只作为提示 |
|
||
| `target_key_source` | 否 | key 来源,例如 `body_current`、`body_thread_evidence` |
|
||
| `body_thread_used_only_as_evidence` | 否 | 历史线程 key 是否仅作为证据 |
|
||
|
||
`group_code`、`confirmation_number`、`reservation_no` 至少一个非空。当前稳定查询能力优先支持 `group_code` 和 `confirmation_number`。
|
||
|
||
全局上下文查询最终依赖“后端解析出的酒店 ID + 业务 key”;`source_message_id` 和 `source_event_index` 不作为查询边界,传入时也不会影响查询结果。
|
||
|
||
### 4.3 成功响应
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"request_id": "req-001",
|
||
"trace_id": "trace-001",
|
||
"data": {
|
||
"matched_order_records": [],
|
||
"pending_or_open_tasks": [],
|
||
"active_workflows": [],
|
||
"terminated_records": [],
|
||
"target_object_validation": {
|
||
"status": "none",
|
||
"matched_object_id": null,
|
||
"matched_object_type": null,
|
||
"can_create_new_booking_task": true,
|
||
"can_create_update_task": false,
|
||
"can_create_cancel_task": false,
|
||
"can_attach_voucher": false,
|
||
"can_attach_rooming_list": false,
|
||
"needs_manual_review_reason": null
|
||
},
|
||
"key_relationships": {
|
||
"group_code_and_confirmation_same_object": null,
|
||
"relationship_evidence": ""
|
||
}
|
||
},
|
||
"warnings": [],
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
### 4.4 主要数据来源
|
||
|
||
| 返回字段 | 来源 |
|
||
| --- | --- |
|
||
| `matched_order_records[]` | `workflow_reservation_order` |
|
||
| `pending_or_open_tasks[]` | `workflow_reservation_task` + `workflow_reservation_ai_transition` |
|
||
| `terminated_records[]` | 订单 `ENDED` / `LOGIC_DELETED`,任务 `FAILED` / `COMPLETED` |
|
||
| `active_workflows[]` | 当前无独立 workflow 表,固定空数组 |
|
||
|
||
## 5. 接口 2:查询对象详情
|
||
|
||
### 5.1 请求
|
||
|
||
| 项目 | 内容 |
|
||
| --- | --- |
|
||
| Method | `POST` |
|
||
| URL | `{TH_HOTEL_API_BASE_URL}/api/ai-query/v1/object-detail` |
|
||
| request_path | `/api/ai-query/v1/object-detail` |
|
||
| Content-Type | `application/json` |
|
||
| 业务动作 | 只读查询对象详情,不创建任务、不修改订单、不写 OPERA |
|
||
|
||
### 5.2 请求体
|
||
|
||
```json
|
||
{
|
||
"object_id": "ORDER:1900000000000000100",
|
||
"object_type": "group_block"
|
||
}
|
||
```
|
||
|
||
字段说明:
|
||
|
||
| 字段 | 是否必填 | 中文说明 |
|
||
| --- | --- | --- |
|
||
| `hotel_id` | 否 | 酒店上下文 ID;SuperAgent 默认不传,后端解析系统酒店。 |
|
||
| `object_id` | 是 | 查询对象 ID,第一版只支持 `ORDER:{order_id}` |
|
||
| `object_type` | 否 | 调用方对象类型提示,第一版不作为强校验 |
|
||
|
||
### 5.3 成功响应
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"request_id": "req-002",
|
||
"trace_id": "trace-001",
|
||
"data": {
|
||
"object_id": "ORDER:1900000000000000100",
|
||
"object_type": "group_block",
|
||
"order_id": "1900000000000000100",
|
||
"order_key_type": "GROUP_CODE",
|
||
"group_code": "GRP-001",
|
||
"confirmation_number": null,
|
||
"reservation_no": null,
|
||
"block_id": null,
|
||
"temporary_order_code": "TMP-1900000000000000100",
|
||
"display_name": "GRP-001",
|
||
"status": "ACTIVE",
|
||
"source_message_id": "1900000000000000001",
|
||
"created_from_task_id": null,
|
||
"created_at": "2026-07-08T01:00:00Z",
|
||
"last_updated_at": "2026-07-08T01:10:00Z",
|
||
"arrival_date": null,
|
||
"departure_date": null,
|
||
"nights": null,
|
||
"guest_count": null,
|
||
"room_items": [],
|
||
"rate_code": null,
|
||
"rate_code_price": null,
|
||
"reservation_type": null,
|
||
"cancel_status": "not_cancelled",
|
||
"can_update": true,
|
||
"can_cancel": true,
|
||
"hard_validation_warnings": [
|
||
{
|
||
"code": "OPERA_PROJECTION_UNAVAILABLE",
|
||
"message": "当前系统尚未接入 OPERA 对象投影,日期、房型、房价等字段无法确认。"
|
||
}
|
||
]
|
||
},
|
||
"warnings": [],
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
说明:接口 2 响应中的 `source_message_id` 当前是本系统内部 SourceMessage Inbox ID,用于对象溯源和排查;不要把该字段当作 SuperAgent 任务结果通知接口的外部 `source_message_id` 使用。
|
||
|
||
## 6. 接口 3:查询邮件会话下所有任务
|
||
|
||
### 6.1 请求
|
||
|
||
| 项目 | 内容 |
|
||
| --- | --- |
|
||
| Method | `POST` |
|
||
| URL | `{TH_HOTEL_API_BASE_URL}/api/ai-query/v1/message-conversation/tasks` |
|
||
| request_path | `/api/ai-query/v1/message-conversation/tasks` |
|
||
| Content-Type | `application/json` |
|
||
| 业务动作 | 只读查询邮件会话下任务,不创建任务、不修改订单、不写 OPERA |
|
||
|
||
### 6.2 请求体
|
||
|
||
按外部邮件会话 ID 查询:
|
||
|
||
```json
|
||
{
|
||
"source_provider": "AGENTBUS",
|
||
"source_channel": "EMAIL",
|
||
"external_conversation_id": "thread-20260708-0001"
|
||
}
|
||
```
|
||
|
||
按外部来源消息 ID 作为锚点反查会话:
|
||
|
||
```json
|
||
{
|
||
"source_provider": "AGENTBUS",
|
||
"source_channel": "EMAIL",
|
||
"source_message_id": "mail-20260708-0001"
|
||
}
|
||
```
|
||
|
||
字段说明:
|
||
|
||
| 字段 | 是否必填 | 中文说明 |
|
||
| --- | --- | --- |
|
||
| `hotel_id` | 否 | 酒店上下文 ID;SuperAgent 默认不传,后端解析系统酒店。 |
|
||
| `source_provider` | 否 | 来源提供方,按会话 ID 查询和按 `source_message_id` 反查时都参与隔离,缺省为 `AGENTBUS` |
|
||
| `source_channel` | 否 | 来源渠道,按会话 ID 查询和按 `source_message_id` 反查时都参与隔离,缺省为 `EMAIL` |
|
||
| `external_conversation_id` | 条件必填 | 外部邮件会话 ID,对应 AgentBus `source.external_conversation_id` |
|
||
| `source_message_id` | 条件必填 | 外部来源消息 ID,对应 AgentBus `source.external_message_id`,不是内部 Inbox ID |
|
||
|
||
`external_conversation_id`、`source_message_id` 至少一个非空。两者同时传入时,第一版以后端直接按 `external_conversation_id` 查询为准。
|
||
|
||
### 6.3 成功响应
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"request_id": "req-003",
|
||
"trace_id": "trace-001",
|
||
"data": {
|
||
"hotel_id": "HOTEL-DEV",
|
||
"external_conversation_id": "thread-20260708-0001",
|
||
"task_count": 2,
|
||
"tasks": [
|
||
{
|
||
"task_id": "1900000000000000400",
|
||
"order_id": "1900000000000000300",
|
||
"external_source_message_id": "mail-20260708-0001",
|
||
"external_conversation_id": "thread-20260708-0001",
|
||
"source_received_at": "2026-07-08T01:00:00Z",
|
||
"source_event_index": 1,
|
||
"catalog_code": "S02",
|
||
"skill_id": "update_booking_amendment_skill",
|
||
"result_type": "normal_task",
|
||
"task_type": "Update Booking",
|
||
"system_task_type": "UPDATE_BOOKING",
|
||
"task_card_type": "UPDATE_BOOKING",
|
||
"task_subtype": "update_stay_dates",
|
||
"task_status": "PENDING_CONFIRM",
|
||
"queue_participation": true,
|
||
"execution_order": 1,
|
||
"parent_task_id": null,
|
||
"parent_source_event_index": null,
|
||
"linked_task_group_id": null,
|
||
"blocked_until_parent_completed": false,
|
||
"completed_at": null,
|
||
"task_created_at": "2026-07-08T01:01:00Z",
|
||
"task_updated_at": "2026-07-08T01:01:00Z"
|
||
}
|
||
]
|
||
},
|
||
"warnings": [],
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
排序规则:
|
||
|
||
1. 先按邮件 `received_at` 正序。
|
||
2. 同一封邮件下,再按任务 `created_at` 正序。
|
||
3. 若时间相同,再按 `task_id` 正序稳定排序。
|
||
|
||
## 7. 接口 4:查询邮件会话下所有受控正文
|
||
|
||
### 7.1 请求
|
||
|
||
| 项目 | 内容 |
|
||
| --- | --- |
|
||
| Method | `POST` |
|
||
| URL | `{TH_HOTEL_API_BASE_URL}/api/ai-query/v1/message-conversation/messages` |
|
||
| request_path | `/api/ai-query/v1/message-conversation/messages` |
|
||
| Content-Type | `application/json` |
|
||
| 业务动作 | 只读查询邮件会话受控正文,不返回附件 URL 或原始未清洗 HTML |
|
||
|
||
### 7.2 请求体
|
||
|
||
请求体字段与接口 3 相同,可按 `external_conversation_id` 查询,也可按外部 `source_message_id` 锚点反查会话。
|
||
|
||
```json
|
||
{
|
||
"source_provider": "AGENTBUS",
|
||
"source_channel": "EMAIL",
|
||
"source_message_id": "mail-20260708-0001"
|
||
}
|
||
```
|
||
|
||
### 7.3 成功响应
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"request_id": "req-004",
|
||
"trace_id": "trace-001",
|
||
"data": {
|
||
"hotel_id": "HOTEL-DEV",
|
||
"external_conversation_id": "thread-20260708-0001",
|
||
"message_count": 2,
|
||
"messages": [
|
||
{
|
||
"external_source_message_id": "mail-20260708-0001",
|
||
"external_conversation_id": "thread-20260708-0001",
|
||
"sender_summary": "guest@example.test",
|
||
"subject": "Booking update",
|
||
"received_at": "2026-07-08T01:00:00Z",
|
||
"source_sent_at": "2026-07-08T00:59:00Z",
|
||
"text_body": "Please update arrival date...",
|
||
"html_body_sanitized": "<html><body>Please update arrival date...</body></html>",
|
||
"html_sanitize_required": true,
|
||
"html_render_mode": "SANITIZED_HTML"
|
||
}
|
||
]
|
||
},
|
||
"warnings": [],
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
安全边界:
|
||
|
||
- `messages[]` 按邮件 `received_at` 正序返回。
|
||
- 不返回 `html_body` 原始未清洗 HTML。
|
||
- 不返回 `attachments`、`inline_images`、`external_url`、附件 URL 或 HTML 中的 `href/src` 外链属性。
|
||
- 后端读取正文时会写入 SourceMessage 原文访问审计。
|
||
|
||
## 8. 接口 5:SuperAgent 通知 AI 任务结果
|
||
|
||
### 8.1 请求
|
||
|
||
| 项目 | 内容 |
|
||
| --- | --- |
|
||
| Method | `POST` |
|
||
| 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 任务结果;V4 JSON 支持 S10/S99 和业务根入站基线,V3 / V2 JSON 继续兼容,旧 S000/S999 文本创建只读特殊任务 |
|
||
|
||
### 8.2 V4 JSON 请求体(当前推荐)
|
||
|
||
V4 普通业务包示例:
|
||
|
||
```json
|
||
{
|
||
"route_code": null,
|
||
"source_message": {
|
||
"source_message_id": "mail-20260718-0001",
|
||
"conversation_id": "thread-001",
|
||
"subject": "Group booking and payment",
|
||
"sender": "agent@example.test",
|
||
"sent_at": "2026-07-18T02:10:00Z",
|
||
"body": "Please create group GRP-001 and note payment attached.",
|
||
"body_content_type": "text/plain",
|
||
"attachments": [
|
||
{
|
||
"id": "att-pay-1",
|
||
"name": "payment-slip.jpg",
|
||
"content_type": "image/jpeg",
|
||
"url": "https://oss.example.test/payment-slip.jpg",
|
||
"size": 251524
|
||
}
|
||
]
|
||
},
|
||
"order_contexts": [
|
||
{
|
||
"order_ref": "order-1",
|
||
"basic_information": {
|
||
"account_code": "QBD_TRAVEL",
|
||
"manual_review": null
|
||
}
|
||
}
|
||
],
|
||
"message_events": [
|
||
{
|
||
"order_ref": "order-1",
|
||
"event_type": "NEW_BOOKING",
|
||
"target_order": {
|
||
"booking_type": "GROUP",
|
||
"locator_type": "GROUP_CODE",
|
||
"locator_value": "GRP-001"
|
||
},
|
||
"arrival_date": "2026-07-26",
|
||
"departure_date": "2026-07-29",
|
||
"rate_code": "GRPA1",
|
||
"booking_scenario": "STANDARD",
|
||
"room_items": [
|
||
{
|
||
"room_type_code": "TWN",
|
||
"room_count": 2
|
||
}
|
||
],
|
||
"manual_review": null
|
||
},
|
||
{
|
||
"order_ref": "order-1",
|
||
"event_type": "PAYMENT",
|
||
"target_order": {
|
||
"booking_type": "GROUP",
|
||
"locator_type": "GROUP_CODE",
|
||
"locator_value": "GRP-001"
|
||
},
|
||
"attachment_ids": ["att-pay-1"],
|
||
"manual_review": null
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
V4 纯通知包示例:
|
||
|
||
```json
|
||
{
|
||
"route_code": "S10",
|
||
"source_message": {
|
||
"source_message_id": "mail-20260718-0002",
|
||
"conversation_id": "thread-002",
|
||
"subject": "Thanks",
|
||
"sender": "guest@example.test",
|
||
"sent_at": "2026-07-18T02:10:00Z",
|
||
"body": "Noted with thanks.",
|
||
"body_content_type": "text/plain",
|
||
"attachments": []
|
||
},
|
||
"order_contexts": [],
|
||
"message_events": []
|
||
}
|
||
```
|
||
|
||
V4 字段说明:
|
||
|
||
| 字段 | 是否必填 | 中文说明 |
|
||
| --- | --- | --- |
|
||
| `route_code` | 是 | 普通业务固定为 `null`;入口通知第一版支持 `S10/S99`,当前新业务语义以 `S10` 为准,`S99` 仅作为兼容入口保留。 |
|
||
| `source_message.source_message_id` | 是 | 外部来源消息 ID,对应 SourceMessage Inbox 的 `external_message_id`。 |
|
||
| `source_message.body_content_type` | 是 | 只能是 `text/plain` 或 `text/html`。 |
|
||
| `source_message.attachments[]` | 是 | 包级附件数组;每个附件必须有 `id/name/content_type/url`,`size` 可省略或为 `null`。 |
|
||
| `order_contexts[]` | 普通业务必填 | 每个 `order_ref` 一项,第一版必须带 `basic_information.account_code` 和 `basic_information.manual_review`。 |
|
||
| `message_events[]` | 普通业务必填 | 逐 event 入站,后端按数组顺序处理。 |
|
||
| `message_events[].event_type` | 是 | 第一版支持 `NEW_BOOKING`、`UPDATE_BOOKING`、`CANCEL_BOOKING`、`TRACE_RESERVATION_NOTES`、`ROOMING_LIST`、`PAYMENT`。 |
|
||
| `message_events[].target_order` | 是 | `GROUP + GROUP_CODE`,或 `FIT + BOOKING_CODE / CONFIRMATION_NUMBER`。 |
|
||
| `message_events[].rate_code` | NEW_BOOKING 必填,可为 `null` | Rate Code 稳定 code;下一阶段必须属于同 `order_ref` 的 Basic Information Account + 当前 `booking_type` 的适用范围。 |
|
||
| `message_events[].manual_review` | 是 | 只能是 `null` 或布尔 `true`;`true` 必须能由当前对象中的未解决字段解释。 |
|
||
| Room Information 展示派生字段 | 不需要 | Nights、Breakfast、Group Booking Status、Adult、Block ID、Confirmation Number、当前订单值和差异摘要均由本系统后端查询或派生;SuperAgent 不输出。 |
|
||
| New Booking 最终订单展示名 | 不需要 | Group Block Name / Fit Name 是信息系统最终订单投影字段,可由用户在 V4 任务卡中确认前编辑;Group 默认来自 `target_order.locator_value` 且 `locator_type=GROUP_CODE`,Fit 默认来自 `guest_name ?? target_order.locator_value`;SuperAgent 仍只输出 `target_order.locator_value` 作为目标定位线索,系统不得回写修改该原始定位值。 |
|
||
| `TRACE_RESERVATION_NOTES.trace_items[].department_code` | Trace 必填 | 第一版固定为 `FO` / `HSK` / `FO+HSK`,不接受自由文本;正式 Department 目录后续再扩展。 |
|
||
| `ROOMING_LIST` 专属业务字段 | 不需要 | 第一版只识别 Rooming List 事项并生成可确认任务卡;SuperAgent 不输出名单 rows、同住分组、附件 ID、Excel 或 PMS 导入参数。 |
|
||
| `PAYMENT.attachment_ids[]` | PAYMENT 必填 | 必须引用同包 `source_message.attachments[].id`;SuperAgent 不在 PAYMENT event 内复制附件名称、URL 或完整附件对象。第一版这些 ID 是只读业务事实,用户只确认卡片,不增删或替换附件集合;前端图片缩略图 / 大图预览和非图片下载由本系统根据这些 ID 匹配 SourceMessage 附件后提供。 |
|
||
|
||
当前已支持的 V4 行为:
|
||
|
||
- 命中 SourceMessage 后保存 AI batch / transition,并按 `source_message + order_contexts[] + message_events[]` 处理。
|
||
- 普通业务包按 `source_message + order_ref` 创建 V4 订单任务,并创建 `SOURCE_MESSAGE_DISPLAY`、`BASIC_INFORMATION` 和业务事件卡。
|
||
- Basic Information 必须先确认;业务卡逐卡确认或复核解阻,确认后永久锁定;V4 第一版不提供前端草稿。
|
||
- `ROOM_INFORMATION` 卡只由 `NEW_BOOKING`、`UPDATE_BOOKING`、`CANCEL_BOOKING` 触发;New / Update / Cancel 的最终值、差异、Nights、Breakfast、Group Booking Status 和本地订单投影展示由本系统后端展示模型提供,不扩大 SuperAgent 输入契约。
|
||
- `ROOMING_LIST` 卡第一版只做事项确认;用户点击“确认卡片”表示已人工处理,不代表名单已解析、Excel 已生成或 PMS 已导入。确认后的 Group Booking Status 自动置 `DEF` 是本系统后端联动,不要求 SuperAgent 在 `ROOMING_LIST` event 中额外输出字段。
|
||
- `route_code=S10/S99` 创建 V4 来源通知,工作台可见,订单列表和订单详情不可见;来源通知只能 ack,不创建订单、不阻塞订单。
|
||
- V4 包级结构错误如果仍能通过 `source_message.source_message_id` 定位 SourceMessage,会返回成功接收并写入 `adapter_contract_error` transition;不创建订单任务、任务卡或来源通知。`source_message_id` 缺失或找不到 SourceMessage 时仍返回明确错误。
|
||
- `PAYMENT.attachment_ids[]` 引用不存在的附件、`UPDATE_BOOKING` 携带不允许字段、目录代码无法匹配当前酒店数据库目录,以及其他 V4 event 契约错误,只写 `adapter_contract_error` transition,不创建用户可处理业务任务。
|
||
- 技术契约错误不会自动转为 S10/S99,也不会创建前端可处理业务任务。
|
||
|
||
当前仍未完成:Payment 卡附件安全摘要和预览 / 下载联动、Account + booking type 过滤 Rate Code 的后端 lookup / 校验和前端联动、真实 OPERA / OHIP、真实 PMS / OPERA / OHIP 目录同步、普通任务切换订单、SuperAgent 目录机器接口。
|
||
|
||
### 8.3 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,
|
||
"visible_message": "未匹配到当前 Agent 支持的业务事件类型,请查看原邮件并决定是否需要回复或进行其他处理。"
|
||
},
|
||
"manual_review": null
|
||
}
|
||
```
|
||
|
||
S99 与 S10 使用相同结构,但 `route_code=S99`,`agent_assessment.status=material_package_unavailable`。当前 V4 来源通知第一版不输出入口 `manual_review` 对象,`manual_review` 固定为 `null`;历史 V3 / 0711 入口复核对象只作为兼容资料,不作为 V4 新数据契约。
|
||
|
||
### 8.4 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",
|
||
"event_role": "travel_agent_request",
|
||
"source_event_index": "E1",
|
||
"current_or_history": "current",
|
||
"case_keys": {
|
||
"group_code": null,
|
||
"confirmation_number": "CNF-001",
|
||
"reservation_number": null,
|
||
"block_code": null
|
||
},
|
||
"relevant_message_excerpt": "Please create a new FIT reservation.",
|
||
"attachments": [],
|
||
"file_references": [],
|
||
"context_used": {},
|
||
"extracted_fields": {
|
||
"booking_object_type": "FIT Reservation",
|
||
"arrival_date": "2026-09-01",
|
||
"departure_date": "2026-09-03",
|
||
"room_quantity": 2,
|
||
"room_type": "Deluxe King",
|
||
"pms_room_type_code": "RM2"
|
||
},
|
||
"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[].event_role` | 是 | 事件来源角色,第一版必须是非空字符串 |
|
||
| `message_events[].source_event_index` | 是 | 可为 `E1` 或数字;后端会归一为数字序号 |
|
||
| `message_events[].current_or_history` | 是 | 第一版只接受 `current` |
|
||
| `message_events[].case_keys` | 是 | 必须包含 `group_code`、`confirmation_number`、`reservation_number`、`block_code` 四个字段,值为 string 或 null |
|
||
| `message_events[].relevant_message_excerpt` | 是 | 当前事件的邮件证据摘录,必须是字符串 |
|
||
| `message_events[].attachments` | 是 | 当前事件引用附件数组,无附件传空数组 |
|
||
| `message_events[].file_references` | 是 | 当前事件引用文件数组,无文件传空数组 |
|
||
| `message_events[].context_used` | 是 | 当前事件使用的上下文对象,无上下文传空对象 |
|
||
| `message_events[].extracted_fields` | 是 | 业务字段主体和 subtype 判别字段,必须是对象 |
|
||
| `message_events[].manual_review` | 否 | `null` 表示普通任务;对象表示 type-known manual review |
|
||
| `unhandled_current_intents[]` | 否 | 第一版只保存 `UNHANDLED_CURRENT_INTENT` transition,不自动创建业务任务 |
|
||
|
||
当前已支持的 V3 行为:
|
||
|
||
- 如果通过 MCP `th_hotel_submit_task_results` 调用,MCP adapter 会在进入业务入站服务前按 `message_events[]` 顺序把 Agent 内部事件 ID 映射为本系统一基 `source_event_index`,并校验 `related_source_event_index`、`parent_source_event_index` 和 `related_source_event_indices[]` 是否悬空或重复;成功响应会额外返回 `mapping_diagnostics`,该诊断不写入业务 JSON。
|
||
- MCP adapter 只拦 transport 层问题,例如未知字段、缺失或重复 `source_event_index`、关系引用悬空或重复、V2 item schema 不完整;V3 event 的业务契约问题,例如 unsupported `event_type`、不完整 `case_keys`、不完整 `manual_review`,继续由业务入站层保存为 `adapter_contract_error` transition。
|
||
- MCP 路径缺失 `source_message.source_message_id` 或整个 `source_message` 时,保留业务入站层 `MISSING_SOURCE_MESSAGE_ID` 错误语义。
|
||
- 40 条 P0.1 路由进入后端枚举 / 稳定配置。
|
||
- `route_code` 是稳定代码,不因路由总数从 42 调整为 40 而重编号;联调方不要按数字连续性判断合法性。
|
||
- V3 结构化 `S10/S99` 兼容路径创建只读 `SOURCE_MESSAGE_ONLY` 任务,任务列表可见,订单列表不可见;V4 新入站不走该模型,改用来源通知。
|
||
- 业务 event 能派生到稳定路由时,复用现有订单 / 任务 / 任务卡创建链路。
|
||
- 完整 Parent split 父事件必须提交为 `event_type=Cancel Allotment`、`extracted_fields.cancel_scope=entire_allotment_control_block`、`task_subtype=cancel_allotment_control_block`,并保留 `relationship_type=linked_parent_release_after_child_split` 作为关系字段。
|
||
- 当前新入站若提交 `event_type=Cancel Booking` 且 `relationship_type=linked_parent_release_after_child_split`,写入 `adapter_contract_error` transition,不创建业务任务;旧 V2 兼容 `ai_task_results[]` 中的同等三元组按请求级 `ADAPTER_CONTRACT_ERROR` 拒绝。
|
||
- 同一个 Parent split cluster 重复提交 Parent 候选时,后续重复 Parent 写入 `adapter_contract_error` transition,不创建第二张 Parent 业务任务。
|
||
- 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`。
|
||
- V3 `message_events[].relevant_message_excerpt` 入站后会归一化到任务卡 AI payload 根路径,供旧字段矩阵读取证据字段;SuperAgent 仍只需要按 V3 event 契约提供该字段。
|
||
- type-known manual review 第一版在同一业务任务卡解阻;New Booking 房型字段主路径已迁移为 `room_items[0]`,例如 `/extracted_fields/room_items/0/pms_room_type_code`。旧扁平字段仍可作为过渡提交 key,解阻接口也支持提交 P0 主 `field_path` 或旧扁平 `field_path`,响应会归一化为 P0 主 `field_path`。
|
||
|
||
`source_message.source_message_id` 缺失时返回 `HTTP 400`,响应体不使用通用错误包装:
|
||
|
||
```json
|
||
{
|
||
"result_type": "infrastructure_input_error",
|
||
"error_code": "missing_source_message_id",
|
||
"retryable": true,
|
||
"missing_fields": [
|
||
"source_message.source_message_id"
|
||
]
|
||
}
|
||
```
|
||
|
||
### 8.5 V2 JSON 兼容请求体
|
||
|
||
```json
|
||
{
|
||
"source_message_id": "mail-20260708-0001",
|
||
"ai_task_results": [
|
||
{
|
||
"source_event_index": 1,
|
||
"catalog_code": "S01",
|
||
"skill_id": "S01_new_booking_skill",
|
||
"result_type": "normal_task",
|
||
"task_type": "New Booking",
|
||
"task_subtype": "new_fit_reservation",
|
||
"current_or_history": "current",
|
||
"case_keys": {
|
||
"group_code": null,
|
||
"confirmation_number": null
|
||
},
|
||
"visible_reason": "邮件正文包含新建预订请求。",
|
||
"relevant_message_excerpt": "Please create a new booking...",
|
||
"attachments": [],
|
||
"file_references": [],
|
||
"context_used": {},
|
||
"extracted_fields": {},
|
||
"manual_review": null,
|
||
"informational_message": null,
|
||
"additional_operations": [],
|
||
"idempotency_key": null
|
||
}
|
||
],
|
||
"extraction_warnings": []
|
||
}
|
||
```
|
||
|
||
字段说明:
|
||
|
||
| 字段 | 是否必填 | 中文说明 |
|
||
| --- | --- | --- |
|
||
| `hotel_id` | 否 | 酒店上下文 ID;SuperAgent 默认不传,后端解析系统酒店后用于反查 SourceMessage Inbox 幂等键。若兼容旧契约传入,单酒店阶段必须与系统酒店一致。 |
|
||
| `source_message_id` | 是 | SuperAgent / Main Agent 原样带回的外部来源消息 ID,对应 AgentBus `source.external_message_id`,一次请求只能有一个 |
|
||
| `source_provider` | 否 | V2 兼容字段;通常不传。写入定位不使用该字段,真实 provider 以 SourceMessage Inbox 入库值为准 |
|
||
| `source_channel` | 否 | V2 兼容字段;通常不传。写入定位不使用该字段,AgentBus 邮件真实入库渠道可能是 `OUTLOOK` |
|
||
| `ai_task_results[]` | 是 | AI 拆分出的任务结果列表,必须保留数组顺序 |
|
||
| `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` 仅历史兼容;MCP 路径会在 adapter 层校验 |
|
||
| `ai_task_results[].task_type` | 是 | AI 原始任务类型 |
|
||
| `ai_task_results[].task_subtype` | 否 | 业务动作 subtype |
|
||
| `ai_task_results[].case_keys` | 否 | 订单关联候选键 |
|
||
| `ai_task_results[].extracted_fields` | 否 | 业务字段主体 |
|
||
|
||
正式联调时,SuperAgent 不需要传 `hotel_id`。后端通过系统酒店和外部 `source_message_id` 查找唯一 `platform_source_message_inbox.external_message_id`,真实 provider/channel 以 Inbox 入库值为准。如果没有找到,返回 `SOURCE_MESSAGE_NOT_FOUND`;如果同一系统酒店下匹配到多条,返回 `SOURCE_MESSAGE_AMBIGUOUS`。本地旧夹具允许在缺少 `hotel_id` 时使用内部数字 SourceMessage ID,但该兼容路径不作为 SuperAgent 正式契约。
|
||
|
||
`informational_message` 结构化任务仅用于历史兼容。新数据如果是纯信息类邮件或无法形成业务素材包,应优先使用 V4 `S10/S99` 来源通知;V3 结构化 `S10/S99` 和下面的 `S000/S999` 文本请求体仅作为旧联调或兼容路径。
|
||
|
||
### 8.6 S000/S999 文本请求体
|
||
|
||
纯信息类邮件:
|
||
|
||
```text
|
||
S000,mail-20260708-0001
|
||
```
|
||
|
||
无法形成业务素材包:
|
||
|
||
```text
|
||
S999,mail-20260708-0001
|
||
```
|
||
|
||
字段规则:
|
||
|
||
| 片段 | 中文说明 |
|
||
| --- | --- |
|
||
| `S000` | 纯信息类邮件,不需要形成业务任务。 |
|
||
| `S999` | 入口阶段无法形成业务素材包,不需要进入业务执行。 |
|
||
| `mail-20260708-0001` | 外部来源消息 ID,对应 SourceMessage Inbox 的 `external_message_id`。 |
|
||
|
||
S000/S999 不在 body 里传 `hotel_id`,后端使用平台酒店表唯一 `ACTIVE` 酒店和外部消息 ID 查询 SourceMessage Inbox。命中后创建 `SOURCE_MESSAGE_ONLY` 只读特殊任务:任务列表可见,订单列表不可见,不允许编辑、确认、转换订单、执行 OPERA 或重试 OPERA,也不参与同订单任务执行顺序阻塞。该文本格式仅为兼容路径;新数据优先使用 V4 `S10/S99` 来源通知。
|
||
|
||
### 8.7 成功响应
|
||
|
||
V4 普通业务包成功响应示例。注意:V4 新业务主线只写 V4 订单任务和任务卡,响应中的 `task_id`、`task_status`、`execution_order` 不再代表旧 `workflow_reservation_task`,因此返回 `null`;前端和后端内部处理应通过 V4 查询接口读取 `order_task_id` / `card_id`。
|
||
|
||
```json
|
||
{
|
||
"request_id": "req-003",
|
||
"source_message_id": "mail-20260708-0001",
|
||
"batch_id": "1900000000000000200",
|
||
"idempotent_replay": false,
|
||
"accepted_count": 1,
|
||
"items": [
|
||
{
|
||
"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": null,
|
||
"system_task_type": "NEW_BOOKING",
|
||
"task_card_type": "NEW_BOOKING",
|
||
"task_status": null,
|
||
"order_status": "ACTIVE",
|
||
"execution_order": null
|
||
}
|
||
],
|
||
"warnings": []
|
||
}
|
||
```
|
||
|
||
历史 V2/V3 兼容入口仍可能返回旧 `task_id` 和 `execution_order`;该路径仅作历史参考,不作为开发阶段 V4 新业务验收重点。
|
||
|
||
旧文本 S000/S999 成功响应示例:
|
||
|
||
```json
|
||
{
|
||
"request_id": "req-004",
|
||
"source_message_id": "mail-20260708-0001",
|
||
"batch_id": "1900000000000000500",
|
||
"idempotent_replay": false,
|
||
"accepted_count": 1,
|
||
"items": [
|
||
{
|
||
"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",
|
||
"task_card_type": "SOURCE_MESSAGE_ONLY",
|
||
"task_status": "COMPLETED",
|
||
"order_status": "TEMPORARY",
|
||
"execution_order": 1
|
||
}
|
||
],
|
||
"warnings": []
|
||
}
|
||
```
|
||
|
||
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 查询接口错误响应
|
||
|
||
```json
|
||
{
|
||
"success": false,
|
||
"request_id": "req-001",
|
||
"trace_id": "trace-001",
|
||
"data": null,
|
||
"warnings": [],
|
||
"error": {
|
||
"code": "AUTH_SIGNATURE_INVALID",
|
||
"message": "签名校验失败。",
|
||
"details": {}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 9.2 任务结果通知接口错误响应
|
||
|
||
```json
|
||
{
|
||
"request_id": null,
|
||
"error_code": "AUTH_SIGNATURE_INVALID",
|
||
"message": "签名校验失败。",
|
||
"details": []
|
||
}
|
||
```
|
||
|
||
### 9.3 常见错误码
|
||
|
||
| 错误码 | HTTP 状态 | 中文说明 |
|
||
| --- | --- | --- |
|
||
| `AUTH_HEADER_MISSING` | 401 | HMAC 必要 Header 缺失 |
|
||
| `AUTH_HEADER_INVALID` | 401 | HMAC Header 格式或长度无效 |
|
||
| `AUTH_TIMESTAMP_INVALID` | 401 | timestamp 格式错误或超出时间窗口 |
|
||
| `AUTH_SIGNATURE_INVALID` | 401 | 签名不匹配或服务端未配置 secret |
|
||
| `AUTH_NONCE_REPLAY` | 409 | nonce 已被使用 |
|
||
| `REQUEST_BODY_TOO_LARGE` | 413 | 请求体超过大小限制 |
|
||
| `REQUEST_CONTENT_TYPE_UNSUPPORTED` | 415 | 查询接口 `Content-Type` 不是 `application/json`,或任务结果通知接口不是 `application/json` / `text/plain` |
|
||
| `REQUEST_BODY_INVALID` | 400 | JSON 不合法,或 S000/S999 文本格式不符合 `结果码,source_message_id` |
|
||
| `QUERY_KEY_REQUIRED` | 400 | 查询接口缺少可用业务 key |
|
||
| `MESSAGE_CONVERSATION_QUERY_KEY_REQUIRED` | 400 | 会话查询缺少 `external_conversation_id` 或 `source_message_id` |
|
||
| `OBJECT_NOT_FOUND` | 404 | 对象详情查询目标不存在 |
|
||
| `MESSAGE_CONVERSATION_NOT_FOUND` | 404 | 外部邮件会话尚未写入 SourceMessage Inbox |
|
||
| `SYSTEM_HOTEL_NOT_CONFIGURED` | 409 | 平台酒店表没有可用 `ACTIVE` 酒店 |
|
||
| `SYSTEM_HOTEL_AMBIGUOUS` | 409 | 单酒店阶段平台酒店表存在多家 `ACTIVE` 酒店 |
|
||
| `HOTEL_ACCESS_DENIED` | 403 | 显式传入的 `hotel_id` 与系统酒店或当前用户授权酒店不一致 |
|
||
| `SOURCE_MESSAGE_NOT_FOUND` | 404 | 任务结果通知或会话锚点引用的外部来源消息尚未写入 SourceMessage Inbox |
|
||
| `SOURCE_MESSAGE_AMBIGUOUS` | 409 | 任务结果通知的外部来源消息在同一系统酒店下匹配到多条 Inbox 记录,后端拒绝随机选择 |
|
||
| `missing_source_message_id` | 400 | V3 请求缺少 `source_message.source_message_id`,响应体为 typed `infrastructure_input_error` |
|
||
|
||
## 10. HMAC 上线配置
|
||
|
||
上线需要配置:
|
||
|
||
| 配置项 | 是否必填 | 中文说明 |
|
||
| --- | --- | --- |
|
||
| `SUPERAGENT_DEV_TASK_RESULT_HMAC_SECRET` | dev 必填 | dev HMAC 共享密钥;查询接口和任务结果通知接口共用 |
|
||
| `SUPERAGENT_TEST_TASK_RESULT_HMAC_SECRET` | test 必填 | test HMAC 共享密钥;查询接口和任务结果通知接口共用 |
|
||
| `SUPERAGENT_PROD_TASK_RESULT_HMAC_SECRET` | prod 必填 | prod HMAC 共享密钥;生产不能为空,只能通过 Secret 注入 |
|
||
| `SUPERAGENT_TASK_RESULT_HMAC_SECRET` | 兼容兜底 | 旧通用 HMAC 变量;优先使用环境专属变量 |
|
||
| `SUPERAGENT_TASK_RESULT_CLOCK_SKEW_SECONDS` | 否 | 请求时间允许偏移,默认 `300` 秒 |
|
||
| `SUPERAGENT_TASK_RESULT_NONCE_TTL_SECONDS` | 否 | nonce 防重放保存时间,默认 `600` 秒 |
|
||
| `SUPERAGENT_TASK_RESULT_MAX_BODY_BYTES` | 否 | 请求体最大字节数,默认 `1048576` |
|
||
| `SUPERAGENT_TEST_ALLOW_LEGACY_INTERNAL_SOURCE_MESSAGE_ID` | 否 | 仅 test 本地旧夹具兼容内部 SourceMessage ID;正式 dev / test 联调和 prod 不应开启 |
|
||
|
||
上线注意事项:
|
||
|
||
- 本系统和 SuperAgent 必须配置同一个 HMAC secret。
|
||
- 生产 secret 只能放在部署平台 Secret 或环境变量中,不能写入仓库、镜像、前端配置或普通文档。
|
||
- SuperAgent 必须使用原始请求体计算 SHA-256,不能使用格式化后 JSON。
|
||
- HMAC canonical string 的第二行必须使用 `request_path`,例如 `/api/ai-query/v1/case-context`。
|
||
- SuperAgent 每次请求必须生成全新的 nonce;同一个 `client_id + nonce` 在 TTL 窗口内不能重复使用。
|
||
- 调用双方服务器时间必须同步,建议使用 NTP。
|
||
- 建议所有接口只暴露在 HTTPS 和可信网络边界内。
|
||
- 轮换 secret 时需要安排双写或短窗口切换;当前第一版后端只支持一个 secret,轮换窗口内需要协调发布顺序。
|