在职前端Leader学习/转行 AI Agent -DAY24

2026.8.5 11:03

学习路线

rust 复制代码
Python -> Python进阶 -> Python数据分析 -> LangChain -> 机器学习 -> 神经网络 -> NLP -> Coze -> Dify -> 大模型应用基础 -> 大模型微调 -> 多模态 -> vibeCoding

复习巩固总结

1. LangChain封装调用大模型

python 复制代码
from langchain.chat_models import init_chat_model
import os
from dotenv import load_dotenv
load_dotenv(override=True)
CLOSEAI_API_KEY = os.getenv("CLOSEAI_API_KEY")
CLOSEAI_BASE_URL = os.getenv("CLOSEAI_BASE_URL")
model = init_chat_model(
    model="openai:deepseek-v4-flash",
    api_key=CLOSEAI_API_KEY,
    base_url=CLOSEAI_BASE_URL,
)
print(model.invoke("一句话介绍下你自己"))

2. invoke - 阻塞式

python 复制代码
# 字典列表(推荐)
conversation = [
    {"role": "system", "content": "你是一个非常友好的AI助手"},
    {"role": "user", "content": "你好,我叫小明"},
]
response1 = model.invoke(conversation)
print(f"AI的回复1:{response1.content}")
# 如果不传递历史,AI 会"失忆"
conversation.append({"role": "assistant", "content": response1.content})
conversation.append({"role": "user", "content": "我叫什么名字?"})
response2 = model.invoke(messages2)
print(f"AI的回复2:{response2.content}")
# 美化输出
from rich import print as rprint
rprint(response2)

3. stream - 流式输出

python 复制代码
from langchain.chat_models import init_chat_model
import os
from dotenv import load_dotenv
load_dotenv(override=True)
CLOSEAI_API_KEY = os.getenv("CLOSEAI_API_KEY")
CLOSEAI_BASE_URL = os.getenv("CLOSEAI_BASE_URL")
model = init_chat_model(
    model="openai:gpt-5.4-mini",
    api_key=CLOSEAI_API_KEY,
    base_url=CLOSEAI_BASE_URL,
)
# model.stream 返回一个迭代器
for chunk in model.stream("帮我解释一下什么是人工智能?"):
    print(chunk.text, end="", flush=True)

4. model_kwargs和extra_body

  • model_kwargs:传递标准 OpenAI 兼容 API 顶层标准参数,存放官方协议规范内、所有厂商通用的生成参数,最终会平铺展开到请求 JSON 的最外层一级字段
  • extra_body:传递厂商私有 / 扩展非标参数,存放不在 OpenAI 官方协议里、仅某一家模型 / 推理后端独有的参数,整个字典会完整嵌套进请求根节点,和 messages、model 同级

5. 4种消息对象中字段的说明

SystemMessage参数列表

  • content :消息内容,字段名可以省略

HumanMessage参数列表

  • content :消息内容,字段名可以省略
  • metadata :元数据字段,可以有很多,自定义
python 复制代码
# name 和 id 都属于元数据字段,当消息类型相同,对消息进行区分。但不是所有模型(例如GPT)都支持这一功能,是否支持取决于模型供应商(例如CloseAI)
HumanMessage(
    content="Hello!",
    name="alice", # 可选,用户名
    id="msg_123", # 可选,message的ID
)

AIMessage参数列表

  • content :模型输出的原始内容,字段名可以省略
  • response_metadata :AIMessage特有属性,LLM的响应中附加元数据,根据不同模型会有不同,如可能会包含本次token使用量等信息。
  • tool_calls :AIMessage特有属性,表示工具调用信息。当LLM决定调用工具时,在AIMessage 中就会包含这个属性,没有工具调用则为空。
js 复制代码
tool_calls=[
    {
        'name': 'get_weather', // 应调用的工具名
        'args': {'city': '杭州'}, // 调用工具的参数
        'id': 'call_00_gIXYOD1Q1OkEXmdDBqXR1578', // 工具调用的唯一标识ID
        'type': 'tool_call'
    }
]

ToolMessage参数列表

  • content :文件内容
  • name :工具名称
  • tool_call_id :工具调用唯一ID,ToolMessage必须紧邻匹配的AIMessage,和前者tool_calls中的id一致。

6. 对话历史优化

python 复制代码
def keep_recent_messages(messages, max_pairs=3):
    """
    保留最近的N轮对话
    :param max_pairs: 保留对话的轮数(每轮 = user + assistant)
    """
    # 分离 system 消息和对话消息
    system_messages = [m for m in messages if m.get("role") == "system"]
    conversation_messages = [m for m in messages if m.get("role") != "system"]
    # 只保留最近的消息对
    recent_messages = conversation_messages[-(max_pairs * 2):]
    # 返回系统消息和最近的消息对
    return system_messages + recent_messages
# 初始化
long_conversation = [
    {"role": "system", "content": "你是 Python 导师"}
]
# 多轮对话....
# 优化:只保留最近 2 轮
optimized = keep_recent_messages(long_conversation, max_pairs=2)
# 添加新的用户问题
optimized.append({"role": "user", "content": "我第一个问题问的是什么?"})
# 使用优化后的历史
response = model.invoke(optimized)
print(f"\nAI 回复: {response.content}")

7. content_blocks

支持类型:包括 text (文本)、 image (图片)、 audio (音频)、 video (视频)、 tool_call (工具调用)以及 reasoning (推理/思维链)。

  • 输入格式化
python 复制代码
HumanMessage(
    # content_blocks写法参考:https://docs.langchain.com/oss/python/langchain/messages#openai
    content_blocks=[
        {'type': 'text', 'text': '这张图里有什么?'},
        {
            'type': 'image',
            'base64': base64_image,
            'mime_type': 'image/png',
        }
    ]
)
  • 输出格式化

不同的模型其输出格式可能不同,仅为提取思考内容,切换模型都可能需要更改代码,非常不方便。content_blocks提供了 统一的输出格式 ,可以将不同格式的响应统一为标准格式。

python 复制代码
model = init_chat_model(
    model="deepseek:deepseek-v4-flash",
    extra_body={"thinking": {"type": "enabled"}},
)
response = model.invoke("你好,一句话回答")
print(response.content_blocks)

8. 提示词模板

实例化 - from_messages()

python 复制代码
from langchain_core.prompts import ChatPromptTemplate
chat_prompt_template = ChatPromptTemplate.from_messages([
    ("system", "你是一个友好的AI助手,你的名字叫{name}"),
    # ("user"),
    ("human", "你好,最近怎么样?"),
    # ("assistant")
    ("ai", "我很好,谢谢"),
    # ("AI", "我很好,谢谢"),  # 报错,注意是小写的ai
    ("human", "{user_input}")
])
result = chat_prompt_template.invoke({"name": "小智", "user_input": "2 + 2 = ?"})
print(result)

更丰富的初始化参数类型

python 复制代码
# 1 - 字符串列表
chat_prompt_template = ChatPromptTemplate.from_messages([
    "你好,我是{name}"  # 会理解为是一个用户消息
])

# 2 - 元组列表
chat_prompt_template = ChatPromptTemplate.from_messages([
    ("system", "你是一个友好的AI助手"),
    ("human", "你好,我是{name}")
])

# 3 - 字典列表
chat_prompt_template = ChatPromptTemplate.from_messages([
    {"role": "system", "content": "你是一个友好的AI助手"},
    {"role": "human", "content": "你好,我是{name}"}
])

# 4 - BaseMessagePromptTemplate参数列表
from langchain_core.promts import SystemMessagePromptTemplate, HumanMessagePromptTemplate
system_mess_template = SystemMessagePromptTemplate.from_messages("你是一个友好的AI助手")
human_mess_template = HumanMessagePromptTemplate.from_messages("你好,我是{name}")
chat_prompt_template = ChatPromptTemplate.from_messages([
    system_mess_template,
    human_mess_template
])

# 5 - BaseChatPromptTemplate参数列表
inner_chat_template1 = ChatPromptTemplate.from_messages([
    ("system", "你是一个友好的AI助手")
])
inner_chat_template2 = ChatPromptTemplate.from_messages([
    ("human", "你好,我是{name}")
])
chat_prompt_template = ChatPromptTemplate.from_messages([
    inner_chat_template1,
    inner_chat_template2
])

模板调用的3种方式

python 复制代码
# 1 - invoke  传入参数:字典列表;返回值类型:ChatPromptValue
result = chat_prompt_template.invoke({"name": "小智", "user_input": "2 + 2 = ?"})

# 2 - format  参数类型:变量值;返回值类型:字符串
result = chat_prompt_template.format(name="小智", user_input="2 + 2 = ?")

# 3 - format_messages  传入参数:变量值;返回值类型:消息列表
result = chat_prompt_template.format_messages(name="小智", user_input="2 + 2 = ?")

# 结合LLM调用
response = model.invoke(result)

LangChain 学习

24. 高级特性

1. 部分变量预填充:partial()

预填充某些固定不变的变量,创建模板的变体。

使用场景:

  • 某些变量在所有调用中都相同
  • 需要为不同用户/场景创建定制模板
jupyter 复制代码
# ChatPromptTemplate的高级特性

## 1. 部分变量预填充:partial()

举例:

from langchain_core.prompts import ChatPromptTemplate
# 原始模板
template = ChatPromptTemplate.from_messages([
    ("system", "你是{role},目标用户是{audience}"),
    ("user", "{task}")
])
result1 = template.invoke({"role": "导游", "audience": "游客", "task": "介绍一下北京的故宫"})
result2 = template.invoke({"role": "导游", "audience": "游客", "task": "介绍一下北京的颐和园"})
print(result1)
print(result2)

上述代码,可以使用partial()优化

