Files
th-hotel-simple/docs/project/integrations/superagent-mcp/submit-payload-mapping.md
2026-07-12 19:39:14 +08:00

11 KiB
Raw Blame History

TH Hotel MCP Submit Payload Mapping

1. 文档信息

项目 内容
文档版本 0.2
日期 2026-07-12
状态 已落地第一版
适用范围 th_hotel_submit_task_results 的 V3/P0.1 payload adapter、schema validator 和一次提交规则

2. 核心原则

th_hotel_submit_task_results 接收的是 SuperAgent 已经冻结的业务结果。MCP 层只负责把业务结果转换为本系统可接收的提交 payload不重新解释业务含义不调用 Skill不根据字段名猜测任务类型。

目标链路:

SuperAgent 业务结果
  -> MCP Submit Payload Adapter
  -> MCP Submit Schema Validator
  -> ReservationAiTaskIntakeService
  -> MCP tool result

实现位置:

server/src/main/java/cn/nianxx/thhotel/integrations/mcp/superagent/service/SuperAgentMcpSubmitPayloadAdapter.java
server/src/main/java/cn/nianxx/thhotel/integrations/mcp/superagent/service/impl/SuperAgentMcpSubmitPayloadAdapterImpl.java

3. 支持的输入形态

形态 判断方式 处理方式
V3 业务根 包含 message_events 校验根结构、source message、event transport 字段和关系索引,再按 message_events[] 顺序映射 source_event_indexevent 业务合法性仍交给业务入站层落 adapter_contract_error
V3 S10/S99 入口通知 包含 route_code 或入口通知 result_type 校验 S10/S99 根字段和 source message 后透传给业务入站服务
V2 兼容任务结果 包含 ai_task_results 保留旧 source_message_id + ai_task_results[] 兼容路径,并在 MCP 层校验完整 item schema

未知根字段会在 MCP 层被拒绝,错误码为 MCP_SUBMIT_PAYLOAD_INVALID

source_message_id 缺失或整个 source_message 缺失属于既有基础设施输入错误MCP adapter 不改写该错误通道;业务入站服务会返回 MISSING_SOURCE_MESSAGE_ID

V3 业务根和 S10/S99 如果携带 MCP schema 中的兼容字段 hotel_idsource_providersource_channeladapter 会接受并在转交业务入站层前移除。业务定位仍以系统酒店和 source_message.source_message_id 为准。

4. source_message_id 定义

字段 规则
source_message.source_message_id V3 必填;必须来自 AgentBus 原始 payload 的外部消息 ID对应 platform_source_message_inbox.external_message_id
source_message_id V2 兼容必填;同样对应 platform_source_message_inbox.external_message_id
hotel_id SuperAgent 正式提交不需要传;本系统按平台唯一 ACTIVE 酒店解析

source_message_id 不是本系统数据库主键,也不是 platform_source_message_inbox.id

5. source_event_index 映射

SuperAgent 业务结果中的 E1E_CHILD_1E_PARENT 等值只是 Agent 内部事件 ID不能直接当成本系统的 source_event_index

MCP adapter 按 message_events[] 数组顺序生成一基数字索引:

业务事件 ID 数组位置 本系统 source_event_index
E_CHILD_1 第 1 个 event 1
E_CHILD_2 第 2 个 event 2
E_PARENT 第 3 个 event 3
E_TRACE 第 4 个 event 4

映射规则:

  • message_events[].source_event_index 输出为数字。
  • related_source_event_index 输出为字符串数字,例如 "1"
  • parent_source_event_index 输出为数字。
  • related_source_event_indices[] 输出为字符串数字数组,保持原顺序。
  • 悬空引用、重复引用、缺失事件 ID 都在 MCP 层拒绝。
  • MCP tool result 会返回 mapping_diagnostics.source_event_index_mapping[],记录原始事件 ID 到本系统索引的映射;该诊断不写入 workflow_reservation_ai_transition.ai_payload_json

6. ai_task_results[] 兼容 item schema

V2 兼容路径仍保留,ai_task_results[] item 以 REST 总契约为准。当前 MCP tools/list 已暴露 item schemaadapter 也会在提交业务层前校验必填字段、字段类型、允许 result_type、未知字段和根级 extraction_warnings

必填字段:

  • source_event_index
  • catalog_code
  • skill_id
  • result_type
  • task_type

允许的 result_type

  • normal_task
  • manual_review
  • informational_message,仅历史兼容

建议 item 结构:

{
  "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,
    "reservation_number": null,
    "block_code": null
  },
  "relevant_message_excerpt": "Please create a new booking.",
  "attachments": [],
  "file_references": [],
  "context_used": {},
  "extracted_fields": {},
  "manual_review": null,
  "additional_operations": [],
  "idempotency_key": null
}

新数据优先使用 V3 业务根或 V3 S10/S99不建议继续新增 V2 ai_task_results[]

7. extraction_warnings 规则

形态 规则
V3 业务根 extraction_warnings 必须存在且为数组,无警告传 []
V2 兼容 extraction_warnings 可存在且为数组;缺省由业务层按空数组处理

MCP 层不解释 warning 语义,不因为 warning 自动创建任务。

8. 成功示例Parent Split

提交给 MCP tool 的业务结果可以使用 Agent 内部事件 ID

