在大语言模型(LLM)日益普及的今天,单纯依靠模型自身“知识”已难以满足复杂应用场景的需求。为了让模型具备调用外部能力(如查询数据库、执行计算、调用 API 等),LangChain 提供了 Tools 机制——一种将函数封装为模型可调用工具的方式。通过这种机制,我们可以赋予大模型“动手做事”的能力,而不仅仅是“聊天”。
本文将深入讲解如何使用 LangChain 的 tool 构造函数,结合 Zod 定义结构化输入 Schema,并以 DeepSeek 模型为例,展示如何绑定工具、触发调用并处理结果。我们将从基础概念出发,逐步构建一个支持天气查询与加法运算的智能助手。
一、什么是 LangChain Tools?
LangChain 中的 Tool 是一种将普通 JavaScript 函数封装为 LLM 可理解、可调用单元的机制。每个 Tool 包含三个核心部分:
- 函数逻辑:实际执行的操作,例如查询天气、执行数学运算等;
- Schema 定义:使用 Zod 库声明输入参数的类型和语义描述,帮助 LLM 理解如何正确调用;
- 元信息:包括工具名称(
name)和功能描述(description),用于 LLM 在推理时判断是否需要调用该工具。
通过这种方式,LLM 不再局限于其训练数据中的静态知识,而是可以动态地与外部世界交互,从而显著扩展其能力边界。
二、准备工作:引入依赖与环境配置
首先,我们需要安装必要的依赖包:
npm install @langchain/deepseek @langchain/core zod dotenv
然后在代码顶部引入所需模块:
import { ChatDeepSeek } from '@langchain/deepseek'
import 'dotenv/config'
import { tool } from '@langchain/core/tools'
import { z } from 'zod'
其中:
ChatDeepSeek是 DeepSeek 官方提供的 LangChain 集成客户端;dotenv/config用于加载.env文件中的环境变量(如 API Key);tool是 LangChain 提供的工具构造函数;z来自 Zod 库,用于定义运行时类型校验和结构化 Schema。
三、构建第一个 Tool:模拟天气查询
我们先创建一个模拟的天气数据库,用于演示:
const fakeWeatherDB = {
北京: { temp: "30°C", condition: "晴", wind: "微风" },
上海: { temp: "28°C", condition: "多云", wind: "东风 3 级" },
广州: { temp: "32°C", condition: "阵雨", wind: "南风 2 级" }
}
接下来,使用 tool() 构造函数定义天气查询工具:
const weatherTool = tool(
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({
city: z.string().describe('要查询天气的城市')
})
}
)
关键点解析:
- 函数体:接收
{ city }对象作为参数,从模拟数据库中查找天气信息。 - name:必须唯一,LLM 在生成工具调用指令时会使用此名称。
- description:自然语言描述,帮助 LLM 理解该工具的用途。
- schema:使用 Zod 定义输入结构。
z.string().describe(...)不仅声明了类型,还通过.describe()提供语义说明,极大提升 LLM 调用的准确性。
注意:Tool 返回值通常为字符串。因为 LLM 的输入输出均为文本,工具结果最终会被拼接到模型回复中,统一为字符串可避免类型混淆。
四、构建第二个 Tool:加法运算
为了展示数值处理能力,我们再定义一个简单的加法工具:
const addTool = tool(
async ({ a, b }) => String(a + b),
{
name: 'add',
description: '计算两个数值的和',
schema: z.object({
a: z.number(),
b: z.number()
})
}
)
这里的关键在于:
- 参数
a和b被明确声明为number类型; - 返回值强制转换为字符串,确保与 LLM 的文本处理流程兼容;
- 描述清晰,使模型能准确识别何时需要调用此工具。
五、绑定工具到模型:启用工具调用能力
LangChain 允许我们将多个工具绑定到 LLM 实例上,使其在推理过程中自动决定是否调用工具:
const model = new ChatDeepSeek({
model: 'deepseek-chat',
temperature: 0
}).bindTools([addTool, weatherTool])
bindTools()接收一个 Tool 数组;- 当用户提问时,模型会分析语义,若判断需要外部能力,就会在响应中包含
tool_calls字段,列出要调用的工具名及参数。
例如,当用户问:“北京今天的天气怎么样?”模型会识别出这是一个天气查询请求,并生成如下调用指令:
{
"name": "get_weather",
"args": { "city": "北京" }
}
六、处理工具调用结果
模型返回的响应对象中,若包含 tool_calls,则说明需要执行工具调用。我们可以遍历这些调用并执行对应逻辑:
const res = await model.invoke('北京今天的天气怎么样?')
if (res.tool_calls?.length) { // 可选链运算符 es6新增 提高代码的简洁 如果有.tool_calls 才.length
const call = res.tool_calls[0]
let result
if (call.name === 'add') {
result = await addTool.invoke(call.args)
} else if (call.name === 'get_weather') {
result = await weatherTool.invoke(call.args)
}
console.log('最终结果:', result)
}
为什么使用 .invoke()?
因为每个由 tool() 创建的对象都实现了 LangChain 的 Runnable 接口,这意味着它可以像模型一样被调用。.invoke() 是同步执行方法,适用于大多数场景;此外还支持 .stream() 等流式调用方式。
七、Zod Schema 的核心价值
LLM 本身并不理解 JavaScript 的类型系统。它只能通过自然语言和结构化提示来推断如何调用函数。Zod 在此过程中扮演了关键角色:
- 类型安全:在运行时校验传入参数是否符合预期;
- 语义增强:通过
.describe()嵌入自然语言说明,例如z.string().describe('要查询天气的城市'),让 LLM 明确知道这个字符串代表“城市名称”;
如果没有 Zod,开发者需手动编写复杂的提示词来约束参数格式,极易出错且维护困难。
八、完整工作流总结
- 定义工具函数:实现具体业务逻辑(如查天气、做加法);
- 使用
tool()封装:提供 name、description 和 Zod Schema; - 绑定到模型:通过
.bindTools()注册可用工具集; - 发起用户查询:模型自动判断是否需要调用工具;
- 解析
tool_calls:提取工具名和参数; - 执行工具并返回结果:将结果反馈给用户或用于后续推理。
这一流程使得 LLM 从“被动回答者”转变为“主动执行者”,真正具备了与现实世界交互的能力。
九、结语
通过 LangChain 的 Tools 机制,我们可以轻松地将任意函数转化为 LLM 可调用的工具。结合 Zod 的结构化 Schema 和清晰的语义描述,不仅提升了调用准确性,也增强了系统的可维护性与可扩展性。
无论是查询数据库、调用第三方 API,还是执行本地计算,Tools 都为大模型打开了通往真实世界的大门。未来,随着多模态、实时数据和复杂任务需求的增长,这种“模型+工具”的架构将成为 AI 应用开发的主流范式。
本文代码已在 Node.js 环境下验证,配合 DeepSeek API 可直接运行。读者可根据自身业务需求,替换
fakeWeatherDB为真实天气 API,或添加更多实用工具,打造属于自己的智能助手。