9.3 KiB
TH Hotel MCP Submit Payload Mapping
1. 文档信息
| 项目 | 内容 |
|---|---|
| 文档版本 | 0.1 |
| 日期 | 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 业务根 | 同时包含 source_message 和 message_events |
先校验根结构、source message、event 必填字段和关系索引,再按 message_events[] 顺序映射 source_event_index |
| V3 S10/S99 入口通知 | 同时包含 source_message 和 route_code |
校验 S10/S99 根字段和 source message 后透传给业务入站服务 |
| V2 兼容任务结果 | 包含 ai_task_results |
保留旧 source_message_id + ai_task_results[] 兼容路径 |
未知根字段会在 MCP 层被拒绝,错误码为 MCP_SUBMIT_PAYLOAD_INVALID。
source_message_id 缺失属于既有基础设施输入错误,MCP adapter 不改写该错误通道;业务入站服务会返回 MISSING_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 业务结果中的 E1、E_CHILD_1、E_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 层拒绝。
6. ai_task_results[] 兼容 item schema
V2 兼容路径仍保留,ai_task_results[] item 以 REST 总契约为准,第一版 MCP schema 只暴露 object,但业务入站会校验核心字段。
建议 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"]
}
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_INVALID,details.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。- 缺失
source_message_id保持既有MISSING_SOURCE_MESSAGE_ID错误。 - 未知根字段在业务层前被拒绝。
- 悬空
related_source_event_indices在业务层前被拒绝。 - 重复
related_source_event_indices在业务层前被拒绝。 E_CHILD_1/E_CHILD_2/E_PARENT按数组顺序映射为1/2/3。- 合法 Parent split 只创建一个 AI batch,并返回
accepted_count=3。