news 2026/9/19 2:31:50

从简单处理器到代码 Agent:smolagents 中“Agent 能力“谱系与 ReAct 实现原理全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从简单处理器到代码 Agent:smolagents 中“Agent 能力“谱系与 ReAct 实现原理全解析

从简单处理器到代码 Agent:smolagents 中"Agent 能力"谱系与 ReAct 实现原理全解析

【免费下载链接】smolagents🤗 smolagents: a barebones library for agents that think in code.项目地址: https://gitcode.com/gh_mirrors/smo/smolagents

本篇技术指南围绕 smolagents 官方《Agent 简介》概念文档展开,系统讲解"什么是 Agent"、Agent 能力从弱到强的连续谱系、多步 Agent 的循环(ReAct)工作结构、何时该用/不该用 Agent 的工程判断,以及 smolagents 为何选择"让 LLM 用代码写动作"的代码 Agent(Code Agent)路线。读完本文,你将掌握 Agent 能力的量化评估框架、smolagents 的核心抽象组成(LLM 引擎、工具列表、系统提示、动作解析器、记忆),并能对照 agents.py 与 code_agent.yaml 的源码实现,亲手搭建一个可运行的最小 Agent。

什么是 Agent:LLM 输出控制工作流的程序

任何高效使用 AI 的系统,都需要为 LLM 提供某种访问现实世界的途径:例如调用搜索工具获取外部信息,或操作某些程序以完成任务。换句话说,LLM 应当具备Agent 能力(agency)。Agent 程序就是 LLM 通往外部世界的门户。

[!TIP] AI Agent 是LLM 输出控制工作流的程序

任何利用 LLM 的系统,都会把 LLM 的输出集成进代码。LLM 输出对代码工作流的影响程度,就是该系统赋予 LLM 的 Agent 能力级别。

需要特别强调的是:按照这一定义,"Agent" 并不是一个非 0 即 1 的离散概念。相反,Agent 能力是一个连续谱系——取决于你在工作流中赋予 LLM 多少权力,系统可以处在谱系上的任意位置。

Agent 能力级别一览

下表展示了 Agent 能力在不同系统中的典型变化:

Agent 能力级别描述名称示例模式
☆☆☆LLM 输出对程序流程没有影响简单处理器process_llm_output(llm_response)
★☆☆LLM 输出决定 if/else 分支路由if llm_decision(): path_a() else: path_b()
★★☆LLM 输出决定函数执行工具调用者run_function(llm_chosen_tool, llm_chosen_args)
★★★LLM 输出控制迭代和程序继续多步 Agentwhile llm_should_continue(): execute_next_step()
★★★一个 agent 工作流可以启动另一个 agent 工作流多 Agentif llm_trigger(): execute_agent()
★★★LLM 直接用代码行动,可自定义工具、启动其他 agent代码 Agentdef custom_tool(args): ...

从表中可以清晰看出演进脉络:从"输出完全不影响流程"的简单处理器,到"决定分支"的路由器,再到"决定调用哪个函数"的工具调用者,最终进化到"控制循环迭代"的多步 Agent、可嵌套启动其他工作流的多 Agent,以及直接在代码层面行动的代码 Agent。Agent 能力越强,LLM 对程序流程的控制权越大,系统能处理的任务也越复杂。

多步 Agent 的核心循环结构

多步 Agent 具有以下代码结构:

memory = [user_defined_task] while llm_should_continue(memory): # 这个循环是多步部分 action = llm_get_next_action(memory) # 这是工具调用部分 observations = execute_action(action) memory += [action, observations]

这个系统在一个循环中运行:每一步执行一个新动作(该动作可能涉及调用一些预定义的工具,这些工具本质上只是函数),直到观察结果表明已达到解决给定任务的满意状态。

值得注意的是,这个伪代码并非凭空设计——它就是 smolagents 中MultiStepAgent的真实执行骨架。在 agents.py 中,MultiStepAgent的类注释明确指出它基于ReAct 框架:目标未达成时,Agent 会持续执行"思考(Reflection/Thought)→ 行动(Action)→ 观察(Observation)"的循环。其_run_stream方法(agents.py)正是:

while not returned_final_answer and self.step_number <= max_steps: ...

循环上限由max_steps控制(默认 20,见 agents.py 中MultiStepAgent.__init__的参数默认值)。每一步中,_step_stream依次完成"生成模型输出 → 解析动作 → 执行工具/代码 → 把观察结果写回记忆"的全流程,与文档中的伪代码一一对应。

何时使用 Agent,何时避免

当你需要 LLM 来决定应用程序的工作流时,Agent 很有用。但它们通常有些过度设计。关键问题是:我真的需要工作流的灵活性来有效解决手头的任务吗?

如果预定义的工作流经常不足,那才意味着你需要更多灵活性。

