!function(){function t(t){document.documentElement.setAttribute("data-theme",t)}var e=function(){var t=null;try{t=new URLSearchParams(window.location.search).get("docusaurus-theme")}catch(t){}return t}()||function(){var t=null;try{t=localStorage.getItem("theme")}catch(t){}return t}();t(null!==e?e:"light")}(),document.documentElement.setAttribute("data-announcement-bar-initially-dismissed",function(){try{return"true"===localStorage.getItem("docusaurus.announcement.dismiss")}catch(t){}return!1}())

[Skip to main content](#__docusaurus_skipToContent_fallback)

LangChain v0.2 is coming soon! Preview the new docs [here](/v0.2/docs/introduction/).

[

![🦜️🔗 Langchain](/v0.1/img/brand/wordmark.png)![🦜️🔗 Langchain](/v0.1/img/brand/wordmark-dark.png)

](/v0.1/)[Docs](/v0.1/docs/get_started/introduction/)[Use cases](/v0.1/docs/use_cases/)[Integrations](/v0.1/docs/integrations/platforms/)[API Reference](https://api.js.langchain.com)

[More](#)

*   [People](/v0.1/docs/people/)
*   [Community](/v0.1/docs/community/)
*   [Tutorials](/v0.1/docs/additional_resources/tutorials/)
*   [Contributing](/v0.1/docs/contributing/)

[v0.1](#)

*   [v0.2](https://js.langchain.com/v0.2/docs/introduction)
*   [v0.1](/v0.1/docs/get_started/introduction/)

[🦜🔗](#)

*   [LangSmith](https://smith.langchain.com)
*   [LangSmith Docs](https://docs.smith.langchain.com)
*   [LangChain Hub](https://smith.langchain.com/hub)
*   [LangServe](https://github.com/langchain-ai/langserve)
*   [Python Docs](https://python.langchain.com/)

[Chat](https://chatjs.langchain.com)[](https://github.com/langchain-ai/langchainjs)

Search

*   [Get started](/v0.1/docs/get_started/)
    
    *   [Introduction](/v0.1/docs/get_started/introduction/)
    *   [Installation](/v0.1/docs/get_started/installation/)
    *   [Quickstart](/v0.1/docs/get_started/quickstart/)
*   [LangChain Expression Language](/v0.1/docs/expression_language/)
    
    *   [Get started](/v0.1/docs/expression_language/get_started/)
    *   [Why use LCEL?](/v0.1/docs/expression_language/why/)
    *   [Interface](/v0.1/docs/expression_language/interface/)
    *   [Streaming](/v0.1/docs/expression_language/streaming/)
    *   [How to](/v0.1/docs/expression_language/how_to/routing/)
        
    *   [Cookbook](/v0.1/docs/expression_language/cookbook/)
        
    *   [LangChain Expression Language (LCEL)](/v0.1/docs/expression_language/)
*   [Modules](/v0.1/docs/modules/)
    
    *   [Model I/O](/v0.1/docs/modules/model_io/)
        
    *   [Retrieval](/v0.1/docs/modules/data_connection/)
        
    *   [Chains](/v0.1/docs/modules/chains/)
    *   [Agents](/v0.1/docs/modules/agents/)
        
        *   [Quick start](/v0.1/docs/modules/agents/quick_start/)
        *   [Concepts](/v0.1/docs/modules/agents/concepts/)
        *   [Agent Types](/v0.1/docs/modules/agents/agent_types/)
            
        *   [How-to](/v0.1/docs/modules/agents/how_to/custom_agent/)
            
            *   [Custom agent](/v0.1/docs/modules/agents/how_to/custom_agent/)
            *   [Returning structured output](/v0.1/docs/modules/agents/how_to/agent_structured/)
            *   [Subscribing to events](/v0.1/docs/modules/agents/how_to/callbacks/)
            *   [Cancelling requests](/v0.1/docs/modules/agents/how_to/cancelling_requests/)
            *   [Custom LLM Agent](/v0.1/docs/modules/agents/how_to/custom_llm_agent/)
            *   [Custom LLM Agent (with a ChatModel)](/v0.1/docs/modules/agents/how_to/custom_llm_chat_agent/)
            *   [Custom MRKL agent](/v0.1/docs/modules/agents/how_to/custom_mrkl_agent/)
            *   [Handle parsing errors](/v0.1/docs/modules/agents/how_to/handle_parsing_errors/)
            *   [Access intermediate steps](/v0.1/docs/modules/agents/how_to/intermediate_steps/)
            *   [Logging and tracing](/v0.1/docs/modules/agents/how_to/logging_and_tracing/)
            *   [Cap the max number of iterations](/v0.1/docs/modules/agents/how_to/max_iterations/)
            *   [Streaming](/v0.1/docs/modules/agents/how_to/streaming/)
            *   [Timeouts for agents](/v0.1/docs/modules/agents/how_to/timeouts/)
        *   [Agents](/v0.1/docs/modules/agents/)
        *   [Tools](/v0.1/docs/modules/agents/tools/)
            
    *   [More](/v0.1/docs/modules/memory/)
        
*   [Security](/v0.1/docs/security/)
*   [Guides](/v0.1/docs/guides/)
    
*   [Ecosystem](/v0.1/docs/ecosystem/)
    
*   [LangGraph](/v0.1/docs/langgraph/)
*   * * *
    

*   [](/v0.1/)
*   [Modules](/v0.1/docs/modules/)
*   [Agents](/v0.1/docs/modules/agents/)
*   How-to
*   Custom MRKL agent

On this page

Custom MRKL agent
=================

This notebook goes through how to create your own custom Modular Reasoning, Knowledge and Language (MRKL, pronounced “miracle”) agent using LCEL.

A MRKL agent consists of three parts:

*   Tools: The tools the agent has available to use.
*   `Runnable`: The `Runnable` that produces the text that is parsed in a certain way to determine which action to take.
*   The agent class itself: this parses the output of the LLMChain to determine which action to take.

In this notebook we walk through how to create a custom MRKL agent by creating a custom `Runnable`.

Custom `Runnable`[​](#custom-runnable "Direct link to custom-runnable")
-----------------------------------------------------------------------

The first way to create a custom agent is to use a custom `Runnable`.

Most of the work in creating the custom `Runnable` comes down to the input and outputs. Because we're using a custom `Runnable` we are not provided with any pre-built input/output parsers. Instead, we must create our own to format the inputs and outputs a the way we define.

Additionally, we need an `agent_scratchpad` input variable to put notes on previous actions and observations. This should almost always be the final part of the prompt. This is a very important step, because without the `agent_scratchpad` the agent will have no context on the previous actions it has taken.

To ensure the prompt we create contains the appropriate instructions and input variables, we'll create a helper function which takes in a list of input variables, and returns the final formatted prompt. We will also do something similar with the output parser, ensuring our input prompts and outputs are always formatted the same way.

The first step is to import all the necessary modules.

tip

See [this section for general instructions on installing integration packages](/v0.1/docs/get_started/installation/#installing-integration-packages).

*   npm
*   Yarn
*   pnpm

    npm install @langchain/openai

    yarn add @langchain/openai

    pnpm add @langchain/openai

    import { AgentExecutor } from "langchain/agents";import { ChatOpenAI } from "@langchain/openai";import { PromptTemplate } from "langchain/prompts";import {  AgentAction,  AgentFinish,  AgentStep,  BaseMessage,  InputValues,  SystemMessage,} from "langchain/schema";import { RunnableSequence } from "@langchain/core/runnables";import { SerpAPI } from "langchain/tools";import { Calculator } from "langchain/tools/calculator";

Next, we'll instantiate our chat model and tools.

    /** * Instantiate the chat model and bind the stop token * @important The stop token must be set, if not the LLM will happily continue generating text forever. */const model = new ChatOpenAI({ temperature: 0 }).bind({  stop: ["\nObservation"],});/** Define the tools */const tools = [  new SerpAPI(process.env.SERPAPI_API_KEY, {    location: "Austin,Texas,United States",    hl: "en",    gl: "us",  }),  new Calculator(),];

After this, we can define our prompts using the `PromptTemplate` class. This will come in handy later when formatting our prompts as it provides a helper `.format()` method we'll use.

    const PREFIX = new PromptTemplate({  template: `Answer the following questions as best you can, but speaking as a pirate might speak. You have access to the following tools:  {tools}`,  inputVariables: ["tools"],});/** * @important This prompt is used as an example for the LLM on how it should * respond to the user. */const TOOL_INSTRUCTIONS = new PromptTemplate({  template: `Use the following format in your response:  Question: the input question you must answer  Thought: you should always think about what to do  Action: the action to take, should be one of [{tool_names}]  Action Input: the input to the action  Observation: the result of the action  ... (this Thought/Action/Action Input/Observation can repeat N times)  Thought: I now know the final answer  Final Answer: the final answer to the original input question`,  inputVariables: ["tool_names"],});const SUFFIX = new PromptTemplate({  template: `Begin! Remember to speak as a pirate when giving your final answer. Use lots of "Args  Question: {input}  {agent_scratchpad}`,  inputVariables: ["input", "agent_scratchpad"],});

Once we've defined our prompts we can create our custom input prompt formatter. This function will take in an arbatary list of inputs and return a formatted prompt. Inside we're first checking `input` and `agent_scratchpad` exist on the input. Then, we're formatting the `agent_scratchpad` as a string in a way the LLM can easily interpret. Finally, we call the `.format()` methods on all our prompts, passing in the input variables. With these, we can then return the final prompt inside a `SystemMessage`.

    async function formatPrompt(inputValues: InputValues) {  /** Verify input and agent_scratchpad exist in the input object. */  if (!("input" in inputValues) || !("agent_scratchpad" in inputValues)) {    throw new Error(      `Missing input or agent_scratchpad in input object: ${JSON.stringify(        inputValues      )}`    );  }  const input = inputValues.input as string;  /** agent_scratchpad will be undefined on the first iteration. */  const agentScratchpad = (inputValues.agent_scratchpad ?? []) as AgentStep[];  /** Convert the list of AgentStep's into a more agent friendly string format. */  const formattedScratchpad = agentScratchpad.reduce(    (thoughts, { action, observation }) =>      thoughts +      [action.log, `\nObservation: ${observation}`, "Thought:"].join("\n"),    ""  );  /** Format our prompts by passing in the input variables */  const formattedPrefix = await PREFIX.format({    tools: tools      .map((tool) => `Name: ${tool.name}\nDescription: ${tool.description}`)      .join("\n"),  });  const formattedToolInstructions = await TOOL_INSTRUCTIONS.format({    tool_names: tools.map((tool) => tool.name).join(", "),  });  const formattedSuffix = await SUFFIX.format({    input,    agent_scratchpad: formattedScratchpad,  });  /** Join all the prompts together, and return as an instance of `SystemMessage` */  const formatted = [    formattedPrefix,    formattedToolInstructions,    formattedSuffix,  ].join("\n");  return [new SystemMessage(formatted)];}

If we're curious as to what the final prompt looks like, we can console log it before returning. It should look like this:

    console.log(new SystemMessage(formatted));

    Answer the following questions as best you can, but speaking as a pirate might speak. You have access to the following tools:  Name: searchDescription: a search engine. useful for when you need to answer questions about current events. input should be a search query.Name: calculatorDescription: Useful for getting the result of a math expression. The input to this tool should be a valid mathematical expression that could be executed by a simple calculator.Use the following format in your response:  Question: the input question you must answer  Thought: you should always think about what to do  Action: the action to take, should be one of [search, calculator]  Action Input: the input to the action  Observation: the result of the action  ... (this Thought/Action/Action Input/Observation can repeat N times)  Thought: I now know the final answer  Final Answer: the final answer to the original input questionBegin! Remember to speak as a pirate when giving your final answer. Use lots of "Args  Question: How many people live in canada as of 2023?

If you want to dive deeper and see the exact steps, inputs, outputs and more you can use [LangSmith](https://docs.smith.langchain.com/) to visualize your agent.

After defining our input prompt formatter, we can define our output parser. This function takes in a message in the form of `BaseMessage`, parses it and either returns an instance of `AgentAction` if there is more work to be done, or `AgentFinish` if the agent is done.

    function customOutputParser(message: BaseMessage): AgentAction | AgentFinish {  const text = message.content;  /** If the input includes "Final Answer" return as an instance of `AgentFinish` */  if (text.includes("Final Answer:")) {    const parts = text.split("Final Answer:");    const input = parts[parts.length - 1].trim();    const finalAnswers = { output: input };    return { log: text, returnValues: finalAnswers };  }  /** Use RegEx to extract any actions and their values */  const match = /Action: (.*)\nAction Input: (.*)/s.exec(text);  if (!match) {    throw new Error(`Could not parse LLM output: ${text}`);  }  /** Return as an instance of `AgentAction` */  return {    tool: match[1].trim(),    toolInput: match[2].trim().replace(/^"+|"+$/g, ""),    log: text,  };}

After this, all that is left is to chain all the pieces together using a `RunnableSequence` and pass it through to the `AgentExecutor` so we can execute our agent.

    const runnable = RunnableSequence.from([  {    input: (i: InputValues) => i.input,    agent_scratchpad: (i: InputValues) => i.steps,  },  formatPrompt,  model,  customOutputParser,]);/** Pass our runnable to `AgentExecutor` to make our agent executable */const executor = AgentExecutor.fromAgentAndTools({  agent: runnable,  tools,});

Once we have our `executor` calling the agent is simple!

    console.log("Loaded agent.");const input = `How many people live in canada as of 2023?`;console.log(`Executing with input "${input}"...`);const result = await executor.invoke({ input });console.log(`Got output ${result.output}`);/**  Loaded agent.  Executing with input "How many people live in canada as of 2023?"...  Got output Arrr, there be 38,781,291 people livin' in Canada in 2023. */

* * *

#### Help us out by providing feedback on this documentation page:

[

Previous

Custom LLM Agent (with a ChatModel)

](/v0.1/docs/modules/agents/how_to/custom_llm_chat_agent/)[

Next

Handle parsing errors

](/v0.1/docs/modules/agents/how_to/handle_parsing_errors/)

*   [Custom `Runnable`](#custom-runnable)

Community

*   [Discord](https://discord.gg/cU2adEyC7w)
*   [Twitter](https://twitter.com/LangChainAI)

GitHub

*   [Python](https://github.com/langchain-ai/langchain)
*   [JS/TS](https://github.com/langchain-ai/langchainjs)

More

*   [Homepage](https://langchain.com)
*   [Blog](https://blog.langchain.dev)

Copyright © 2024 LangChain, Inc.