day14-LangChain结构化输出和流式输出

今日内容

---------------重点核心部分---------------
# 1 LangChain核心组件之 Agents智能体
# 2 LangChain核心组件之 Models模型
# 3 LangChain核心组件之 Messages消息
# 4 LangChain核心组件之 Tools工具
# 5 LangChain核心组件之 Short-term memory短期记忆
# 6 LangChain核心组件之 Streaming流式传输
# 7 LangChain核心组件之Structured output 结构化输出
---------------高级应用部分---------------
# 8 LangChain高级之Middleware 中间件
# 9 LangChain高级之Guardrails 护栏
# 10 LangChain高级之runtime运行时
# 11 LangChain高级之Context engineering上下文工程
# 12 LangChain高级之Model Context Protocol (MCP)模型上下文协议
# 13 LangChain高级之 Human-in-the-loop 人机交互
# 14 LangChain高级之 Multi-agent 多智能体
# 15 LangChain高级之 Retrieval 检索
# 16 LangChain高级之 Long-term memory 长期记忆
--------------使用LangSmith-需要付费---------------------
# 17 LangSmith Studio
# 18 Test 测试
# 19 Agent 聊天用户界面
# 20 Deployment  部署
# 21 Observability 可观测性

1 LangChain 核心组件之流式传输(Streaming)

# 1 定义
##  LangChain 实现了一个流式传输系统来提供实时更新。
## 流式传输对于增强基于 LLM(大型语言模型)构建的应用程序的响应能力至关重要。通过渐进式地显示输出,即使在完整的响应准备好之前,流式传输也能显著改善用户体验,尤其是在处理 LLM 的延迟时。

# 2 概述 (Overview)
## LangChain 的流式传输系统允许您将代理运行的实时反馈展示给您的应用程序。

## LangChain 流式传输可能实现的功能:
    流式传输代理进度 — 在每个代理步骤后获取状态更新。
    流式传输 LLM 令牌 — 在语言模型生成令牌时进行流式传输。
    流式传输自定义更新 — 发出用户定义的信号(例如,"Fetched 10/100 records")。
    流式传输多种模式 — 可选择 updates(代理进度)、messages(LLM 令牌 + 元数据)或 custom(任意用户数据)。 
    
# 3 代理进度 (Agent progress)
要流式传输代理进度,请使用 stream_mode="updates" 的 stream 或 astream 方法。这会在每个代理步骤后发出一个事件。

例如,如果您有一个调用工具一次的代理,您应该会看到以下更新:

LLM 节点: 带有工具调用请求的 AIMessage
工具节点: 带有执行结果的 ToolMessage
LLM 节点: 最终的 AI 响应
    
agent---》问问题---》大模型解决不了--》调用工具--》返回给大模型--》输出给我们
# 回顾一下

models 调用工具麻烦--》bind工具--》自己执行tool--》执行的结果,手动给 models--》大模型返回
agent  调用工具非常简单--》@tool装饰去---》创建agent时---》传入可用工具【会自动读取工具的doc文档】--》LangChain会自己调用工具--》并返回给大模型---》最终输出



# 4  LLM 令牌 (LLM tokens):要在 LLM 生成令牌时流式传输它们,请使用 stream_mode="messages"。您可以在下面看到agent流式传输工具调用和最终响应的输出


# 5 自定义更新 (Custom updates):要流式传输工具在执行过程中发出的更新,您可以使用 get_stream_writer-->langgraph中的工具

# 6 自定义更新多种模式
	-stream_mode=【messages,updates】
    
# 7  禁用流式传输 (Disable streaming):在某些应用程序中,您可能需要为给定的模型禁用单个令牌的流式传输。这对于像多代理系统这样的场景很有用,以便控制哪些代理流式传输它们的输出

1.1 Agent进度

要流式传输代理进度,请使用 `stream_mode="updates"` 的 [`stream`]方法。这会在**每个代理步骤**后发出一个事件
from typing import Literal
from typing_extensions import TypedDict
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
from langchain.tools import tool
from langgraph.config import get_stream_writer