{
  "source_message": {
    "source_message_id": "mail-20260712-parent-split-001",
    "subject": "Parent split booking request",
    "from": "agent@example.test",
    "cc": [],
    "received_at": "2026-07-12T04:00:00Z",
    "source_channel": "Email"
  },
  "message_events": [
    {
      "event_type": "New Booking",
      "event_role": "travel_agent_request",
      "source_event_index": "E_CHILD_1",
      "current_or_history": "current",
      "case_keys": {
        "group_code": "CHILD-A",
        "confirmation_number": null,
        "reservation_number": null,
        "block_code": null
      },
      "relevant_message_excerpt": "Create child group A.",
      "attachments": [],
      "file_references": [],
      "context_used": {},
      "extracted_fields": {
        "booking_object_type": "Group Block"
      },
      "manual_review": null
    },
    {
      "event_type": "New Booking",
      "event_role": "travel_agent_request",
      "source_event_index": "E_CHILD_2",
      "current_or_history": "current",
      "case_keys": {
        "group_code": "CHILD-B",
        "confirmation_number": null,
        "reservation_number": null,
        "block_code": null
      },
      "relevant_message_excerpt": "Create child group B.",
      "attachments": [],
      "file_references": [],
      "context_used": {},
      "extracted_fields": {
        "booking_object_type": "Group Block"
      },
      "manual_review": null
    },
    {
      "event_type": "Cancel Allotment",
      "event_role": "travel_agent_request",
      "source_event_index": "E_PARENT",
      "current_or_history": "current",
      "case_keys": {
        "group_code": "PARENT",
        "confirmation_number": null,
        "reservation_number": null,
        "block_code": "PARENT"
      },
      "relevant_message_excerpt": "Release parent after child split.",
      "attachments": [],
      "file_references": [],
      "context_used": {},
      "related_event_type": "New Booking",
      "requires_downstream_hard_validation": true,
      "related_source_event_indices": ["E_CHILD_1", "E_CHILD_2"],
      "extracted_fields": {
        "relationship_type": "linked_parent_release_after_child_split",
        "parent_group_code": "PARENT",
        "cancel_scope": "entire_allotment_control_block",
        "parent_release_or_cancel_candidate": true,
        "release_reason": "parent_to_child_allocation_split",
        "allocation_split_from_parent": true,
        "child_group_codes": ["CHILD-A", "CHILD-B"]
      },
      "manual_review": null
    }
  ],
  "case_candidates": [],
  "extraction_warnings": [],
  "unhandled_current_intents": []
}

MCP adapter 进入业务层前会把父事件关系映射为:

{
  "source_event_index": 3,
  "related_source_event_indices": ["1", "2"]
}

MCP tool result 同时返回非业务诊断:

{
  "mapping_diagnostics": {
    "mapping_policy": "message_events_array_order_1_based",
    "source_event_index_mapping": [
      {
        "original_source_event_index": "E_CHILD_1",
        "mapped_source_event_index": 1
      },
      {
        "original_source_event_index": "E_CHILD_2",
        "mapped_source_event_index": 2
      },
      {
        "original_source_event_index": "E_PARENT",
        "mapped_source_event_index": 3
      }
    ]
  }
}

9. 成功示例:跨 Child Trace

当 Trace 覆盖完整 Parent split 的全部 Child 时,不能只绑定第一个 Child。业务结果应保留全部 Child 引用:

{
  "event_type": "Trace",
  "source_event_index": "E_TRACE",
  "related_source_event_indices": ["E_CHILD_1", "E_CHILD_2"],
  "extracted_fields": {
    "trace_subtype": "general_request"
  }
}

MCP adapter 会把它映射为:

{
  "source_event_index": 4,
  "related_source_event_indices": ["1", "2"]
}

10. 拒绝示例

未知字段:

{
  "source_message": {},
  "message_events": [],
  "case_candidates": [],
  "extraction_warnings": [],
  "unhandled_current_intents": [],
  "unexpected_root": true
}

返回:

{
  "isError": true,
  "structuredContent": {
    "success": false,
    "error": {
      "code": "MCP_SUBMIT_PAYLOAD_INVALID",
      "details": {
        "field": "unexpected_root"
      }
    }
  }
}

悬空关系:

{
  "related_source_event_indices": ["E_CHILD_1", "E_UNKNOWN_CHILD"]
}

同样返回 MCP_SUBMIT_PAYLOAD_INVALIDdetails.field=related_source_event_indices,不会调用业务写入 Service。

11. 一次提交和失败不重试

当前 MCP endpoint 与后端业务 Service 同进程:

  • adapter 校验失败时,不调用 ReservationAiTaskIntakeService
  • adapter 校验通过后,只调用一次 ReservationAiTaskIntakeService.accept
  • MCP endpoint 不做自动重试。
  • 业务结果不会因为 MCP 响应被回写或污染。

当前回归测试:

./mvnw -Dtest=SuperAgentMcpControllerTest,SuperAgentMcpSubmitEnabledControllerTest test

覆盖内容:

  • tools/list 暴露 V3 submit schema。
  • tools/list 暴露 V2 ai_task_results[] item schema 和 V3 relationship_type
  • 缺失 source_message_id 或整个 source_message 保持既有 MISSING_SOURCE_MESSAGE_ID 错误。
  • 未知根字段在业务层前被拒绝。
  • V2 item 缺必填字段在业务层前被拒绝。
  • 悬空 related_source_event_indices 在业务层前被拒绝。
  • 重复 related_source_event_indices 在业务层前被拒绝。
  • E_CHILD_1/E_CHILD_2/E_PARENT 按数组顺序映射为 1/2/3
  • 跨 Child Trace 关系映射为完整 ["1", "2"],不压缩到第一个 Child。
  • S10/S99 可通过 MCP 写入工具创建只读来源消息任务。
  • V3 event 业务契约错误由业务入站层保存为 adapter_contract_error transition。
  • 合法 Parent split 只创建一个 AI batch并返回 accepted_count=3