跳到主要内容LangChain 实战:工具调用与结构化输出 | 极客日志PythonAI
LangChain 实战:工具调用与结构化输出
本文详解 LangChain 中工具调用与结构化输出的核心用法。内容涵盖工具创建的三种方式(装饰器、Pydantic、BaseTool)、本地与第三方工具集成流程、以及 Pydantic、TypedDict、JSON Schema 等多种结构化输出方案。重点介绍了在信息提取、意图理解和智能助手三大场景下的实战代码,展示了如何让 AI 从单纯聊天转变为具备执行能力的智能体。通过对比不同方案的优缺点,为开发者提供选型建议,确保数据处理的准确性与规范性。
RedisGeek0 浏览 LangChain 实战:工具调用与结构化输出
工具调用是 LangChain 的核心功能之一,它允许 AI 模型调用外部函数或 API 来完成特定任务。简单来说,就是让大模型从'只会聊天'变成'能干活'。
比如获取实时天气,LLM 本身没有联网能力,这时候就需要借助工具去搜索;或者查询数据库表数据,也需要通过工具交互。下面咱们一步步拆解怎么实现。
工具创建方式
LangChain 提供了三种主要方式来定义工具,根据场景复杂度选择即可。
1. 直接用 @tool 装饰函数
最简单,适合逻辑简单的小工具。
from langchain_core.tools import tool
@tool
def add(a: int, b: int) -> int:
"""计算两个数的和"""
return a + b
2. 用 @tool + Pydantic 自定义参数结构
参数更清晰,文档字符串会被模型用来理解工具功能。
from pydantic import BaseModel
class AddInput(BaseModel):
a: int
b: int
@tool(args_schema=AddInput)
def add(a: int, b: int) -> int:
return a + b
3. 继承 BaseTool 写类
最灵活,适合复杂逻辑或异步操作(比如调 HTTP API)。
from langchain_core.tools import BaseTool
class AddTool(BaseTool):
name = "add_tool"
description = "计算两个数的和"
def ():
a + b
_run
self, a: int, b: int
return
本地自定义工具实战
这里以累加求和为例,演示完整的工具绑定与调用流程。
定义工具
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
注意: 函数必须有类型注解,文档字符串(docstring)对模型理解至关重要。
绑定工具到模型
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])
如果想强制模型调用工具,可以在 bind 时设置 tool_choice="any"。
工具调用流程
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)
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_message 是经过框架整合的响应。如果模型决定调用工具,tool_calls 列表会包含名称、参数和 ID。必须使用 ToolMessage 包装结果并传入 tool_call_id,否则模型无法关联上下文。
第三方工具集成
以 Tavily 搜索为例,演示如何接入外部服务。
集成与多轮调用
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])
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)
实际运行时,模型可能会先搜索,拿到结果后再分析生成回答,这就是多轮工具调用的价值。
结构化输出
结构化输出允许模型返回符合特定格式的数据,而不是纯文本。这对于数据存储、API 响应生成非常有用。
1. Pydantic BaseModel(推荐)
定义模型
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}")
优点: IDE 有完整提示,自动校验数据,支持嵌套结构。
2. TypedDict
from typing import TypedDict, Annotated
class TestOutputDict(TypedDict):
weather: Annotated[str, "详细天气情况"]
city_name: Annotated[str, "城市名称"]
注意: TypedDict 不支持 Field(),需用 Annotated 添加描述。类型检查相对较弱。
3. JSON Schema
import json
json_schema = {
"title": "CityInfo",
"description": "城市信息",
"type": "object",
"properties": {
"city": {"type": "string", "description": "城市名称"},
"weather": {"type": "string", "description": "当前天气"},
"attractions": {"type": "array", "items": {"type": "string"}}
},
"required": ["city", "weather"]
}
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 typing import Literal
class PersonInfo(BaseModel):
name: str
age: int
class CityInfo(BaseModel):
city: str
population: int
class Response(BaseModel):
type: Literal["person", "city"] = Field(description="响应类型")
person: PersonInfo | None = Field(default=None)
city: CityInfo | None = Field(default=None)
model = llm.with_structured_output(Response)
result = model.invoke("上海人口多少?")
if result.type == "city":
print(result.city)
三大实际应用场景
场景 1:信息提取器
from pydantic import BaseModel, Field
class Education(BaseModel):
school: str
degree: str
major: str
class ResumeInfo(BaseModel):
name: str
phone: str
education: list[Education]
skills: list[str]
model = llm.with_structured_output(ResumeInfo)
resume_text = "张三,电话:138****1234...教育背景:清华大学..."
result = model.invoke(f"请从以下简历中提取信息:\n{resume_text}")
print(f"姓名:{result.name}")
print(f"技能:{', '.join(result.skills)}")
场景 2:提示词增强
帮助 AI 更好理解用户意图,将模糊需求转换为明确指令。
from typing import Literal
class SearchIntent(BaseModel):
intent_type: Literal["informational", "transactional"]
keywords: list[str]
filters: dict[str, str]
model = llm.with_structured_output(SearchIntent)
user_query = "我想买个性价比高的笔记本电脑,预算 5000 左右"
intent = model.invoke(f"分析以下用户需求:{user_query}")
if intent.intent_type == "transactional":
search_params = {"category": "笔记本电脑", "price_range": intent.filters.get("price_range")}
场景 3:工具调用 + 结构化输出组合
from langchain_tavily import TavilySearch
class WeatherResult(BaseModel):
location: str
temperature: str
weather: str
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}, 温度:{result.temperature}")
总结对比
| 场景 | 核心价值 | 技术特点 |
|---|
| 信息提取器 | 非结构化 → 结构化 | 单向转换,数据标准化 |
| 提示词增强 | 模糊意图 → 明确指令 | 双向交互,意图明确化 |
| Tool 联合使用 | 智能决策 + 精准执行 | 多步骤,工具协作 |
- 需要类型灵活切换 → 可选结构化输出
- 固定数据提取 → Pydantic 模型
- 复杂嵌套结构 → JSON Schema 或嵌套 Pydantic
- 简单键值对 → TypedDict
相关免费在线工具
- RSA密钥对生成器
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
- Mermaid 预览与可视化编辑
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
- curl 转代码
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
- Markdown转HTML
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online