# 1 连接model大模型
model = ChatOpenAI(
    model="qwen-plus",
    api_key="sk-6459007b813946289da12857950c955b",
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)

# 2 编写一个工具
@tool
def get_weather(city: str) -> str:
    """获取给定城市的天气。"""
    return f"It's always sunny in {city}!"

# 3 创建agent
agent = create_agent(
    model=model,
    tools=[get_weather], # 有一个工具供model使用
)

# 4 流式传输
for chunk in agent.stream(
    {"messages": [{"role": "user", "content": "上海的天气怎么样?"}]},
    stream_mode="updates"
):
    for step, data in chunk.items():
        print(f"step: {step}")
        print(f"content: {data['messages'][-1].content_blocks}")

step: model
content: [{'type': 'tool_call', 'name': 'get_weather', 'args': {'city': '上海'}, 'id': 'call_c389f4a1c2a04ff697d8e4'}]
step: tools
content: [{'type': 'text', 'text': "It's always sunny in 上海!"}]
step: model
content: [{'type': 'text', 'text': '上海的天气总是晴朗的!'}]

1.2 LLM令牌token

# 要在 LLM 生成token时流式传输它们,请使用 `stream_mode="messages"`。您可以在下面看到代理流式传输工具调用和最终响应的输出
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
from langchain.tools import tool

# 1 连接model大模型
model = ChatOpenAI(
    model="qwen-plus",
    api_key="sk-6459007b813946289da12857950c955b",
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)

# 2 编写一个工具
@tool
def get_weather(city: str) -> str:
    """获取给定城市的天气。"""
    return f"It's always sunny in {city}!"

# 3 创建agent
agent = create_agent(
    model=model,
    tools=[get_weather], # 有一个工具供model使用
)

# 4 流式传输
for  token, metadata in agent.stream(
    {"messages": [{"role": "user", "content": "上海的天气怎么样?"}]},
    stream_mode="messages"
):
    print(f"node: {metadata['langgraph_node']}")
    print(f"content: {token.content_blocks}")
    print("\n")



node: model
content: [{'type': 'tool_call_chunk', 'id': 'call_46da6ef34fb24f62a04d66', 'name': 'get_weather', 'args': '', 'index': 0}]


node: model
content: [{'type': 'tool_call_chunk', 'id': 'call_46da6ef34fb24f62a04d66', 'name': '', 'args': '{"city": "', 'index': 0}]


node: model
content: [{'type': 'tool_call_chunk', 'id': '', 'name': None, 'args': '上海"}', 'index': 0}]


node: model
content: []


node: model
content: []


node: tools
content: [{'type': 'text', 'text': "It's always sunny in 上海!"}]


node: model
content: []


node: model
content: [{'type': 'text', 'text': '上海'}]


node: model
content: [{'type': 'text', 'text': '的天气总是'}]


node: model
content: [{'type': 'text', 'text': '晴'}]


node: model
content: [{'type': 'text', 'text': '朗的'}]


node: model
content: [{'type': 'text', 'text': '!'}]


node: model
content: []


node: model
content: []

1.3 自定义更新

# 要流式传输工具在执行过程中发出的更新,可以使用 [`get_stream_writer`]
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
from langchain.tools import tool
#  pip install langgraph
from langgraph.config import get_stream_writer
# 1 连接model大模型
model = ChatOpenAI(
    model="qwen-plus",
    api_key="sk-6459007b813946289da12857950c955b",
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)

# 2 编写一个工具
@tool
def get_weather(city: str) -> str:
    """获取给定城市的天气。"""
    #  使用 langgraph的get_stream_writer
    writer = get_stream_writer()
    # 流式传输任何任意数据
    writer(f"Looking up data for city: {city}")

    writer(f"Acquired data for city: {city}")
    return f"It's always sunny in {city}!"

