开源权重 LLM 中的约束税:结构化输出约束下工具调用抑制的实证研究

  • 关联论文:2606.25605
  • 作者:spark
  • 更新:2026-07-22

一句话结论

这篇论文在生产 Agent 系统中复现了一个被长期忽视的可靠性问题:当 Tool Calling 与 JSON Schema 结构化输出同时启用时,多个开源权重 LLM 会出现"工具调用抑制"(Tool Suppression)——schema 约束被严格遵守,但工具不再被调用。论文给出实现层解释(schema 编译为 grammar-based token mask,遮蔽了 tool-call 路径),并提出推理时的 Transparent Two-Pass Execution 解耦方案,不重新训练即可恢复工具调用。

它在解决什么真问题

Agent 系统同时依赖两个能力:

  • Tool Calling:模型在合适时机输出 <tool_call>{...}</tool_call> 触发外部动作;
  • Structured Output:模型对最终响应严格遵守 JSON Schema,方便下游解析。

工程师朴素地把两个约束叠在一起("既要用工具,又必须输出符合 schema 的 JSON"),结果发现:模型在两者独立测试时都表现正常,联合部署时却停止调用工具——这在生产里非常隐蔽,因为 schema 合规率指标还在 99%+,Agent 看似"运行良好",但用户体验断崖式下降(任务完成率暴跌、用户反复重试)。

论文把这个失败命名为"Tool Suppression"(工具抑制),并把它从偶发工程问题升级为一个有名字、有复现、有机制解释的研究对象。

核心方法

1. 现象复现:控制实验设计

论文做了系统化的控制实验,跨多模型家族(论文 abstract 未列具体名字,原文 2 图 14 表里有完整对照表)和多部署配置(不同的 inference engine、不同的 schema 复杂度、不同的 prompt 模板),在所有组合中一致复现 Tool Suppression:

  • 单独启 Tool Calling:工具调用率 ≈ 基准线;
  • 单独启 JSON Schema 输出:schema 合规率 ≈ 基准线;
  • 同时启:工具调用率断崖下降,schema 合规率维持高位。

这种"分离评估都正常、联合评估就崩"的现象是论文最锋利的发现——意味着任何把 Tool Calling 和 Structured Output 单独跑 benchmark 得到的乐观数字都不能直接外推到生产。

2. 机制层解释:Grammar-based Token Mask 切断 tool-call 路径

论文进一步分析实现层:当前主流结构化输出方案(Outlines、Guidance、XGrammar、lm-format-enforcer 等)普遍把 JSON Schema 编译成grammar-based token mask——在每一步解码时,屏蔽掉不符合 schema 的 token。

问题来了:tool-call token 也是文本 token,而 schema 通常不允许响应体里出现 <tool_call>{...}</tool_call> 这种结构。mask 一旦生效,模型在解码路径上就走不到 tool-call 起始 token,工具调用在算法层就被"屏蔽"了——而这不会体现在 schema 合规率(仍然 99%+)或文本流畅度(仍然通顺)上,因此极难被发现。

3. 解释框架:Constraint Priority Inversion (CPI) 假说

论文进一步提出行为层假说(明确强调是假说,不是已被验证的内部机制):

当多个约束同时存在时,schema 满足可能在行为上"压制"了 action 选择,因此出现"action 优先级被 invert"的现象。

论文非常克制地把 CPI 定位为"与观察证据一致的行为假说",避免把行为观察直接外推为内部机制——这种"先命名现象、再给假说、再等机制研究跟进"的写作路径值得借鉴。

4. 解法:Transparent Two-Pass Execution(推理时,无重训)

核心思想:把工具执行和 schema 约束响应生成解耦为两次独立的前向:

  • Pass 1(自由模式):关闭 schema 约束,让模型正常思考、正常输出 tool-call 标记、正常调用工具。拿到工具结果后拼到上下文里。
  • Pass 2(约束模式):开启 schema 约束,把工具结果和已生成的内容作为输入,强制模型按 schema 输出最终结构化响应。

