[控场AI]
· 9 分钟阅读· 4,723 字

从零构建AI编程Agent:用Gemini打造你的命令行助手

从零构建AI编程Agent:用Gemini打造你的命令行助手

从零用Gemini API构建命令行编程Agent,深入理解工具调用与Agent循环的底层机制。

Boot.dev的Lane Wagner设计了一门实战课程,带领开发者用Google Gemini Flash API从零构建一个类似Claude Code的命令行编程Agent,其核心目标不是生产可用的工具,而是让开发者真正理解AI Agent的底层运作原理。课程揭示了Agent与普通聊天机器人的本质差异:Agent能通过工具调用自主获取项目上下文,并在循环中连续执行任务直到目标达成。整个Agent仅由四个工具函数构成(列目录、读文件、写文件、执行Python),配合安全护栏设计和错误字符串返回机制,使LLM能在循环中自我纠错。课程特别澄清了一个关键认知:LLM并不直接调用函数,它只是决策引擎,通过结构化文本响应表达调用意图,实际执行由程序代码完成。最终的演示中,Agent成功自主定位并修复了一个故意植入的计算器运算优先级Bug,验证了这套极简架构的实际能力。

为什么要从零构建编程Agent

Boot.dev的Lane Wagner带来了一门与众不同的实战课程:不是教你如何使用现成的AI工具,而是带你用Google的Gemini Flash API从零构建一个属于自己的编程Agent。这个Agent类似于OpenAI的Codex或Anthropic的Claude Code,只不过运行在命令行而非编辑器的图形界面中。

课程作者用了一个精妙的比喻:当下AI领域正在上演一场"淘金热",与其自己下场挖金子(指盲目地做氛围编程),不如去"卖铲子"——也就是亲手构建编程Agent。但构建的真正目的并非赚钱,而是像程序员学习二叉树逻辑一样,理解我们每天使用的工具在底层究竟如何运作,从而更有效地驾驭它们。

这个项目要构建的是一个能够自主修改代码的Agent,而不是简单的GPT封装。它能扫描文件系统、修改文件,甚至运行代码获取反馈,判断哪些有效、哪些失败,然后再次尝试修复Bug或实现新功能。

Agent与普通聊天机器人的本质区别

一个编程Agent和普通的ChatGPT有什么不同?关键在于上下文与循环。

当你在浏览器里用ChatGPT处理代码问题时,必须不断地复制粘贴代码,手动告诉它预期行为。而编程Agent(如Cursor或Claude Code)有能力扫描项目目录、查看文件、运行代码、更新文件内容——它能自主获取当前情况的上下文,这正是它在处理项目时强大得多的根本原因。

课程中明确了Agent的核心定义:让它成为Agent的关键在于它能在循环中执行任务。与其只提供一个提示词并返回单次响应,Agent可以不断自我提示、反复处理,针对用户给出的单个输入进行多轮操作。而它生成这种反馈循环的方式,就是通过所谓的"工具调用"(tool calling)。

Agent工作原理演示

作者还展示了Boot.dev平台上自家的聊天机器人Boot作为示例——它采用苏格拉底式教学法引导学生,并且是"智能体化"的。当用户询问平台游戏机制时,Boot会先调用一个提供游戏文档的函数读取内容,再做出回应,这正是工具调用的典型应用。

工具调用(Tool Calling / Function Calling) 是现代LLM API的核心扩展能力,由OpenAI于2023年率先在API中标准化,随后Google、Anthropic等主要厂商相继跟进。其本质是:开发者在发送请求时附带一份"函数说明书"(包括函数名、参数名称和类型、用途描述),模型在生成回复时若判断需要调用某函数,就以结构化JSON而非自由文本的形式输出调用意图。开发者程序检测到这种结构化响应后,负责在本地真正执行对应函数,再将执行结果塞回对话上下文,模型读取结果后继续推理。这套机制使模型得以"感知外部世界",但实际上模型本身始终被隔离在云端,对本地环境没有任何直接访问权限——所有副作用都由调用方代码负责执行,这也是文章后续强调"LLM只是决策引擎"的技术根源。

四个工具调用:Agent的全部能力

整个Agent仅由Google Gemini驱动,并提供四种工具调用。作者强调,仅凭这四个工具,Agent能做的事情就已经相当惊人:

  • 列出目录文件:相当于赋予它执行 ls 命令的能力
  • 读取文件内容:能获取目录中任何需要的信息
  • 覆盖写入文件:使它可以进行自我更新和修改
  • 执行Python代码:这是最关键的一环

有了这四个工具,当你对它说"我这有个bug,字符串没切分开,去修复它",Agent就会查看应用目录结构,找到可能出问题的文件进行修改,运行程序验证效果,如果没成功就再次修改、再次运行,循环操作直到它认为解决了问题或失败为止。

技术前提条件

开始项目前你需要准备:Python 3.10或更高版本;UV这款现代化的项目与包管理器(Boot.dev已将所有Python项目从pip和venv迁移至UV);以及一个类Unix的Shell环境(Windows用户强烈建议使用WSL)。

理解LLM、Token与Gemini API

课程选择Gemini有两个原因:它提供免费层级,且是一个非常出色的基础模型,在免费额度内就能完成大量工作。整个项目全程都控制在Gemini的免费限制范围内。

关于Token的理解非常重要。调用AI API几乎都是基于Token用量计费的。一个Token大约相当于4个字符——把提示词的字符总数除以4,就能大致估算实际消耗的Token数。作者在实际演示中发现,连空格都会计入Token数量,这是容易被忽略的细节。

获取API Key的配置过程

