修正 SuperAgent 来源消息 ID 契约

This commit is contained in:
andy
2026-07-08 11:12:11 +08:00
parent fb82386fdb
commit b58c6baafe
12 changed files with 254 additions and 84 deletions

View File

@@ -4,7 +4,7 @@
| 项目 | 内容 |
| --- | --- |
| 文档版本 | 0.1 |
| 文档版本 | 0.2 |
| 日期 | 2026-07-07 |
| 状态 | 后端接口契约草稿 |
| 适用范围 | SuperAgent / Main Agent 调用本系统提交 AI 任务结果 |
@@ -34,6 +34,21 @@
- SuperAgent 不直连数据库,只能通过本接口提交任务结果。
- 本接口只做技术校验和系统接收,不替代用户确认和 OPERA 模拟操作。
### 2.1 SourceMessage ID 口径
本接口请求体中的 `source_message_id` 是 SuperAgent / Main Agent 从通用溯源字段中原样带回的外部来源消息 ID对应 AgentBus 邮件 payload 的 `source.external_message_id`,不是 `platform_source_message_inbox.id`
后端正式处理路径:
```text
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 签名,做到简单、可实现、可防重放。
@@ -106,7 +121,8 @@ X-TH-Hotel-SuperAgent-Signature: sha256=<lowercase-hex-signature>
```json
{
"source_message_id": "1900000000000000001",
"hotel_id": "HOTEL-TEST",
"source_message_id": "mail-20260708-0001",
"ai_task_results": [
{
"source_event_index": 1,
@@ -140,7 +156,10 @@ X-TH-Hotel-SuperAgent-Signature: sha256=<lowercase-hex-signature>
| 字段 | 是否必填 | 中文说明 |
| --- | --- | --- |
| `source_message_id` | 是 | 关联本系统 SourceMessage ID一次请求只能有一个 |
| `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[]` | 否 | 抽取警告;不直接等同于业务任务 |
@@ -177,8 +196,9 @@ X-TH-Hotel-SuperAgent-Signature: sha256=<lowercase-hex-signature>
- 鉴权签名合法。
- 请求体大小不超过限制。
- JSON 可解析。
- `hotel_id` 存在。仅本地旧夹具兼容缺少 `hotel_id``source_message_id` 为内部数字 ID 的调用。
- 顶层只有一个 `source_message_id`
- `source_message_id` 对应的 SourceMessage 存在
- `source_message_id` 对应的外部来源消息已经写入 SourceMessage Inbox
- `ai_task_results[]` 是非空数组。
- `result_type` 属于允许值。
- `task_type` 属于当前系统可识别的稳定值或可进入 Fallback 处理。
@@ -214,7 +234,7 @@ request_payload_sha256 = sha256(raw_request_body)
batch_idempotency_key =
sha256(
"superagent-task-result-batch:v1"
+ "|" + source_message_id
+ "|" + source_message_internal_id
+ "|" + request_payload_sha256
)
```
@@ -222,7 +242,7 @@ sha256(
作用:
- 相同请求体重复提交时识别为幂等重放。
- 同一个 `source_message_id` 如果提交了不同请求体,不会被误认为同一个批次。
- 同一个外部 `source_message_id` 解析到的内部 SourceMessage 如果提交了不同请求体,不会被误认为同一个批次。
### 6.3 item 幂等键
@@ -232,7 +252,7 @@ sha256(
item_idempotency_key =
sha256(
"superagent-task-result-item:v1"
+ "|" + source_message_id
+ "|" + source_message_internal_id
+ "|" + source_event_index
+ "|" + array_index
+ "|" + catalog_code
@@ -255,7 +275,7 @@ sha256(
| 场景 | 处理方式 |
| --- | --- |
| 完全相同请求体重复提交 | 返回已有 batch 和 item不重复创建任务 |
| 同一 `source_message_id` 不同请求体 | 作为潜在冲突处理,第一版建议拒绝并返回 `IDEMPOTENCY_CONFLICT`,除非后续明确支持重新抽取版本 |
| 同一外部 `source_message_id` 不同请求体 | 作为潜在冲突处理,第一版建议拒绝并返回 `IDEMPOTENCY_CONFLICT`,除非后续明确支持重新抽取版本 |
| 同一请求内 item 幂等键重复 | 拒绝请求,返回 `DUPLICATE_TASK_RESULT_ITEM` |
## 7. 系统映射规则
@@ -286,7 +306,7 @@ sha256(
```json
{
"request_id": "req-20260707-0001",
"source_message_id": "1900000000000000001",
"source_message_id": "mail-20260708-0001",
"batch_id": "1900000000000001001",
"idempotent_replay": false,
"accepted_count": 1,
@@ -315,7 +335,7 @@ sha256(
```json
{
"request_id": "req-20260707-0002",
"source_message_id": "1900000000000000001",
"source_message_id": "mail-20260708-0001",
"batch_id": "1900000000000001001",
"idempotent_replay": true,
"accepted_count": 1,
@@ -352,8 +372,9 @@ sha256(
| 409 | `AUTH_NONCE_REPLAY` | Nonce 重放 |
| 413 | `REQUEST_BODY_TOO_LARGE` | 请求体过大 |
| 400 | `INVALID_JSON` | JSON 不可解析 |
| 400 | `HOTEL_ID_REQUIRED` | 使用外部 `source_message_id` 时缺少 `hotel_id` |
| 400 | `SOURCE_MESSAGE_REQUIRED` | `source_message_id` 缺失 |
| 404 | `SOURCE_MESSAGE_NOT_FOUND` | SourceMessage 不存在 |
| 404 | `SOURCE_MESSAGE_NOT_FOUND` | 外部来源消息尚未写入 SourceMessage Inbox |
| 400 | `TASK_RESULTS_EMPTY` | `ai_task_results[]` 为空 |
| 400 | `TASK_RESULT_UNSUPPORTED_TYPE` | `result_type``task_type` 不可识别 |
| 400 | `DUPLICATE_TASK_RESULT_ITEM` | 同一请求内 item 重复 |
@@ -371,8 +392,8 @@ sha256(
- nonce 重放时返回 `409`
- 签名错误时返回 `401`
- 一个请求只能包含一个 `source_message_id`
- `source_message_id` 不存在时返回 `404`
- 外部 `source_message_id` 未匹配 SourceMessage Inbox 时返回 `404`
- 相同请求重复提交不会重复创建任务。
- 同一 `source_message_id` 不同请求体重复提交返回 `409`
- 同一外部 `source_message_id` 不同请求体重复提交返回 `409`
- 成功接收后能保存 AI 过渡层记录,并返回 batch、task 和 order 标识。
- 日志和响应不暴露 secret、签名原文、完整邮件正文或附件 URL。