Skip to main content

如何将 Runnables 转换为 Tools

前提条件

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

为了方便起见,接受字符串或对象输入的 Runnables 可以使用 asTool 方法转换为工具,该方法允许为参数指定名称、描述和额外的模式信息。

在这里,我们将演示如何使用此方法将 LangChain 的 Runnable 转换为可供代理、链或聊天模型使用的工具。

兼容性

该功能需要 @langchain/core>=0.2.16。有关升级的指南,请参见此处

asTool

相较于一般的 Runnables,Tools 有一些额外的要求:

  • 它们的输入被限制为可序列化的类型,具体来说是字符串和对象;
  • 它们包含名称和描述,用以说明其使用方式和时机;
  • 它们包含一个详细的 schema 属性用于描述参数。也就是说,虽然一个工具(作为 Runnable)可能接受单个对象作为输入,但用于构建该对象的具体键和类型信息应在 schema 字段中指定。

因此,asTool() 方法需要这些额外的信息才能从一个 runnable 创建一个工具。以下是一个基本示例:

import { RunnableLambda } from "@langchain/core/runnables";
import { z } from "zod";

const schema = z.object({
a: z.number(),
b: z.array(z.number()),
});

const runnable = RunnableLambda.from((input: z.infer<typeof schema>) => {
return input.a * Math.max(...input.b);
});

const asTool = runnable.asTool({
name: "My tool",
description: "Explanation of when to use the tool.",
schema,
});

asTool.description;
Explanation of when to use the tool.
await asTool.invoke({ a: 3, b: [1, 2] });
6

支持接收字符串输入的可运行对象:

const firstRunnable = RunnableLambda.from<string, string>((input) => {
return input + "a";
});

const secondRunnable = RunnableLambda.from<string, string>((input) => {
return input + "z";
});

const runnable = firstRunnable.pipe(secondRunnable);
const asTool = runnable.asTool({
name: "append_letters",
description: "Adds letters to a string.",
schema: z.string(),
});

asTool.description;
Adds letters to a string.
await asTool.invoke("b");
baz

在一个智能体中

下面我们会将 LangChain Runnables 作为工具整合进一个智能体应用中。我们将通过以下组件进行演示:

  • 一个文档检索器
  • 一个简单的RAG链,允许智能体将相关查询委托给该链。

我们首先实例化一个支持工具调用的聊天模型:

按照RAG 教程,我们首先构建一个检索器:

import { ChatOpenAI } from "@langchain/openai";

const llm = new ChatOpenAI({ model: "gpt-3.5-turbo-0125", temperature: 0 });

import { Document } from "@langchain/core/documents";
import { MemoryVectorStore } from "langchain/vectorstores/memory";
import { OpenAIEmbeddings } from "@langchain/openai";

const documents = [
new Document({
pageContent:
"Dogs are great companions, known for their loyalty and friendliness.",
}),
new Document({
pageContent: "Cats are independent pets that often enjoy their own space.",
}),
];

const vectorstore = await MemoryVectorStore.fromDocuments(
documents,
new OpenAIEmbeddings()
);

const retriever = vectorstore.asRetriever({
k: 1,
searchType: "similarity",
});

接下来,我们创建一个预置的 LangGraph 代理,并为其提供工具:

import { createReactAgent } from "@langchain/langgraph/prebuilt";

const tools = [
retriever.asTool({
name: "pet_info_retriever",
description: "Get information about pets.",
schema: z.string(),
}),
];

const agent = createReactAgent({ llm: llm, tools });

const stream = await agent.stream({
messages: [["human", "What are dogs known for?"]],
});

for await (const chunk of stream) {
// Log output from the agent or tools node
if (chunk.agent) {
console.log("AGENT:", chunk.agent.messages[0]);
} else if (chunk.tools) {
console.log("TOOLS:", chunk.tools.messages[0]);
}
console.log("----");
}
AGENT: AIMessage {
"id": "chatcmpl-9m9RIN1GQVeXcrVdp0lNBTcZFVHb9",
"content": "",
"additional_kwargs": {
"tool_calls": [
{
"id": "call_n30LPDbegmytrj5GdUxZt9xn",
"type": "function",
"function": "[Object]"
}
]
},
"response_metadata": {
"tokenUsage": {
"completionTokens": 17,
"promptTokens": 52,
"totalTokens": 69
},
"finish_reason": "tool_calls"
},
"tool_calls": [
{
"name": "pet_info_retriever",
"args": {
"input": "dogs"
},
"type": "tool_call",
"id": "call_n30LPDbegmytrj5GdUxZt9xn"
}
],
"invalid_tool_calls": [],
"usage_metadata": {
"input_tokens": 52,
"output_tokens": 17,
"total_tokens": 69
}
}
----
TOOLS: ToolMessage {
"content": "[{\"pageContent\":\"Dogs are great companions, known for their loyalty and friendliness.\",\"metadata\":{}}]",
"name": "pet_info_retriever",
"additional_kwargs": {},
"response_metadata": {},
"tool_call_id": "call_n30LPDbegmytrj5GdUxZt9xn"
}
----
AGENT: AIMessage {
"id": "chatcmpl-9m9RJ3TT3ITfv6R0Tb7pcrNOUtnm8",
"content": "Dogs are known for being great companions, known for their loyalty and friendliness.",
"additional_kwargs": {},
"response_metadata": {
"tokenUsage": {
"completionTokens": 18,
"promptTokens": 104,
"totalTokens": 122
},
"finish_reason": "stop"
},
"tool_calls": [],
"invalid_tool_calls": [],
"usage_metadata": {
"input_tokens": 104,
"output_tokens": 18,
"total_tokens": 122
}
}
----