from langchain_core.prompts import ChatPromptTemplate
# 原始模板
template = ChatPromptTemplate.from_messages([
    ("system", "你是{role},目标用户是{audience}"),
    ("user", "{task}")
])
# 部分变量预填充
# template.partial(role="导游", audience="游客")
final_template = template.partial(role="导游", audience="游客")
# result1 = template.invoke({"role": "导游", "audience": "游客", "task": "介绍一下北京的故宫"})
# result2 = template.invoke({"role": "导游", "audience": "游客", "task": "介绍一下北京的颐和园"})
# result1 = template.invoke({"task": "介绍一下北京的故宫"})
# result2 = template.invoke({"task": "介绍一下北京的颐和园"})
result1 = final_template.invoke({"task": "介绍一下北京的故宫"})
result2 = final_template.invoke({"task": "介绍一下北京的颐和园"})
print(result1)
print(result2)

举例:

# 场景:为不同部门创建专用模板
base_template = ChatPromptTemplate.from_messages([
    ("system", "你是{department}的{role}"),
    ("user", "{task}")
])
# IT 部门
it_template = base_template.partial(
    department="IT 部门",
    role="技术支持"
)
# 销售部门
sales_template = base_template.partial(
    department="销售部门",
    role="销售顾问"
)
sales_template.invoke({"task":"为什么每年年底汽车会促销"})

2026.08.05 13:16

2. 消息占位符

当你不确定消息提示模板使用什么角色,或者希望在格式化过程中 插入消息列表 时,该怎么办? 这就需要使用消息占位符,负责在特定位置添加消息列表。

使用场景:多轮对话系统存储历史消息以及Agent的中间步骤处理此功能非常有用。

jupyter 复制代码
## 2、消息占位符

### 2.1 使用placeholder

举例

template = ChatPromptTemplate.from_messages([
    ("system", "你是一个AI助手"),
    ("placeholder", "{conversation}")
])
result = template.invoke({
    "conversation": [
        ("human", "你好,请问明天的天气如何?"),
        ("ai", "明天天气晴朗"),
        ("human", "后天的天气怎么样?")
    ]
})
print(result)

### 2.2 使用MessagesPlaceholder

举例:

from langchain_core.prompts import MessagesPlaceholder
template = ChatPromptTemplate.from_messages([
    ("system", "你是一个AI助手"),
    # ("placeholder", "{conversation}")
    MessagesPlaceholder(variable_name="conversation")
])
result = template.invoke({
    "conversation": [
        ("human", "你好,请问明天的天气如何?"),
        ("ai", "明天天气晴朗"),
        ("human", "后天的天气怎么样?")
    ]
})
print(result)

from langchain_core.messages import HumanMessage, AIMessage
result = template.invoke({
    "conversation": [
        # ("human", "你好,请问明天的天气如何?"),
        # ("ai", "明天天气晴朗"),
        # ("human", "后天的天气怎么样?")
        HumanMessage("你好,请问明天的天气如何?"),
        AIMessage("明天天气晴朗"),
        HumanMessage("后天的天气怎么样?"),
    ]
})
print(result)

举例:存储历史消息

from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
prompt_template = ChatPromptTemplate.from_messages(
    [
        ("system", "你是一个非常友好的AI助手"),
        MessagesPlaceholder(variable_name="history"),
        ("human", "{question}")
    ]
)
prompt_template.invoke(
    {
        "history": [
            ("human", "5 + 2 = ?"),
            ("ai", "5 + 2 = 7")
        ],
        "question": "结果再乘以4呢?"
    }
)

3. 可复用模板库

jupyter 复制代码
## 3、可复用模板库

定义了具体的存放模板的py文件

from langchain_core.prompts import ChatPromptTemplate
class PromptLibrary:
    """可复用的提示词模板库"""
    TRANSLATOR = ChatPromptTemplate.from_messages([
        ("system", "你是专业翻译,精通{source_lang}和{target_lang}"),
        ("user", "翻译以下文本:\n{text}")
    ])
    CODE_REVIEWER = ChatPromptTemplate.from_messages([
        ("system", "你是{language}代码审查专家,重点关注{focus}"),
        ("user", "审查代码:\n```{language}\n{code}\n```")
    ])
    SUMMARIZER = ChatPromptTemplate.from_messages([
        ("system", "你是内容摘要专家"),
        ("user", "将以下内容总结为{num}个要点:\n{content}")
    ])
    TUTOR = ChatPromptTemplate.from_messages([
        ("system", "你是{subject}导师,学生水平:{level}"),
        ("user", "{question}")
    ])
    
其他文件中,进行调用:

# from templates import PromptLibrary
messages = PromptLibrary.TRANSLATOR.format_messages(
    source_lang="英语",
    target_lang="中文",
    text="Hello World"
)
python 复制代码
# templates/
# ├── __init__.py
# ├── common.py # 通用模板
# ├── translation.py # 翻译相关
# └── coding.py # 编程相关
# common.py
from langchain_core.prompts import ChatPromptTemplate
FRIENDLY_ASSISTANT = ChatPromptTemplate.from_messages([
    ("system", "你是一个友好的助手"),
    ("user", "{input}")
])

4. 模板组合(了解)

python 复制代码
# 定义可复用的部分
role_part = "你是一个{domain}专家。"
style_part = "回答风格:{style}。"
constraint_part = "限制:{constraint}。"
# 组合
full_system = role_part + style_part + constraint_part
template = ChatPromptTemplate.from_messages([
    ("system", full_system),
    ("user", "{question}")
])
python 复制代码
template1 = ChatPromptTemplate.from_messages([
    ("system", "你是助手")
])
template2 = ChatPromptTemplate.from_messages([
    ("user", "{input}")
])
# 组合(LangChain 1.0 支持)
combined = template1 + template2

25. Tools概述

1. 工具的重要性

构建更强大的AI工程应用,只有生成文本这样的" 纸上谈兵 "能力自然是不够的。

工具是赋予大语言模型 与外部世界交互能力 的关键组件,从而能让智能体执行搜索、计算、数据库查询、邮件发送或调用第三方API等,进而构建功能强大的AI应用。借助工具,大模型才能从" 认识世界 "走向" 改变世界 "。

大模型和智能体的区别:智能体能直接调用Tools

2. 工具调用的方式

在LangChain中,工具(Tools)实际上是指明确定义了输入和输出的 可调用函数 。因此, 工具调用(Tool Calling) 也被称为 函数调用(Function Calling) 。

jupyter 复制代码
# Tool使用的概述

## 1、工具的调用方式

### 1.1 方式1:直接调用

from langchain_core.tools import tool
@tool
def get_weather(city: str) -> str:
    """
    获取指定城市的天气信息
    参数:
        city: 城市名称,如"北京"、"上海"
    返回:
        天气信息字符串
    """
    # 你的实现
    return city + "晴天,温度 15°C"
    
get_weather.invoke({"city": "北京"})

### 1.2 方式2:基于模型进行调用

from langchain.chat_models import init_chat_model
from dotenv import load_dotenv
import os
# 从.env文件中加载环境变量
load_dotenv(override=True)
CLOSEAI_API_KEY = os.getenv("CLOSEAI_API_KEY")
CLOSEAI_BASE_URL = os.getenv("CLOSEAI_BASE_URL")
model = init_chat_model(
    model="gpt-5.4-mini",
    model_provider="openai",
    api_key=CLOSEAI_API_KEY,
    base_url=CLOSEAI_BASE_URL
)

from langchain_core.tools import tool
# 定义工具
@tool
def get_weather(city: str) -> str:
    """获取指定城市的天气"""
    # 你的实现
    return "晴天,温度 15°C"
# 绑定工具
model_with_tools = model.bind_tools([get_weather])
# AI 可以决定是否调用工具
response = model_with_tools.invoke("北京天气如何?")
# response = model_with_tools.invoke("2 + 3 = ?")
# 检查 AI 是否要调用工具
if response.tool_calls:
    print("AI 想调用工具:", response.tool_calls)
else:
    print("AI 直接回答:", response.content)

3. 工具调用的整体流程

flowchart LR subgraph 参与方 U[用户] Bot[AI助手或应用] LLM[模型] Tool[天气查询工具<br/>get_weather] end U --①今天天气如何--> Bot Bot --②用户问题:xxx<br/>可调用get_weather、工具描述--> LLM LLM --③get_weather调用参数--> Bot Bot --④解析参数执行工具、记录结果--> Tool Bot --⑤工具结果+对话记录打包--> LLM LLM --⑥整合数据生成最终回答--> Bot Bot --⑦返回回答给用户--> U

4. 从Message流转看工具的调用

jupyter 复制代码
## 2、从Message流转看工具的调用

from langchain.chat_models import init_chat_model
from dotenv import load_dotenv
import os
# 从.env文件中加载环境变量
load_dotenv(override=True)
CLOSEAI_API_KEY = os.getenv("CLOSEAI_API_KEY")
CLOSEAI_BASE_URL = os.getenv("CLOSEAI_BASE_URL")
model = init_chat_model(
    model="gpt-5.4-mini",
    model_provider="openai",
    api_key=CLOSEAI_API_KEY,
    base_url=CLOSEAI_BASE_URL
)

from langchain.messages import HumanMessage, ToolMessage
from rich import print as rprint
@tool
def get_weather(city: str):
    """获取天气的工具"""
    return f"{city}天气晴朗~"
# 将模型和工具绑定
model_with_tools = model.bind_tools([get_weather])
# 声明一个消息列表
messages = [
    HumanMessage("今天北京天气如何")
]
# 模型生成调用工具请求
response = model_with_tools.invoke(messages)
# 添加AIMessage到消息列表中
messages.append(response)
print(response)
rprint(response)  # AIMessage
tool_calls = response.tool_calls
for tool_call in tool_calls:
    if tool_call["name"] == "get_weather":
        # 大模型和Agent的主要区别在于:大模型不会主动的调用工具,所以这时候我们需要主动让工具调用
        # 返回的是ToolMessage类型消息,添加到消息列表中
        tool_response = get_weather.invoke(tool_call)
        print(type(tool_response))
        messages.append(tool_response)
