如何将 Runnables 转换为工具
为方便起见,接受字符串或对象输入的 Runnables
可以使用 asTool
方法转换为工具,该方法允许指定名称、描述以及参数的其他模式信息。
在这里,我们将演示如何使用此方法将 LangChain Runnable
转换为可供代理、链或聊天模型使用的工具。
兼容性
此功能需要 @langchain/core>=0.2.16
。请在此处查看升级指南。
asTool
与通用 Runnables 相比,工具还有一些额外的要求
- 它们的输入被限制为可序列化的,特别是字符串和对象;
- 它们包含名称和描述,指示如何以及何时应使用它们;
- 它们包含用于其参数的详细
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
也支持接受字符串输入的 Runnables
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 教程,我们首先构建一个检索器
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 追踪,了解上述运行在内部发生了什么。