确定性工作流优先:一个冲浪旅行网站的例子

假设你在开发一个处理冲浪旅行网站客户请求的应用程序。你可以提前知道请求将属于两个类别之一(基于用户选择),并且为这两种情况都准备好了预定义工作流:

  1. 想要了解旅行信息?⇒ 给他们访问搜索栏以搜索你的知识库
  2. 想与销售交谈?⇒ 让他们填写联系表单

如果这个确定性工作流能覆盖所有查询,那就直接编码实现吧!这将为你提供一个 100% 可靠的系统,不会因为让不可预测的 LLM 干预工作流而引入错误风险。为了简单和稳健起见,工程上建议默认不启用任何 Agent 行为。

工作流不可预知时:Agent 登场

但如果工作流不能提前很好地确定呢?例如用户问:

"I can come on Monday, but I forgot my passport so risk being delayed to Wednesday, is it possible to take me and my stuff to surf on Tuesday morning, with a cancellation insurance?"

这个问题涉及许多因素(到达时间、天气、装备运输、取消保险),上述预定义的标准很可能都不足以满足这个请求。这正是 Agent 设置发挥作用的地方:你可以创建一个多步 Agent,让它访问天气 API 获取天气预报、Google Maps API 计算旅行距离、员工在线仪表盘以及构建在知识库上的 RAG 系统,自主编排这些工具来完成复杂的请求。

从工程史的角度看,直到最近,计算机程序仍局限于预定义工作流,试图通过堆积 if/else 分支来处理复杂性,只能处理"计算这些数字的总和"或"找到这个图中的最短路径"这类极其狭窄的任务。但实际上,大多数现实生活中的任务都不适合预定义工作流。Agent 系统为程序打开了现实世界任务的大门。

为什么选择 smolagents

对于链(chain)或路由器(router)这类低阶 Agent 用例,你可以自己编写全部代码,这样反而更好——因为你能更好地控制和理解自己的系统。

但一旦你开始追求更复杂的行为,比如让 LLM 调用函数(即"工具调用")或让 LLM 运行 while 循环("多步 Agent"),一些抽象就变得必要:

  • 工具调用需要解析:你需要解析 Agent 的输出,因此输出需要预定义格式,如"Thought: I should call tool 'get_weather'. Action: get_weather(Paris).",并用预定义函数解析它;同时给 LLM 的系统提示应当告知它这个格式。
  • 多步循环需要记忆:当 LLM 输出决定循环时,你需要根据上一次循环迭代中发生的情况给 LLM 不同的提示——所以你需要某种记忆能力。

从这两个例子出发,一个功能完整的 Agent 系统至少需要以下要素:

  • 一个作为系统引擎的LLM
  • Agent 可以访问的工具列表
  • 从 LLM 输出中提取工具调用的解析器
  • 与解析器同步的系统提示
  • 记忆能力

此外,既然我们给了 LLM 决策空间,它们必然会犯错:所以还需要错误日志记录和重试机制。所有这些元素都需要紧密耦合才能形成一个功能良好的系统——这正是 smolagents 提供基础构建块让它们协同工作的原因。

源码印证:五大要素在仓库中的落点

这些抽象并非停留在文档概念层面,在仓库中都有具体实现:

  • LLM 引擎Model抽象基类及其众多子类(InferenceClientModelTransformersModelLiteLLMModelAzureOpenAIModelMLXModel等)定义在 models.py,负责generategenerate_stream
  • 工具列表Tool/BaseTool类与@tool装饰器定义在 tools.py,默认工具箱(网页搜索、Python 解释器、语音转录等)在 default_tools.py。
  • 解析器MultiStepAgent.extract_action(agents.py)按分隔 token(如<code>/</code>)切分 LLM 输出得到 rationale 与 action,缺少分隔符时抛出AgentParsingError;代码块提取工具parse_code_blobsextract_code_from_text位于 utils.py。
  • 系统提示:由 Jinja 模板渲染,populate_template(agents.py)将工具列表、授权导入、代码块标签等变量注入 code_agent.yaml,错误类型AgentError家族(解析/生成/执行/工具调用错误)也定义在 utils.py。
  • 记忆AgentMemoryActionStepSystemPromptStepTaskStepFinalAnswerStep等内存步骤类全部位于 memory.py,write_memory_to_messages(agents.py)负责把记忆转换为发送给 LLM 的聊天消息列表。

代码 Agent:为什么让 LLM 用代码写动作

在多步 Agent 中,每一步 LLM 都可以编写一个动作,形式为调用外部工具。常见的动作书写格式(Anthropic、OpenAI 等广泛使用)通常是"将动作写成工具名称和参数的 JSON,然后解析以确定执行哪个工具、使用哪些参数"的不同变体。