print("=====================> messages <=====================")
for msg in messages:
    msg.pretty_print()
print("=====================> messages <=====================")
final_response = model_with_tools.invoke(messages)
print(f"final_response: \n{final_response}")
flowchart LR S[&#34;开始&#34;] S --> Step1[&#34;1.初始化模型和工具<br/>model.bind_tools([get_weather])&#34;] Step1 --> Step2[&#34;2.创建HumanMessage<br/>messages = [HumanMessage('今天北京天气如何')]&#34;] Step2 --> Step3[&#34;3.调用模型 Invoke&#34;] Step3 --> Judge{存在 tool_calls?} Judge -- 有 --> Step4[&#34;4.生成带tool_calls的AIMessage&#34;] Step4 --> Step5[&#34;5.将AI消息追加到messages&#34;] Step5 --> Step6[&#34;6.执行工具,生成ToolMessage&#34;] Step6 --> Step7[&#34;7.ToolMessage写入消息列表&#34;] Step7 --> Step3 Judge -- 无 --> Step8[&#34;8.输出最终回答:北京今天天气晴朗&#34;] Step8 --> End[&#34;结束&#34;]

26. 工具的定义方式1:不使用@tool

1. 模型绑定工具并发送请求

jupyter 复制代码
# 不使用@tool的方式定义工具

## 1、举例

# 1、模型的初始化
from langchain.chat_models import init_chat_model
from dotenv import load_dotenv
import os
from rich import print as rprint
# 从.env文件中加载环境变量
load_dotenv(override=True)
CLOSEAI_API_KEY = os.getenv("CLOSEAI_API_KEY")
CLOSEAI_BASE_URL = os.getenv("CLOSEAI_BASE_URL")
model = init_chat_model(
    model="gpt-5.4-mini",
    model_provider="openai",
    api_key=CLOSEAI_API_KEY,
    base_url=CLOSEAI_BASE_URL
)
# 2、声明一个函数(工具)
def get_weather(city: str):
    return f"{city}天气晴朗~~"
# 3、将函数绑定在模型上
model_with_tools = model.bind_tools([get_weather])
# 4、调用模型
response = model_with_tools.invoke("北京的天气怎么样")
rprint(response)

2. 工具描述的各部分详解

了解:convert_to_openai_tool

执行 model.bind_tools(get_weather) ,底层最终会调用 convert_to_openai_tool 生成工具描述。

jupyter 复制代码
## 2、工具描述的各部分详解

### 2.1 了解convert_to_openai_tool

执行 model.bind_tools([get_weather]) ,底层最终会调用 convert_to_openai_tool 生成工具描述。所以我们可以直接调用后者查看解析后的工具描述。

from langchain_core.utils.function_calling import convert_to_openai_tool
def get_weather(city: str):
    return f"{city}天气晴朗~~"
rprint(convert_to_openai_tool(get_weather))

上面示例输出如下

bash 复制代码
{
    'type': 'function',
    'function': {
        'name': 'get_weather',
        'description': '',
        'parameters': {
            'properties': {
                'city': {
                    'type': 'string'
                }
            },
            'required': ['city'],
            'type': 'object'
        }
    }
}

问题:为什么不使用@tool装饰器修饰的函数,也可以理解为工具呢?

查看 convert_to_openai_tool 底层源码:

bash 复制代码
elif isinstance(function, langchain_core.tools.base.BaseTool):
    oai_function = cast("dict", _format_tool_to_openai_function(function))
elif callable(function):  # 上面示例走这个elif
    oai_function = cast(
        "dict", _convert_python_function_to_openai_function(function)
    )
description说明
jupyter 复制代码
### 2.2 description说明

from langchain_core.utils.function_calling import convert_to_openai_tool
def get_weather(city: str):
    """
    查询城市的天气
    """
    return f"{city}天气晴朗~~"
rprint(convert_to_openai_tool(get_weather))
参数说明

这里的 docstring 必须遵循 Google 风格 。

jupyter 复制代码
### 2.3 参数说明

from langchain_core.utils.function_calling import convert_to_openai_tool
# 注意函数功能描述和入参描述之间必须有一个空行
# 入参描述必须是 Args,不是 args
# 错误示例:city: 具体的城市  中间不能是中文冒号
def get_weather(city: str):
    """
    查询城市的天气

    Args:
        city: 具体的城市

    Returns:
        返回城市的天气
    """
    return f"{city}天气晴朗~~"
rprint(convert_to_openai_tool(get_weather))

AI 依赖 docstring 来理解工具。

python 复制代码
# ❌ 不好:太模糊
@tool
def tool1(x: str) -> str:
"""做一些事情"""
...
python 复制代码
# ✅ 好:清晰明确
@tool
def search_products(query: str) -> str:
    """
    在产品数据库中搜索产品
    
    Args:
        query: 搜索关键词,如"笔记本电脑"、"手机"
        
    Returns:
        产品列表的 JSON 字符串
    """
    ...
参数类型说明
jupyter 复制代码
### 2.4 参数类型说明

举例1:正确的

from langchain_core.utils.function_calling import convert_to_openai_tool
# def get_weather(city: str):
def get_weather(city):
    """
    查询城市的天气
    """
    return f"{city}天气晴朗~~"
rprint(convert_to_openai_tool(get_weather))

举例2:如下的代码运行会报错

要求:如果在docstring中声明了参数的描述,则必须在函数声明处指明参数的类型

from langchain_core.utils.function_calling import convert_to_openai_tool
# def get_weather(city: str):
def get_weather(city):
    """
    查询城市的天气

    Args:
        city: 具体的城市
    """
    return f"{city}天气晴朗~~"
rprint(convert_to_openai_tool(get_weather))
参数默认值说明
jupyter 复制代码
### 2.5 参数默认值说明

一旦参数设置了默认值,则打印的结果中的required字段中就不再包含此参数

举例1:

from langchain_core.utils.function_calling import convert_to_openai_tool
# def get_weather(city):
def get_weather(city: str = "beijing"):
    """
    查询城市的天气

    Args:
        city: 具体的城市
    """
    return f"{city}天气晴朗~~"
rprint(convert_to_openai_tool(get_weather))

举例2:

from langchain_core.utils.function_calling import convert_to_openai_tool
# def get_weather(city: str = "beijing"):
def get_weather(dt: str, city: str = "beijing"):
    """
    查询城市的天气

    Args:
        city: 具体的城市
        dt: 时间
    """
    return f"{city}天气晴朗~~"
rprint(convert_to_openai_tool(get_weather))

27. 工具的定义方式2:使用@tool装饰器(推荐)

使用 @tool 装饰器修饰,可以自动将普通 Python 函数转化为智能体可调用的工具。

此方式 最直接 ,代码量极少

1. 自定义工具描述:description

jupyter 复制代码
# 使用@tool装饰器定义工具

## 1、自定义工具描述:description

举例1:

函数使用@tool装饰器修饰以后,就是一个可以被模型识别的工具了

如下的程序报错了,因为:
在没有提供description参数的情况下,要求函数必须提供docstring

from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print as rprint
from langchain_core.tools import tool
@tool
def get_weather(city: str):
    return f"{city}天气晴朗"
rprint(convert_to_openai_tool(get_weather))

修改为:

from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print as rprint
from langchain_core.tools import tool
@tool
def get_weather(city: str):
    """获取城市的天气"""
    return f"{city}天气晴朗"
rprint(convert_to_openai_tool(get_weather))

举例2:使用description参数

from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print as rprint
from langchain_core.tools import tool
@tool(description="获取具体城市的天气情况")
def get_weather(city: str):
    return f"{city}天气晴朗"
rprint(convert_to_openai_tool(get_weather))

举例3:

在同时声明了description和docstring的情况下,description的优先级更高

from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print as rprint
from langchain_core.tools import tool
@tool(description="获取具体城市的天气情况")
def get_weather(city: str):
    """获取城市的天气"""
    return f"{city}天气晴朗"
rprint(convert_to_openai_tool(get_weather))

举例4:

当我们没有向 @tool 传递 description 参数时,默认情况下, tool 会将 docstring 整体视为description

不使用 @tool 装饰器时,docstring不合法会被视为普通文本,作为 description ,但如果使用了 @tool 时 docstring 不合法,将会抛出异常

from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print as rprint
from langchain_core.tools import tool
# @tool(description="获取具体城市的天气情况")
# @tool
@tool(parse_docstring=True)
def get_weather(city: str):
    """
    获取城市的天气

    Args:
        city: 城市
    """
    return f"{city}天气晴朗"
rprint(convert_to_openai_tool(get_weather))

举例5:

from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print as rprint
from langchain_core.tools import tool
# @tool(parse_docstring=True)
@tool(parse_docstring=True, description="获取具体城市的天气")
def get_weather(city: str):
    """
    获取城市的天气

    Args:
        city: 城市
    """
    return f"{city}天气晴朗"
rprint(convert_to_openai_tool(get_weather))

2. 更改工具名称:name_or_callable

jupyter 复制代码
## 2、更改工具名称:name_or_callable

开发中,习惯使用函数名作为工具名称,不推荐自定义工具名称。

举例:

from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print as rprint
from langchain_core.tools import tool
# @tool(parse_docstring=True)
# @tool(parse_docstring=True, description="获取具体城市的天气")
@tool(parse_docstring=True, name_or_callable="getWeather")
def get_weather(city: str):
    """
    获取城市的天气

    Args:
        city: 城市
    """
    return f"{city}天气晴朗"
rprint(convert_to_openai_tool(get_weather))

说明:不要使用config或runtime作为参数名,这些是LangChain内部保留的。

3. 自定义args_schema

1. 使用Pydantic模型定义
jupyter 复制代码
## 3、自定义args_schema

### 3.1 使用Pydantic模型定义

举例1:

from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print as rprint
from langchain_core.tools import tool
from pydantic import BaseModel
class WeatherInput(BaseModel):
    city: str
# @tool(parse_docstring=True, name_or_callable="getWeather")
# @tool(parse_docstring=True)
@tool(args_schema=WeatherInput)
def get_weather(city: str):
# def get_weather(city: int):
    """
    获取城市的天气
    """
    return f"{city}天气晴朗"
rprint(convert_to_openai_tool(get_weather))

举例2:

from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print as rprint
from langchain_core.tools import tool
from pydantic import BaseModel, Field
class WeatherInput(BaseModel):
    # city: str
    city: str = Field(
        description="具体的城市",
        default="北京"
    )
@tool(args_schema=WeatherInput)
def get_weather(city: str):
    """
    获取城市的天气
    """
    return f"{city}天气晴朗"
rprint(convert_to_openai_tool(get_weather))

举例3:

from typing import Literal
class WeatherInput(BaseModel):
    # city: str
    city: str = Field(
        description="具体的城市",
        default="北京"
    )
    unit: Literal["celsius", "fahrenheit"]
    # include_forecast: bool = False
    include_forecast: bool = Field(
        default=False,
        description="是否包含未来五天的天气预报"
    )
@tool(args_schema=WeatherInput)
# def get_weather(city: str):
# def get_weather(city: str, unit: Literal["celsius", "fahrenheit"] = "celsius"):
# def get_weather(city: str, unit: str = "celsius"):
def get_weather(city: str, unit: str = "celsius", include_forecast: bool = True):
    """
    获取城市的天气
    """
    return f"{city}天气晴朗"
rprint(convert_to_openai_tool(get_weather))
2. 使用Json Schema定义

在 LangChain 中,还可以直接使用 JSON Schema 字典 来定义工具的参数模式。这种方式提供了极大的灵活性。

因为工具参数模式可以基于数据库配置或用户输入在 运行时动态生成 ,所以这种方式特别适合参数结构需要动态生成的场景。

jupyter 复制代码
### 3.2 使用Json Schema定义

举例:

json_schema = {
    'type': 'function',
    'function': {
        'name': 'get_weather',
        'description': '获取城市的天气',
        'parameters': {
            'properties': {
                'city': {'default': '北京', 'description': '具体的城市111', 'type': 'string'},
                'unit': {'enum': ['celsius', 'fahrenheit'], 'type': 'string'},
                'include_forecast': {
                    'default': False,
                    'description': '是否包含未来五天的天气预报111',
                    'type': 'boolean'
                }
            },
            'required': ['unit'],
            'type': 'object'
        }
    }
}

# @tool(args_schema=WeatherInput)
@tool(args_schema=json_schema)
def get_weather(city: str, unit: str = "celsius", include_forecast: bool = True):
    """
    获取城市的天气
    """
    return f"{city}天气晴朗"

28. 工具的应用案例

1. 使用args_schema

jupyter 复制代码
# 工具的应用案例

## 1、使用args_schema

举例

from langchain.chat_models import init_chat_model
from dotenv import load_dotenv
import os
# 从.env文件中加载环境变量
load_dotenv(override=True)
CLOSEAI_API_KEY = os.getenv("CLOSEAI_API_KEY")
CLOSEAI_BASE_URL = os.getenv("CLOSEAI_BASE_URL")
model = init_chat_model(
    model="gpt-5.4-mini",
    model_provider="openai",
    api_key=CLOSEAI_API_KEY,
    base_url=CLOSEAI_BASE_URL
)

from pydantic import BaseModel, Field
from langchain_core.utils.function_calling import convert_to_openai_tool
from langchain_core.tools.convert import tool
# 定义工具
class WeatherSchema(BaseModel):
    city: str = Field(
        default="北京",
        description="具体的城市名称"
    )
    if_forecast: bool = Field(
        default=False,
        description="是否包含明日天气"
    )
# @tool
@tool("get_weather_and_forecast", description="查询当日的天气,可以包含明天的天气预报", args_schema=WeatherSchema)
def get_weather(city: str, if_forecast: bool):
    res = f"{city}今天天气不错"
    if if_forecast:
        # res += '\n明天天气也不错'
        res += '\n明天下雨'
    return res
# print(convert_to_openai_tool(get_weather))

from langchain_core.messages import HumanMessage
# 1、将工具绑定到模型上
model_with_tools = model.bind_tools([get_weather])
# 2、维护一个消息列表
messages = [
    HumanMessage('今天杭州的天气怎么样?明天呢?')
]
# 3、调用模型,得到响应:AIMessage
# model_with_tools.invoke("")
response = model_with_tools.invoke(messages)
messages.append(response)
# 4、获取响应中的tool_calls字段信息
tool_calls = response.tool_calls
for tool_call in tool_calls:
    # if tool_call["name"] == "get_weather":
    if tool_call["name"] == "get_weather_and_forecast":
        # 5、调用工具(因为大模型不能直接调用工具,所以此时我们主动让工具调用执行)
        # 调用完,返回ToolMessage的实例
        tool_message = get_weather.invoke(tool_call)
        messages.append(tool_message)
# 6、调用模型,得到AIMessage
final_response = model.invoke(messages)
# 7、添加到消息列表中
messages.append(final_response)
# 8、遍历消息列表
for msg in messages:
    msg.pretty_print()

2. 撰写docstring

jupyter 复制代码
## 2、撰写docstring

from langchain.chat_models import init_chat_model
from dotenv import load_dotenv
import os
# 从.env文件中加载环境变量
load_dotenv(override=True)
CLOSEAI_API_KEY = os.getenv("CLOSEAI_API_KEY")
CLOSEAI_BASE_URL = os.getenv("CLOSEAI_BASE_URL")
model = init_chat_model(
    model="gpt-5.4-mini",
    model_provider="openai",
    api_key=CLOSEAI_API_KEY,
    base_url=CLOSEAI_BASE_URL
)

from pydantic import BaseModel, Field
from langchain_core.utils.function_calling import convert_to_openai_tool
from langchain_core.tools.convert import tool
# 定义工具
# class WeatherSchema(BaseModel):
#     city: str = Field(
#         default="北京",
#         description="具体的城市名称"
#     )
#     if_forecast: bool = Field(
#         default=False,
#         description="是否包含明日天气"
#     )
# @tool("get_weather_and_forecast", description="查询当日的天气,可以包含明天的天气预报", args_schema=WeatherSchema)
# @tool("get_weather_and_forecast", description="")
@tool("get_weather_and_forecast", parse_docstring=True)
# def get_weather(city: str, if_forecast: bool):
# def get_weather(city: str = "北京", if_forecast: bool):
def get_weather(city: str = "北京", if_forecast: bool = False):
    """
    查询当日的天气,可以包含明天的天气预报

    Args:
        city: 城市名称,
        if_forecast: 是否包含明天的天气
    """
    res = f"{city}今天天气不错"
    if if_forecast:
        res += '\n明天下雨'
    return res
print(convert_to_openai_tool(get_weather))

from langchain_core.messages import HumanMessage
# 1、将工具绑定到模型上
model_with_tools = model.bind_tools([get_weather])
# 2、维护一个消息列表
messages = [
    HumanMessage('今天杭州的天气怎么样?明天呢?')
]
# 3、调用模型,得到响应:AIMessage
# model_with_tools.invoke("")
response = model_with_tools.invoke(messages)
messages.append(response)
# 4、获取响应中的tool_calls字段信息
tool_calls = response.tool_calls
for tool_call in tool_calls:
    # if tool_call["name"] == "get_weather":
    if tool_call["name"] == "get_weather_and_forecast":
        # 5、调用工具(因为大模型不能直接调用工具,所以此时我们主动让工具调用执行)
        # 调用完,返回ToolMessage的实例
        tool_message = get_weather.invoke(tool_call)
        messages.append(tool_message)
# 6、调用模型,得到AIMessage
final_response = model.invoke(messages)
# 7、添加到消息列表中
messages.append(final_response)
# 8、遍历消息列表
for msg in messages:
    msg.pretty_print()

3. 多工具调用

jupyter 复制代码
## 3、多工具调用

from langchain_core.tools import tool
from langchain_core.messages import HumanMessage, AIMessage, ToolMessage
from rich import print as rprint
# 1.定义工具
# 定义股票查询工具
@tool(parse_docstring=True)
def get_stock_price(company: str, timeframe: str = "today") -> str:
    """
    获取指定公司的股票价格信息

    Args:
        company: 公司名称(如:苹果公司, 微软公司, 谷歌公司)
        timeframe: 时间范围(today-今日, week-本周, month-本月)
    """
    # 模拟股票数据
    mock_data = {
        "苹果公司": {"today": 185.20, "week": 183.50, "month": 180.75},
        "微软公司": {"today": 415.86, "week": 412.30, "month": 405.42},
        "谷歌公司": {"today": 15.42, "week": 15.20, "month": 14.85}
    }
    if company in mock_data:
        price = mock_data[company].get(timeframe, "未知时间范围")
        return f"{company} {timeframe}价格: {price}美元"
    else:
        return f"未找到股票代码 {company} 的数据"
# 定义新闻搜索工具
@tool(parse_docstring=True)
def search_news(company: str) -> str:
    """
    搜索指定公司的财经新闻

    Args:
        company: 公司名称

    Returns:
        公司的财经新闻,每个新闻占一行
    """
    # 模拟新闻数据
    mock_news = {
        "苹果公司": [
            "苹果发布新款iPhone,股价上涨3%",
            "苹果与欧盟达成反垄断和解协议",
            "苹果将在印度扩大生产规模"
        ],
        "微软公司": [
            "微软Azure云业务季度增长超预期",
            "微软完成对Nuance的收购",
            "微软推出新一代AI助手Copilot"
        ],
        "谷歌公司": [
            "谷歌发布新AI模型,性能提升20%",
            "谷歌与OpenAI合作,开发新的AI助手",
            "谷歌在欧洲展开AI研究项目"
        ]
    }
    news_list = mock_news.get(company, [f"未找到{company}的相关新闻"])
    return "\n".join(news_list)
# rprint(convert_to_openai_tool(search_news))
# 2.初始化模型并绑定工具
tools = [get_stock_price, search_news]
model_with_tools = model.bind_tools(tools)
message_list = []
human_message = HumanMessage(content="苹果公司今天的股价是多少?最近有什么新闻?")
# human_message = HumanMessage(content="比较一下微软和苹果的股价")
# human_message = HumanMessage(content="腾讯最近有什么重大新闻?")
# human_message = HumanMessage(content="海水为什么是咸的?")
message_list.append(human_message)
# 3.工具调用
while True:
    response = model_with_tools.invoke(message_list)
    # rprint(response)
    # break
    # 将返回的AIMessage添加到消息列表
    message_list.append(response)
    # 如果模型不需要调用工具,直接退出循环
    if not response.tool_calls:
        print("没有工具调用,直接返回答案")
        break
    # 如果有调用工具,处理工具调用响应
    # 4.开发者根据模型的响应,调用工具并获取结果
    for tool_call in response.tool_calls:
        if tool_call["name"] == "get_stock_price":
            stock_result = get_stock_price.invoke(tool_call)
            print("stock_result", stock_result)
            message_list.append(stock_result)
        if tool_call["name"] == "search_news":
            news_result = search_news.invoke(tool_call)
            print("news_result", news_result)
            message_list.append(news_result)
# print("response", response)
# print(response.content)
for msg in message_list:
    msg.pretty_print()

4. 多工具调用

jupyter 复制代码
## 4、多工具调用

from langchain.tools import tool
from langchain.messages import HumanMessage
@tool(parse_docstring=True)
def get_weather(city: str) -> str:
    """
    获取当日天气

    Args:
        city: 城市名称
    """
    return f'{city}当天晴朗'
@tool(parse_docstring=True)
def get_news() -> str:
    """
    获取当日新闻
    """
    return "近期,受全球储蓄芯片短缺等多重因素影响,多地回收商称废旧手机回收市场迎来"火热潮",回收价格普遍上涨,旧手机成"香饽饽"。"
model_with_tools = model.bind_tools([get_weather, get_news])
messages = [
    HumanMessage("今天杭州天气如何?今天新闻是什么?别瞎编")
]
response = model_with_tools.invoke(messages)
response.pretty_print()
messages.append(response)
for tool_call in response.tool_calls:
    if tool_call["name"] == "get_weather":
        tool_msg = get_weather.invoke(tool_call)
        print(tool_msg)
        messages.append(tool_msg)
    elif tool_call["name"] == "get_news":
        tool_msg = get_news.invoke(tool_call)
        print(tool_msg)
        messages.append(tool_msg)
    else:
        raise Exception("不存在的工具")
final_response = model.invoke(messages)
messages.append(final_response)
for msg in messages:
    msg.pretty_print()

29. 拓展:强制实用工具(了解)

bind_tools 可以传递参数 tool_choice ,用于控制是否强制使用工具。

该字段最终会作为 payload 的 tool_choice 字段传递给模型,OpenAI和Deepseek的官方API服务对于 tool_choice 的取值做了相同的规定。

  • none :模型不会调用任何工具。
  • auto : 默认值 ,模型可以自主决定不调用或调用任意数量的工具。
  • required :模型必须调用工具,数量不限。
  • 此外, tool_choice 还支持传递 any ,等价于 required 。
jupyter 复制代码
# tool_choice的使用

## 1、tool_choice的取值为none

from langchain.chat_models import init_chat_model
from dotenv import load_dotenv
import os
# 从.env文件中加载环境变量
load_dotenv(override=True)
CLOSEAI_API_KEY = os.getenv("CLOSEAI_API_KEY")
CLOSEAI_BASE_URL = os.getenv("CLOSEAI_BASE_URL")
model = init_chat_model(
    model="gpt-5.4-mini",
    model_provider="openai",
    api_key=CLOSEAI_API_KEY,
    base_url=CLOSEAI_BASE_URL
)

from langchain.tools import tool
from langchain.messages import HumanMessage
from rich import print as rprint
@tool(parse_docstring=True)
def get_weather(city: str) -> str:
    """
    获取当日天气

    Args:
        city: 城市名称
    """
    return f'{city}当天晴朗'
model_with_tools = model.bind_tools([get_weather], tool_choice="none")
response = model_with_tools.invoke("北京今天的天气如何?")
rprint(response)

## 2、tool_choice的取值为auto

from langchain.tools import tool
from langchain.messages import HumanMessage
from rich import print as rprint
@tool(parse_docstring=True)
def get_weather(city: str) -> str:
    """
    获取当日天气

    Args:
        city: 城市名称
    """
    return f'{city}当天晴朗'
# model_with_tools = model.bind_tools([get_weather], tool_choice="none")
model_with_tools = model.bind_tools([get_weather], tool_choice="auto")
# response = model_with_tools.invoke("北京今天的天气如何?")
response = model_with_tools.invoke("2 + 3 = ?")
rprint(response)

## 3、tool_choice取值为required

from langchain.tools import tool
from langchain.messages import HumanMessage
from rich import print as rprint
@tool(parse_docstring=True)
def get_weather(city: str) -> str:
    """
    获取当日天气

    Args:
        city: 城市名称
    """
    return f'{city}当天晴朗'
# model_with_tools = model.bind_tools([get_weather], tool_choice="auto")
model_with_tools = model.bind_tools([get_weather], tool_choice="required")
response = model_with_tools.invoke("北京今天的天气如何?")
rprint(response)

from langchain.tools import tool
from langchain.messages import HumanMessage
from rich import print as rprint
@tool(parse_docstring=True)
def get_weather(city: str) -> str:
    """
    获取当日天气

    Args:
        city: 城市名称
    """
    return f'{city}当天晴朗'
model_with_tools = model.bind_tools([get_weather], tool_choice="required")
# response = model_with_tools.invoke("北京今天的天气如何?")
response = model_with_tools.invoke("2 + 3 = ?")
rprint(response)

## 4、强制调用指定的工具

from langchain.tools import tool
from langchain.messages import HumanMessage
@tool(parse_docstring=True)
def get_weather1(city: str) -> str:
    """
    获取当日天气

    Args:
        city: 城市名称
    """
    return f'{city}当天晴朗'
@tool(parse_docstring=True)
def get_weather2(city: str) -> str:
    """
    获取当日天气

    Args:
        city: 城市名称
    """
    return f'{city}当天晴朗'
# model.bind_tools([get_weather1, get_weather2], tool_choice="get_weather2")
model_with_tools = model.bind_tools([get_weather1, get_weather2], tool_choice="get_weather2")
response = model_with_tools.invoke("2 + 3 = ?")
rprint(response)

from langchain.tools import tool
from langchain.messages import HumanMessage
@tool(parse_docstring=True)
def get_weather1(city: str) -> str:
    """
    获取当日天气

    Args:
        city: 城市名称
    """
    return f'{city}当天晴朗'
@tool(parse_docstring=True)
def get_weather2(city: str) -> str:
    """
    获取当日天气

    Args:
        city: 城市名称
    """
    return f'{city}当天晴朗'
# model_with_tools = model.bind_tools([get_weather1, get_weather2], tool_choice="get_weather2")
model_with_tools = model.bind_tools([get_weather1, get_weather2], tool_choice="get_weather1")
# response = model_with_tools.invoke("2 + 3 = ?")
response = model_with_tools.invoke("北京今天的天气如何?")
rprint(response)

30. 实践经验总结

1. 清晰的描述

python 复制代码
# ✅ 好
@tool(parse_docstring=True)
def search_flights(origin: str, destination: str, date: str) -> str:
    """
    搜索航班信息
    
    Args:
        origin: 出发城市,如"北京"
        destination: 目的地城市,如"上海"
        date: 出发日期,格式 YYYY-MM-DD
        
    Returns:
    可用航班的 JSON 列表
    """

2. 功能单一

python 复制代码
# ❌ 不好:一个工具做太多事
@tool
def do_everything(action: str, data: str) -> str:
    """做各种事情"""
    if action == "weather": ...
    elif action == "calculate": ...
    elif action == "search": ...
# ✅ 好:每个工具做一件事
@tool
def get_weather(city: str) -> str:
    """获取天气"""
    ...
@tool
def calculator(operation: str, a: float, b: float) -> str:
    """计算"""
    ...

3. 如何处理工具失败?

三层防护:

第1层:工具内部处理
python 复制代码
@tool
def divide(a: float, b: float) -> str:
    """
    除法计算
    
    Args:
        a: 被除数
        b: 除数
    """
    try:
        if b == 0:
            return "错误:除数不能为零"
        result = a / b
        return f"{a} / {b} = {result}"
    except Exception as e:
        return f"计算错误:{e}"
第2层:Agent 级重试(使用 prompt)
ini 复制代码
agent = create_agent(
    model=model,
    tools=[...],
    prompt="如果工具失败,尝试使用其他方法解决问题。"
)
第3层:调用级重试

@retry 就像是一个 容错保险 。

python 复制代码
from tenacity import retry, stop_after_attempt
# 1. 配置重试规则:如果失败,最多尝试 3 次(即第 1 次正常调用 + 2 次重试)
@retry(stop=stop_after_attempt(3))
def call_agent(question):
    # 2. 核心业务逻辑:调用 LangChain 的 Agent
    return agent.invoke({"messages": [{"role": "user", "content": question}]})

① 你调用 call_agent("你好") 。

② 程序进入函数,执行 agent.invoke(...) 。

③ 如果执行成功:正常返回结果, @retry 什么都不做。

④ 如果执行失败(报错): @retry 会拦截这个错误,不让程序直接崩溃。它会默默地帮你再次触发 agent.invoke(...) 。

⑤ 如果连续 3 次都报错:它终于放弃了,把第 3 次的报错真正抛出来,程序此时才会报错中止。

4. 返回字符串

python 复制代码
# ✅ 好:返回字符串
@tool
def get_user_info(user_id: str) -> str:
    """获取用户信息"""
    user = {"id": user_id, "name": "张三"}
    return json.dumps(user, ensure_ascii=False) # 转成 JSON 字符串
# ❌ 不好:返回字典(某些情况可能有问题)
@tool
def get_user_info(user_id: str) -> dict:
    """获取用户信息"""
    return {"id": user_id, "name": "张三"}

强烈建议工具返回字符串(str)。因为:

1)大模型(LLM)的本质只吃"文本"

