Skip to content

配置 DeepSeek 模型的 max_output_size 后报错 400 Invalid max_tokens value #306

Description

@chengkeen

[Bug] 通过 /provider 添加 DeepSeek 等模型后,自动写入的 max_output_size 导致 400 错误

版本: 0.7.0
组件: packages/agent-core/src/session/provider-manager.ts + packages/kosong/src/providers/openai-legacy.ts

问题描述

通过 /provider 命令添加 DeepSeek 后,kimi-code 会自动将模型目录中的 max_output_size 写入配置文件。但实际请求时会报 400 错误:

Error: [provider.api_error] 400 Invalid max_tokens value, the valid range of max_tokens is [1, 393216]

关键点:这不是用户手动配置的问题——用户只需 /provider 选择模型,系统就会自动在 config.toml 中生成包含 max_output_size = 384000 的模型配置,然后直接报错,开箱即崩。

复现步骤

  1. 执行 /provider 添加 DeepSeek 提供商
  2. 从模型列表中选择 deepseek-v4-flashdeepseek-v4-pro
  3. 系统自动生成如下配置(用户无需手动编辑):
    [models."deepseek/deepseek-v4-flash"]
    provider = "deepseek"
    model = "deepseek-v4-flash"
    max_context_size = 1000000
    max_output_size = 384000   # ← 从模型目录自动获取的
  4. 启动会话,发送任意消息
  5. 请求失败,返回 400 Invalid max_tokens value

根因分析

问题由两个因素叠加导致,缺一不可

因素一:max_output_size 被模型目录自动填充但不被消费

packages/kosong/src/catalog.ts:44 中,CatalogModel 定义了 maxOutputSize,从模型目录 API 返回的 model.limit.output 中提取(第 130 行):

maxOutputSize: typeof output === 'number' && output > 0 ? output : undefined,

当通过 /provider 添加模型时,系统调用 applyOpenPlatformConfig / applyCustomRegistryProvider 等函数将这些模型写入 config.tomlmax_output_size 被自动写入每个模型的配置中

然而在 packages/agent-core/src/session/provider-manager.tstoKosongProviderConfig() 函数中,maxOutputSize 仅传递给了 anthropic 提供者,openai 提供者完全忽略了该字段:

// provider-manager.ts:220-238
case 'anthropic':
  return {
    type: 'anthropic',
    ...(maxOutputSize !== undefined ? { defaultMaxTokens: maxOutputSize } : {}),  // ✅ 传递
  };
case 'openai':    // ← DeepSeek、StepFun 等走这里
  return {
    type: 'openai',
    // ❌ maxOutputSize 未传递
  };

OpenAILegacyChatProvider 的接口和构造函数实际上已经准备好接收 maxTokens 参数:

// openai-legacy.ts:59-64
export interface OpenAILegacyOptions {
  maxTokens?: number | undefined;  // ← 接口已有此字段
}

// openai-legacy.ts:360-361
if (options.maxTokens !== undefined) {
    this._generationKwargs.max_tokens = options.maxTokens;
}

即使这里传过去了也不会解决报错(见因素二),所以这是一个双重 bug

因素二:0.7.0 新增的 withMaxCompletionTokens 发送了错误的值

0.7.0 为 OpenAILegacyChatProvider 新增了 withMaxCompletionTokens 实现(openai-legacy.ts:479):

withMaxCompletionTokens(maxCompletionTokens: number): OpenAILegacyChatProvider {
    return this.withGenerationKwargs({ max_tokens: maxCompletionTokens });
}

agent/index.ts:200 中,completion budget 系统计算预算时:

const completionBudgetConfig = resolveCompletionBudget({
    reservedContextSize: loopControl?.reservedContextSize,
});

当环境变量 KIMI_MODEL_MAX_COMPLETION_TOKENS 未设置时,resolveCompletionBudget 回退到 reservedContextSize(默认 50000)或 DEFAULT_UNKNOWN_CONTEXT_FALLBACK(32000)。但在 computeCompletionBudgetCap 中:

const cap = args.budget.hardCap ?? (maxCtx > 0 ? maxCtx : ...);

这里的 maxCtx 来自模型的 capability.max_context_tokens,而该值来自配置文件中的 max_context_size。对于 DeepSeek V4,max_context_size = 1000000,因此 cap = 1000000

最终向 DeepSeek API 发送了 max_tokens: 1000000,而 DeepSeek V4 的 max_tokens 上限是 384,000(官方文档标注),导致 400 错误。

0.6.0 没有此问题,因为当时 OpenAILegacyChatProvider 未实现 withMaxCompletionTokens,completion budget 系统直接跳过它,DeepSeek 使用服务端默认值。

影响范围

  • 所有通过 /provider 添加的 DeepSeek 模型(V4 Flash、V4 Pro 等)
  • 只要满足:模型 max_context_size > 393216 且未设置 KIMI_MODEL_MAX_COMPLETION_TOKENS 环境变量
  • 受影响的模型在 catalog.ts 中都有 maxOutputSize 字段,添加后自动写入配置文件
  • 开箱即崩:用户什么都不用做,/provider 选完模型就触发

建议修复方案

推荐方案 A(两处联动修复,根治问题,与 anthropic 行为一致):

第 1 处provider-manager.ts — 将 maxOutputSize 作为 maxTokens 传递给 openai 提供者:

case 'openai':
  return {
    type: 'openai',
    model,
    baseUrl: ...,
    apiKey: ...,
    reasoningKey,
+   ...(maxOutputSize !== undefined ? { maxTokens: maxOutputSize } : {}),
    ...defaultHeadersField(provider.customHeaders),
  };

第 2 处openai-legacy.ts — 添加 _explicitMaxTokens 保护和 clamp,参考 anthropic.ts 的实现:

// constructor
+ this._explicitMaxTokens = options.maxTokens !== undefined;
  if (options.maxTokens !== undefined) {
      this._generationKwargs.max_tokens = options.maxTokens;
  }

// withMaxCompletionTokens
  withMaxCompletionTokens(maxCompletionTokens: number): OpenAILegacyChatProvider {
+     const requestedCap = maxCompletionTokens;
+     const existingCap = this._generationKwargs.max_tokens;
+     const clone = this._withGenerationKwargs({
+         max_tokens:
+             existingCap === undefined || this._explicitMaxTokens
+                 ? existingCap ?? requestedCap
+                 : Math.min(existingCap, requestedCap),
+     });
+     return clone;
  }

方案 B(备选,仅修复 400 错误)

computeCompletionBudgetCapwithMaxCompletionTokens 中对 max_tokens 做合理 clamp,避免把 max_context_size 直接当作 max_tokens 发送。

临时规避措施

export KIMI_MODEL_MAX_COMPLETION_TOKENS=384000

环境变量作为 hardCap 优先级高于上下文大小,会被 resolveCompletionBudget 优先使用,避免回退到 max_context_size

或者在配置文件中手动删除 max_output_size 字段(如果模型是通过 /provider 添加的,下次刷新会被重新写入)。

环境信息

  • kimi-code 版本: 0.7.0
  • 操作系统: Linux
  • 模型: DeepSeek V4 Flash / V4 Pro(通过 /provider 添加)
  • Provider: openai

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions