英语轻松读发新版了,欢迎下载、更新

代码代理:代理AI的未来|迈向数据科学

2025-05-27 00:11:54 英文原文

作者:Mariya Mansurova

AI代理。LLM不再只是工具。他们成为我们生活中的积极参与者,提高生产力并改变我们的生活和工作方式。 

  • Openai最近推出操作员,可以自主执行各种任务的AI代理,从浏览网络到填写表格和安排约会。
  • 引入了拟人化MCP(模型上下文协议)这是AI助手如何与外界互动的新标准。已经有了超过五千个活跃的MCP服务器,采用率正在迅速增长。
  • AI代理也正在改变软件工程的景观。诸如Github Copilot的代理模式,Claude Code,OpenAI Codex等工具不仅是提高开发人员的生产率和代码质量但也使该领域民主化,使没有技术背景的人可以使用软件开发。

我们以前研究了不同的AI代理框架,例如Langgraph或者CREWAI。在本文中,我想讨论我最近一直在探索的新版本。拥抱面。这是一个有趣的框架,因为它实现了代码代理的概念。

在本文中,我们将探讨几个主题:

  • 什么是代码代理(预告片:这与氛围编码无关)。
  • 如何在实践中使用拥抱面Smolagents框架。 
  • 是否可以为LLM提供这么多代理机构。
  • 代码代理与传统工具称呼代理之间性能的真正差异。

AI代理的回顾

让我们从快速刷新开始:AI代理到底是什么?拥抱面提供对代理商的含义清晰明确的定义。 

AI代理是LLM输出控制工作流程的程序。

因此,当我们希望系统根据观察结果推理和采取行动时,我们需要一个代理流。实际上,代理不是二进制变量(是或否),而是频谱。 

  • 一方面,我们可以完全没有代理机构的系统,例如一个简单的过程,其中LLM定义了文本的情感,翻译或总结它。 
  • 下一个级别是路由,LLM可以在其中分类一个传入的问题,并决定采取哪种途径,例如,如果客户询问其当前订单的状态,并将对话转移给人类CS代理商,则调用工具。 
  • 更先进的系统可以表现出更高的代理水平。这些可能包括执行其他LLM(多代理设置)的能力,甚至可以随时创建新工具。

代码代理属于这个更高级的类别。它们是多步代理,以代码的形式执行工具调用,与使用工具名称和参数的JSON格式进行了更传统的方法相反。

最近的几篇论文表明,在代理流中使用代码会带来更好的结果: 

当您考虑它时,这是有道理的。几十年来,我们一直在开发编程语言来解决复杂的问题。因此,与简单的JSON配置相比,这些语言更适合LLM的任务。另一个好处是,由于大量可用的培训数据,LLMS已经相当擅长使用通用编程语言编写代码。

这种方法还带来其他一些好处:

  • 通过生成代码,LLM不限于预定义的一组工具,并且可以创建自己的功能。
  • 它可以使用条件和循环将多个工具组合在单个操作中,这有助于减少完成任务所需的步骤数。一个 
  • 它还使模型可以与更广泛的输出(例如生成图表,图像或其他复杂对象)一起使用。

这些好处仅仅是理论上的;我们可以在实践中观察它们。在可执行的代码操作引起了更好的LLM代理作者表明,代码代理的表现要优于传统方法,达到更高的成功率并以更少的步骤完成任务,从而降低了成本。” 

图表Wang,Xingyao等人可执行的代码操作引起了更好的LLM代理

代码代理看起来很有希望,这激发了我在实践中尝试这种方法。

拥抱面Smolagents框架

首先尝试

幸运的是,我们不需要从头开始构建代码代理,因为Huggingface发布了一个方便的库,称为Smolagents这实现了这种方法。 

让我们从安装图书馆开始。 

PIP安装Smolagents [Litellm]#我已经使用了Litellm,因为我打算将其与OpenAi模型一起使用

接下来,让我们建立一个基本示例。要初始化代理,我们只需要两个参数:模型工具。一个 

我计划将OpenAI用于模型,可以通过Litellm。但是,该框架也支持其他选项。您可以通过霍拉马或者变形金刚模型,或通过推理提供者或选择其他选项(您可以在文档)。一个 

我没有指定任何工具,但使用了add_base_tools = true,所以我的经纪人有一个默认工具集,例如Python的口译员或DuckDuckgo搜索。让我们尝试一个简单的问题。

来自SmolAgents进口,litellmmodelmodel = litellmmodel(model_id =“ openai/gpt-4o-mini”,api_key = config ['Openai_api_key'])agent = codeagent(tools = [],model = model,add_base_tools = true)agent.run(”“”我有5个不同的球,然后随机选择2。我能得到多少球的组合?”“”,)

结果,我们看到了一个格式非常好的执行流。这真是太神奇了,可以让您完美理解该过程。

作者的图像

因此,代理在一个步骤中找到答案,并编写了Python代码来计算组合数。 

输出非常有用,但是我们可以更深入地了解与执行相关的完整信息(包括提示),Agent.Memory.Steps。让我们看一下代理使用的系统提示。 

您是一名专家助理,可以使用代码斑点解决任何任务。您将有一项任务尽可能地解决。为此,您已获得访问工具列表的访问:这些工具基本上是Python函数,您可以使用代码调用。要解决任务,您必须计划继续进行一系列步骤,“思想:”,“代码:”,和“观察:”序列。在“思想:”序列中的每个步骤中,您应该首先解释您朝着解决任务和所需工具的推理使用。然后在“代码:”序列中,您应该简单地编写代码Python。代码序列必须以'<end_code>'序列结尾。在每个中间步骤中,您可以使用“ print()”保存您将需要的任何重要信息。然后,这些打印输出将出现在“观察:”字段中,下一步将作为输入可用。最后,您必须使用最终答案Final_answer工具。这里有一些使用名义工具的示例:<...>

很明显,Smolagents实现了反应方法(Yao等人在论文中引入。反应:在语言模型中协同推理和行动)并使用一些弹药提示技术。

Smolagents库处理代理工作流程中涉及的所有幕后工作:将系统提示组装到LLM(即可用工具),解析输出并执行生成的代码的所有必要信息。它还提供了全面的记录和重试机制,以帮助纠正错误。

此外,库提供了内存管理功能。默认情况下,所有执行结果都保存到内存,但是您可以自定义此行为。例如,您可以从内存中删除一些中间结果以减少令牌数量或逐步执行代理。当我们在这里深入了解内存管理时,您可以在中找到方便的代码示例文档

安全

现在,是时候讨论代码代理方法的缺点了。通过允许执行任意代码来给予LLM更多代理商会引入更高的风险。实际上,LLM可以错误地运行有害代码(因为LLM远非完美),或者是由于迅速注射或妥协模型等有针对性的攻击。 

为了减轻这些风险,在Smolagents库中实施的本地Python执行人有大量的安全检查: 

  • 默认情况下,除非已明确添加该软件包,否则不允许导入附加_authorized_imports列表 
  • 此外,默认情况下的子模型将被阻止,因此您必须具体授权它们(即numpy。*)。之所以这样做,是因为某些软件包可以暴露潜在有害的子模型,即Random._OS。 
  • 执行操作的总数被封顶,防止了无限的循环和资源膨胀。 
  • 解释器中未明确定义的任何操作都会引起错误。 

让我们测试这些安全措施是否实际工作。

来自smolagents.local_python_executor导入localpythonexecutorcustom_executor = localpythonexecutor([[“ numpy。*”,“ andand”])#功能具有漂亮格式的异常def run_capture_exception(命令:str):尝试:custom_executor(harmful_command)除例外为E:打印(“错误:\ n”,e)#未经授权的导入被阻止harmful_command =“导入OS; exit_code = os.system('<bad_command>')”run_capture_exception(harmful_command)#错误:由于:#InverseTerError:不允许进口OS。授权进口#是:['datetime','itertools','re','数学','statistics','time',queue',#“ numpy。#除非具体说明,否则也会阻止子模块harmful_command =“来自随机导入_OS; exit_code = _OS.System('<bad_command>')”run_capture_exception(harmful_command)#错误:执行代码在line'exit_code = _OS.System('<bad_command>')'#由于:InverteTerror:禁止访问模块:OS#迭代次数的帽子断裂无限循环harmful_command ='''而真:经过'''run_capture_exception(harmful_command)#错误:代码执行在“ true:pass”的行中失败:#InverteTerError:循环中的最大1000000迭代次数#超过#未定义的操作不起作用harmful_command =“!echo <bad_command>”custom_executor(harmful_command)#错误:第1行上的代码解析失败:SyntaxError

看来我们有一些与代码代理的安全网。但是,尽管有这些保障,但在您在本地执行代码时,风险仍然存在。例如,LLM可以在计算机上递归创建线程或创建太多文件,从而导致资源膨胀。一种可能的解决方案是在沙盒环境中执行代码,例如使用Docker或解决方案E2B。我愿意冒险并在本地运行我的代码,但是如果您更喜欢更避免风险的方法,则可以遵循Sandbox设置指导文档

代码代理与传统工具销售代理

它声称与传统的基于JSON的方法相比,代码代理的性能更好。让我对此进行测试。 
我将使用我上一篇文章中描述的指标变更分析的任务有意义的KPI变化。我们将从一个简单的情况开始:分析一个简单的度量标准(收入)按一个维度(国家)分配。

raw_df = pd.read_csv('absolute_metrics_example.csv',sep ='\ t')df = raw_df.groupby('country')[['revenue_before','revenue_after_scenario_2']]。sum()\。列= {'Revenue_after_scenario_2':'efter',''Revenue_before':'之前'})
作者的图像

Smolagents库支持两个类,我们可以用它们比较两种方法: 

  • 代码通过生成和执行代码来起作用的代理,
  • 工具卡传统的基于JSON的代理商。 

我们的代理商将需要一些工具,所以让我们实施它们。有创建工具的多个选项在Smolagents中:我们可以重复使用Langchain工具,从HuggingFace Hub下载它们,也可以简单地创建Python函数。我们将通过编写几个Python功能并用注释来采取最直接的方法@工具。一个 

我将创建两个工具:一种用于估计指标之间的相对差异,而另一个用于计算列表的总和。由于LLM将使用这些工具,因此提供详细的描述至关重要。 

@工具def calculate_metric_increase(之前:float,之后:float) - > float:”“”计算之前和之后度量的百分比变化args:在之前:之前的价值之后:值之后”“”返回(之前 - 之后) * 100/之前@工具def calculate_sum(值:列表) - > float:”“”计算列表总和args:值:数字列表”“”返回总和(值)

预告片:稍后,我将意识到我应该为代理商提供更多工具,但我确实忽略了它们。

代码

让我们从一个代码开始。我用我们之前定义的工具初始化了代理商,并授权使用一些可能有帮助的Python软件包。 

代理= codeagent(模型=模型,工具= [calculate_metric_increase,calculate_sum],max_steps = 10,附加_authorized_imports = [“ pandas”,“ numpy”,“ matplotlib。*”,“绘制。*”],verbosity_level = 1)任务=“”“”这是一个数据范围,显示逐个细分市场的收入,比较值之前和之后。您能帮我了解变化吗?具体来说:1。估计每个细分市场的总收入和收入无论是绝对的还是一个百分比,都改变了。2。计算每个部分对总数的贡献变化收入。请在输出中围绕所有浮点数到两个小数点。”“”agent.run(任务,附加_args = {“ data”:df},)

总体而言,代码代理仅使用5,451个输入和669个输出令牌完成了任务。结果看起来也很合理。

{'total_before':1731985.21,'total_after':1599065.55,'total_change':-132919.66,'sement_changes':{'absolute_change':{'其他':4233.09,'uk':-4376.25,'france':-132847.57,“德国”:-690.99,'意大利':979.15,'Spain':-217.09},'persationage_change':{'其他':0.67,'uk':-0.91,“法国”:-55.19,'德国':-0.43,'Italy':0.81,'Spain':-0.23},'warmitution_to_change':{'其他':-3.18,'uk':3.29,'法国':99.95,'德国':0.52,'意大利':-0.74,'西班牙':0.16}}}}}

让我们看一下执行流。LLM收到了以下提示。 

'''' -``这是一个显示逐段收入的大熊猫数据框架''比较之前和之后的值。您能帮我了解变化吗?特别是:1。估计每种收入的总收入和收入如何无论是绝对的而言,细分市场已经改变百分比。2。计算每个部分对总数的贡献变化收入。 -请将输出中的所有浮点数圆成两个小数点。 -``您得到了这些其他论点,您可以使用钥匙作为python中的变量访问代码:{'df':之后国家其他632767.39 637000.48英国481409.27 477033.02法国240704.63 107857.06德国160469.75 159778.76意大利120352.31 121331.46西班牙96281.86 96064.77}。 -°litellmmodel-openai/gpt-4o-mini –âââââââââââ–

在第一步中,LLM生成了数据框并执行了所有计算。有趣的是,它选择独立编写所有代码,而不是使用提供的工具。 

更令人惊讶的是,LLM根据输入数据重新创建了数据框,而不是直接引用它。这种方法不是理想的(尤其是在使用大量数据集时),因为它可能导致错误和更高的标记使用情况。通过使用更明确的系统提示,可以改善此行为。这是代理在第一步中执行的代码。

导入大熊猫作为pd#从提供的数据创建数据框数据= {“之前”:[632767.39,481409.27,240704.63,160469.75,120352.31,96281.86],“之后”:[637000.48,477033.02,107857.06,159778.76,121331.46,96064.77]}index = ['其他','uk',“法国”,“德国”,“意大利”,“西班牙”]df = pd.dataframe(数据,索引=索引)#在之前和之后计算总收入total_before = df ['之前']。sum()total_after = df ['after']。sum()#计算每个细分市场的绝对和百分比变化df ['absolute_change'] = df ['after']  -  df ['之前']df ['persationage_change'] =(df ['absolute_change'] /df ['之前']) * 100#计算总收入变更total_change = total_after -total_before#计算每个细分市场对总更改的贡献df ['runtution_to_change'] =(df ['absolute_change'] /total_change) * 100#圆形结果df = df.Round(2)#打印计算结果打印(“以前的总收入:”,total_before)打印(“以下总收入:”,total_fter)打印(“收入的总变化:”,total_change)打印(DF)

在第二步中,LLM只是指参考上一步上计算的变量(真的很整洁)来构建最终答案。 

final_answer({“ total_before”:round(total_before,2),“ total_after”:round(total_after,2),“ total_change”:round(total_change,2),“ segment_changes”:df [['absolute_change','百分比_change','贡献_to_change']]。to_dict()}))

它效果很好。

工具卡

现在,是时候看看传统的工具呼叫代理如何解决此问题了。我们以类似的方式初始化了它,并执行了任务。

来自SmolAgents Import toolcallingagent传统_agent = toolcallingagent(模型=模型,工具= [calculate_metric_increase,calculate_sum],max_steps = 30,)任务=“”“”这是一个数据范围,显示逐个细分市场的收入,比较值之前和之后。您能帮我了解变化吗?具体来说:1。估计每个细分市场的总收入和收入无论是绝对的还是一个百分比,都改变了。2。计算每个部分对总数的贡献变化收入。请在输出中围绕所有浮点数到两个小数点。”“”传统_agent.run(任务,附加_args = {“ data”:df},)

结果远非理想:只有相对变化是正确的,而其余数字是纯粹的幻觉。我必须承认,核心问题是缺乏适当的工具(特别是计算差异并估算股票的工具)。但是,代理应该标记缺少工具,而不是生成随机数。

总收入变化:-7319.66(-7.67%)。按细分市场变化: - 其他:+232.09(-0.67%) - 英国:-4376.25(0.91%) - 法国:-132847.57(55.19%) - 德国:-690.99(0.43%) - 意大利:+979.15(-0.81%) - 西班牙:-217.09(0.23%)对总变化的贡献: - 其他:0.03% - 英国:-59.88% - 法国:-181.77% - 德国:-9.43% - 意大利: +13.38% - 西班牙:-0.03%

在资源使用方面,工具称呼代理的性能差得多:12个步骤,有29,201个输入和1,695个输出令牌。因此,在代理执行方面,代码代理显然可以节省成本。

让我们更深入地了解代理商实际做的事情。首先,花了四次迭代才能弄清楚如何致电计算_sum正确并估算之前和之后的总收入。 

 - â–''''' - - - â–'''呼叫工具:“计算_sum”与参数:{'values':{''':[632767.39,481409.27,240704.63,160469.75,120352.31,96281.86],''':[637000.48,477033.02,107857.06,159778.76,121331.46,96064.77]}}}}'''''''''与参数的工具“计算_​​sum”无效呼叫{“ values”:{“ frone”:[632767.39,481409.27,240704.63,160469.75,120352.31,96281.86],“之后”:[637000.48,477033.02,107857.06,159778.76,121331.46,96064.77]}}:+:'int'和'str'的未支撑操作数类型您应该使用正确的输入参数调用此工具。预期输入:{“ values”:{“ type”:“ object”,“描述”:“值列表”}}}返回输出类型:数字工具说明:“计算列表的总和”[步骤1:持续时间4.06秒|输入令牌:1,190 |输出:250] - - - - â–'''呼叫工具:“计算_sum”与参数:{'values':{''':[632767.39,481409.27,240704.63,160469.75,120352.31,96281.86],''':[637000.48,477033.02,107857.06,159778.76,121331.46,96064.77]}}}}'''''''''与参数的工具“计算_​​sum”无效呼叫{“ values”:{“ frone”:[632767.39,481409.27,240704.63,160469.75,120352.31,96281.86],“之后”:[637000.48,477033.02,107857.06,159778.76,121331.46,96064.77]}}:+:'int'和'str'的未支撑操作数类型您应该使用正确的输入参数调用此工具。预期输入:{“ values”:{“ type”:“ object”,“描述”:“值列表”}}}返回输出类型:数字工具说明:“计算列表的总和”[步骤2:持续时间8.60秒|输入令牌:2,815 |输出:386] - - - - â–'''呼叫工具:“计算_sum”与参数:{'values':[632767.39,481409.27,240704.63,160469.75,120352.31,96281.86]}}'''''''''观察:1731985.2100000002[步骤3:持续时间1.23秒|输入令牌:4,871 |输出:488] - - - - â–'''呼叫工具:“计算_sum”与参数:{'values':[637000.48,477033.02,107857.06,159778.76,121331.46,96064.77]}}'''''''''观察:1599065.55

接下来的七个步骤花了计算_metric_increase工具。

 - - - - â–'''呼叫工具:“计算_metric_increase'参数:{''':1731985.21,'之后':1599065.55}''''───────────────────────────────╯Observations: 7.674410799385517<-- similar tool calls for all country segments -->

In the end, the agent put together a final call. 

So, if the LLM had had tools to calculate the absolute difference and the share of the sum, it would have taken an additional 14 iterations and even more tokens.Of course, we can prevent such inefficiencies by carefully designing the tools we provide:

  • We could modify our functions to work with lists of values instead of single items, which would significantly reduce the number of steps. 
  • Additionally, we could create more complex functions that calculate all necessary metrics at once (similar to what the code agent did).This way, LLM wouldn’t need to perform calculations step-by-step.However, this approach might reduce the flexibility of the system. 

Even though the results weren’t ideal due to a poor choice of tools, I still find this example quite insightful.It’s clear that code agents are more powerful, cost-efficient and flexible as they can invent their own comprehensive tools and perform multiple actions in a single step.

You can find the complete code and execution logs ongithub

概括

We’ve learned a lot about the code agents.Now, it’s time to wrap things up with a quick summary.

Code agents are LLM agents that “think” and act using Python code.Instead of calling tools via JSON, they generate and execute actual code.It makes them more flexible and cost-efficient as they can invent their own comprehensive tools and perform multiple actions in a single step. 

HuggingFace has brought this approach to life in their framework, smolagents.Smolagents makes it easy to build quite complex agents without much hassle, while also providing safety measures during the code execution. 

In this article, we’ve explored the basic functionality of the smolagents library.But there’s a lot more to it.In the next article, we will dive into more advanced features (like multi-agent setup and planning steps) to build the agent that can narrate KPI changes.敬请关注!

Thank you a lot for reading this article.I hope this article was insightful for you.

参考

This article is inspired by the “Building Code Agents with Hugging Face smolagents” short course by DeepLearning.AI.

关于《代码代理:代理AI的未来|迈向数据科学》的评论


暂无评论

发表评论

摘要

总而言之,通过使用“ Smolagents”库对代码代理的探索揭示了几个关键见解:1。**代码代理灵活性**:与传统的基于工具的代理不同,该代理在预定义的功能集中运行,代码代理可以动态编写和执行Python代码,以有效地解决复杂的任务。这种灵活性使他们能够处理各种类型的问题,而无需大量的预定义工具列表。2。**成本效率**:通过通过生成的代码而不是迭代工具调用执行多个操作,代码代理减少了与任务完成相关的步骤和总体计算成本。3。**工具发明**:代码代理可以通过即时创建Python功能来生成自己的全面“工具”。这减少了对可能有限或不适合复杂任务的预定义工具的依赖,从而使其具有高度适应性。4。**安全措施**:尽管具有灵活性,但代码代理配备了安全功能,以确保安全执行生成的代码并防止意外后果。5。**工具限制**:传统的基于工具的代理可能会在需要更复杂操作的任务上挣扎,如果必要的工具不可用或定义明确。6。**未来的方向**:进一步的探索可能涉及高级功能,例如多代理设置,计划步骤甚至KPI变化的叙事生成。提供的示例表明,代码代理可以通过生成Python代码来直接从值列表中计算总和来快速总结关键财务指标(例如,在之前和之后的总收入)。同时,由于其预定义工具的局限性,基于工具的代理商在类似的任务上挣扎,并且需要多个步骤才能得出相同的结论效率低下。从本质上讲,尽管两种方法都具有其优点,但本文强调了代码代理在涉及动态数据分析或财务指标的复杂解决任务时的卓越性能和适应性。