响应结构

所有 REST 接口统一使用以下响应包装结构。判断请求是否成功以 success 为准,错误重试策略可参考 httpStatusCode。

字段类型说明
successBooleantrue 表示请求成功,false 表示失败,是判断请求是否成功的唯一标准。
httpStatusCodeInteger标准 HTTP 响应码,用于错误类型区分,可作为重试策略判定依据。
dataObject响应数据对象,类型随接口变化。
messageString接口返回的提示信息(如有)。

SSE 事件结构

智能体运行接口(POST /api/agent/run/stream 与恢复接口 GET /api/agent/run/stream)采用标准 SSE 协议推送。每条事件的 data 字段为一个 JSON 对象,统一信封结构如下。

事件信封结构

字段类型说明
eventTypeString事件类型枚举值,见下方「事件类型一览」。
dataObject事件数据载体,不同事件类型携带不同结构(通过 type 字段区分)。
timestampString事件产生时间,ISO 格式。
stepInteger当前执行步骤编号(对应一次 LLM 调用),可为 null。
totalTokensLong累积消耗的 Token 总数(实时递增),可为 null。

事件类型一览

事件类型分类说明是否终止事件
SESSION_CREATED会话Agent 开始执行,返回会话标识—
TEXT_START文本文本输出片段开始—
TEXT_DELTA文本文本增量内容(流式逐字输出)—
TEXT_END文本文本输出片段结束—
REASONING_START推理模型推理过程开始—
REASONING_DELTA推理推理增量内容—
REASONING_END推理推理过程结束—
TOOL_START工具工具调用开始—
TOOL_RUNNING工具工具正在执行中—
TOOL_COMPLETED工具工具执行成功—
TOOL_FAILED工具工具执行失败—
TOOL_EVENT工具工具侧信道事件(执行过程中的附加信息)—
PERMISSION_ASK权限需要用户确认权限(阻塞等待决策)—
PERMISSION_ASK_RESOLVED权限权限确认结果已返回—
MESSAGE_COMPLETED消息Agent 执行完成是
MESSAGE_SUSPENDED消息Agent 执行挂起(等待用户操作)—
MESSAGE_FAILED消息Agent 执行失败是
SUPPLEMENTARY_INPUT_CONSUMED补充输入Agent 消费了用户的运行时追加输入—
ATTACHMENT_CHANGED附件附件发生变更(新建或更新),前端可实时刷新附件列表—
ATTACHMENT_SENT附件附件被主动发送给用户—
HEARTBEAT心跳连接保活信号,客户端应忽略—
AGENT_STATUS运行期状态Agent 运行期瞬时状态信号(如 LLM 限流、网络重试、上下文压缩等)—

终止事件说明: 收到 MESSAGE_COMPLETED 或 MESSAGE_FAILED 后 SSE 流将关闭;MESSAGE_SUSPENDED 不会关闭流,Agent 恢复后会继续推送事件。

各事件数据结构详解

1. SESSION_CREATED — 会话创建

Agent 开始执行时首先推送此事件,客户端可获取会话和消息标识用于后续操作。

字段类型说明
sessionIdLong会话 ID
userMessageIdLong触发本次执行的用户消息 ID
assistantMessageIdLongAgent 响应的助手消息 ID

2. TEXT_START / TEXT_DELTA / TEXT_END — 文本流式输出

Agent 生成的文本回复通过这三个事件流式推送。典型流程:TEXT_START → 多次 TEXT_DELTA → TEXT_END。

字段类型说明
textString文本内容。TEXT_START 为初始文本,TEXT_DELTA 为增量文本,TEXT_END 不携带文本字段。
syntheticBoolean是否为合成文本(如系统生成的提示信息)。仅 TEXT_START 事件携带此字段。

前端处理建议: 将 TEXT_START 和所有 TEXT_DELTA 的 text 拼接即为完整回复内容。

3. REASONING_START / REASONING_DELTA / REASONING_END — 推理过程

仅在使用支持推理的模型(如 QwQ 等深度思考模型)时产生,展示模型的思考过程。

字段类型说明
textString推理文本。REASONING_START 为初始内容,REASONING_DELTA 为增量内容,REASONING_END 不携带文本内容。

前端处理建议: 可折叠展示推理过程,帮助用户理解 Agent 的决策逻辑。

4. TOOL_START — 工具调用开始

Agent 决定调用某个工具时推送,携带工具名称、调用 ID 与参数。

5. TOOL_RUNNING — 工具执行中

工具正在执行的进度通知,可能多次推送。

6. TOOL_COMPLETED — 工具执行完成

工具成功返回结果,output 为工具输出的字符串(通常为 JSON)。

7. TOOL_FAILED — 工具执行失败

工具执行异常时推送,errorMessage 为错误信息。

工具事件字段汇总:

字段类型说明出现时机
toolCallIdString工具调用唯一标识所有 TOOL_* 事件
titleString工具调用的展示标题TOOL_RUNNING
toolNameString工具名称所有 TOOL_* 事件
argumentsString工具调用参数(JSON 格式)TOOL_START
outputString工具执行输出结果TOOL_COMPLETED
errorMessageString错误信息TOOL_FAILED

8. TOOL_EVENT — 工具侧信道事件

