news 2026/1/1 8:47:45

LangChain 三 : Tools 工具

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LangChain 三 : Tools 工具

AI 不再嘴炮:用 Tools 给大模型接上“执行力”

在大模型应用开发中,单纯的对话能力早已无法满足复杂场景需求,大模型仅凭自身训练数据很难给出准确答案。这时候,LangChain Tools 就像给大模型装上了「手脚」,让它从「只会聊天」升级为「能办实事」。今天我们就来深入聊聊 LangChain Tools 的核心知识,结合实战代码带你快速掌握这个强大功能!

一、LangChain Tools 是什么? 🤔

LangChain Tools(工具)是 LangChain 框架中扩展大模型能力的核心组件。它的本质是将「具体的业务逻辑或操作能力」封装成标准化接口,让大模型能根据用户请求自主判断:要不要调用工具?调用哪个工具?该传什么参数?最终通过工具弥补大模型自身的局限性。

简单来说:

  • 大模型是「大脑」🧠:负责分析问题、判断需求、制定方案;
  • Tools 是「手脚」👋:负责执行具体操作(比如查天气、算数据、调接口)。

二者结合,才能实现从「思考」到「行动」的完整闭环。

二、LangChain Tools 为了解决什么问题? 🚫➡️✅

大模型虽然强大,但存在不少「天然短板」,而 Tools 正是为了弥补这些短板而生:

  1. 突破知识滞后性⏳大模型的训练数据有时间截止线,无法获取实时信息(比如 2024 年的天气、最新股票行情)。通过自定义工具(如对接实时天气 API 的weatherTool),就能让大模型获取「新鲜数据」。
  2. 访问私有数据 / 系统🔒企业内部数据库、本地文件、私有 API 等信息,大模型无法直接访问。通过工具封装这些私有资源的访问逻辑(如下文示例中的fakeWeatherDB模拟私有天气库),既能安全调用数据,又不泄露敏感信息。
  3. 扩展操作边界🚀除了查数据、算结果,工具还能实现更复杂的操作:发送邮件、操作数据库、执行代码、控制硬件设备等。只要能写成函数的逻辑,都能封装成工具让大模型调用。

三、LangChain Tools 的核心组成要素 🔑

一个完整的 LangChain 工具由两部分组成:「核心执行逻辑」和「工具配置对象」。我们以下面核心代码中的weatherTooladdTool为例,拆解每个要素的作用:

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_weatheradd),大模型通过这个名称确定要调用的工具。注意:多个工具绑定时名称必须唯一,否则会出现调用混乱(就像两个人重名会认错人一样)。

  • 工具描述(description)📝大模型判断是否调用该工具的「核心依据」。描述需清晰说明工具的功能和适用场景,比如:

    • weatherTool的描述是「查询指定城市的今日天气情况」,大模型看到用户问「北京天气」时,就知道该调用它;
    • addTool的描述是「计算两个数字的和,仅支持纯数字加法运算」,用户问「3+4 等于几」时,大模型会匹配到这个工具。
  • 参数校验 Schema(schema)🧩用zod库定义的参数规则,有两个核心作用:

    • 代码层面:校验传入工具的参数是否合法(比如city必须是字符串,ab必须是数字),防止非法参数导致报错;
    • 大模型层面:告诉大模型「需要传什么参数、参数是什么意思」。比如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,适合户外活动~」这样的自然语言。示例中我们为了简化流程,直接打印了结果。

执行结果亮个相:

五、面试官会问这些问题 🎯

  1. Q:工具的description字段有什么作用?能不能省略?A:不能省略。description是大模型判断是否调用工具的核心依据,描述越清晰,大模型调用工具的准确率越高。比如如果addTool的描述写成「处理数字」,大模型可能在用户问「3 乘以 4」时也错误调用它。
  2. Q:为什么工具的执行逻辑必须是异步函数?A:因为工具常涉及网络请求(如调用 API)、IO 操作(如读文件)等耗时任务,异步执行可避免阻塞整个程序流程,保证应用的响应速度。
  3. Q:zod在工具中起到了什么作用?A:有两个作用:① 代码层面校验参数合法性(比如防止给addTool传字符串);② 告诉大模型参数的类型和含义,指导它正确传参(比如city必须是中文城市名)。
  4. Q:多个工具绑定时,需要注意什么?A:工具的name必须唯一,否则会出现覆盖或调用混乱;同时description要区分度高,避免大模型混淆工具的功能(比如一个查天气、一个查空气质量,描述要明确区分)。
  5. Q:如何处理工具调用失败的情况?A:可以在工具执行逻辑中加入错误处理(如try/catch),返回明确的错误信息(如「查询天气失败,请检查城市名称」);也可以将错误信息反馈给大模型,让它决定重试或提示用户。

六、结语 🌟

LangChain Tools 是大模型从「对话助手」升级为「实用工具」的关键。通过封装工具,我们能让大模型突破自身局限,对接实时数据、执行具体操作、访问私有资源,真正解决实际问题。

文中我们的示例代码(加法工具、天气查询工具)已经包含了工具开发的核心逻辑,你可以在此基础上扩展:比如对接真实的天气 API、封装数据库查询工具、实现邮件发送工具等。记住:只要能写成函数的逻辑,都能成为大模型的「工具」!

快去动手试试吧,让你的大模型应用从「能说会道」变成「能干实事」~ 🚀

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2025/12/29 5:21:18

Jupyter Notebook直连PyTorch-GPU:PyTorch-CUDA-v2.6镜像使用教程

Jupyter Notebook直连PyTorch-GPU:PyTorch-CUDA-v2.6镜像使用教程 在深度学习项目中,最让人头疼的往往不是模型调参,而是环境配置——明明代码写好了,却因为CUDA版本不匹配、驱动缺失或依赖冲突导致torch.cuda.is_available()返回…

作者头像 李华
网站建设 2025/12/29 5:21:06

Unity游戏视觉优化工具集:突破马赛克限制的专业解决方案

Unity游戏视觉优化工具集:突破马赛克限制的专业解决方案 【免费下载链接】UniversalUnityDemosaics A collection of universal demosaic BepInEx plugins for games made in Unity3D engine 项目地址: https://gitcode.com/gh_mirrors/un/UniversalUnityDemosaic…

作者头像 李华
网站建设 2025/12/29 5:21:05

手把手教你用PotatoNV实现华为设备bootloader解锁

手把手教你用PotatoNV实现华为设备bootloader解锁 【免费下载链接】PotatoNV Unlock bootloader of Huawei devices on Kirin 960/95х/65x/620 项目地址: https://gitcode.com/gh_mirrors/po/PotatoNV 想要完全掌控你的华为设备吗?bootloader解锁是开启设备…

作者头像 李华
网站建设 2025/12/29 5:20:55

一文说清PCB绘制中的信号完整性与布线策略

一文说清PCB绘制中的信号完整性与布线策略当你的电路板“跑”不起来,问题可能出在哪儿?你有没有遇到过这样的情况:原理图没问题,元器件焊接也没错,电源正常供电,MCU也上电了——可系统就是不稳定&#xff0…

作者头像 李华
网站建设 2025/12/29 5:20:30

基于Multisim的教学平台部署:主数据库修复实战案例

一次“Multisim打不开”的惊魂排查:主数据库修复全记录实验室的早晨总是从一台台电脑亮起开始的。那天,我刚走进电子技术实训中心,就听见几个学生围在角落的机子前嘀咕:“老师,这Multisim点开就报错,说找不…

作者头像 李华
网站建设 2025/12/29 5:20:16

颠覆传统!Prettify如何让Anki学习效率提升300%

颠覆传统!Prettify如何让Anki学习效率提升300% 【免费下载链接】anki-prettify Collection of customizable Anki flashcard templates with modern and clean themes. 项目地址: https://gitcode.com/gh_mirrors/an/anki-prettify 还在为单调乏味的Anki卡片…

作者头像 李华