LLM 结构化输出原理、实现方式及实践
kafm

结构化输出的意义

LLM 是输出不可控的概率模型,而结构化输出则增强输出的可控性。
有了这种可控性,LLM 就具备了一个程序的交互 interface 可以稳定地接入上下游程序,比如调用工具

结构化输出的原理及实现方式

实现格式化输出通常有以下几种手段,

  • 提示词工程
    通过指令 “只输出JSON”,few-shot 示例等提示词技巧引导模型进行格式化输出
  • 后处理
    instruct 等库对模型输出进行后处理,进行提取或修复等。包括解析后反馈错误重新生成等。
  • 约束解码
    在选择 token 时用 mask 机制直接排除不符合输出格式的候选 token,在有限输出空间内选择 token。
    {"key": value},从仅让模型预测格式串中的value部分,到约束value的格式/类型,约束解码都可以保证可靠的格式化输出。
    再进一步,通过等价的状态机进行约束解码,可以进行任意的格式化输出。约束解码是结构化输出的主要工程手段。
  • SFT / RL
    通过后训练手段形成输出偏好,使模型遵循一定模式。可靠性高,是结构化输出的理论保障、能力来源。

当前许多模型厂商从 SFT/RL 的角度增强模型的格式化输出能力,并从推理端实现约束解码(例如Ollama),从而提供可靠的结构化输出 API。

工具调用与结构化输出,先有鸡还是先有蛋

依靠结构化输出的结果,我们才能稳定地处理工具调用
然而,如果模型能调用给定的工具,那么任意输出格式都可以包装成工具,通过 LLM 填写调用参数,也就实现了指定格式的输出
看起来两者相互成就,已经变成了鸡生蛋,蛋生鸡的问题
许多厂商都强调模型有tool use/ function call能力,却不强调模型支持结构化输出,可能工具使用能力是结构化输出的内核吧,结构化输出结合了众多工程手段来提供可靠性

本地模型在 langchain 中的结构化输出

本文在 langchain 以下生态中进行讨论(2026-02-02)

1
2
3
langchain-core            1.2.7
langchain-openai 1.1.7
langchain-ollama 1.0.1

YesNoJudge 类的 JSON 格式,搭配不同 with_structured_output() 的格式化输出方式(即method参数)测试结构化输出表现
method 参数可选项有 json_schema, function_callingjson_mode,其中json_mode因无法给出确定格式的 JSON ,需要后处理,不推荐使用。(来源:https://platform.openai.com/docs/guides/structured-outputs#json-mode)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class YesNoJudge(BaseModel):
"""return judge result from the context with this function."""

yes_or_no: Literal["yes", "no"] = Field(
description="The answer should be either 'yes' or 'no'"
)
explanation: str


judge = ChatOpenAI(
model=model_name, temperature=0.6, streaming=True, **fast_judge_config
)
judge = judge.with_structured_output(
schema=YesNoJudge, method="function_calling", strict=True, include_raw=True
)
image 图:指定`json_schema` 方式,在 prompt 中提示按JSON格式输出后,gpt-oss:20b 仍调用不存在的工具进行格式化输出 image 图:指定 `function_calling` 方式,在 prompt 中提示按JSON格式输出后,qwen3:4b 会在内容中输出 json,不调用工具 image 图:指定 `function_calling` 方式,去除json prompt,qwen3:4b 仍倾向于遵循指令在内容中输出结果,不调用工具 image 图:指定 `function_calling` 方式,去除指令式prompt,qwen3:4b 有概率调用工具返回结果

可见 Qwen 模型更重视指令遵循,而 GPT 偏向于使用工具

langchain 中 with_structured_output() 的实现简析

不同 backend 生态包的 with_structured_output() 实现不同,
对于langchan-openai:

  • function_calling 把 JSON schema 作为 tool 传入,并填充 tool_choice 参数,强制 LLM 使用工具输出工具调用的 schema
    相当于把工具调用能力转化为结构化输出能力
  • json_schema 方式则是对输出内容进行解析,该参数
  • json_mode 建议配合 prompt 使用,全靠模型发挥
    而 create_agnet API 中 response_format 也是直接调用 LLM 后端的结构化输出能力

langchain + langchain-openai + Ollama 后端结构化输出踩的坑

为什么用 langchain-openai 不用 langchain-ollama,因为想着兼容 OpenAI API 会好一些
甚至用 langchain-ollama + Ollama 还一直出现 502 报错。。openai / curl 均正常

  • 看似兼容,实则不兼容
    如 langchain[openai, ollama] 中 chat_models 的 with_structured_output() 实现不一致
    且将 Ollama 作为后端使用 langchain-openai 时 with_structured_output 的表现和模型高度相关,需根据模型调优 method 参数
    实际支持多个后端仍需要进行适配

  • 多处实现,效果不一
    langchain[openai, ollama]chat_modelswith_structured_output()from langchain.agents import create_agentresponse_format都用于结构化输出,然而可靠性不同

  • 过度封装
    源码里到处是消除警告的类型不匹配 # type: ignore
    MessageState 的消息 messages 传入 AgentState 的 messages 提示类型不匹配,有点搞笑