8191 个 token 全在思考、正文为 0:DeepSeek 的 max_output_tokens 到底该设多少
DeepSeek Responses 返回HTTP 200却没有正文,usage里8191个output tokens全部是reasoning,通常不是网络故障,而是隐藏思考耗尽max_output_tokens。本文用DeepSeek V4 Flash 0731的8K、16K、32K真实请求解释output_tokens与reasoning_tokens关系:8K high首轮归零,16K max四卡只剩开头,32K能跑完high/max长篇,但max复杂写卡仍可能耗尽32767 reasoning。附Chat与Responses参数、Agent工具边界、计费预扣和生产兜底。

一条看起来很怪的请求:HTTP 200,SSE 正常结束,等了 109.5 秒,正文却是空字符串。usage 里写着 output_tokens=8191、reasoning_tokens=8191,终止状态是incomplete,原因是 max_output_tokens。
这不是“模型没返回”,恰恰相反:模型已经把额度用完了,只是全用在隐藏思考。我们用 DeepSeek-V4-Flash-0731 官方 Responses 端点,把 8K、16K 和 32K 包络逐级跑了一遍。本文只讲这个工程问题; 小说质量、20 轮复读和角色卡结果在完整发布日评测里。
max_output_tokens 不是“正文最多写多少”
对 reasoning 模型,输出预算至少容纳两部分:隐藏 reasoning 和最终可见正文。DeepSeek 的Responses API在终局 usage 里分别报告总 output 和 reasoning 明细,但限制作用在两者合计上。
所以把 max_output_tokens 设成 8192,不代表“模型可以思考完再写 8192 tokens 正文”,而是“思考和正文一起最多 8192 左右”。high/max 的一次长推理完全可能在正文出现前撞线。
8K、16K、32K 的真实结果
| 任务 | 输出上限 | reasoning | 可见正文 | 结果 |
|---|---|---|---|---|
| 原创长篇 · high 首轮 | 8192 | 8191 | 0 | incomplete/length |
| 原创长篇 · max 第4轮 | 8192 | 8189 | 0 | incomplete/length |
| 四题材写卡 · max | 16384 | 约16200 | 仅第一张卡开头 | 结构失败 |
| 原创长篇 · high 12轮 | 32768 | 合计84322 | 12/12有正文 | 完成,但仅3/12守长度 |
| 原创长篇 · max 12轮 | 32768 | 合计76972 | 12/12有正文 | 完成,但仅2/12守长度 |
| 四题材写卡 · high | 32768 | 27099 | 4155字、四卡齐 | 完成 |
| 四题材写卡 · max 独立复测 | 32768 | 32767 | 0 | 仍被截断 |
这张表说明两件事。第一,32K 对普通高思考长篇很有价值:两档从 8K 的空正文变成连续 12 轮都有正文。第二,32K 不是“彻底修复”:复杂 max 写卡仍可能把 32767 全部花在 reasoning。 同一个 max 写卡在更早的一次 32K 请求中曾以 16617 output tokens 完成四卡;独立复测又失败, 说明推理用量具有明显采样波动。
effort 不是单调旋钮
很多人直觉上认为 max 一定比 high 想得久。本轮 32K 长篇恰好相反:high 12 轮 reasoning 合计 84322,首正文中位 89.5 秒、费用 0.02752 美元;max reasoning 合计 76972,首正文中位 43.5 秒、费用 0.02558 美元。max 仍会出现单轮 13232 reasoning,但整体没有单调变大。
effort 更像改变模型搜索策略的提示,不是固定 token 配额。不能拿档位名称直接预测账单、等待和质量, 更不能在 UI 上暗示“越高一定越好”。
Responses 与 Chat 应该怎么传
Responses 形态的关键字段是:
{
"model": "deepseek-v4-flash",
"reasoning": { "effort": "high" },
"max_output_tokens": 32768,
"stream": true,
"input": [{ "role": "user", "content": "..." }]
}OpenAI Chat 兼容入口通常使用 max_tokens或模型接受的max_completion_tokens,再由代理层统一转换。无论字段叫什么,计费预扣使用的额度必须和真正转发给上游的额度一致; 如果只按 8K 预扣、却把客户端的 100K 原样转发,就会产生正文已经给出、余额却补扣不回来的敞口。
为什么仍然要有限制
错的是所有模型统一锁死 8K,不是“存在上限”这件事。上限至少承担三项工作:限制最坏账单、让余额预扣可计算、 防止失控请求长时间占用连接。合理的处理顺序是:读取用户请求,按模型支持值与产品上限收敛,按收敛后的同一数字预扣并转发。
32K 是本轮对大窗口 DeepSeek 验证过的起点。none/low 通常用不到这么多,但“上限”不是“实际消费”;只要按实际 usage 结算,正常短回复不会因为上限变大而自动收满 32K 的钱。真正会变化的是预扣占用和输入上下文预留,因此小窗口模型仍需要显式较小配置。
生产兜底不要自动悄悄重试
检测到 reasoning-only 截断后,最诱人的做法是后台自动再发一次 64K。问题是第一笔 8K/32K reasoning 已经真实计费,第二笔又会重新推理;用户看到一段正文,却可能付了两次钱。更诚实的流程是:保留本次 usage,明确告诉用户正文尚未生成, 提供“降思考重试”或“提高上限重试”的显式选择。
Agent 还多一个独立边界:本轮 thinking=max + tool_choice=required 返回 400,tool_choice=auto 的两步工具链才正常完成。因此提高输出上限不能修复参数不兼容,两个问题要分开处理。
一张可执行的设置表
| 场景 | 建议 effort | 建议输出上限 | 理由 |
|---|---|---|---|
| 普通续写、改写 | none / low | 大窗口模型32768 | 本轮更快、更便宜;小窗口按能力收口 |
| 复杂剧情规划 | high(按次) | 32768 | 降低正文前截断;仍检查incomplete |
| 超长多卡一次生成 | 拆任务优先 | 32768起 | max在32K仍可能全耗reasoning |
| Agent工具调用 | 按任务;tool_choice=auto | 32768 | required与thinking组合当前会400 |
| 真正关闭思考 | 显式none/disabled | 按正文需求 | 省略字段会跟随模型默认 |
最后记住一句:32K 解决的是“给 reasoning 和正文更大的共同房间”,不是“让模型自动知道什么时候停止思考”。 参数只能解除工程截断,长度服从、事实连续性和模板化仍要靠任务拆分、提示词和质量检查。
常见问题
DeepSeek 返回200但content为空,是什么原因?
先看终止状态和usage。如果terminal_status=incomplete、finish_reason=length,output_tokens与reasoning_tokens又几乎相等,说明隐藏思考耗尽了max_output_tokens,正文还没开始。这与HTTP失败、SSE断流和输入上下文超限是不同问题。
reasoning tokens 算不算 max_output_tokens?
算。DeepSeek Responses的output_tokens包含reasoning和可见输出。本轮high请求设置max_output_tokens=8192,最终output_tokens=8191、reasoning_tokens=8191、正文0,正是两者共享同一预算的直接证据。
max_output_tokens 设成32768够吗?
普通高思考长篇明显够用得多:high和max各12轮都完成并有正文。但它不是保证,max四题材写卡仍出现32767 tokens全部用于reasoning、正文0。32K适合作为已确认具备大上下文和大输出窗模型的工程默认;小窗口BYOK必须按模型能力收口,复杂max任务仍要识别截断并允许用户降档或重做任务拆分。
为什么不直接设置64K?
输出上限同时决定最坏费用、预扣额度和上下文预留。无条件抬到64K会让普通请求占用更多余额,也会挤压小窗口模型的输入空间;而本轮普通长篇在32K已经12/12完成。应先用32K覆盖常见请求,再根据真实复杂任务和模型上限单独放宽。
关闭思考时应该传off还是不传?
不要把“不传参数”当成关闭。对于支持Responses reasoning的模型应显式传reasoning.effort=none;Chat形态按模型协议显式传thinking.type=disabled。省略字段通常表示跟随模型默认,DeepSeek V4 Flash可能回到high。
有想法或问题?来 Discord 聊聊 →