2)乱码与格式问题

如果你返回一个包含中文的字典 {"name": "张三"} ,LangChain 在强制将其转换为字符串时,默认可能会采用 Unicode 编码,变成 {"name": "\u5f20\u4e09"} 。

大模型虽然能理解 Unicode,但极易受到干扰。直接看到中文 张三 的大模型,和看到 \u5f20\u4e09 的大模型,其输出的稳定性和准确率是有差距的。

5. 选择同步 vs 异步

  • 同步工具 :简单场景,CPU 密集型任务
  • 异步工具 :IO 密集型(API 调用、数据库、文件操作)

31. 结构化输出概述

1. 什么是结构化输出

要求模型最终返回一个符合预定义结构的数据对象,例如固定字段的JSON、Pydantic 模型、TypedDict,而不再是无格式的自然语言文本。

它的核心目标是把" 自然语言回答 "变成" 程序可以稳定消费的数据 "。

例如,不是让模型输出:

盗梦空间在2010年上映,导演是克里斯托弗·诺兰,评分9.3。

而是让它输出成类似这样的结构:

python 复制代码
{
    "title": "盗梦空间",
    "year": 2010,
    "director": "克里斯托弗·诺兰",
    "rating": 9.3
}

这样做的价值主要有三点:

  • 更容易被代码处理:下游系统可以直接读字段,而不是再从自然语言里做解析。
  • 结果更稳定:减少"模型说法变了但意思差不多"导致的解析失败。
  • 更适合工程化:适用于表单抽取、分类、路由、调用工具参数生成、工作流状态传递等场景。

