|
|
from __future__ import annotations |
|
|
|
|
|
from typing import List, Optional, Sequence, Union |
|
|
|
|
|
from langchain_core.language_models import BaseLanguageModel |
|
|
from langchain_core.prompts import BasePromptTemplate |
|
|
from langchain_core.runnables import Runnable, RunnablePassthrough |
|
|
from langchain_core.tools import BaseTool |
|
|
from langchain_core.tools.render import ToolsRenderer, render_text_description |
|
|
|
|
|
from langchain.agents import AgentOutputParser |
|
|
from langchain.agents.format_scratchpad import format_log_to_str |
|
|
from langchain.agents.output_parsers import ReActSingleInputOutputParser |
|
|
|
|
|
|
|
|
def create_react_agent( |
|
|
llm: BaseLanguageModel, |
|
|
tools: Sequence[BaseTool], |
|
|
prompt: BasePromptTemplate, |
|
|
output_parser: Optional[AgentOutputParser] = None, |
|
|
tools_renderer: ToolsRenderer = render_text_description, |
|
|
*, |
|
|
stop_sequence: Union[bool, List[str]] = True, |
|
|
) -> Runnable: |
|
|
"""Create an agent that uses ReAct prompting. |
|
|
|
|
|
Based on paper "ReAct: Synergizing Reasoning and Acting in Language Models" |
|
|
(https://arxiv.org/abs/2210.03629) |
|
|
|
|
|
.. warning:: |
|
|
This implementation is based on the foundational ReAct paper but is older and not well-suited for production applications. |
|
|
For a more robust and feature-rich implementation, we recommend using the `create_react_agent` function from the LangGraph library. |
|
|
See the [reference doc](https://langchain-ai.github.io/langgraph/reference/prebuilt/#langgraph.prebuilt.chat_agent_executor.create_react_agent) |
|
|
for more information. |
|
|
|
|
|
Args: |
|
|
llm: LLM to use as the agent. |
|
|
tools: Tools this agent has access to. |
|
|
prompt: The prompt to use. See Prompt section below for more. |
|
|
output_parser: AgentOutputParser for parse the LLM output. |
|
|
tools_renderer: This controls how the tools are converted into a string and |
|
|
then passed into the LLM. Default is `render_text_description`. |
|
|
stop_sequence: bool or list of str. |
|
|
If True, adds a stop token of "Observation:" to avoid hallucinates. |
|
|
If False, does not add a stop token. |
|
|
If a list of str, uses the provided list as the stop tokens. |
|
|
|
|
|
Default is True. You may to set this to False if the LLM you are using |
|
|
does not support stop sequences. |
|
|
|
|
|
Returns: |
|
|
A Runnable sequence representing an agent. It takes as input all the same input |
|
|
variables as the prompt passed in does. It returns as output either an |
|
|
AgentAction or AgentFinish. |
|
|
|
|
|
Examples: |
|
|
|
|
|
.. code-block:: python |
|
|
|
|
|
from langchain import hub |
|
|
from langchain_community.llms import OpenAI |
|
|
from langchain.agents import AgentExecutor, create_react_agent |
|
|
|
|
|
prompt = hub.pull("hwchase17/react") |
|
|
model = OpenAI() |
|
|
tools = ... |
|
|
|
|
|
agent = create_react_agent(model, tools, prompt) |
|
|
agent_executor = AgentExecutor(agent=agent, tools=tools) |
|
|
|
|
|
agent_executor.invoke({"input": "hi"}) |
|
|
|
|
|
# Use with chat history |
|
|
from langchain_core.messages import AIMessage, HumanMessage |
|
|
agent_executor.invoke( |
|
|
{ |
|
|
"input": "what's my name?", |
|
|
# Notice that chat_history is a string |
|
|
# since this prompt is aimed at LLMs, not chat models |
|
|
"chat_history": "Human: My name is Bob\\nAI: Hello Bob!", |
|
|
} |
|
|
) |
|
|
|
|
|
Prompt: |
|
|
|
|
|
The prompt must have input keys: |
|
|
* `tools`: contains descriptions and arguments for each tool. |
|
|
* `tool_names`: contains all tool names. |
|
|
* `agent_scratchpad`: contains previous agent actions and tool outputs as a string. |
|
|
|
|
|
Here's an example: |
|
|
|
|
|
.. code-block:: python |
|
|
|
|
|
from langchain_core.prompts import PromptTemplate |
|
|
|
|
|
template = '''Answer the following questions as best you can. You have access to the following tools: |
|
|
|
|
|
{tools} |
|
|
|
|
|
Use the following format: |
|
|
|
|
|
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 |
|
|
|
|
|
Begin! |
|
|
|
|
|
Question: {input} |
|
|
Thought:{agent_scratchpad}''' |
|
|
|
|
|
prompt = PromptTemplate.from_template(template) |
|
|
""" |
|
|
missing_vars = {"tools", "tool_names", "agent_scratchpad"}.difference( |
|
|
prompt.input_variables + list(prompt.partial_variables) |
|
|
) |
|
|
if missing_vars: |
|
|
raise ValueError(f"Prompt missing required variables: {missing_vars}") |
|
|
|
|
|
prompt = prompt.partial( |
|
|
tools=tools_renderer(list(tools)), |
|
|
tool_names=", ".join([t.name for t in tools]), |
|
|
) |
|
|
if stop_sequence: |
|
|
stop = ["\nObservation"] if stop_sequence is True else stop_sequence |
|
|
llm_with_stop = llm.bind(stop=stop) |
|
|
else: |
|
|
llm_with_stop = llm |
|
|
output_parser = output_parser or ReActSingleInputOutputParser() |
|
|
agent = ( |
|
|
RunnablePassthrough.assign( |
|
|
agent_scratchpad=lambda x: format_log_to_str(x["intermediate_steps"]), |
|
|
) |
|
|
| prompt |
|
|
| llm_with_stop |
|
|
| output_parser |
|
|
) |
|
|
return agent |
|
|
|