工具执行过程中产生的附加结构化信息,用于前端做差异化展示(如需要用户登录、资源已创建等)。

字段类型说明
toolCallIdString工具调用唯一标识
toolNameString工具名称
categoryString事件分类标识(如 auth_required、resource_created)
payloadObject结构化数据,内容随 category 变化
userMessageString用户友好的提示信息(可选)

前端处理建议: 根据 category 决定展示方式,payload 提供所需数据,userMessage 可直接展示给用户。

9. PERMISSION_ASK — 权限确认请求

当 Agent 执行的工具触发了权限规则时推送,需要用户确认后才能继续执行。

字段类型说明
askIdString确认请求唯一标识(回复时需携带)
principalIdLong发起请求的用户 ID
toolNameString触发确认的工具名称
toolCallIdString工具调用 ID
argumentsString工具参数(JSON),供用户了解将要执行的操作
ruleDescriptionString规则描述,告知用户为什么需要确认
rulePatternString触发的规则模式

前端处理建议: 展示确认对话框,显示 ruleDescription 和 arguments,用户决策后调用权限回复接口。

10. PERMISSION_ASK_RESOLVED — 权限确认结果

用户对权限请求做出决策后(或超时)推送。

字段类型说明
askIdString确认请求唯一标识
approvedBoolean是否批准(true = 批准,false = 拒绝)
reasonString结果原因(如「用户已批准」、「用户拒绝」、「等待超时」)

11. MESSAGE_COMPLETED — 执行完成

Agent 完成所有处理后推送,SSE 流随后关闭。

字段类型说明
messageIdLong消息 ID

12. MESSAGE_FAILED — 执行失败

Agent 执行过程中发生不可恢复的错误,SSE 流随后关闭。

字段类型说明
messageIdLong消息 ID
errorMessageString错误信息
errorCodeString运行错误码(如 LLM_ERROR),用于错误分类统计
recoverableBoolean是否可从失败点恢复(前端可据此渲染"重试"按钮)
networkErrorBoolean是否为网络类错误(DNS / 连接 / 读取超时等瞬态故障)

13. MESSAGE_SUSPENDED — 执行挂起

Agent 执行被挂起等待外部操作(如权限确认、节点关停等),SSE 流保持打开。

字段类型说明
messageIdLong消息 ID
suspendReasonObject挂起原因,包含 type 字段标识挂起类型以及对应上下文数据

各挂起类型的字段详解:

PERMISSION_ASK(等待权限确认):

字段类型说明
typeString固定为 PERMISSION_ASK
askIdString确认请求唯一标识
toolCallIdString触发确认的工具调用 ID
toolNameString工具名称
argumentsString工具参数(JSON)
pendingToolCallsArray同批次尚未执行的后续工具调用
stepInteger挂起时的步骤编号
messageIdLong关联的 assistant 消息 ID

MANUAL_SUSPEND(手动挂起):

字段类型说明
typeString固定为 MANUAL_SUSPEND
pendingToolCallsArray挂起时尚未执行的工具调用
stepInteger挂起时的步骤编号
messageIdLong关联的 assistant 消息 ID

SHUTDOWN_INTERRUPT(节点关闭中断):

字段类型说明
typeString固定为 SHUTDOWN_INTERRUPT
pendingToolCallsArray挂起时尚未执行的工具调用(崩溃恢复时为空)
stepInteger挂起时的步骤编号(-1 表示未知)
messageIdLong关联的 assistant 消息 ID

ASYNC_TOOL_PENDING(异步工具待完成):

字段类型说明
typeString固定为 ASYNC_TOOL_PENDING
asyncTaskIdString外部异步任务 ID
toolCallIdString触发异步工具的工具调用 ID
toolNameString工具名称
argumentsString工具参数(JSON)
pendingToolCallsArray异步工具之后尚未执行的工具调用
stepInteger挂起时的步骤编号
messageIdLong关联的 assistant 消息 ID
submittedAtMsLong提交时间戳(毫秒)
timeoutMsLong超时时长(毫秒)
awakeStrategyString唤醒策略(POLL / NOTIFY)
toolPartIdLong恢复时定位原 ToolPart 的 partId

14. SUPPLEMENTARY_INPUT_CONSUMED — 补充输入已消费

Agent 在执行步骤间隙消费了用户追加的输入,开启新的对话轮次。

字段类型说明
inputIdString被消费的补充输入 ID
contentString输入内容(前端展示用)
userMessageIdLong新创建的用户消息 ID
assistantMessageIdLong新创建的助手消息 ID(后续回复归属此消息)

15. ATTACHMENT_CHANGED / ATTACHMENT_SENT — 附件变更与发送

智能体在执行过程中产生或发送附件时推送,前端可据此实时刷新附件列表。

两者携带相同的数据结构,通过 eventType 区分语义:ATTACHMENT_CHANGED 表示「文件被转存为会话附件」,ATTACHMENT_SENT 表示「附件被主动发送给用户」。

