mirror of
				https://github.com/hwchase17/langchain.git
				synced 2025-10-31 07:41:40 +00:00 
			
		
		
		
	Various improvements to the Agents & Callbacks sections of the documentation including formatting, spelling, and grammar fixes to improve readability.
		
			
				
	
	
		
			357 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			357 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| The LLM Agent is used in an `AgentExecutor`. This `AgentExecutor` can largely be thought of as a loop that:
 | |
| 1. Passes user input and any previous steps to the Agent (in this case, the LLM Agent)
 | |
| 2. If the Agent returns an `AgentFinish`, then return that directly to the user
 | |
| 3. If the Agent returns an `AgentAction`, then use that to call a tool and get an `Observation`
 | |
| 4. Repeat, passing the `AgentAction` and `Observation` back to the Agent until an `AgentFinish` is emitted.
 | |
| 
 | |
| `AgentAction` is a response that consists of `action` and `action_input`. `action` refers to which tool to use, and `action_input` refers to the input to that tool. `log` can also be provided as more context (that can be used for logging, tracing, etc).
 | |
| 
 | |
| `AgentFinish` is a response that contains the final message to be sent back to the user. This should be used to end an agent run.
 | |
| 
 | |
| In this notebook we walk through how to create a custom LLM agent.
 | |
| 
 | |
| 
 | |
| 
 | |
| ## Set up environment
 | |
| 
 | |
| Do necessary imports, etc.
 | |
| 
 | |
| 
 | |
| ```python
 | |
| from langchain.agents import Tool, AgentExecutor, LLMSingleActionAgent, AgentOutputParser
 | |
| from langchain.prompts import StringPromptTemplate
 | |
| from langchain import OpenAI, SerpAPIWrapper, LLMChain
 | |
| from typing import List, Union
 | |
| from langchain.schema import AgentAction, AgentFinish, OutputParserException
 | |
| import re
 | |
| ```
 | |
| 
 | |
| ## Set up tool
 | |
| 
 | |
| Set up any tools the agent may want to use. This may be necessary to put in the prompt (so that the agent knows to use these tools).
 | |
| 
 | |
| 
 | |
| ```python
 | |
| # Define which tools the agent can use to answer user queries
 | |
| search = SerpAPIWrapper()
 | |
| tools = [
 | |
|     Tool(
 | |
|         name = "Search",
 | |
|         func=search.run,
 | |
|         description="useful for when you need to answer questions about current events"
 | |
|     )
 | |
| ]
 | |
| ```
 | |
| 
 | |
| ## Prompt template
 | |
| 
 | |
| This instructs the agent on what to do. Generally, the template should incorporate:
 | |
| 
 | |
| - `tools`: which tools the agent has access and how and when to call them.
 | |
| - `intermediate_steps`: These are tuples of previous (`AgentAction`, `Observation`) pairs. These are generally not passed directly to the model, but the prompt template formats them in a specific way.
 | |
| - `input`: generic user input
 | |
| 
 | |
| 
 | |
| ```python
 | |
| # Set up the base template
 | |
| template = """Answer the following questions as best you can, but speaking as a pirate might speak. 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! Remember to speak as a pirate when giving your final answer. Use lots of "Arg"s
 | |
| 
 | |
| Question: {input}
 | |
| {agent_scratchpad}"""
 | |
| ```
 | |
| 
 | |
| 
 | |
| ```python
 | |
| # Set up a prompt template
 | |
| class CustomPromptTemplate(StringPromptTemplate):
 | |
|     # The template to use
 | |
|     template: str
 | |
|     # The list of tools available
 | |
|     tools: List[Tool]
 | |
| 
 | |
|     def format(self, **kwargs) -> str:
 | |
|         # Get the intermediate steps (AgentAction, Observation tuples)
 | |
|         # Format them in a particular way
 | |
|         intermediate_steps = kwargs.pop("intermediate_steps")
 | |
|         thoughts = ""
 | |
|         for action, observation in intermediate_steps:
 | |
|             thoughts += action.log
 | |
|             thoughts += f"\nObservation: {observation}\nThought: "
 | |
|         # Set the agent_scratchpad variable to that value
 | |
|         kwargs["agent_scratchpad"] = thoughts
 | |
|         # Create a tools variable from the list of tools provided
 | |
|         kwargs["tools"] = "\n".join([f"{tool.name}: {tool.description}" for tool in self.tools])
 | |
|         # Create a list of tool names for the tools provided
 | |
|         kwargs["tool_names"] = ", ".join([tool.name for tool in self.tools])
 | |
|         return self.template.format(**kwargs)
 | |
| ```
 | |
| 
 | |
| 
 | |
| ```python
 | |
| prompt = CustomPromptTemplate(
 | |
|     template=template,
 | |
|     tools=tools,
 | |
|     # This omits the `agent_scratchpad`, `tools`, and `tool_names` variables because those are generated dynamically
 | |
|     # This includes the `intermediate_steps` variable because that is needed
 | |
|     input_variables=["input", "intermediate_steps"]
 | |
| )
 | |
| ```
 | |
| 
 | |
| ## Output parser
 | |
| 
 | |
| The output parser is responsible for parsing the LLM output into `AgentAction` and `AgentFinish`. This usually depends heavily on the prompt used.
 | |
| 
 | |
| This is where you can change the parsing to do retries, handle whitespace, etc.
 | |
| 
 | |
| 
 | |
| ```python
 | |
| class CustomOutputParser(AgentOutputParser):
 | |
| 
 | |
|     def parse(self, llm_output: str) -> Union[AgentAction, AgentFinish]:
 | |
|         # Check if agent should finish
 | |
|         if "Final Answer:" in llm_output:
 | |
|             return AgentFinish(
 | |
|                 # Return values is generally always a dictionary with a single `output` key
 | |
|                 # It is not recommended to try anything else at the moment :)
 | |
|                 return_values={"output": llm_output.split("Final Answer:")[-1].strip()},
 | |
|                 log=llm_output,
 | |
|             )
 | |
|         # Parse out the action and action input
 | |
|         regex = r"Action\s*\d*\s*:(.*?)\nAction\s*\d*\s*Input\s*\d*\s*:[\s]*(.*)"
 | |
|         match = re.search(regex, llm_output, re.DOTALL)
 | |
|         if not match:
 | |
|             raise OutputParserException(f"Could not parse LLM output: `{llm_output}`")
 | |
|         action = match.group(1).strip()
 | |
|         action_input = match.group(2)
 | |
|         # Return the action and action input
 | |
|         return AgentAction(tool=action, tool_input=action_input.strip(" ").strip('"'), log=llm_output)
 | |
| ```
 | |
| 
 | |
| 
 | |
| ```python
 | |
| output_parser = CustomOutputParser()
 | |
| ```
 | |
| 
 | |
| ## Set up LLM
 | |
| 
 | |
| Choose the LLM you want to use!
 | |
| 
 | |
| 
 | |
| ```python
 | |
| llm = OpenAI(temperature=0)
 | |
| ```
 | |
| 
 | |
| ## Define the stop sequence
 | |
| 
 | |
| This is important because it tells the LLM when to stop generation.
 | |
| 
 | |
| This depends heavily on the prompt and model you are using. Generally, you want this to be whatever token you use in the prompt to denote the start of an `Observation` (otherwise, the LLM may hallucinate an observation for you).
 | |
| 
 | |
| ## Set up the Agent
 | |
| 
 | |
| We can now combine everything to set up our agent:
 | |
| 
 | |
| 
 | |
| ```python
 | |
| # LLM chain consisting of the LLM and a prompt
 | |
| llm_chain = LLMChain(llm=llm, prompt=prompt)
 | |
| ```
 | |
| 
 | |
| 
 | |
| ```python
 | |
| tool_names = [tool.name for tool in tools]
 | |
| agent = LLMSingleActionAgent(
 | |
|     llm_chain=llm_chain,
 | |
|     output_parser=output_parser,
 | |
|     stop=["\nObservation:"],
 | |
|     allowed_tools=tool_names
 | |
| )
 | |
| ```
 | |
| 
 | |
| ## Use the Agent
 | |
| 
 | |
| Now we can use it!
 | |
| 
 | |
| 
 | |
| ```python
 | |
| agent_executor = AgentExecutor.from_agent_and_tools(agent=agent, tools=tools, verbose=True)
 | |
| ```
 | |
| 
 | |
| 
 | |
| ```python
 | |
| agent_executor.run("How many people live in canada as of 2023?")
 | |
| ```
 | |
| 
 | |
| <CodeOutputBlock lang="python">
 | |
| 
 | |
| ```
 | |
| 
 | |
| 
 | |
|     > Entering new AgentExecutor chain...
 | |
|     Thought: I need to find out the population of Canada in 2023
 | |
|     Action: Search
 | |
|     Action Input: Population of Canada in 2023
 | |
| 
 | |
|     Observation:The current population of Canada is 38,658,314 as of Wednesday, April 12, 2023, based on Worldometer elaboration of the latest United Nations data. I now know the final answer
 | |
|     Final Answer: Arrr, there be 38,658,314 people livin' in Canada as of 2023!
 | |
| 
 | |
|     > Finished chain.
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
|     "Arrr, there be 38,658,314 people livin' in Canada as of 2023!"
 | |
| ```
 | |
