Skip to main content

如何创建工具

预备知识

本指南假定您熟悉以下概念:

在构建自己的代理时,您需要为其提供一个可供使用的工具列表。虽然 LangChain 包含一些预置工具,但使用自定义逻辑的工具通常会更加有用。本指南将带您了解一些创建自定义工具的方法。

这里最大的区别在于第一个函数需要包含多个输入字段的对象,而第二个函数只接受包含单个字段的对象。一些较旧的代理只能与需要单一输入的函数配合使用,因此了解这种区别非常重要。

LangChain 提供了多种为不同应用场景构建工具的方法。下面我将展示创建工具的两种最常见方式,以及它们的适用场景。

工具模式

兼容性

仅在 @langchain/core 0.2.19 及以上版本中可用。

创建工具的最简单方法是通过 StructuredToolParams 模式。LangChain 中每个支持工具调用的聊天模型都接受通过此模式将工具绑定到模型。此模式仅包含三个字段:

  • name - 工具的名称。
  • schema - 使用 Zod 对象定义的工具模式。
  • description(可选) - 工具的描述。

该模式不包含与工具配对的函数,因此应仅在生成的输出不需要作为参数传递给函数的情况下使用。

import { z } from "zod";
import { StructuredToolParams } from "@langchain/core/tools";

const simpleToolSchema: StructuredToolParams = {
name: "get_current_weather",
description: "Get the current weather for a location",
schema: z.object({
city: z.string().describe("The city to get the weather for"),
state: z.string().optional().describe("The state to get the weather for"),
}),
};

tool 函数

兼容性

仅适用于 @langchain/core 0.2.7 及以上版本。

tool 包装函数是一种将 JavaScript 函数转换为工具的便捷方法。它需要该函数本身以及一些定义工具的附加参数。当生成的工具调用执行一个函数时,应优先使用此方法而非 StructuredToolParams 工具。其中最重要的参数包括:

  • 工具的 name,LLM 将使用它作为上下文以及引用该工具的标识
  • 可选但建议提供的 description,LLM 将使用它作为上下文以了解何时使用该工具
  • 一个 schema,用于定义工具输入的结构

tool 函数将返回 StructuredTool 类的一个实例,因此它与 LangChain 库中所有现有的工具调用基础设施兼容。

import { z } from "zod";
import { tool } from "@langchain/core/tools";

const adderSchema = z.object({
a: z.number(),
b: z.number(),
});
const adderTool = tool(
async (input): Promise<string> => {
const sum = input.a + input.b;
return `The sum of ${input.a} and ${input.b} is ${sum}`;
},
{
name: "adder",
description: "Adds two numbers together",
schema: adderSchema,
}
);

await adderTool.invoke({ a: 1, b: 2 });
"The sum of 1 and 2 is 3"

DynamicStructuredTool

你还可以使用 DynamicStructuredTool 类来声明工具。以下是一个示例——请注意,工具必须始终返回字符串!

import { DynamicStructuredTool } from "@langchain/core/tools";
import { z } from "zod";

const multiplyTool = new DynamicStructuredTool({
name: "multiply",
description: "multiply two numbers together",
schema: z.object({
a: z.number().describe("the first number to multiply"),
b: z.number().describe("the second number to multiply"),
}),
func: async ({ a, b }: { a: number; b: number }) => {
return (a * b).toString();
},
});

await multiplyTool.invoke({ a: 8, b: 9 });
"72"

DynamicTool

对于需要工具只接受单个输入的旧代理,您可以将相关参数传递给 DynamicTool 类。这在使用仅支持接受单个输入工具的旧代理时非常有用。在这种情况下,不需要模式:

import { DynamicTool } from "@langchain/core/tools";

const searchTool = new DynamicTool({
name: "search",
description: "look things up online",
func: async (_input: string) => {
return "LangChain";
},
});

await searchTool.invoke("foo");
"LangChain"

返回工具执行的产物

有时,工具执行会产生一些产物,我们希望这些产物对链或代理中的下游组件可用,但不希望将其暴露给模型本身。例如,如果某个工具返回了自定义对象(如 Documents),我们可能希望向模型传递该输出的某些视图或元数据,而不是直接传递原始输出。与此同时,我们可能希望在其他地方(例如下游工具中)能够访问到这个完整输出。

ToolToolMessage 接口使得我们可以区分工具输出中面向模型的部分(ToolMessage.content)和面向模型之外使用的部分(ToolMessage.artifact)。

兼容性

此功能在 @langchain/core>=0.2.16 中添加。请确保你的包版本是最新的。

如果你想让你的工具区分消息内容和其他产物,我们需要做三件事:

  • 在定义工具时,将 response_format 参数设置为 "content_and_artifact"
  • 确保返回一个 [content, artifact] 形式的元组。
  • 使用 ToolCall(像工具调用模型生成的那些)来调用工具,而不是直接使用所需的 schema。

下面是一个示例。首先,创建一个新工具:

import { z } from "zod";
import { tool } from "@langchain/core/tools";

const randomIntToolSchema = z.object({
min: z.number(),
max: z.number(),
size: z.number(),
});

const generateRandomInts = tool(
async ({ min, max, size }) => {
const array: number[] = [];
for (let i = 0; i < size; i++) {
array.push(Math.floor(Math.random() * (max - min + 1)) + min);
}
return [
`Successfully generated array of ${size} random ints in [${min}, ${max}].`,
array,
];
},
{
name: "generateRandomInts",
description: "Generate size random ints in the range [min, max].",
schema: randomIntToolSchema,
responseFormat: "content_and_artifact",
}
);

如果您直接使用工具参数调用我们的工具,您将只获得输出中的content部分:

await generateRandomInts.invoke({ min: 0, max: 9, size: 10 });
"Successfully generated array of 10 random ints in [0, 9]."

但如果你通过 ToolCall 调用我们的工具,你将收到一个包含 Tool 生成的内容和产物的 ToolMessage:

await generateRandomInts.invoke({
name: "generateRandomInts",
args: { min: 0, max: 9, size: 10 },
id: "123", // required
type: "tool_call",
});
ToolMessage {
lc_serializable: true,
lc_kwargs: {
content: "Successfully generated array of 10 random ints in [0, 9].",
artifact: [
7, 7, 1, 4, 8,
4, 8, 3, 0, 9
],
tool_call_id: "123",
name: "generateRandomInts",
additional_kwargs: {},
response_metadata: {}
},
lc_namespace: [ "langchain_core", "messages" ],
content: "Successfully generated array of 10 random ints in [0, 9].",
name: "generateRandomInts",
additional_kwargs: {},
response_metadata: {},
id: undefined,
tool_call_id: "123",
artifact: [
7, 7, 1, 4, 8,
4, 8, 3, 0, 9
]
}

相关内容

你现在已了解了几种在 LangChain 中创建自定义工具的方法。

接下来,你可能有兴趣学习如何使用聊天模型调用工具

你还可以查看如何创建自己的其他模块的自定义版本


Was this page helpful?


You can also leave detailed feedback on GitHub.