大模型开发手记(八):LangChain Agent格式化输出

目录

前言

  1. 在构建智能体应用、大模型应用时,一个常见的痛点是如何从模型的自然语言回复中可靠地提取关键信息。比如,你希望模型返回一个包含姓名、邮箱、电话的JSON对象,但它可能回复"好的,这是你要的信息:张三,邮箱是zhangsan@example.com,电话是123456"。为了在程序中使用这些数据,你不得不编写复杂的正则表达式或提示工程来解析。这不仅低效,而且容易出错。
  2. LangChain的智能体(Agent)提供了结构化输出功能,允许我们直接要求模型以预定义的格式(如Pydantic模型、JSON、数据类)返回数据。这样,我们获得的就是一个可以直接使用的Python对象,无需任何解析。

一、什么是结构化输出?

  1. 结构化输出是指让智能体在生成最终回答时(中间过程大模型自由发挥),严格按照你定义的模式(Schema) 返回数据。LangChain的create_agent函数通过response_format参数支持这一功能。

    python 复制代码
    from langchain.agents import create_agent
    from pydantic import BaseModel, Field
    
    class ContactInfo(BaseModel):
        name: str = Field(description="姓名")
        email: str = Field(description="邮箱")
        phone: str = Field(description="电话")
    
    agent = create_agent(
        model="openai:gpt-4",  # 这里用openai:gpt-4示例
        tools=[],               # 工具列表,非必须
        response_format=ContactInfo
    )
    
    result = agent.invoke({
        "messages": [{"role": "user", "content": "提取联系人信息:张三,zhangsan@example.com,13800138000"}]
    })
    
    # 输出直接就是 ContactInfo 对象
    print(result["structured_response"])
    # ContactInfo(name='张三', email='zhangsan@example.com', phone='13800138000')

当response_format被设置后,智能体最终状态中会增加一个structured_response键,其值就是按照模式验证后的数据。

二、response_format的三种配置方式

LangChain为response_format提供了灵活的配置方式,以适应不同的模型能力和需求。

2.1 直接传入模式(自动选择策略)

  1. 最常用的方式:直接将一个模式类(Pydantic模型、数据类等)传给response_format。LangChain会根据所选模型自动判断使用提供商原生策略 还是工具调用策略

  2. 提供商原生策略,说白了就是模型厂商Api端天然支持格式化输出的功能,仅支持 OpenAI、Grok 这类自带该功能的模型

  3. 工具调用策略是 LangChain 的通用方案,适配绝大多数无原生结构化输出的模型,本质上langchain会自动生成一个格式化输出的工具,并把该工具信息和要求最终结果要调用这个工具等信息封装进系统提示词,最后智能体的最终输出来调用这个工具进行格式化。

  4. 本人亲测,阿里云百联的 的服务器api端,这两种方式都不支持,不支持工具调用策略原因是,langchain自动生成的http入参中,tool_choice这个字段是个object,而阿里api端这个字段只支持auto和none,说白了就是langchain对阿里的兼容不是很好

    python 复制代码
    # 使用 Pydantic 模型
    from pydantic import BaseModel
    
    class WeatherRequest(BaseModel):
        city: str
        date: str
    
    agent = create_agent(
        model="openai:gpt-4",
        response_format=WeatherRequest
    )

2.2 提供商策略(ProviderStrategy)

  1. 某些模型(如OpenAI、Grok)通过API原生支持结构化输出。这种模式下,模型直接返回符合JSON Schema的数据,可靠性最高。你可以显式使用ProviderStrategy来启用,但通常直接传模式类时,如果模型支持,LangChain会自动采用此策略。

    python 复制代码
    from langchain.agents.structured_output import ProviderStrategy
    from pydantic import BaseModel
    
    class MovieReview(BaseModel):
        title: str
        rating: int  # 1-10
        summary: str
    
    agent = create_agent(
        model="openai:gpt-4",
        response_format=ProviderStrategy(schema=MovieReview)
    )

2.3 工具调用策略(ToolStrategy)

  1. 对于所有支持工具调用的模型,LangChain可以利用工具调用来模拟结构化输出。它会创建一个特殊的工具,让模型以工具参数的形式返回结构化数据。这是通用的后备方案。

    python 复制代码
    from langchain.agents.structured_output import ToolStrategy
    from pydantic import BaseModel
    from typing import Literal
    
    class OrderInfo(BaseModel):
        order_id: str
        status: Literal["pending", "shipped", "delivered"]
        items: list[str]
    
    agent = create_agent(
        model="openai:gpt-3.5-turbo",  # 也支持工具调用
        response_format=ToolStrategy(schema=OrderInfo)
    )

三、定义模式的四种常用方法

无论使用哪种策略,你都需要定义一个模式来描述期望的输出结构。LangChain支持多种方式,你可以根据偏好和需求选择。

3.1 Pydantic模型(推荐)

Pydantic模型提供了强大的数据验证功能,支持字段描述、类型约束、默认值等,是Python生态中处理结构化数据的首选。

python 复制代码
from pydantic import BaseModel, Field
from typing import Optional

class Product(BaseModel):
    name: str = Field(description="产品名称")
    price: float = Field(description="价格", gt=0)
    in_stock: bool = Field(description="是否有库存")
    tags: Optional[list[str]] = Field(None, description="标签列表")

