LangChain 三 : Tools 工具
本文深入讲解 LangChain Tools 的核心原理与实战用法,通过天气查询和加法计算两个示例,展示如何为大模型赋予“手脚”,突破其知识边界,实现从“会聊天”到“能办事”的关键升级。
AI 不再嘴炮:用 Tools 给大模型接上“执行力”
在大模型应用开发中,单纯的对话能力早已无法满足复杂场景需求,大模型仅凭自身训练数据很难给出准确答案。这时候,LangChain Tools 就像给大模型装上了「手脚」,让它从「只会聊天」升级为「能办实事」。今天我们就来深入聊聊 LangChain Tools 的核心知识,结合实战代码带你快速掌握这个强大功能!
一、LangChain Tools 是什么? 🤔
LangChain Tools(工具)是 LangChain 框架中扩展大模型能力的核心组件。它的本质是将「具体的业务逻辑或操作能力」封装成标准化接口,让大模型能根据用户请求自主判断:要不要调用工具?调用哪个工具?该传什么参数?最终通过工具弥补大模型自身的局限性。
简单来说:
- 大模型是「大脑」🧠:负责分析问题、判断需求、制定方案;
- Tools 是「手脚」👋:负责执行具体操作(比如查天气、算数据、调接口)。
二者结合,才能实现从「思考」到「行动」的完整闭环。
二、LangChain Tools 为了解决什么问题? 🚫➡️✅
大模型虽然强大,但存在不少「天然短板」,而 Tools 正是为了弥补这些短板而生:
- 突破知识滞后性 ⏳大模型的训练数据有时间截止线,无法获取实时信息(比如 2024 年的天气、最新股票行情)。通过自定义工具(如对接实时天气 API 的
weatherTool),就能让大模型获取「新鲜数据」。 - 访问私有数据 / 系统 🔒企业内部数据库、本地文件、私有 API 等信息,大模型无法直接访问。通过工具封装这些私有资源的访问逻辑(如下文示例中的
fakeWeatherDB模拟私有天气库),既能安全调用数据,又不泄露敏感信息。 - 扩展操作边界 🚀除了查数据、算结果,工具还能实现更复杂的操作:发送邮件、操作数据库、执行代码、控制硬件设备等。只要能写成函数的逻辑,都能封装成工具让大模型调用。
三、LangChain Tools 的核心组成要素 🔑
一个完整的 LangChain 工具由两部分组成:「核心执行逻辑」和「工具配置对象」。我们以下面核心代码中的 weatherTool 和 addTool 为例,拆解每个要素的作用:
Tools核心代码:
const weatherTool = tool(
async({city}) => {
// 工具做什么事情 从fakeWeatherDB中获取天气信息
const weather = fakeWeatherDB[city];
if(!weather) {
return `暂无${city}的天气信息`;
}
return `当前${city}的天气是${weather.condition},温度${weather.temp},风力${weather.wind}`;
},
// 配置schema 定义工具的输入参数的类型 传给大模型
{
name:"get_weather",
description:"查询指定城市的今日天气情况", // 函数的描述
schema:z.object({
city:z.string().describe("要查询天气的城市")
})
}
)
// 函数 定义一个加法工具
const addTool = tool(
// 两个参数
// 等大模型来调用这个函数(工具)
// 参数是一个对象,包含a和b两个属性,可以解构出来
async({a,b}) => String(a + b),
{
name:"add", // 函数名字
description:"计算两个数字的和", // 函数的描述
// 定义参数的类型 参数schema 是一个对象 调用时也要传一个对象
schema:z.object({
a:z.number().describe("第一个数字"),
b:z.number().describe("第二个数字")
})
}
)
1. 核心执行逻辑(异步函数) 🚀
这是工具的「灵魂」,负责接收参数、执行具体操作、返回结果。比如:
weatherTool的执行逻辑是从fakeWeatherDB中查询城市天气并格式化结果;addTool的执行逻辑是计算两个数字的和并返回字符串。
关键要求:
- 必须是「异步函数」(带
async关键字):因为工具常涉及网络请求(如调用 API)、IO 操作(如读文件)等耗时任务,异步执行可避免阻塞整个流程。 - 入参格式:需与
schema定义的结构一致,推荐用对象解构(如async ({ city }) => {...}或async ({ a, b }) => {...}),方便参数匹配。
2. 工具配置对象(给大模型的「使用说明书」) 📖
配置对象是大模型理解工具的关键,包含 3 个核心字段:
-
工具名称(name) 🏷️工具的唯一标识(如
get_weather、add),大模型通过这个名称确定要调用的工具。注意:多个工具绑定时名称必须唯一,否则会出现调用混乱(就像两个人重名会认错人一样)。 -
工具描述(description) 📝大模型判断是否调用该工具的「核心依据」。描述需清晰说明工具的功能和适用场景,比如:
weatherTool的描述是「查询指定城市的今日天气情况」,大模型看到用户问「北京天气」时,就知道该调用它;addTool的描述是「计算两个数字的和,仅支持纯数字加法运算」,用户问「3+4 等于几」时,大模型会匹配到这个工具。
-
参数校验 Schema(schema) 🧩用
zod库定义的参数规则,有两个核心作用:- 代码层面:校验传入工具的参数是否合法(比如
city必须是字符串,a和b必须是数字),防止非法参数导致报错; - 大模型层面:告诉大模型「需要传什么参数、参数是什么意思」。比如
city: z.string().describe("要查询天气的城市,必须是中文城市名称"),大模型就知道要传中文城市名。
- 代码层面:校验传入工具的参数是否合法(比如
四、LangChain Tools 的核心工作流程 📝
先来看看完整代码:
// 1. 导入 DeepSeek 聊天模型(用于和大模型进行对话及工具调用交互)
// 从 @langchain/deepseek 包中导出,专门适配 DeepSeek 大模型的 LangChain 封装
import { ChatDeepSeek } from '@langchain/deepseek';
// 2. 导入 dotenv 配置(用于加载 .env 文件中的环境变量,如 DeepSeek API KEY)
// 执行此导入后,.env 文件中的变量会被注入到 process.env 中,无需额外配置
import "dotenv/config";
// 3. 导入 LangChain 核心工具装饰器/工具创建函数(用于封装自定义工具,供大模型调用)
// 来自 @langchain/core/tools 包,是 LangChain 工具系统的核心API
import { tool } from "@langchain/core/tools";
// 4. 导入 zod 数据校验库(用于定义工具输入参数的校验规则和描述,同时提供给大模型理解参数含义)
// zod 不仅能做参数类型校验,还能通过 describe 方法给大模型提供参数说明,是 LangChain 工具调用的标配
import { z } from 'zod';
// 5. 模拟天气数据库(无真实接口请求,用本地对象模拟城市天气数据,方便演示工具调用效果)
// 键:城市名称,值:包含温度、天气状况、风力的天气详情对象
const fakeWeatherDB = {
北京: { temp: "30°C", condition: "晴", wind: "微风" },
上海: { temp: "28°C", condition: "多云", wind: "东风 3 级" },
广州: { temp: "32°C", condition: "阵雨", wind: "南风 2 级" },
};
// 6. 创建 天气查询工具(使用 tool 函数封装自定义工具,供大模型自动调用)
// tool 函数接收两个参数:① 工具执行的异步函数 ② 工具配置项(名称、描述、参数校验规则)
const weatherTool = tool(
// 工具核心执行逻辑:异步函数,接收解构后的参数对象(city 城市名)
async ({ city }) => {
// 从模拟天气数据库中获取目标城市的天气信息
const weather = fakeWeatherDB[city];
// 容错处理:如果数据库中没有该城市的天气数据,返回提示信息
if (!weather) {
return `暂无${city}的天气信息`;
}
// 格式化返回结果,让大模型和用户都能清晰理解
return `当前${city}的天气是${weather.condition},温度${weather.temp},风力${weather.wind}`;
},
// 工具配置项:给大模型提供工具元信息,用于大模型判断是否调用该工具及如何传参
{
name: "get_weather", // 工具唯一标识(大模型通过该名称识别工具)
description: "查询指定城市的今日天气情况", // 工具功能描述(核心!大模型根据该描述判断何时调用此工具)
schema: z.object({ // 参数校验规则(zod 定义),同时给大模型提供参数说明
city: z.string().describe("要查询天气的城市,必须是中文城市名称(如:北京、上海)")
})
}
);
// 7. 创建 加法计算工具(同样使用 tool 函数封装,演示多参数工具的定义)
const addTool = tool(
// 工具核心执行逻辑:异步函数,接收解构后的两个数字参数 a 和 b
// 计算求和后转换为字符串返回(大模型更易解析字符串格式结果)
async ({ a, b }) => String(a + b),
// 工具配置项:定义工具元信息和多参数校验规则
{
name: "add", // 工具唯一标识(大模型通过该名称识别加法工具)
description: "计算两个数字的和,仅支持纯数字加法运算", // 工具功能描述,明确工具适用场景
// 多参数校验规则:z.object 中定义多个参数,分别指定类型和描述
schema: z.object({
a: z.number().describe("第一个要相加的数字,必须是数字类型(整数或小数)"),
b: z.number().describe("第二个要相加的数字,必须是数字类型(整数或小数)")
})
}
);
// 8. 初始化 DeepSeek 聊天模型实例(配置模型参数,建立和 DeepSeek 大模型的连接)
const model = new ChatDeepSeek({
model: "deepseek-chat", // 指定使用的 DeepSeek 模型版本(deepseek-chat 为对话模型)
temperature: 0, // 温度参数(0 表示输出结果确定性强,无随机性;值越大随机性越高)
}).bindTools([addTool, weatherTool]); // 给模型绑定自定义工具(核心!让大模型知晓可用的工具列表)
// 9. 第一个请求:让模型计算 3 和 4 的和(触发加法工具调用)
const res = await model.invoke("计算3和4的和");
// 10. 处理模型返回结果,执行工具调用并获取最终结果
// 可选链操作符 ?. 说明:
// - 作用:判断 res.tool_calls 是否存在,若存在则访问 length 属性;若不存在则返回 undefined,不会报错
// - 场景:如果大模型未识别到需要调用工具(如用户问题无需工具),则 res 中无 tool_calls 属性,直接访问 res.tool_calls.length 会抛出 TypeError
// - 优势:简化容错处理代码,无需额外写 if (res.tool_calls) 判断,让代码更简洁优雅
if (res.tool_calls?.length) {
// 此处仅处理第一个工具调用(示例中用户问题仅需调用一个工具)
if (res.tool_calls[0].name === "add") {
// 调用加法工具的 invoke 方法,传入模型返回的工具参数(res.tool_calls[0].args)
const result = await addTool.invoke(res.tool_calls[0].args);
// 打印最终计算结果
console.log("最终结果:", result);
}
}
// 11. 第二个请求:让模型查询北京的天气(触发天气查询工具调用)
const res2 = await model.invoke("查询北京的天气");
// 12. 同理处理天气查询的工具调用结果
if (res2.tool_calls?.length) {
// 判断调用的工具是否为天气查询工具(get_weather)
if (res2.tool_calls[0].name === "get_weather") {
// 调用天气工具的 invoke 方法,传入模型返回的城市参数
const result = await weatherTool.invoke(res2.tool_calls[0].args);
// 打印最终天气查询结果
console.log("最终结果:", result);
}
}
结合上面代码,我们一步步拆解工具调用的完整流程,看看大模型是如何「思考并行动」的:
步骤 1:定义 / 封装工具 🔨
用 LangChain 提供的 tool 函数,将业务逻辑和配置对象封装成工具实例。以 weatherTool 为例:
javascript
// 天气查询工具定义
const weatherTool = tool(
// 核心执行逻辑:从模拟数据库查询天气
async ({ city }) => {
const weather = fakeWeatherDB[city];
return weather ?
`当前${city}的天气是${weather.condition},温度${weather.temp},风力${weather.wind}` :
`暂无${city}的天气信息`;
},
// 工具配置:给大模型的「使用说明」
{
name: "get_weather",
description: "查询指定城市的今日天气情况",
schema: z.object({
city: z.string().describe("要查询天气的城市,必须是中文城市名称(如:北京、上海)")
})
}
);
同理,addTool 也按照这个结构封装,定义加法逻辑和参数规则。
步骤 2:将工具绑定到大模型 🧩
通过模型的 .bindTools() 方法,把工具「告诉」大模型,让它知道自己有哪些「技能」:
javascript
// 初始化DeepSeek模型并绑定工具
const model = new ChatDeepSeek({
model: "deepseek-chat", // 指定模型版本
temperature: 0, // 0表示结果更确定,无随机性
}).bindTools([addTool, weatherTool]); // 绑定工具列表
这一步是工具调用的前提 —— 不绑定工具,大模型就只能「空谈」,无法执行任何操作。
步骤 3:发送用户请求,模型生成工具调用指令 📤
通过 model.invoke(用户请求) 向大模型提问,大模型会分析需求并决定是否调用工具:
javascript
// 示例1:请求计算3和4的和
const res = await model.invoke("计算3和4的和");
// 示例2:请求查询北京天气
const res2 = await model.invoke("查询北京的天气");
大模型会返回包含 tool_calls 的结果:
- 如果需要调用工具,
tool_calls里会包含工具名称(如add)和参数(如{ a: 3, b: 4 }); - 如果不需要调用工具(比如简单闲聊),则没有
tool_calls。
步骤 4:解析指令,执行工具并获取结果 🛠️
解析 tool_calls 中的信息,调用对应的工具执行逻辑:
javascript
// 处理加法工具调用
if (res.tool_calls?.length) { // 可选链操作符:避免无tool_calls时报错
if (res.tool_calls[0].name === "add") {
const result = await addTool.invoke(res.tool_calls[0].args);
console.log("最终结果:", result); // 输出:7
}
}
// 处理天气工具调用
if (res2.tool_calls?.length) {
if (res2.tool_calls[0].name === "get_weather") {
const result = await weatherTool.invoke(res2.tool_calls[0].args);
console.log("最终结果:", result); // 输出:当前北京的天气是晴,温度30°C,风力微风
}
}
这里的 ?.(可选链操作符)是个小技巧:如果大模型没调用工具(无 tool_calls),直接访问 res.tool_calls.length 会报错,用 ?. 可优雅避免这种情况。
步骤 5:(可选)整理结果,生成自然语言回复 💬
复杂场景中,可将工具结果再次传给大模型,让它整理成更友好的回复。比如将天气结果传给模型,让它生成「今天北京天气晴朗,温度 30°C,适合户外活动~」这样的自然语言。示例中我们为了简化流程,直接打印了结果。
执行结果亮个相:

五、面试官会问这些问题 🎯
- Q:工具的
description字段有什么作用?能不能省略? A:不能省略。description是大模型判断是否调用工具的核心依据,描述越清晰,大模型调用工具的准确率越高。比如如果addTool的描述写成「处理数字」,大模型可能在用户问「3 乘以 4」时也错误调用它。 - Q:为什么工具的执行逻辑必须是异步函数? A:因为工具常涉及网络请求(如调用 API)、IO 操作(如读文件)等耗时任务,异步执行可避免阻塞整个程序流程,保证应用的响应速度。
- Q:
zod在工具中起到了什么作用? A:有两个作用:① 代码层面校验参数合法性(比如防止给addTool传字符串);② 告诉大模型参数的类型和含义,指导它正确传参(比如city必须是中文城市名)。 - Q:多个工具绑定时,需要注意什么? A:工具的
name必须唯一,否则会出现覆盖或调用混乱;同时description要区分度高,避免大模型混淆工具的功能(比如一个查天气、一个查空气质量,描述要明确区分)。 - Q:如何处理工具调用失败的情况? A:可以在工具执行逻辑中加入错误处理(如
try/catch),返回明确的错误信息(如「查询天气失败,请检查城市名称」);也可以将错误信息反馈给大模型,让它决定重试或提示用户。
六、结语 🌟
LangChain Tools 是大模型从「对话助手」升级为「实用工具」的关键。通过封装工具,我们能让大模型突破自身局限,对接实时数据、执行具体操作、访问私有资源,真正解决实际问题。
文中我们的示例代码(加法工具、天气查询工具)已经包含了工具开发的核心逻辑,你可以在此基础上扩展:比如对接真实的天气 API、封装数据库查询工具、实现邮件发送工具等。记住:只要能写成函数的逻辑,都能成为大模型的「工具」!
快去动手试试吧,让你的大模型应用从「能说会道」变成「能干实事」~ 🚀
更多推荐



所有评论(0)