From eee66409528acac244dcfa6cce7f0ffecf5ca67d Mon Sep 17 00:00:00 2001 From: V-LiuShuang Date: Fri, 18 Sep 2026 10:23:50 +0800 Subject: [PATCH] add --- harness-chat.md | 993 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 993 insertions(+) create mode 100644 harness-chat.md diff --git a/harness-chat.md b/harness-chat.md new file mode 100644 index 0000000..663c8ac --- /dev/null +++ b/harness-chat.md @@ -0,0 +1,993 @@ +# Harness Chat 接口文档 + +`POST /harness/chat` + +## 1. 接口说明 + +- 路径:`/harness/chat` +- 方法:`POST` +- 内容类型:`application/json` +- 返回类型:`Flux`(**流式响应**,支持 SSE 与 JSON 数组两种方式) +- 功能:向智能体发送一条用户输入,返回一段**细粒度智能体事件流**。调用方按事件类型逐条解析即可渲染「思考中 / 正在调用工具 / 流式输出答案 / 需要人工确认」等 UI 状态。 + +> 源码位置:`mis-common/mis-common-harness/java/com/lcfc/harness/chat/HarnessChatGateway.java#L18-L23` +> +> ```java +> @PostMapping("/chat") +> public Flux chat(@RequestBody AgentChatRequest request) { +> ChatUiChannel channel = HarnessAgent.builder().build().channel(ChatUiChannel.create()); +> SendOptions sendOptions = SendOptions.of(request.getUserId(), request.getSessionId()); +> return channel.sendStream(sendOptions, request.getInput()); +> } +> ``` + +## 2. 请求 + +### 2.1 请求头 + +| Header | 值 | 说明 | +| --- | --- | --- | +| `Content-Type` | `application/json` | 必填 | +| `Accept` | `text/event-stream` 或 `application/json` | 决定响应格式(见 §3) | + +### 2.2 请求体(`AgentChatRequest`) + +| 字段 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | +| `sessionId` | String | ✅ | 会话 ID;同一用户不同会话相互隔离 | +| `userId` | String | ✅ | 用户 ID;用于智能体命名空间隔离 / 会话路由 | +| `input` | String | ✅ | 用户输入内容 | +| `chatModel` | Object | ❌ | 聊天模型配置(`name`、`stream`、`temperature`) | + +`chatModel` 结构: + +| 字段 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| `name` | String | - | 模型名称,如 `gpt-oss-120b` | +| `stream` | Boolean | `true` | 是否流式 | +| `temperature` | Double | `0.2` | 温度 | + +### 2.3 请求示例 + +```json +POST /harness/chat +Host: localhost:8080 +Content-Type: application/json +Accept: text/event-stream + +{ + "sessionId": "sess-20260918-001", + "userId": "user-10001", + "input": "帮我梳理今天的任务清单,生成一份 markdown 并保存到工作区", + "chatModel": { + "name": "gpt-oss-120b", + "stream": true, + "temperature": 0.2 + } +} +``` + +> ⚠️ 注意:当前实现中 `chatModel` 并未被使用(`HarnessAgent.builder().build()` 未传入模型配置)。如需按请求指定模型,需要扩展 `HarnessChatGateway`。 + +## 3. 响应 + +### 3.1 格式一:SSE(推荐,`Accept: text/event-stream`) + +每发生一个事件,服务端推送一条 `data:`,事件之间以空行分隔: + +``` +data:{"type":"AGENT_START","id":"4f9c1e2d8a7b4c5d9e0f1a2b3c4d5e6f","createdAt":"2026-09-18T02:30:45.123456Z","sessionId":"sess-20260918-001","replyId":"reply-7f3a91","name":"assistant","role":"assistant"} + +data:{"type":"THINKING_BLOCK_START","id":"...","createdAt":"...","replyId":"reply-7f3a91","blockId":"blk-001"} + +data:{"type":"TEXT_BLOCK_DELTA","id":"...","createdAt":"...","replyId":"reply-7f3a91","blockId":"blk-002","delta":"已完成"} +``` + +### 3.2 格式二:JSON 数组(默认 / `Accept: application/json`) + +整个事件流序列化为一个 JSON 数组,随事件产生逐个写出: + +```json +[ + { "type": "AGENT_START", "...": "..." }, + { "type": "THINKING_BLOCK_START", "...": "..." }, + { "type": "TEXT_BLOCK_DELTA", "...": "..." } +] +``` + +### 3.3 HTTP 状态 + +| 状态码 | 说明 | +| --- | --- | +| `200` | 正常,返回事件流 | +| `400` | 校验失败(`sessionId` / `userId` / `input` 为空时抛 `400 Bad Request`) | +| `500` | 智能体执行异常(模型未配置、会话中断等) | + +## 4. 事件通用结构 + +所有事件(`AgentEvent`)都包含以下通用字段,通过 `type` 字段区分具体事件类型: + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `type` | String | **类型判别器**,取值见 §5 | +| `id` | String | 事件唯一 ID(32 位十六进制,无连字符 UUID) | +| `createdAt` | String | 事件创建时间,ISO-8601 格式(如 `2026-09-18T02:30:45.123456Z`) | +| `source` | String | 事件来源路径。顶层智能体事件为 `null`;子智能体转发的事件为 `main/researcher` 形式。**无值时缺省(不输出)** | +| `metadata` | Object | 附加元数据,**空时不输出** | + +仅 `type` / `id` / `createdAt` 必现;其余字段视事件类型而定。以下各事件示例均基于同一会话回环 `"replyId":"reply-7f3a91"`。 + +## 5. AgentEvent 类型详解与数据示例 + +### 5.1 AGENT_START — 智能体开始处理 + +表示一次智能体调用开始。 + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `sessionId` | String | 会话 ID | +| `replyId` | String | 本轮回复 ID,用于关联同一轮事件 | +| `name` | String | 智能体名称 | +| `role` | String | 默认 `"assistant"` | + +```json +{ + "type": "AGENT_START", + "id": "4f9c1e2d8a7b4c5d9e0f1a2b3c4d5e6f", + "createdAt": "2026-09-18T02:30:45.123456Z", + "sessionId": "sess-20260918-001", + "replyId": "reply-7f3a91", + "name": "assistant", + "role": "assistant" +} +``` + +### 5.2 THINKING_BLOCK_START — 思考块开始 + +模型开始推理(ReAct 的 reasoning 阶段)。 + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `replyId` | String | 回复 ID | +| `blockId` | String | 思考块 ID | + +```json +{ + "type": "THINKING_BLOCK_START", + "id": "1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d", + "createdAt": "2026-09-18T02:30:45.200000Z", + "replyId": "reply-7f3a91", + "blockId": "blk-001" +} +``` + +### 5.3 THINKING_BLOCK_DELTA — 思考内容增量 + +逐段推送思考内容。 + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `replyId` | String | 回复 ID | +| `blockId` | String | 思考块 ID | +| `delta` | String | 增量文本 | + +```json +{ + "type": "THINKING_BLOCK_DELTA", + "id": "2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e", + "createdAt": "2026-09-18T02:30:45.210000Z", + "replyId": "reply-7f3a91", + "blockId": "blk-001", + "delta": "用户希望将任务清单整理成 markdown 文件并保存,需要先规划目录结构。" +} +``` + +### 5.4 THINKING_BLOCK_END — 思考块结束 + +```json +{ + "type": "THINKING_BLOCK_END", + "id": "3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f", + "createdAt": "2026-09-18T02:30:45.350000Z", + "replyId": "reply-7f3a91", + "blockId": "blk-001" +} +``` + +### 5.5 MODEL_CALL_START — 模型调用开始 + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `replyId` | String | 回复 ID | + +```json +{ + "type": "MODEL_CALL_START", + "id": "4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f90", + "createdAt": "2026-09-18T02:30:45.180000Z", + "replyId": "reply-7f3a91" +} +``` + +### 5.6 MODEL_CALL_END — 模型调用结束(含 token 用量) + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `replyId` | String | 回复 ID | +| `usage` | Object | token 用量统计 | + +`usage` 结构: + +```json +{ + "type": "MODEL_CALL_END", + "id": "5e6f7a8b9c0d1e2f3a4b5c6d7e8f9001", + "createdAt": "2026-09-18T02:30:45.680000Z", + "replyId": "reply-7f3a91", + "usage": { + "inputTokens": 1250, + "outputTokens": 320, + "cachedTokens": 512, + "time": 0.482, + "totalTokens": 1570 + } +} +``` + +| usage 字段 | 类型 | 说明 | +| --- | --- | --- | +| `inputTokens` | Integer | 输入 token 数(含缓存部分) | +| `outputTokens` | Integer | 输出 token 数 | +| `cachedTokens` | Integer | 命中 prompt 缓存的输入 token 数(来自 inputTokens 的子集) | +| `time` | Number | 本次调用耗时(秒) | +| `totalTokens` | Integer | `inputTokens + outputTokens`(派生字段) | + +### 5.7 TOOL_CALL_START — 工具调用开始 + +模型决定调用某个工具(ReAct 的 acting 阶段)。 + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `replyId` | String | 回复 ID | +| `toolCallId` | String | 工具调用 ID | +| `toolCallName` | String | 工具名 | + +```json +{ + "type": "TOOL_CALL_START", + "id": "6f7a8b9c0d1e2f3a4b5c6d7e8f900112", + "createdAt": "2026-09-18T02:30:45.700000Z", + "replyId": "reply-7f3a91", + "toolCallId": "call_9f2c4d", + "toolCallName": "shell_execute" +} +``` + +### 5.8 TOOL_CALL_DELTA — 工具入参流式增量 + +工具参数以 JSON 字符串形式分片送达。 + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `replyId` | String | 回复 ID | +| `toolCallId` | String | 工具调用 ID | +| `toolCallName` | String | 工具名 | +| `delta` | String | 入参 JSON 片段 | + +```json +{ + "type": "TOOL_CALL_DELTA", + "id": "7a8b9c0d1e2f3a4b5c6d7e8f90011223", + "createdAt": "2026-09-18T02:30:45.720000Z", + "replyId": "reply-7f3a91", + "toolCallId": "call_9f2c4d", + "toolCallName": "shell_execute", + "delta": "{\"command\":\"cat /tasks.json > " +} +``` + +```json +{ + "type": "TOOL_CALL_DELTA", + "id": "8b9c0d1e2f3a4b5c6d7e8f9001122334", + "createdAt": "2026-09-18T02:30:45.730000Z", + "replyId": "reply-7f3a91", + "toolCallId": "call_9f2c4d", + "toolCallName": "shell_execute", + "delta": "\\\"/workspace/tasks.md\"}" +} +``` + +### 5.9 TOOL_CALL_END — 工具调用结束 + +```json +{ + "type": "TOOL_CALL_END", + "id": "9c0d1e2f3a4b5c6d7e8f900112233445", + "createdAt": "2026-09-18T02:30:45.740000Z", + "replyId": "reply-7f3a91", + "toolCallId": "call_9f2c4d", + "toolCallName": "shell_execute" +} +``` + +### 5.10 TOOL_RESULT_START — 工具执行结果开始 + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `replyId` | String | 回复 ID | +| `toolCallId` | String | 工具调用 ID | +| `toolCallName` | String | 工具名 | +| `metadata` | Object | 可选,如携带 `taskId` 等转发元数据(缺省) | + +```json +{ + "type": "TOOL_RESULT_START", + "id": "0d1e2f3a4b5c6d7e8f90011223344556", + "createdAt": "2026-09-18T02:30:46.050000Z", + "replyId": "reply-7f3a91", + "toolCallId": "call_9f2c4d", + "toolCallName": "shell_execute" +} +``` + +### 5.11 TOOL_RESULT_TEXT_DELTA — 工具结果文本增量 + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `replyId` | String | 回复 ID | +| `toolCallId` | String | 工具调用 ID | +| `toolCallName` | String | 工具名 | +| `delta` | String | 结果文本片段 | + +```json +{ + "type": "TOOL_RESULT_TEXT_DELTA", + "id": "1e2f3a4b5c6d7e8f9001122334455667", + "createdAt": "2026-09-18T02:30:46.060000Z", + "replyId": "reply-7f3a91", + "toolCallId": "call_9f2c4d", + "toolCallName": "shell_execute", + "delta": "exit_code=0\n" +} +``` + +```json +{ + "type": "TOOL_RESULT_TEXT_DELTA", + "id": "2f3a4b5c6d7e8f9001122334455667788", + "createdAt": "2026-09-18T02:30:46.070000Z", + "replyId": "reply-7f3a91", + "toolCallId": "call_9f2c4d", + "toolCallName": "shell_execute", + "delta": "任务清单已保存到 /workspace/tasks.md" +} +``` + +### 5.12 TOOL_RESULT_DATA_DELTA — 工具结果二进制数据增量 + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `replyId` | String | 回复 ID | +| `toolCallId` | String | 工具调用 ID | +| `toolCallName` | String | 工具名 | +| `data` | Object | 一个多态 ContentBlock(`type` 判别器),常见为 `data` 类型携带 base64 数据 | + +`data` 的 `type` 取值:`text` / `thinking` / `image` / `audio` / `video` / `tool_use` / `tool_result` / `hint` / `data`。 + +```json +{ + "type": "TOOL_RESULT_DATA_DELTA", + "id": "3a4b5c6d7e8f900112233445566778899", + "createdAt": "2026-09-18T02:30:46.080000Z", + "replyId": "reply-7f3a91", + "toolCallId": "call_9f2c4d", + "toolCallName": "render_chart", + "data": { + "type": "data", + "source": { + "type": "base64", + "media_type": "image/png", + "data": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==" + }, + "id": "d4a3b2c1d5e6f7a8b9c0d1e2f3a4b5c6", + "name": "chart.png" + } +} +``` + +> URL 型数据源示例:`"source": { "type": "url", "url": "https://cdn.example.com/tmp/chart.png", "mime_type": "image/png" }` + +### 5.13 TOOL_RESULT_END — 工具结果结束(含执行状态) + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `replyId` | String | 回复 ID | +| `toolCallId` | String | 工具调用 ID | +| `toolCallName` | String | 工具名 | +| `state` | String | 执行结果:`success` / `error` / `interrupted` / `denied` / `running` | + +```json +{ + "type": "TOOL_RESULT_END", + "id": "4b5c6d7e8f90011223344556677889900", + "createdAt": "2026-09-18T02:30:46.150000Z", + "replyId": "reply-7f3a91", + "toolCallId": "call_9f2c4d", + "toolCallName": "shell_execute", + "state": "success" +} +``` + +**失败示例(state = error):** + +```json +{ + "type": "TOOL_RESULT_END", + "id": "5c6d7e8f9001122334455667788990011", + "createdAt": "2026-09-18T02:30:47.200000Z", + "replyId": "reply-7f3a91", + "toolCallId": "call_9f2c4d", + "toolCallName": "shell_execute", + "state": "error" +} +``` + +### 5.14 DATA_BLOCK_START — 二进制数据块开始 + +用于向用户直接推送二进制内容(图片 / 音频 / 视频 / 文件),通常出现在最终答案阶段。 + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `replyId` | String | 回复 ID | +| `blockId` | String | 数据块 ID | + +```json +{ + "type": "DATA_BLOCK_START", + "id": "6d7e8f900112233445566778899001122", + "createdAt": "2026-09-18T02:30:48.000000Z", + "replyId": "reply-7f3a91", + "blockId": "blk-003" +} +``` + +### 5.15 DATA_BLOCK_DELTA — 二进制数据块增量 + +`delta` 为二进制内容的 **base64** 分片。 + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `replyId` | String | 回复 ID | +| `blockId` | String | 数据块 ID | +| `delta` | String | base64 数据分片 | + +```json +{ + "type": "DATA_BLOCK_DELTA", + "id": "7e8f90011223344556677889900112233", + "createdAt": "2026-09-18T02:30:48.010000Z", + "replyId": "reply-7f3a91", + "blockId": "blk-003", + "delta": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==" +} +``` + +### 5.16 DATA_BLOCK_END — 二进制数据块结束 + +```json +{ + "type": "DATA_BLOCK_END", + "id": "8f9001122334455667788990011223344", + "createdAt": "2026-09-18T02:30:48.100000Z", + "replyId": "reply-7f3a91", + "blockId": "blk-003" +} +``` + +### 5.17 TEXT_BLOCK_START — 文本块开始 + +```json +{ + "type": "TEXT_BLOCK_START", + "id": "900112233445566778899001122334455", + "createdAt": "2026-09-18T02:30:47.500000Z", + "replyId": "reply-7f3a91", + "blockId": "blk-002" +} +``` + +### 5.18 TEXT_BLOCK_DELTA — 文本块流式增量(最终答案) + +前端据此逐字渲染智能体回复。 + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `replyId` | String | 回复 ID | +| `blockId` | String | 文本块 ID | +| `delta` | String | 文本增量 | + +```json +{ + "type": "TEXT_BLOCK_DELTA", + "id": "a011223344556677889900112233445566", + "createdAt": "2026-09-18T02:30:47.510000Z", + "replyId": "reply-7f3a91", + "blockId": "blk-002", + "delta": "已完成," +} +``` + +```json +{ + "type": "TEXT_BLOCK_DELTA", + "id": "b112233445566778899001122334455667", + "createdAt": "2026-09-18T02:30:47.520000Z", + "replyId": "reply-7f3a91", + "blockId": "blk-002", + "delta": "任务清单已保存至 /workspace/tasks.md" +} +``` + +### 5.19 TEXT_BLOCK_END — 文本块结束 + +```json +{ + "type": "TEXT_BLOCK_END", + "id": "c223344556677889900112233445566778", + "createdAt": "2026-09-18T02:30:47.600000Z", + "replyId": "reply-7f3a91", + "blockId": "blk-002" +} +``` + +### 5.20 AGENT_RESULT — 智能体最终结果(含完整 Msg) + +在 `AGENT_END` 之前发出,携带本轮最终回复消息 `Msg`。调用方可以从事件流直接取结果,无需另外订阅返回值。 + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `result` | Object | 最终消息体(多态,`role` 为判别器:`USER` / `ASSISTANT` / `SYSTEM` / `TOOL`) | + +`result`(Msg)字段: + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `id` | String | 消息 ID(UUID) | +| `name` | String | 消息发送方名称,可为 `null` | +| `role` | String | 角色:`USER` / `ASSISTANT` / `SYSTEM` / `TOOL` | +| `content` | Array | 内容块数组(多态,`type` 判别器) | +| `metadata` | Object | 元数据,如 `agentscope_generate_reason` | +| `timestamp` | String | 消息时间戳,格式 `yyyy-MM-dd HH:mm:ss.SSS` | +| `usage` | Object | token 用量(同 §5.6),可为 `null` | + +```json +{ + "type": "AGENT_RESULT", + "id": "d33445566778899001122334455667788", + "createdAt": "2026-09-18T02:30:48.200000Z", + "result": { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "assistant", + "role": "ASSISTANT", + "content": [ + { + "type": "text", + "text": "已完成,任务清单已保存至 /workspace/tasks.md" + } + ], + "metadata": { + "agentscope_generate_reason": "MODEL_STOP" + }, + "timestamp": "2026-09-18 10:30:48.200", + "usage": { + "inputTokens": 1250, + "outputTokens": 320, + "cachedTokens": 512, + "time": 2.46, + "totalTokens": 1570 + } + } +} +``` + +> 多模态回复的 `content` 举例(图片 + 文本混排): +> ```json +> "content": [ +> { "type": "text", "text": "这是生成的图表:" }, +> { "type": "data", "source": { "type": "url", "url": "https://cdn.example.com/chart.png", "mime_type": "image/png" }, "id": "d4a3b2c1d5e6f7a8b9c0d1e2f3a4b5c6", "name": "chart.png" } +> ] +> ``` + +### 5.21 AGENT_END — 智能体结束处理 + +```json +{ + "type": "AGENT_END", + "id": "e455667788990011223344556677889900", + "createdAt": "2026-09-18T02:30:48.300000Z", + "replyId": "reply-7f3a91" +} +``` + +### 5.22 EXCEED_MAX_ITERS — ReAct 循环超过最大迭代次数 + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `replyId` | String | 回复 ID | +| `maxIters` | Integer | 配置的最大迭代次数 | +| `currentIter` | Integer | 实际到达的迭代次数 | + +```json +{ + "type": "EXCEED_MAX_ITERS", + "id": "f566778899001122334455667788990011", + "createdAt": "2026-09-18T02:30:52.000000Z", + "replyId": "reply-7f3a91", + "maxIters": 10, + "currentIter": 10 +} +``` + +### 5.23 REQUIRE_USER_CONFIRM — 需要用户确认工具调用(HITL 暂停) + +智能体在执行敏感工具前暂停,等待用户逐条确认(前端应弹出授权窗口)。工具调用状态为 `asking`。 + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `replyId` | String | 回复 ID | +| `toolCalls` | Array | 待确认的 `ToolUseBlock` 列表 | + +`ToolUseBlock` 字段:`id`、`name`、`input`(Map)、`content`(流式原始内容,可空)、`metadata`、`state`(`pending` / `asking` / `allowed` / `submitted` / `finished`)。 + +```json +{ + "type": "REQUIRE_USER_CONFIRM", + "id": "6778899001122334455667788990011223", + "createdAt": "2026-09-18T02:30:45.750000Z", + "replyId": "reply-7f3a91", + "toolCalls": [ + { + "id": "call_9f2c4d", + "name": "shell_execute", + "input": { + "command": "rm -rf /workspace/old/" + }, + "content": null, + "metadata": {}, + "state": "asking" + }, + { + "id": "call_5a1b02", + "name": "http_post", + "input": { + "url": "https://api.example.com/orders", + "body": "{\"id\": 10086}" + }, + "content": null, + "metadata": {}, + "state": "asking" + } + ] +} +``` + +### 5.24 USER_CONFIRM_RESULT — 用户确认结果回传 + +用户对 `REQUIRE_USER_CONFIRM` 的响应结果(同一会话的下一轮调用事件流中可能出现)。 + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `replyId` | String | 回复 ID | +| `confirmResults` | Array | `ConfirmResult` 列表 | + +`ConfirmResult` 字段: + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `confirmed` | Boolean | 是否批准 | +| `toolCall` | Object | 对应的 `ToolUseBlock`(用户可修改入参) | +| `rules` | Array | 用户新增的权限规则(允许后续免确认),可为空 | + +```json +{ + "type": "USER_CONFIRM_RESULT", + "id": "7889900112233445566778899001122334", + "createdAt": "2026-09-18T02:31:05.000000Z", + "replyId": "reply-7f3a91", + "confirmResults": [ + { + "confirmed": true, + "toolCall": { + "id": "call_9f2c4d", + "name": "shell_execute", + "input": { + "command": "rm -rf /workspace/old/" + }, + "content": null, + "metadata": {}, + "state": "asking" + }, + "rules": [ + { + "tool_name": "shell_execute", + "rule_content": "rm -rf /workspace/*", + "behavior": "allow", + "source": "userSettings" + } + ] + }, + { + "confirmed": false, + "toolCall": { + "id": "call_5a1b02", + "name": "http_post", + "input": { + "url": "https://api.example.com/orders", + "body": "{\"id\": 10086}" + }, + "content": null, + "metadata": {}, + "state": "asking" + }, + "rules": null + } + ] +} +``` + +### 5.25 ALL_TOOLS_DENIED — 本轮所有工具均被拒绝 + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `deniedToolCalls` | Array | 被拒绝的 `ToolUseBlock` 列表 | + +```json +{ + "type": "ALL_TOOLS_DENIED", + "id": "89900112233445566778899001122334455", + "createdAt": "2026-09-18T02:31:05.100000Z", + "deniedToolCalls": [ + { + "id": "call_5a1b02", + "name": "http_post", + "input": { + "url": "https://api.example.com/orders", + "body": "{\"id\": 10086}" + }, + "content": null, + "metadata": {}, + "state": "asking" + } + ] +} +``` + +### 5.26 REQUIRE_EXTERNAL_EXECUTION — 需要外部(进程外)执行工具 + +工具需由调用方在进程外执行,智能体暂停等待结果。 + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `replyId` | String | 回复 ID | +| `toolCalls` | Array | 待外部执行的 `ToolUseBlock` 列表 | + +```json +{ + "type": "REQUIRE_EXTERNAL_EXECUTION", + "id": "9001122334455667788990011223344556", + "createdAt": "2026-09-18T02:30:45.900000Z", + "replyId": "reply-7f3a91", + "toolCalls": [ + { + "id": "call_0f3e9a", + "name": "human_approval_flow", + "input": { + "title": "发布订单 10086 到生产环境" + }, + "content": null, + "metadata": {}, + "state": "pending" + } + ] +} +``` + +### 5.27 EXTERNAL_EXECUTION_RESULT — 外部执行结果回传 + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `replyId` | String | 回复 ID | +| `toolResults` | Array | `ToolResultBlock` 列表 | + +`ToolResultBlock` 字段:`id`、`name`、`output`(ContentBlock 数组)、`metadata`、`state`(`success` / `error` / `interrupted` / `denied` / `running`)。 + +```json +{ + "type": "EXTERNAL_EXECUTION_RESULT", + "id": "a112233445566778899001122334455667", + "createdAt": "2026-09-18T02:31:20.000000Z", + "replyId": "reply-7f3a91", + "toolResults": [ + { + "id": "call_0f3e9a", + "name": "human_approval_flow", + "output": [ + { + "type": "text", + "text": "审批人张三已批准,订单 10086 已发布。" + } + ], + "metadata": {}, + "state": "success" + } + ] +} +``` + +### 5.28 REQUEST_STOP — 请求暂停执行 + +中间件 / 权限引擎要求智能体在当前步骤结束后暂停(如预算超限、合规审查点)。调用方可在下一轮使用相同 `sessionId` 继续该会话,待执行的工具调用会自动恢复。 + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `reason` | String | 停止原因(人类可读) | +| `generateReason` | String | 停止原因枚举名:`MODEL_STOP` / `TOOL_CALLS` / `STRUCTURED_OUTPUT` / `TOOL_SUSPENDED` / `REASONING_STOP_REQUESTED` / `ACTING_STOP_REQUESTED` / `PERMISSION_ASKING` / `MIDDLEWARE_STOP_REQUESTED` / `ALL_TOOLS_DENIED` / `INTERRUPTED` / `MAX_ITERATIONS`,默认 `MIDDLEWARE_STOP_REQUESTED` | + +```json +{ + "type": "REQUEST_STOP", + "id": "b223344556677889900112233445566778", + "createdAt": "2026-09-18T02:30:49.000000Z", + "reason": "单轮 token 消耗超过预算上限 100K,已暂停" +} +``` + +```json +{ + "type": "REQUEST_STOP", + "id": "c334455667788990011223344556677889", + "createdAt": "2026-09-18T02:30:49.100000Z", + "reason": "权限引擎要求用户确认高风险操作", + "generateReason": "PERMISSION_ASKING" +} +``` + +### 5.29 SUBAGENT_EXPOSED — 子智能体对外暴露 + +通过 `agent_spawn(expose_to_user=true)` 生成的子智能体成为可寻址的独立会话入口,SSE 消费端据此在 UI 渲染新的会话卡片。 + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `subagentId` | String | 子智能体实例 ID | +| `agentId` | String | 子智能体定义 ID | +| `sessionId` | String | 子智能体会话 ID | +| `label` | String | 展示名称 | + +```json +{ + "type": "SUBAGENT_EXPOSED", + "id": "d445566778899001122334455667788990", + "createdAt": "2026-09-18T02:31:02.000000Z", + "subagentId": "sub-0a1b2c3d", + "agentId": "data-analyst", + "sessionId": "sess-sub-88", + "label": "数据分析助手" +} +``` + +### 5.30 HINT_BLOCK — 一次性提示块 + +一次性完整下发(不流式),用途包括团队消息、后台工具结果、用户打断等。 + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `replyId` | String | 回复 ID | +| `blockId` | String | 提示块 ID | +| `hintSource` | String | 来源(团队消息发送者姓名、`system` 或 `null`) | +| `hint` | String | 完整提示内容 | + +```json +{ + "type": "HINT_BLOCK", + "id": "e556677889900112233445566778899001", + "createdAt": "2026-09-18T02:31:03.000000Z", + "replyId": "reply-7f3a91", + "blockId": "blk-hint-01", + "hintSource": "alice", + "hint": "alice 加入了会话,可以开始协作。" +} +``` + +### 5.31 CUSTOM — 自定义扩展事件 + +业务中间件用于推送自有状态(未知 `name` 前端应跳过不报错)。约定 `name`:`state_updated`(任务/权限状态变化)、`team_updated`(团队变更)等。 + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `name` | String | 事件名 | +| `value` | Object | 任意 JSON 负载 | + +```json +{ + "type": "CUSTOM", + "id": "f667788990011223344556677889900112", + "createdAt": "2026-09-18T02:31:04.000000Z", + "name": "task_progress", + "value": { + "taskId": "task-201", + "progress": 66, + "status": "RUNNING", + "detail": "正在生成页面截图" + } +} +``` + +```json +{ + "type": "CUSTOM", + "id": "a778899001122334455667788990011223", + "createdAt": "2026-09-18T02:31:04.500000Z", + "name": "team_updated", + "value": { + "action": "MEMBER_ADDED", + "member": "bob", + "teamId": "team-007" + } +} +``` + +## 6. 典型一轮完整事件流(SSE,按顺序) + +以「用户要求整理任务清单并保存为 markdown」为例(含工具调用): + +```text +data:{"type":"AGENT_START","id":"...","createdAt":"...","sessionId":"sess-20260918-001","replyId":"reply-7f3a91","name":"assistant","role":"assistant"} + +data:{"type":"MODEL_CALL_START","id":"...","createdAt":"...","replyId":"reply-7f3a91"} + +data:{"type":"THINKING_BLOCK_START","id":"...","createdAt":"...","replyId":"reply-7f3a91","blockId":"blk-001"} + +data:{"type":"THINKING_BLOCK_DELTA","id":"...","createdAt":"...","replyId":"reply-7f3a91","blockId":"blk-001","delta":"先读取任务源文件,再生成 markdown"} + +data:{"type":"THINKING_BLOCK_END","id":"...","createdAt":"...","replyId":"reply-7f3a91","blockId":"blk-001"} + +data:{"type":"MODEL_CALL_END","id":"...","createdAt":"...","replyId":"reply-7f3a91","usage":{"inputTokens":520,"outputTokens":80,"cachedTokens":0,"time":0.41,"totalTokens":600}} + +data:{"type":"TOOL_CALL_START","id":"...","createdAt":"...","replyId":"reply-7f3a91","toolCallId":"call_9f2c4d","toolCallName":"shell_execute"} + +data:{"type":"TOOL_CALL_DELTA","id":"...","createdAt":"...","replyId":"reply-7f3a91","toolCallId":"call_9f2c4d","toolCallName":"shell_execute","delta":"{\"command\":\"cat /tasks.json\"}"} + +data:{"type":"TOOL_CALL_END","id":"...","createdAt":"...","replyId":"reply-7f3a91","toolCallId":"call_9f2c4d","toolCallName":"shell_execute"} + +data:{"type":"TOOL_RESULT_START","id":"...","createdAt":"...","replyId":"reply-7f3a91","toolCallId":"call_9f2c4d","toolCallName":"shell_execute"} + +data:{"type":"TOOL_RESULT_TEXT_DELTA","id":"...","createdAt":"...","replyId":"reply-7f3a91","toolCallId":"call_9f2c4d","toolCallName":"shell_execute","delta":"3 个任务待办..."} + +data:{"type":"TOOL_RESULT_END","id":"...","createdAt":"...","replyId":"reply-7f3a91","toolCallId":"call_9f2c4d","toolCallName":"shell_execute","state":"success"} + +data:{"type":"MODEL_CALL_START","id":"...","createdAt":"...","replyId":"reply-7f3a91"} + +data:{"type":"MODEL_CALL_END","id":"...","createdAt":"...","replyId":"reply-7f3a91","usage":{"inputTokens":1600,"outputTokens":330,"cachedTokens":512,"time":1.8,"totalTokens":1930}} + +data:{"type":"TEXT_BLOCK_START","id":"...","createdAt":"...","replyId":"reply-7f3a91","blockId":"blk-002"} + +data:{"type":"TEXT_BLOCK_DELTA","id":"...","createdAt":"...","replyId":"reply-7f3a91","blockId":"blk-002","delta":"已完成,"} + +data:{"type":"TEXT_BLOCK_DELTA","id":"...","createdAt":"...","replyId":"reply-7f3a91","blockId":"blk-002","delta":"任务清单已保存至 /workspace/tasks.md"} + +data:{"type":"TEXT_BLOCK_END","id":"...","createdAt":"...","replyId":"reply-7f3a91","blockId":"blk-002"} + +data:{"type":"AGENT_RESULT","id":"...","createdAt":"...","result":{"id":"550e8400-e29b-41d4-a716-446655440000","name":"assistant","role":"ASSISTANT","content":[{"type":"text","text":"已完成,任务清单已保存至 /workspace/tasks.md"}],"metadata":{"agentscope_generate_reason":"MODEL_STOP"},"timestamp":"2026-09-18 10:30:48.200","usage":{"inputTokens":1600,"outputTokens":330,"cachedTokens":512,"time":1.8,"totalTokens":1930}}} + +data:{"type":"AGENT_END","id":"...","createdAt":"...","replyId":"reply-7f3a91"} +``` + +事件一般顺序:`AGENT_START` → (`MODEL_CALL_*` / `THINKING_BLOCK_*` / `TOOL_CALL_*` / `TOOL_RESULT_*` 可能多轮循环) → `TEXT_BLOCK_*` → `AGENT_RESULT` → `AGENT_END`。涉及 HITL 时会插入 `REQUIRE_USER_CONFIRM`(见 §5.23)。 + +## 7. 注意事项 + +1. **延续会话**:同一 `(userId, sessionId)` 组合的上下文会自动累积;不同会话互不干扰。`REQUEST_STOP` 或 `REQUIRE_USER_CONFIRM` 暂停后,用相同 `sessionId` 再发一次请求即可恢复执行。 +2. **事件 ID**:`id` 为 32 位十六进制(无连字符 UUID),可用于调试与去重;`createdAt` 为 ISO-8601(UTC)。 +3. **未知事件**:前端对 `type: CUSTOM` 中未知的 `name` 应静默跳过;对表外未知 `type` 应尽量容忍(服务端可能升级新增事件)。 +4. **空字段省略**:顶层事件的 `source`、`metadata` 及无值字段在 JSON 中缺省(`NON_NULL` 策略);`Msg` 内部字段(如 `name`、`usage`)则为 `null` 或对象。 \ No newline at end of file