字段类型说明
attachmentIdLong附件 ID
sessionIdLong所属会话 ID
messageIdLong关联消息 ID(可为 null)
fileNameString文件名
fileExtensionString文件扩展名(如 pdf、xlsx、png),前端可据此选择图标
contentTypeStringMIME 类型(如 application/pdf),前端可据此决定预览方式
fileSizeLong文件大小(字节)
virtualPathString虚拟路径,用于构造下载 URL(/api/agent/attachment/download?path=...)
sourceString附件来源:USER_UPLOAD(用户上传)、AGENT_GENERATED(智能体生成)
changeTypeString变更类型:CREATED(新建)、UPDATED(更新)

前端处理建议: 收到此事件后可实时刷新附件列表,无需在消息结束后再调用 GET /api/agent/attachment/getByMessage 轮询。

16. HEARTBEAT — 心跳保活

每 15 秒推送一次,用于保持 SSE 连接不被中间代理或防火墙关闭。

注意: 心跳事件不携带 SSE ID,不影响断线重连的 lastEventId 计算。客户端应直接忽略此事件。

17. AGENT_STATUS — Agent 运行期状态

Agent 执行过程中的瞬时状态信号。该事件不写入消息记录、不回灌 LLM 上下文,任务终态后不可见。前端收到后可展示临时提示(如"模型限流中,正在重试..."),下一条内容事件抵达即隐含恢复。

字段类型说明
codeString状态码枚举,见下方「状态码一览」
levelString严重等级:INFO(信息提示)/ WARN(警告,可恢复)/ ERROR(错误,未到终态)
messageString用户可读文案,前端可直接展示
attributesObject结构化扩展属性,不同 code 携带不同字段(如 attempt、maxRetries、backoffMs)

状态码一览:

状态码等级说明
LLM_RATE_LIMITEDWARN模型限流,正在重试中
LLM_NETWORK_RETRYINGWARN模型网络抖动,正在重连中
LLM_AUTO_RECOVERINGWARN网络类失败已被拦截,正在从失败点自动恢复
LLM_EMPTY_RESPONSE_RETRYINGWARN模型返回空回复,正在自动重试
CONTEXT_COMPACTINGINFO正在对历史上下文做语义压缩

前端处理建议: 根据 level 统一渲染样式(颜色/图标),按 code 做差异化处理。状态码采用只追加策略,前端遇到未知 code 时按 level 降级展示即可。

典型事件流示例

一次完整的 Agent 执行,事件推送顺序示例:

text
SESSION_CREATED          → 会话建立
REASONING_START          → 模型开始思考
REASONING_DELTA …        → 思考过程流式输出
REASONING_END            → 思考完成
TOOL_START               → 决定调用工具
TOOL_RUNNING             → 工具执行中
TOOL_COMPLETED           → 工具返回结果
ATTACHMENT_CHANGED       → 文件产出被转存为附件
AGENT_STATUS             → 模型限流,正在重试中(瞬时状态)
TEXT_START               → 开始生成回复(状态隐含恢复)
TEXT_DELTA …             → 回复文本流式输出
ATTACHMENT_SENT          → 附件被主动发送给用户
TEXT_END                 → 文本输出完成
MESSAGE_COMPLETED        → 执行结束,SSE 流关闭

断线重连机制

  1. 每个非心跳事件携带递增的 SSE id(在 SSE 协议层,非 JSON 内部)。
  2. 客户端断线后,使用 lastEventId 参数重连:GET /api/agent/run/stream?requestId={requestId}&lastEventId={lastId}。
  3. 服务端从断点之后的事件开始推送,确保不丢失任何事件。
  4. 心跳事件不分配 ID,不干扰重连定位。

Webhook 事件结构

配置 Hook(拦截型 pre_tool_use / user_message / pre_scheduled_task,观察型 post_tool_use / session_start / agent_completion / post_llm_call / post_scheduled_task)的 dispatch_type=webhook 后,系统会以 HTTP POST 调用客户回调地址。本节定义信封字段、HTTP 头、签名算法、各 hookType 的 context 子结构,以及拦截型响应格式。

调用流程: 触发点命中 → 构造事件信封(含 eventId / traceId / 业务 context)→ 计算 HMAC 签名 → POST 客户 URL → 拦截型同步等待响应解析为 Allow/Deny/Modify;观察型异步投递,失败按秒级快档 + 分钟级阶梯退避重试,争取最终送达。

HTTP 请求规范

项值
MethodPOST
Content-Typeapplication/json
超时拦截型默认 3000 ms,观察型默认 5000 ms(可被 dispatch_config.timeoutMs 覆盖)
重试拦截型不重试;观察型按退避阶梯重试(详见下方投递可靠性),重试期间 eventId / occurredAt 不变,dispatchedAt / deliveryAttempt 更新并重新签名
幂等客户应基于 eventId 去重(重试时同一事件 eventId 一致)

请求头

头部出现时机说明
X-Request-Id当前调用链有 traceId 时链路追踪 ID,与信封 traceId 字段同值
X-Webhook-Timestamp配置了 signingSecret 时签名时间戳(epoch 毫秒),与本次请求体一起参与签名
X-Webhook-Signature配置了 signingSecret 时签名串,格式 sha256=<hex>
Authorization 等配置了 auth 时由统一鉴权模型 FinxHttpAuth 注入,支持 NONE / API_KEY / BEARER / BASIC / CUSTOM / CUSTOM_HTTP_TOKEN

签名算法