多项研究论文(如《Executable Code Actions Elicit Better LLM Agents》等)表明,让 LLM 以代码片段形式书写动作更加自然、灵活,效果更好

原因很简单:我们专门设计了编程语言,使其成为表达计算机执行动作的最佳方式。如果 JSON 片段是更好的表达方式,JSON 早就成为顶级编程语言了。换句话说,Agent 将编写程序来解决用户的问题——用 Python 代码块表达程序,显然比用 JSON 容易得多。

与 JSON 动作相比,代码动作的四大优势

  • 可组合性(Composability):你能像定义 Python 函数一样,把 JSON 动作嵌套在一起,或定义一组 JSON 动作供以后复用吗?代码天然支持函数定义与复用。
  • 对象管理(Object management):你如何在 JSON 中存储像generate_image这样的动作的输出?代码中的变量可以自然地持有任意复杂对象(图像、音频、数据帧等)。
  • 通用性(Generality):代码被构建为可以简单地表达任何你能让计算机做的事情,从循环、条件、数据处理到任意第三方库调用。
  • LLM 训练数据中的表示(Representation in training data):大量高质量代码动作已经包含在 LLM 的训练数据中,意味着 LLM 已经为代码形式的动作完成了预训练。

代码 Agent 在 smolagents 中的落地形态

smolagents 提供两种 Agent 形态(详见 guided_tour):

  • CodeAgent(默认推荐,agents.py):动作由 LLM 以代码格式书写,然后被解析并执行。其类注释原话是 "In this agent, the tool calls will be formulated by the LLM in code format, then parsed and executed."
  • ToolCallingAgent(agents.py):使用 JSON 风格的工具调用,借助model.get_tool_call利用 LLM 引擎自带的工具调用能力。它与CodeAgent工作方式相似,但由于不执行代码,也没有additional_authorized_imports参数。

CodeAgent的系统提示加载自 code_agent.yaml,其中定义了完整的Thought → Code → Observation循环规范:每一步必须先写Thought:推理,再用代码块标签(默认<code></code>,可通过code_block_tags参数改为 markdown 风格)包裹 Python 代码,并用print()输出重要信息供下一步的Observation使用,最终通过final_answer工具返回答案。该提示还内置了 11 条硬性规则,例如:只使用自己定义过的变量;工具参数必须直接传参而不能用 dict(wikipedia_search(query="...")而非wikipedia_search({'query': "..."}));只能导入授权列表内的模块;代码执行之间的状态会持久保留;不要放弃任务等。

在 smolagents 中落地:从概念到可运行

快速上手:构建你的第一个代码 Agent

首先安装(默认工具包):

pip install "smolagents[toolkit]"

然后初始化一个最小 Agent。你至少需要两个参数:model(驱动 Agent 的文本生成模型)和tools(Agent 可用的工具列表,可以为空列表,也可通过add_base_tools=True叠加默认工具箱)。

以 Hugging Face Inference API 为例(guided_tour 提供了TransformersModelLiteLLMModelAzureOpenAIModelMLXModel、Ollama 等多种接入方式):

from smolagents import CodeAgent, InferenceClientModel model = InferenceClientModel(model_id="meta-llama/Llama-3.3-70B-Instruct", token="<YOUR_HUGGINGFACEHUB_API_TOKEN>") agent = CodeAgent(tools=[], model=model, add_base_tools=True) agent.run("Could you give me the 118th number in the Fibonacci sequence?")

CodeAgent的核心构造参数(agents.py)包括:

参数默认值说明
tools必填Agent 可用的Tool列表
model必填生成动作的Model
additional_authorized_importsNone额外授权导入的模块名列表,如['requests', 'bs4']
planning_intervalNone每 N 步运行一次规划(planning)步骤的间隔
executor_type"local"代码执行器类型:"local"/"blaxel"/"e2b"/"modal"/"docker"
max_print_outputs_lengthNoneprint输出的最大长度
stream_outputsFalse是否流式输出(要求模型实现generate_stream
use_structured_outputs_internallyFalse是否在每步使用结构化生成(对许多模型可提升性能,对应 structured_code_agent.yaml)
code_block_tags("<code>", "</code>")代码块开闭标签,可传"markdown"使用 ```python 风格

系统提示与动作解析的协同机制

CodeAgent.initialize_system_prompt(agents.py)把工具定义、managed_agents、授权导入列表、自定义指令以及代码块开闭标签注入 code_agent.yaml 模板,生成完整的系统提示。执行时,_step_stream(agents.py)以["Observation:", "Calling tools:"]等作为停止序列调用模型,再把模型输出按代码块标签切分、提取、交给执行器运行——系统提示中声明的格式与解析器严格同步,这正是文档强调"解析器与系统提示需要紧密耦合"的工程体现。

代码执行安全与沙箱化

默认情况下,代码在本地环境执行,local_python_executor.py 通过 AST 级别的安全检查(evaluate_ast等)限制可调用函数与可导入模块——唯一可调用的是你提供的工具和一组预定义的安全函数(如printmath模块),默认禁止安全列表之外的导入。你可以通过additional_authorized_imports参数授权额外导入:

from smolagents import CodeAgent model = InferenceClientModel() agent = CodeAgent(tools=[], model=model, additional_authorized_imports=['requests', 'bs4']) agent.run("Could you get me the title of the page at url 'https://huggingface.co/blog'?")

[!WARNING] LLM 可以生成任意代码然后执行:不要添加任何不安全的导入!

如果生成的代码尝试非法操作或出现常规 Python 错误,执行将停止。需要更强隔离时,可以通过executor_type="e2b"(需先设置E2B_API_KEY环境变量)或executor_type="docker"切换到远程沙箱执行,对应实现位于 remote_executors.py 的E2BExecutorDockerExecutorBlaxelExecutorModalExecutor(注意:从源码看,托管子 Agent 目前尚不支持远程代码执行)。若希望追踪每一步的完整日志,可以查看agent.logs,或用agent.write_memory_to_messages()将运行记忆转成聊天消息列表。

小结

本文从"Agent 是 LLM 输出控制工作流的程序"这一核心定义出发,梳理了 Agent 能力的五级连续谱系,论证了"确定性工作流优先、Agent 用于工作流不可预知场景"的工程原则,并解释了 smolagents 选择代码 Agent 路线的四大理由(可组合性、对象管理、通用性、训练数据表示)。对照 agents.py、code_agent.yaml、memory.py 与 local_python_executor.py 的源码,可以看到文档所述的 LLM 引擎、工具列表、解析器、系统提示、记忆与错误重试六大要素在仓库中均有对应的真实实现,ReAct 循环也以while not returned_final_answer and self.step_number <= max_steps的形式真实运行在每一轮 Agent 任务中。理解这层概念与实现的对应关系,是配置好、调试好、扩展好你自己的 Agent 系统的起点。

【免费下载链接】smolagents🤗 smolagents: a barebones library for agents that think in code.项目地址: https://gitcode.com/gh_mirrors/smo/smolagents

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 2:31:16

HarmonyOS如何开发星闪SLE智能家居控制应用?从原理到实战全解析

做智能家居这些年&#xff0c;我一直在关注短距无线通信方案的演进。蓝牙功耗低但时延不稳定&#xff0c;WiFi带宽够但费电&#xff0c;Zigbee组网强但速率太低。所以当星闪SLE出现在公开技术资料里的时候&#xff0c;我就觉得这个方向值得提前押注——低时延、高并发、低功耗&…

作者头像 李华
网站建设 2026/9/19 2:31:07

天地图市级节点多源地理数据聚合:从HTML解析到空间服务发布

简介&#xff1a;一份围绕“天地图常州”的地理数据解析与聚合方法研究PDF&#xff0c;聚焦大数据算法在地理信息公共服务平台中的应用&#xff0c;适合地理信息、数据挖掘及智慧城市方向的研究者、平台开发者和相关专业学生。该研究针对“天地图”基础测绘数据难以满足公众服务…

作者头像 李华
网站建设 2026/9/19 2:31:05

RSMA安全传输:预编码优化与NOMA/SDMA对比仿真

简介&#xff1a;面向无线通信安全研究的论文复现资料&#xff0c;围绕速率分割多址接入&#xff08;RSMA&#xff09;的安全传输预编码优化展开系统阐述。内容将RSMA与NOMA、SDMA统一于下行广播模型中&#xff0c;详细介绍用户消息拆分、公共流与私有流预编码设计、连续干扰消…

作者头像 李华
网站建设 2026/9/19 2:30:38

MBP标签技术:重组蛋白纯化的高效解决方案

1. MBP标签技术概述&#xff1a;重组蛋白纯化的关键工具在生物制药和生命科学研究领域&#xff0c;重组蛋白表达与纯化一直是核心挑战。作为全球领先的生命科学解决方案提供商&#xff0c;Cytiva开发的MBP&#xff08;麦芽糖结合蛋白&#xff09;标签技术已成为解决这一难题的利…

作者头像 李华
网站建设 2026/9/19 2:30:19

循环语句在游戏性能优化中的核心应用:从测试到开发实战

最近团队在优化一款中大型手游的帧率表现&#xff0c;排查到某个副本玩法时&#xff0c;发现一个很不起眼的NPC批量刷新逻辑&#xff0c;居然在低端机上吃掉了将近4ms的耗时。定位到最后&#xff0c;问题根源就是一段三层嵌套的循环语句。这类情况在游戏项目里太常见了&#xf…

作者头像 李华