| 
 | |
| </CodeOutputBlock>
 | |
| 
 | |
| ## Adding Memory
 | |
| 
 | |
| If you want to add memory to the agent, you'll need to:
 | |
| 
 | |
| 1. Add a place in the custom prompt for the `chat_history`
 | |
| 2. Add a memory object to the agent executor.
 | |
| 
 | |
| 
 | |
| ```python
 | |
| # Set up the base template
 | |
| template_with_history = """Answer the following questions as best you can, but speaking as a pirate might speak. 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! Remember to speak as a pirate when giving your final answer. Use lots of "Arg"s
 | |
| 
 | |
| Previous conversation history:
 | |
| {history}
 | |
| 
 | |
| New question: {input}
 | |
| {agent_scratchpad}"""
 | |
| ```
 | |
| 
 | |
| 
 | |
| ```python
 | |
| prompt_with_history = CustomPromptTemplate(
 | |
|     template=template_with_history,
 | |
|     tools=tools,
 | |
|     # This omits the `agent_scratchpad`, `tools`, and `tool_names` variables because those are generated dynamically
 | |
|     # This includes the `intermediate_steps` variable because that is needed
 | |
|     input_variables=["input", "intermediate_steps", "history"]
 | |
| )
 | |
| ```
 | |
| 
 | |
| 
 | |
| ```python
 | |
| llm_chain = LLMChain(llm=llm, prompt=prompt_with_history)
 | |
| ```
 | |
| 
 | |
| 
 | |
| ```python
 | |
| tool_names = [tool.name for tool in tools]
 | |
| agent = LLMSingleActionAgent(
 | |
|     llm_chain=llm_chain,
 | |
|     output_parser=output_parser,
 | |
|     stop=["\nObservation:"],
 | |
|     allowed_tools=tool_names
 | |
| )
 | |
| ```
 | |
| 
 | |
| 
 | |
| ```python
 | |
| from langchain.memory import ConversationBufferWindowMemory
 | |
| ```
 | |
| 
 | |
| 
 | |
| ```python
 | |
| memory=ConversationBufferWindowMemory(k=2)
 | |
| ```
 | |
| 
 | |
| 
 | |
| ```python
 | |
| agent_executor = AgentExecutor.from_agent_and_tools(agent=agent, tools=tools, verbose=True, memory=memory)
 | |
| ```
 | |
| 
 | |
| 
 | |
| ```python
 | |
| agent_executor.run("How many people live in canada as of 2023?")
 | |
| ```
 | |
| 
 | |
| <CodeOutputBlock lang="python">
 | |
| 
 | |
| ```
 | |
| 
 | |
| 
 | |
|     > Entering new AgentExecutor chain...
 | |
|     Thought: I need to find out the population of Canada in 2023
 | |
|     Action: Search
 | |
|     Action Input: Population of Canada in 2023
 | |
| 
 | |
|     Observation:The current population of Canada is 38,658,314 as of Wednesday, April 12, 2023, based on Worldometer elaboration of the latest United Nations data. I now know the final answer
 | |
|     Final Answer: Arrr, there be 38,658,314 people livin' in Canada as of 2023!
 | |
| 
 | |
|     > Finished chain.
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
|     "Arrr, there be 38,658,314 people livin' in Canada as of 2023!"
 | |
| ```
 | |
| 
 | |
| </CodeOutputBlock>
 | |
| 
 | |
| 
 | |
| ```python
 | |
| agent_executor.run("how about in mexico?")
 | |
| ```
 | |
| 
 | |
| <CodeOutputBlock lang="python">
 | |
| 
 | |
| ```
 | |
| 
 | |
| 
 | |
|     > Entering new AgentExecutor chain...
 | |
|     Thought: I need to find out how many people live in Mexico.
 | |
|     Action: Search
 | |
|     Action Input: How many people live in Mexico as of 2023?
 | |
| 
 | |
|     Observation:The current population of Mexico is 132,679,922 as of Tuesday, April 11, 2023, based on Worldometer elaboration of the latest United Nations data. Mexico 2020 ... I now know the final answer.
 | |
|     Final Answer: Arrr, there be 132,679,922 people livin' in Mexico as of 2023!
 | |
| 
 | |
|     > Finished chain.
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
|     "Arrr, there be 132,679,922 people livin' in Mexico as of 2023!"
 | |
| ```
 | |
| 
 | |
| </CodeOutputBlock>
 |