基于开源模型搭建Agent系统教程
作者: NLP前沿 来源: NLP前沿
“
一篇非常基础非常基础的Agent博客
https://huggingface.co/blog/open-source-llms-as-agents
introducton
大型语言模型(LLMs)经过causal language modeling训练后,可以处理各种任务,但它们通常在逻辑、计算和搜索等基本任务上表现不佳。最糟糕的情况是,它们在某个领域(如数学)表现不佳,但仍然试图自行处理所有计算。
为了克服这一弱点,除了其他方法,可以将LLM集成到一个系统中,使其能够调用工具:这样的系统称为LLM Agent。
在本文中,我们将解释ReAct代理的内部工作原理,然后展示如何使用最近集成到LangChain中的ChatHuggingFace类来构建它们。最后,我们将对比几个开源LLM与GPT-3.5和GPT-4。
What are agents?
LLM Agent的定义非常广泛:LLM Agent是所有利用LLM作为引擎并可以根据环境产生的observation产生action的系统。它们可以使用感知⇒反思⇒行动循环的多个迭代来完成任务,并经常通过规划或知识管理系统进行增强以提高性能。
在这里,我们将重点放在ReAct代理上。ReAct是一种构建代理的方法,基于“reasoning”和“acting”这两个词的连接。在提示词中,会包含模型可以使用哪些工具,并要求它“一步一步”地思考(也称为思维链行为),以规划和执行下一个行动以达到最终答案。
Toy example of a ReAct agent’s inner working
上面的图看起来非常高级,但背后的原理其实非常简单。
在下面这个colab notebook中:使用 Transformers 库实现了一个简单的工具代理。
https://colab.research.google.com/drive/1j_vsc28FwZEDocDxVxWJ6Fvxd18FK8Gl?usp=sharing
LLM 在循环中被调用,其中包含一个基本的提示词。
Here is a question: "{question}"
You have access to these tools: {tools_descriptions}.
You should first reflect with ‘Thought: {your_thoughts}’, then you either:
- call a tool with the proper JSON formatting,
- or your print your final answer starting with the prefix ‘Final Answer:’
然后可以解析LLM的输出:
-
如果它包含字符串‘Final Answer:’,循环结束并打印答案,
-
否则,LLM应该输出一个工具调用:你可以解析这个输出以获取工具名称和参数,然后使用这些参数调用该工具。然后将此工具调用的输出附加到提示中,并使用这些扩展信息再次调用LLM,直到它有足够的信息最终提供问题的最终答案。
例如,当回答问题:1:23:45 有多少秒时,LLM 的输出可能是这样的。
Thought: I need to convert the time string into seconds.
Action:
{
"action": "convert_time",
"action_input": {
"time": "1:23:45"
}
}
由于此输出不包含字符串’Final Answer:’,它在调用一个工具:因此我们解析此输出并获取工具调用参数:使用参数{“time”: “1:23:45”} 调用工具convert_time。运行此工具调用返回{‘seconds’: ‘5025’}。因此我们将整个这段文字追加到提示词中。
现在的新提示词是(一个略微更详细的版本):
Here is a question: "How many seconds are in 1:23:45?"
You have access to these tools:
- convert_time: converts a time given in hours:minutes:seconds into seconds.
You should first reflect with ‘Thought: {your_thoughts}’, then you either:
- call a tool with the proper JSON formatting,
- or your print your final answer starting with the prefix ‘Final Answer:’
Thought: I need to convert the time string into seconds.
Action:
{
"action": "convert_time",
"action_input": {
"time": "1:23:45"
}
}
Observation: {'seconds': '5025'}
我们再次调用LLM,使用这个新提示词。鉴于它可以访问观察中调用结果的工具,LLM现在很可能会输出:
Thought: I now have the information needed to answer the question.
Final Answer: There are 5025 seconds in 1:23:45.
任务已完成!
Challenges of agent systems
一般来说,运行LLM引擎的代理系统中困难的部分包括:
-
从提供的工具中选择一个能够帮助实现预期目标的工具:例如,当被问到“大于30,000的最小质数是多少?”时,代理可以使用搜索工具进行调用,但这并不会有所帮助。
-
以严谨的参数格式调用工具:例如,当尝试计算一辆汽车在10分钟内行驶3公里的速度时,必须调用计算器工具来将距离除以时间:即使你的计算器工具接受JSON格式的调用:{”tool”: “Calculator”, “args”: “3km/10min”},也存在许多陷阱,例如:
-
拼写错误工具名称:“calculator”或“Compute”都跟工具名对不上
-
给出参数的名称而不是它们的值:“args”: “distance/time”
-
非标准化的格式:“args”: “3km in 10minutes”
-
-
高效地提取和利用前面观察到的信息,无论是初始背景还是使用工具后返回的观察结果。
“
说白了,一种就是模型能吐出来一个action,action_input,可能会出现模型吐出来的工具名可能跟实际的工具名对不上,或者参数名不对,或者参数值不标准。另外一种就是模型可能吐不出有效的action,对结果帮助不大
那么,一个完整的代理系统设置会是什么样子呢?
Running agents with LangChain
创建ChatModel并为其提供工具的代码非常简单,您可以在Langchain文档中查看所有类说明
from langchain_community.llms import HuggingFaceHub
from langchain_community.chat_models.huggingface import ChatHuggingFace
llm = HuggingFaceHub(
repo_id="HuggingFaceH4/zephyr-7b-beta",
task="text-generation",
)
chat_model = ChatHuggingFace(llm=llm)
你可以通过给 chat_model 提供 ReAct 风格的提示和工具来将其转化为一个代理。
from langchain import hub
from langchain.agents import AgentExecutor, load_tools
from langchain.agents.format_scratchpad import format_log_to_str
from langchain.agents.output_parsers import (
ReActJsonSingleInputOutputParser,
)
from langchain.tools.render import render_text_description
from langchain_community.utilities import SerpAPIWrapper
# setup tools
tools = load_tools(["serpapi", "llm-math"], llm=llm)
# setup ReAct style prompt
prompt = hub.pull("hwchase17/react-json")
prompt = prompt.partial(
tools=render_text_description(tools),
tool_names=", ".join([t.name for t in tools]),
)
# define the agent
chat_model_with_stop = chat_model.bind(stop=["\nObservation"])
agent = (
{
"input": lambda x: x["input"],
"agent_scratchpad": lambda x: format_log_to_str(x["intermediate_steps"]),
}
| prompt
| chat_model_with_stop
| ReActJsonSingleInputOutputParser()
)
# instantiate AgentExecutor
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
agent_executor.invoke(
{
"input": "Who is the current holder of the speed skating world record on 500 meters? What is her current age raised to the 0.43 power?"
}
)
并且代理将处理输入:
Thought: To answer this question, I need to find age of the current speedskating world record holder. I will use the search tool to find this information.
Action:
{
"action": "search",
"action_input": "speed skating world record holder 500m age"
}
Observation: ...
Agents Showdown: how do open-source LLMs perform as general purpose reasoning agents?
我们希望衡量开源LLM作为通用推理代理的表现。因此,我们选择需要使用逻辑和基本工具(计算器和互联网搜索)的问题。最终数据集是从其他3个数据集中抽取的样本的组合:
-
用于测试互联网搜索能力:我们从HotpotQA中选择了一些问题:这原本是一个检索数据集,但可以用于一般性问题回答,可以访问互联网。一些问题最初需要结合来自各种来源的信息:在我们的设置中,这意味着执行几个步骤的互联网搜索来结合结果。
-
对于计算器的使用,我们添加了来自GSM8K的问题:这个数据集测试小学数学能力,完全可以通过正确利用4种运算符(加、减、乘、除)来解决。
-
我们还从GAIA中挑选了一些问题,这是一个对通用人工智能助理来说非常困难的基准测试。原始数据集中的问题可能需要许多其他不同的工具,比如代码解释器或pdf阅读器:我们精选了不需要其他工具而只需搜索和计算器的问题。
评估是使用基于Prometheus提示格式的GPT-4作为打分模型。代码使用以下仓库
https://github.com/aymeric-roucher/benchmark_agents/
评测的几个英文模型,具体的就不贴了,结果如下:
简单结果看就是llama2-70b比较菜,在agent场景,mixtral 7b*8略优于gpt3.5
更多AI工具,参考Github-AiBard123,国内AiBard123