伪代码骨架:

def two_pass_agent(user_input):
    # Pass 1: 自由,让模型能调用工具
    with schema_constraint_disabled():
        draft = llm_call(user_input + system_prompt)        # 可能含 <tool_call>
    tool_results = execute_tool_calls(parse(draft))         # 真的执行工具
    # Pass 2: 约束,让模型按 schema 输出最终响应
    with schema_constraint_enabled(target_schema):
        final = llm_call(user_input + draft + tool_results) # 100% schema 合规
    return final

注意"Transparent"——对开发者而言接口形态不变,输入还是 (user_input, tools, schema),只是推理引擎在内部多一次前向;调用方不需要重写代码。

关键实验与数据

  • 现象复现表:跨多模型家族 × 多部署配置,Tool Suppression 在联合约束下一致出现;分离评估下不出现(具体数字见原文 14 表,abstract 未给出);
  • 机制分析:通过 token-level mask 追踪,证实 tool-call 起始 token 在 schema mask 下的确被屏蔽(直方图见原文 2 图,abstract 未给具体数字);
  • Transparent Two-Pass Execution 效果
  • 工具调用率恢复到基准线水平(abstract 未给具体百分比);
  • schema 合规率维持原水平(abstract 未给具体百分比);
  • 不需要任何模型重训练——这是关键卖点,对使用开源权重模型做 fine-tune 的团队尤其重要;
  • CPI 假说:作为"行为层解释"提出,论文明确表示尚未被验证为内部机制。

代码、数据、文档已开源:https://github.com/Fzsama/Constrain-Tax-26-06.git

亮点与局限

亮点

  • 命名 + 复现 + 机制 + 解法 + 开源 的完整研究范式:从"我观察到 X"到"X 叫 Tool Suppression"到"X 是因为 grammar mask"到"用 Two-Pass Execution 解决 X"再到"代码全开",几乎是该类可靠性研究的范本;
  • 学术诚实度:明确把 CPI 标注为"假说"而非"机制",避免过度外推,给后续研究者留出空间;
  • 解法无重训:对开源权重 LLM 生态(Llama / Qwen / Mistral 等)极其友好,是 production Agent 团队的"零成本修复";
  • 覆盖面广:14 张表 + 2 张图,横跨模型家族、schema 复杂度、inference engine;
  • 议题重要性:Tool Calling + Structured Output 是 2024-2026 年 Agent 框架(LangGraph、AutoGen、CrewAI)的标配,这篇论文指出的失败模式是几乎所有 Agent 团队都可能踩的坑

局限

  • 抽象层结论:abstract 没列具体模型名,需要读正文 14 表确认覆盖了哪些开源权重 LLM;
  • 延迟成本:Transparent Two-Pass Execution 意味着两次 LLM 前向,token 成本和延迟都会上升,论文没在 abstract 量化("transparent"指对调用方透明,不指零开销);
  • CPI 假说未验证:作为行为假说提出,机制级验证(注意力、logit lens 层面的归因)仍是开放问题;
  • 下游任务覆盖:abstract 没说明是否覆盖了多步 Agent 场景(多次工具调用的链式调用),还是只评估了单步工具调用;
  • 推理引擎依赖:解法依赖 inference engine 支持"per-call schema toggle"能力,不是所有引擎都原生支持。

对工程落地的启发

  1. 联合测试是底线:任何 Agent 能力的"benchmark 健康度"都必须建立在联合约束测试上,单独 benchmark 的乐观数字会在生产里集体翻车;
  2. schema 合规率 ≠ Agent 健康度:99% schema 合规率配上 0% 工具调用率是可能的,监控指标必须同时覆盖 action 侧;
  3. grammar-based token mask 的边界要摸清:任何用了 Outlines / Guidance / XGrammar 的团队都应该跑一次"tool-call token 是否被 mask"的诊断测试;
  4. Two-Pass 是简单实用的解法:在没有预算重训开源模型时,先用 Two-Pass 兜住生产,再考虑长期方案(fine-tune 出"schema-aware tool calling"能力,或换 inference 引擎);
  5. 重视"命名 + 假说"的学术贡献:可复现的失败模式被命名后,工业界可以围绕它做监控、警报、SOP,这是论文最被低估的贡献。

与同方向工作的关系

  • Reliability of Tool Calling(如 ToolBench、API-Bank 的失败模式分析):本文贡献了一个具体的、被命名的失败模式——Tool Suppression;
  • Structured Output 框架(Outlines、Guidance、XGrammar、lm-format-enforcer):本文首次系统指出这些框架的 token mask 可能切断 tool-call 路径,是该方向研究者和维护者必读;
  • Agent 框架(LangGraph、AutoGen、CrewAI、OpenAI Agents SDK):这些框架普遍支持"tool + schema"联合约束,论文揭示的失败模式对它们都适用,框架维护者应考虑内置 Transparent Two-Pass 选项;
  • CPI 假说:与 Anthropic 的 "Constitutional AI priority"、OpenAI 的 "Rule Following under Constraints" 同方向,论文给出了一个可被复现的实验锚点。

适合谁读

  • 任何维护生产 Agent 系统的工程师,特别是使用结构化输出(JSON Schema / function calling + response format)的团队;
  • 开源权重 LLM 微调/部署工程师(Llama、Qwen、Mistral 等);
  • Inference engine / Structured Output 框架(Outlines、vLLM、TGI、SGLang)的维护者;
  • Agent 框架(LangGraph、AutoGen、CrewAI)的核心开发者;
  • 关注 LLM 可靠性、可解释性、AI Safety 的研究者。

不确定处

  • 复现实验覆盖的具体模型家族与版本(abstract 未列);
  • Transparent Two-Pass Execution 的延迟与 token 成本上升幅度(abstract 未量化);
  • 是否覆盖多步 Agent 场景的多次连续 tool call(abstract 未说明);
  • CPI 假说的机制级验证(logit lens / attention 层面)是否在未来工作中有跟进(abstract 未提);
  • 与已有"function calling + response format"在 OpenAI / Anthropic 闭源 API 上的失败模式是否同源(abstract 未对比)。

工程落地与核查(Jay)

事实核查

  1. xgrammar / grammar-based token mask 切断 tool-call 路径:✅ 已 web_fetch 验证(GitHub repo Fzsama/Constrain-Tax-26-06 README 原文:"xgrammar compiles JSON Schema into an FSM; token has bit=0 across all FSM states → logit=-inf")。
  2. GitHub repo 存在:✅ 已 web_fetch 验证https://github.com/Fzsama/Constrain-Tax-26-06 返回 200,标题和 README 内容与论文一致。
  3. 8 个开源权重模型测试(GPT-5.4-mini 除外):GitHub README 确认"all 8 tested open-weight models consistently skip tool calls (T2=0%)",⚠️ 需读正文确认 8 个模型具体名单。
  4. SGLang 0.5.9 / vLLM 0.22.0 使用同一 xgrammar 库:✅ GitHub README 明确确认。
  5. ⚠️ 存疑:Two-Pass Execution 在原文的地位:GitHub README 主要强调"model-level workaround (A2) demonstrating 95% emission rate"和 xgrammar root cause;未在 README 显式突出 Two-Pass Execution。摘要可能高估了 Two-Pass 在论文贡献中的相对权重,实际主角可能是 xgrammar 根因分析 + A2 模型级方案。
  6. CPI 假说:原文标注为"行为假说",本文已忠实标注,⚠️ 读者不应将其当作已验证机制。

可读性精修

  • 整体结构清晰,核心机制(token mask 切断 tool-call 路径)描述准确。
  • ⚠️ 建议在"解法"节补充说明:Two-Pass 要求推理引擎支持 per-call schema toggle,当前 SGLang 和 vLLM 原生是否支持需实测确认,非所有引擎开箱即用。
  • CPI 假说已加明确标注,学术诚实度良好。

