21 KiB
M002 SuperAgent Task Result API Contract
文档状态:阶段记录。本文记录 M002 阶段 SuperAgent 提交 AI 任务结果的入站接口设计。 当前对外接口总契约以
../integrations/superagent-api-contract.md为准; 如字段、错误码、鉴权或请求范围出现重复描述,以总契约和当前代码实现为准。
文档信息
| 项目 | 内容 |
|---|---|
| 文档版本 | 0.6 |
| 日期 | 2026-07-12 |
| 状态 | V2 兼容 + M002 V3 CP1-CP7 入站解析、同卡复核、P0 fixtures 回归和 P0.1 Parent Group 路由修订;后续业务流程以 M002-order-task-workflow-v3.md 为准 |
| 适用范围 | SuperAgent / Main Agent 调用本系统提交 AI 任务结果 |
| 主要读者 | 后端、SuperAgent 对接方、测试、后续协作 agent |
1. 文档定位
本文定义 SuperAgent / Main Agent 向本系统提交 V3 source_message + message_events[] 业务根、结构化 S10/S99 入口通知、V2 ai_task_results[] 兼容结果,以及旧 S000/S999 特殊入口结果的后端接口契约。
本文承接 M002-order-task-workflow-v2.md,只定义本系统入站接口、鉴权、幂等、请求响应和技术校验边界,不定义 SuperAgent 内部 prompt、Skill 实现、OPERA 真实接口或前端展示细节。
2026-07-11 后,M002 后续开发基线已迁移到 M002-order-task-workflow-v3.md。2026-07-12 起,Parent Group / Allotment 路由采用 P0.1 增量修订:当前后端目标为结构化 S10/S99 入站、V3 业务根基础解析、40 路由枚举 / 稳定配置、UNHANDLED_CURRENT_INTENT 和 route 相关字段最小落库、type-known manual review 同卡解阻第一版、typed infrastructure_input_error 响应、P0 fixtures 回归基线和 Parent split Cancel Allotment 路由。旧 S000/S999 和 ai_task_results[] 仍作为兼容路径保留。对外联调以 docs/project/integrations/superagent-api-contract.md 为准。
2. 接口概览
| 项目 | 内容 |
|---|---|
| Method | POST |
| Path | /api/integrations/superagent/task-results |
| Content-Type | application/json 或 text/plain |
| 响应格式 | application/json |
| 一次请求范围 | 只能包含一个 source_message_id |
| 业务动作 | 接收 AI 结果或入口结果、写入 AI 过渡层,按可支持路由生成订单 / 任务 / 任务卡 |
| 鉴权方式 | HMAC-SHA256 签名 |
中文说明:
- 该接口是服务到服务的入站接口,不给前端直接调用。
- SuperAgent 不直连数据库,只能通过本接口提交任务结果或入口处理结果。
- 本接口只做技术校验和系统接收,不替代用户确认和 OPERA 模拟操作。
application/json用于 V3 结构化S10/S99、V3 业务根或 V2normal_task/manual_review兼容结构化任务。text/plain用于旧S000,source_message_id/S999,source_message_id特殊入口结果兼容。- P0.1 后,V3 业务根中的完整 Parent split 父事件必须使用
event_type=Cancel Allotment和task_subtype=cancel_allotment_control_block;当前新入站不接受Cancel Booking + linked_parent_release_after_child_split作为合法业务任务。V3message_events[]中的旧三元组按 event 写入 adapter contract error;旧 V2ai_task_results[]兼容入口提交该三元组时按请求级ADAPTER_CONTRACT_ERROR拒绝。
2.1 SourceMessage ID 口径
本接口请求体中的 source_message_id 是 SuperAgent / Main Agent 从通用溯源字段中原样带回的外部来源消息 ID,对应 AgentBus 邮件 payload 的 source.external_message_id,不是 platform_source_message_inbox.id。
后端正式处理路径:
系统酒店 + source_message_id
→ platform_source_message_inbox.external_message_id
→ platform_source_message_inbox.id
→ workflow_* 表 source_message_id 内部外键
数据库 workflow_* 表中的 source_message_id 仍然保存内部 SourceMessage Inbox ID。只有对外接口的 source_message_id 使用外部来源消息 ID。任务结果通知不要求 SuperAgent 传数据库层 source_provider / source_channel;后端按系统酒店和外部消息 ID 查唯一 Inbox 记录,真实 provider/channel 以 SourceMessage Inbox 入库值为准。无 hotel_id 时仅兼容本地旧夹具使用内部数字 ID,正式 SuperAgent 调用不得依赖该兼容路径。
3. 鉴权方案
第一版使用 HMAC-SHA256 签名,做到简单、可实现、可防重放。
3.1 环境变量
| 环境变量 | 是否必填 | 中文说明 |
|---|---|---|
SUPERAGENT_DEV_TASK_RESULT_HMAC_SECRET |
dev 必填 | dev SuperAgent 任务结果入站签名密钥,只能通过 Secret 注入 |
SUPERAGENT_TEST_TASK_RESULT_HMAC_SECRET |
test 必填 | test SuperAgent 任务结果入站签名密钥,只能通过 Secret 注入 |
SUPERAGENT_PROD_TASK_RESULT_HMAC_SECRET |
prod 必填 | prod SuperAgent 任务结果入站签名密钥,只能通过 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 |
如果当前 profile 对应的 SUPERAGENT_*_TASK_RESULT_HMAC_SECRET 和旧通用变量都为空,生产环境应拒绝接口调用。
3.2 请求 Header
| Header | 是否必填 | 中文说明 |
|---|---|---|
X-TH-Hotel-SuperAgent-Client-Id |
是 | 调用方客户端 ID,用于区分不同 SuperAgent 调用方 |
X-TH-Hotel-SuperAgent-Timestamp |
是 | UTC 时间,ISO-8601 格式,例如 2026-07-07T08:30:00Z |
X-TH-Hotel-SuperAgent-Nonce |
是 | 每次请求唯一随机值,用于防重放 |
X-TH-Hotel-SuperAgent-Signature |
是 | HMAC 签名,格式 sha256=<lowercase-hex> |
X-TH-Hotel-Request-Id |
否 | 调用方请求 ID,用于排查和日志串联 |
3.3 签名串
签名使用原始请求体字节计算 SHA-256,再参与 HMAC。
规范签名串:
POST
/api/integrations/superagent/task-results
<X-TH-Hotel-SuperAgent-Timestamp>
<X-TH-Hotel-SuperAgent-Nonce>
<X-TH-Hotel-SuperAgent-Client-Id>
<lowercase-hex-sha256-of-raw-body>
签名算法:
signature = HMAC_SHA256(当前 profile 生效的 SuperAgent HMAC secret, canonical_string)
Header 写法:
X-TH-Hotel-SuperAgent-Signature: sha256=<lowercase-hex-signature>
3.4 服务端校验
服务端必须按顺序完成以下校验:
- 校验必要 Header 是否存在。
- 校验 timestamp 可解析且在允许时间窗口内。
- 校验同一个
client_id + nonce在 TTL 窗口内没有被使用过。 - 计算原始请求体 SHA-256。
- 使用 HMAC secret 重新计算签名。
- 使用常量时间比较签名。
- 校验
Content-Type是否为application/json或text/plain,避免不支持的媒体类型消耗 nonce。 - 鉴权通过后再按 body 内容解析 JSON 或 S000/S999 文本。
日志和错误响应不得输出 secret、签名原文、完整请求体、邮件正文、附件 URL 或个人敏感信息。
4. 请求体
4.1 JSON 结构化任务请求体
JSON 请求体沿用 AI 导入文档定义的聚合结构。
{
"hotel_id": "HOTEL-TEST",
"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": []
}
4.2 顶层字段
| 字段 | 是否必填 | 中文说明 |
|---|---|---|
hotel_id |
否 | 酒店上下文 ID;SuperAgent 默认不传,单酒店阶段由后端解析系统酒店 |
source_message_id |
是 | 外部来源消息 ID,对应 AgentBus source.external_message_id;一次请求只能有一个 |
source_provider |
否 | V2 兼容字段;通常不传。写入定位不使用该字段,真实 provider 以 SourceMessage Inbox 入库值为准 |
source_channel |
否 | V2 兼容字段;通常不传。写入定位不使用该字段,AgentBus 邮件真实入库渠道可能是 OUTLOOK |
ai_task_results[] |
是 | AI 拆分出的任务结果列表,必须保留数组顺序 |
extraction_warnings[] |
否 | 抽取警告;不直接等同于业务任务 |
V2 第一版要求 ai_task_results[] 至少包含一条记录。没有业务动作的纯信息类邮件不要提交空数组,也不要新生成 informational_message;新数据优先使用 V3 结构化 S10/S99,旧联调或兼容场景仍可使用 S000,source_message_id 文本结果。
4.3 ai_task_results[] 字段
| 字段 | 是否必填 | 中文说明 |
|---|---|---|
source_event_index |
是 | AI current 事件序号,建议从 1 开始 |
catalog_code |
是 | Skill 目录代码,例如 S01、S02 |
skill_id |
是 | Skill 标识 |
result_type |
是 | V2 当前代码契约只接受 normal_task、manual_review;informational_message 仅历史兼容 |
task_type |
是 | AI 原始任务类型 |
task_subtype |
否 | 业务动作 subtype;有则用于任务卡路由 |
current_or_history |
否 | 当前或历史标识 |
case_keys |
否 | 订单关联候选键 |
visible_reason |
否 | 给用户看的生成原因 |
relevant_message_excerpt |
否 | 相关邮件片段,注意不要超长 |
attachments / file_references |
否 | 附件和文件引用 |
context_used |
否 | AI 使用的上下文 |
extracted_fields |
否 | 业务字段主体 |
manual_review |
条件必填 | result_type=manual_review 时应提供 |
informational_message |
条件必填 | 仅历史兼容;新数据不再生成 |
additional_operations |
否 | 附加动作建议 |
idempotency_key |
否 | 可忽略;本系统第一版自行生成幂等键 |
4.4 S000/S999 文本结果请求体
当 SuperAgent 入口阶段没有结构化任务 JSON 时,可以直接提交纯文本 body:
S000,mail-20260708-0001
或:
S999,mail-20260708-0001
字段说明:
| 片段 | 中文说明 |
|---|---|
S000 |
纯信息类邮件,不形成业务素材包 |
S999 |
入口阶段无法形成业务素材包 |
source_message_id |
逗号后面的值,外部来源消息 ID,对应 AgentBus source.external_message_id |
处理规则:
- 第一版使用系统默认酒店反查 SourceMessage Inbox,不要求文本 body 携带
hotel_id。 - 后端按
默认酒店 + external_message_id查询唯一 SourceMessage;真实 provider/channel 以 Inbox 入库值为准。 - 命中后创建
SOURCE_MESSAGE_ONLY只读任务。 - 任务列表可见,订单列表不可见。
- 不允许编辑、确认、转换订单、执行 OPERA 或重试 OPERA。
- 同一 SourceMessage 重复提交相同文本 body 返回幂等重放。
- 同一 SourceMessage 已经存在不同 AI 结果请求时返回幂等冲突。
5. 技术校验边界
本接口只做技术校验,不做业务合法性判断。
5.1 必须校验
- 鉴权签名合法。
- 请求体大小不超过限制。
- JSON body 可解析;S000/S999 文本 body 必须符合
结果码,source_message_id。 - JSON body 中
hotel_id可不传;正式 REST / MCP 调用由后端解析系统酒店。仅本地旧夹具在显式开启兼容开关时允许缺少hotel_id且source_message_id为内部数字 ID 的调用。 - S000/S999 文本 body 第一版使用系统默认酒店,不读取
hotel_id。 - 顶层只有一个
source_message_id。 source_message_id对应的外部来源消息已经写入 SourceMessage Inbox。ai_task_results[]是非空数组。- JSON
result_type属于允许值。 task_type属于当前系统可识别的稳定值或可进入 Fallback 处理。- 同一个请求内
source_event_index和数组顺序可保存。 - 关键字符串长度不超过数据库限制。
- V3 P0.1 Parent split 当前合法结构必须是
Cancel Allotment + cancel_allotment_control_block;Cancel Booking + linked_parent_release_after_child_split属于当前 producer 契约错误,只能作为历史 payload 只读兼容。 - Parent 只提供
case_keys.group_code或只提供case_keys.block_code时,后端会在 adapter 派生副本中补齐另一边,不回写原始 payload。 - Parent
case_keys.group_code与case_keys.block_code原始候选冲突时,SuperAgent 应输出manual_review.reason_code=target_object_unclear和非空context_used.parent_identity_candidates[];后端会创建同卡 type-known manual review。缺少该复核结构时按 adapter contract error 处理。 - 同一个 Parent split cluster 只能有一个 Parent 候选;重复 Parent 候选不创建第二张业务任务卡。
5.2 不在本接口判断
- 不判断 AI 任务类型是否业务正确。
- 不判断房型、价格、日期、Rate Code 是否合理。
- 不判断 Cancel Booking 前是否已经有 New Booking。
- 不因为字段缺失自动改成 Fallback。
- 不直接执行 OPERA 模拟。
- 不直接把 AI 原始值写入 OPERA 参数。
- 不把 Parent split 的 adapter 契约校验理解为业务成功;即使生成
Cancel Allotment任务,也必须等待用户确认、Preflight 和后续 OPERA/OHIP 接入。
6. 幂等设计
idempotency_key 第一版由系统生成,不依赖 SuperAgent 传值。
6.1 请求哈希
系统必须保存原始请求体的 SHA-256:
request_payload_sha256 = sha256(raw_request_body)
6.2 批次幂等键
批次幂等键建议:
batch_idempotency_key =
sha256(
"superagent-task-result-batch:v1"
+ "|" + source_message_internal_id
+ "|" + request_payload_sha256
)
作用:
- 相同请求体重复提交时识别为幂等重放。
- 同一个外部
source_message_id解析到的内部 SourceMessage 如果提交了不同请求体,不会被误认为同一个批次。 - S000/S999 文本 body 也使用同一批次幂等规则。
6.3 item 幂等键
每条 ai_task_results[] 的幂等键建议:
item_idempotency_key =
sha256(
"superagent-task-result-item:v1"
+ "|" + source_message_internal_id
+ "|" + source_event_index
+ "|" + array_index
+ "|" + catalog_code
+ "|" + skill_id
+ "|" + result_type
+ "|" + task_type
+ "|" + task_subtype
+ "|" + item_payload_sha256
)
说明:
array_index按 AI 返回列表顺序保存,建议从 1 开始。item_payload_sha256是单个 item 规范 JSON 或原始片段的 SHA-256。- 数据库应对
hotel_id + item_idempotency_key建唯一约束。
6.4 重复提交处理
| 场景 | 处理方式 |
|---|---|
| 完全相同请求体重复提交 | 返回已有 batch 和 item,不重复创建任务 |
同一外部 source_message_id 不同请求体 |
作为潜在冲突处理,第一版建议拒绝并返回 IDEMPOTENCY_CONFLICT,除非后续明确支持重新抽取版本 |
| 同一请求内 item 幂等键重复 | 拒绝请求,返回 DUPLICATE_TASK_RESULT_ITEM |
7. 系统映射规则
接口接收后,应将 AI 字段映射到系统字段。
| AI 字段 | 系统字段 | 中文说明 |
|---|---|---|
task_type |
ai_task_type |
保留 AI 原始任务类型 |
result_type |
result_type |
保留 AI 结果类型 |
task_type + result_type |
system_task_type |
映射为系统主任务类型 |
task_type + task_subtype |
task_card_type |
映射为任务卡类型 |
source_event_index + array_index |
execution_order |
生成同订单任务顺序 |
| 完整 item JSON | ai_payload_json |
保存 AI 原始 payload |
case_keys |
case_keys_json |
保存订单候选键 |
extracted_fields |
extracted_fields_json |
保存业务字段 |
manual_review |
manual_review_json |
保存人工复核结构 |
informational_message |
informational_message_json |
仅历史兼容的信息提醒结构 |
S000/S999 文本结果 |
SOURCE_MESSAGE_ONLY 只读特殊任务 |
保存入口阶段原始结果,不创建真实业务订单 |
系统主任务类型映射以 M002-order-task-workflow-v2.md 为准。
8. 响应体
8.1 创建成功
首次成功创建时返回 201 Created。
{
"request_id": "req-20260707-0001",
"source_message_id": "mail-20260708-0001",
"batch_id": "1900000000000001001",
"idempotent_replay": false,
"accepted_count": 1,
"items": [
{
"source_event_index": 1,
"array_index": 1,
"ai_transition_id": "1900000000000002001",
"order_id": "1900000000000003001",
"task_id": "1900000000000004001",
"system_task_type": "NEW_BOOKING",
"task_card_type": "NEW_BOOKING",
"task_status": "PENDING_CONFIRM",
"order_status": "TEMPORARY",
"execution_order": 1
}
],
"warnings": []
}
S000 / S999 文本结果创建成功时,同样返回 201 Created。这类结果会创建只读特殊任务和隐藏技术订单,供任务列表展示和任务详情查看来源邮件;该隐藏技术订单不会出现在订单列表。
{
"request_id": "req-20260707-0003",
"source_message_id": "mail-20260708-0001",
"batch_id": "1900000000000005001",
"idempotent_replay": false,
"accepted_count": 1,
"items": [
{
"source_event_index": 1,
"array_index": 1,
"ai_transition_id": "1900000000000006001",
"order_id": "1900000000000007001",
"task_id": "1900000000000008001",
"system_task_type": "SOURCE_MESSAGE_ONLY",
"task_card_type": "SOURCE_MESSAGE_ONLY",
"task_status": "COMPLETED",
"order_status": "TEMPORARY",
"execution_order": 1
}
],
"warnings": []
}
前端通过任务详情查看该类任务时,字段列表和 OPERA 操作列表为空,按钮应全部只读;source_message_only_result 会返回 entry_result_code、entry_result_meaning、entry_result_description、entry_result_source_message_id、result_type、route_code、agent_assessment、notification、manual_review 和 raw_answer。
8.2 幂等重放
相同请求体重复提交时返回 200 OK。
{
"request_id": "req-20260707-0002",
"source_message_id": "mail-20260708-0001",
"batch_id": "1900000000000001001",
"idempotent_replay": true,
"accepted_count": 1,
"items": [],
"warnings": [
{
"code": "IDEMPOTENT_REPLAY",
"message": "相同请求已经处理,本次未重复创建任务。"
}
]
}
9. 错误响应
错误响应统一结构:
{
"request_id": "req-20260707-0003",
"error_code": "AUTH_SIGNATURE_INVALID",
"message": "签名校验失败。",
"details": []
}
常见错误码:
| HTTP 状态 | error_code | 中文说明 |
|---|---|---|
| 401 | AUTH_HEADER_MISSING |
鉴权 Header 缺失 |
| 401 | AUTH_TIMESTAMP_INVALID |
请求时间无效或超出窗口 |
| 401 | AUTH_SIGNATURE_INVALID |
签名不匹配 |
| 409 | AUTH_NONCE_REPLAY |
Nonce 重放 |
| 413 | REQUEST_BODY_TOO_LARGE |
请求体过大 |
| 400 | INVALID_JSON |
JSON 不可解析 |
| 400 | HOTEL_ID_MISMATCH |
显式 hotel_id 或历史内部 SourceMessage ID 所属酒店与系统酒店不一致 |
| 409 | SYSTEM_HOTEL_NOT_CONFIGURED |
平台酒店表没有 ACTIVE 酒店,无法解析系统酒店 |
| 409 | SYSTEM_HOTEL_AMBIGUOUS |
单酒店阶段平台酒店表存在多家 ACTIVE 酒店 |
| 400 | SOURCE_MESSAGE_REQUIRED |
source_message_id 缺失 |
| 404 | SOURCE_MESSAGE_NOT_FOUND |
外部来源消息尚未写入 SourceMessage Inbox |
| 409 | SOURCE_MESSAGE_AMBIGUOUS |
同一系统酒店下存在多条相同外部 source_message_id 的 Inbox 记录,后端拒绝随机选择 |
| 400 | TASK_RESULTS_EMPTY |
ai_task_results[] 为空 |
| 400 | TASK_RESULT_UNSUPPORTED_TYPE |
result_type 或 task_type 不可识别 |
| 400 | DUPLICATE_TASK_RESULT_ITEM |
同一请求内 item 重复 |
| 409 | IDEMPOTENCY_CONFLICT |
同一 SourceMessage 出现不同请求体重复提交 |
| 500 | INTERNAL_ERROR |
系统内部错误 |
错误响应不得返回原始请求体、邮件正文、附件 URL、Token、签名 secret 或完整个人敏感信息。
10. 验收标准
第一版接口实现完成时至少满足:
- 缺失 HMAC Header 时返回
401。 - timestamp 超出窗口时返回
401。 - nonce 重放时返回
409。 - 签名错误时返回
401。 - 一个请求只能包含一个
source_message_id。 - 外部
source_message_id未匹配 SourceMessage Inbox 时返回404。 - 相同请求重复提交不会重复创建任务。
- 同一外部
source_message_id不同请求体重复提交返回409。 - 成功接收后能保存 AI 过渡层记录,并返回 batch、task 和 order 标识。
- 日志和响应不暴露 secret、签名原文、完整邮件正文或附件 URL。