大模型WebUI:Gradio全解12——使用Agents和Tools智能代理工具构建UI(1)
大模型WebUI:Gradio全解12——使用Agents和Tools智能代理工具构建UI(1)
- 前言
- 本篇摘要
- 12. 使用Agents和Tools智能代理工具构建UI
- 12.1 transformers.agents原理及示例
- 12.1.1 代理概念、类型和构建
- 1. 代理概念
- 2. 代理类型
- 3. 如何构建代理?
- 12.1.2 定义大模型引擎Engines
- 1. 模型函数:llm_engine
- 2. TransformersEngine类
- 3. HfApiEngine类
- 4. HfEngine
- 12.1.3 创建和使用工具Tools
- 1. 默认工具箱
- 2. 创建新工具
- 3. 管理代理的工具箱
- 4. 使用工具集合
- 12.1.4 系统提示
- 1. 系统提示示例
- 2. 系统提示格式
- 3. 修改系统提示
- 12.1.5 其它设置
- 1. 导入代码
- 2. run的运行参数
- 3. 检查代理运行情况
- 12.1.6 使用transformers.agents的构建Gradio UI
- 1. ChatMessage数据类
- 2. 构建Gradio UI示例
- 参考文献
前言
本系列文章主要介绍WEB界面工具Gradio。Gradio是Hugging Face发布的简易WebUI开发框架,它基于FastAPI和svelte,可以使用机器学习模型、python函数或API开发多功能界面,并可部署人工智能模型,是当前热门的非常易于展示机器学习大语言模型LLM及扩散模型DM的WebUI框架。
本系列文章分为五部分:Gradio介绍、HuggingFace资源与工具库、Gradio基础功能实战、Gradio与大模型融合实战和Gradio高级功能实战。第一部分Gradio介绍,包括三章内容:第一章先介绍Gradio的概念,包括详细技术架构、历史、应用场景、与其他框架Gradio/NiceGui/StreamLit/Dash/PyWebIO的区别,然后详细介绍了Gradio的安装与运行,安装包括Linux/Win/Mac三类系统安装,运行包括普通方式和热重载方式;第二章介绍Gradio的4种部署方式,包括本地部署launch()、huggingface托管、FastAPI挂载和Gradio-Lite浏览器集成;第三章介绍Gradio的三种Client,包括python客户端、javascript客户端和curl客户端,方便读者对Gradio整体把握。第二部分介绍著名网站Hugging Face的各类资源和工具库,因为Gradio演示中经常用到Hugging Face的models及某些场景需要部署在spaces,包括两章内容:第四章详解三类资源models/datasets/spaces的使用,第五章实战六类工具库transformers/diffusers/datasets/PEFT/accelerate/optimum实战。第三部分是Gradio基础功能实战,进入本系列文章的核心,包括四章内容:第六章讲解Gradio库的模块架构和环境变量,第七章讲解Gradio高级抽象界面类Interface,第八章讲解Gradio底层区块类Blocks,第九章讲解补充特性Additional Features。第四部分是Gradio与大模型融合的实战,包括三章内容:第十章讲解融合大模型的多模态聊天机器人组件Chatbot,第十一章讲解使用Agents和Tools智能代理工具构建UI,第十二章讲述将Gradio用于LLM Agents的Gradio Tools。第五部分详解Gradio高级功能,包括三章内容:第十三章讲述Discord Bot/Slack Bot/Website Widget部署,第十四章讲述数据科学与绘图Data Science And Plots,第十五章讲述数据流Streaming。
本系列文章讲解细致,涵盖Gradio大部分组件和功能,代码均可运行并附有大量运行截图,方便读者理解并应用到开发中,Gradio一定会成为每个技术人员实现各种奇思妙想的最称手工具。
本系列文章目录如下:
- 《Gradio全解1——Gradio简介》
- 《Gradio全解1——Gradio的安装与运行》
- 《Gradio全解2——Gradio的3+1种部署方式实践》
- 《Gradio全解2——浏览器集成Gradio-Lite》
- 《Gradio全解3——Gradio Client:python客户端》
- 《Gradio全解3——Gradio Client:javascript客户端》
- 《Gradio全解3——Gradio Client:curl客户端》
- 《Gradio全解4——剖析Hugging Face:详解三类资源models/datasets/spaces》
- 《Gradio全解5——剖析Hugging Face:实战六类工具库transformers/diffusers/datasets/PEFT/accelerate/optimum》
- 《Gradio全解6——Gradio库的模块架构和环境变量》
- 《Gradio全解7——Interface:高级抽象界面类(上)》
- 《Gradio全解7——Interface:高级抽象界面类(下)》
- 《Gradio全解8——Blocks:底层区块类(上)》
- 《Gradio全解8——Blocks:底层区块类(下)》
- 《Gradio全解9——Additional Features:补充特性(上)》
- 《Gradio全解9——Additional Features:补充特性(下)》
- 《Gradio全解10——Chatbot:融合大模型的多模态聊天机器人(1)》
- 《Gradio全解10——Chatbot:融合大模型的多模态聊天机器人(2)》
- 《Gradio全解10——Chatbot:融合大模型的多模态聊天机器人(3)》
- 《Gradio全解10——Chatbot:融合大模型的多模态聊天机器人(4)》
- 《Gradio全解10——Chatbot:融合大模型的多模态聊天机器人(5)》
- 《Gradio全解11——使用Agents和Tools智能代理工具构建UI(1)》
- 《Gradio全解11——使用Agents和Tools智能代理工具构建UI(2)》
- 《Gradio全解11——使用Agents和Tools智能代理工具构建UI(3)》
- 《Gradio全解11——使用Agents和Tools智能代理工具构建UI(4)》
- 《Gradio全解12——Gradio Tools:将Gradio用于LLM Agents》
- 《Gradio全解系列13——Discord Bot/Slack Bot/Website Widget部署》
- 《Gradio全解系列14——Data Science And Plots:数据科学与绘图》
- 《Gradio全解15——Streaming:数据流(上)》
- 《Gradio全解15——Streaming:数据流(下)》
本篇摘要
本章介绍如何使用Agents和Tools智能代理工具构建Gradio UI,主要内容包括transformers.agents原理及示例用法、langchain agents的原理及示例用法、langgraph的原理及示例用法和使用显示思考的Gemini 2.0 Flash Thinking API构建UI。
12. 使用Agents和Tools智能代理工具构建UI
Gradio的组件Chatbot原生支持显示中间思考过程和工具使用情况(参考其参数metadata用法),这使得它非常适合为LLM Agent、思维链(Chain-of-Thought, CoT)或推理演示创建用户界面,本章将展示如何使用gr.Chatbot和gr.ChatInterface来显示思考过程和工具使用情况。
本章讲解四种代理和工具方式构建用户界面,其中前三种为代理方式,包括transformers.agents、langchain agents和langgraph,第四种使用显示思考的思维链工具Gemini 2.0 Flash Thinking API构建UI。因为Agents作为连接各大模型和工具的桥梁,是打通人工通用智能(AGI)的最后一公里,对从业者和人工智能行业都是至关重要的一环,所以本章将重点放在Agents智能体的原理及应用步骤拆解,然后才是通过Gradio构建UI。作者为此阅读了大量文献,以尽量阐述清楚智能代理,请读者不吝三连和评论,共同进步变强。
12.1 transformers.agents原理及示例
本节讲述transformers.agents原理及示例,内容包括代理概念类型和构建、定义大模型引擎Engines、创建和使用Tools、系统提示、其它设置和使用transformers.agents构建Gradio UI。
12.1.1 代理概念、类型和构建
本小节讲述代理的概念、类型和构建方法,对理解代理至关重要。
1. 代理概念
经过大量因果语言建模(causal language modeling)训练的大型语言模型(LLMs)能够处理多种任务,但在逻辑、计算和搜索等基本任务上往往表现不佳。当在这些不擅长的领域中被prompt时,大模型通常无法生成我们期望的答案,克服这一弱点的一种方法是创建代理LLM Agent。
LLM Agent的定义非常宽泛:通常指的是所有将LLM作为核心引擎,并能够根据观察对其环境施加影响的系统。这些系统能够通过多次迭代“感知 ⇒ 思考 ⇒ 行动”的循环来实现既定任务,并常常融入规划或知识管理系统以提升其表现效能。并且LLM Agent可以访问工具(Tools),这些工具通常是用于执行任务的函数,它们必须包含代理正确使用它们所需的所有描述。我们可以在论文《The Rise and Potential of Large Language Model Based Agents: A Survey》的研究中找到对智能体领域的精彩评述。
2. 代理类型
代理可以设计为一系列动作/工具(actions/tools)并一次性运行它们,也可以逐个计划后执行动作/工具,并在启动下一个动作之前等待前面每个动作的结果。因此根据代理设计理念的不同,一般分为两种类型:
- Code agent:该代理有一个规划步骤,然后生成Python代码以一次性执行所有动作。它原生支持处理工具内不同的输入和输出类型,因此是多模态任务的推荐选择;
- React agent:它采用一种基于“推理 (Reasoning)”与“行动 (Acting)”结合的方式逐步解决给定任务,并以此来构建智能体。Transformers实现了三种版本的React agent:
(1)ReactAgent:原始的推理执行代理,它的行动将从大语言模型(LLM)的输出中解析出来,它并不常用,经常会被下面两种衍生代理代替;
(2)ReactJsonAgent:工具调用将由LLM以JSON代码块生成,然后解析并执行;
(3)ReactCodeAgent:是一种新型的ReactJsonAgent,工具调用将由LLM以Python代码块生成,这对于具有强大编码性能的LLM非常有效。
我们将重点放在ReAct智能体上,因为它是解决推理任务的首选代理。在提示词中,我们阐述了模型能够利用哪些工具,并引导它逐步思考“step by step” (亦称为思维链行为Chain-of-Thought),以规划并实施其后续动作。ReAct框架(ReAct: Synergizing Reasoning and Acting in Language Models)使代理可以在基于先前观察的基础上进行多次非常高效的思考。读者可以阅读Open-source LLMs as LangChain Agents博客文章(https://huggingface.co/blog/open-source-llms-as-agents)以了解更多关于使用ReAct代理的信息。
单步Code agent与多步React agent执行流程区别可参考下图:
3. 如何构建代理?
要初始化一个代理,需要以下参数:
- llm_engine:此参数设置为代理提供动力引擎的LLM,代理并不完全是LLM,它更像是一个使用LLM作为引擎的程序;
- 工具箱:代理从中挑选工具来执行任务;
- 系统提示(system prompt):LLM引擎将根据这个提示生成其输出;
- 解析器:用于从LLM的输出中提取需要调用的工具及其参数。
在代理系统初始化时,工具的属性被用来生成工具描述,将其嵌入到代理的system_prompt中,以便代理知道它可以使用哪些工具以及为什么使用这些工具。然后LLM根据系统提示输出解决方案,解析器解析LLM的输出后,决定调用哪些工具继续执行。最后当已经得到答案或者满足停止条件将结束会话,否则继续迭代执行。下面对这四方面内容逐一讲述。当然开始之前,请使用以下命令额外安装transformers的agents以安装所有默认依赖项:pip install transformers[agents]。
12.1.2 定义大模型引擎Engines
我们可以自由创建和使用用于代理框架的引擎Engines,但需要满足以下条件:
- 它遵循输入消息的消息格式(List[Dict[str, str]]),并返回一个字符串;
- 它在传入的参数stop_sequences指定的序列处停止生成输出。
下面就来了解下四种不同的创建引擎方法:模型函数llm_engine、TransformersEngine、HfApiEngine和HfEngine,请注意区分它们适用的场景。
1. 模型函数:llm_engine
我们可以通过定义一个接受消息列表并返回文本的函数llm_engine来构建LLM引擎,这个可调用对象还需要接受一个stop参数以指示何时停止生成。
from huggingface_hub import login, InferenceClientlogin("<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.contentreturn answer
此外,llm_engine还可以接受一个grammar参数。如果在代理初始化时指定了grammar,这个参数将连同初始化时定义的grammar一起传递给llm_engine的调用,以实现受约束的生成,从而强制代理生成格式正确的输出。
2. TransformersEngine类
为了方便起见,我们可以添加一个TransformersEngine,它通过预初始化的Pipeline作为输入实现上述功能,或可选的使用model_id,以便使用transformers在本地机器上运行推理。示例代码如下:
from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline, TransformersEnginemodel_name = "HuggingFaceTB/SmolLM-135M-Instruct"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name)pipe = pipeline("text-generation", model=model, tokenizer=tokenizer)
engine = TransformersEngine(pipe)
engine([{"role": "user", "content": "Ok!"}], stop_sequences=["great"])
# 输出
"What a "
3. HfApiEngine类
由于代理行为通常需要更强的模型,如Llama-3.1-70B-Instruct,这些模型目前较难在本地运行,因此我们还提供了HfApiEngine类,HfApiEngine是一个封装了Hugging Face Inference API客户端的引擎,即在底层初始化了一个huggingface_hub.InferenceClient,用于执行大语言模型(LLM)的调用。
该引擎通过Hugging Face的推理API与语言模型进行通信,它可以在无服务器模式(serverless)下使用,也可以与专用端点(dedicated endpoint)一起使用,并支持诸如停止序列(stop sequences)和语法自定义(grammar customization)等功能。程序中甚至可以留空llm_engine参数,默认情况下会创建一个HfApiEngine。示例代码如下:
from transformers import HfApiEnginellm_engine = HfApiEngine(model="meta-llama/Meta-Llama-3-70B-Instruct")
4. HfEngine
对于本地部署的具有Inference API的LLM,我们还可以直接使用包中提供的 HfEngine 类来获取一个调用我们自己的Inference API的LLM引擎,示例代码如下:
from transformers.agents import HfEnginellm_engine = HfEngine("meta-llama/Meta-Llama-3-70B-Instruct")
12.1.3 创建和使用工具Tools
工具是代理使用的原子函数,包括各种属性和执行方法。例如PythonInterpreterTool:它有一个名称、描述、输入描述、输出类型,以及一个执行操作的 call 方法。当代理初始化时,工具属性会被用来生成工具描述,并将其嵌入到代理的系统提示中,这让代理知道它可以使用哪些工具以及为什么使用这些工具。工具是代理的核心部分,工具的好坏和数量决定了代理能力的大小。本节将从默认工具箱、创建新工具、管理工具箱及使用工具集等方面讲解如何使用工具。
1. 默认工具箱
Transformers附带了一个默认工具箱,用于增强代理的功能。构建Agent时需要一个tools参数,它接受一个工具列表(List[Tools])——这个列表可以是空的,但可以在代理初始化时,通过定义可选参数add_base_tools=True,将默认工具箱添加到工具列表。默认工具箱中工具如下:
- 文档问答:给定一个图像格式的文档(如PDF),回答关于该文档的问题(Donut);
- 图像问答:给定一张图像,回答关于该图像的问题(VILT);
- 语音转文本:给定一段人声录音,将语音转录为文本(Whisper);
- 文本转语音:将文本转换为语音(SpeechT5);
- 翻译:将给定的句子从源语言翻译为目标语言;
- DuckDuckGo搜索*:使用DuckDuckGo浏览器执行网络搜索;
- Python代码解释器:在安全环境中运行由LLM生成的Python代码。此工具仅在初始化 ReactJsonAgent时通过add_base_tools=True添加,因为基于代码的代理已经可以原生执行Python代码。
我们还可以通过调用load_tool()函数并指定要执行的任务来手动使用工具,示例代码如下:
from transformers import load_tooltool = load_tool("text-to-speech")
audio = tool("This is a text to speech tool")
2. 创建新工具
我们可以为Hugging Face默认工具未涵盖的用例创建自己的工具。以返回HF Hub上某个任务下载量最多的模型为例创建一个工具,步骤如下:
- 创建核心功能代码:
from huggingface_hub import list_modelstask = "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_tool(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_tool;
- 输入和输出的类型提示:输入的类型提示为函数的入参,输出类型提示为符号->后的类型,方便大模型调用;
- 函数描述:描述函数的作用和返回说明,其中包括一个“Args:”部分,描述每个参数的作用(注意这里不需要类型指示,因为会从类型提示中提取)。所有这些将在初始化时自动嵌入到代理的系统提示中,因此请尽量使它们清晰易懂!
- 最后将工具直接添加到初始化代理的参数tools中。现在,我们可以创建一个代理,比如CodeAgent,并使用创建的工具model_download_tool,示例代码如下:
from transformers import CodeAgent, HfApiEnginellm_engine = HfApiEngine(model="meta-llama/Meta-Llama-3-70B-Instruct")
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_tool(task="text-to-video")
print(f"The most downloaded model for the 'text-to-video' task is {most_downloaded_model}.")
==== The output:
"The most downloaded model for the 'text-to-video' task is ByteDance/AnimateDiff-Lightning."
3. 管理代理的工具箱
当我们已经初始化了一个带有工具箱的代理时,从头开始重新初始化以添加想使用的工具会很不方便。此时可以使用Transformers代理,通过toolbox中函数add_tool()和update_tool()来添加或替换工具来管理代理的工具箱。
让我们将上面定义的model_download_tool添加到一个仅使用默认工具箱初始化的现有代理中,代码如下:
from transformers import CodeAgentagent = 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,或者调整系统提示模板以确保更新所有使用被替换工具的示例。
4. 使用工具集合
我们还可以通过使用transformers中的对象ToolCollection来利用工具集合,并通过参数collection_slug指定想要使用的集合片,它们将作为列表传递给代理进行初始化,示例代码如下:
from transformers import ToolCollection, ReactCodeAgentimage_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.")
为了加快启动速度,工具只有在被代理调用时才会加载。另外,实际运行时可能会报错:ImportError: cannot import name ‘cached_download’ from ‘huggingface_hub’. Did you mean: ‘hf_hub_download’?。原因可能是工具内部调用有问题,只能等官方更新。运行正常时,上面代码将会产生以下类似图片:
12.1.4 系统提示
在代理系统初始化时,工具的属性被用来生成工具描述,将其嵌入到代理的system_prompt中,以便代理知道它可以使用哪些工具以及为什么使用这些工具,然后LLM根据系统提示输出解决方案。本小节将展示系统提示示例、解析系统提示格式以及如何修改系统提示。
1. 系统提示示例
系统提示(system prompt)和输出解析器(output parser)是自动定义的,但我们可以通过调用代理上的system_prompt_template和tool_parser轻松查看它们。
代理,或者更准确地说,驱动代理的LLM,会根据系统提示生成输出。系统提示可以根据预期的任务进行定制和调整。在系统提示中,尽可能清楚地解释我们想要执行的任务非常重要。由于代理是由LLM驱动的,且每次run()操作都是独立的,提示中的微小变化可能会产生完全不同的结果,因此也可以连续运行代理以执行不同的任务:每次运行时,agent.task和agent.logs属性都会重新初始化。
例如,查看ReactCodeAgent的系统提示(以下版本略有简化):
>>> print(agent.system_prompt_template)
>>> 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 should 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!
2. 系统提示格式
观察上面系统提示示例,可以发现系统提示一般包括:
- 一段介绍,解释代理应如何行为以及工具是什么;
- 所有工具的描述,这些描述由<<tool_descriptions>>标记定义,该标记在运行时动态替换为用户定义/选择的工具,工具描述来源于工具属性,包括名称、描述、输入和输出类型,以及一个用户可以优化的简单jinja2模板;
- 示例examples,示例中尽量包含所提到的工具及使用方法;
- 预期的输出格式,对final_answer的具体要求。
系统提示格式只是起到引导作用,并没有具体的格式规定,可根据自己需求添加或删除,但一般情况是:越具体越清晰的系统提示会产生更好的输出效果!
3. 修改系统提示
我们可以改进系统提示,例如通过添加对输出格式的解释以强制产生需要的输出。为了获得最大的灵活性,甚至可以通过将自定义提示作为参数传递给system_prompt参数来覆盖整个系统提示模板。具体操作时,可通过agent.system_prompt_template获取初始化的系统提示,根据自己需要修改后进行替换,替换代码如下:
from transformers import ReactJsonAgent
from transformers.agents import PythonInterpreterToolagent = ReactJsonAgent(tools=[PythonInterpreterTool()], system_prompt="{your_custom_prompt}")
请确保在模板中的某个位置定义<<tool_descriptions>>字符串,以便代理知道可用的工具。
12.1.5 其它设置
除了引擎、工具和系统提示外,还有一些辅助设置,可以帮助我们更好的使用代理,比如对于CodeAgent、ReactCodeAgent,在执行生成代码时,可能需要导入某些运行库;或者agent运行时需添加句子、文件等可替换参数;当代理运行完毕后查看代理的运行情况等,下面逐一讲述。
1. 导入代码
Python解释器会在一组与工具一起传递的输入上执行代码,这应该是安全的,因为解释器只能调用传入的工具函数(特别是Hugging Face工具)和print函数,因此这里已经限制了可以执行的内容。
默认情况下,Python解释器也不允许在安全列表之外进行导入,因此所有最明显的攻击都不应该成为问题。但我们仍然可以通过在初始化ReactCodeAgent或CodeAgent时,将授权的模块作为字符串列表传递给additional_authorized_imports参数来授权额外的导入,示例如下:
from transformers import ReactCodeAgentagent = 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解释器会在尝试执行任何非法操作的代码处停止,或者当代理生成的代码存在常规的Python错误时也会停止。LLM可以生成任意代码后执行,但请注意,不要添加任何不安全的导入!
2. run的运行参数
当代理调用方法run()时,可以添加额外的参数,比如参数sentence可以将文本作为额外参数传递给模型,示例如下:
from transformers import CodeAgent, HfApiEnginellm_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?",
)
另外,也可以使用这个参数来指示模型使用的本地或远程文件的路径,如下::
from transformers import ReactCodeAgentagent = 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")
3. 检查代理运行情况
当代理成功运行后,可以通过一些有用的属性检查运行后发生的情况:agent.logs 存储了代理的详细日志。在代理运行的每一步,所有内容都会被存储在一个字典中,然后追加到agent.logs中。
运行agent.write_inner_memory_from_logs() 会为代理的日志创建一个内部记忆,化作聊天消息列表供LLM查看。此方法会遍历日志的每一步,并仅将其感兴趣的内容存储为消息:例如,它会将系统提示和任务保存在不同的消息中,然后对于每一步,它会将LLM输出存储为一条消息,工具调用输出存储为另一条消息。如果我们想在更高层次的视角来了解发生了什么,可以使用这个方法——但并非所有日志都会被此方法转录。
为方便理解ReAct流程,可以参考下面ReactCodeAgent解决问题时的执行步骤:
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: 6Final answer: 6
12.1.6 使用transformers.agents的构建Gradio UI
在用transformers.agents的构建Gradio UI之前,先回顾下Gradio的数据类ChatMessage,然后使用ChatMessage构建UI。
1. ChatMessage数据类
在Gradio的聊天机器人chatbot中,每条消息都是一个类型为ChatMessage的数据类(假设聊天机器人的type=“message”,推荐使用此类型,因为tuple类型即将被废弃)。ChatMessage及其子类MetadataDict、OptionDict的结构如下:
@dataclass
class ChatMessage:content: str | Componentrole: Literal["user", "assistant"]metadata: MetadataDict = Noneoptions: list[OptionDict] = Noneclass MetadataDict(TypedDict):title: NotRequired[str]id: NotRequired[int | str]parent_id: NotRequired[int | str]log: NotRequired[str]duration: NotRequired[float]status: NotRequired[Literal["pending", "done"]]class OptionDict(TypedDict):label: NotRequired[str]value: str
对于我们的需求来说,最重要的键是metadata键,它接受一个字典MetadataDict。如果这个字典中包含消息的标题(title),它将会显示在一个可折叠的框中,代表一个“思考”。就是这么简单,看看这个例子:
import gradio as grwith gr.Blocks() as demo:chatbot = gr.Chatbot(type="messages",value=[gr.ChatMessage(role="user", content="What is the weather in San Francisco?"),gr.ChatMessage(role="assistant", content="I need to use the weather API tool?",metadata={"title": "🧠 Thinking"}])demo.launch()
运行截图参照上一章内容。除了title之外,提供给metadata的字典还可以包含以下几个可选键:
- log:一个可选的字符串值,会以较淡的字体显示在思考标题旁边;
- duration:一个可选的数值,表示思考/工具使用的持续时间(以秒为单位),会以较淡的字体显示在思考标题旁边的括号内;
- status:如果设置为pending,会在思考标题旁边显示一个加载动画;如果设置为done,思考的可折叠框会关闭;如果未提供,思考的可折叠框会默认打开且不显示加载动画;
- id和parent_id:如果提供了这些键,可以用来将思考嵌套在其他思考中。
下面,我们展示了一个完整的示例,演示如何使用gr.Chatbot和gr.ChatInterface来展示工具使用或思考的用户界面。
2. 构建Gradio UI示例
我们将创建一个简单的Gradio应用程序代理,该代理可以使用文本生成图像的工具。提示:请确保先理解了前面内容或阅读了transformers.agents文档,地址:https://huggingface.co/docs/transformers/en/agents。
我们将从导入transformers和gradio中的必要类开始,代码如下:
import gradio as gr
from dataclasses import asdict
from transformers import Tool, ReactCodeAgent # type: ignore
from transformers.agents import stream_to_gradio, HfApiEngine # type: ignore# Import tool from Hub
image_generation_tool = Tool.from_space( # type: ignorespace_id="black-forest-labs/FLUX.1-schnell",name="image_generator",description="Generates an image following your prompt. Returns a PIL Image.",api_name="/infer",
)llm_engine = HfApiEngine("Qwen/Qwen2.5-Coder-32B-Instruct")
# Initialize the agent with both tools and engine
agent = ReactCodeAgent(tools=[image_generation_tool], llm_engine=llm_engine)# Building UI
def interact_with_agent(prompt, history):messages = []yield messagesfor msg in stream_to_gradio(agent, prompt):messages.append(asdict(msg)) # type: ignoreyield messagesyield messagesdemo = gr.ChatInterface(interact_with_agent,chatbot= gr.Chatbot(label="Agent",type="messages",avatar_images=(None,"https://em-content.zobj.net/source/twitter/53/robot-face_1f916.png",),),examples=[["Generate an image of an astronaut riding an alligator"],["I am writing a children's book for my daughter. Can you help me with some illustrations?"],],type="messages",
)if __name__ == "__main__":demo.launch()
运行截图如下:
作者在本地运行时会报JSON解析错误:JSONDecodeError: Expecting value: line 1 column 1 (char 0),解决办法无从查找,可能和使用VPN代理环境有关。读者可以将代码复制到Google Colab上运行,也可以直接通过Hugging Face的演示查看效果:https://huggingface.co/spaces/gradio/agent_chatbot。
从输出可以看到思考、工具调用和输出结果的调用过程,正是ReactCodeAgent代理的推理执行过程。
参考文献
- Gradio - guides - Chatbots
- License to Call: Introducing Transformers Agents 2.0
- Hugging Face - Transformers - Agents and tools