3.2 数据类(Dataclass)

如果你喜欢Python原生的数据类,也可以直接使用,注意:数据类不支持字段描述,但可以通过注释或文档字符串补充。

python 复制代码
from dataclasses import dataclass
from typing import Optional

@dataclass
class Product:
    name: str
    price: float
    in_stock: bool
    tags: Optional[list[str]] = None

3.3 TypedDict

如果你只需要类型提示而不需要运行时验证,可以使用TypedDict。

python 复制代码
from typing_extensions import TypedDict
from typing import Optional

class Product(TypedDict):
    name: str
    price: float
    in_stock: bool
    tags: Optional[list[str]]

3.4 JSON Schema

如果你希望以纯字典的形式定义模式,可以直接提供JSON Schema字典。这在需要动态生成模式或与外部系统交互时很有用。

python 复制代码
product_schema = {
    "type": "object",
    "properties": {
        "name": {"type": "string"},
        "price": {"type": "number", "minimum": 0},
        "in_stock": {"type": "boolean"},
        "tags": {"type": "array", "items": {"type": "string"}}
    },
    "required": ["name", "price", "in_stock"]
}

agent = create_agent(
    model="openai:gpt-4",
    response_format=product_schema  # 直接传入字典
)

四、工具调用策略的高级功能

当使用ToolStrategy时,你可以获得两个额外的控制能力:自定义工具消息和错误处理。

4.1 自定义工具消息内容(tool_message_content)

在工具调用过程中,Agent会在对话历史中添加一条工具消息(ToolMessage)。默认情况下,这条消息会显示返回的结构化数据。你可以通过tool_message_content自定义这条消息的内容,让对话更自然。

python 复制代码
from langchain.agents.structured_output import ToolStrategy
from pydantic import BaseModel

class Task(BaseModel):
    description: str
    assignee: str
    due_date: str

agent = create_agent(
    model="openai:gpt-4",
    tools=[],
    response_format=ToolStrategy(
        schema=Task,
        tool_message_content="任务已创建并分配给相关人员。"
    )
)

result = agent.invoke({
    "messages": [{"role": "user", "content": "安排张三在周五前完成市场调研报告"}]
})
# 最终的工具消息将显示 "任务已创建并分配给相关人员。" 而不是原始数据

4.2 智能错误处理(handle_errors)

  1. 模型在调用工具、生成结构化输出时可能会犯错。ToolStrategy内置了重试机制,可以捕获错误并通过工具消息反馈给模型,提示其修正。我们还可以通过handle_errors参数自定义错误处理行为。

  2. 默认行为:handle_errors=True,捕获所有异常并使用默认错误模板提示模型重试。

    python 复制代码
    response_format = ToolStrategy(schema=Task)  # 默认启用错误处理
  3. 自定义错误消息:将handle_errors设为字符串,当发生任何错误时,模型将收到该字符串作为工具消息。

    python 复制代码
    response_format = ToolStrategy(
        schema=Task,
        handle_errors="格式不正确,请按照要求的JSON格式重新生成。"
    )
  4. 仅处理特定异常:可以指定只对某些异常类型进行重试,其他异常则抛出。

    python 复制代码
    from pydantic import ValidationError
    
    response_format = ToolStrategy(
        schema=Task,
        handle_errors=ValidationError  # 只在验证错误时重试
    )

五、联合类型:让模型选择输出模式

  1. ToolStrategy支持使用Union类型来定义多个格式化模板类,模型从多个可能的输出模式中选择一个

    python 复制代码
    from typing import Union
    from pydantic import BaseModel
    
    class ContactInfo(BaseModel):
        name: str
        email: str
    
    class EventDetails(BaseModel):
        event_name: str
        date: str
    
    agent = create_agent(
        model="openai:gpt-4",
        response_format=ToolStrategy(schema=Union[ContactInfo, EventDetails])
    )
    
    result = agent.invoke({
        "messages": [{"role": "user", "content": "记住:下周五下午3点开产品评审会"}]
    })
    # 输出可能是 EventDetails 对象
    print(result["structured_response"])
    # EventDetails(event_name='产品评审会', date='下周五下午3点')
相关推荐
qq_5470261792 小时前
LangChain 会话记忆(Conversation Memory)
langchain
Vital4 小时前
AI Agent(写一个简易的MCP天气查询工具)
langchain·ai编程·cursor
逸尘谈PM6 小时前
智能体框架对比:OpenClaw、LangChain、AutoGPT、CrewAI 深度对比
人工智能·ai·langchain·职场·2026年
前进的李工8 小时前
LangChain使用之Model IO(提示词模版之ChatPromptTemplate)
java·前端·人工智能·python·langchain·大模型
张张123y10 小时前
知识图谱从0到1:AI应用开发的核心技术
人工智能·langchain·transformer·知识图谱
勇往直前plus12 小时前
大模型开发手记(九):LangChain Agent 中间件-提升Agent的可靠性与可控性
中间件·langchain
java1234_小锋12 小时前
基于LangChain的RAG与Agent智能体开发 - 使用LangChain调用大模型设置流式输出
langchain·rag
诗酒当趁年华12 小时前
langchain核心组件5-短期记忆
langchain
啊巴矲12 小时前
白从零开始勇闯人工智能:LangChain中的检索增强生成(RAG)
langchain