跳到主要内容
LangChain 工具调用与结构化输出实战 | 极客日志
Python AI
LangChain 工具调用与结构化输出实战 综述由AI生成 深入探讨了 LangChain 中工具调用与结构化输出的核心用法。工具调用部分涵盖了三种创建方式(装饰器、Pydantic 参数、BaseTool 类),演示了如何绑定本地及第三方工具(如 Tavily 搜索)并处理多轮调用。结构化输出部分对比了 Pydantic、TypedDict 和 JSON Schema 三种方案,重点介绍了如何通过动态类型选择实现灵活响应。文章最后结合简历解析、意图识别及智能助手三个实际场景,展示了如何将两者结合以实现从聊天到执行的转变,为构建自动化 AI 应用提供了实战指南。
无尘 发布于 2026/3/21 更新于 2026/5/12 11 浏览LangChain 工具调用与结构化输出实战
工具调用(Tool Calling)是 LangChain 的核心功能之一,它允许 AI 模型调用外部函数或 API 来完成特定任务。比如获取实时天气或查询数据库,LLM 本身无法直接获取这些信息,但借助工具就能通过外部服务完成。
![工具调用流程示意图]
工具创建的三种方式
直接用 @tool 装饰函数
最简单的方式,适合轻量级的小工具。
from langchain_core.tools import tool
@tool
def add (a: int , b: int ) -> int :
return a + b
用 @tool + 自定义参数结构(Pydantic)
这种方式参数更清晰,能写详细说明,推荐用于需要明确输入约束的场景。
from pydantic import BaseModel, Field
from langchain_core.tools import tool
class AddInput (BaseModel ):
a: int = Field(description="第一个数字" )
b: int = Field(description="第二个数字" )
@tool(args_schema=AddInput )
def add (a: int , b: int ) -> int :
return a + b
继承 BaseTool 写类
最灵活,适合复杂逻辑或异步操作(比如调用第三方 API)。
from langchain_core.tools import BaseTool
class AddTool (BaseTool ):
name: str =
description: =
( ) -> :
a + b
"add"
str
"计算两个整数的和"
def
_run
self, a: int , b: int
int
return
本地自定义工具实战 我们来看一个完整的本地工具流程,从定义到绑定再到执行。
1. 定义工具 使用 @tool 装饰器标记函数,记得加上类型注解和文档字符串,这有助于模型理解工具用途。
from langchain_core.tools import tool
@tool
def sum_to_n (n: int ) -> int :
"""计算从 0 累加到 n 的结果"""
total = 0
for i in range (n + 1 ):
total += i
return total
2. 绑定工具到模型 通过 bind_tools() 方法将工具绑定到模型实例。如果需要强制模型调用工具,可以设置 tool_choice="any"。
from langchain.chat_models import init_chat_model
llm = init_chat_model(
model="deepseek-chat" ,
model_provider="deepseek" ,
api_key="your-api-key" ,
temperature=0.7 ,
max_tokens=8192 ,
)
bound_llm = llm.bind_tools([sum_to_n])
3. 工具调用流程 模型会先分析用户意图,决定是否调用工具。如果决定调用,我们需要手动执行工具并将结果反馈给模型。
from langchain_core.messages import HumanMessage, SystemMessage, ToolMessage
messages = [
SystemMessage(content="你是一个数学助手。当用户给你一个数字 n 时,使用 sum_to_n 工具计算从 0 累加到 n 的结果。" ),
HumanMessage(content="10" ),
]
ai_message = bound_llm.invoke(messages)
messages.append(ai_message)
if ai_message.tool_calls:
for tool_call in ai_message.tool_calls:
if tool_call["name" ] == "sum_to_n" :
result = sum_to_n.invoke(tool_call["args" ])
messages.append(
ToolMessage(content=str (result), tool_call_id=tool_call["id" ])
)
final_msg = bound_llm.invoke(messages).content
print (final_msg)
工作流程: 用户输入 → AI 分析 → 决定调用工具 → 执行工具 → 返回结果 → AI 生成最终回答。
4. AI 响应结构解析 当模型决定调用工具时,ai_message 会包含关键信息,注意 tool_calls 列表里包含了工具名称、参数和调用 ID。
{
'content' : '我来计算从 0 累加到 10 的结果。' ,
'tool_calls' : [
{
'name' : 'sum_to_n' ,
'args' : {'n' : 10 },
'id' : 'call_00_xxx' ,
'type' : 'tool_call'
}
],
'response_metadata' : {
'token_usage' : {...},
'model_name' : 'deepseek-chat' ,
'finish_reason' : 'tool_calls'
}
}
第三方工具集成(Tavily 搜索) 集成第三方工具通常需要提供 API Key,并且要注意参数名的准确性。
1. 集成第三方工具 from langchain_tavily import TavilySearch
tavily_tool = TavilySearch(
max_results=4 ,
tavily_api_key="your-tavily-api-key"
)
bound_llm = llm.bind_tools([tavily_tool])
2. 多轮工具调用 有时模型可能需要多次调用工具才能拿到满意的结果,我们可以用一个循环来处理。
from langchain_core.messages import HumanMessage, SystemMessage, ToolMessage
messages = [
SystemMessage(content="你是一个天气助手。使用 tavily_search 工具搜索天气情况。" ),
HumanMessage(content="2026 年 3 月 2 日北京天气情况" ),
]
max_rounds = 3
for round_num in range (1 , max_rounds + 1 ):
ai_message = bound_llm.invoke(messages)
messages.append(ai_message)
if not ai_message.tool_calls:
print (ai_message.content)
break
for tool_call in ai_message.tool_calls:
result = tavily_tool.invoke(tool_call["args" ])
tool_message = ToolMessage(
content=str (result), tool_call_id=tool_call["id" ]
)
messages.append(tool_message)
3. 实际输出示例
白天天气:阴天,大部分地区有小雪或雨夹雪,北风转南风 2-3 级,最高气温 5-6℃。
夜间天气:阴天,山区有小雪,最低气温 -1℃到 0℃。
结构化输出(Structured Output) 结构化输出允许模型返回符合特定格式的数据,而不是纯文本。这对于数据提取、API 响应生成等场景非常有用。
1. Pydantic BaseModel(推荐)
定义 Pydantic 模型 使用 Field() 添加字段描述,帮助模型理解每个字段的含义。
from pydantic import BaseModel, Field
class TestOutput (BaseModel ):
"""城市信息的 Pydantic 模型"""
weather: str = Field(description="这个城市现在的详细天气情况,包括天气状况、温度、风力等" )
city_name: str = Field(description="城市名称" )
famous_person: str = Field(description="在这个城市出生的一个著名名人,包括简短介绍" )
绑定结构化输出 from langchain_deepseek import ChatDeepSeek
llm_deepseek = ChatDeepSeek(
model_name="deepseek-chat" ,
api_key="your-api-key" ,
base_url="https://api.deepseek.cn/v1" ,
)
struct_output_model = llm_deepseek.with_structured_output(TestOutput)
result = struct_output_model.invoke("上海" )
print (f"天气:{result.weather} " )
print (f"城市:{result.city_name} " )
print (f"名人:{result.famous_person} " )
优点: 类型安全,IDE 有完整代码提示,自动数据验证,支持复杂的嵌套结构。
2. TypedDict from typing import TypedDict, Annotated
class TestOutputDict (TypedDict ):
"""城市信息的字典类型"""
weather: Annotated[str , "这个城市现在的详细天气情况" ]
city_name: Annotated[str , "城市名称" ]
population: Annotated[int , "城市人口数量(万人)" ]
struct_output_model_dict = llm_deepseek.with_structured_output(TestOutputDict)
result_dict = struct_output_model_dict.invoke("上海" )
缺点: 类型检查较弱,不支持 Field() 的高级功能。
3. JSON Schema import json
json_schema = {
"title" : "CityInfo" ,
"description" : "城市信息的 JSON 格式" ,
"type" : "object" ,
"properties" : {
"city" : {"type" : "string" , "description" : "城市名称" },
"weather" : {"type" : "string" , "description" : "当前天气情况" },
"attractions" : {
"type" : "array" ,
"description" : "城市的著名景点列表" ,
"items" : {"type" : "string" }
},
"gdp" : {"type" : "number" , "description" : "城市 GDP(亿元)" }
},
"required" : ["city" , "weather" , "attractions" , "gdp" ]
}
struct_output_model_json = llm_deepseek.with_structured_output(json_schema)
result_json = struct_output_model_json.invoke("北京" )
print (json.dumps(result_json, ensure_ascii=False , indent=2 ))
4. 可选结构化输出(动态类型选择) 当你需要模型根据用户输入自动选择 返回不同类型的结构化数据时,可以使用此模式。
from pydantic import BaseModel, Field
from typing import Literal
class PersonInfo (BaseModel ):
name: str = Field(description="姓名" )
age: int = Field(description="年龄" )
class CityInfo (BaseModel ):
city: str = Field(description="城市名称" )
population: int = Field(description="人口(万人)" )
class NormalAnswer (BaseModel ):
answer: str = Field(description="根据用户的提问正常答复的内容" )
class Response (BaseModel ):
"""响应结构,模型根据问题选择返回人物、城市信息或普通回答"""
type : Literal ["person" , "city" , "normal" ] = Field(
description="响应类型:person 表示人物,city 表示城市,normal 表示普通回答"
)
person: PersonInfo | None = Field(default=None , description="人物信息" )
city: CityInfo | None = Field(default=None , description="城市信息" )
normal: NormalAnswer | None = Field(default=None , description="普通回答" )
model = llm_deepseek.with_structured_output(Response)
result1 = model.invoke("上海" )
if result1.type == "city" :
print (f"城市:{result1.city.city} " )
三大实际应用场景
场景 1:作为信息提取器 from pydantic import BaseModel, Field
class Education (BaseModel ):
school: str = Field(description="学校名称" )
degree: str = Field(description="学位" )
class ResumeInfo (BaseModel ):
name: str = Field(description="姓名" )
phone: str = Field(description="电话" )
education: list [Education] = Field(description="教育经历列表" )
model = llm.with_structured_output(ResumeInfo)
resume_text = "张三,电话:138****1234..."
result = model.invoke(f"请从以下简历中提取信息:\n{resume_text} " )
场景 2:作为提示词增强 帮助 AI 更好理解用户意图,将模糊需求转换为明确指令。
from pydantic import BaseModel, Field
from typing import Literal
class SearchIntent (BaseModel ):
intent_type: Literal ["informational" , "transactional" , "navigational" ] = Field(
description="意图类型"
)
keywords: list [str ] = Field(description="提取的关键词列表" )
model = llm.with_structured_output(SearchIntent)
user_query = "我想买个性价比高的笔记本电脑,预算 5000 左右"
intent = model.invoke(f"分析以下用户需求:{user_query} " )
场景 3:与 Tool 联合使用 结构化输出 + 工具调用的完美组合,实现智能决策 + 精准执行。
from langchain_tavily import TavilySearch
from pydantic import BaseModel, Field
class WeatherResult (BaseModel ):
location: str = Field(description="地点" )
temperature: str = Field(description="温度范围" )
weather: str = Field(description="天气状况" )
tavily_tool = TavilySearch(max_results=4 , tavily_api_key="your-key" )
bound_llm = llm.bind_tools([tavily_tool])
structured_llm = bound_llm.with_structured_output(WeatherResult)
result = structured_llm.invoke("2026 年 3 月 2 日北京天气情况" )
print (f"地点:{result.location} " )
总结与建议 场景 核心价值 技术特点 信息提取器 非结构化 → 结构化 单向转换、数据标准化 提示词增强 模糊意图 → 明确指令 双向交互、意图明确化 Tool 联合使用 智能决策 + 精准执行 多步骤、工具协作
需要类型灵活切换 → 使用可选结构化输出。
固定的数据提取 → 使用单一 Pydantic 模型。
复杂嵌套结构 → 使用 JSON Schema 或嵌套 Pydantic 模型。
简单键值对 → 使用 TypedDict。
参考资源 相关免费在线工具 RSA密钥对生成器 生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
Mermaid 预览与可视化编辑 基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
随机西班牙地址生成器 随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online
curl 转代码 解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online