这个 LangSmith 追踪 展示了上述运行背后的执行过程。

更进一步,我们甚至可以从一个完整的 RAG 链 创建一个工具:

import { StringOutputParser } from "@langchain/core/output_parsers";
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { RunnableSequence } from "@langchain/core/runnables";

const SYSTEM_TEMPLATE = `
You are an assistant for question-answering tasks.
Use the below context to answer the question. If
you don't know the answer, say you don't know.
Use three sentences maximum and keep the answer
concise.

Answer in the style of {answer_style}.

Context: {context}`;

const prompt = ChatPromptTemplate.fromMessages([
["system", SYSTEM_TEMPLATE],
["human", "{question}"],
]);

const ragChain = RunnableSequence.from([
{
context: (input, config) => retriever.invoke(input.question, config),
question: (input) => input.question,
answer_style: (input) => input.answer_style,
},
prompt,
llm,
new StringOutputParser(),
]);

下面我们会再次调用该智能体。请注意,智能体会在其 tool_calls 中填充所需的参数:

const ragTool = ragChain.asTool({
name: "pet_expert",
description: "Get information about pets.",
schema: z.object({
context: z.string(),
question: z.string(),
answer_style: z.string(),
}),
});

const agent = createReactAgent({ llm: llm, tools: [ragTool] });

const stream = await agent.stream({
messages: [["human", "What would a pirate say dogs are known for?"]],
});

for await (const chunk of stream) {
// Log output from the agent or tools node
if (chunk.agent) {
console.log("AGENT:", chunk.agent.messages[0]);
} else if (chunk.tools) {
console.log("TOOLS:", chunk.tools.messages[0]);
}
console.log("----");
}
AGENT: AIMessage {
"id": "chatcmpl-9m9RKY2nAa8LeGoBiO7N1SR4nAoED",
"content": "",
"additional_kwargs": {
"tool_calls": [
{
"id": "call_ukzivO4jRn1XdDpuVTI6CvtU",
"type": "function",
"function": "[Object]"
}
]
},
"response_metadata": {
"tokenUsage": {
"completionTokens": 30,
"promptTokens": 63,
"totalTokens": 93
},
"finish_reason": "tool_calls"
},
"tool_calls": [
{
"name": "pet_expert",
"args": {
"context": "pirate",
"question": "What are dogs known for?",
"answer_style": "short"
},
"type": "tool_call",
"id": "call_ukzivO4jRn1XdDpuVTI6CvtU"
}
],
"invalid_tool_calls": [],
"usage_metadata": {
"input_tokens": 63,
"output_tokens": 30,
"total_tokens": 93
}
}
----
TOOLS: ToolMessage {
"content": "Dogs are known for their loyalty, companionship, and ability to provide emotional support to their owners.",
"name": "pet_expert",
"additional_kwargs": {},
"response_metadata": {},
"tool_call_id": "call_ukzivO4jRn1XdDpuVTI6CvtU"
}
----
AGENT: AIMessage {
"id": "chatcmpl-9m9RMwAEc14TTKtitq3CH2x9wpGik",
"content": "A pirate would say that dogs are known for their loyalty, companionship, and ability to provide emotional support to their owners.",
"additional_kwargs": {},
"response_metadata": {
"tokenUsage": {
"completionTokens": 26,
"promptTokens": 123,
"totalTokens": 149
},
"finish_reason": "stop"
},
"tool_calls": [],
"invalid_tool_calls": [],
"usage_metadata": {
"input_tokens": 123,
"output_tokens": 26,
"total_tokens": 149
}
}
----

查看上述运行的 LangSmith 跟踪,了解内部运行情况。

相关内容


Was this page helpful?


You can also leave detailed feedback on GitHub.