Prhub

#50540 [Rust Frontend] Align tool rendering for Kimi K3

原始 PR 作者 BugenZhao 合并时间 2026-08-04 16:03 文件变更 4 提交数 3 评论 0 代码增减 +85 / -9

执行摘要

对齐 Rust K3 渲染器 tool 声明缺省行为

PR body 明确说明目标是“Align the Rust Kimi K3 renderer's tool declarations with the Python and checkpoint encoding behavior”,并指出对应 Python 修复为 PR#50228,本 PR 只覆盖 Rust renderer 行为。此前 Rust 渲染器将缺失的函数描述序列化为空字符串,且对每个 tool 都省略 strict 字段,这与 Python 侧行为及平台 token 化不一致,可能导致同一请求在 Rust/Python 双栈下产出不同提示词。

值得精读,尤其是关注 Rust/Python 前端双栈一致性的工程师。核心看点在 write_tool_declare 的 Option 驱动序列化方式(描述、strict 缺省省略、显式保留)以及 golden fixture 测试的组织方法(expect_file + 输入 JSON + 输出文本)。对于只使用 Python 前端的用户,本 PR 属于配套对齐,了解行为即可。

讨论亮点

本 PR 没有实质性的 review 讨论线程。zhewenl 以“LGTM”批准,Isotr0py 直接批准,claude[bot] 仅输出仓库配置提示。说明该变更在 #50228 已确立语义的前提下没有争议,属明确的对齐修复。

实现拆解

  1. 变更入口:rust/src/chat/src/renderer/kimi_k3/encoding.rswrite_tool_declare 函数,负责把工具列表序列化为嵌入 system 消息的 JSON 声明。
  2. 核心改造:description 字段由 unwrap_or_default() 恒输出空字符串,改为仅当 tool.descriptionSome 时才插入;strict 字段由从不输出改为当 tool.strictSome(bool) 时插入 Value::Bool(strict),从而保证 strict: false 也能被保留,缺省时才省略;parameters 沿用 sort_json 处理且不清理 JSON 内部的 null,用户写入的 "default": null["integer", "null"] 等 schema 内容得以原样保留。
  3. 测试配套:tests.rs 新增三个测试,覆盖缺失可选字段时省略、显式可选字段保留、malformed 参数按 raw-text 渲染三个行为;dynamic_system_tool_declare_input.json 增加 strict: truestrict: false 及嵌套 default: null 的 schema,dynamic_system_tool_declare_output.txt 同步更新为新的期望输出。
  4. 验证方式:按 Test Plan 执行 cargo fmtcargo clippy -D warningscargo test -p vllm-chat renderer::kimi_k3::tests --lib,17 个 K3 渲染器测试全部通过。
  5. 影响范围:仅影响 Rust chat renderer 的 K3 tool 声明输出,不触及推理内核、Python 前端或其他模型渲染路径。
文件 模块 状态 重要度
rust/src/chat/src/renderer/kimi_k3/encoding.rs 渲染器 modified 5.99
rust/src/chat/src/renderer/kimi_k3/tests.rs 渲染器测试 modified 7.4
rust/src/chat/src/renderer/kimi_k3/fixtures/dynamic_system_tool_declare_input.json 测试夹具 modified 3.49
rust/src/chat/src/renderer/kimi_k3/fixtures/dynamic_system_tool_declare_output.txt 测试夹具 modified 1.9

关键符号

write_tool_declare tool_declare_omits_absent_fields_and_preserves_parameter_nulls tool_declare_preserves_present_optional_fields malformed_tool_arguments_render_as_raw_text

关键源码片段

rust/src/chat/src/renderer/kimi_k3/encoding.rs core-logic

核心渲染逻辑所在文件,`write_tool_declare` 的 description/strict 序列化策略是本 PR 的行为变更点。

// 生成 tool 声明段:description / strict 仅在显式提供时输出,
// 与 Python 侧 encoding_k3.py 及 checkpoint 编码保持一致。
fn write_tool_declare(
    out: &mut K3TokenWriter<'_>,
    tools: &[ChatTool],
    dynamic: bool,
) -> Result<()> {
    let mut specs = Vec::with_capacity(tools.len());
    for tool in tools {
        let mut function = Map::new();
        // description 缺省时不再输出空字符串,避免与平台行为不一致
        if let Some(description) = &tool.description {
            function.insert(
                "description".to_string(),
                Value::String(description.clone()),
            );
        }
        function.insert("name".to_string(), Value::String(tool.name.clone()));
        // parameters 整体 Json 值原样保留,包括用户写入的 null 默认值
        function.insert("parameters".to_string(), sort_json(&tool.parameters));
        // strict 显式提供时保留布尔值(包括 false),缺省则省略该字段
        if let Some(strict) = tool.strict {
            function.insert("strict".to_string(), Value::Bool(strict));
        }
        specs.push(json!({
            "function": Value::Object(function),
            "type": "function",
        }));
    }
    // 以 compact_json 序列化为单行 JSON,嵌入 system 消息体
    let payload = compact_json(&sort_json(&Value::Array(specs)))?;    let body = if dynamic {
        format!(
            "## New Tools Available\n\
             The system dynamically extends the toolset via lazy-loading.\n\
             You have access to all existing and extended tools.\n\
             Here are the specs for the extended tools.\n\n\
             ```json\n\
             {payload}\n\
             ```"
        )
    } else {
        format!(
            "# Tools\n\
             Here are the available tools, described in JSONSchema.\n\n\
             ```json\n\
             {payload}\n\
             ```"
        )
    };    // 以内部 system 消息形式写入 token 流
    write_internal_system(out, "tool-declare", &body)
}
rust/src/chat/src/renderer/kimi_k3/tests.rs test-coverage

新增三个回归测试,锁定缺失字段省略、显式字段保留和 malformed 参数 raw-text 回退行为,是本 PR 的质量保障核心。

// 覆盖缺失 description / strict、以及 parameters 内用户显式 null 的完整渲染路径
#[test]
fn tool_declare_omits_absent_fields_and_preserves_parameter_nulls() {
    let mut request = crate::request::ChatRequest::for_test();
    request.tools = vec![ChatTool {
        name: "lookup".to_string(),
        description: None, // 缺失描述:不应输出 description 字段
        parameters: json!({
            "type": "object",
            "properties": {
                "limit": {
                    "type": ["integer", "null"],
                    "default": null // 参数 schema 内的 null 必须原样保留
                }
            }
        }),
        strict: None, // 未显式提供:不应输出 strict 字段
    }];    let rendered = render_request(&request);    // 断言参数 schema 的 null 与联合类型被保留,且顶层可选字段被省略
    assert!(rendered.contains(
        r#"[{"function":{"name":"lookup","parameters":{"properties":{"limit":{"default":null,"type":["integer","null"]}},"type":"object"}},"type":"function"}]"#
    ));
    assert!(!rendered.contains(r#""description":"#));
    assert!(!rendered.contains(r#""strict":"#));
}

评论区精华

没有提炼出高价值讨论线程

当前评论区没有形成足够清晰的争议点或结论,后续有更多讨论时会体现在这里。

风险与影响

  1. 行为变更风险:对缺失 description 的 tool,prompt 中不再出现 "description":"";对显式设置 strict 的 tool 会新增对应字段。这会使 Rust 渲染出的提示词与旧版本不同,可能影响依赖旧格式的回归基准、日志记录或基于提示词内容的缓存命中。
  2. 双栈漂移风险:Rust 与 Python 渲染逻辑仍各自独立实现,本 PR 只对齐当前已知差异,后续 Python 侧若再次调整(如 #50228 之后的迭代),Rust 侧仍可能重新漂移,当前缺少跨栈 golden 对比测试来兜底。
  3. 测试覆盖层面:新增单测和 fixture 已锁定三个关键行为,但未对消息级 tool(developer 消息内 tools)单独做 Rust 侧断言,该场景目前只通过 fixture 间接覆盖。
  4. 安全风险:无,变更仅涉及提示词文本序列化。

影响用户:使用 Kimi K3 模型并通过 Rust 前端发起 tool-calling 请求的用户会观察到提示词中 tool 声明的变化——更加贴近 Python 平台行为,缺失描述不再产生空字段,显式 strict 生效,这有助于提升模型输出与平台的一致性。影响系统:改动局限在 rust/src/chat/src/renderer/kimi_k3/,编译与测试链路清晰,对推理性能无影响。影响团队:后续 K3 前端相关修改需要同时考虑 Python 与 Rust 两侧,双栈一致性维护成本上升,建议把 #50228 与本文的测试语义合并为跨栈契约文档或共享 fixture。

提示词序列化行为变更 Rust/Python 双栈易漂移 golden 测试覆盖

关联 Issue

#50228 [Bugfix][Kimi K3] Fix message-level tools, response_format passthroug…

完整报告

参与讨论