Files
th-hotel-simple/docs/project/requirements/M002-superagent-task-result-api-contract.md

20 KiB
Raw Blame History

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/S999ai_task_results[] 仍作为兼容路径保留。对外联调以 docs/project/integrations/superagent-api-contract.md 为准。

2. 接口概览

项目 内容
Method POST
Path /api/integrations/superagent/task-results
Content-Type application/jsontext/plain
响应格式 application/json
一次请求范围 只能包含一个 source_message_id
业务动作 接收 AI 结果或入口结果、写入 AI 过渡层,按可支持路由生成订单 / 任务 / 任务卡
鉴权方式 HMAC-SHA256 签名

中文说明:

  • 该接口是服务到服务的入站接口,不给前端直接调用。
  • SuperAgent 不直连数据库,只能通过本接口提交任务结果或入口处理结果。
  • 本接口只做技术校验和系统接收,不替代用户确认和 OPERA 模拟操作。
  • application/json 用于 V3 结构化 S10/S99、V3 业务根或 V2 normal_task / manual_review 兼容结构化任务。
  • text/plain 用于旧 S000,source_message_id / S999,source_message_id 特殊入口结果兼容。
  • P0.1 后V3 业务根中的完整 Parent split 父事件必须使用 event_type=Cancel Allotmenttask_subtype=cancel_allotment_control_block;当前新入站不接受 Cancel Booking + linked_parent_release_after_child_split 作为合法业务任务,旧 V2 ai_task_results[] 兼容入口也不能继续提交该三元组。

2.1 SourceMessage ID 口径

本接口请求体中的 source_message_id 是 SuperAgent / Main Agent 从通用溯源字段中原样带回的外部来源消息 ID对应 AgentBus 邮件 payload 的 source.external_message_id,不是 platform_source_message_inbox.id

后端正式处理路径:

hotel_id + source_provider(默认 AGENTBUS) + source_channel(默认 EMAIL) + 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。无 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 服务端校验

服务端必须按顺序完成以下校验:

  1. 校验必要 Header 是否存在。
  2. 校验 timestamp 可解析且在允许时间窗口内。
  3. 校验同一个 client_id + nonce 在 TTL 窗口内没有被使用过。
  4. 计算原始请求体 SHA-256。
  5. 使用 HMAC secret 重新计算签名。
  6. 使用常量时间比较签名。
  7. 校验 Content-Type 是否为 application/jsontext/plain,避免不支持的媒体类型消耗 nonce。
  8. 鉴权通过后再按 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用于反查 SourceMessage Inbox 幂等键
source_message_id 外部来源消息 ID对应 AgentBus source.external_message_id;一次请求只能有一个
source_provider 来源提供方,第一版缺省为 AGENTBUS
source_channel 来源渠道,第一版缺省为 EMAIL
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_taskmanual_reviewinformational_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
  • 后端按 默认酒店 + AGENTBUS + EMAIL + external_message_id 查询 SourceMessage。
  • 命中后创建 SOURCE_MESSAGE_ONLY 只读任务。
  • 任务列表可见,订单列表不可见。
  • 不允许编辑、确认、转换订单、执行 OPERA 或重试 OPERA。
  • 同一 SourceMessage 重复提交相同文本 body 返回幂等重放。
  • 同一 SourceMessage 已经存在不同 AI 结果请求时返回幂等冲突。

5. 技术校验边界

本接口只做技术校验,不做业务合法性判断。

5.1 必须校验

  • 鉴权签名合法。
  • 请求体大小不超过限制。
  • JSON body 可解析S000/S999 文本 body 必须符合 结果码,source_message_id
  • JSON body 中 hotel_id 存在。仅本地旧夹具兼容缺少 hotel_idsource_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_blockCancel Booking + linked_parent_release_after_child_split 属于当前 producer 契约错误,只能作为历史 payload 只读兼容。
  • 同一个 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_codeentry_result_meaningentry_result_descriptionentry_result_source_message_idresult_typeroute_codeagent_assessmentnotificationmanual_reviewraw_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
400 TASK_RESULTS_EMPTY ai_task_results[] 为空
400 TASK_RESULT_UNSUPPORTED_TYPE result_typetask_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。