LangChain Tools 使用指南
一、Tools 是什么?
Tool(工具) 是连接 LLM 和外部能力的桥梁。
LLM 本身只能"思考",不能执行操作。Tool 让 LLM 可以:
- 调用计算器做精确计算
- 读写文件
- 查询数据库
- 调用外部 API
- 执行任意 Python 函数
核心思路:LLM 决定"要不要调"和"调什么",你负责执行。
二、完整使用案例
python
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
import json
# ====== 1. 定义工具 ======
@tool
def add(a: int, b: int) -> int:
"""两数相加,返回它们的和"""
return a + b
@tool
def multiply(a: int, b: int) -> int:
"""两数相乘,返回它们的积"""
return a * b
@tool
def get_weather(city: str) -> str:
"""查询某个城市的当前天气"""
data = {"北京": "晴 25C", "上海": "小雨 22C", "深圳": "多云 28C"}
return data.get(city, f"暂无{city}的天气数据")
@tool
def write_file(filename: str, content: str) -> str:
"""写入内容到桌面文件"""
path = f"C:\\Users\\Enty\\Desktop\\{filename}"
with open(path, "w", encoding="utf-8") as f:
f.write(content)
return f"已写入 {path}"
tools = [add, multiply, get_weather, write_file]
# ====== 2. 绑定工具到 LLM ======
llm = ChatOpenAI(
model="deepseek-v4-flash",
api_key="sk-xxx",
base_url="https://api.deepseek.com",
)
llm_with_tools = llm.bind_tools(tools)
# ====== 3. 运行 Agent 循环 ======
def run_agent(prompt: str, max_turns: int = 5):
messages = [{"role": "user", "content": prompt}]
for turn in range(1, max_turns + 1):
response = llm_with_tools.invoke(messages)
messages.append(response)
# LLM 没有调工具,直接给出回答
if not response.tool_calls:
return response.content
# LLM 想调工具,逐个执行
for tool_call in response.tool_calls:
tool_name = tool_call["name"]
tool_args = tool_call["args"]
# 找到对应的工具函数并执行
matched = next(t for t in tools if t.name == tool_name)
result = matched.invoke(tool_args)
# 把结果放回消息列表,LLM 会继续推理
messages.append({
"role": "tool",
"content": json.dumps(result, ensure_ascii=False),
"tool_call_id": tool_call["id"],
})
return "达到最大轮数"
if __name__ == "__main__":
result = run_agent("北京天气怎么样?")
print(result)
三、各步骤详解
第 1 步:@tool 装饰器
python
@tool
def add(a: int, b: int) -> int:
"""两数相加,返回它们的和"""
return a + b
@tool 把普通 Python 函数注册为 LangChain 工具。
自动生成三样东西:
| 来源 | 作用 |
|---|---|
函数名 add |
LLM 知道这个工具叫 add |
文档字符串 两数相加... |
LLM 知道什么时候该用这个工具 |
参数签名 a: int, b: int |
LLM 知道需要传什么参数、类型是什么 |
LLM 看到这段 JSON Schema(自动生成,开发者看不到):
json
{
"name": "add",
"description": "两数相加,返回它们的和",
"parameters": {
"type": "object",
"properties": {
"a": {"type": "integer"},
"b": {"type": "integer"}
},
"required": ["a", "b"]
}
}
第 2 步:bind_tools()
python
llm_with_tools = llm.bind_tools(tools)
把工具列表注册到 LLM 实例上。之后每次调用 llm_with_tools.invoke(),API 请求里都会带着工具定义。
LLM 收到问题后有两种选择:
- 直接回答 →
response.tool_calls为空 - 调用工具 →
response.tool_calls有内容
第 3 步:run_agent 循环
3.1 调用 LLM
python
response = llm_with_tools.invoke(messages)
把当前消息发给 LLM,LLM 返回回答或工具调用请求。
3.2 判断要不要调工具
python
if not response.tool_calls:
return response.content
response.tool_calls 为空 → LLM 直接回答了,结束循环。
response.tool_calls 有内容 → LLM 想调工具,继续执行。
3.3 解析工具调用
python
tool_call["name"] # "add" → 调哪个工具
tool_call["args"] # {"a": 3, "b": 5} → 参数是什么
tool_call["id"] # "call_xxx" → 本次调用的唯一 ID
3.4 执行工具
python
matched = next(t for t in tools if t.name == tool_name)
result = matched.invoke(tool_args)
从工具列表中找到名称匹配的函数,传入参数执行。
3.5 返回结果给 LLM
python
messages.append({
"role": "tool",
"content": json.dumps(result, ensure_ascii=False),
"tool_call_id": tool_call["id"],
})
工具执行结果必须放回消息列表,LLM 才能继续推理。tool_call_id 用于对应这次调用。
3.6 继续下一轮
python
# LLM 拿到工具结果后,可能再调下一个工具,或者直接回答
response = llm_with_tools.invoke(messages)
四、完整交互流程
用户: "北京天气怎么样?写到一个文件里"
第 1 轮:
LLM 回复 → tool_calls = [get_weather(city="北京")]
执行 → "晴 25C"
放回消息列表
第 2 轮:
LLM 回复 → tool_calls = [write_file(filename="weather.txt", content="北京: 晴 25C")]
执行 → "已写入 ..."
放回消息列表
第 3 轮:
LLM 回复 → content = "已完成!已查询北京天气并写入文件"
tool_calls 为空 → 结束
五、关键概念总结
| 概念 | 一句话 |
|---|---|
@tool |
把函数变成 LLM 能调用的工具 |
| 函数签名 | 决定了 LLM 能填什么参数 |
| 文档字符串 | 决定了 LLM 什么时候调这个工具 |
bind_tools() |
把工具注册到 LLM |
tool_calls |
LLM 返回的"我想调 XX 工具"信号 |
tool_call_id |
关联工具调用和结果 |
| 循环 | LLM 可以调多次、跨多步,直到给出最终回答 |