Skip to content

手动配置的 OpenAI 兼容模型开启 Thinking 后请求不携带推理参数(On 静默无效) #3448

Description

@LunarFeller

你运行的 Kimi Code 版本是?

kimi version 0.39.1

你使用的是哪个开放平台/订阅?

未使用官方平台;通过 Web UI「手动添加」/ config.toml 自行配置的 OpenAI 兼容端点

你使用的是哪个模型?

自部署的deepseek-v4-flash模型(OpenAI 兼容协议)

你的电脑平台是?

Darwin 24.6.0 arm64 arm

你遇到了什么问题?

我通过 Web UI 的「添加供应商 → 手动添加」配置了一个 OpenAI 兼容端点(以下示例隐去真实调用地址)

Image

该端点的行为特点是:只有请求显式携带 reasoning_effort 时才会推理,不传参时完全不思考

手动添加表单没有 Thinking 相关字段,但保存到 config.toml 时自动带了 adaptive_thinking = true

[providers.my-openai]
base_url = "https://gateway.example.com/v1"
type = "openai"
api_key = "sk-mock"

[models."my-openai/deepseek-v4-flash"]
provider = "my-openai"
model = "deepseek-v4-flash"
max_context_size = 256000
capabilities = [ "tool_use", "thinking" ]
display_name = "deepseek-v4-flash"
adaptive_thinking = true  # Web 手动添加自动写入的
Image

于是界面上出现了 Thinking 开关。切到 On 后,状态栏也显示 thinking 已开启——但抓包发现请求体里没有任何推理参数。端点是否思考完全取决于其服务端默认配置(我的部署默认不思考),On 静默无效。在 TUI 里手动写 config.toml 配置同一端点表现一致(根因在共用的 wire 层)。

抓包对比(隐去认证信息):

// 手动配置的模型,Thinking On —— 没有 reasoning_effort
{
  "model": "my-model",
  "messages": ["..."],
  "stream": true,
  "prompt_cache_key": "session_564f43b4-369d-46ef-9183-191fde53fd3a",
  "max_tokens": 131072,
  "tools": ["...26 个工具..."],
  "stream_options": { "include_usage": true }
}
// 对照:通过 /provider 目录(models.dev)添加的官方 DeepSeek 模型,Thinking On —— 有参数
{
  "model": "deepseek-v3.2",
  "reasoning_effort": "medium",
  "...": "其余字段同上"
}

为确认这不是无害降级,用同一问题对端点做了 curl 对照:

# 不带 reasoning_effort
curl .../chat/completions -d '{"model":"my-model","messages":[{"role":"user","content":"9.11和9.9哪个大?"}]}'
# → reasoning_tokens: 0,reasoning_content: null,回答错误(9.11 大)

# 带 reasoning_effort
curl .../chat/completions -d '{"...": "同上", "reasoning_effort": "high"}'
# → reasoning_tokens: 117,有完整推理过程,回答正确(9.9 大)

即:Thinking On 不发送参数,会让本应思考的模型静默地不思考,直接损害回答质量。

复现步骤?

无需真实网关,本地一个 mock 服务即可复现:

  1. 保存下面内容并运行 node mock.mjs(监听 127.0.0.1:8899,打印请求体并返回固定的流式响应):
// mock.mjs
import http from 'node:http';
http.createServer((req, res) => {
  let raw = '';
  req.on('data', (c) => (raw += c));
  req.on('end', () => {
    const body = JSON.parse(raw);
    console.log('reasoning_effort =', JSON.stringify(body.reasoning_effort ?? null));
    res.writeHead(200, { 'Content-Type': 'text/event-stream' });
    const chunk = { id: 'x', object: 'chat.completion.chunk', created: 0, model: 'my-model', choices: [{ index: 0, delta: { role: 'assistant', content: 'ok' }, finish_reason: null }] };
    const done = { id: 'x', object: 'chat.completion.chunk', created: 0, model: 'my-model', choices: [{ index: 0, delta: {}, finish_reason: 'stop' }], usage: { prompt_tokens: 1, completion_tokens: 1, total_tokens: 2 } };
    res.write(`data: ${JSON.stringify(chunk)}\n\n`);
    res.write(`data: ${JSON.stringify(done)}\n\n`);
    res.write('data: [DONE]\n\n');
    res.end();
  });
}).listen(8899, '127.0.0.1');
  1. 配置 config.toml(同上方问题描述中的内容,base_url 改为 http://127.0.0.1:8899

  2. 启动 CLI → /model 选中该模型 → Thinking 切到 On → 发送任意消息

  3. mock 终端输出 reasoning_effort = null —— On 没有产生任何推理参数

期望的行为是什么?

核心期望:对未声明 support_efforts 的布尔 Thinking 模型,Thinking On 不应静默无效。

按优先级:

  1. 首选:支持在模型上配置 on_effort(On 时作为 reasoning_effort 发送),并在模型选择器里引导用户选择档位——让 On 有明确、可控的效果
  2. 底线:至少在 UI 上明确提示"On 不会发送任何参数",让用户知情,而不是静默无作为

/provider 的手动添加入口属于相关但独立的改进,见补充信息。)

补充信息

根因分析

OpenAI 兼容 wire 的原始设计是有意不为布尔 on 编码参数。packages/kosong/src/providers/openai-legacy.ts 中的原注释:

Determine reasoning_effort. 'on' has no wire encoding on chat-completions APIs, so it sends no reasoning_effort field; only a concrete effort (low/medium/high/...) is passed through verbatim.

responses wire 同理(effort === 'on' ? undefined : effort)。这个设计隐含的前提是"On 只对服务端默认就会推理的模型有意义",没有覆盖"手动配置、必须显式传参才思考"的端点。而文档(Providers and Models 页)声称 "the CLI automatically handles the reasoning_content field and reasoning_effort injection",该承诺实际只对带 support_efforts 元数据的目录模型成立。Anthropic / Kimi 协议对布尔 On 有原生编码(thinking 对象),不受影响。

同类工具的做法

布尔思考的"裸 On"在业内主流工具中并不存在或已被视为缺陷:

  • Roo Code:OpenAI Compatible 模型的设置里,"Enable Reasoning Effort" 复选框与档位下拉绑定——勾选即必须选定一个档位
  • Codex:没有布尔思考开关,配置项直接就是具体档位(model_reasoning_effort
  • aider--reasoning-effort 开关直接接受档位值
  • Cline:存在与本报告相同的缺陷报告(cline#13539:自托管 OpenAI 兼容模型的 reasoning_effort 从不发送),说明这类端点场景普遍存在

设计演进:TUI 缺的「手动添加」入口

修复方案与实现

修复方案(on_effort 字段 + 选择器档位引导)与独立的 TUI「手动添加」feature 已在本地完成实现并通过端到端验证,具体改动、设计取舍和验证过程见下方评论。如获 /approve 可立即提 PR(bug 修复与 feature 可拆分)。

需要官方侧处理的部分

Web 手动表单缺 Thinking 配置项(截图如下):表单只有名称 / 协议 / API Key / Base URL / 模型 ID / 上下文,没有 Thinking 字段,却给每个手动模型统一写死 adaptive_thinking = true——开关出现了但什么参数都不发。源码在 code-app 仓库,本仓库无法修改,需官方排期。

Image

Contribution

  • 我愿意自己提交修复此 bug 的 PR(请先等待维护者在本 issue 中批准)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions