代理和工具
什么是代理?
训练用于执行 因果语言建模 的大型语言模型 (LLM) 可以处理各种各样的任务,但它们通常难以处理诸如逻辑、计算和搜索之类的基本任务。当在它们表现不佳的领域中被提示时,它们通常无法生成我们期望它们生成的答案。
克服这种弱点的一种方法是创建一个代理。
代理是一个使用 LLM 作为其引擎的系统,它可以访问称为工具的函数。
这些工具是用于执行任务的函数,它们包含代理正确使用它们的所有必要描述。
代理可以被编程为
- 设计一系列操作/工具,并立即运行它们,例如 CodeAgent
- 一次计划和执行一个操作/工具,并在启动下一个操作之前等待每个操作的结果,例如 ReactJsonAgent
代理类型
代码代理
该代理有一个规划步骤,然后生成 Python 代码来一次性执行所有操作。它原生处理不同类型的工具输入和输出,因此它是多模态任务的推荐选择。
React 代理
这是解决推理任务的首选代理,因为 ReAct 框架 (Yao 等人,2022) 使其在基于先前观察思考方面非常高效。
我们实现了两个版本的 ReactJsonAgent
- ReactJsonAgent 在其输出中生成工具调用作为 JSON。
- ReactCodeAgent 是一种新型的 ReactJsonAgent,它将工具调用生成代码块,这对于具有强大编码能力的 LLM 非常有效。
阅读 开源 LLM 作为 LangChain 代理 博客文章以了解更多关于 ReAct 代理的信息。
例如,以下是如何使用 ReAct 代码代理处理以下问题。
>>> agent.run(
... "How many more blocks (also denoted as layers) in BERT base encoder than the encoder from the architecture proposed in Attention is All You Need?",
... )
=====New task=====
How many more blocks (also denoted as layers) in BERT base encoder than the encoder from the architecture proposed in Attention is All You Need?
====Agent is executing the code below:
bert_blocks = search(query="number of blocks in BERT base encoder")
print("BERT blocks:", bert_blocks)
====
Print outputs:
BERT blocks: twelve encoder blocks
====Agent is executing the code below:
attention_layer = search(query="number of layers in Attention is All You Need")
print("Attention layers:", attention_layer)
====
Print outputs:
Attention layers: Encoder: The encoder is composed of a stack of N = 6 identical layers. Each layer has two sub-layers. The first is a multi-head self-attention mechanism, and the second is a simple, position- 2 Page 3 Figure 1: The Transformer - model architecture.
====Agent is executing the code below:
bert_blocks = 12
attention_layers = 6
diff = bert_blocks - attention_layers
print("Difference in blocks:", diff)
final_answer(diff)
====
Print outputs:
Difference in blocks: 6
Final answer: 6
如何构建代理?
要初始化代理,您需要以下参数
- 一个为代理提供动力的 LLM - 代理不完全是 LLM,更像是使用 LLM 作为引擎的程序。
- 一个系统提示:LLM 引擎将使用什么提示来生成输出
- 一个工具箱,代理从中选择工具来执行
- 一个解析器,用于从 LLM 输出中提取要调用的工具以及使用哪些参数
在代理系统初始化时,工具属性用于生成工具描述,然后嵌入到代理的 system_prompt
中,让它知道可以使用哪些工具以及原因。
首先,请安装 agents
扩展程序以安装所有默认依赖项。
pip install transformers[agents]
通过定义一个接受 消息 列表并返回文本的 llm_engine
方法来构建您的 LLM 引擎。此可调用对象还需要接受一个 stop
参数,该参数指示何时停止生成。
from huggingface_hub import login, InferenceClient
login("<YOUR_HUGGINGFACEHUB_API_TOKEN>")
client = InferenceClient(model="meta-llama/Meta-Llama-3-70B-Instruct")
def llm_engine(messages, stop_sequences=["Task"]) -> str:
response = client.chat_completion(messages, stop=stop_sequences, max_tokens=1000)
answer = response.choices[0].message.content
return answer
您可以使用任何 llm_engine
方法,只要
- 它遵循 消息格式 (
List[Dict[str, str]]
) 来接收其输入messages
,并且返回str
。 - 它在参数
stop_sequences
中传递的序列处停止生成输出
此外,llm_engine
还可以接收一个 grammar
参数。如果您在代理初始化时指定了 grammar
,那么此参数将传递给对 llm_engine 的调用,使用您在初始化时定义的 grammar
,以允许 受限生成 以强制使用格式正确的代理输出。
您还需要一个 tools
参数,它接受一个 Tools
列表 - 它可以是一个空列表。您还可以通过定义可选参数 add_base_tools=True
在您的 tools
列表之上添加默认工具箱。
现在您可以创建一个代理,例如 CodeAgent,并运行它。您还可以使用预初始化的管道创建一个 TransformersEngine,以使用 transformers
在您的本地机器上运行推理。为了方便起见,由于代理行为通常需要更强大的模型(例如 Llama-3.1-70B-Instruct
),而这些模型目前难以在本地运行,因此我们还提供了 HfApiEngine 类,它在幕后初始化 huggingface_hub.InferenceClient
。
from transformers import CodeAgent, HfApiEngine
llm_engine = HfApiEngine(model="meta-llama/Meta-Llama-3-70B-Instruct")
agent = CodeAgent(tools=[], llm_engine=llm_engine, add_base_tools=True)
agent.run(
"Could you translate this sentence from French, say it out loud and return the audio.",
sentence="Où est la boulangerie la plus proche?",
)
这在紧急需要法式长棍面包时非常有用!您甚至可以不定义参数 llm_engine
,默认情况下会创建一个 HfApiEngine。
from transformers import CodeAgent
agent = CodeAgent(tools=[], add_base_tools=True)
agent.run(
"Could you translate this sentence from French, say it out loud and give me the audio.",
sentence="Où est la boulangerie la plus proche?",
)
请注意,我们使用了额外的 sentence
参数:您可以将文本作为附加参数传递给模型。
您还可以使用它来指示模型要使用的本地或远程文件的路径
from transformers import ReactCodeAgent
agent = ReactCodeAgent(tools=[], llm_engine=llm_engine, add_base_tools=True)
agent.run("Why does Mike not know many people in New York?", audio="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/recording.mp3")
提示和输出解析器已自动定义,但您可以通过在代理上调用 system_prompt_template
来轻松查看它们。
print(agent.system_prompt_template)
尽可能清楚地解释您要执行的任务非常重要。每次 run() 操作都是独立的,由于代理由 LLM 提供动力,因此提示中的细微差异可能会导致完全不同的结果。您也可以连续为不同的任务运行代理:每次属性 agent.task
和 agent.logs
将被重新初始化。
代码执行
Python 解释器在与您的工具一起传递的一组输入上执行代码。这应该是安全的,因为唯一可以调用的函数是您提供的工具(尤其是 Hugging Face 的工具)和 print 函数,因此您在可执行的操作上已经受到限制。
Python 解释器默认情况下也不允许在安全列表之外进行导入,因此所有最明显的攻击都不成问题。您仍然可以通过在 ReactCodeAgent 或 CodeAgent 的初始化时将授权模块作为字符串列表传递给 additional_authorized_imports
参数来授权额外的导入。
>>> from transformers import ReactCodeAgent
>>> agent = ReactCodeAgent(tools=[], additional_authorized_imports=['requests', 'bs4'])
>>> agent.run("Could you get me the title of the page at url 'https://huggingface.co/blog'?")
(...)
'Hugging Face – Blog'
执行将停止在任何尝试执行非法操作的代码或代理生成的代码出现常规 Python 错误的地方。
LLM 可以生成任意代码,然后执行这些代码:不要添加任何不安全的导入!
系统提示
代理,或者更确切地说驱动代理的 LLM,会根据系统提示生成输出。系统提示可以自定义和定制以适应预期任务。例如,检查 ReactCodeAgent 的系统提示(以下版本略微简化)。
You will be given a task to solve as best you can. You have access to the following tools: <<tool_descriptions>> To solve the task, you must plan forward to proceed in a series of steps, in a cycle of 'Thought:', 'Code:', and 'Observation:' sequences. At each step, in the 'Thought:' sequence, you should first explain your reasoning towards solving the task, then the tools that you want to use. Then in the 'Code:' sequence, you shold write the code in simple Python. The code sequence must end with '/End code' sequence. During each intermediate step, you can use 'print()' to save whatever important information you will then need. These print outputs will then be available in the 'Observation:' field, for using this information as input for the next step. In the end you have to return a final answer using the `final_answer` tool. Here are a few examples using notional tools: --- {examples} Above example were using notional tools that might not exist for you. You only have acces to those tools: <<tool_names>> You also can perform computations in the python code you generate. Always provide a 'Thought:' and a 'Code:\n```py' sequence ending with '```<end_code>' sequence. You MUST provide at least the 'Code:' sequence to move forward. Remember to not perform too many operations in a single code block! You should split the task into intermediate code blocks. Print results at the end of each step to save the intermediate results. Then use final_answer() to return the final result. Remember to make sure that variables you use are all defined. Now Begin!
系统提示包括
- 一个解释代理应如何表现以及什么是工具的介绍。
- 所有工具的描述,由
<<tool_descriptions>>
标记定义,该标记在运行时会动态替换为用户定义/选择的工具。- 工具描述来自工具属性
name
、description
、inputs
和output_type
,以及您可以改进的简单jinja2
模板。
- 工具描述来自工具属性
- 预期的输出格式。
您可以改进系统提示,例如,通过添加对输出格式的解释。
为了最大限度地提高灵活性,您可以通过将自定义提示作为参数传递给 system_prompt
参数来覆盖整个系统提示模板。
from transformers import ReactJsonAgent
from transformers.agents import PythonInterpreterTool
agent = ReactJsonAgent(tools=[PythonInterpreterTool()], system_prompt="{your_custom_prompt}")
请确保在 template
中的某个地方定义 <<tool_descriptions>>
字符串,以便代理了解可用的工具。
检查代理运行
以下是一些有用的属性,用于检查运行后发生的情况。
agent.logs
存储代理的细粒度日志。在代理运行的每一步,所有内容都存储在一个字典中,然后附加到agent.logs
。- 运行
agent.write_inner_memory_from_logs()
会创建一个代理日志的内部记忆供 LLM 查看,以聊天消息列表的形式。此方法会遍历日志的每一步,只存储它感兴趣的内容作为消息:例如,它会将系统提示和任务保存在单独的消息中,然后针对每一步,它会将 LLM 输出存储为消息,将工具调用输出存储为另一条消息。如果您想查看事件的高级视图,可以使用此方法,但并非所有日志都会由此方法转录。
工具
工具是代理要使用的原子函数。
例如,您可以检查 PythonInterpreterTool
:它具有名称、描述、输入描述、输出类型和 __call__
方法来执行操作。
当代理被初始化时,工具属性用于生成工具描述,该描述被烘焙到代理的系统提示中。这使代理知道它可以使用哪些工具以及原因。
默认工具箱
Transformers 带有一个默认工具箱,用于增强代理,您可以在初始化代理时使用参数 add_base_tools = True
将它添加到代理中。
- 文档问答:给定一个以图像格式存在的文档(例如 PDF),回答有关该文档的问题(Donut)。
- 图像问答:给定一张图像,回答有关该图像的问题(VILT)。
- 语音转文本:给定一段人物讲话的录音,将语音转录成文本(Whisper)。
- 文本转语音:将文本转换为语音(SpeechT5)。
- 翻译:将给定的句子从源语言翻译成目标语言。
- DuckDuckGo 搜索*:使用 DuckDuckGo 浏览器执行网络搜索。
- Python 代码解释器:在安全环境中运行 LLM 生成的 Python 代码。只有在您使用
add_base_tools=True
初始化时,此工具才会添加到 ReactJsonAgent 中,因为基于代码的代理已经可以本地执行 Python 代码。
您可以通过调用 load_tool() 函数和要执行的任务来手动使用工具。
from transformers import load_tool
tool = load_tool("text-to-speech")
audio = tool("This is a text to speech tool")
创建一个新工具
您可以为 Hugging Face 默认工具未涵盖的用例创建自己的工具。例如,让我们创建一个工具,从 Hub 返回给定任务下载次数最多的模型。
您将从下面的代码开始。
from huggingface_hub import list_models
task = "text-classification"
model = next(iter(list_models(filter=task, sort="downloads", direction=-1)))
print(model.id)
此代码可以快速转换为工具,只需将其包装在函数中并添加 tool
装饰器即可。
from transformers import tool
@tool
def model_download_counter(task: str) -> str:
"""
This is a tool that returns the most downloaded model of a given task on the Hugging Face Hub.
It returns the name of the checkpoint.
Args:
task: The task for which
"""
model = next(iter(list_models(filter="text-classification", sort="downloads", direction=-1)))
return model.id
该函数需要:
- 一个清晰的名称。名称通常描述工具的功能。由于代码返回了针对任务下载次数最多的模型,因此让我们使用
model_download_counter
。 - 对输入和输出进行类型提示。
- 一个描述,其中包括一个“参数:”部分,其中描述了每个参数(这次不带类型指示,它将从类型提示中提取)。所有这些都将在初始化时自动烘焙到代理的系统提示中:因此,请尽量使它们尽可能清晰!
此定义格式与 apply_chat_template
中使用的工具模式相同,唯一的区别是添加了 tool
装饰器:有关我们的工具使用 API 的更多信息,请阅读 此处。
然后,您可以直接初始化您的代理。
from transformers import CodeAgent
agent = CodeAgent(tools=[model_download_tool], llm_engine=llm_engine)
agent.run(
"Can you give me the name of the model that has the most downloads in the 'text-to-video' task on the Hugging Face Hub?"
)
您将获得以下内容:
======== New task ======== Can you give me the name of the model that has the most downloads in the 'text-to-video' task on the Hugging Face Hub? ==== Agent is executing the code below: most_downloaded_model = model_download_counter(task="text-to-video") print(f"The most downloaded model for the 'text-to-video' task is {most_downloaded_model}.") ====
以及输出:"The most downloaded model for the 'text-to-video' task is ByteDance/AnimateDiff-Lightning."
。
管理代理的工具箱
如果您已经初始化了一个代理,那么从头开始使用您想要使用的工具重新初始化它会很麻烦。使用 Transformers,您可以通过添加或替换工具来管理代理的工具箱。
让我们将 model_download_tool
添加到一个仅使用默认工具箱初始化的现有代理中。
from transformers import CodeAgent
agent = CodeAgent(tools=[], llm_engine=llm_engine, add_base_tools=True)
agent.toolbox.add_tool(model_download_tool)
现在我们可以利用新工具和之前的文本转语音工具。
agent.run(
"Can you read out loud the name of the model that has the most downloads in the 'text-to-video' task on the Hugging Face Hub and return the audio?"
)
音频 |
---|
在向已经正常工作的代理中添加工具时要小心,因为它可能会使选择偏向您的工具或选择除已经定义的工具以外的其他工具。
使用 agent.toolbox.update_tool()
方法替换代理工具箱中的现有工具。如果您的新工具是现有工具的一对一替换,则这很有用,因为代理已经知道如何执行该特定任务。只需确保新工具遵循与被替换工具相同的 API,或者调整系统提示模板以确保所有使用被替换工具的示例都已更新。
使用工具集合
您可以通过使用 ToolCollection 对象来利用工具集合,并使用您要使用的集合的 slug。然后将它们作为列表传递以初始化您的代理,并开始使用它们!
from transformers import ToolCollection, ReactCodeAgent
image_tool_collection = ToolCollection(collection_slug="huggingface-tools/diffusion-tools-6630bb19a942c2306a2cdb6f")
agent = ReactCodeAgent(tools=[*image_tool_collection.tools], add_base_tools=True)
agent.run("Please draw me a picture of rivers and lakes.")
为了加快启动速度,只有在代理调用时才会加载工具。
这将为您提供以下图像:
< > 在 GitHub 上更新