2. 传统方式 vs 结构化输出

1、传统的几种方式(繁琐、不推荐)
python 复制代码
# 1. 提示词要求JSON
prompt = "以JSON格式返回:{name, age, occupation}"
response = model.invoke(prompt)
python 复制代码
# 2. 手动解析
import json
data = json.loads(response.content)
python 复制代码
# 3. 手动验证类型
if not isinstance(data['age'], int):
    raise ValueError("age must be int")
python 复制代码
# 4. 手动创建对象
person = Person(**data)
2、结构化输出(简洁)
python 复制代码
# 一步到位
structured_llm = model.with_structured_output(Person)
person = structured_llm.invoke("张三是一名 30 岁的软件工程师")
# ✅ 自动解析、验证、创建对象

为什么第2种结构化输出机制这么受欢迎?

有了 Pydantic 等结构化方案结合 .with_structured_output() 之后:

  • Prompt 变干净了: 字段的 description 直接充当了 Prompt 的一部分。
  • 类型安全: 编辑器能自动补全,代码运行前就能做类型检查。
  • 极其稳定: 依托大模型厂商底层的 JSON 模式,输出错误率降到了极低。

3. 结构化输出模式

  • Pydantic(字段校验、描述、嵌套结构,功能最丰富)
  • TypedDict(轻量类型约束)
  • JSON Schema(与前后端/跨语言接口最通用)
  • dataclass

