添加 multi-llm 路由智能体(LangGraph),支持多 LLM 分发能力;在 Gateway 中注册 /api/multi-llm 路由,并豁免其 CSRF 和认证校验;同时新增 asyncmy 依赖和聊天流格式文档。 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
475 lines
11 KiB
Markdown
475 lines
11 KiB
Markdown
# New Chat 接口流式返回格式说明
|
||
|
||
## 接口概览
|
||
|
||
- **路径**:`POST /new_chat`
|
||
- **传输协议**:Server-Sent Events (SSE)
|
||
- **Content-Type**:`text/event-stream`
|
||
- **协议基础**:Vercel AI SDK Data Stream Protocol
|
||
|
||
---
|
||
|
||
## 请求格式
|
||
|
||
```json
|
||
{
|
||
"chat_id": 123,
|
||
"search_space_id": 456,
|
||
"user_query": "用户问题",
|
||
"pic_query": [{"image_url": "/tmp/xxx.png"}],
|
||
"model_name": "gpt-4o",
|
||
"enable_citations": true,
|
||
}
|
||
```
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| `chat_id` | int | ✅ | 会话 ID,同时作为 LangGraph 的 thread_id |
|
||
| `search_space_id` | int | ✅ | 搜索空间 ID |
|
||
| `user_query` | string | ✅ | 用户问题 |
|
||
| `pic_query` | list\|null | ❌ | 图片路径列表,格式 `[{"image_url": "..."}]` |
|
||
| `model_name` | string\|null | ❌ | 指定模型名称(插件场景使用) |
|
||
| `enable_citations` | bool | ❌ | 是否启用引用(默认 true) |
|
||
| `messages` | list\|null | ❌ | 历史消息列表 |
|
||
| `attachments` | list\|null | ❌ | 上传附件(已解析为 Markdown) |
|
||
| `mentioned_document_ids` | list\|null | ❌ | 前端 @ 引用的文档 ID 列表 |
|
||
|
||
---
|
||
|
||
## 响应 Headers
|
||
|
||
```
|
||
Content-Type: text/event-stream
|
||
Cache-Control: no-cache
|
||
Connection: keep-alive
|
||
X-Accel-Buffering: no
|
||
```
|
||
|
||
---
|
||
|
||
## SSE 数据帧格式
|
||
|
||
每条消息均为标准 SSE 格式:
|
||
|
||
```
|
||
data: {JSON 对象}\n\n
|
||
```
|
||
|
||
流结束时发送:
|
||
|
||
```
|
||
data: [DONE]\n\n
|
||
```
|
||
|
||
---
|
||
|
||
## 事件类型详解
|
||
|
||
### 1. 消息生命周期
|
||
|
||
#### `start` — 消息开始
|
||
|
||
```json
|
||
{"type": "start", "messageId": "msg_abc123"}
|
||
```
|
||
|
||
| 字段 | 说明 |
|
||
|------|------|
|
||
| `messageId` | 本次消息的唯一 ID |
|
||
|
||
---
|
||
|
||
#### `finish` — 消息结束
|
||
|
||
```json
|
||
{"type": "finish"}
|
||
```
|
||
|
||
---
|
||
|
||
#### `[DONE]` — 流终止标记
|
||
|
||
```
|
||
data: [DONE]
|
||
```
|
||
|
||
> 这是流中最后一帧,为纯字符串,不是 JSON。
|
||
|
||
---
|
||
|
||
### 2. 步骤控制(多轮工具调用)
|
||
|
||
#### `start-step` — 步骤开始
|
||
|
||
```json
|
||
{"type": "start-step"}
|
||
```
|
||
|
||
#### `finish-step` — 步骤结束
|
||
|
||
```json
|
||
{"type": "finish-step"}
|
||
```
|
||
|
||
> 每次 LLM 调用算一个 step。工具调用期间会有多个 step 交替出现。
|
||
|
||
---
|
||
|
||
### 3. 文本块(start / delta / end 模式)
|
||
|
||
#### `text-start` — 文本块开始
|
||
|
||
```json
|
||
{"type": "text-start", "id": "text_abc123"}
|
||
```
|
||
|
||
#### `text-delta` — 文本增量内容
|
||
|
||
```json
|
||
{"type": "text-delta", "id": "text_abc123", "delta": "Hello, "}
|
||
```
|
||
|
||
#### `text-end` — 文本块结束
|
||
|
||
```json
|
||
{"type": "text-end", "id": "text_abc123"}
|
||
```
|
||
|
||
---
|
||
|
||
### 4. 推理块(Reasoning,适用于 DeepSeek R1 等思维链模型)
|
||
|
||
#### `reasoning-start` — 推理块开始
|
||
|
||
```json
|
||
{"type": "reasoning-start", "id": "reasoning_abc123"}
|
||
```
|
||
|
||
#### `reasoning-delta` — 推理增量内容
|
||
|
||
```json
|
||
{"type": "reasoning-delta", "id": "reasoning_abc123", "delta": "让我先思考..."}
|
||
```
|
||
|
||
#### `reasoning-end` — 推理块结束
|
||
|
||
```json
|
||
{"type": "reasoning-end", "id": "reasoning_abc123"}
|
||
```
|
||
|
||
> 触发时机:
|
||
> - 模型返回 `additional_kwargs.reasoning_content`(DeepSeek R1 原生)
|
||
> - 模型输出中包含 `<think>...</think>` 标签(自动剥离)
|
||
|
||
---
|
||
|
||
### 5. 工具调用
|
||
|
||
#### `tool-input-start` — 工具调用开始
|
||
|
||
```json
|
||
{"type": "tool-input-start", "toolCallId": "call_abc123", "toolName": "search_knowledge_base"}
|
||
```
|
||
|
||
#### `tool-input-delta` — 工具输入流式内容(增量)
|
||
|
||
```json
|
||
{"type": "tool-input-delta", "toolCallId": "call_abc123", "inputTextDelta": "San Fra"}
|
||
```
|
||
|
||
#### `tool-input-available` — 工具输入完整参数
|
||
|
||
```json
|
||
{
|
||
"type": "tool-input-available",
|
||
"toolCallId": "call_abc123",
|
||
"toolName": "search_knowledge_base",
|
||
"input": {"query": "机器学习入门"}
|
||
}
|
||
```
|
||
|
||
#### `tool-output-available` — 工具执行结果
|
||
|
||
```json
|
||
{
|
||
"type": "tool-output-available",
|
||
"toolCallId": "call_abc123",
|
||
"output": {"status": "completed", "result_length": 1234}
|
||
}
|
||
```
|
||
|
||
> 注意:`search_knowledge_base` 只返回摘要(不含全文),`write_todos`、`link_preview`、`display_image`、`scrape_webpage`、`generate_podcast` 会返回完整 output 供前端渲染组件。
|
||
|
||
---
|
||
|
||
### 6. 来源引用
|
||
|
||
#### `source-url` — URL 来源
|
||
|
||
```json
|
||
{
|
||
"type": "source-url",
|
||
"sourceId": "https://example.com",
|
||
"url": "https://example.com",
|
||
"title": "示例页面"
|
||
}
|
||
```
|
||
|
||
#### `source-document` — 文档来源
|
||
|
||
```json
|
||
{
|
||
"type": "source-document",
|
||
"sourceId": "doc_123",
|
||
"mediaType": "file",
|
||
"title": "报告.pdf",
|
||
"description": "第三季度报告"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 7. 文件
|
||
|
||
```json
|
||
{"type": "file", "url": "https://example.com/image.png", "mediaType": "image/png"}
|
||
```
|
||
|
||
---
|
||
|
||
### 8. 自定义数据(`data-*` 类型)
|
||
|
||
#### `data-terminal-info` — 终端信息提示
|
||
|
||
```json
|
||
{
|
||
"type": "data-terminal-info",
|
||
"data": {
|
||
"text": "知识库搜索已完成",
|
||
"type": "success"
|
||
}
|
||
}
|
||
```
|
||
|
||
`type` 枚举值:`info` | `error` | `success` | `warning`
|
||
|
||
---
|
||
|
||
#### `data-further-questions` — 推荐追问问题
|
||
|
||
```json
|
||
{
|
||
"type": "data-further-questions",
|
||
"data": {
|
||
"questions": ["问题1", "问题2", "问题3"]
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### `data-thinking-step` — 思考步骤(思维链 UI 展示)
|
||
|
||
```json
|
||
{
|
||
"type": "data-thinking-step",
|
||
"data": {
|
||
"id": "thinking-1",
|
||
"title": "正在搜索知识库",
|
||
"status": "in_progress",
|
||
"items": ["Query: 机器学习入门"]
|
||
}
|
||
}
|
||
```
|
||
|
||
| 字段 | 说明 |
|
||
|------|------|
|
||
| `id` | 步骤唯一 ID(格式 `thinking-N`) |
|
||
| `title` | 步骤标题 |
|
||
| `status` | `pending` \| `in_progress` \| `completed` |
|
||
| `items` | 步骤详情列表(可选) |
|
||
|
||
> 同一个 `id` 的步骤事件会被重复发送以更新状态(先 `in_progress` 后 `completed`)。
|
||
|
||
**常见步骤标题对照:**
|
||
|
||
| 工具 / 场景 | 标题 |
|
||
|------------|------|
|
||
| 初始分析 | 正在理解您的请求 / 正在分析您的内容 |
|
||
| `search_knowledge_base` | 查询个人知识库中 |
|
||
| `search_web` | 网查搜索中 |
|
||
| `link_preview` | 正在获取链接预览 |
|
||
| `display_image` | 正在分析图片 |
|
||
| `scrape_webpage` | 正在抓取网页 |
|
||
| `write_todos`(首次) | 正在创建计划 |
|
||
| `write_todos`(更新) | 正在更新进度 |
|
||
| `generate_podcast` | 正在生成播客 |
|
||
|
||
---
|
||
|
||
### 9. 错误
|
||
|
||
```json
|
||
{
|
||
"type": "error",
|
||
"errorText": "API rate limit exceeded",
|
||
"errorType": "llm",
|
||
"errorCode": "rate_limit"
|
||
}
|
||
```
|
||
|
||
| 字段 | 说明 |
|
||
|------|------|
|
||
| `errorText` | 错误描述文本 |
|
||
| `errorType` | 错误类型(见下表) |
|
||
| `errorCode` | 具体错误代码(可选) |
|
||
|
||
**errorType 枚举:**
|
||
|
||
| 值 | 含义 |
|
||
|----|------|
|
||
| `llm` | LLM / API 错误(模型不存在、限流、密钥错误等) |
|
||
| `network` | 网络连通性错误 |
|
||
| `timeout` | 请求超时 |
|
||
| `authentication` | 鉴权 / 权限错误 |
|
||
| `rate_limit` | 速率限制 |
|
||
| `validation` | 输入验证错误 |
|
||
| `internal` | 内部服务器错误 |
|
||
| `unknown` | 未知错误 |
|
||
|
||
---
|
||
|
||
### 10. 中断(Human-in-the-loop)
|
||
|
||
```json
|
||
{
|
||
"type": "interrupt",
|
||
"messageId": "msg_abc123",
|
||
"interrupt": {
|
||
"action_requests": [...],
|
||
"review_configs": [...]
|
||
},
|
||
"payload": {"expertAgentId": 42}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 完整流示例(普通问答)
|
||
|
||
```
|
||
data: {"type":"start","messageId":"msg_abc123"}
|
||
|
||
data: {"type":"start-step"}
|
||
|
||
data: {"type":"data-thinking-step","data":{"id":"thinking-1","title":"正在理解您的请求","status":"in_progress","items":["Processing: 什么是向量数据库?"]}}
|
||
|
||
data: {"type":"data-thinking-step","data":{"id":"thinking-1","title":"正在理解您的请求","status":"completed","items":["Processing: 什么是向量数据库?"]}}
|
||
|
||
data: {"type":"text-start","id":"text_xyz789"}
|
||
|
||
data: {"type":"text-delta","id":"text_xyz789","delta":"向量数据库是"}
|
||
|
||
data: {"type":"text-delta","id":"text_xyz789","delta":"一种专为存储和检索"}
|
||
|
||
data: {"type":"text-delta","id":"text_xyz789","delta":"高维向量而设计的数据库..."}
|
||
|
||
data: {"type":"text-end","id":"text_xyz789"}
|
||
|
||
data: {"type":"finish-step"}
|
||
|
||
data: {"type":"finish"}
|
||
|
||
data: [DONE]
|
||
```
|
||
|
||
---
|
||
|
||
## 完整流示例(含工具调用)
|
||
|
||
```
|
||
data: {"type":"start","messageId":"msg_abc123"}
|
||
|
||
data: {"type":"start-step"}
|
||
|
||
data: {"type":"data-thinking-step","data":{"id":"thinking-1","title":"正在理解您的请求","status":"in_progress","items":["Processing: 搜索最新 AI 论文"]}}
|
||
|
||
data: {"type":"data-thinking-step","data":{"id":"thinking-1","title":"正在理解您的请求","status":"completed","items":["Processing: 搜索最新 AI 论文"]}}
|
||
|
||
data: {"type":"data-thinking-step","data":{"id":"thinking-2","title":"查询个人知识库中","status":"in_progress","items":["Query: 最新 AI 论文"]}}
|
||
|
||
data: {"type":"tool-input-start","toolCallId":"call_xxx","toolName":"search_knowledge_base"}
|
||
|
||
data: {"type":"tool-input-available","toolCallId":"call_xxx","toolName":"search_knowledge_base","input":{"query":"最新 AI 论文"}}
|
||
|
||
data: {"type":"data-thinking-step","data":{"id":"thinking-2","title":"Searching knowledge base","status":"completed","items":["Query: 最新 AI 论文","Found relevant information (2048 chars)"]}}
|
||
|
||
data: {"type":"tool-output-available","toolCallId":"call_xxx","output":{"status":"completed","result_length":2048}}
|
||
|
||
data: {"type":"data-terminal-info","data":{"text":"知识库搜索已完成","type":"success"}}
|
||
|
||
data: {"type":"text-start","id":"text_xyz789"}
|
||
|
||
data: {"type":"text-delta","id":"text_xyz789","delta":"根据知识库中的内容..."}
|
||
|
||
data: {"type":"text-end","id":"text_xyz789"}
|
||
|
||
data: {"type":"finish-step"}
|
||
|
||
data: {"type":"finish"}
|
||
|
||
data: [DONE]
|
||
```
|
||
|
||
---
|
||
|
||
## 完整流示例(含推理过程)
|
||
|
||
```
|
||
data: {"type":"start","messageId":"msg_abc123"}
|
||
|
||
data: {"type":"start-step"}
|
||
|
||
data: {"type":"reasoning-start","id":"reasoning_r01"}
|
||
|
||
data: {"type":"reasoning-delta","id":"reasoning_r01","delta":"让我先分析这个问题..."}
|
||
|
||
data: {"type":"reasoning-delta","id":"reasoning_r01","delta":"需要考虑以下几个方面..."}
|
||
|
||
data: {"type":"reasoning-end","id":"reasoning_r01"}
|
||
|
||
data: {"type":"text-start","id":"text_xyz789"}
|
||
|
||
data: {"type":"text-delta","id":"text_xyz789","delta":"综合分析后,结论是..."}
|
||
|
||
data: {"type":"text-end","id":"text_xyz789"}
|
||
|
||
data: {"type":"finish-step"}
|
||
|
||
data: {"type":"finish"}
|
||
|
||
data: [DONE]
|
||
```
|
||
|
||
---
|
||
|
||
## 标准消息流顺序
|
||
|
||
```
|
||
start
|
||
└─ start-step
|
||
├─ [data-thinking-step × N] 思考步骤(可多次)
|
||
├─ [reasoning-start/delta/end] 推理过程(可选)
|
||
├─ [tool-input-start] 工具调用开始(可多次)
|
||
├─ [tool-input-available] 工具参数确认
|
||
├─ [tool-output-available] 工具结果
|
||
├─ [data-terminal-info] 终端提示(可多次)
|
||
├─ [source-url / source-document] 来源引用(可选)
|
||
├─ text-start
|
||
│ └─ text-delta × N 流式文本
|
||
└─ text-end
|
||
finish-step
|
||
finish
|
||
[DONE]
|
||
```
|