Skip to main content

How to use Runnables as Tools

Prerequisites

This guide assumes familiarity with the following concepts:

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:

  • a document retriever;
  • a simple RAG chain, allowing an agent to delegate relevant queries to it.

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: []
}
----
tip

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
}
----
tip

See LangSmith trace for the above run.


Was this page helpful?


You can also leave detailed feedback on GitHub.