环境
- opencode 版本:1.18.x(Windows 11,PowerShell 终端)
- 模型:
deepseek-v4-flash(通过 OpenAI 兼容接口接入)
- 接入方式:第三方中转服务(OpenAI 兼容协议)
问题描述
通过中转接入的模型在流式输出时,思考(reasoning)内容没有被放到标准的 reasoning_content 字段,而是被直接混入 content 字段,并且以**极细粒度(每 chunk 1~4 个字符)**逐词推送。
opencode 将 content 流的开头部分(正式回答之前的思考文字)渲染成 Thought 思考块,因为粒度极细,产生一行一个 Thought 的刷屏现象,终端滚动失控。
复现步骤
- 配置 OpenAI 兼容中转 provider,接入支持思考的模型(如 deepseek-v4-flash)
- 发起一个能触发深度思考的复杂问题
- 观察 TUI:思考阶段出现大量单行
Thought 提示快速滚动刷屏
SSE 证据(抓包原始数据)
1. 所有 chunk 的 reasoning_content 均为空
对同一请求抓取的 2081 个流式 chunk 中,0 个 chunk 携带非空 reasoning_content 字段:
data: {"id":"xxx-...","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant","content":"","reasoning_content":"","function_call":null,"refusal":"","tool_calls":[]},"finish_reason":""}],"usage":null}
2. 思考内容被逐词塞进 content 字段
2079 个非空 content chunk,内容为思考文字被切成 1~4 字符的碎片:
data: {"id":"xxx-...","choices":[{"index":0,"delta":{"content":"这个","reasoning_content":"","..." }},"finish_reason":""}]}
data: {"id":"xxx-...","choices":[{"index":0,"delta":{"content":"问题","reasoning_content":"","..." }},"finish_reason":""}]}
data: {"id":"xxx-...","choices":[{"index":0,"delta":{"content":"涉及","reasoning_content":"","..." }},"finish_reason":""}]}
data: {"id":"xxx-...","choices":[{"index":0,"delta":{"content":"分布式","reasoning_content":"","..." }},"finish_reason":""}]}
...
3. chunk 长度统计
- 总 chunk 数:2081
- 含非空
reasoning_content:0
- 非空
content chunk:2079
- content 总字符数:4438
- chunk 平均长度:2.1 字符
- 93% 的 chunk 长度 ≤ 4 字符
4. 拼接后的 content 结构
将 2079 个 chunk 拼接后,可见内容依次是:一段"我将通过……路径构建方案"的思考铺垫 → 完整回答。思考段与正式回答之间没有协议层面的分隔标记(无 reasoning_content、无 thinking 字段),opencode 无法区分二者。
预期行为
思考内容应走标准字段(如 OpenAI 的 reasoning_content / Anthropic 的 thinking),或 content 流中思考与正文之间应有可识别的边界;opencode 不应将普通 content 流开头的文字误判为思考块并逐 chunk 渲染。
影响
- 终端刷屏,对话不可读
/thinking 切换无法隐藏该提示行(只能隐藏 reasoning 块本身,但该内容走的 content 字段,隐藏无效)
- 无法在保留模型推理能力的同时关闭视觉噪音
备注
问题根源疑似在中转服务对上游思考流的透传方式不规范;同时 opencode 对 content 流中思考文字的识别/渲染策略也可以更稳健(例如检测连续高频短 chunk 时合并渲染)。具体责任归属待 opencode 维护者确认。
附件
- 完整 SSE 抓包文件:
wb_stream2.txt(681KB,2081 个 chunk)
- 分析脚本:
analyze_stream.py(路径中不含任何服务商信息)
环境
deepseek-v4-flash(通过 OpenAI 兼容接口接入)问题描述
通过中转接入的模型在流式输出时,思考(reasoning)内容没有被放到标准的
reasoning_content字段,而是被直接混入content字段,并且以**极细粒度(每 chunk 1~4 个字符)**逐词推送。opencode 将
content流的开头部分(正式回答之前的思考文字)渲染成Thought思考块,因为粒度极细,产生一行一个 Thought 的刷屏现象,终端滚动失控。复现步骤
Thought提示快速滚动刷屏SSE 证据(抓包原始数据)
1. 所有 chunk 的
reasoning_content均为空对同一请求抓取的 2081 个流式 chunk 中,0 个 chunk 携带非空
reasoning_content字段:data: {"id":"xxx-...","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant","content":"","reasoning_content":"","function_call":null,"refusal":"","tool_calls":[]},"finish_reason":""}],"usage":null}2. 思考内容被逐词塞进
content字段2079 个非空
contentchunk,内容为思考文字被切成 1~4 字符的碎片:3. chunk 长度统计
reasoning_content:0contentchunk:20794. 拼接后的 content 结构
将 2079 个 chunk 拼接后,可见内容依次是:一段"我将通过……路径构建方案"的思考铺垫 → 完整回答。思考段与正式回答之间没有协议层面的分隔标记(无
reasoning_content、无thinking字段),opencode 无法区分二者。预期行为
思考内容应走标准字段(如 OpenAI 的
reasoning_content/ Anthropic 的thinking),或 content 流中思考与正文之间应有可识别的边界;opencode 不应将普通 content 流开头的文字误判为思考块并逐 chunk 渲染。影响
/thinking切换无法隐藏该提示行(只能隐藏 reasoning 块本身,但该内容走的 content 字段,隐藏无效)备注
问题根源疑似在中转服务对上游思考流的透传方式不规范;同时 opencode 对 content 流中思考文字的识别/渲染策略也可以更稳健(例如检测连续高频短 chunk 时合并渲染)。具体责任归属待 opencode 维护者确认。
附件
wb_stream2.txt(681KB,2081 个 chunk)analyze_stream.py(路径中不含任何服务商信息)