How to use Runnables as Tools
Here we will demonstrate how to convert a LangChain Runnable
into a
tool that can be used by agents, chains, or chat models.
Dependenciesβ
Note: this guide requires @langchain/core
>= 0.2.16. We will also
use OpenAI for embeddings, but
any LangChain embeddings should suffice. We will use a simple
LangGraph agent for
demonstration purposes.
@langchain/core @langchain/langgraph @langchain/openai zod
LangChain [tools](/docs/concepts#tools) are interfaces that an agent, chain, or chat model can use to interact with the world. See [here](/docs/how_to/#tools) for how-to guides covering tool-calling, built-in tools, custom tools, and more information.
LangChain tools-- instances of [StructuredTool](https://v02.api.js.langchain.com/classes/langchain_core_tools.StructuredTool.html)-- are [Runnables](/docs/concepts/#interface) with additional constraints that enable them to be invoked effectively by language models:
- Their inputs are constrained to be serializable, specifically strings and objects;
- They contain names and descriptions indicating how and when they should be used;
- They contain a detailed `schema` property for their arguments. That is, while a tool (as a `Runnable`) might accept a single object input, the specific keys and type information needed to populate an object should be specified in the `schema` field.
Runnables that accept string or object inputs can be converted to tools using the [`asTool`](https://api.js.langchain.com/classes/langchain_core_runnables.Runnable.html#asTool) method, which allows for the specification of names, descriptions, and additional schema information for arguments.
## Basic usage
With object input:
::: {.cell execution_count=1}
``` {.typescript .cell-code}
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<z.infer<typeof schema>, number>((input) => {
return input.a * Math.max(...input.b);
})
const asTool = runnable.asTool({
name: "My tool",
description: "Explanation of when to use tool.",
schema,
})
:::
console.log(asTool.description);
Explanation of when to use tool.
await asTool.invoke({ a: 3, b: [1, 2] });
6
String input is also supported:
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({
schema: z.string(),
});
await asTool.invoke("b");
baz
In agentsβ
Below we will incorporate LangChain Runnables as tools in an agent application. We will demonstrate with:
We first instantiate a chat model that supports tool calling:
Following the RAG tutorial, letβs first construct a retriever:
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",
});
We next create use a simple pre-built LangGraph agent and provide it the tool:
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, tools });
for await (const chunk of await agent.stream({
messages: [["human", "What are dogs known for?"]],
})) {
if (chunk.tools) {
console.log("ToolMessage", {
name: chunk.tools.messages[0].name,
content: chunk.tools.messages[0].content,
tool_call_id: chunk.tools.messages[0].tool_call_id,
});
} else if (chunk.agent) {
console.log("AIMessage", {
content: chunk.agent.messages[0].content,
tool_calls: chunk.agent.messages[0].tool_calls,
});
}
console.log("----");
}
AIMessage {
content: '',
tool_calls: [
{
name: 'pet_info_retriever',
args: [Object],
type: 'tool_call',
id: 'call_HAyKm6RayDyup1aj75Jfxl11'
}
]
}
----
ToolMessage {
name: 'pet_info_retriever',
content: '[{"pageContent":"Dogs are great companions, known for their loyalty and friendliness.","metadata":{}}]',
tool_call_id: 'call_HAyKm6RayDyup1aj75Jfxl11'
}
----
AIMessage {
content: 'Dogs are known for being great companions, known for their loyalty and friendliness.',
tool_calls: []
}
----
See LangSmith trace for the above run.
Going further, we can create a simple RAG chain that takes an additional parameterβ here, the βstyleβ of the answer.
import { StringOutputParser } from "@langchain/core/output_parsers";
import { ChatPromptTemplate } from "@langchain/core/prompts";
import {
RunnablePassthrough,
RunnableSequence,
} from "@langchain/core/runnables";
const prompt = ChatPromptTemplate.fromMessages([
[
"system",
`
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}.
Question: {question}
Context: {context}
`,
],
]);
type RagChainInput = {
question: string;
answer_style: string;
};
const ragChain = RunnableSequence.from([
RunnablePassthrough.assign({
context: async (input: RagChainInput, config) =>
retriever.invoke(input.question, config),
}),
prompt,
llm,
new StringOutputParser(),
]);
const ragTool = ragChain.asTool({
name: "pet_expert",
description: "Get information about pets.",
schema: z.object({
question: z.string(),
answer_style: z.string(),
}),
});
Below we again invoke the agent. Note that the agent populates the
required parameters in its tool_calls
:
const agent = createReactAgent({ llm, tools: [ragTool] });
for await (const chunk of await agent.stream({
messages: [["human", "What would a pirate say dogs are known for?"]],
})) {
if (chunk.tools) {
console.log("ToolMessage", {
name: chunk.tools.messages[0].name,
content: chunk.tools.messages[0].content,
tool_call_id: chunk.tools.messages[0].tool_call_id,
});
} else if (chunk.agent) {
console.log("AIMessage", {
content: chunk.agent.messages[0].content,
tool_calls: chunk.agent.messages[0].tool_calls[0],
});
}
console.log("----");
}
AIMessage {
content: '',
tool_calls: {
name: 'pet_expert',
args: { question: 'What are dogs known for?', answer_style: 'pirate' },
type: 'tool_call',
id: 'call_rWmwOVaPNaJ1PvrrN8QJDOeu'
}
}
----
ToolMessage {
name: 'pet_expert',
content: "Arrr, dogs be known fer their loyalty to their mateys, their keen sense o' smell, and their barkin' to alert ye of danger. They be good companions on land and sea, always ready to stand by yer side. Dogs be fetchin', protectin', and bringin' joy to all who befriend 'em.",
tool_call_id: 'call_rWmwOVaPNaJ1PvrrN8QJDOeu'
}
----
AIMessage {
content: "A pirate would say that dogs are known for their loyalty to their mateys, their keen sense of smell, and their barkin' to alert ye of danger. They are good companions on land and sea, always ready to stand by yer side. Dogs be fetchin', protectin', and bringin' joy to all who befriend 'em.",
tool_calls: undefined
}
----
See LangSmith trace for the above run.