只有 Pydantic 返回的是Schema类实例,其余三种方式返回的都是 字典 ;也只有 Pydantic 在类型不 匹配时会抛出异常。

问题:现在所有模型都支持"本章要讲解的结构化输出方式"吗?

大部分现代模型支持(通过函数调用):

  • ✅ OpenAI (gpt-4, gpt-3.5-turbo)
  • ✅ Anthropic (claude-3)
  • ✅ Groq (llama-3)
  • ❌ 某些旧模型不支持

如果不支持,LangChain 会回退到提示词 + JSON 解析。

32. 四种模式的使用

1. Pydantic

它通过在运行时强制执行类型提示,确保数据的正确性和一致性,是 生产场景首选 。

1. 基本使用
  • 所有结构化输出的数据模型都必须继承 BaseModel
  • 使用 类型提示 。Pydantic 支持丰富的字段类型:str 、int、float、Listxxx、Optionalxxx
  • 使用 Field() 添加字段默认值和描述,帮助 LLM 理解字段含义
jupyter 复制代码
# Pydantic格式的使用

## 1、基本使用

举例:

from langchain.chat_models import init_chat_model
from dotenv import load_dotenv
import os
# 从.env文件中加载环境变量
load_dotenv(override=True)
CLOSEAI_API_KEY = os.getenv("CLOSEAI_API_KEY")
CLOSEAI_BASE_URL = os.getenv("CLOSEAI_BASE_URL")
model = init_chat_model(
    model="gpt-5.4-mini",
    model_provider="openai",
    api_key=CLOSEAI_API_KEY,
    base_url=CLOSEAI_BASE_URL
)

from pydantic import BaseModel, Field
class Person(BaseModel):
    """人物信息"""
    name: str = Field(
        description="姓名"
    )
    age: int = Field(
        description="年龄"
    )
    occupation: str = Field(
        description="职业"
    )
# 创建结构化输出的大语言模型
structured_model = model.with_structured_output(Person)
# result = model.invoke("张三是一名30岁的软件工程师")
result = structured_model.invoke("张三是一名30岁的软件工程师")
print(result)
print(type(result))  # Person

print(f"姓名: {result.name}")
print(f"年龄: {result.age}")
print(f"职业: {result.occupation}")

举例2:

class MovieModel(BaseModel):
    """电影的详细信息"""
    title: str = Field(
        description="电影标题"
    )
    year: int= Field(
        description="发行年份"
    )
    actor: str= Field(
        description="电影导演"
    )
    rating: float= Field(
        description="电影评分,满分十分"
    )
structured_model = model.with_structured_output(MovieModel)
# model.invoke("给出电影盗梦空间的信息")
result = structured_model.invoke("给出电影盗梦空间的信息")
print(result)

举例3:

from pydantic import BaseModel, Field
# 定义输出结构
class SentimentAnalysis(BaseModel):
    """情感分析结果"""
    sentiment: str = Field(description="情感倾向:positive/negative/neutral")
    confidence: float = Field(description="置信度,0-1之间")
    keywords: list[str] = Field(description="关键词列表")
# ✅ v1.x:使用 with_structured_output
structured_model = model.with_structured_output(SentimentAnalysis)
# 调用
text = "这个课程内容很实用,学到了很多知识,强烈推荐!"
result = structured_model.invoke(
    f"分析以下文本的情感:\n{text}"
)
print(f"类型: {type(result)}") # <class 'SentimentAnalysis'>
print(f"情感: {result.sentiment}")
print(f"置信度: {result.confidence}")
print(f"关键词: {result.keywords}")
2. 高级特性
情况1:可选字段
jupyter 复制代码
## 2、高级特性

### 2.1 情况1:可选字段

举例

from pydantic import BaseModel, Field
class Person(BaseModel):
    """人物信息"""
    name: str = Field(
        description="姓名"
    )
    age: int = Field(
        description="年龄"
    )
    occupation: str = Field(
        description="职业"
    )
# 创建结构化输出的大语言模型
structured_model = model.with_structured_output(Person)
# result = structured_model.invoke("张三是一名30岁的软件工程师")
result = structured_model.invoke("张三是一名软件工程师")
print(result)  # age: 0
print(type(result))

作为对比

from pydantic import BaseModel, Field
from typing import Optional
class Person(BaseModel):
    """人物信息"""
    name: str = Field(
        description="姓名"
    )
    # age: int = Field(
    age: Optional[int] = Field(
        description="年龄"
    )
    occupation: str = Field(
        description="职业"
    )
# 创建结构化输出的大语言模型
structured_model = model.with_structured_output(Person)
result = structured_model.invoke("张三是一名软件工程师")
print(result)  # age: None
print(type(result))
情况2:默认值
jupyter 复制代码
### 2.2 情况2:默认值

不同的模型供应商,对于此字段的支持是不同的。比如:CloseAI平台的gpt-5.4-mini模型就不支持此字段,而OpenRouter平台的gpt-5.4-mini模型就支持此字段

CloseAI平台的模型:

from langchain.chat_models import init_chat_model
from dotenv import load_dotenv
import os
# 从.env文件中加载环境变量
load_dotenv(override=True)
CLOSEAI_API_KEY = os.getenv("CLOSEAI_API_KEY")
CLOSEAI_BASE_URL = os.getenv("CLOSEAI_BASE_URL")
model_with_closeai = init_chat_model(
    model="gpt-5.4-mini",
    model_provider="openai",
    api_key=CLOSEAI_API_KEY,
    base_url=CLOSEAI_BASE_URL
)

OpenRouter平台的模型:(1、需要充值  2、需要魔法)

from langchain_openrouter import ChatOpenRouter
from dotenv import load_dotenv
import os
load_dotenv(override=True)
OPENROUTER_API_KEY = os.getenv("OPENROUTER_API_KEY")
OPENROUTER_API_BASE = os.getenv("OPENROUTER_API_BASE")
model_with_openrouter = ChatOpenRouter(
    model="openai/gpt-5.4-mini",
    api_key=OPENROUTER_API_KEY,
    base_url=OPENROUTER_API_BASE,
)

