当前位置: 首页 > article >正文

LangChain解锁LLM大语言模型的结构化输出能力:调用 with_structured_output() 方法

什么是LLM的结构化输出能力?

在一些工业级LLM应用或比较复杂的LLM应用编排环节,我们需要用LLM的输出作为下一环节的输入,而这个过程往往对LLM输出的格式有一定要求,比如JSON、XML、YAML、CSV、Markdown 表格和HTML 等比较常见的格式。因此我们需要通过各种方式手段让LLM具备符合我们期望的结构化输出能力,即模型能够按照用户指定的格式或规则生成内容(而不仅仅是自由形式的文本)的能力。这种能力使LLM的输出结果更易于被程序解析、集成到自动化流程中,以一种更为可控的方式满足特定落地场景的需求。

如何让LLM具备该能力?

以下内容参考自 langchain官方文档教程:How to return structured data from a model (内容&代码解释由Deepseek-R1部分帮忙完成)

调用 with_structured_output() 函数是一种最为简单且可靠的结构化LLM输出的方式。它专为支持原生APIs对输出进行结构化的模型设计(例如工具 / 函数调用或 JSON 模式),并在底层直接调用LLM原生具备的这些能力。

该方法通过接收一个 模式(schema) 来指定输出属性的名称、类型和描述,返回一个类似模型的 Runnable 对象。与普通模型不同,此对象输出的不是字符串或消息,而是符合指定模式的结构化对象。模式可通过以下三种方式定义:

  1. TypedDict 类(类型化字典):Runnable 返回字典
  2. JSON Schema(JSON 模式):Runnable 返回字典
  3. Pydantic 类:返回 Pydantic 对象

接下来我们用实际的示例代码具体看以上三种方式的落地实现。

目标:希望LLM根据用户输入的信息生成相关的笑话并将该笑话的铺垫(set up)和笑点(punchline)分离出来,并同时给这个笑话的好笑程度以十分制进行打分(rating)。

1. Pydantic 类

代码如下:

# 导入所需库
from langchain_ollama import ChatOllama  # Ollama聊天模型接口
from typing import Optional             # 用于定义可选类型
from pydantic import BaseModel, Field   # 数据验证和字段定义工具

# 初始化Ollama聊天模型
llm = ChatOllama(
    model="llama3.1:latest",   # 指定使用的模型版本
    temperature=0.8            # 控制生成随机性(0-1,越高越有创意)
)

# 使用Pydantic定义结构化输出格式
class Joke(BaseModel):
    """Joke to tell user."""
    
    setup: str = Field(description="The setup of the joke")  # 必填字段,字符串类型
    punchline: str = Field(description="The punchline to the joke") 
    rating: Optional[int] = Field(  # 可选字段,整型(可为None)
        default=None, 
        description="How funny the joke is, from 1 to 10"
    )

# 将模型包装为结构化输出生成器
structured_llm = llm.with_structured_output(Joke)  # 绑定Pydantic模型到LLM

# 调用结构化模型生成内容
result = structured_llm.invoke("Tell me a joke about cats")  # 输入提示词

让LLM讲一个关于猫猫的笑话,结构化输出如下:

print(result)
# result变量是Joke()这个Pydantic类的对象
setup='Why did the cat join a band?' punchline='Because it wanted to be a purr-cussionist' rating=8

# 将对象的三个属性打印出来
print(result.setup)
Why did the cat join a band?
print(result.punchline)
Because it wanted to be a purr-cussionist
print(result.rating)
8