服务端使用 HMAC-SHA256 对 timestamp + "." + rawBody 签名(rawBody 是实际发送的字节序列),结果以 sha256=<lowercase-hex> 形式置入 X-Webhook-Signature。

客户端验签步骤:

  1. 校验 X-Webhook-Timestamp 与本地时钟偏差在合理窗口内(建议 ±5 分钟),防止重放。
  2. 用约定的 signingSecret 重新计算 HMAC-SHA256,与请求头里的签名做常时间比较(如 Java MessageDigest.isEqual、Python hmac.compare_digest),防止时序攻击。
  3. 校验失败一律拒绝处理。

签名算法可能演进。 头部前缀 sha256= 用于将来切换算法(如 sha512=),客户端应解析前缀而非硬编码算法名。

dispatch_config 字段详解

Hook 的投递行为由 dispatch_config(JSON)控制,字段如下:

字段类型默认值说明
urlString—(必填)回调地址,须以 http:// 或 https:// 开头(生产建议 HTTPS)
timeoutMsInteger拦截型 3000 / 观察型 5000单次请求超时(毫秒)
maxRetriesInteger3仅观察型生效;只截断快档重试次数(0~3 档),分钟级阶梯恒定附加
signingSecretString不签名HMAC 签名密钥;服务端仅存储不回显,出参中 mask 为 ***
failureModeStringALLOW仅拦截型生效;失败时 ALLOW(fail-open 放行)或 DENY(fail-closed 拒绝)
authObject不鉴权统一鉴权模型 FinxHttpAuth,类型见下表

auth 鉴权类型一览:

类型注入方式
NONE不附加鉴权头
API_KEY自定义 Header(默认 X-API-Key)+ 凭证值
BEARERAuthorization: Bearer <credential>(前缀可自定义)
BASICAuthorization: Basic <credential>
CUSTOM自定义静态请求头集合
CUSTOM_HTTP_TOKEN按 tokenFetchConfig 先调用客户端点换取动态 token 再注入

toolNameMatch 匹配语法(仅对 pre_tool_use / post_tool_use 生效,默认 *):

写法语义示例
* 或留空匹配全部工具*
/正则/斜杠包裹的正则匹配/^transfer.*/
a|b|c管道分隔枚举,命中其一即匹配transferFunds|payOrder
其余与工具名精确匹配transferFunds

不支持 glob 通配(如 transfer* 会被当作精确匹配处理)。

signingSecret 更新语义(编辑 Hook 时):

传值行为
不传或传 ***保留原密钥
传新值替换为新密钥
传空字符串 ""清除密钥,后续投递不再签名

投递可靠性

拦截型: 不重试。超时 / 非 2xx / 解析失败直接按 failureMode 降级。

观察型重试阶梯: 首投失败后按以下阶梯退避重投,每档附加 ±25% 随机抖动:

阶段间隔说明
快档1.5s → 3s → 6s兜网络抖动 / 瞬时 5xx;档数受 maxRetries 截断(0~3 档)
分钟级阶梯1m → 3m → 5m → 10m → 20m → 30m兜持续故障,恒定附加在快档之后,不受 maxRetries 影响

默认配置(maxRetries=3)下最多 1 次首投 + 9 次重试,总投递窗口约 70 分钟。

重试条件: 仅 429、5xx 与网络类错误(超时 / 连接失败)触发重试;其余 4xx 视为客户端明确拒绝,不重试直接丢弃。重试期间 eventId / occurredAt 不变,dispatchedAt / deliveryAttempt 每次更新并重新签名。

熔断与降级: 平台按回调 URL 维度做健康统计,连续多次「无响应」类故障(超时 / 网络 / 连接错误)会触发短暂熔断,随后以单探针试探恢复;熔断期间拦截型直接按 failureMode 短路降级。收到 HTTP 响应(含 4xx / 5xx)视为端点存活,不计入熔断失败。另有拦截型并发上限与观察型在途重试上限等舱壁保护。

熔断阈值、窗口时长与并发上限均为平台内部保护参数,可能随版本调整,不构成接口契约;客户端只需实现幂等与验签即可。

事件信封

所有 hookType 共用同一个顶层信封,业务字段在 context 中。

字段类型说明
schemaVersionString协议版本号,当前固定为 "1.0"
eventIdString事件唯一 ID(TSID),重试时不变,客户端据此幂等
hookTypeString触发类型,枚举见下方「hookType 一览」
hookNameStringHook 配置名称(创建时定义)
occurredAtLong事件发生时间(epoch 毫秒),首次发出时记录,重试不变
dispatchedAtLong本次投递时间(epoch 毫秒),每次重试更新
deliveryAttemptInteger投递尝试次数,首次为 1,重试递增
traceIdString链路追踪 ID(可空),与请求头 X-Request-Id 同值
sessionIdLong会话 ID(部分 hookType 适用,可空)
principalIdLong主体 ID / 用户标识(部分 hookType 适用,可空)
agentIdLong智能体 ID(部分 hookType 适用,可空)
contextObject业务上下文,结构因 hookType 而异

hookType 一览