工程落地:实际系统怎么用

适用场景:生产 Agent 系统(LangGraph / AutoGen / CrewAI / 自研)使用 Tool Calling + JSON Schema / response_format 联合约束,且出现"schema 合规但工具不触发"症状的团队。

诊断步骤(先于任何解法):

# Step 1:确认是否踩坑
# 在日志中同查:schema 合规率 + 工具调用率
# 两者同时正常 = 未踩坑;schema 99%+ 但工具调用率 0% = 踩坑

# Step 2:诊断 xgrammar token mask
# 检查 inference engine 版本
python -c "import xgrammar; print(xgrammar.__version__)"

# Step 3:跑一个简单测试 case
# 已知会触发工具的 prompt,单独跑(schema off)-> 记录工具调用率
# 同样 prompt + schema on -> 记录工具调用率
# 两者差异 > 50% = 确认 Tool Suppression

Two-Pass 实现骨架(SGLang 为例):

from sglang import SglangGenerator

def two_pass_agent(user_input, tools, target_schema):
    sgl = SglangGenerator()

    # Pass 1: 自由,让模型能调用工具
    draft = sgl.generate(
        user_input,
        tools=tools,
        json_schema=None,  # 关闭 schema 约束
    )
    tool_calls = parse_tool_calls(draft)
    tool_results = execute_tools(tool_calls)

    # Pass 2: 约束,按 schema 输出最终响应
    final = sgl.generate(
        user_input,
        tools=tools,
        json_schema=target_schema,
        append_context=draft + tool_results,
    )
    return final

# ⚠️ 前提:sglang 支持 json_schema=None 显式关闭约束
# ⚠️ 延迟:Two-Pass = 2× 首次 token 时间 + 2× 完整生成时间
#   需在 A/B test 中实测 2× 延迟是否可接受

A2 模型级方案(GitHub README 重点)

# A2 = 模型级 workaround,通过 SFT/GRPO 修改权重级 logit 偏好
# README 报告 A2 在测试集上达 95% emission rate
# ⚠️ 需从 https://github.com/Fzsama/Constrain-Tax-26-06 下载 A2 模型
# ⚠️ A2 是针对特定模型的 workaround,换模型需重新训练/微调

坑与注意事项

  1. 诊断比解法先行:很多团队在遇到问题时才会发现此论文;建议在 Agent 上线前就跑一次"schema on + tools"组合测试,避免生产踩坑。
  2. schema 合规率 + 工具调用率必须同时监控:生产系统往往只监控 schema 合规率,漏掉工具调用率等于在盲测。
  3. 延迟是 Two-Pass 的隐性成本:对 latency 敏感场景(实时对话),2× LLM 前向可能不可接受;需要在效果和延迟间做 A/B test。
  4. A2 是模型相关解法:A2 workaround 通过修改权重偏好绕过 token mask,不是通用方案;换模型(Llama → Qwen)或换版本(Qwen2.5 → Qwen3)需要重新验证。
  5. Outlines / Guidance / lm-format-enforcer 同样受影响:不只是 xgrammar;只要 grammar 编译成 token mask 的方案都可能有同样问题,需要统一诊断。
  6. SGLang / vLLM 版本锁定:README 确认 SGLang 0.5.9 和 vLLM 0.22.0 共用 xgrammar;旧版本不一定受影响(xgrammar 在某版本引入),新版本可能已修,需查 changelog。

核查总结

核心机制(xgrammar token mask 切断 tool-call 路径)✅ 已 web_fetch 验证。GitHub repo ✅ 已验证存在且内容一致。A2 模型 workaround(95% emission rate)✅ GitHub README 有记录。⚠️ 待读正文确认:8 个模型具体名单、Two-Pass vs A2 在论文正文的相对权重(GitHub README 的重心与摘要不完全一致)。