举例1:使用CloseAI平台的模型

from pydantic import BaseModel, Field
# from typing import Optional
class Person(BaseModel):
    """人物信息"""
    name: str = Field(
        description="姓名"
    )
    # age: Optional[int] = Field(
    age: int = Field(
        # 10,
        default=10,
        description="年龄"
        # description="年龄(默认值为20)"
    )
    occupation: str = Field(
        description="职业"
    )
# 创建结构化输出的大语言模型
# structured_model = model.with_structured_output(Person)
structured_model = model_with_closeai.with_structured_output(Person)
result = structured_model.invoke("张三是一名软件工程师")
print(result)  # age: 0 -> age: 20 -> age: 0
print(type(result))

作为对比:使用OpenRouter平台的模型

from pydantic import BaseModel, Field
class Person(BaseModel):
    """人物信息"""
    name: str = Field(
        description="姓名"
    )
    age: int = Field(
        default=10,
        description="年龄"
    )
    occupation: str = Field(
        description="职业"
    )
# 创建结构化输出的大语言模型
# structured_model = model_with_closeai.with_structured_output(Person)
structured_model = model_with_openrouter.with_structured_output(Person)
result = structured_model.invoke("张三是一名软件工程师")
print(result)  # age: 10
print(type(result))

举例2:

class Config(BaseModel):
    timeout: Optional[int] = Field(30,description="超时时间(单位秒)")
    retry: bool = Field(False,description="是否支持重试")
    max_attempts: int = Field(6,description="最大重试次数")
# 测试
structured_llm = model_with_closeai.with_structured_output(Config)
structured_llm.invoke("配置要求:支持重试,最多重试5次")
# timeout: None

# 测试
structured_llm = model_with_openrouter.with_structured_output(Config)
structured_llm.invoke("配置要求:支持重试,最多重试5次")
# timeout: 30

举例3:

from typing import Optional
from pydantic import BaseModel, Field
class Product(BaseModel):
    """产品信息"""
    name: str = Field(description="产品名称")
    price: float = Field(description="价格")
    description: Optional[str] = Field(description="产品描述")
    stock: int = Field(default=100, description="库存")
# 测试
structured_llm = model_with_openrouter.with_structured_output(Product)
print("\n场景1:完整信息")
result1 = structured_llm.invoke("iPhone 15 售价 5999 元,最新款智能手机,库存 50 台")
print(result1)
print("\n场景2:缺少描述和库存")
result2 = structured_llm.invoke("MacBook Pro 售价 12999 元")
print(result2)  # description: None

2026.08.05 20:43

情况3:枚举类型
python 复制代码
from enum import Enum
class Priority(str, Enum):
    LOW = "低"
    MEDIUM = "中"
    HIGH = "高"
class Task(BaseModel):
    title: str
    priority: Priority # 只能是 LOW/MEDIUM/HIGH
python 复制代码
from enum import Enum
class Status(str, Enum):
    ACTIVE = "激活"
    INACTIVE = "未激活"
class User(BaseModel):
    status: Status # 只能是 ACTIVE 或 INACTIVE
jupyter 复制代码
### 2.3 情况3:枚举类型

举例:
方式1:

from enum import Enum
# 定义枚举类型
class Priority(str, Enum):
    LOW = "低"
    MEDIUM = "中"
    HIGH = "高"

class CustomerInfo(BaseModel):
    """客户信息"""
    name: str = Field(description="客户姓名")
    phone: str = Field(description="电话号码")
    email: Optional[str] = Field(description="邮箱")
    issue: str = Field(description="问题描述")
    urgency: Priority = Field(description="紧急程度")
# 测试
# structured_llm = model_with_openrouter.with_structured_output(CustomerInfo)
structured_llm = model.with_structured_output(CustomerInfo)
conversation = """
客服: 您好,请问有什么可以帮助您?
客户: 我是王小明,电话 138-1234-5678,我的订单一直没发货,很着急!
客服: 好的,我帮您查一下
"""
result = structured_llm.invoke(f"从以下客服对话中提取客户信息:\n{conversation}")
print(result)
# print("\n提取结果:")
# print(f" 客户: {result.name}")
# print(f" 电话: {result.phone}")
# print(f" 邮箱: {result.email or '未提供'}")
# print(f" 问题: {result.issue}")
# print(f" 紧急程度: {result.urgency.value}")

方式2:

from typing import Literal
# from enum import Enum
# 定义枚举类型
# class Priority(str, Enum):
#     LOW = "低"
#     MEDIUM = "中"
#     HIGH = "高"

class CustomerInfo(BaseModel):
    """客户信息"""
    name: str = Field(description="客户姓名")
    phone: str = Field(description="电话号码")
    email: Optional[str] = Field(description="邮箱")
    issue: str = Field(description="问题描述")
    # urgency: Priority = Field(description="紧急程度")
    urgency: Literal["低", "中", "高"] = Field(description="紧急程度")
# 测试
# structured_llm = model_with_openrouter.with_structured_output(CustomerInfo)
structured_llm = model.with_structured_output(CustomerInfo)
conversation = """
客服: 您好,请问有什么可以帮助您?
客户: 我是王小明,电话 138-1234-5678,我的订单一直没发货,很着急!
客服: 好的,我帮您查一下
"""
result = structured_llm.invoke(f"从以下客服对话中提取客户信息:\n{conversation}")
print(result)
情况4:列表提取
jupyter 复制代码
### 2.4 情况4:列表提取

举例1:

from typing import List
class Person(BaseModel):
    """任务信息"""
    name: str = Field(descriptions="姓名")
    age: int = Field(descriptions="年龄")
class PersonList(BaseModel):
    """人物列表"""
    people: List[Person]  # 多个Person的对象
# model.with_structured_output(Person)
structured_model = model.with_structured_output(PersonList)
result = structured_model.invoke("张三 30岁,李四 40岁")
print(result)

举例2:

class Review(BaseModel):
    """产品评论"""
    product: str
    rating: int = Field(description="评分 1-5")
    pros: List[str] = Field(description="优点列表")
    cons: List[str] = Field(description="缺点列表")
structured_llm = model.with_structured_output(Review)
review = structured_llm.invoke("""
iPhone 17 很棒!摄像头强大,手感好。但是价格贵,没有充电器。4分。
""")
print(review)

举例3:

class Invoice(BaseModel):
    """发票信息"""
    invoice_number: str = Field(description="发票号")
    date: str = Field(description="日期")
    total_amount: float = Field(description="总金额")
    items: List[str] = Field(description="商品")
# 测试
structured_llm = model.with_structured_output(Invoice)
invoice_text = """
发票号: INV-2024-001
日期: 2024-01-15
总金额: 1299.00
商品: MacBook Pro, AppleCare+
"""
invoice = structured_llm.invoke(f"提取发票信息:{invoice_text}")
print(invoice)
情况5:嵌套结构
jupyter 复制代码
### 2.5 情况5:嵌套结构

举例1:

class Address(BaseModel):
    """地点描述"""
    city: str = Field(description="城市")
    district: str = Field(description="区域")
class Company(BaseModel):
    """公司信息"""
    name: str = Field(description="公司名称")
    address: Address = Field(description="公司所在地")
structured_model = model.with_structured_output(Company)
# model.invoke("阿里巴巴在杭州的滨江区")
result = structured_model.invoke("阿里巴巴在杭州的滨江区")
print(result)

举例2:

from pydantic import BaseModel, Field
from typing import List
# 1. 定义嵌套的 Pydantic 模型
class Actor(BaseModel):
    """演员信息"""
    name: str = Field(description="演员姓名")
    role: str = Field(description="饰演的角色")
class Movie(BaseModel):
    """电影信息"""
    title: str = Field(description="电影标题")
    year: int = Field(description="上映年份")
    director: str = Field(description="导演")
    cast: List[Actor] = Field(description="演员列表") # 定义列表字段
    rating: float = Field(description="评分")
# 2. 初始化模型并绑定输出结构
structured_model = model.with_structured_output(Movie)
# 3. 调用模型,直接获取 Movie 实例
response = structured_model.invoke("请介绍电影《盗梦空间》")
# 4. 访问嵌套数据
print(f"电影名: {response.title}")
print(f"上映年份: {response.year}")
print(f"导演: {response.director}")
print(f"演员列表: {response.cast}")
print(f"评分: {response.rating}")

举例3:

from pydantic import BaseModel
from typing import List
class Aspect(BaseModel):
    """评论维度"""
    name: str = Field(description="维度名称,如:质量、价格、服务")
    score: int = Field(description="评分,1-5")
    comment: str = Field(description="具体评价")
class ProductReview(BaseModel):
    """产品评论分析"""
    overall_sentiment: str = Field(description="整体情感:positive/negative/neutral")
    overall_score: int = Field(description="综合评分,1-5")
    aspects: List[Aspect] = Field(description="各维度评价")
    summary: str = Field(description="一句话总结")
# 创建结构化模型
structured_model = model.with_structured_output(ProductReview)
# 测试
review_text = """
这款笔记本电脑性能非常强大,运行大型软件毫无压力。
屏幕色彩鲜艳,看视频很舒服。
不过价格有点贵,而且风扇噪音较大。
客服态度很好,物流也快。
总体来说还是值得购买的。
"""
result = structured_model.invoke(
    f"分析以下产品评论:\n{review_text}"
)
print(f"整体情感: {result.overall_sentiment}")
print(f"综合评分: {result.overall_score}/5")
print(f"\n各维度评价:")
for aspect in result.aspects:
    print(f" - {aspect.name}: {aspect.score}/5 - {aspect.comment}")
print(f"\n总结: {result.summary}")