hookType分类触发时机是否影响执行流
pre_tool_use拦截型工具参数修复后、实际执行前Allow / Deny / Modify
user_message拦截型Agent 入口处接收到用户输入时Allow / Deny / Modify
pre_scheduled_task拦截型定时任务实际执行前Allow / Deny / Modify
post_tool_use观察型工具执行完成或失败后仅通知
session_start观察型会话创建后仅通知
agent_completion观察型Agent 执行完成或失败后仅通知
post_llm_call观察型每次 LLM 流式调用完成后仅通知
post_scheduled_task观察型定时任务执行完成或失败后仅通知

拦截型 vs 观察型差异:

维度拦截型观察型
调用方式同步阻塞,等待响应异步 fire-and-forget
响应要求必须 2xx + JSON body(缺字段按 Allow)仅要求 2xx,body 忽略
失败处理按 dispatch_config.failureMode 决定 ALLOW(默认)或 DENY(fail-closed)按退避阶梯重试,阶梯耗尽仍失败仅记日志
重试不重试秒级快档(受 maxRetries 截断)+ 分钟级阶梯持续重投,详见投递可靠性

各 hookType 的 context 结构

1. pre_tool_use — 工具调用前(拦截型)

字段类型说明
toolNameString工具名称
toolCallIdString工具调用 ID
argumentsObject工具调用参数;LLM 输出为合法 JSON 时是对象 / 数组,否则为原始字符串

Modify 决策时 modifiedValue 应为新的 arguments JSON 字符串。

2. post_tool_use — 工具调用后(观察型)

字段类型说明
toolNameString工具名称
toolCallIdString工具调用 ID
argumentsObject工具调用参数;合法 JSON 时是对象 / 数组,否则为原始字符串
outputObject工具执行输出;合法 JSON 时是对象,否则为原始字符串
succeededBoolean是否执行成功
errorMessageString错误信息(失败时非空)
durationMsLong执行耗时(毫秒)

3. user_message — 用户消息(拦截型)

字段类型说明
userInputString用户输入文本
chatModeString聊天模式:single_chat / group_chat(小写)
triggerSourceObject触发来源,含 sourceType(小写:human_message / scheduled_task / api_call / internal)、channelType(如 im)、channelProvider(dingtalk / wecom / wechat / feishu / qq);非渠道消息时 channel 字段为 null
bizKeyString业务键(可选)
userNicknameString用户昵称(可选)

Modify 决策时 modifiedValue 应为新的 userInput 字符串。

4. session_start — 会话开始(观察型)

字段类型说明
newSessionBoolean是否为新创建的会话;false 表示恢复已有会话

会话 / 主体 / 智能体标识请读信封级 sessionId / principalId / agentId。

5. agent_completion — Agent 执行完成(观察型)

字段类型说明
succeededBoolean是否执行成功
errorMessageString错误信息(失败时非空)
errorCodeString错误码(失败时非空,用于错误分类统计)
modelString模型名称
durationMsLong运行总耗时(毫秒)
totalTokensLong总 Token 消耗
stepCountInteger总步数
triggerSourceTypeString触发来源类型(小写):human_message / scheduled_task / api_call / internal
requestIdString本次 Agent Run 的唯一标识(TSID)
assistantMessageIdLong本轮回答消息 ID
userMessageIdLong触发本轮的用户消息 ID
newSessionBoolean是否为新创建的会话(首轮对话)
agentNameString智能体名称
channelProviderString渠道提供商(dingtalk / wecom / wechat / feishu / qq),仅 human_message 触发时有值

判断成功 / 失败: 以 succeeded 字段为准,不要依赖 errorMessage 是否为空。

6. post_llm_call — LLM 调用后(观察型)

字段类型说明
modelString模型名称
stepInteger当前步数
durationMsLongLLM 调用耗时(毫秒)
promptTokensLong输入令牌数
completionTokensLong输出令牌数
totalTokensLong总令牌数
cachedTokensLong缓存命中令牌数
reasoningTokensLong推理 / 思考过程令牌数

7. pre_scheduled_task — 定时任务前(拦截型)

字段类型说明
taskIdLong定时任务 ID
taskNameString任务名称

信封级 sessionId 语义: Pre 阶段运行时会话尚未创建,仅当任务配置了会话复用(reuseSession=true)时为任务绑定的会话 ID,否则为 null;实际执行会话以 post_scheduled_task 为准。

8. post_scheduled_task — 定时任务后(观察型)

字段类型说明
taskIdLong定时任务 ID
taskNameString任务名称
succeededBoolean是否执行成功
errorMessageString错误信息(失败时非空)
errorCodeString错误码(失败时非空)
durationMsLong任务执行总耗时(毫秒)
requestIdString对应 agent_run.request_id;在创建运行记录前提前失败时为 null(无对应运行记录)

信封级 sessionId 语义: 正常为本次执行实际使用的会话 ID;执行未产生会话(如提前失败)时回退为任务绑定的会话,仍无则为 null。

拦截型响应格式

拦截型 Hook 必须返回 HTTP 2xx 与 JSON 响应体;观察型仅需 HTTP 2xx,body 被忽略。

字段类型说明
decisionString决策枚举:allow / deny / modify(不区分大小写)
reasonString拒绝原因(decision=deny 时使用,反馈给 LLM 或用户)
modifiedValueString修改后的值(decision=modify 时使用);pre_tool_use 为新 arguments JSON 字符串,user_message 为新 userInput

