from typing import Any, List, Sequence, Tuple, Union from langchain_core._api import deprecated from langchain_core.agents import AgentAction, AgentFinish from langchain_core.callbacks import Callbacks from langchain_core.language_models import BaseLanguageModel from langchain_core.prompts.base import BasePromptTemplate from langchain_core.prompts.chat import AIMessagePromptTemplate, ChatPromptTemplate from langchain_core.runnables import Runnable, RunnablePassthrough from langchain_core.tools import BaseTool from langchain.agents.agent import BaseSingleActionAgent from langchain.agents.format_scratchpad import format_xml from langchain.agents.output_parsers import XMLAgentOutputParser from langchain.agents.xml.prompt import agent_instructions from langchain.chains.llm import LLMChain from langchain.tools.render import ToolsRenderer, render_text_description @deprecated("0.1.0", alternative="create_xml_agent", removal="0.3.0") class XMLAgent(BaseSingleActionAgent): """Agent that uses XML tags. Args: tools: list of tools the agent can choose from llm_chain: The LLMChain to call to predict the next action Examples: .. code-block:: python from langchain.agents import XMLAgent from langchain tools = ... model = """ tools: List[BaseTool] """List of tools this agent has access to.""" llm_chain: LLMChain """Chain to use to predict action.""" @property def input_keys(self) -> List[str]: return ["input"] @staticmethod def get_default_prompt() -> ChatPromptTemplate: base_prompt = ChatPromptTemplate.from_template(agent_instructions) return base_prompt + AIMessagePromptTemplate.from_template( "{intermediate_steps}" ) @staticmethod def get_default_output_parser() -> XMLAgentOutputParser: return XMLAgentOutputParser() def plan( self, intermediate_steps: List[Tuple[AgentAction, str]], callbacks: Callbacks = None, **kwargs: Any, ) -> Union[AgentAction, AgentFinish]: log = "" for action, observation in intermediate_steps: log += ( f"{action.tool}{action.tool_input}" f"{observation}" ) tools = "" for tool in self.tools: tools += f"{tool.name}: {tool.description}\n" inputs = { "intermediate_steps": log, "tools": tools, "question": kwargs["input"], "stop": ["", ""], } response = self.llm_chain(inputs, callbacks=callbacks) return response[self.llm_chain.output_key] async def aplan( self, intermediate_steps: List[Tuple[AgentAction, str]], callbacks: Callbacks = None, **kwargs: Any, ) -> Union[AgentAction, AgentFinish]: log = "" for action, observation in intermediate_steps: log += ( f"{action.tool}{action.tool_input}" f"{observation}" ) tools = "" for tool in self.tools: tools += f"{tool.name}: {tool.description}\n" inputs = { "intermediate_steps": log, "tools": tools, "question": kwargs["input"], "stop": ["", ""], } response = await self.llm_chain.acall(inputs, callbacks=callbacks) return response[self.llm_chain.output_key] def create_xml_agent( llm: BaseLanguageModel, tools: Sequence[BaseTool], prompt: BasePromptTemplate, tools_renderer: ToolsRenderer = render_text_description, *, stop_sequence: Union[bool, List[str]] = True, ) -> Runnable: """Create an agent that uses XML to format its logic. Args: llm: LLM to use as the agent. tools: Tools this agent has access to. prompt: The prompt to use, must have input keys `tools`: contains descriptions for each tool. `agent_scratchpad`: contains previous agent actions and tool outputs. 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 "" 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. Example: .. code-block:: python from langchain import hub from langchain_community.chat_models import ChatAnthropic from langchain.agents import AgentExecutor, create_xml_agent prompt = hub.pull("hwchase17/xml-agent-convo") model = ChatAnthropic() tools = ... agent = create_xml_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 for each tool. * `agent_scratchpad`: contains previous agent actions and tool outputs as an XML string. Here's an example: .. code-block:: python from langchain_core.prompts import PromptTemplate template = '''You are a helpful assistant. Help the user answer any questions. You have access to the following tools: {tools} In order to use a tool, you can use and tags. You will then get back a response in the form For example, if you have a tool called 'search' that could run a google search, in order to search for the weather in SF you would respond: searchweather in SF 64 degrees When you are done, respond with a final answer between . For example: The weather in SF is 64 degrees Begin! Previous Conversation: {chat_history} Question: {input} {agent_scratchpad}''' prompt = PromptTemplate.from_template(template) """ # noqa: E501 missing_vars = {"tools", "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)), ) if stop_sequence: stop = [""] if stop_sequence is True else stop_sequence llm_with_stop = llm.bind(stop=stop) else: llm_with_stop = llm agent = ( RunnablePassthrough.assign( agent_scratchpad=lambda x: format_xml(x["intermediate_steps"]), ) | prompt | llm_with_stop | XMLAgentOutputParser() ) return agent