【LangChain系列三】聊天模型中:工具调用,让大模型拥有超能力
文章目录
【LangChain系列三】聊天模型中:工具调用,让大模型拥有超能力
大模型再聪明,也有两个硬伤:知识有截止日期,没法直接调外部 API。工具调用就是解决这个问题的。
聊天模型的局限
你问 ChatGPT “今天上海天气怎么样”,它答不上来——因为它没有联网能力。你让它帮你查数据库,它也做不到——因为它只是个语言模型,没有手。
工具调用(Tool Calling)的本质就是给大模型装上"手"。模型不直接执行操作,而是输出一段结构化的调用指令,由外部程序去执行。
整个流程分三步:定义工具 → 绑定工具 → 调用工具。
用 @tool 装饰器创建工具
最简单的方式,一个装饰器搞定:
from langchain_core.tools import tool
@tool
def search_weather(city: str) -> str:
"""查询指定城市的当前天气信息"""
# 这里调用真实天气 API
return f"{city}今天晴,25°C"
三个要素缺一不可:
- 函数名
search_weather→ 工具名称,模型通过这个名字引用工具 - 文档字符串
"查询指定城市的当前天气信息"→ 工具描述,告诉模型这个工具能干嘛 - 类型提示
city: str→ 参数说明,告诉模型怎么填参数
LangChain 会自动从这些信息生成 JSON Schema 交给模型。模型看到的大概长这样:
{
"name": "search_weather",
"description": "查询指定城市的当前天气信息",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string"}
},
"required": ["city"]
}
}
三种定义工具的方式
方式一:@tool 装饰器(推荐)
@tool
def multiply(a: int, b: int) -> int:
"""将两个整数相乘"""
return a * b
简单直接,大多数场景够用。
方式二:Pydantic 类
from pydantic import BaseModel, Field
class MultiplyTool(BaseModel):
"""将两个整数相乘"""
a: int = Field(description="第一个乘数")
b: int = Field(description="第二个乘数")
# 转成工具
from langchain_core.tools import StructuredTool
multiply = StructuredTool.from_function(
func=lambda a, b: a * b,
args_schema=MultiplyTool
)
Pydantic 的好处是参数描述更精确,适合参数复杂的工具。
方式三:Annotated 注解
from typing import Annotated
@tool
def divide(
a: Annotated[float, "被除数"],
b: Annotated[float, "除数,不能为0"]
) -> float:
"""两个数相除"""
return a / b
直接在类型注解里写描述,比文档字符串更贴近参数。
工具的 content 和 artifact
有些工具执行后既要给模型看结果,又要给后续组件留数据。用 response_format="content_and_artifact" 来处理:
@tool(response_format="content_and_artifact")
def get_stock_price(symbol: str) -> tuple:
"""查询股票实时价格"""
# 模拟查询
price = 150.25
detail = {"symbol": symbol, "price": price, "volume": 1000000}
return f"{symbol}当前价格:{price}元", detail
返回的元组里,第一个元素是 content(给模型看的文本),第二个是 artifact(留给后续处理的原始数据)。
绑定工具到模型
创建好工具后,绑定到模型上:
from langchain_deepseek import ChatDeepSeek
llm = ChatDeepSeek(model="deepseek-chat", temperature=0)
llm_with_tools = llm.bind_tools([search_weather, multiply])
绑定后模型就知道自己有哪些工具可以用。调用时模型会自己判断要不要用工具、用哪个工具。
调用工具
单工具调用
response = llm_with_tools.invoke("北京今天天气怎么样?")
# 检查模型是否要调用工具
if response.tool_calls:
for tc in response.tool_calls:
print(f"工具:{tc['name']}")
print(f"参数:{tc['args']}")
print(f"ID:{tc['id']}")
response.tool_calls 是一个列表,每个元素包含 name、args、id 三个字段。
拿到工具调用指令后,你需要自己执行并把结果喂回去:
from langchain_core.messages import ToolMessage
# 执行工具
tool_result = search_weather.invoke(tc['args'])
# 把结果发回模型
messages = [
response,
ToolMessage(content=tool_result, tool_call_id=tc['id'])
]
final_response = llm_with_tools.invoke(messages)
多工具调用
模型可能同时调用多个工具,遍历 tool_calls 就行:
messages = [response]
for tc in response.tool_calls:
# 根据工具名找到对应的函数
tool_fn = {"search_weather": search_weather, "multiply": multiply}[tc['name']]
result = tool_fn.invoke(tc['args'])
messages.append(ToolMessage(content=str(result), tool_call_id=tc['id']))
final = llm_with_tools.invoke(messages)
工具选择策略
默认情况下,模型自己决定用不用工具。你也可以强制:
# 强制使用工具(任意一个)
llm.bind_tools([search_weather], tool_choice="any")
# 强制使用特定工具
llm.bind_tools([search_weather], tool_choice="search_weather")
# 禁止使用工具
llm.bind_tools([search_weather], tool_choice="none")
LangChain 内置工具
LangChain 提供了不少现成工具,以 Tavily 搜索为例:
from langchain_community.tools import TavilySearchResults
search = TavilySearchResults(max_results=3)
llm_with_search = llm.bind_tools([search])
response = llm_with_search.invoke("最新发布的 Python 3.13 有什么新特性?")
需要先申请 Tavily API Key 并设置环境变量 TAVILY_API_KEY。
DeepSeek 的坑
用 DeepSeek 做工具调用时有几个要注意的地方:
- 关闭思考模式:DeepSeek 的思考模式可能干扰工具调用格式
- 强制工具调用:建议设
tool_choice="any",不然模型有时候会"偷懒"不用工具
llm = ChatDeepSeek(
model="deepseek-chat",
temperature=0
).bind_tools(
tools=[search_weather],
tool_choice="any"
)
小结
工具调用是 LangChain 里最实用的能力之一。模型负责理解意图和生成调用指令,你负责执行和返回结果。这种"大脑+双手"的架构,让大模型从聊天机器人进化成了真正的智能代理。
下一篇我们聊结构化输出——怎么让模型吐出程序能直接用的数据。
觉得有帮助的话,点个赞👍收藏⭐支持一下吧!
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐

所有评论(0)