容错策略:

  • 响应体为空、缺 decision 字段、或 decision 为未知值 → 按 allow 处理。
  • decision=modify 但缺 modifiedValue → 降级为 allow 并记录告警日志。
  • HTTP 非 2xx、超时、解析失败 → 按 dispatch_config.failureMode 处理:ALLOW(默认,fail-open)或 DENY(fail-closed,适用于审批 / 合规场景)。

客户端实现要点

  1. 幂等去重: 用 eventId 做 24 小时窗口去重,重试事件携带相同 eventId。
  2. 快速 ACK: 拦截型对延迟敏感,建议在 1 秒内返回响应;耗时业务(如二次 RPC)应改用观察型并异步处理。
  3. 签名验证: 生产环境必须校验 X-Webhook-Signature,避免接口被未授权调用伪造事件。
  4. 失败模式选型: 审批 / 合规 / 风控类拦截 Hook 应配置 failureMode=DENY;业务降级容忍场景保持默认 ALLOW。
  5. 观察型不要做关键决策: 观察型可能因网络问题最终失败,关键状态机不可依赖其投递成功。
  • 信封字段未来可能新增(向前兼容),客户端解析时忽略未知字段,不要严格 schema 校验。
  • 同一 eventId 可能因网络抖动被多次接收,业务侧必须做幂等。
  • signingSecret 仅服务端单向存储,出参中会被 mask 为 "***",丢失后须重新生成并下发。
  • pre_tool_use 的 modifiedValue 必须是合法 JSON 字符串,否则会被工具参数解析阶段拒绝。
  • 拦截型超时时间过短可能导致客户处理逻辑被中断,建议根据实际响应耗时设置 timeoutMs。