使用上述这种基于Pydantic类对LLM输出进行格式化方法时,除了类的字段结构定义,以下元素对结构化输出的生成也是至关重要:

  1. 类名称(如Joke
    作为模型理解任务类型的提示(例如生成笑话)。
  2. 类的文档字符串(如"""Joke to tell user."""
    描述类的整体目的,为模型提供上下文。
  3. 字段名称与描述(如description="笑话的铺垫部分"
    明确每个字段的具体含义和预期内容。

底层实现原理

大多数情况下,with_structured_output() 方法利用模型的 函数/工具调用接口(Function/Tool Calling API)。从开发者视角可以理解为:上述所有元数据信息(类名、文档字符串、字段描述)实际上都会被整合到模型的输入提示中,指导其生成符合规范的结构化输出。

设计建议

  • 语义清晰的类名:使用WeatherReport而非Data等泛化名称
  • 详细的文档字符串:明确说明生成任务的目标和范围
  • 精准的字段描述:避免模糊定义(如用"用户年龄(1-100岁)"替代"年龄"

这些元数据共同构成模型的“结构化生成指南”,将直接影响输出的准确性和可控性。

2. TypedDict 类

若有以下需求,可通过 TypedDict 定义模式(Schema)而非基于Pydantic 类的方式:

  1. 不需要参数验证:希望跳过 Pydantic 的数据类型检查。
  2. 需要流式输出:支持逐步生成结构化内容而非一次性返回。
  3. 避免依赖 Pydantic:简化项目依赖或适配其他数据模型框架。

示例代码如下:

# 导入所需库
from typing import Optional  # 支持可选类型
from typing_extensions import Annotated, TypedDict  # 类型扩展工具
from langchain_ollama import ChatOllama  # Ollama聊天模型接口

# 初始化Ollama聊天模型
llm = ChatOllama(
    model="llama3.1:latest",  # 指定模型版本
    temperature=0.8  # 控制生成随机性(0-1,越高越有创意)
)

# 使用TypedDict定义结构化输出格式(不包含数据验证)
class Joke(TypedDict):
    """Joke to tell user."""
    
    # Annotated语法结构:[类型, 默认值(可选), 描述]
    # setup字段:字符串类型,必须生成,无默认值,带描述
    setup: Annotated[str, ..., "The setup of the joke"]  # 等同于 Field(description="...")
    
    # 以下是替代写法示例(注释掉的代码):
    # setup: str                    # 最简单写法:只有类型,无默认值和描述
    # setup: Annotated[str, ...]    # 等效于上方的简单写法
    # setup: Annotated[str, "foo"]  # 带默认值但无描述的写法(默认值仅用于提示构造)
    
    # punchline字段:字符串类型,必须生成,无默认值,带描述
    punchline: Annotated[str, ..., "The punchline of the joke"]
    # rating字段:可选整型,默认值为None,带描述
    rating: Annotated[Optional[int], None, "How funny the joke is, from 1 to 10"]

# 将模型包装为结构化输出生成器(使用TypedDict模式)
structured_llm = llm.with_structured_output(Joke)

结构化输出如下(result是一个dict类型的变量):

# 调用模型生成结构化内容
# 注意:即使定义默认值(如rating的None),若模型未生成该字段,值仍会缺失
# 需要通过代码处理可能的缺失字段(例如 result.get("rating", None))
result = structured_llm.invoke("Tell me a joke about cats")
print(result)  # 示例输出:{'setup': '...', 'punchline': '...', 'rating': 7}
{'punchline': 'Why did the cat join a band? Because it wanted to be the purr-cussionist!', 'rating': 8, 'setup': 'A cat walks into a music store and says...'}
3. JSON Schema

和通过 TypedDict 定义模式(Schema)的实现效果一致,只不过是通过定义JSON Schema格式的方式而非定义TypedDict 子类来创建结构化输出模板(和让LLM支持function calling的方法大同小异)。

示例代码如下:

from langchain_ollama import ChatOllama  # 导入Ollama聊天模型接口

# 初始化Ollama聊天模型
llm = ChatOllama(
    model="llama3.1:latest",  # 指定使用的模型版本(需与本地/服务器部署的模型一致)
    temperature=0.8            # 控制生成随机性(0-1,越高越有创意)
)

# 定义JSON Schema格式的结构化输出模板
json_schema = {
    "title": "joke",          # 模式标题(用于模型理解生成内容的类型)
    "description": "Joke to tell user.",  # 模式描述(整体任务说明)
    "type": "object",         # 指定输出为JSON对象
    "properties": {           # 定义对象的各个字段
        "setup": {            # 必填字段:笑话的铺垫部分
            "type": "string", # 字段类型为字符串
            "description": "The setup of the joke",
        },
        "punchline": {        # 必填字段:笑话的笑点部分
            "type": "string",
            "description": "The punchline to the joke",
        },
        "rating": {           # 可选字段:笑话评分(1-10)
            "type": "integer",
            "description": "How funny the joke is, from 1 to 10",
            "default": None,  # 默认值为None(若模型未生成则保留空值)
        },
    },
    "required": ["setup", "punchline"],  # 必须包含的字段(rating为可选)
}

# 将模型包装为结构化输出生成器(基于JSON Schema)
structured_llm = llm.with_structured_output(json_schema)

结构化输出如下(result是一个dict类型的变量):

# 调用模型生成结构化内容
# 注意:
# 1. 即使定义了rating的默认值None,若模型未生成该字段,结果中可能不存在此键
# 2. 输出为标准的Python字典,可通过result["setup"]访问字段值
# 3. JSON Schema不执行自动数据验证(与Pydantic不同),需自行处理类型错误
result = structured_llm.invoke("Tell me a joke about cats")
print(result) 
{'punchline': 'Why did the cat join a band? Because it wanted to be the purr-cussionist!', 'rating': 8, 'setup': 'A cat walked into a music store and asked the owner if he could join his band.'}

小结

在本篇博客中,我们学习了如何通过最简单高效的 with_structured_output() 方法调用的方式让LLM具备结构化输出的能力。在具体实现时,有三种可选方式可对输出格式进行规范,分别是基于 TypedDict 类(类型化字典)、JSON Schema(JSON 模式)和 Pydantic 类。当然,除了本篇博客中提到的实现方式,还存在多种其他技术方案可对模型输出进行结构化控制。例如:

  • 小样本示例引导(Few-shot prompting):通过提供输入 - 输出样例指导模型生成格式;
  • 结构化方法指定(Specifying the method for structuring outputs):显式定义 JSON、XML 等数据格式规则;
  • 直接解析模型输出(Direct prompting and parsing):结合自然语言指令与后处理解析逻辑。

关于这些技术的具体实现与应用场景,我们后续博客再见,感兴趣的朋友可以持续关注。


http://www.kler.cn/a/568252.html

相关文章:

  • ORM Bee V2.5.2.x 发布,支持 CQRS; sql 性能分析;更新 MongoDB ORM分片
  • 六十天前端强化训练之第五天响应式设计原理深度解析
  • 0301 leetcode - 1502.判断是否能形成等差数列、 682.棒球比赛、657.机器人能否返回原点
  • java数据结构_Map和Set_9.1
  • 【K8S】Kubernetes 基本架构、节点类型及运行流程详解(附架构图及流程图)
  • CES Asia 2025前瞻:网络安全与数据隐私成焦点
  • 在Linux上安装go环境
  • 【开源免费】基于SpringBoot+Vue.JS网络海鲜市场系统(JAVA毕业设计)
  • 1.2.3 使用Spring Initializr方式构建Spring Boot项目
  • 学习路程十一 langchain核心组件 Memory
  • 万能Prompt模板:三步打造高效Deep Research工作流
  • Python的pdf2image库将PDF文件转换为PNG图片
  • etcd 3.15 三节点集群管理指南
  • MySQL表字段数量上限解析
  • 【自学笔记】Oracle基础知识点总览-持续更新
  • 基于Spring Boot和Vue的餐饮管理系统设计与实现
  • PHP面试题--前端篇
  • Redis Cluster集群详解
  • 边缘渲染架构深度解析:从CDN到边缘计算的性能跃迁
  • STM32编译过程