# 3 创建agent
agent = create_agent(
    model=model,
    tools=[get_weather], # 有一个工具供model使用
)

# 4 流式传输
for chunk in agent.stream(
    {"messages": [{"role": "user", "content": "上海天气怎么样?"}]},
    stream_mode="custom"
):
    print(chunk)

Looking up data for city: 上海
Acquired data for city: 上海

1.4 自定义更新多种模式[多种模式组合]

可以通过将流模式作为列表传递来指定多种流式传输模式:`stream_mode=["updates", "custom"]`
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
from langchain.tools import tool
#  pip install langgraph
from langgraph.config import get_stream_writer
# 1 连接model大模型
model = ChatOpenAI(
    model="qwen-plus",
    api_key="sk-6459007b813946289da12857950c955b",
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)

# 2 编写一个工具
@tool
def get_weather(city: str) -> str:
    """获取给定城市的天气。"""
    #  使用 langgraph的get_stream_writer
    writer = get_stream_writer()
    # 流式传输任何任意数据
    writer(f"Looking up data for city: {city}")
    writer(f"Acquired data for city: {city}")
    return f"It's always sunny in {city}!"

# 3 创建agent
agent = create_agent(
    model=model,
    tools=[get_weather], # 有一个工具供model使用
)

# 4 流式传输
for stream_mode, chunk in agent.stream(
    {"messages": [{"role": "user", "content": "上海天气怎么样?"}]},
    stream_mode=["updates", "custom"]
):
    print(f"stream_mode: {stream_mode}")
    print(f"content: {chunk}")
    print("\n")





