如何让大模型输出稳定结构化输出
🍏

如何让大模型输出稳定结构化输出

Tags
Date
Created
Mar 24, 2026 07:25 AM
Blocking
Blocked by
  • 大模型本身具有很强的随机性,在单论对话中一个模型的错误影响不大,但是在 agent 中出现错误可能导致 整个 agent 崩溃
  1. 约束解码(保证格式 100% 正确但不保证内容正确)
      • 模型生成 token 的时候会根据预定义的 json schema 限制它能选择的 token 集合
        • 例如 json 的第一个字符必须是 左括号, json schema 就会限制它第一个字符只允许生成 左括号的 token
      • 缺点:强制约束远离首选 token 从低概率的 token 中选择 最终输出可能语法正确,但是语义质量可能会下降
      • 基础模型可能在约束中收益,而指令微调模型可能出现性能退化
  1. 验证 + 重试 循环(修复语义层面的正确性)
      • 如果要求模型生成结果 1-5, schema 能保证生成的是整数,但是无法保证不会输出 6
      • 重试机制会自动将上下文发送给大模型,让它有机会纠正问题,构建自愈系统
      • 用 pydantic 或者 zod 定义带有业务规则的 schema
        • 比如字段的取值范围,字段之间的逻辑关系
        • 模型输出后被传入 pydantic验证器判断,如果失败,将错误信息和原始输出一起返回给模型纠正
  1. 如果模型不支持原身的约束解码
      • 把结构化输出伪装成工具调用,模型通过微调学会了怎么按 schema 生成函数调用参数,泛化这个能力,定义一个虚拟工具,将期望的输出格式定义成它的输入 schema,然后强制模型调用这个虚拟工具,拿到结果
      • 几乎所有主流模型都支持工具调用,可以使用这种方式
  1. logit masking
      • agent 在每一步都要从一组工具中选择下一个动作,这本身也是一种结构化输出
      • 但是工具太多的时候模型容易选错,可以用的方法有 动态增删工具,当前用不到的工具就移除,但是 Manus发现因为工具定义在 上下文的前部不能这么做,任何变更都会导致后面所有内容的 kv cache 失效,代价非常大
        • KV Cache 的复用必须满足“严格的前缀匹配”(Strict Prefix Matching)。只要中间或开头有一个 Token 发生了改变,哪怕后面的内容一模一样,后面的 KV Cache 也必须全部作废重新计算
      • Manus 的方案是 工具定义永远不变但在解码的时候通过遮蔽 logits 来屏蔽当前不需要的工具,工具命名精心设计,例如 浏览器工具用 browser_ 开头 文件工具用 file_ 开头,这样按前缀遮蔽就很简单,保证了 kv cache
        •  在生成每一个 Token 时,模型的最后一层(通常是一个线性层)会为词表中的每一个 Token 打一个“原始分数”,这个分数就叫 Logits
        • 然后,这个 Logits 数组会经过一个 Softmax 函数,转换成概率分布(总和为 100%)。分数越高的 Token,变成的概率就越大,模型最终就会根据这个概率去抽取(Sample)下一个词
        • 在模型算出 Logits 之后、进行 Softmax 变成概率之前,人为地去修改这个分数数组
        • 如果你想彻底屏蔽某个词(比如当前不想用文件工具,要屏蔽 file_ 这个词),你只需要在程序里把代表 file_ 这个 Token 的 Logits 分数强制改成负无穷大
        • schema 也是同理
  1. 多 agent 之间的结构化通信
      • 当一个任务被多个 agent 执行时 agent 之间传递的信息也必须是结构化的
      • 比较好的做法(参考自Manus)是将 子 agent 当工具来调用, planner 定义输出 schema 子agent 通过约束解码填充这个 schema
      • open code 压缩机制也遵循这个原则,当上下文过长需要压缩时,它就输出固定格式的摘要,包含 goal、instructions、discoveries、accomplished、relevant files
      • 核心原则:多 agent 通信不能依赖自然语音的模糊性必须有明确的 schema 契约,谁生产什么样的数据,数据长什么样,谁消费数据,等等
  1. 对抗模式锁定(对抗长步骤)
      • agent 执行几十步后累积了大量相似的观测队,模型会陷入一种模式锁定的状态,会有很高的概率重复之前的输出模式,Manus 的 agent 因为在上下文中看到了批量类似操作就不断重复相同的动作
      • Manus 给出的方法是 引入变异,输出的 schema 不变,但是同一个 schema 下的序列化方式有微小的变化,打破模型的自回归惯性和模式坍缩,这个方案的细节纰漏很少
      • 实践中更常见的方法是,结合上下文压缩,减少重复 pattern 的堆积,适时的 context reset 来缓解这个问题
  • schema 中每个字段的描述,不是给人看的文档而是给模型的 instruct
    • 例如 rating 可以让模型评分,不考虑结构只考虑内容, 远比一个数据类型效果更好
  • 结构化推理分阶段
    • 先让模型自由思考,最终输出时再施加 schema 约束(Claude的方案)
  • 结构化约束的悖论(定期重新评估)
    • 上面的所有限制可能随着模型进步而变成模型枷锁,会被限制性能,添加约束是为了改善性能,但是随着模型的性能增长反而会降低性能
    • 建议使用不同的模型评估 agent 体系,如果agent 不会随着更强的模型而提升,说明控制机制可能在拖累 模型