在代码实现上,程序从硬编码提示词逐步演进:先改为通过命令行参数接收动态提示词(sys.argv),再引入消息列表来存储对话上下文。这里引出一个核心认知:与LLM的对话本质上是一个交替出现的user和model消息数组。当使用API时,必须自行保留这些历史记录,否则模型就无法记住过去的对话——这与ChatGPT网页版自动记住上下文的体验截然不同。

Token是LLM处理和定价的基本单位,但不同语言的Token效率差异显著。英文文本中一个Token平均对应约4个字符,而中文、日文等非拉丁字母语言通常一个汉字就消耗1至2个Token,意味着相同信息量的中文提示词往往比英文贵。理解Token还有助于把握上下文窗口(Context Window)的限制——Gemini Flash 1.5拥有100万Token的上下文窗口,这意味着可以将大型代码库的多个文件同时放入一次请求中。然而上下文越长,每次请求的费用和延迟也越高。对于Agent循环来说,每次迭代都要把完整消息历史重新发送给API,随着对话轮次增加,Token消耗会线性增长,这正是生产级Agent需要做上下文压缩或摘要的原因。

文件操作函数的安全护栏

构建四个工具函数时,一个贯穿始终的设计原则是安全护栏。所有函数都会检查目标路径是否在工作目录之外,如果越界就返回错误字符串。

工作目录边界检查

你可能没注意到,这些函数在出错时返回的是字符串而非抛出异常。原因很直接:LLM在调用这些函数,我们希望LLM能读到清晰的错误信息,从而在下一个Agent循环中纠正错误——判断是不是调用方式不对,还是哪个环节出了问题。

在构建过程中,作者坚持使用标准库的路径处理函数(如 os.path.dirname、os.path.join),而非手动字符串解析。这些函数能妥善处理跨操作系统的差异(Windows与Linux的路径处理方式不同)以及各种边缘情况。

测试文件保存与调试

关于执行Python代码这一最危险的能力,课程特别强调了安全风险:即便将范围限制在特定目录内,LLM仍可能创建新Python文件,而那段代码本身可以跳出工作目录执行操作。因此这始终是一个玩具教学项目,不应用于日常开发替代Codex或Claude等生产级工具。此外还为代码执行添加了30秒超时限制,防止无限运行消耗CPU。

系统提示词与函数声明的工作机制

这里揭示了一个可能让人意外的事实:LLM其实并不直接调用函数。AI提供商没有办法接入我们的本地运行环境,一切交互仅仅是文本。

真实的运作流程是:首先通过文本告诉LLM它有哪些可用函数以及如何使用(这是通过 types.FunctionDeclaration 构建的结构化声明);然后LLM以结构化方式响应"我想调用write_file函数,用这个路径和这个内容";最后由我们的程序去真正执行该函数。LLM只是决策引擎,由它决定调用哪一个工具,实际调用由Agent完成。

系统提示词(system prompt)则是一种特殊的、权重更高的提示词。几乎所有主流LLM提供商都允许通过API设置,它比普通用户提示词更具权威性。作者演示了经典的"提示词注入"实验——让系统提示词要求模型只回答"我是机器人",然后尝试用"忽略之前所有指令"来绕过。在LLM早期阶段这类手段有效,但供应商投入大量工作后,现在模型能更好地遵循系统提示词。

从单次调用到完整Agent循环

一个Agent由两部分组成:工具调用和循环。此前的实现只能做单次工具调用,要让它真正智能体化,必须让它能自主连续执行操作,直到满足用户需求。

这里引入了第三种消息角色。此前只处理user和model,现在增加 tool 角色——model表示模型想调用的工具,tool则是我们反馈给模型的函数执行结果。对话上下文因此变成:用户请求 → 模型想调用某工具 → 工具返回结果 → 模型再调用 → 再返回结果……如此循环,直到最后一条model角色消息返回纯文本答复。

实现上,把 generate_content 调用放进一个最多20次迭代的循环中,每次都传递完整的messages列表,让LLM根据当前状态执行下一步。退出条件是响应返回了 text 属性(纯文本答复)。

最终的"致命一击"测试令人印象深刻:作者故意把计算器的加号运算优先级改错,使 3 + 7 * 2 算出错误结果20。运行Agent后,它自主扫描目录、读取文件、定位bug、修改代码、运行测试验证,最终成功修复。在这个过程中作者还通过优化系统提示词,让Agent学会在宣布"一切正常"之前先扫描目录、运行测试确认——这正是构建项目无关Agent的关键思路。

Agent循环(Agentic Loop)是ReAct(Reasoning + Acting)架构的工程实现。ReAct由Google Research于2022年提出,其核心思路是让模型交替执行"推理"(分析当前状态、制定下一步计划)和"行动"(调用工具获取外部信息),并将行动结果再次输入推理步骤,形成闭环。文章中描述的"user → model想调用工具 → tool返回结果 → model再调用……"正是这一模式的直接体现。设置最多20次迭代上限是工程上的必要保险:LLM在某些情况下可能陷入反复调用同一工具却无法前进的死循环(俗称"tool loop"),硬性迭代上限可防止无限消耗API配额和计算资源。LangChain、LlamaIndex等主流Agent框架在底层也都实现了类似的循环与退出机制。

这门课程的真正价值

这个项目的目标从来不是训练LLM,也不是做一个完美的生产工具,而是让开发者深入理解AI Agent的底层运作方式。正如课程反复强调的:在工作中利用AI工具并没有错,但了解它们的工作原理非常重要。

如果你的竞争对手不仅是优秀的开发者,还是精通AI工具的优秀开发者,那么理解这些技术的原理就成了职场竞争力的关键。从零构建一个Agent,既能提升Python和函数式编程技能(课程大量涉及高阶函数),也能让你在使用Cursor、Claude Code这类工具时心中有数。

分享:

相关推荐