DeepSeek Responses API 接入说明
Commit:
4391ba3— update Responses API support 日期: 2026-08-04 范围:src/gateway/unified_gateway.rs、src/llm/sse.rs、src/config/settings.rs、apps/gliding_code/src/config.rs、config.yaml及测试适配
1. 背景
DeepSeek 官方推出 Responses API(POST /v1/responses)作为新一代接口,相比 Chat Completions 具备语义化的流式事件(response.*)、显式的工具调用往返结构、原生推理内容(reasoning)输出等能力。截至 2026-08-04,该接口仅支持 deepseek-v4-flash 模型(deepseek-v4-pro 预计 2026 年 8 月初启用)。
本次修改为 Gliding Horse 接入 Responses API,同时保持对 Chat Completions 的完全兼容:
deepseek-v4-flash请求默认路由到/v1/responses;- 其余模型(含
deepseek-v4-pro)自动回退到/v1/chat/completions; - 下游调用方(Agent Runner、工具执行器、TUI 等)无感知——两种协议在网关层统一收敛为内部
ChatCompletionResponse/StreamEvent词汇表。
2. 整体架构
flowchart TB
subgraph Config["配置层"]
ENV["环境变量<br/>USE_RESPONSES_API<br/>AGENT_OS_GATEWAY_USE_RESPONSES_API"]
YAML["config.yaml<br/>gateway.use_responses_api"]
Cfg["CliConfig<br/>apps/gliding_code/src/config.rs"]
S["Settings<br/>src/config/settings.rs<br/>GatewaySettings.use_responses_api"]
ENV --> Cfg
YAML --> S
Cfg -->|"构造 GatewaySettings"| S
end
subgraph GW["统一网关 UnifiedGateway<br/>src/gateway/unified_gateway.rs"]
R["RwLock use_responses_api<br/>+ set_use_responses_api()"]
BR["should_use_responses_api(model)"]
CAP["is_responses_capable_model(model)<br/>deepseek-v4-flash*"]
BR --> CAP
subgraph NonStream["非流式路径"]
N1["chat / chat_with_model / chat_with_params"]
N2["build_responses_body<br/>messages → instructions + input items"]
N3["send_responses_request<br/>→ send_with_retry(指数退避重试)"]
N4["parse_responses_response<br/>output items → ChatCompletionResponse"]
end
subgraph Stream["流式路径"]
S1["stream_chat_with_params"]
S2["build_responses_body(stream: true)"]
S3["send_stream_request<br/>Accept: text/event-stream"]
end
end
subgraph SSE["流式事件解析 src/llm/sse.rs"]
P1["SseParser::push → parse_frame"]
P2{"type 前缀 == response.* ?"}
P3["parse_responses_api_event<br/>response.* → StreamEvent"]
P4["parse_openai_stream_event<br/>chat completions 事件"]
P1 --> P2
P2 -->|是| P3
P2 -->|否| P4
end
subgraph ACC["流式聚合 src/llm/stream_types.rs"]
A1["StreamAccumulator::process_event"]
A2["StreamResponse<br/>thought / content / tool_calls / usage"]
A1 --> A2
end
subgraph Downstream["下游消费方(无感知)"]
D1["Agent Runner / SA"]
D2["Tool Executor"]
D3["Gliding Code TUI"]
D4["stream_processor.rs MessageStream"]
end
R --> BR
BR -->|"开启 且 模型为 v4-flash"| N2
BR -->|"开启 且 模型为 v4-flash"| S2
BR -->|"关闭 或 非 v4-flash"| CC["/v1/chat/completions 原有路径"]
N2 --> N3 --> N4
S2 --> S3
S3 -->|"HTTP body 流"| P1
P3 --> A1
P4 --> A1
N4 --> D1
N4 --> D2
A2 --> D3
A2 --> D4
D4 --> D3
设计要点:Responses API 是网关内部的一条协议适配分支,所有 Responses 特有的结构(input items、semantic events)都在网关 / SSE 解析层完成转换,业务层看到的数据形状与 Chat Completions 完全一致。
3. 配置与开关
3.1 配置项
| 位置 | 字段 / 变量 | 默认值 | 说明 |
|---|---|---|---|
src/config/settings.rs | GatewaySettings.use_responses_api | false(程序化默认)/ true(CLI 默认) | 是否启用 Responses API 路由 |
config.yaml | gateway.use_responses_api | true | YAML 配置入口 |
apps/gliding_code/src/config.rs | USE_RESPONSES_API | true | 环境变量,1 / true 视为开启 |
| 同上(兼容) | AGENT_OS_GATEWAY_USE_RESPONSES_API | — | 兼容别名 |
unified_gateway.rs | set_use_responses_api(&self, enabled: bool) | — | 运行时动态切换 |
# config.yaml 示例
gateway:
base_url: "https://api.deepseek.com"
api_key: "sk-..."
timeout_seconds: 300
max_retries: 3
retry_base_ms: 500
# deepseek-v4-flash 走 Responses API (/v1/responses),deepseek-v4-pro 继续走 chat completions
use_responses_api: true
model_mapping:
planning: "deepseek-v4-pro"
execution: "deepseek-v4-flash"
# 环境变量方式
export USE_RESPONSES_API=1 # 显式开启
export USE_RESPONSES_API=0 # 强制走 chat completions
3.2 模型能力判定
/// 仅 deepseek-v4-flash 支持 Responses API;
/// deepseek-v4-pro 在 DeepSeek 启用前继续使用 chat completions。
fn is_responses_capable_model(model: &str) -> bool {
let m = model.to_lowercase();
m == "deepseek-v4-flash" || m.starts_with("deepseek-v4-flash-")
}
fn should_use_responses_api(&self, model: &str) -> bool {
*self.use_responses_api.read().unwrap() && Self::is_responses_capable_model(model)
}
即使
use_responses_api为true,非deepseek-v4-flash模型也绝不会被路由到/v1/responses——这是硬性安全边界,防止对尚不支持该接口的模型产生 400 错误。
4. 非流式请求(chat / chat_with_model / chat_with_params)
sequenceDiagram
participant Caller as 调用方
participant GW as UnifiedGateway
participant API as DeepSeek /v1/responses
Caller->>GW: chat_with_params(model, messages, temperature, max_tokens, tools, tool_choice)
GW->>GW: should_use_responses_api(model)?
alt 是(v4-flash 且开关开启)
GW->>GW: build_responses_body()
Note over GW: system → instructions<br/>user/assistant → input items<br/>tool_calls → function_call<br/>tool 消息 → function_call_output
GW->>API: POST {base}/v1/responses
API-->>GW: { id, output[], usage{} }
GW->>GW: parse_responses_response()
Note over GW: message→text<br/>reasoning→reasoning_content<br/>function_call/custom_tool_call→tool_calls<br/>usage 归一化
GW-->>Caller: ChatCompletionResponse(形状与 chat completions 一致)
else 否(其他模型或开关关闭)
GW-->>Caller: 走 /v1/chat/completions 原有逻辑
end
4.1 消息转换:responses_input_items
| Chat Completions 消息 | Responses API input item |
|---|---|
第一条非空 system | instructions(顶层字段) |
其余 system / developer / user | {type: message, role, content:[{type: input_text, text}]} |
assistant(无工具调用) | {type: message, role: assistant, content:[{type: output_text, text}]} |
assistant(有工具调用) | message item + 每个调用一个 {type: function_call, call_id, name, arguments} |
tool | {type: function_call_output, call_id, output} |
4.2 工具定义转换:convert_responses_tools
Chat Completions 将函数定义嵌套在 function 键下,Responses API 要求扁平结构:
// chat completions(入参)
{ "type": "function", "function": { "name": "get_weather", "description": "...", "parameters": {...} } }
// responses(转换后)
{ "type": "function", "name": "get_weather", "description": "...", "parameters": {...} }
非函数工具(web_search、自定义工具)原样透传。
4.3 响应解析:parse_responses_response
flowchart LR
RAW["POST /v1/responses 响应<br/>{ id, output[], usage }"]
RAW --> M{遍历 output items}
M -->|"type == message"| T["content[] 各 block 的 text<br/>拼接为 content"]
M -->|"type == reasoning"| R["content[] 的 reasoning_text<br/>拼接为 reasoning_content"]
M -->|"type == function_call"| F["call_id/name/arguments<br/>→ ResponseToolCall{function}"]
M -->|"type == custom_tool_call"| C["id/name/input<br/>→ ResponseToolCall{custom}"]
T --> RESP
R --> RESP
F --> RESP
C --> RESP
RESP["ChatCompletionResponse<br/>choices[0].message{content, reasoning_content, tool_calls}<br/>finish_reason: stop | tool_calls<br/>usage{input_tokens→prompt_tokens, output_tokens→completion_tokens}"]
关键点:Responses API 的 usage 字段是 input_tokens / output_tokens,而 Chat Completions 是 prompt_tokens / completion_tokens——解析层完成归一化,下游统计与计费展示无需改动。
4.4 重试与错误处理:send_with_retry
两种协议共享同一重试骨架(重构自原有逻辑):
- 指数退避:
retry_base_ms * 2^(attempt-1); - 4xx 客户端错误立即终止(不重试),并将请求体前 8K 字符嵌入错误信息便于 TUI 直接排查;
- 5xx / 网络错误按
max_retries重试; - 解析失败(JSON 无效 / 结构不符)也会重试并记录响应长度日志。
5. 流式请求(stream_chat_with_params)
sequenceDiagram
participant Caller as 调用方
participant GW as UnifiedGateway
participant MS as MessageStream
participant SP as StreamingProcessor / SseParser
participant ACC as StreamAccumulator
participant API as DeepSeek /v1/responses (stream)
Caller->>GW: stream_chat_with_params(...)
GW->>GW: build_responses_body(stream: true)
GW->>API: POST {base}/v1/responses(Accept: text/event-stream)
API-->>MS: HTTP chunk 流
MS->>SP: push_chunk(bytes)
SP->>SP: parse_frame → 识别 response.* 前缀
SP->>ACC: StreamEvent 事件流
ACC->>ACC: process_event 聚合
MS-->>Caller: collect_with_callback / collect_all → StreamResponse
5.1 事件映射:parse_responses_api_event
Responses API 流由语义事件组成(type: "response.*"),且没有 data: [DONE] 终止帧——终止由 response.completed / response.incomplete / response.failed 承担。parse_frame 通过 type 前缀分流到 Responses 解析器:
| Responses API 事件 | 内部 StreamEvent | 备注 |
|---|---|---|
response.created | MessageStart { id, model } | 记录 message_id / model |
response.output_item.added(function_call / custom_tool_call) | ContentBlockStart { ToolUse{id, name} } | 工具块开始 |
response.output_text.delta | ContentBlockDelta { TextDelta } | 正文增量 |
response.reasoning_text.delta | ContentBlockDelta { ThinkingDelta } | 推理内容(TUI 中以思考步骤呈现) |
response.function_call_arguments.delta | ContentBlockDelta { ToolCallDelta{arguments} } | 工具参数增量 |
response.custom_tool_call_input.delta | ContentBlockDelta { ToolCallDelta{arguments} } | 自定义工具参数增量 |
response.completed | MessageDelta { finish_reason } | completed+有工具调用 → tool_calls;否则 → stop;附 usage |
response.incomplete | MessageDelta { finish_reason: "length" } | 输出截断 |
response.failed | MessageDelta { finish_reason: "error" } | 失败 |
5.2 流式终止语义
flowchart TD
START["流开始"] --> CREATED["response.created"]
CREATED --> LOOP["增量事件循环<br/>output_text / reasoning_text / function_call_arguments delta"]
LOOP --> TERM{"终止事件"}
TERM -->|"response.completed"| C1{"output 含 function_call?"}
C1 -->|是| R1["finish_reason = tool_calls"]
C1 -->|否| R2["finish_reason = stop"]
TERM -->|"response.incomplete"| R3["finish_reason = length"]
TERM -->|"response.failed"| R4["finish_reason = error"]
R1 --> END["MessageStream::next_event 返回 None<br/>collect_all / collect_with_callback 完成"]
R2 --> END
R3 --> END
R4 --> END
5.3 与 Chat Completions 流式路径的关系
SseParser/MessageStream/StreamAccumulator完全复用;- 差异仅在
parse_frame内部:response.*前缀走新解析器,其余走原有parse_openai_stream_event; [DONE]帧仍被兼容处理(parse_frame中payload == "[DONE]"返回None),因此两种协议可在同一代码路径内共存。
6. 调用链全景
flowchart TB
subgraph App["应用层"]
TUI["Gliding Code TUI<br/>/model deepseek-v4-flash"]
AR["Agent Runner / SA 编排"]
TE["Tool Executor"]
end
subgraph GW2["UnifiedGateway"]
CHAT["chat_with_params(非流式)"]
STREAM["stream_chat_with_params(流式)"]
end
subgraph Proto["协议层"]
RP["/v1/responses"]
CC["/v1/chat/completions"]
end
subgraph Conv["转换层"]
BODY["build_responses_body"]
PARSER["parse_responses_response"]
SSEP["parse_responses_api_event"]
end
subgraph Core["核心层"]
RETRY["send_with_retry"]
MSTREAM["MessageStream + StreamAccumulator"]
end
subgraph DL["DeepSeek API"]
D1["deepseek-v4-flash<br/>Responses API 原生"]
D2["deepseek-v4-pro<br/>Chat Completions"]
end
TUI --> AR
AR --> CHAT
AR --> STREAM
TE --> CHAT
CHAT -->|"v4-flash 且开启"| BODY --> RP --> RETRY --> PARSER
STREAM -->|"v4-flash 且开启"| BODY --> RP --> MSTREAM --> SSEP
CHAT -->|"其他模型"| CC --> RETRY
STREAM -->|"其他模型"| CC --> MSTREAM
RETRY --> D1
RETRY --> D2
MSTREAM --> D1
MSTREAM --> D2
7. 配置与使用示例
7.1 完整启用配置
# 1) API Key
export DEEPSEEK_API_KEY="sk-..."
# 2) 显式启用 Responses API(v4-flash 默认已开启,可省略)
export USE_RESPONSES_API=1
# 3) 运行 Gliding Code,默认模型 deepseek-v4-flash 即走 /v1/responses
./glidingcode "设计一个知识图谱的 schema"
# 4) 切换到 v4-pro(自动回退 chat completions)
./glidingcode --model deepseek-v4-pro "分析这段代码的时间复杂度"
7.2 运行时动态切换(编程接口)
// 任意时刻可切换,无需重建网关
gateway.set_use_responses_api(false); // 强制全部模型走 chat completions
gateway.set_use_responses_api(true); // 恢复 v4-flash 走 responses
7.3 观察点
| 现象 | 说明 |
|---|---|
日志出现 LLM API call successful | 非流式调用完成(含 usage) |
流式正常结束、无 [DONE] 依赖 | 说明 response.completed 终止事件被正确解析 |
| TUI 中出现可展开的思考步骤 | response.reasoning_text.delta → ThinkingDelta → thinking 聚合 |
| 工具调用正常往返 | function_call items / function_call_arguments.delta 转换正确 |
| 4xx 错误附 8K 请求体预览 | 便于直接定位请求构造问题 |
8. 测试覆盖
新增 / 适配的测试(cargo test --lib,全量 1193 项通过):
| 测试 | 位置 | 验证点 |
|---|---|---|
test_build_responses_body_converts_messages | unified_gateway.rs | messages → instructions + input items 转换 |
test_responses_api_text_stream | sse.rs | 文本流全链路(created + delta + completed)聚合正确 |
test_responses_api_no_done_terminator | sse.rs | 不依赖 [DONE],completed 即终止 |
test_responses_api_reasoning_delta | sse.rs | 推理增量 → thinking 聚合 |
test_responses_api_tool_call_stream | sse.rs | function_call_arguments.delta → 工具调用聚合 |
test_responses_api_custom_tool_call_delta | sse.rs | 自定义工具参数增量解析 |
test_responses_api_incomplete_sets_length | sse.rs | response.incomplete → finish_reason: length |
test_responses_api_failed_sets_error | sse.rs | response.failed → finish_reason: error |
test_responses_api_live_non_streaming / _streaming / _tool_call | unified_gateway.rs | 真实 API 端到端(无 DEEPSEEK_API_KEY 时自动跳过) |
各模块 use_responses_api: false 适配 | 4 处测试结构体 | 新字段向后兼容 |
9. 边界与限制
- 模型支持面:Responses API 目前仅
deepseek-v4-flash;deepseek-v4-pro在官方启用前自动走 chat completions(is_responses_capable_model硬性判定)。 - 温度等参数:思考模式下
temperature等参数不生效(DeepSeek 文档说明,兼容性静默忽略,不报错);网关仍透传参数,由 API 侧处理。 - 无降级重试:若
/v1/responses返回 4xx(如参数不合法),不会自动降级到 chat completions——这是有意设计,避免掩盖请求构造错误;可通过set_use_responses_api(false)手动回退。 - 流式终止:Responses 流没有
[DONE];若服务端异常断流且无终止事件,MessageStream依赖底层 HTTP 流结束(None)自然终止。 - usage 归一化:
input_tokens/output_tokens→prompt_tokens/completion_tokens的映射在解析层完成,计费展示沿用原有逻辑。
10. 文件变更清单
| 文件 | 变更 | 说明 |
|---|---|---|
src/gateway/unified_gateway.rs | +678 | Responses API 非流式/流式接入、消息与响应转换、重试重构、运行时开关、测试 |
src/llm/sse.rs | +323 | parse_responses_api_event 语义事件解析、流式测试 |
src/config/settings.rs | +5 | GatewaySettings.use_responses_api 字段 |
apps/gliding_code/src/config.rs | +8 | 环境变量读取(USE_RESPONSES_API / 兼容别名),默认开启 |
config.yaml | +2 | 配置项与注释 |
src/core/agent_runner/tests.rs | +1 | 结构体字段适配 |
src/core/sa/tests.rs | +1 | 结构体字段适配 |
src/skill_graph/skill_creator.rs | +4 | 结构体字段适配 |
src/tools/tool_executor/tests.rs | +1 | 结构体字段适配 |
src/worker/agent_os_worker.rs | +2 | 结构体字段适配 |