你好,我是司沐。

带你深入解析smolagents框架”系列博客是我开的一个新坑,希望带你深入 smolagents 框架的各个核心组件,探索CodeAgentToolCallingAgent、基类MultiStepAgent的设计思路、二开思路,以及工具定义、记忆编排、代码沙箱等组件的深入解析,最终实现快速无痛地在其中加入定制化功能,实现自己客制化的“smol

在这里插入图片描述

本期,我们要讲到的是CodeAgent抽象。


考虑到代码解读比较枯燥且冗长,我计划将CodeAgent解读分为两期,分别是本期【如何搭建一个CodeAgent——从需求分析出发】和下一期【如何搭建一个CodeAgent——源码级执行流分析】

CodeAgentsmolagents 框架中最具张力的实现。与传统的“JSON 填空式” Agent 不同,CodeAgent 的核心理念是“代码即工具” (Code as Tool)。

它不只是调用工具,而是将每个工具当作一个函数,通过编写 Python 代码来编排工具、处理数据和控制逻辑。
在这里插入图片描述

对比起JSON方式执行工具,Code方式有非常多的优势:

  • 一轮交互可以组合调用多个工具

    这个很好理解,如果给定两个工具(函数),大模型可以写一段代码,先调用函数A,并把A的返回值做特定处理后再交给函数B作为入参的一部分,最终再输出结果。而JSON模式的极限也只不过是同时写两段JSON而已,第一次调用的内容并不能作为第二次调用的输入使用。

  • 支持丰富的控制流/数据流(如循环和变量重用)

    也十分好理解,模型可以通过编写代码来对执行中间过程的任何数据做处理,比如求加权平均数、计算方差,甚至更复杂的策略。

  • 比起JSON,Code方式正确率更高

    这一点已经被许多论文反复证明了,比较经典的一篇就是Executable Code Actions Elicit Better LLM Agents
    这篇论文中提到,在使用了Code as Tool方式后,基于17种LLM在API-Bank和新建的多工具多步任务集(M3 ToolEval)上的测试结果显示:CodeAct的任务成功率比JSON/Text基线高出约20个百分点。
    在复杂任务(需多次工具调用)上,CodeAct最高可提升约20%的成功率,并且所需操作步骤减少约30%

    在这里插入图片描述

由于Code as Tool范式赋予了模型自己制定循环、条件语句、变量重用等等策略、还能利用丰富的代码预训练数据、自动调试等能力,所以这样的范式可预见地在大多数场景下优于仅生成JSON接口调用。


本文将用自上而下的视角,以一个具体例子出发,拆解 CodeAgent 的全生命周期。

  1. 首先,把大象装进冰箱里分几步? 我们来一点点分析:如果给定任务和工具,想让Agent多轮调用工具完成任务,需要Agent具备哪些能力?
  2. 再从 宏观行为模式 入手,展示框架在执行任务时从前到后都做了什么事情;
  3. 有了宏观基础之后,进而做 源码级执行流分析 ,深入解读框架是如何实现这些行为的。
  4. 最后,给整个CodeAgent框架做一个回顾总结

为了方便理解,我们将使用这样一个具体案例任务:
“你能告诉我明天西安的天气怎么样吗?”

在这里插入图片描述

一、把大象装进冰箱需要几步?

设想这样一个场景:

你有一个Agent,
你想问问他明天天气怎么样,
你问他:明天天气怎么样?
他回答:明天西安天气晴朗,气温25度。

这个过程中,Agent至少要做些什么,才能回复给你答案?


最直接的,Agent里面肯定要有个大模型,才能发挥主动性帮你查资料。
所以,Agent要有加载和调用大模型的能力。
在这里插入图片描述

同样显然的,Agent肯定需要联网。
无论是通过搜索引擎还是天气API,一定要有一个实时的数据源作为工具配置到Agent中。
也就是说,Agent需要导入工具的能力。
在这里插入图片描述


我们知道,在Code as Agent范式中,工具的本质是一个Python函数。
那要执行函数,就必须要有一个Python运行环境。
所以,Agent要内置一个Python解释器。
在这里插入图片描述

既然要知道用户输入的问题,最后也要给用户输出答案,这个答案还不能与调用工具时的输出混在一起,要清晰明了。
那么,Agent要有输入用户问题和输出最终答案的方法。
在这里插入图片描述


刚才想了这么多,看起来Agent的上下文至少包含用户输入消息通过代码调用工具工具的执行结果Agent最终输出这四种。
如果工具报错,我们还需要让Agent知道错哪了,让他改正,这又是第五种上下文类型:错误返回
对于这么复杂的上下文类型,Agent要有管理上下文的能力。
在这里插入图片描述

