最近 kimi code 从 python 迁移到了 ts 版本。于是做了个简单的分析。
根据对 kimi-code 源代码(特别是 packages/kosong/src/providers/kimi.ts、kimi-schema.ts、kimi-files.ts 等)以及 OpenCode 相关兼容性 Issue 的分析,以下是 kimi-code 针对 kimi-k2.6(及 Kimi 系列模型)做的专用处理,以及相对于 OpenCode 的关键差异。
1. 原生 Kimi Provider(非通用 OpenAI 兼容层)
kimi-code 没有直接把 Kimi 当"又一个 OpenAI 兼容接口"处理,而是实现了专门的 kimi provider 类型:
| 特性 | kimi-code | OpenCode |
|---|---|---|
| Provider 类型 | 专门的 'kimi' 类型,独立适配 | 通过 OpenAI/Anthropic 通用 bridge 访问 |
| 专有字段 | 原生处理 reasoning_content、thinking、generationKwargs | bridge 层常丢失 reasoning_content |
| 认证头 | 支持 kimiRequestHeaders 和 X-Msh-Tool-Call-Id 等 Moonshot 专有 header | 通用 header 转发 |
2. reasoning_content 的全生命周期处理
kimi-k2.6 默认开启 thinking,且要求多轮对话历史中保留 reasoning_content,否则工具调用后会报 400 错误。
kimi-code 的处理:
convertMessage:将内部think类型的 content part 提取并序列化为reasoning_content字段,确保历史消息中 thinking 内容不丢失- 流式解析:在
_convertStreamResponse和_convertNonStreamResponse中都专门提取delta.reasoning_content/message.reasoning_content - TUI 渲染:有专门的
ThinkingComponent组件实时显示 thinking 内容,支持展开/收起和 spinner 动画
OpenCode 的问题:
OpenCode Go 的 bridge 在第二轮对话时丢失 reasoning_content,导致 Moonshot API 返回:
| |
3. JSON Schema 专用规范化(kimi-schema.ts)
Moonshot 的工具参数验证器对 JSON Schema 有严格且特殊的要求,这是 OpenCode 与 kimi-k2.6 不兼容的主要来源之一。
kimi-code 的 normalizeKimiToolSchema 做了:
- 解引用
$ref:将$defs/definitions中的定义内联展开,消除外部引用 - 补全缺失的
type:Kimi 验证器会拒绝省略type的嵌套属性(比如 MCP 产生的 enum-only schema),kimi-code 会推断并补全type: string/object/array等 - 循环引用检测:遇到循环引用时保留原始
$ref,避免无限递归
OpenCode 的问题:
生成 schema 使用 #/definitions/ 而非 Moonshot 要求的 #/$defs/,且没有为 Kimi 做 schema 类型推断和补全,导致复杂工具调用直接 400。
4. Thinking 模式的原生配置体系
kimi-code 从配置层到 UI 层都内置了对 Kimi thinking 模式的支持:
配置解析:
ThinkingConfigSchema支持mode: auto/on/off和effort: low/medium/high/xhigh/max模型能力标注:
ModelAlias支持capabilities: ['thinking', 'always_thinking']模型选择器 UI:按
←→键可切换 thinking on/off,always-on模型不可关闭Provider 方法:
withThinking(effort)会正确生成:1 2 3 4{ "reasoning_effort": "high", "extra_body": { "thinking": { "type": "enabled" } } }token 预算:自动将 legacy
max_tokens规范化为 Kimi 偏好的max_completion_tokens
OpenCode 的问题:
通过 Anthropic bridge 时硬编码发送 thinking content blocks,但 Kimi API 只支持 text/image_url/video_url/video,导致:
| |
5. Moonshot 原生服务集成
kimi-code 内置了 Moonshot 专属服务,而非使用通用本地实现:
MoonshotFetchURLProvider:优先调用 Moonshot 的coding-fetch服务(已做页面文本提取),失败才 fallback 到本地MoonshotWebSearchProvider:直接调用 Moonshot 搜索 API,支持enable_page_crawlingKimiFiles:视频上传到 Moonshot 文件服务,生成ms://<file-id>格式的video_url
6. 工具调用层细节
- 内置函数:以
$开头的工具名会被识别为 Kimi builtin function,序列化为type: 'builtin_function' - Usage 提取:支持 Moonshot 特有的
choices[0].usage位置,以及cached_tokens等字段 - Finish Reason 映射:将 OpenAI 格式的
stop/tool_calls/length等映射到内部统一枚举
7. CLI 核心与 LLM SDK 的架构隔离
这是一个容易被忽视但很重要的架构差异。
kimi-code 的核心 CLI(apps/kimi-code)本身不直接依赖任何 OpenAI 或 Anthropic 的 TypeScript SDK。 查看其 package.json,核心依赖只有 TUI 渲染(pi-tui)、命令行解析(commander)、语法高亮(cli-highlight)等通用库。所有与 LLM provider 的交互都被隔离在自研的 kosong 包中。
packages/kosong 虽然内部使用了 openai 和 @anthropic-ai/sdk 作为实现细节(因为 Kimi API 是 OpenAI-compatible 的),但对外提供的是统一的 LLM 抽象接口。CLI 核心只依赖 kosong,不感知底层 vendor SDK。
OpenCode 则不同。 其 packages/opencode 核心包直接依赖了大量 vendor SDK:
@ai-sdk/openai@ai-sdk/anthropic@ai-sdk/google@ai-sdk/azure@openrouter/ai-sdk-provider- ……(共计十余个 provider-specific 包)
这意味着 OpenCode 的核心代码与各个 vendor 的 SDK 深度耦合,而 kimi-code 的核心 CLI 保持干净,模型交互完全通过自研抽象层隔离。
8. Commit 历史揭示的演进路径
以上的代码结构差异只是静态结果。更有意思的是看两边的 commit 历史——动态演进方向完全不同。
kimi-code:原生设计,持续消除配置负担
842e699 — “Kimi For Coding”(初始提交)
这是整个项目的起点。初始代码就包含了:
packages/kosong/src/providers/kimi.ts:专用 Kimi providerpackages/kosong/src/providers/kimi-schema.ts:专用 JSON Schema 规范化器packages/kosong/src/providers/kimi-files.ts:专用文件上传服务
结论:kimi-code 从一开始就把 Kimi API 当作一等公民设计,不是后期打补丁适配。
d95b013 fix(catalog): preserve reasoning fields in custom model (#70)
这个 commit 修复了一个很隐蔽的问题。models.dev 用 interleaved 字段标记 reasoning 支持,但早期代码把 interleaved=true 当作 undefined 处理,导致通过 /connect 选择的模型静默丢失 reasoning 能力。
修复内容:
interleaved=true映射为默认的reasoning_contentupdate-catalog.mjs的 allowlist 加入interleaved,否则 release build 的离线 catalog 会再次丢失该字段
61f7d0e fix(kosong): make openai-compatible thinking work without reasoning_key (#78)
这是 reasoning 处理的核心 commit,展示了 kimi-code 对兼容性的深层思考。diff 显示了三层关键设计:
Inbound 自动扫描(响应解析)
1 2const KNOWN_REASONING_KEYS = ['reasoning_content', 'reasoning_details', 'reasoning'] as const; // 自动扫描三个字段,第一个字符串值获胜Outbound 默认回写(请求序列化)
1 2const DEFAULT_OUTBOUND_REASONING_KEY = KNOWN_REASONING_KEYS[0]; // 'reasoning_content' // 默认以 reasoning_content 回传,无需用户配置自动注入
reasoning_effort(历史连续性)1 2// 当历史中有 ThinkPart 但 caller 未显式设置 reasoning_effort 时, // 自动注入 'medium',避免 One API / DeepSeek 等严格网关返回 400
边界情况也处理得很细:blank reasoning_key("")被 normalize 为 undefined;caller 通过 withGenerationKwargs 显式设置的值不会被自动注入覆盖。
验证目标明确写的是:
Manually verified end-to-end against the real DeepSeek API with a hand-written config.toml that does not set reasoning_key: thinking content renders, no 400, multi-turn conversations work.
OpenCode:通用层设计,OpenAI-centric
eb84f46 fix(llm): split OpenAI reasoning summary blocks (#29000)
这个 commit 展示了 OpenCode 处理 reasoning 的完全不同思路——围绕 OpenAI Responses API 设计:
- 维护
encrypted_content和item_reference的状态机 - 按
item_id+summary_index折叠多个 summary parts store:false时过滤缺少encrypted_content的 reasoning items
这与 Kimi 的 reasoning_content 机制完全不同。 Kimi 不需要 encrypted_content 或 item_reference,而是直接在 message 上附加 reasoning_content 字段。
一个残酷的事实
- OpenCode Issue #26331 “Bug: OpenCode Go bridge layer incompatible with kimi-k2.6 tool calls” — 状态:至今仍是 open
- OpenCode Issue #27054 “KIMI K2.6 showing error in Opencode GO” — 状态:closed,但关闭方式是禁用 MCP(workaround)
#27054 的最后一个 comment:
The workaround is to disable your MCP and then initiate the session
这不是修复,这是回避问题。
Commit 历史对比总结
| 维度 | kimi-code | OpenCode |
|---|---|---|
| 初始设计 | 初始提交即包含完整 Kimi provider + schema 规范化 + 文件服务 | 通用多模型架构,后期通过 bridge 适配 |
| reasoning 机制 | 围绕 reasoning_content 字段设计,自动扫描/回写/注入 effort | 围绕 OpenAI Responses 的 encrypted_content + item_reference 设计 |
| schema 处理 | 专用 normalizeKimiToolSchema,解引用 $ref + 补全 type | 通用 schema 验证,友好错误提示为主 |
| 配置哲学 | 让 OpenAI-compatible 网关"零配置工作",自动推断所有字段 | 依赖用户通过 bridge/config 手动适配 |
| issue 状态 | 持续发布 reasoning 相关 patch(#70, #78) | kimi-k2.6 兼容性问题 #26331 至今 open |
总结:核心差异
| 维度 | kimi-code | OpenCode |
|---|---|---|
| 架构定位 | 原生为 Kimi/Moonshot 设计,专用 provider | 通用多模型代理,通过 bridge 适配 |
| Thinking/Reasoning | 原生支持,全链路保留 reasoning_content | bridge 层易丢失,导致 400 |
| JSON Schema | 专用 normalizeKimiToolSchema 做解引用和类型补全 | 通用 schema 生成,不符合 Kimi 验证器要求 |
| API 格式 | 直接生成 Moonshot 原生格式(含 thinking 配置、$defs 规范等) | 经 OpenAI/Anthropic 协议转换,产生格式不匹配 |
| 服务集成 | 内置 Moonshot fetch/search/file 服务 | 使用通用本地工具 |
| 核心依赖 | CLI 核心不直接依赖 vendor SDK,通过自研 kosong 包隔离 | 核心包直接耦合 @ai-sdk/openai 等十余个 vendor SDK |
从 commit 历史看,kimi-code 的演进方向是持续消除用户配置负担(reasoning_key 从必填 → 可选覆盖 → 自动推断;interleaved 从被过滤 → 正确映射),而 OpenCode 的演进方向是持续深化 OpenAI 生态集成(Responses API、encrypted reasoning、item reference),对 Kimi 的适配停留在通用 bridge 层。
这就是 commit 层面的真相:一个是原生演进,一个是 bridge 间隙。
