Skip to main content

自定义LLM代理

LangChain

这个笔记本介绍了如何创建自己的自定义LLM代理。

一个LLM代理由三个部分组成:

  • PromptTemplate: 这是用于指导语言模型做什么的提示模板
  • LLM: 这是为代理提供动力的语言模型
  • stop sequence: 指示LLM在找到此字符串时停止生成
  • OutputParser: 这确定如何将LLM输出解析为AgentActionAgentFinish对象

LLMAgent 在 AgentExecutor 中被使用。AgentExecutor 可以被视为一个循环,它:

  1. 将用户输入和任何先前的步骤传递给 Agent(在本例中为 LLMAgent)
  2. 如果 Agent 返回 AgentFinish,则直接将其返回给用户
  3. 如果 Agent 返回 AgentAction,则使用它调用一个工具并获取一个 Observation
  4. 重复上述步骤,将 AgentActionObservation 传递回 Agent,直到发出 AgentFinish

AgentAction 是一个由 actionaction_input 组成的响应。action 指的是要使用的工具,action_input 指的是该工具的输入。log 也可以作为更多的上下文提供(可用于日志记录、跟踪等)。

AgentFinish 是一个包含要发送回用户的最终消息的响应。这应该用于结束 Agent 运行。

在本笔记本中,我们将介绍如何创建一个自定义的 LLM Agent。

设置环境

进行必要的导入等操作。

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

设置工具

设置 Agent 可能想要使用的任何工具。这可能需要放入提示中(以便 Agent 知道何时使用这些工具)。

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"
)
]

提示模板

这将指示 Agent 该如何操作。通常,模板应包含:

  • tools:Agent 可以访问哪些工具以及如何何时调用它们。
  • intermediate_steps:这些是之前的(AgentActionObservation)对的元组。通常情况下,它们不会直接传递给模型,但是提示模板会以特定的方式对其进行格式化。
  • input:通用用户输入
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}"""
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)
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"]
)

输出解析器

输出解析器负责将 LLM 的输出解析为 AgentActionAgentFinish。这通常严重依赖于所使用的提示。

这是您可以更改解析以进行重试、处理空格等操作的位置

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)
output_parser = CustomOutputParser()

设置 LLM

选择您想要使用的 LLM!

llm = OpenAI(temperature=0)

定义停止序列

这很重要,因为它告诉 LLM 何时停止生成。

这严重依赖于所使用的提示和模型。通常情况下,您希望这是您在提示中用于表示 Observation 开始的令牌(否则,LLM 可能会为您产生虚构的观察结果)。

设置 Agent

现在我们可以将所有内容组合起来设置我们的 Agent

LLM chain consisting of the LLM and a prompt
llm_chain = LLMChain(llm=llm, prompt=prompt)
tool_names = [tool.name for tool in tools]
agent = LLMSingleActionAgent(
llm_chain=llm_chain,
output_parser=output_parser,
stop=["\nObservation:"],
allowed_tools=tool_names
)

使用 Agent

现在我们可以使用它了!

agent_executor = AgentExecutor.from_agent_and_tools(agent=agent, tools=tools, verbose=True)
agent_executor.run("How many people live in canada as of 2023?")


> 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!"

添加内存

如果您想向 Agent 添加内存,您需要:

  1. 在自定义提示中添加聊天历史的位置
  2. 向 Agent 执行器添加一个内存对象。
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}"""
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"]
)
llm_chain = LLMChain(llm=llm, prompt=prompt_with_history)
tool_names = [tool.name for tool in tools]
agent = LLMSingleActionAgent(
llm_chain=llm_chain,
output_parser=output_parser,
stop=["\nObservation:"],
allowed_tools=tool_names
)
from langchain.memory import ConversationBufferWindowMemory
memory=ConversationBufferWindowMemory(k=2)
agent_executor = AgentExecutor.from_agent_and_tools(agent=agent, tools=tools, verbose=True, memory=memory)
agent_executor.run("How many people live in canada as of 2023?")


> 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!"
agent_executor.run("how about in mexico?")


> 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!"