说明:LLM 能力有限,复杂嵌套结构可能会出错。所以建议:

  • 嵌套层级 ≤ 3 层
python 复制代码
class Bad(BaseModel):
    user: User
        company: Company
            address: Address
                country: Country # 4 层嵌套,容易出错
  • 使用清晰的 description
  • 必要时拆分成多个调用
情况6:限制情况
jupyter 复制代码
### 2.6 情况6:限制情况

from pydantic import ValidationError
class User(BaseModel):
    name: str = Field(description="姓名", min_length=2, max_length=50)
    age: int = Field(description="年龄", le=150)  # <= 150
    email: str = Field(description="邮箱")
# user1 = User(name="tom", age=20, email="tom@126.com")
# print(user1)
try:
    # user2 = User(name="tom", age=200, email="tom@126.com")
    user1 = User(name="tom", age=20, email="tom@126.com")
    print(f"[OK]{user1}")
except ValidationError as e:
    print(f"[FAIL]{e}")
    
from pydantic import ValidationError
try:
    user2 = User(name="tom", age=200, email="tom@126.com")  # 运行报错 age太大
    # print(user2)
    print(f"[OK]{user2}")
except ValidationError as e:
    print(f"[FAIL]{e}")
    
举例1:

使用CloseAI平台的模型

class Product(BaseModel):
    """产品信息(严格验证)"""
    name: str = Field(description="产品名称(字符串类型)", min_length=2)
    price: float = Field(description="价格,数字类型", gt=0)  # >0
    stock: int = Field(description="库存,整数类型", ge=0)  # >=0
structured_model = model_with_closeai.with_structured_output(Product)
# structured_model.invoke("华为mate 80 promax 价格是-7999,当前库存-100")
result = structured_model.invoke("华为mate 80 promax 价格是7999,当前库存100")
print(result)

# result = structured_model.invoke("华为mate 80 promax 价格是7999,当前库存100")
result = structured_model.invoke("华为mate 80 promax 价格是-7999,当前库存-100")
print(result)  # price=7999.0 stock=100

使用OpenRouter平台的模型

class Product(BaseModel):
    """产品信息(严格验证)"""
    name: str = Field(description="产品名称(字符串类型)", min_length=2)
    price: float = Field(description="价格,数字类型", gt=0)  # >0
    stock: int = Field(description="库存,整数类型", ge=0)  # >=0
# structured_model = model_with_closeai.with_structured_output(Product)
structured_model = model_with_openrouter.with_structured_output(Product)
result = structured_model.invoke("华为mate 80 promax 价格是7999,当前库存100")
print(result)

# result = structured_model.invoke("华为mate 80 promax 价格是7999,当前库存100")
result = structured_model.invoke("华为mate 80 promax 价格是-7999,当前库存-100")
print(result)  # price=1.0 stock=0
3. 工作流程图解
graph TD 1[Pydantic BookInfo模型] -->2[生成JSON Schema] 2 -->3[LLM大模型] 用户提示词 -->3 3 -->4[输出JSON字符串] 4 -->5{Pydantic校验} 5 --失败-->3 5 --成功-->6[BookInfo实例对象]

2. TypedDict

1. 什么是TypedDict

TypedDict 是 Python 3.8+ 引入的一种类型提示工具,即带有类型声明的字典结构。适合需要快速定义字典结构且无需 Pydantic 重量级功能的场景。

1、普通 dict 没有类型信息

2、TypedDict 可以进一步说明:

  • 这个字典应该有哪些字段
  • 每个字段的类型是什么
  • TypedDict 主要是类型声明,不是运行时强校验器。
2. 基本使用

Annotated 用来在"类型"之外,再附加一些额外信息,即元数据。类似于 Pydantic 的 Field 。

基本形式:

Annotated类型, 附加信息1, 附加信息2, ...

jupyter 复制代码
## 2、基本使用

举例1:

CloseAI平台的模型

from langchain.chat_models import init_chat_model
from dotenv import load_dotenv
import os
# 从.env文件中加载环境变量
load_dotenv(override=True)
CLOSEAI_API_KEY = os.getenv("CLOSEAI_API_KEY")
CLOSEAI_BASE_URL = os.getenv("CLOSEAI_BASE_URL")
model_with_closeai = init_chat_model(
    model="gpt-5.4-mini",
    model_provider="openai",
    api_key=CLOSEAI_API_KEY,
    base_url=CLOSEAI_BASE_URL
)

OpenRouter平台的模型:

from langchain_openrouter import ChatOpenRouter
from dotenv import load_dotenv
import os
load_dotenv(override=True)
OPENROUTER_API_KEY = os.getenv("OPENROUTER_API_KEY")
OPENROUTER_API_BASE = os.getenv("OPENROUTER_API_BASE")
model_with_openrouter = ChatOpenRouter(
    model="openai/gpt-5.4-mini",
    api_key=OPENROUTER_API_KEY,
    base_url=OPENROUTER_API_BASE,
)

from typing_extensions import Annotated
class MoveTypedDict(TypedDict):
    """电影的信息"""
    title: Annotated[str, "电影的名称"]
    year: Annotated[int, "电影的上映时间,四位数"]
    director: Annotated[str, "电影的导演"]
    rating: Annotated[float, "电影的评分,满分是10分,可以包含一位小数"]
# structured_model = model_with_closeai.with_structured_output(xx)
structured_model = model_with_closeai.with_structured_output(MoveTypedDict)
response = structured_model.invoke("给我介绍一下电影《星际穿越》")
print(response)
print(type(response))  # dict

举例2:嵌套结构的使用

from typing import List
class Actor(TypedDict):
    """演员的信息"""
    name: Annotated[str, "演员的名字"]
    role: Annotated[str, "扮演的角色"]
class MoveTypedDict(TypedDict):
    """电影的信息"""
    title: Annotated[str, "电影的名称"]
    year: Annotated[int, "电影的上映时间,四位数"]
    director: Annotated[str, "电影的导演"]
    rating: Annotated[float, "电影的评分,满分是10分,可以包含一位小数"]
    cast: Annotated[List[Actor], "演员的列表"]  # 嵌套列表的结构
structured_model = model_with_closeai.with_structured_output(MoveTypedDict)
response = structured_model.invoke("给我介绍一下电影《星际穿越》")
print(response)
print(type(response))  # dict

举例3:... 的使用(意思是必填)

不同的魔心提供商对应的效果不同

以CloseAI平台为例:(失效了)

# class MoveTypedDict(TypedDict):
class MoveDict(TypedDict):
    """电影的信息"""
    # title: Annotated[str, "电影的名称"]
    # title: Annotated[str, "电影的名称"]
    # year: Annotated[int, "电影的上映时间,四位数"]
    # director: Annotated[str, "电影的导演"]
    # rating: Annotated[float, "电影的评分,满分是10分,可以包含一位小数"]
    title: Annotated[str, ..., "电影的名称"]
    title: Annotated[str, ..., "电影的名称"]
    year: Annotated[int, ..., "电影的上映时间,四位数"]
    director: Annotated[str, ..., "电影的导演"]
    rating: Annotated[float, ..., "电影的评分,满分是10分,可以包含一位小数"]
# structured_model = model_with_closeai.with_structured_output(MoveTypedDict)
# response = structured_model.invoke("给我介绍一下电影《星际穿越》")
structured_model = model_with_closeai.with_structured_output(MoveDict)
response = structured_model.invoke("根据这段话抽取盗梦空间的信息,不包含的信息可以留空:盗梦空间在2010年上映,导演是克里斯托弗·诺兰。")
print(response)

以OpenRouter平台为例

# class MoveTypedDict(TypedDict):
class MoveDict(TypedDict):
    """电影的信息"""
    title: Annotated[str, ..., "电影的名称"]
    title: Annotated[str, ..., "电影的名称"]
    year: Annotated[int, ..., "电影的上映时间,四位数"]
    director: Annotated[str, ..., "电影的导演"]
    rating: Annotated[float, ..., "电影的评分,满分是10分,可以包含一位小数"]
# structured_model = model_with_closeai.with_structured_output(MoveDict)
structured_model = model_with_openrouter.with_structured_output(MoveDict)
response = structured_model.invoke("根据这段话抽取盗梦空间的信息,不包含的信息可以留空:盗梦空间在2010年上映,导演是克里斯托弗·诺兰。")
print(response)  # rating: 0

说明:上述代码的 ... 是Python的字面量,等价于 Ellipsis ,可以理解为占位符。下游框架(如 LangChain)可以对 ... 作定制化处理,如LangChain中Annotated的 ... 表示当前字段是必须存在的, 不可省略,

相关推荐
Zldaisy3d1 小时前
连续纤维增材制造的机翼已飞上天,复材打印在低空飞行器上还需翻过几道坎?
java·前端·数据库
喜欢睡觉1 小时前
从"白一下"到"丝滑切换"——前端路由的进化史
前端
玉宇夕落1 小时前
React 多种路由学习:HashRouter 源码级剖析
前端
他们叫我秃子1 小时前
前端开发转 Go 全栈(三):从函数到 error,Go 连“失败”都要明确返回
前端·后端·go
Canace1 小时前
GPT-5.6 到底怎么选?一文搞懂 Sol、Terra、Luna 和 Ultra
前端·人工智能·chatgpt
用户059540174461 小时前
LangChain Memory 踩坑实录:10种异常场景差点搞崩生产,我们写了一套自动化测试保命
前端·css
名字还没想好☜1 小时前
React useImperativeHandle 实战:让父组件安全地调用子组件的方法
前端·javascript·react.js·react·forwardref
CodeSheep1 小时前
FFmpeg 9.0正式发布:代号“Lei”,以纪念中国开发者雷霄骅
前端·后端·程序员
IT_陈寒1 小时前
小心!Java里的这个空指针问题绝对坑过你
前端·人工智能·后端