LLM在生成调用工具的代码之前,一般都会先自言自语几句,梳理一下思路,所以LLM的输出是自然语言与代码混合输出的。
为了把其中的代码单独提取出来,Agent需要有从字符串中提取代码的能力。
在这里插入图片描述


在执行代码时,如果LLM写出了危险的代码,比如调用os库删掉了用户的文件,或者通过request库下载到了一个病毒,就非常糟心了。
所以,Agent需要有约定LLM能使用的Python库的能力。
在这里插入图片描述

我们已经说了很多复杂的行为了,如果让Agent自己去探索自己的能力边界,是一件很折磨Agent的事情;
为了不折磨Agent,我们应该给Agent通过提示词说清楚,它能够通过什么方式与约定,使用上面那些能力。
但每次都人为规定,有些麻烦,如果想开箱即用,最好给一组默认提示词。
这意味着,Agent需要读取默认系统提示词的能力。
在这里插入图片描述


还有疏漏吗?
看起来没有了
那我们就带着问题与对问题的思考,一起进入下一个阶段吧!


二、宏观行为模式 —— CodeAgent 的两阶段行为

在宏观视角下,CodeAgent 的生命周期可以清晰地划分为两个阶段:初始化运行

1. 初始化阶段:配置大脑(LLM)与沙盒

在这里插入图片描述

当我们执行 agent = CodeAgent(...) 时,系统主要完成了三件事:

  1. 知道自己能做到什么(注册工具与可用Python库)
    Agent 并不是全知全能的。初始化时,它确立了自己的“能力范围”。

    • 工具加载:传入的 tools(如 search_baidu)被注册到 Agent 内部。
    • 安全围栏:它初始化了 authorized_imports。默认情况下,Agent 只能导入 Python 标准库。

      如果你希望它能用 pandasnumpy,必须在初始化时显式授权。这是防止 Agent 生成风险代码的第一道防线。

  2. 知道自己要如何做事(加载提示词模板)
    根据参数(是否使用结构化输出),Agent 会从 YAML 文件加载对应的 System Prompt。

    这个 Prompt 是一个动态模板,告诉 LLM:“你是一个 Python 编程专家,你可以使用以下工具…”,并定义了代码块的格式(如 ```python)。

  3. 拿到做事的家伙什(创建Python执行器)
    这是 Agent 的“手脚”。Agent 默认会创建一个 LocalPythonExecutor(在本地运行),但它也支持 Docker、E2B 等沙箱环境。

    这一步实现了逻辑(LLM)与执行(Runtime)的解耦

2. 运行阶段:ReAct 循环

在这里插入图片描述

当我们调用 agent.run("西安明天的天气") 时,Agent 进入核心工作流。这是一个标准的 ReAct (Reasoning + Acting) 循环,但被赋予了代码解释器的能力。

  1. 记忆重置与任务注入
    如果是新的一轮对话,内存(AgentMemory)会被清空。系统提示词(System Prompt)和用户的任务(Task)被封装成 TaskStep 压入记忆栈。

  2. 规划(可选)
    如果配置了 planning_interval,Agent 会先进入“思考时间”,生成一个多步计划,而不急于写代码。

  3. ReAct 迭代循环
    这是最关键的流程。父类 MultiStepAgent 控制循环次数(默认 20 步),每一轮循环调用子类 CodeAgent 的核心逻辑:

    • Think:LLM 观察当前的历史记录,生成一段包含 Python 代码的文本。
    • Code Parsing:Agent 从漫无边际的回复中,精准提取出代码块。
    • Act:将代码扔进 PythonExecutor 执行。
    • Observe这是 CodeAgent 的精髓。执行器不仅返回函数的 return 值,还会捕获标准输出(print 的内容)。如果代码报错(例如 ImportError),错误堆栈也会变成 Observation 反馈给 LLM。这种机制赋予了 Agent 自我调试(Self-Debugging) 的能力。
  4. 终止
    直到 LLM 决定调用 final_answer 工具,或者达到最大步数,循环结束,返回结果。


感谢阅读本篇~

本篇文章主要从需求分析出发,讲了想要实现一个好用的Agent,需要为其配备哪些能力;随后以smolagents框架中的CodeAgent为例,从宏观的视角讲了它的实现思路。

在下一篇中,我们会对CodeAgent做代码级别的精读,深入代码腹地来吃透框架。
在看完下篇之后,你甚至可以动手写一个属于自己的框架!

我是司沐,我们下期见~

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