response.json
# 统一响应包装 {
"httpStatusCode": 200,
"message": "success",
"data": {},
"success": true
}
sse-events.json
# 事件信封结构 {
"eventType": "TEXT_DELTA",
"data": { "type": "text", "...": "..." },
"timestamp": "2026-05-07T10:30:00",
"step": 2,
"totalTokens": 1520
}
# 1. SESSION_CREATED — 会话创建 {
"eventType": "SESSION_CREATED",
"data": {
"type": "session",
"sessionId": 10001,
"userMessageId": 20001,
"assistantMessageId": 20002
},
"timestamp": "2026-05-07T10:30:00",
"step": null,
"totalTokens": null
}
# 2. TEXT_DELTA — 文本流式输出 {
"eventType": "TEXT_DELTA",
"data": {
"type": "text",
"text": "根据您的需求,我建议",
"synthetic": false
},
"timestamp": "2026-05-07T10:30:01",
"step": 1,
"totalTokens": 256
}
# 3. REASONING_DELTA — 推理过程 {
"eventType": "REASONING_DELTA",
"data": {
"type": "reasoning",
"text": "让我先分析一下用户的问题…"
},
"timestamp": "2026-05-07T10:30:00",
"step": 1,
"totalTokens": 128
}
# 4. TOOL_START — 工具调用开始 {
"eventType": "TOOL_START",
"data": {
"type": "tool",
"toolCallId": "call_abc123",
"title": "查询订单信息",
"toolName": "queryOrder",
"arguments": "{\"orderId\":\"ORD-2026001\"}"
},
"timestamp": "2026-05-07T10:30:02",
"step": 1,
"totalTokens": 512
}
# 5. TOOL_RUNNING — 工具执行中 {
"eventType": "TOOL_RUNNING",
"data": {
"type": "tool",
"toolCallId": "call_abc123",
"title": "查询订单信息",
"toolName": "queryOrder"
}
}
# 6. TOOL_COMPLETED — 工具执行完成 {
"eventType": "TOOL_COMPLETED",
"data": {
"type": "tool",
"toolCallId": "call_abc123",
"title": "查询订单信息",
"toolName": "queryOrder",
"output": "{\"status\":\"已发货\"}"
},
"timestamp": "2026-05-07T10:30:03",
"step": 1,
"totalTokens": 512
}
# 7. TOOL_FAILED — 工具执行失败 {
"eventType": "TOOL_FAILED",
"data": {
"type": "tool",
"toolCallId": "call_abc123",
"toolName": "queryOrder",
"errorMessage": "订单不存在"
},
"timestamp": "2026-05-07T10:30:03",
"step": 1,
"totalTokens": 512
}
# 8. TOOL_EVENT — 工具侧信道事件 {
"eventType": "TOOL_EVENT",
"data": {
"type": "tool_side",
"toolCallId": "call_abc123",
"toolName": "connectDatabase",
"category": "auth_required",
"payload": {
"authUrl": "https://example.com/oauth",
"provider": "mysql"
},
"userMessage": "需要您授权数据库访问权限"
}
}
# 9. PERMISSION_ASK — 权限确认请求 {
"eventType": "PERMISSION_ASK",
"data": {
"type": "permission_ask",
"askId": "ask_xyz789",
"principalId": 1001,
"toolName": "deleteRecord",
"toolCallId": "call_def456",
"arguments": "{\"recordId\":\"REC-001\"}",
"ruleDescription": "删除操作需要您的确认",
"rulePattern": "delete*"
}
}
# 10. PERMISSION_ASK_RESOLVED — 权限确认结果 {
"eventType": "PERMISSION_ASK_RESOLVED",
"data": {
"type": "permission_ask_resolved",
"askId": "ask_xyz789",
"approved": true,
"reason": "用户已批准"
}
}
# 11. MESSAGE_COMPLETED — 执行完成(终止) {
"eventType": "MESSAGE_COMPLETED",
"data": {
"type": "message",
"messageId": 20002
},
"timestamp": "2026-05-07T10:30:10",
"step": 3,
"totalTokens": 2048
}
# 12. MESSAGE_FAILED — 执行失败(终止) {
"eventType": "MESSAGE_FAILED",
"data": {
"type": "message",
"messageId": 20002,
"errorMessage": "模型调用超时,请稍后重试",
"errorCode": "LLM_ERROR",
"recoverable": true,
"networkError": true
},
"timestamp": "2026-05-07T10:30:10",
"step": 2,
"totalTokens": 1024
}
# 13. MESSAGE_SUSPENDED — 执行挂起(ASYNC_TOOL_PENDING 示例) {
"eventType": "MESSAGE_SUSPENDED",
"data": {
"type": "message",
"messageId": 20002,
"suspendReason": {
"type": "ASYNC_TOOL_PENDING",
"asyncTaskId": "task_456",
"toolCallId": "call_def456",
"toolName": "generateReport",
"arguments": "{\"type\":\"财务分析\"}",
"pendingToolCalls": [],
"step": 2,
"messageId": 20002,
"submittedAtMs": 1749300000000,
"timeoutMs": 300000,
"awakeStrategy": "POLL",
"toolPartId": 105
}
},
"timestamp": "2026-05-07T10:30:20",
"step": 2,
"totalTokens": 1024
}
# 14. SUPPLEMENTARY_INPUT_CONSUMED — 补充输入已消费 {
"eventType": "SUPPLEMENTARY_INPUT_CONSUMED",
"data": {
"type": "supplementary_input",
"inputId": "input_001",
"content": "请额外关注退款状态",
"userMessageId": 20003,
"assistantMessageId": 20004
}
}
# 15. ATTACHMENT_CHANGED — 附件变更 {
"eventType": "ATTACHMENT_CHANGED",
"data": {
"type": "attachment",
"attachmentId": 30001,
"sessionId": 10001,
"messageId": 20002,
"fileName": "Q3财务分析报告.pdf",
"fileExtension": "pdf",
"contentType": "application/pdf",
"fileSize": 245760,
"virtualPath": "/attachments/2026/06/30001_Q3_report.pdf",
"source": "AGENT_GENERATED",
"changeType": "CREATED"
},
"timestamp": "2026-05-07T10:30:04",
"step": 2,
"totalTokens": 768
}
# 16. HEARTBEAT — 心跳保活(每 15 秒) {
"eventType": "HEARTBEAT",
"timestamp": "2026-05-07T10:30:15"
}
# 17. AGENT_STATUS — 运行期瞬时状态 {
"eventType": "AGENT_STATUS",
"data": {
"type": "agent_status",
"code": "LLM_RATE_LIMITED",
"level": "WARN",
"message": "模型限流中,正在第 2 次重试...",
"attributes": {
"attempt": 2,
"maxRetries": 5,
"backoffMs": 3000
}
},
"timestamp": "2026-05-07T10:30:05",
"step": 2,
"totalTokens": 768
}
webhook-events.json
# Webhook 事件信封通用结构 + 请求头 // POST /your/webhook/endpoint
// Content-Type: application/json
// X-Request-Id: req-7a8b9c0d1e2f3g4h
// X-Webhook-Timestamp: 1749300000000
// X-Webhook-Signature: sha256=2c1f47e9b6b5a3c8d4f0e9a1b8c7d6e5f4a3b2c1d0e9f8a7b6c5d4e3f2a1b0c9
{
"schemaVersion": "1.0",
"eventId": "0E7G6QYZX1234567",
"hookType": "pre_tool_use",
"hookName": "财务审批 Hook",
"occurredAt": 1749300000000,
"dispatchedAt": 1749300000000,
"deliveryAttempt": 1,
"traceId": "req-7a8b9c0d1e2f3g4h",
"sessionId": 10001,
"principalId": 1001,
"agentId": 5001,
"context": { "...": "各 hookType 不同,详见下方" }
}
# 1. pre_tool_use — 工具调用前(拦截型) {
"schemaVersion": "1.0",
"eventId": "0E7G6QYZX1234567",
"hookType": "pre_tool_use",
"hookName": "财务审批 Hook",
"occurredAt": 1749300000000,
"dispatchedAt": 1749300000000,
"deliveryAttempt": 1,
"traceId": "req-7a8b9c0d1e2f3g4h",
"sessionId": 10001,
"principalId": 1001,
"agentId": 5001,
"context": {
"toolName": "transferFunds",
"toolCallId": "call_abc123",
"arguments": { "amount": 50000, "target": "ACC-002" }
}
}
# 2. post_tool_use — 工具调用后(观察型) {
"schemaVersion": "1.0",
"eventId": "0E7G6QYZX1234568",
"hookType": "post_tool_use",
"hookName": "工具审计日志",
"occurredAt": 1749300003000,
"dispatchedAt": 1749300003000,
"deliveryAttempt": 1,
"traceId": "req-7a8b9c0d1e2f3g4h",
"sessionId": 10001,
"principalId": 1001,
"agentId": 5001,
"context": {
"toolName": "queryOrder",
"toolCallId": "call_abc123",
"arguments": { "orderId": "ORD-2026001" },
"output": { "status": "已发货" },
"succeeded": true,
"errorMessage": null,
"durationMs": 320
}
}
# 3. user_message — 用户消息(拦截型) {
"schemaVersion": "1.0",
"eventId": "0E7G6QYZX1234569",
"hookType": "user_message",
"hookName": "输入内容安全审核",
"occurredAt": 1749300010000,
"dispatchedAt": 1749300010000,
"deliveryAttempt": 1,
"traceId": "req-7a8b9c0d1e2f3g4h",
"sessionId": 10001,
"principalId": 1001,
"agentId": 5001,
"context": {
"userInput": "帮我查一下 Q3 财报",
"chatMode": "single_chat",
"triggerSource": {
"sourceType": "human_message",
"channelType": "im",
"channelProvider": "dingtalk"
},
"bizKey": "order-2026-001",
"userNickname": "张三"
}
}
# 4. session_start — 会话开始(观察型) {
"schemaVersion": "1.0",
"eventId": "0E7G6QYZX123456A",
"hookType": "session_start",
"hookName": "会话追踪",
"occurredAt": 1749300011000,
"dispatchedAt": 1749300011000,
"deliveryAttempt": 1,
"traceId": "req-7a8b9c0d1e2f3g4h",
"sessionId": 10001,
"principalId": 1001,
"agentId": 5001,
"context": {
"newSession": true
}
}
# 5. agent_completion — Agent 执行完成(观察型) {
"schemaVersion": "1.0",
"eventId": "0E7G6QYZX123456B",
"hookType": "agent_completion",
"hookName": "执行结果统计",
"occurredAt": 1749300020000,
"dispatchedAt": 1749300020000,
"deliveryAttempt": 1,
"traceId": "req-7a8b9c0d1e2f3g4h",
"sessionId": 10001,
"principalId": 1001,
"agentId": 5001,
"context": {
"succeeded": true,
"errorMessage": null,
"errorCode": null,
"model": "qwen-max",
"durationMs": 8420,
"totalTokens": 2048,
"stepCount": 3,
"triggerSourceType": "human_message",
"requestId": "0E7G6R01AB234567",
"assistantMessageId": 20003,
"userMessageId": 20002,
"newSession": true,
"agentName": "财报助手",
"channelProvider": "dingtalk"
}
}
# 6. post_llm_call — LLM 调用后(观察型) {
"schemaVersion": "1.0",
"eventId": "0E7G6QYZX123456C",
"hookType": "post_llm_call",
"hookName": "LLM 调用度量",
"occurredAt": 1749300015000,
"dispatchedAt": 1749300015000,
"deliveryAttempt": 1,
"traceId": "req-7a8b9c0d1e2f3g4h",
"sessionId": 10001,
"principalId": 1001,
"agentId": 5001,
"context": {
"model": "qwen-max",
"step": 2,
"durationMs": 2150,
"promptTokens": 850,
"completionTokens": 320,
"totalTokens": 1170,
"cachedTokens": 0,
"reasoningTokens": 128
}
}
# 7. pre_scheduled_task — 定时任务前(拦截型) {
"schemaVersion": "1.0",
"eventId": "0E7G6QYZX123456D",
"hookType": "pre_scheduled_task",
"hookName": "定时任务准入",
"occurredAt": 1749301800000,
"dispatchedAt": 1749301800000,
"deliveryAttempt": 1,
"traceId": "req-9z8y7x6w5v4u3t2s",
"sessionId": null,
"principalId": 1001,
"agentId": 5001,
"context": {
"taskId": 9001,
"taskName": "每日财报生成"
}
}
# 8. post_scheduled_task — 定时任务后(观察型) {
"schemaVersion": "1.0",
"eventId": "0E7G6QYZX123456E",
"hookType": "post_scheduled_task",
"hookName": "定时任务结果统计",
"occurredAt": 1749301860000,
"dispatchedAt": 1749301860000,
"deliveryAttempt": 1,
"traceId": "req-9z8y7x6w5v4u3t2s",
"sessionId": 10002,
"principalId": 1001,
"agentId": 5001,
"context": {
"taskId": 9001,
"taskName": "每日财报生成",
"succeeded": true,
"errorMessage": null,
"errorCode": null,
"durationMs": 60000,
"requestId": "0E7G6R01AB234568"
}
}
# 拦截型响应体(allow / deny / modify 三种) // allow:放行
{ "decision": "allow" }

// deny:拒绝,reason 将反馈给 LLM 或用户
{
"decision": "deny",
"reason": "此转账金额超出授权限额,需人工审批"
}

// modify:修改后放行,modifiedValue 为新参数/输入 JSON 字符串
{
"decision": "modify",
"modifiedValue": "{\"amount\":10000,\"target\":\"ACC-002\"}"
}