Files
th-hotel-simple/docs/project/integrations/superagent-agentbus-project-integration-guide.md
T
鲨鱼辣椒 694c4317a3 checkpoint: complete recoverable V2 pre-separation baseline
Complete the selective V2 checkpoint with its minimal AgentBus, object-storage, replay persistence, and validated-workbench shared dependency closure.
2026-08-20 17:09:00 +08:00

877 lines
38 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# TH Hotel SuperAgent 与 AgentBus 项目接入记录
## 1. 文档定位
本文保存 TH Hotel 项目中已经验证过的 SuperAgent 与 AgentBus 接入经验、项目路径、表名和
验证记录。
本文是当前项目专属记录,不应整份复制到其他项目。可复用的通用接入规则应沉淀到
`docs/import/reusable/integrations/superagent-agentbus-portable-integration-guide.md`。
本文不是 SuperAgent 或 AgentBus 官方协议文档,也不记录任何真实 Token、API Key、
Session ID、Run ID、邮箱正文、附件 URL 或客户个人信息。其他项目接入时,应把本文作为
工程边界、配置清单和验证顺序参考;具体字段仍以提供方最新协议和真实测试响应为准。
相关本项目验证记录:
- `docs/superagent-integration-notes.md`
- `docs/agentbus-integration-notes.md`
- `docs/agentbus-production-flow.md`
- `docs/architecture/ADR-003-ai-provider-boundary.md`
## 2. 两条链路的职责边界
SuperAgent 和 AgentBus 不应被设计成同一个模块。
```text
AgentBus
→ 接收 Email / LINE 等外部渠道消息
→ 保存 SourceMessage Inbox
→ 受控异步分发 / Replay
→ 调用 AI 能力端口
→ SuperAgent Provider Adapter
→ 保存 AI Capability Invocation
→ 业务 Schema 校验
→ 人工确认
→ Case / Task / Operation / Receipt
```
| 能力 | 定位 | 负责什么 | 不负责什么 |
| --- | --- | --- | --- |
| AgentBus | 外部消息通道适配器 | WebSocket 连接、接收入站 frame、保存原始来源事实 | 不做 AI 抽取、不创建业务 Task、不回复客户、不调用业务写接口 |
| SuperAgent | 外部 AI / Agent 能力提供方 | 创建 Agent Session、发送消息、解析 SSE、返回建议或回答 | 不决定业务动作、不绕过人工确认、不直接写业务系统 |
| SourceMessage Inbox | 平台缓冲层 | 不可变保存来源消息和捕获状态 | 不表达 AI 结论或业务归属 |
| AiCapabilityPort | 平台能力端口 | 隔离业务层和具体 Provider SDK / HTTP 协议 | 不暴露 Provider DTO 给领域层 |
核心原则:
- 前端不直接调用 SuperAgent 或 AgentBus,不接触任何 Provider Secret。
- AgentBus 实时入口必须先落来源事实;如需推送 SuperAgent,只能通过入库后的受控异步 dispatch / outbox 链路完成,不直接生成 Case、Task、Operation 或客户回复。
- SuperAgent 返回内容只能作为建议、证据或审计结果,不能直接改变业务最终状态。
- 业务写操作必须经过平台规则校验、权限控制、幂等控制和人工确认。
## 3. 推荐模块拆分
其他 Java / Spring Boot 项目接入时,建议按以下模块复制思路,而不是复制 TH Hotel 的
预订部业务代码。
```text
platform
├── message
│ ├── SourceMessageInbox
│ ├── MessageEvent
│ └── Evidence
├── ai
│ ├── AgentCapabilityPort
│ ├── AgentCapabilityRequest
│ ├── AgentCapabilityResult
│ └── AiCapabilityInvocation
└── system
├── SuperAgentProbeController
├── AgentBusProbeStatusController
└── SourceMessageReplayController
integrations
├── ai
│ └── superagent
└── messaging
└── agentbus
```
TH Hotel 当前 M001 相关代码中的可参考文件:
| 目的 | 参考文件 |
| --- | --- |
| SourceMessage 查询与原文 API | `server/src/main/java/cn/nianxx/thhotel/platform/message/control/SourceMessageController.java` |
| SourceMessage 捕获服务 | `server/src/main/java/cn/nianxx/thhotel/platform/message/service/impl/SourceMessageCaptureServiceImpl.java` |
| SourceMessage 原文读取服务 | `server/src/main/java/cn/nianxx/thhotel/platform/message/service/impl/SourceMessageOriginalServiceImpl.java` |
| SourceMessage 持久化边界 | `server/src/main/java/cn/nianxx/thhotel/platform/message/repository/MybatisSourceMessageInboxRepository.java` |
| AgentBus WebSocket 客户端 | `server/src/main/java/cn/nianxx/thhotel/integrations/messaging/agentbus/adapter/AgentBusWebSocketClient.java` |
| AgentBus frame 处理 | `server/src/main/java/cn/nianxx/thhotel/integrations/messaging/agentbus/adapter/AgentBusFrameProcessor.java` |
| AgentBus 到 SourceMessage 适配 | `server/src/main/java/cn/nianxx/thhotel/integrations/messaging/agentbus/adapter/AgentBusSourceMessageAdapter.java` |
| AgentBus 状态 API | `server/src/main/java/cn/nianxx/thhotel/platform/system/control/AgentBusProbeStatusController.java` |
| SourceMessage 表结构 | `server/src/main/resources/db/migration/V1__create_source_message_inbox.sql` |
| SourceMessage 原文读取审计表 | `server/src/main/resources/db/migration/V2__create_source_message_original_access_audit.sql` |
SourceMessage Replay 到 MessageEvent / Evidence 尚未实现,需等 MessageEvent、Evidence 字段模型确认后再进入后续 checkpoint。
AgentBus SourceMessage 入库后自动分发 SuperAgent 后端 V1 已实现,默认关闭,详见
`docs/project/requirements/M007-agentbus-superagent-auto-dispatch-v1.md`。
## 4. SuperAgent 对接
### 4.1 运行时配置
最小配置建议:
```text
AI_PROVIDER_ENABLED=false
DEERFLOW_BASE_URL=https://superagent.nianxx.cn
DEERFLOW_OPEN_API_KEY=
SUPERAGENT_PROBE_ENABLED=false
SUPERAGENT_PROBE_ACCESS_KEY=
SUPERAGENT_CONNECT_TIMEOUT=15s
SUPERAGENT_READ_TIMEOUT=180s
SUPERAGENT_MAX_MESSAGE_CHARS=4000
SUPERAGENT_EXTERNAL_SUBJECT_ID=your-project-superagent-probe
```
变量说明:
| 变量 | 是否 Secret | 说明 |
| --- | --- | --- |
| `AI_PROVIDER_ENABLED` | 否 | 是否启用真实 SuperAgent Provider Adapter。默认关闭。 |
| `DEERFLOW_BASE_URL` | 否 | SuperAgent / DeerFlow Open API 地址。 |
| `DEERFLOW_OPEN_API_KEY` | 是 | Open API Key,应只存在后端环境变量或 Secret Manager。 |
| `SUPERAGENT_PROBE_ENABLED` | 否 | 是否开放本项目自己的探针接口。生产默认关闭。 |
| `SUPERAGENT_PROBE_ACCESS_KEY` | 是 | 调用探针接口的本地访问密钥,不是 Provider API Key。 |
| `SUPERAGENT_CONNECT_TIMEOUT` | 否 | 建立连接超时。 |
| `SUPERAGENT_READ_TIMEOUT` | 否 | 旧版 RestClient SSE 读取超时兼容变量;当前 JDK SSE 客户端不设置整段 SSE 固定读取超时,断流恢复由 run/events 机制处理。 |
| `SUPERAGENT_MAX_MESSAGE_CHARS` | 否 | 单次发送给 Provider 的消息长度上限。 |
| `SUPERAGENT_EXTERNAL_SUBJECT_ID` | 否 | 创建 Agent Session 时使用的外部主体标识。 |
### 4.2 当前已验证的 Open API 调用形态
当前 TH Hotel 已验证的流程:
```text
POST /api/open/agent-sessions
→ 获取 session_id
→ POST /api/open/agent-sessions/{sessionId}/messages/stream
→ 读取 text/event-stream
→ 解析最终 answer、run、profile、model、token usage
```
2026-07-12 Open API 文档中,Java SSE 调用使用 `Authorization: Bearer <DEERFLOW_OPEN_API_KEY>` 或
`X-DeerFlow-Open-API-Key` 鉴权,并通过 `X-Request-ID`、`idempotency_key` 和 metadata 做调用关联。
当前 TH Hotel 后端 `SuperAgentOpenApiClientImpl` 已发送 CSRF double-submit。CSRF token 由后端每次请求临时生成,不走环境变量,不作为长期 Secret 保存。
2026-08-10 对 `https://superagent.nianxx.cn` 的真实合成 smoke 证明:用户提供的 Curl 示例未携带
CSRF 时当前服务端返回 403;添加同值 `X-CSRF-Token` 与 `csrf_token` Cookie 后 session 创建成功。
因此示例文档的鉴权片段不能单独作为当前运行契约,后端 client 的 double-submit 行为必须保留。
```text
Authorization: Bearer <DEERFLOW_OPEN_API_KEY>
X-Request-ID: <stable-request-id>
X-CSRF-Token: <temporary-random-token>
Cookie: csrf_token=<same-temporary-random-token>
```
Debug EML 和 M007 AgentBus 自动分发复用同一个 Open API client,因此两条链路都会携带上述 CSRF header / cookie。
### 4.3 Session 请求示例
```json
{
"external_subject_id": "your-project-superagent-probe",
"idempotency_key": "your-project-superagent-session-<correlation-id>",
"metadata": {
"source": "your-project",
"purpose": "provider-connectivity-test"
}
}
```
### 4.4 SSE 消息请求示例
```json
{
"message": "请介绍一下你是谁。",
"idempotency_key": "your-project-superagent-message-<correlation-id>",
"metadata": {
"source": "your-project",
"purpose": "provider-flow-test"
}
}
```
### 4.5 SSE 解析口径
TH Hotel 当前观测并处理的事件类型:
| Event | 用途 |
| --- | --- |
| `metadata` | 读取 Run、Thread、Profile 等调用元数据 |
| `messages` | 流式消息增量或中间消息 |
| `values` | 阶段性或最终聚合状态 |
| `message.final` | 未开启公开 Trace 时的核心最终回答,data 包含 `run_id` 与 `text` |
| `end` | SSE 正常结束标志 |
解析最终答案时,不要简单拼接所有 `messages`。当前实现从后期 `values.messages[]` 中选择:
```text
type = ai
response_metadata.finish_reason = stop
content 非空
```
并记录:
- `run_id`
- `resolved_profile_id`
- `resolved_profile_version_id`
- `response_metadata.model_name`
- `usage_metadata.input_tokens`
- `usage_metadata.output_tokens`
- `usage_metadata.total_tokens`
- 已出现的 SSE event types
Trace 开启时仍要求公开 `run.completed status=success`;Trace 关闭时,core `message.final` 负责提供最终
文本,收到 `end` 后必须再查询 `GET /runs/{run_id}`,只有权威状态为 `success` 才成功,并从其
`metadata.resolved_profile_id` / `metadata.resolved_profile_version_id` 补齐审计字段。如果没有收到
`end`、无法找到最终回答或无法确认 run 成功,应视为协议失败,不要伪造成成功结果。
### 4.5.1 2026-07-12 SSE 断流恢复要求
2026-07-12 导入的 `docs/import/20260712/OPEN_AGENT_API_JAVA_SSE_CLIENT.md` 已补充 Java 后端调用
SuperAgent Open API 的稳定性要求。后续 TH Hotel 的共享 SuperAgent Open API client 必须满足:
- 初始 `messages/stream` 请求携带稳定 `X-Request-ID`。当前 Parsing Agent 与 Booking Business Agent 必须追加
`?include_trace=true`,显式 false 在调用前 fail closed;legacy Field Recovery 仍固定默认为 false。
- 同一业务 SourceMessage 的 `idempotency_key` 在所有尝试中保持不变。
- 初始 POST 成功后保存响应头 `Content-Location`,解析并保存 SuperAgent `run_id`。
- SSE 必须按帧解析 `event:`、`data:`、`id:` 和 heartbeat comment,并保存 `lastEventId`。
- 成功条件必须同时满足最终 AI 内容、顶层 `event: end`、权威 run success,且没有顶层 `error` 或
`run.failed`。权威 success 可来自公开 Trace 的 `run.completed`,无 Trace 时必须来自 run status 查询。
- EOF、Premature EOF、incomplete chunked response 不能当成功。
- 如果已有 `run_id`,断流后不得重新 POST 初始消息,应先查询 `GET /runs/{run_id}`,再通过 `GET /runs/{run_id}/events` 携带 `Last-Event-ID` 恢复。
- 恢复失败应记录为可诊断失败,不返回部分回答。
中文说明:该要求同时适用于 Debug EML 和 M007 AgentBus 自动分发链路。实现时应优先改造共享
SuperAgent Open API client,避免调试链路和生产链路行为分叉。
### 4.6 平台能力端口
其他项目建议定义一个稳定端口,例如:
```java
public interface AgentCapabilityPort {
AgentCapabilityResult invoke(AgentCapabilityRequest request);
}
```
领域层只依赖这个端口,不依赖 SuperAgent HTTP DTO、SSE event、Profile ID 或厂商 SDK。
建议 `AgentCapabilityResult` 至少包含:
```text
providerCode
responseSchemaVersion
providerSessionId
providerRequestId
providerProfileId
providerProfileVersionId
providerModelId
outputText 或 outputReference
usageMetadata
```
### 4.7 调用审计
建议每次外部能力调用都写入不可变审计表。TH Hotel 当前表为
`platform_ai_capability_invocation`,参考:
- `server/src/main/resources/db/migration/V2__create_ai_capability_invocation.sql`
- `docs/database/ai-capability-invocation-data-dictionary.md`
审计表应记录成功与失败,不应记录:
- Provider API Key
- Cookie
- Authorization
- Chain of Thought
- Provider 内部 Plan / Memory
- 未脱敏的个人信息
### 4.8 M012 Layer 3 字段 Recovery 同步等待
M012 新增一条与旧 V4 `task-results`、MCP submit、M007 AgentBus dispatch 均独立的后端出站链路:
```text
MANUAL_EML → QBD deterministic Parser
→ RecoveryRequestSet(仅 UNRESOLVED + RECOVERABLE 字段及最小锁定上下文)
→ SuperAgent 专用解析 Agent / installed fixed-channel-field-recovery Skill
→ 信息系统在 max-wait 内等待完整 SSE 成功结果
→ 严格 JSON-only RecoveryPatchSet
→ 本地 Validator / dependency resolver / projector
→ PostgreSQL invocation audit + atomic Assembly + EffectiveFactView artifact
```
运行配置使用 `booking.field-recovery.*`,默认关闭。启用时至少需要同时满足:
- `booking.postgres.enabled=true`;
- `booking.field-recovery.enabled=true`;
- `booking.field-recovery.open-api.base-url=https://superagent.nianxx.cn`(可按环境覆盖);
- `booking.field-recovery.open-api.api-key` 由解析 Agent 专属部署 Secret 注入;
- `booking.field-recovery.open-api.include-trace=false`;当前解析 Agent 外部应用策略禁用公开 Trace;
- 该 `df_open_...` token 对应的外部应用策略已绑定安装 Recovery Skill 的已发布 Profile,并开启 API exposure;
- `max-wait` 有界且不超过 10 分钟,请求和最终回答均受字符数上限保护。
公开 API 请求不传 `profile_id`。Profile 由 token 对应的外部应用策略选择;`external_subject_id` 只是
信息系统侧主体标识,Recovery 由 `source_message_id` 哈希稳定派生。通用 client factory 只复用
session/SSE transport;解析 Agent 与未来 Booking Business Agent 必须分别建立外部应用、Secret、
wrapper/config 和调用审计,不能共享 key。
真实合成 smoke 已确认该 token 可完成 session、无 Trace SSE、core `message.final`、`end` 与 run status
success,并解析到已发布 Profile/version;完整 `RecoveryRequestSet → RecoveryPatchSet` 业务输出和
PostgreSQL V3 事务链仍待测试/预生产联调,不能据此宣称字段 Recovery 已上线。
当前 checkpoint 只允许 `MANUAL_EML` 等待。`AGENTBUS` 仍在 WebSocket frame 线程同步进入 Booking
编排器,因此明确跳过该调用;只有先把 AgentBus 的 Booking 下游迁入受控 worker 后,才可在 worker
内等待解析 Agent。Provider 输出只是 patch 建议,不直接覆盖 Parser artifact、不进入 PMS/Opera,也不在本
checkpoint 提前喂给 Layer 4/5。
### 4.9 M012 Fixed-Channel Parsing Agent v1 durable 出站边界
新的QBD/LIANTAI共用Parsing Agent不复制4.8的`MANUAL_EML`同步例外。代码已完成专属
`booking.parsing-agent.*` properties、SuperAgent wrapper、adapter及CP4 pre-Context durable主链,但Port只
能由worker调用;AgentBus WebSocket callback、手工HTTP线程和前端请求线程都不得直接等待。
```text
SourceMessage + immutable public ParserResult
→ durable execution claim/lease/fence
→ complete Current/History + deterministic evidence registry
→ fixed-channel-parsing-agent-v1 AgentRequest
→ Parsing Agent专属external app/token + shared session/SSE transport
→ resolved Profile ID gate + published version audit
→ strict local decoder/semantic validator
→ deterministic local merger
→ normalized Agent result + authoritative Layer3 artifact
```
运行边界:
- Parsing Agent、Field Recovery和Booking Business三者只共享无身份transport factory;各自拥有key、wrapper、
config、hash subject与审计,禁止fallback。
- adapter要求`include-trace=true`并由配置校验防止关闭,限制UTF-8 request/message/response bytes与整体
deadline;History超限时 fail closed,绝不截断或摘要替代。
- Trace 模式必须收到公开`run.completed(status=success)`和顶层`end`。既有execution audit仍保留安全聚合;
PostgreSQL V13共用journal另在语义解析/去重前按实际接收顺序保存每条公开事件,并保存transport最终交给decoder
的完整答案。重复投递照录,失败/超时/断流时保留已收到部分。
- journal只记录Provider返回,不记录API Key、Authorization、Cookie、CSRF、数据库密码、请求头或出站完整请求;
不推导平台未返回的隐藏思考。
- expected Profile ID必须由部署配置提供并与run metadata精确一致;实际published version只记录,不参与任务放行。
Main Prompt和Skill不由API request绑定;其平台安装/发布证据仍是单独release gate。
- adapter仍只把raw JSON作为业务候选送到进程内validator;完整返回原文由共用journal作为运行证据保存,不写入
Parser artifact,也不改变Parser-only review或Layer 3业务边界。
- 当前`booking.parsing-agent.enabled/provider-enabled/worker-enabled=false`且production强制false;CP4 fake链
完成不代表真实Provider、scheduler或生产执行已经启用。
### 4.10 M012 Booking Business Agent V2 durable 出站边界
Layer 3/4 到 Layer 5 的正式运行路径复用既有 `booking.agent.open-api.*`、
`SuperAgentBookingBusinessOpenApiClient` 和共享 SSE transport,不创建第二套 Booking client、凭据或状态机。
Booking、Parsing、Field Recovery、AgentBus 与 Debug EML 只共享无身份 transport;Booking Agent 必须使用自己的
外部应用 Secret,禁止 key fallback。
```text
final Layer3ResultV2
→ Layer 4 + canonical BookingDecisionInputV2
→ 短事务保存 Layer 3/4/input、enqueue execution、标记 AWAITING_BOOKING_AGENT
→ commit
→ V11 worker claim(SKIP LOCKED + lease + fencing)
→ 无数据库事务:专用 Booking port 调用 SuperAgent
→ Profile/版本/大小/deadline + JSON-only CandidateDecision 严格校验
→ 短事务提交 Candidate 与成功 attempt audit
→ 后续 claim 从持久 Candidate 恢复 Layer 6
```
运行边界:
- 只有 `booking.postgres.enabled`、`booking.agent.enabled`、`booking.agent.provider-enabled` 和
`booking.agent.worker-enabled` 同时为 true,durable worker 才注册;默认值与 production profile 均保持关闭。
- Provider 调用前有“当前线程无 Spring transaction”的可执行保护。Provider 超时、断流、临时网络错误或非法
Candidate 不得回滚已经提交的 Parsing、Layer 3/4 和 input。
- 同一 processing run 只有一个 execution,幂等键为 `booking-v2:<canonical input sha256>`。到期 lease 可由
重启后的 worker 接管;已保存 Candidate 只恢复 Layer 6,不再调用 Provider。
- 超时、断流、HTTP 408/425/429/5xx 和临时 transport 错误按有限次数、上限退避重试;JSON、契约、逻辑或平台
Profile 版本、input hash、evidence/reference 和大小错误不可重试,直接形成安全 `FAIL_CLOSED` Risk 后进入 Layer 6。
- 每个真正发出的 Provider attempt 必须先有受控 invocation audit;记录 input/response hash、execution/attempt、
Provider session/run/profile/version、duration、event types 与 failure class。V13 journal再以该invocation/attempt为
关联保存Provider实际返回的公开事件与最终答案原文;出站完整request、Secret、Authorization、Cookie、CSRF和
数据库密码不进入journal。
- Booking 调用必须携带`include_trace=true`,显式 false 在调用前拒绝;成功必须同时具备公开
`run.completed(status=success)`和顶层`end`。既有`public_trace_events`继续作安全聚合,完整公开返回以journal
为权威;Provider返回不做摘要、截断、脱敏或去重。
- Candidate 只是 Layer 5 候选,必须由本地 decoder 与 Layer 6 Validator 校验;Agent 不得直接创建 TaskCard、调用
PMS/Opera 或改变 Layer 6 的业务职责。
历史 CP5 证据记录测试 Profile ID `85ab8334-4e1c-4716-8ec0-9c3c099cec9b`、发布版本 ID
`9fed48c0-1f53-4d5a-8ba0-52865c738d83`(v7)及一次真实 Candidate 调用。该证据不能替代目标部署环境的当前
发布核验;本次实现环境没有专用 key、PostgreSQL 连接和 live flags,未重新查询或修改平台。放行前必须用最小
合成数据重新验证 resolved Profile ID/version,并完成 QBD、普通 LianTai 各 New/Update/Cancel/Allotment 的八场景
真实联调。该矩阵只证明核心接线,不代表 Rooming List 全业务完成。
2026-08-14 RC6 后续已用单项合成 Trace/Extra Bed 关闭“当前 resolved 身份+RC6 严格 payload”薄门禁:Profile
仍为 `85ab8334-4e1c-4716-8ec0-9c3c099cec9b`,发布 version ID 为
`b91686f4-b8b8-4ac0-aeb4-b7a2f54a0a41`,严格 Candidate/Layer 6 验证 1/1 通过。八场景、PostgreSQL 和
AgentBus 双 Agent 全链仍是独立部署门禁。
## 5. AgentBus 对接
### 5.1 运行时配置
最小配置建议:
```text
AGENTBUS_PROBE_ENABLED=false
AGENTBUS_WS_URL=wss://mesh.nianxx.cn/ws
AGENTBUS_WS_TOKEN=
AGENTBUS_BOT_ADDRESS=bot:external:listener
AGENTBUS_WS_RECONNECT_DELAY=5s
AGENTBUS_CONNECT_TIMEOUT=15s
AGENTBUS_SAMPLE_ENABLED=false
AGENTBUS_SAMPLE_DIR=var/agentbus-samples
AGENTBUS_MAX_FRAME_BYTES=1048576
AGENTBUS_MAX_SAMPLES=100
AGENTBUS_CAPTURE_ENABLED=true
AGENTBUS_SUPERAGENT_DISPATCH_ENABLED=false
AGENTBUS_SUPERAGENT_DISPATCH_WORKER_ENABLED=false
AGENTBUS_REPLY_MODE=NONE
AUTH_DEV_BOOTSTRAP_DEFAULT_HOTEL_ID=HOTEL-DEV
```
变量说明:
| 变量 | 是否 Secret | 说明 |
| --- | --- | --- |
| `AGENTBUS_PROBE_ENABLED` | 否 | 是否启用 AgentBus WebSocket 监听。默认关闭。 |
| `AGENTBUS_WS_URL` | 否 | AgentBus WebSocket 地址。 |
| `AGENTBUS_WS_TOKEN` | 是 | WebSocket 鉴权 Token。 |
| `AGENTBUS_BOT_ADDRESS` | 否 | 当前 Bot / Listener 地址。 |
| `AGENTBUS_WS_RECONNECT_DELAY` | 否 | 断线后的重连间隔。 |
| `AGENTBUS_CONNECT_TIMEOUT` | 否 | WebSocket 连接超时。 |
| `AGENTBUS_SAMPLE_ENABLED` | 否 | 是否保存本地原始 frame 样本。生产应默认关闭。 |
| `AGENTBUS_SAMPLE_DIR` | 否 | 本地样本目录,可能含 PII,不得提交。 |
| `AGENTBUS_MAX_FRAME_BYTES` | 否 | 单个入站 frame 最大字节数。 |
| `AGENTBUS_MAX_SAMPLES` | 否 | 最多保留的本地样本数。 |
| `AGENTBUS_CAPTURE_ENABLED` | 否 | 是否写入 SourceMessage Inbox。 |
| `AGENTBUS_SUPERAGENT_DISPATCH_ENABLED` | 否 | AgentBus 新邮件入库后是否创建 SuperAgent 自动分发记录,默认关闭。 |
| `AGENTBUS_SUPERAGENT_DISPATCH_WORKER_ENABLED` | 否 | 是否启动 SuperAgent 自动分发 worker,默认关闭。 |
| `AGENTBUS_SUPERAGENT_DISPATCH_MAX_ATTEMPTS` | 否 | 单条 dispatch 最大尝试次数,默认 `3`。 |
| `AGENTBUS_SUPERAGENT_DISPATCH_BATCH_SIZE` | 否 | worker 每轮领取数量,默认 `10`。 |
| `AGENTBUS_SUPERAGENT_DISPATCH_LOCK_TTL` | 否 | worker 抢占锁有效期,默认 `5m`。 |
| `SUPERAGENT_AGENTBUS_EXTERNAL_SUBJECT_ID` | 否 | AgentBus 自动分发创建 SuperAgent session 的 external subject id。 |
| `SUPERAGENT_OPEN_API_SSE_RECOVERY_MAX_ATTEMPTS` | 否 | SSE 断流恢复最大次数,默认 `5`。 |
| `AUTH_DEV_BOOTSTRAP_DEFAULT_HOTEL_ID` | 否 | dev 初始化平台酒店;M005 后 AgentBus 捕获运行时从 `platform_hotel` 唯一 `ACTIVE` 酒店解析系统酒店,不再依赖 `AGENTBUS_DEFAULT_HOTEL_ID`。 |
| `AGENTBUS_REPLY_MODE` | 否 | 调试回复模式。真实客户渠道应保持 `NONE`。 |
| `SOURCE_MESSAGE_*_ORIGINAL_READ_ACCESS_KEY` | 否 | 已废弃。SourceMessage 原文 / 会话正文读取已迁移到前端 Bearer 登录 + `SOURCE_MESSAGE_READ` + `SOURCE_MESSAGE_ORIGINAL_READ` + 酒店访问权,不再配置原文读取 key。 |
### 5.2 WebSocket 连接
当前实现使用 JDK `HttpClient` 的 WebSocket:
```text
Authorization: Bearer <AGENTBUS_WS_TOKEN>
GET <AGENTBUS_WS_URL>?ready=1
```
连接成功后应能收到 `session.ready`。
状态查询接口示例:
```text
GET /api/system/agentbus-probe
```
响应只应返回连接状态、计数器和最近错误代码,不返回 Token 或原始消息。
### 5.3 入站 frame 处理边界
推荐处理顺序:
```text
收到 raw WebSocket frame
→ 限制单帧大小
→ 可选本地采样
→ JSON 解析
→ 忽略 session.ready / task.progress / task.result 等控制事件
→ 将业务 payload 映射为 CaptureSourceMessageCommand
→ 写入 SourceMessage Inbox
→ 如 M007 dispatch 配置开启且为新建 RECEIVED Inbox,创建 SuperAgent dispatch run
```
实时链路禁止:
- 自动发送 ACK。
- 自动发送 `task.result`。
- 自动回复客户。
- 直接创建 MessageEvent、Evidence、AI Recognition、Case、Task、Operation 或 Receipt。
- 直接调用 OHIP、ERP、支付系统等业务写接口。
- 在 WebSocket 回调事务内同步等待 SuperAgent 返回。
### 5.4 当前已确认的 Outlook Payload 关键字段
AgentBus 后续确认的 Outlook 邮件 payload 包括:
```text
text
body.content_type
body.html
body.text
inline_images[]
attachments[]
received_at
source.channel
source.channel_account
source.external_message_id
source.external_conversation_id
source.sender
source.subject
source.sent_at
source.web_link
reply_policy.mode
reply_policy.final_only
```
当前 SourceMessage 捕获只依赖少量稳定字段:
| AgentBus 字段 | 平台字段 |
| --- | --- |
| `source.channel` | `channel`,例如 `EMAIL` |
| `source.external_message_id` | `externalMessageId`,作为幂等键组成部分 |
| `source.external_conversation_id` | `externalConversationId` |
| `payload.received_at` 或 `source.received_at` | `receivedAt`,作为邮件来源接收时间 |
| `source.sent_at` | `sourceSentAt`,作为邮件来源发送时间 |
| `source.sender` | `senderSummary`,当前作为发件人展示值不打码 |
| frame `id` | `agentbusFrameId` |
| frame `session_id` | `agentbusSessionId` |
| `payload` 规范 JSON | `payloadJson` 与 `payloadSha256` |
不要根据 envelope 的 `from`、`to`、`conversation_id` 猜测酒店、业务 Case 或下游 Task。
### 5.5 SourceMessage Inbox
建议单独建表保存 AgentBus 入站事实。TH Hotel 当前表为:
- `platform_source_message_inbox`
- `platform_source_message_replay_attempt`
参考:
- `server/src/main/resources/db/migration/V16__create_source_message_inbox.sql`
- `docs/agentbus-production-flow.md`
推荐幂等键:
```text
hotel_id + provider + channel + external_message_id
```
中文说明:M005 后 `hotel_id` 由本系统后端解析,不要求 AgentBus payload 携带酒店;单酒店阶段要求 `platform_hotel` 只有一家 `ACTIVE` 酒店。
重复投递时返回已有 Inbox,不覆盖原始 payload,不创建重复记录。
如果 payload 缺少必要字段或格式不符合预期,也应保存为 `FAILED` Inbox,并记录安全错误
摘要。错误摘要不得包含:
- 邮件正文
- HTML
- 附件 URL
- 完整邮箱地址
- Token / Cookie / Secret
### 5.6 Replay 到 MessageEvent / Evidence
SourceMessage Inbox 不应等同于正式业务消息。推荐增加受控 Replay:
```text
POST /api/system/source-message-inbox/{inboxId}/replay
Header: X-TH-Hotel-Source-Replay-Key
```
Replay 负责:
- 从 Inbox payload 提取 MessageEvent 字段。
- 保存正文或正文引用。
- 保存 Evidence 摘要或附件引用。
- 记录 replay attempt。
- 返回 MessageEvent ID 和状态。
Replay 接口默认关闭,仅在本地、UAT 或受控生产运维场景开启。
开发测试 AgentBus EML replay 的身份策略另有一项隔离规则:`PRESERVE_IDENTITY` 保留 EML 的 message/conversation
身份用于幂等验证;`FRESH_DELIVERY` 每次同时生成独立 message 与 conversation 身份。后者用于真实双 Agent 回放,
避免同一 EML 的早期调试副本被后续运行误当作真实 History;该规则不改变真实 AgentBus 入站邮件的 conversation 身份。
### 5.7 AgentBus 入库后自动分发 SuperAgent
M007 已实现的自动分发链路不是 Debug EML,也不是 SourceMessage Replay。它只负责把 AgentBus 新入库邮件异步交给 SuperAgent Open API:
```text
AgentBus 新业务 frame
→ SourceMessage Inbox RECEIVED
→ platform_superagent_dispatch_run PENDING
→ worker 调用 SuperAgent Open API
→ 保存 session_id / run_id / raw answer / parsed json / 状态
→ 等待 SuperAgent 后续通过 task-results 或 MCP 提交业务结果
```
第一版规则:
- 仅处理 `provider=AGENTBUS` 且 `capture_status=RECEIVED` 的新 SourceMessage。
- 重复 AgentBus 投递不重复创建 dispatch run。
- `provider=DEBUG_EML_UPLOAD` 不进入生产 dispatch。
- `FAILED` SourceMessage 不进入 dispatch。
- dispatch 成功不代表已经创建订单或任务。
- 任务创建仍由 SuperAgent 后续调用本系统任务结果通知接口或 MCP 写入工具触发。
- Debug EML 虽然不进入生产 dispatch,但 V4 smoke 默认复用实时 AgentBus V4 Open API subject:`th-hotel-agentbus-source-message`。历史 `th-hotel-debug-eml-upload` profile 如仍需排查旧 V2/V3 页面问题,必须通过环境变量显式指定,不能作为 M002 V4 smoke 默认 profile。
M011 CP3 已在 worker 调用 SuperAgent Open API 前增加可配置的 Booking Excel 附件预处理:
```text
SourceMessage payload + 附件引用
→ 识别 Excel 附件类型
→ 排除 PASSENGER_ROSTER 人员名单
→ 按月份窗口抽取 BOOKING_SURCHARGE / BOOKING_UPDATE 高亮行
→ 把 attachment_extractions[] 追加到发给 SuperAgent 的 AgentBus Outlook-like payload
```
中文说明:该预处理只增强 SuperAgent 输入证据,不改变 SourceMessage Inbox 的来源事实定位,也不直接创建订单、任务或客户回复。解析失败第一版建议写入安全 warning 并按配置决定是否继续调用 SuperAgent;日志和 dispatch run 不得保存完整附件 URL、签名参数、API Key、Cookie、Secret 或整份 Excel 内容。详细规则见
`docs/project/requirements/M011-booking-excel-pre-superagent-enrichment-v1.md`。
开启方式:
```text
AGENTBUS_TEST_SUPERAGENT_DISPATCH_INCLUDE_BOOKING_EXCEL_EXTRACTIONS=true
RESERVATION_BOOKING_EXCEL_EXTRACTION_ENABLED=true
```
中文说明:`agentbus.superagent-dispatch.include-booking-excel-extractions` 只控制 AgentBus worker 是否把抽取结果追加给 SuperAgent;`reservation.booking-excel-extraction.enabled` 是解析服务总开关。两者必须同时开启才会产生有效高亮行结果。当前测试机已开启该增强,生产环境默认关闭,生产开启需单独确认。
当前表名为 `platform_superagent_dispatch_run`,详细字段、状态流转、错误分类和验收标准见
`docs/project/requirements/M007-agentbus-superagent-auto-dispatch-v1.md`。
## 6. 其他项目最小落地顺序
### 阶段 1:SuperAgent 连通性
目标:
```text
后端探针
→ 创建 SuperAgent Session
→ 发送一条无 PII 测试消息
→ 解析 SSE 最终回答
→ 返回非敏感元数据
```
验收:
- HTTP 连接成功。
- SSE 收到 `end`。
- 最终回答非空。
- 日志不出现 API Key、Cookie、Session 原始值或客户信息。
### 阶段 2:AgentBus 连接
目标:
```text
AgentBus WebSocket
→ session.ready
→ 状态接口可见 connected/sessionReady
```
验收:
- 连接成功。
- 可断线重连。
- 不发送客户回复。
- 不保存本地 raw sample,除非临时排障。
### 阶段 3:SourceMessage Inbox
目标:
```text
AgentBus 入站业务 frame
→ SourceMessage Inbox
```
验收:
- 正常 payload 保存为 `RECEIVED`。
- 无效 payload 保存为 `FAILED`。
- 重复外部消息不重复入库。
- 查询接口只返回安全摘要。
### 阶段 4:自动分发 SuperAgent
目标:
```text
SourceMessage Inbox
→ SuperAgent dispatch / outbox
→ SuperAgent Open API
→ 保存 dispatch run
```
验收:
- 仅新建 `AGENTBUS` + `RECEIVED` SourceMessage 创建 dispatch。
- 重复邮件不重复 dispatch。
- Debug EML 不进入生产 dispatch。
- SuperAgent SSE 成功条件和断流恢复符合 2026-07-12 新文档。
- dispatch 成功不直接创建订单或任务。
### 阶段 5:手动 Replay
目标:
```text
SourceMessage Inbox
→ MessageEvent / Evidence
```
验收:
- 同一 Inbox 可以按不同 `replayRunId` 多次 replay。
- 相同 `replayRunId` 幂等。
- Replay 失败有 attempt 记录。
- 响应不返回客户正文、HTML、附件 URL 或 Token。
### 阶段 6:业务接入 SuperAgent
目标:
```text
MessageEvent
→ AgentCapabilityPort
→ SuperAgent Adapter
→ AiCapabilityInvocation
→ 业务 Schema 校验
```
验收:
- Provider 返回记录为审计,不直接触发业务写操作。
- 结构化输出必须通过 Schema 校验。
- 无法映射或不可信结果进入人工处理。
## 7. 安全与日志清单
必须放入 Secret 管理,不得提交仓库:
- `DEERFLOW_OPEN_API_KEY`
- `SUPERAGENT_PROBE_ACCESS_KEY`
- `AGENTBUS_WS_TOKEN`
- `SOURCE_MESSAGE_REPLAY_ACCESS_KEY`
- 数据库密码
- 任何真实客户渠道 Token
普通日志和错误响应不得输出:
- Authorization
- Cookie
- Provider API Key
- AgentBus Token
- 邮件正文和 HTML
- 附件 URL
- 客人姓名、邮箱、电话、证件号
- 支付信息
本地采样要求:
- `AGENTBUS_SAMPLE_ENABLED` 默认 `false`。
- 只在隔离测试或排障时临时开启。
- 样本目录必须被 `.gitignore` 忽略。
- 排障结束后删除样本。
## 8. 测试建议
SuperAgent 建议覆盖:
- 缺失 API Key 时启动或调用失败。
- Bearer API Key 缺失、错误或权限不足时转换为受控错误。
- 创建 Session 成功。
- SSE 正常结束并解析最终回答。
- SSE 缺少 `end` 时失败。
- SSE 缺少最终回答时失败。
- Trace 模式缺少 `run.completed status=success`,或无 Trace 模式无法通过 run status 确认 success 时失败。
- Parsing/Booking 专用客户端的初始消息 URL 精确包含 `include_trace=true`,并拒绝显式 false 配置。
- 所有公开事件在业务解析前按原 `data` 内容与交付顺序进入journal;多行/空白不改写,重连重复投递照录;最终
交给decoder的答案另有`FINAL_ANSWER`记录。
- 出站API Key、Authorization、Cookie、CSRF、数据库密码和请求头不得进入journal;平台未返回的隐藏思考不补写。
- 无 Trace core `message.final` 能提取最终回答,并由 run metadata 补齐 Profile/version。
- SSE 断流后携带 `Last-Event-ID` 通过 `/runs/{run_id}/events` 恢复,且不重发初始 POST。
- HTTP 401 / 403 / 404 / 409 / 5xx 错误转换。
- 连接超时,以及 SSE 断流后的 run/events 恢复。
AgentBus 建议覆盖:
- `session.ready` 只更新状态,不写 Inbox。
- 控制事件不写 Inbox。
- 正常 Outlook payload 写入 Inbox。
- `captureEnabled=false` 时忽略业务 frame。
- 无效 payload 写入 `FAILED` Inbox。
- 超大 frame 被拒绝并记录错误代码。
- 重复外部消息保持幂等。
- 查询接口不返回原始 payload。
- Replay 相同 run id 幂等。
## 9. 常见误区
### 9.1 把 AgentBus 当 AI Provider
AgentBus 是消息入口,不是抽取模型。它可以传递 Email / LINE 原始事实,但不应该直接产生
业务最终判断。
### 9.2 把 SuperAgent 返回当业务事实
SuperAgent 返回的是 Provider 输出。即使未来返回结构化 JSON,也必须经过平台 Schema、
业务规则、Case 匹配和人工确认。
### 9.3 让浏览器直接调用 Provider
浏览器不能持有 Provider Key、AgentBus Token 或 replay access key。前端只调用本项目后端。
### 9.4 实时入口直接生成 Task
实时 AgentBus 链路如果直接创建 Task,会导致重复投递、字段不完整、后续协议变化和人工
回溯都难处理。先落 Inbox,再 Replay,是更稳的路线。
### 9.5 在文档或测试里保存真实邮件
真实邮件、附件 URL、客户姓名和联系方式都可能是敏感数据。测试夹具应使用合成数据。
## 9.1 2026-08-17 完整公开返回与生命周期日志(ADR-017)
- fixed-channel Parsing Agent 与 Booking Business Agent 的初始 message 请求必须带 `include_trace=true`;显式
false 在调用前拒绝。legacy Field Recovery 继续 no-Trace。
- 信息系统在测试与生产使用同一V13追加式journal:SuperAgent实际返回的session响应、每条公开事件、恢复run响应、
HTTP错误正文和最终答案,按原顺序、原内容保存,并记录双时间、event/session/run/execution/invocation/attempt。
- 既有安全Provider audit继续用于聚合诊断;完整返回以journal为权威。journal不参与Parser、Layer5、Layer6或任务卡。
- 开发回放的lifecycle V2按同一replay/processing run读取journal,复制与导出复用同一JSON。读取失败返回503,不
降级为残缺日志。
- 出站凭据/请求与未返回的隐藏思考排除。旧调用保持空journal;不得重发同一消息来“补日志”,否则会启动新run。
## 10. 接入前检查清单
接入 SuperAgent 前确认:
- [ ] 已获得 Open API Key 和允许访问的 Base URL。
- [ ] 已确认当前 Open API 鉴权方式;当前后端 client 会自动发送临时 CSRF double-submit header / cookie。
- [ ] 已确认 Session、Message、Run 的生命周期。
- [ ] 已确认 SSE 最终答案或结构化结果所在字段。
- [ ] 已定义 `AgentCapabilityPort` 和调用审计表。
- [ ] 已确认 Provider 输出不会直接触发业务写操作。
接入 AgentBus 前确认:
- [ ] 已获得 WebSocket URL、Token 和 Bot Address。
- [ ] 已确认真实渠道 payload 字段。
- [ ] 已确认外部消息稳定幂等键。
- [ ] 已确认断线重连和重复投递语义。
- [ ] 已确认是否允许 ACK 或客户回复;默认按禁止处理。
- [ ] 已建立 SourceMessage Inbox 和 Replay attempt。
- [ ] 已定义原始 payload 的保存、访问、保留和删除策略。
进入生产前确认:
- [ ] 所有 Secret 均通过环境变量或 Secret Manager 注入。
- [ ] `.env.example` 只有占位值。
- [ ] 日志脱敏已验证。
- [ ] 自动回复保持关闭。
- [ ] 自动业务写操作保持关闭,除非经过单独评审。
- [ ] 监控至少覆盖连接状态、失败次数、Replay 失败和 Provider 调用失败。