stream_mode: updates
content: {'model': {'messages': [AIMessage(content='', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 19, 'prompt_tokens': 150, 'total_tokens': 169, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_provider': 'openai', 'model_name': 'qwen-plus', 'system_fingerprint': None, 'id': 'chatcmpl-9331be53-ba66-9f67-a4cc-7c9676dc714d', 'finish_reason': 'tool_calls', 'logprobs': None}, id='lc_run--019cb8dd-2714-7f91-80e6-8dccf38b4071-0', tool_calls=[{'name': 'get_weather', 'args': {'city': '上海'}, 'id': 'call_00677a024ca540c5a30a54', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 150, 'output_tokens': 19, 'total_tokens': 169, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})]}}


stream_mode: custom
content: Looking up data for city: 上海


stream_mode: custom
content: Acquired data for city: 上海


stream_mode: updates
content: {'tools': {'messages': [ToolMessage(content="It's always sunny in 上海!", name='get_weather', id='d522c05f-b896-4fd1-a6ef-a1bb945919eb', tool_call_id='call_00677a024ca540c5a30a54')]}}


stream_mode: updates
content: {'model': {'messages': [AIMessage(content='上海的天气总是晴朗的!', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 8, 'prompt_tokens': 190, 'total_tokens': 198, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_provider': 'openai', 'model_name': 'qwen-plus', 'system_fingerprint': None, 'id': 'chatcmpl-6800de1c-ef4d-98d0-8a1b-2d5da68163bd', 'finish_reason': 'stop', 'logprobs': None}, id='lc_run--019cb8dd-2bd1-75a0-ab5d-e8e5fa87fd49-0', tool_calls=[], invalid_tool_calls=[], usage_metadata={'input_tokens': 190, 'output_tokens': 8, 'total_tokens': 198, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})]}}


1.5 禁用流式传输

# 1 多Model使用
agent.stream() 都会流式传输
# 创建DeepSeek的model时--》streaming=False
# 创建千问的model时--》不指定

# 某种条件下到了 DeepSeek 回答的时候,就没有流式传输,即便 agent.steam()



from langchain_openai import ChatOpenAI
model = ChatOpenAI(
    model=model,
    streaming=False
)

2 LangChain核心组件之结构化输出

# 1 概念:之前学过--》规定大模型给我们返回的数据格式符合我们的要求
    # 1 Pydantic 方式 :返回对象形式
    # 2 TypedDict方式:字典形式
    # 3 JSON Schema :字典形式--我们可以自己转json格式
    
# 2 介绍 盗梦空间--》非常多数据--》我们只要我们关注的:Movie对象形式,字典形式{}
	-名字
    -导演
    -评分
	
    
    
# 3 今天研究:Pydantic 返回对象的详细形式


# 4 结构化输出允许 智能体(agents) 以特定的、可预测的格式返回数据。这样,您无需解析自然语言响应,即可获得 JSON 对象、Pydantic 模型 或 数据类(dataclasses) 形式的结构化数据,供您的应用程序直接使用。

# 5 LangChain 的 create_agent 会自动处理结构化输出。用户设置所需的结构化输出 模式(schema),当模型生成结构化数据时,它会被捕获、验证,并作为智能体状态中 'structured_response' 键的值返回
def create_agent(
    model=model,
    ....
    response_format: Union[
        ToolStrategy[StructuredResponseT],
        ProviderStrategy[StructuredResponseT],
        type[StructuredResponseT],
        None,
    ]

2.1 响应格式response_format

# 1 response_format 参数有哪些
    -ToolStrategy[StructuredResponseT]: 使用 工具调用(tool calling) 实现结构化输出。
    -ProviderStrategy[StructuredResponseT]: 使用 提供商原生(provider-native) 的结构化输出功能。
    -type[StructuredResponseT]: 模式类型(Schema type) - 会根据模型功能自动选择最佳策略。
    -None: 不进行结构化输出。
        
        
# 2 当直接提供模式类型时,LangChain 会自动选择:
    -ProviderStrategy: 适用于支持原生结构化输出的模型(例如 OpenAI、Grok)。
    -ToolStrategy: 适用于所有其他模型。
        
        
# 3 结构化响应将在智能体最终状态的 structured_response 键中返回

image-20260304210105561

image-20260304210233601

### 响应格式-之ToolStrategy
from pydantic import BaseModel
from langchain.agents import create_agent
from langchain.agents.structured_output import ProviderStrategy,ToolStrategy
from langchain_openai import ChatOpenAI
# 1 连接model
model = ChatOpenAI(
    model="deepseek-chat",
    api_key="sk-f360d39d84354ac58a15ddbc9eb81023",
    base_url="https://api.deepseek.com",
)
# 2 创建一个类,继承 BaseModel --》使用pydantic 格式
class ContactInfo(BaseModel):
    name: str
    email: str
    QQ: str
# 3 创建agent
agent = create_agent(
    model=model,
    # 会比大模型直接支持,耗费资源--》因为内部调用了工具
    response_format=ToolStrategy(ContactInfo)  #  大模型不支持我们要求的格式--》使用ToolStrategy 意思是,langchain内置了一个tool,能够把大模型返回的字符串,处理成我们要的格式

)
# 4 agent交互
result = agent.invoke({
    "messages": [{"role": "user", "content": "从以下内容提取联系信息: LiuQingzheng, liuqingzheng2020@163.com.com, 306334678"}]
})

print(type(result["structured_response"]))


### 响应格式-之ProviderStrategy
from pydantic import BaseModel
from langchain.agents import create_agent
from langchain.agents.structured_output import ProviderStrategy,ToolStrategy
from langchain_openai import ChatOpenAI
# 1 连接model
##### 报错:this response_format type is unavailable now---》DeepSeek大模型不支持格式化输出
# model = ChatOpenAI(
#     model="deepseek-chat",
#     api_key="sk-f360d39d84354ac58a15ddbc9eb81023",
#     base_url="https://api.deepseek.com",
# )
### 千问支持--》不报错
model = ChatOpenAI(
    model="qwen-plus",
    api_key="sk-6459007b813946289da12857950c955b",
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)

# 2 创建一个类,继承 BaseModel --》使用pydantic 格式
class ContactInfo(BaseModel):
    name: str
    email: str
    QQ: str
# 3 创建agent
agent = create_agent(
    model=model,
    # 大模型支持直接返回这种格式--》直接就返回
    # 大模型不支持返回格式---》报错
    response_format=ProviderStrategy(ContactInfo)

)
# 4 agent交互
result = agent.invoke({
    "messages": [{"role": "user", "content": "从以下内容提取联系信息: LiuQingzheng, liuqingzheng2020@163.com.com, 306334678"}]
})

print(type(result["structured_response"]))


2.2 模型提供商策略

# 1 一些模型提供商通过其 API 原生支持结构化输出(目前仅限 OpenAI)。在可用时,这是最可靠的方法。

# 2 要使用此策略,请配置 ProviderStrategy:
    class ProviderStrategy(Generic[SchemaT]):
        schema: type[SchemaT]
            
            
# 3 schema (必需) 定义结构化输出格式的模式。
    # 支持:
        Pydantic 模型: 带有字段验证的 BaseModel 子类。
        数据类 (Dataclasses): 带有类型注解的 Python 数据类。
        TypedDict: 类型化字典类。
        JSON Schema: 带有 JSON 模式规范的字典。
            
# 4 当您将模式类型直接传递给 create_agent.response_format 并且模型支持原生结构化输出时,LangChain 会自动使用 ProviderStrategy:


# 1 模型提供商策略之Pydantic
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
from pydantic import BaseModel, Field
model = ChatOpenAI(
    model="qwen-plus",
    api_key="sk-6459007b813946289da12857950c955b",
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)


class ContactInfo(BaseModel):
    """一个人的联系信息。"""
    name: str = Field(description="该人的姓名")
    email: str = Field(description="该人的电子邮件地址")
    QQ: str = Field(description="该人的QQ号码")

agent = create_agent(
    model=model,
    response_format=ContactInfo  # 自动选择 ProviderStrategy
)

result = agent.invoke({
    "messages": [{"role": "user", "content": "从以下内容提取联系信息: LiuQingzheng, liuqingzheng2020@163.com.com, 306334678"}]
})

print(result["structured_response"])


# 2 模型提供商策略之Dataclass
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
from dataclasses import dataclass # python 原生的,不需要额外安装

model = ChatOpenAI(
    model="qwen-plus",
    api_key="sk-6459007b813946289da12857950c955b",
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)


@dataclass
class ContactInfo:
    """一个人的联系信息。"""
    name: str # 该人的姓名
    email: str # 该人的电子邮件地址
    QQ: str # 该人的QQ号码

agent = create_agent(
    model=model,
    response_format=ContactInfo  # 自动选择 ProviderStrategy
)

result = agent.invoke({
    "messages": [{"role": "user", "content": "从以下内容提取联系信息: LiuQingzheng, liuqingzheng2020@163.com.com, 306334678"}]
})

print(result["structured_response"])


# 3 模型提供商策略之TypedDict
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
from typing_extensions import TypedDict
model = ChatOpenAI(
    model="qwen-plus",
    api_key="sk-6459007b813946289da12857950c955b",
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)


class ContactInfo(TypedDict):
    """一个人的联系信息。"""
    name: str # 该人的姓名
    email: str # 该人的电子邮件地址
    QQ: str # 该人的QQ号码

agent = create_agent(
    model=model,
    response_format=ContactInfo  # 自动选择 ProviderStrategy
)

result = agent.invoke({
    "messages": [{"role": "user", "content": "从以下内容提取联系信息: LiuQingzheng, liuqingzheng2020@163.com.com, 306334678"}]
})

print(type(result["structured_response"]))
print(result["structured_response"].get('name'))



# 4 模型提供商策略之JSON Schema
#### JSON Schema 示例
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
from typing_extensions import TypedDict
model = ChatOpenAI(
    model="qwen-plus",
    api_key="sk-6459007b813946289da12857950c955b",
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)


contact_info_schema = {
    "type": "object",
    "description": "一个人的联系信息。",
    "properties": {
        "name": {"type": "string", "description": "该人的姓名"},
        "email": {"type": "string", "description": "该人的电子邮件地址"},
        "QQ": {"type": "string", "description": "该人的QQ号码"}
    },
    "required": ["name", "email", "phone"]
}

agent = create_agent(
    model=model,
    response_format=contact_info_schema  # 自动选择 ProviderStrategy
)

result = agent.invoke({
    "messages": [{"role": "user", "content": "从以下内容提取联系信息: LiuQingzheng, liuqingzheng2020@163.com.com, 306334678"}]
})

print(result["structured_response"]['QQ'])

### 注意:
如果提供商对您选择的模型原生支持结构化输出,那么编写 response_format=ProductReview 和 response_format=ToolStrategy(ProductReview) 在功能上是等效的。无论哪种情况,如果不支持结构化输出,智能体都将退回到工具调用策略。

2.3 工具调用策略

2.3.1 基本使用

# 1 对于不支持原生结构化输出的模型,LangChain 使用 工具调用(tool calling) 来达到相同的效果。这适用于所有支持工具调用的模型,即大多数现代模型。

# 2 要使用此策略,请配置 ToolStrategy
class ToolStrategy(Generic[SchemaT]):
    schema: type[SchemaT]
    tool_message_content: str | None
    handle_errors: Union[
        bool,
        str,
        type[Exception],
        tuple[type[Exception], ...],
        Callable[[Exception], str],
    ]
    
# 3 schema (必需) 定义结构化输出格式的模式。
     支持:
        Pydantic 模型: 带有字段验证的 BaseModel 子类。
        数据类 (Dataclasses): 带有类型注解的 Python 数据类。
        TypedDict: 类型化字典类。
        JSON Schema: 带有 JSON 模式规范的字典。
        联合类型 (Union types): 多个模式选项。模型将根据上下文选择最合适的模式。
            
# 4 tool_message_content (可选)
    生成结构化输出时,返回的工具消息的自定义内容。
    如果未提供,默认为显示结构化响应数据的消息。
    
# 5 handle_errors (可选) 结构化输出验证失败的错误处理策略。默认为 True。
    True: 捕获所有错误并使用默认错误模板。
    str: 捕获所有错误并使用此自定义消息。
    type[Exception]: 仅捕获此异常类型并使用默认消息。
    tuple[type[Exception], ...]: 仅捕获这些异常类型并使用默认消息。
    Callable[[Exception], str]: 返回错误消息的自定义函数。
    False: 不重试,让异常传播。
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
from typing import Literal
from typing_extensions import TypedDict
from langchain.agents.structured_output import ToolStrategy
from pydantic import BaseModel, Field
model = ChatOpenAI(
    model="qwen-plus",
    api_key="sk-6459007b813946289da12857950c955b",
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)

# Pydantic Model 示例:
# 为简洁起见,省略了 Dataclass、TypedDict、JSON Schema 和 Union Types 的 ToolStrategy 示例,它们的结构与 Provider Strategy 类似
class ProductReview(BaseModel):
    """对产品评论的分析。"""
    rating: int | None = Field(description="产品的评分", ge=1, le=5)
    sentiment: Literal["positive", "negative"] = Field(description="评论的情感倾向")  # 枚举:只能从 某几个中选
    key_points: list[str] = Field(description="评论的要点。小写,每条 1-3 个词。")

agent = create_agent(
    model=model,
    response_format=ToolStrategy(ProductReview)
)

result = agent.invoke({
    "messages": [{"role": "user", "content": "Analyze this review: 'Great product: 5 out of 5 stars. Fast shipping, but expensive'"}]
})

print(result["structured_response"])

2.3.2 自定义工具消息内容

# tool_message_content 参数允许您自定义生成结构化输出,供给工具调用

# 1 tool_message_content 参数允许您自定义生成结构化输出,供给工具调用
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
from typing import Literal
from typing_extensions import TypedDict
from langchain.tools import tool
from langchain.agents.structured_output import ToolStrategy
from pydantic import BaseModel, Field
model = ChatOpenAI(
    model="qwen-plus",
    api_key="sk-6459007b813946289da12857950c955b",
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)

class MeetingAction(BaseModel):
    """从会议记录中提取的行动事项。"""
    task: str = Field(description="需要完成的具体任务")
    assignee: str = Field(description="负责该任务的人员")
    priority: Literal["low", "medium", "high"] = Field(description="优先级")
        
@tool
def save_meeting(name: str,action:str):
    """把会议行动人和行动内容记录."""
    print(name)
    print(action)

agent = create_agent(
    model=model,
    tools=[save_meeting],
    response_format=ToolStrategy(
        schema=MeetingAction,
        tool_message_content="行动事项已捕获并添加到会议记录中!"
    )
)

result = agent.invoke({
    "messages": [{"role": "user", "content": "From our meeting: Sarah needs to update the project timeline as soon as possible"}]
})
print(result["structured_response"])

'''
不加:tool_message_content
返回:task='Update the project timeline' assignee='Sarah' priority='high'

加:tool_message_content="行动事项已捕获并添加到会议记录中!"
返回:
Sarah
update the project timeline
'''
'''

2.3.3 错误处理

# 模型在通过工具调用生成结构化输出时可能会出错。LangChain 提供了智能的重试机制来自动处理这些错误
	-多个结构化输出错误 (Multiple structured outputs error):当模型错误地调用了多个结构化输出工具时,智能体会通过 ToolMessage 提供错误反馈,并提示模型重试
	-模式验证错误 (Schema validation error):当结构化输出与预期模式不匹配时,智能体会提供具体的错误反馈
    -错误处理策略 (Error handling strategies):可以使用 handle_errors 参数来自定义错误处理方式:
# 1 多个结构化输出错误 (Multiple structured outputs error):当模型错误地调用了多个结构化输出工具时,
# 智能体会通过 ToolMessage 提供错误反馈,并提示模型重试
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
from langchain.agents.structured_output import ToolStrategy
from pydantic import BaseModel, Field
from typing import Union
model = ChatOpenAI(
    model="qwen-plus",
    api_key="sk-6459007b813946289da12857950c955b",
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)
# model = ChatOpenAI(
#     model="deepseek-chat",
#     api_key="sk-eb2163f3e26d4a008d1f739bc018c568",
#     base_url="https://api.deepseek.com",
# )

class ContactInfo(BaseModel):
    name: str = Field(description="Person's name")
    email: str = Field(description="Email address")

class EventDetails(BaseModel):
    event_name: str = Field(description="Name of the event")
    date: str = Field(description="Event date")

agent = create_agent(
    model=model,
    response_format=ToolStrategy(Union[ContactInfo, EventDetails])  # Default: handle_errors=True
)

result=agent.invoke({
    "messages": [{"role": "user", "content": "Extract info: John Doe (john@email.com) is organizing Tech Conference on March 15th"}]
})
print(result["structured_response"])

'''
================================ Human Message =================================

Extract info: John Doe (john@email.com) is organizing Tech Conference on March 15th
None
================================== Ai Message ==================================
Tool Calls:
  ContactInfo (call_1)
 Call ID: call_1
  Args:
    name: John Doe
    email: john@email.com
  EventDetails (call_2)
 Call ID: call_2
  Args:
    event_name: Tech Conference
    date: March 15th
================================= Tool Message =================================
Name: ContactInfo

Error: Model incorrectly returned multiple structured responses (ContactInfo, EventDetails) when only one is expected.
 Please fix your mistakes.
================================= Tool Message =================================
Name: EventDetails

Error: Model incorrectly returned multiple structured responses (ContactInfo, EventDetails) when only one is expected.
 Please fix your mistakes.
================================== Ai Message ==================================
Tool Calls:
  ContactInfo (call_3)
 Call ID: call_3
  Args:
    name: John Doe
    email: john@email.com
================================= Tool Message =================================
Name: ContactInfo

Returning structured response: {'name': 'John Doe', 'email': 'john@email.com'}

'''
# 2 模式验证错误 (Schema validation error):当结构化输出与预期模式不匹配时,智能体会提供具体的错误反馈
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
from typing import Literal
from typing_extensions import TypedDict
from langchain.tools import tool
from langchain.agents.structured_output import ToolStrategy
from pydantic import BaseModel, Field
from typing import Union
# model = ChatOpenAI(
#     model="qwen-plus",
#     api_key="sk-6459007b813946289da12857950c955b",
#     base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
# )
model = ChatOpenAI(
    model="deepseek-chat",
    api_key="sk-eb2163f3e26d4a008d1f739bc018c568",
    base_url="https://api.deepseek.com",
)

class ProductRating(BaseModel):
    rating: int | None = Field(description="Rating from 1-5", ge=1, le=5)
    comment: str = Field(description="Review comment")

agent = create_agent(
    model=model,
    response_format=ToolStrategy(ProductRating),  # Default: handle_errors=True
    system_prompt="You are a helpful assistant that parses product reviews. Do not make any field or value up."
)

result=agent.invoke({
    "messages": [{"role": "user", "content": "Parse this: Amazing product, 10/10!"}]
})
print(result["structured_response"])

'''
================================ Human Message =================================

Parse this: Amazing product, 10/10!
================================== Ai Message ==================================
Tool Calls:
  ProductRating (call_1)
 Call ID: call_1
  Args:
    rating: 10
    comment: Amazing product
================================= Tool Message =================================
Name: ProductRating

Error: Failed to parse structured output for tool 'ProductRating': 1 validation error for ProductRating.rating
  Input should be less than or equal to 5 [type=less_than_equal, input_value=10, input_type=int].
 Please fix your mistakes.
================================== Ai Message ==================================
Tool Calls:
  ProductRating (call_2)
 Call ID: call_2
  Args:
    rating: 5
    comment: Amazing product
================================= Tool Message =================================
Name: ProductRating

Returning structured response: {'rating': 5, 'comment': 'Amazing product'}
'''
# 3 错误处理策略 (Error handling strategies):可以使用 handle_errors 参数来自定义错误处理方式:
# ZeroDivisionError
# ZeroDivisionError,ValueError ,TypeError# python 内置的错误
class F(TypeError):
    pass
'''
1 自定义错误消息:
ToolStrategy(
    schema=ProductRating,
    handle_errors="请提供 1-5 之间的有效评分并包含评论。"
)
(如果 handle_errors 是一个字符串,智能体将始终使用固定的工具消息提示模型重试。)



2 仅处理特定异常:
ToolStrategy(
    schema=ProductRating,
    handle_errors=ValueError  # 仅在 ValueError 时重试,否则抛出
)


3 处理多个异常类型:
ToolStrategy(
    schema=ProductRating,
    handle_errors=(ValueError, TypeError)  # 在 ValueError 和 TypeError 时重试
)


4 自定义错误处理函数:如果出了错误,都会执行这个函数--》在函数中,我们写自己的逻辑
def custom_error_handler(error: Exception) -> str:
    if isinstance(error, StructuredOutputValidationError):
        return "格式存在问题。请重试。"
    elif isinstance(error, MultipleStructuredOutputsError):
        return "返回了多个结构化输出。请选择最相关的一个。"
    elif isinstance(error, ValueError):
        return "类型错误"
    else:
        return f"错误: {str(error)}"

ToolStrategy(
    schema=ToolStrategy(Union[ContactInfo, EventDetails]),
    handle_errors=custom_error_handler
)


5 不进行错误处理:
response_format = ToolStrategy(
    schema=ProductRating,
    handle_errors=False  # 所有错误都抛出
)

'''
posted @ 2026-03-19 19:57  凫弥  阅读(4)  评论(0)    收藏  举报