MoonshotAI / MoonshotAI/kimi-code
手动配置的 OpenAI 兼容模型开启 Thinking 后请求不携带推理参数(On 静默无效)
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 7.5k
- Forks
- 1.2k
- Avg merge
- 11h 53m
- Merged PRs (30d)
- 350
Description
你运行的 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 兼容端点(以下示例隐去真实调用地址)
该端点的行为特点是:只有请求显式携带 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 手动添加自动写入的
于是界面上出现了 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 服务即可复现:
- 保存下面内容并运行
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');
-
配置
config.toml(同上方问题描述中的内容,base_url改为http://127.0.0.1:8899) -
启动 CLI →
/model选中该模型 → Thinking 切到 On → 发送任意消息 -
mock 终端输出
reasoning_effort = null—— On 没有产生任何推理参数
期望的行为是什么?
核心期望:对未声明 support_efforts 的布尔 Thinking 模型,Thinking On 不应静默无效。
按优先级:
- 首选:支持在模型上配置
on_effort(On 时作为reasoning_effort发送),并在模型选择器里引导用户选择档位——让 On 有明确、可控的效果 - 底线:至少在 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 缺的「手动添加」入口
- TUI 的
/provider自 PR #264(2026-06-01,随 v0.7.0 发布)起只有「目录添加」和「注册表(api.json)」两个入口,文档也一直按"两条路径"描述 - Web 端在 PR #2599(2026-08-05,随 v0.33.0 发布,bundle 来自 code-app 仓库)起有了第三个入口「手动添加」,但 TUI/CLI 一直未跟进,两端能力自此不对齐
- 自部署用户在 TUI 里唯一看似可用的入口是「注册表」,把网关 Base URL 填进去会得到误导性报错
Failed to import registry: Internal Server Error(该入口期望的是 api.json 注册表文档,不是端点)
修复方案与实现
修复方案(on_effort 字段 + 选择器档位引导)与独立的 TUI「手动添加」feature 已在本地完成实现并通过端到端验证,具体改动、设计取舍和验证过程见下方评论。如获 /approve 可立即提 PR(bug 修复与 feature 可拆分)。
需要官方侧处理的部分
Web 手动表单缺 Thinking 配置项(截图如下):表单只有名称 / 协议 / API Key / Base URL / 模型 ID / 上下文,没有 Thinking 字段,却给每个手动模型统一写死 adaptive_thinking = true——开关出现了但什么参数都不发。源码在 code-app 仓库,本仓库无法修改,需官方排期。
Contribution
- 我愿意自己提交修复此 bug 的 PR(请先等待维护者在本 issue 中批准)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with packages/kosong/src/providers/openai-legacy.ts, then reproduce the request using the provided mock.mjs and config.toml with Thinking enabled. Verify that the completed change gives manually configured models an explicit reasoning parameter or clearly communicates that On sends none, while preserving the existing behavior for catalog models.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- node.js, typescript
- Domain
- api, backend-api-design, cli
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 35/100