| name | langchain-tool-calling |
| description | 聊天模型如何调用工具 - 包括 bindTools、工具选择策略、并行工具调用和工具消息处理 |
| language | js |
langchain-tool-calling (JavaScript/TypeScript)
概述
工具调用允许聊天模型请求执行外部函数。模型根据用户输入决定调用哪些工具,然后将结果传回模型以进行进一步推理。这是代理行为的基础。
核心概念:
- bindTools():将工具绑定到模型
- 工具调用:模型请求执行工具(在 AIMessage.tool_calls 中)
- 工具消息:传回模型的结果(ToolMessage)
- 工具选择:控制模型可以使用的工具
何时使用工具调用
| 场景 | 使用工具调用? | 原因 |
|---|
| 需要外部数据(API、数据库) | ✅ 是 | 模型无法直接访问外部数据 |
| 带操作的多步推理 | ✅ 是 | 模型根据结果决定下一步操作 |
| 简单问答 | ❌ 否 | 不需要工具 |
| 预定工作流 | ⚠️ 可能 | 考虑模型是否需要决定步骤 |
决策表
工具选择策略
| 策略 | 使用时机 | 示例 |
|---|
"auto"(默认) | 模型决定是否/使用哪个工具 | 通用目的 |
"any" | 强制模型使用至少一个工具 | 提取、分类 |
"tool_name" | 强制特定工具 | 当您知道需要哪个工具时 |
"none" | 防止工具使用 | 工具执行后 |
处理工具调用
| 模式 | 使用时机 | 示例 |
|---|
| 手动执行 | 代理外部 | 测试、自定义工作流 |
| 代理循环 | 生产使用 | createAgent 自动处理 |
| 并行执行 | 多个独立工具 | 天气 + 新闻查询 |
代码示例
基本工具调用
import { ChatOpenAI } from "@langchain/openai";
import { tool } from "langchain";
import { z } from "zod";
const getWeather = tool(
async ({ location }: { location: string }) => {
return `${location}的天气:晴天,72°F`;
},
{
name: "get_weather",
description: "获取位置的当前天气",
schema: z.object({
location: z.string().describe("城市名称"),
}),
}
);
const model = new ChatOpenAI({ model: "gpt-4.1" });
const modelWithTools = model.bindTools([getWeather]);
const response = await modelWithTools.invoke(
"旧金山的天气怎么样?"
);
console.log(response.tool_calls);
手动执行工具调用
import { ChatOpenAI } from "@langchain/openai";
import { tool } from "langchain";
import { ToolMessage } from "langchain";
const getTool = tool(
async ({ location }) => `${location}的天气:晴天`,
{
name: "get_weather",
description: "获取天气",
schema: z.object({ location: z.string() }),
}
);
const model = new ChatOpenAI({ model: "gpt-4.1" });
const modelWithTools = model.bindTools([getTool]);
const messages = [{ role: "user", content: "纽约的天气怎么样?" }];
const response1 = await modelWithTools.invoke(messages);
const toolResults = [];
for (const toolCall of response1.tool_calls || []) {
const result = await getTool.invoke(toolCall);
toolResults.push(result);
}
messages.push(response1);
messages.push(...toolResults);
const response2 = await modelWithTools.invoke(messages);
console.log(response2.content);
工具选择:强制工具使用
import { ChatOpenAI } from "@langchain/openai";
import { tool } from "langchain";
const extractInfo = tool(
async ({ name, email }) => ({ name, email }),
{
name: "extract_info",
description: "提取姓名和邮箱",
schema: z.object({
name: z.string(),
email: z.string(),
}),
}
);
const model = new ChatOpenAI({ model: "gpt-4.1" });
const modelWithTools = model.bindTools([extractInfo], {
tool_choice: "extract_info",
});
const response = await modelWithTools.invoke(
"联系人:John Doe (john@example.com)"
);
console.log(response.tool_calls[0].args);
工具选择:强制任意工具
const modelWithTools = model.bindTools(
[tool1, tool2, tool3],
{ tool_choice: "any" }
);
const response = await modelWithTools.invoke("处理这些数据");
并行工具调用
import { ChatOpenAI } from "@langchain/openai";
import { tool } from "langchain";
const getWeather = tool(
async ({ location }) => `${location}的天气:晴天`,
{
name: "get_weather",
description: "获取天气",
schema: z.object({ location: z.string() }),
}
);
const getNews = tool(
async ({ topic }) => `关于${topic}的最新新闻`,
{
name: "get_news",
description: "获取新闻",
schema: z.object({ topic: z.string() }),
}
);
const model = new ChatOpenAI({ model: "gpt-4.1" });
const modelWithTools = model.bindTools([getWeather, getNews]);
const response = await modelWithTools.invoke(
"获取纽约的天气和关于 AI 的新闻"
);
console.log(response.tool_calls);
工具消息结构
import { ToolMessage } from "langchain";
const toolMessage = new ToolMessage({
content: "巴黎的天气:晴天,72°F",
tool_call_id: "call_abc123",
name: "get_weather",
});
const result = await getTool.invoke({
name: "get_weather",
args: { location: "Paris" },
id: "call_abc123",
});
处理工具错误
import { ChatOpenAI } from "@langchain/openai";
import { tool } from "langchain";
import { ToolMessage } from "langchain";
const riskyTool = tool(
async ({ data }) => {
if (!data) throw new Error("缺少数据");
return "成功";
},
{
name: "risky_tool",
description: "可能失败的工具",
schema: z.object({ data: z.string().optional() }),
}
);
const model = new ChatOpenAI({ model: "gpt-4.1" });
const modelWithTools = model.bindTools([riskyTool]);
const response = await modelWithTools.invoke("处理这个");
const toolResults = [];
for (const toolCall of response.tool_calls || []) {
try {
const result = await riskyTool.invoke(toolCall);
toolResults.push(result);
} catch (error) {
toolResults.push(
new ToolMessage({
content: `错误:${error.message}`,
tool_call_id: toolCall.id,
name: toolCall.name,
})
);
}
}
提供商特定的内置工具
import { ChatOpenAI } from "@langchain/openai";
const model = new ChatOpenAI({
model: "gpt-4.1",
tools: [{ type: "code_interpreter" }],
});
import { ChatAnthropic } from "@langchain/anthropic";
const claude = new ChatAnthropic({
model: "claude-sonnet-4-5-20250929",
});
条件工具绑定
import { ChatOpenAI } from "@langchain/openai";
const model = new ChatOpenAI({ model: "gpt-4.1" });
function getModelWithTools(userRole: string) {
const tools = [publicTool];
if (userRole === "admin") {
tools.push(adminTool);
}
return model.bindTools(tools);
}
const adminModel = getModelWithTools("admin");
const userModel = getModelWithTools("user");
对话中的工具调用
import { ChatOpenAI } from "@langchain/openai";
import { tool } from "langchain";
const searchTool = tool(
async ({ query }) => `${query}的搜索结果`,
{
name: "search",
description: "搜索网络",
schema: z.object({ query: z.string() }),
}
);
const model = new ChatOpenAI({ model: "gpt-4.1" });
const modelWithTools = model.bindTools([searchTool]);
const messages = [
{ role: "user", content: "搜索 LangChain" },
];
const response1 = await modelWithTools.invoke(messages);
messages.push(response1);
for (const toolCall of response1.tool_calls || []) {
const result = await searchTool.invoke(toolCall);
messages.push(result);
}
const response2 = await modelWithTools.invoke(messages);
console.log(response2.content);
messages.push(response2);
messages.push({ role: "user", content: "告诉我更多" });
const response3 = await modelWithTools.invoke(messages);
边界
您可以配置的内容
✅ 哪些工具可用:bindTools([tool1, tool2])
✅ 工具选择策略:auto、any、特定工具、none
✅ 工具执行逻辑:自定义错误处理、重试
✅ 工具参数:通过工具模式
✅ 多个工具调用:模型可以调用多个工具
您无法配置的内容
❌ 强制模型推理:无法控制模型如何决定
❌ 工具调用顺序:模型决定(可以并行调用)
❌ 阻止所有工具调用:使用 tool_choice 或不绑定工具
❌ 在模型生成后修改工具调用:工具调用是不可变的
常见陷阱
1. 忘记传回工具结果
const response1 = await modelWithTools.invoke(messages);
const toolResult = await tool.invoke(response1.tool_calls[0]);
messages.push(response1);
messages.push(toolResult);
const response2 = await modelWithTools.invoke(messages);
2. 工具调用 ID 不匹配
const response = await modelWithTools.invoke("获取天气");
const toolMessage = new ToolMessage({
content: "晴天",
tool_call_id: "wrong_id",
name: "get_weather",
});
const toolMessage = new ToolMessage({
content: "晴天",
tool_call_id: response.tool_calls[0].id,
name: "get_weather",
});
const toolMessage = await getTool.invoke(response.tool_calls[0]);
3. 不检查工具调用
const response = await modelWithTools.invoke("你好");
await tool.invoke(response.tool_calls[0]);
if (response.tool_calls && response.tool_calls.length > 0) {
for (const toolCall of response.tool_calls) {
await tool.invoke(toolCall);
}
} else {
console.log(response.content);
}
4. 多次绑定工具
const model = new ChatOpenAI({ model: "gpt-4.1" });
const withTool1 = model.bindTools([tool1]);
const withTool2 = withTool1.bindTools([tool2]);
const withBothTools = model.bindTools([tool1, tool2]);
5. 异步工具执行未等待
const toolResults = response.tool_calls.map(async (tc) => {
return await tool.invoke(tc);
});
messages.push(...toolResults);
const toolResults = await Promise.all(
response.tool_calls.map(tc => tool.invoke(tc))
);
messages.push(...toolResults);
const toolResults = [];
for (const toolCall of response.tool_calls) {
const result = await tool.invoke(toolCall);
toolResults.push(result);
}
6. 工具选择混淆
const model = new ChatOpenAI({ model: "gpt-4.1" });
model.bindTools([tool], "required");
model.bindTools([tool], { tool_choice: "any" });
model.bindTools([tool], { tool_choice: "tool_name" });
model.bindTools([tool]);
文档链接