我如何使用智能体编程
这是我持续自我教育的第二部分,关于如何将我的编程经验适应到一个会说话的计算机世界。第一部分我如何使用LLM编程涵盖了LLM可以如何适应我们现有的工具(基本上是自动完成)以及仔细的提示如何可以取代传统的网络搜索。现在我想谈谈使用智能体编程这个更困难、更有回报的行为。
1、定义智能体
从LLM背景下"智能体"这个词的定义开始是值得的。"AI"炒作周期一直在抛出这个非常通用的词汇,时间比智能体实际成为有用的构造更长。因此,有一些烟雾和镜子营销和一般的神秘主义需要挖掘才能找到这个词的任何价值。对于有工程背景的人来说,现在有一个直接的定义:智能体是9行代码。也就是说,智能体是一个包含LLM调用的for循环。LLM可以在没有人类在环的情况下执行命令并查看其输出。
就这样。像所有简单的事情一样,你的本能,相当合理地,可能会说"那又怎样?"它是一个for循环。但结果是对原始大型语言模型能力的惊人改进。
2、白板编程
想象你站在白板前,用记号笔用C编写一个函数,旨在测试UTF-8字符串是否有效。(这实际上发生在我身上,这是一种标准的面试技巧。风险很高,这次面试改变了我职业生涯的方向!)你在这个任务上的表现取决于你作为程序员的经验,以及你掩饰无法使用外部资源的能力。你需要记住UTF-8的编码。你需要不将你的C语法与你职业生涯中使用的所有其他类C编程语言混淆(是先名字后类型还是先类型后名字?)。在日常生活中,如果你犯了错误,你会从编译器得到反馈,你可以查找UTF-8的规范,最重要的是你可以编写程序并在其中添加一些printf来看看你做错了什么。
要求一个没有智能体的LLM写代码等同于要求你在白板上写代码。这是在挖掘半遗忘的记忆,在一个极其低效的基板上执行解析器,试图避免幻觉出一个真正有用的编程接口。LLM能够凭空创造程序是一个令人惊叹的技术成就,我认为自己有幸活着看到这一点,但将GPU连接到虚拟白板不会做大量有用的编程也就不足为奇了。
但如果我们给LLM的不仅仅是一个虚拟白板会发生什么?如果它能够调用编译器,看到编译器错误,并在我们看到结果之前有机会修复它们会怎样?如果它能够使用grep和cat来读取项目中的现有文件会怎样?如果它能够修补多个现有文件,包括单元测试并反复运行测试会怎样?智能体是反馈驱动的LLM。
3、智能体是具有环境反馈的LLM
就像人类在有反馈的环境中茁壮成长一样,当给LLM一组令人惊讶的小核心工具时,它们会从不错的演示变成有用的程序员,这些工具对程序员来说非常熟悉:
bash(cmd)patch(hunks)todo(tasks)web_nav(url), web_eval(script), web_logs(), web_screenshot(), 等keyword_search(keywords)codereview()
智能体非常擅长使用bash工具导航代码库:find、cat、grep -R,就像我们这些在IDE之前的人倾向于导航代码库一样。预先指示它将更改提交到git,它通过使用bash工具运行git add、git commit等来完成。
与没有这些工具的LLM生成代码相比,结果显著不同。值得注意的是:
- API使用大大改善,因为智能体可以网络搜索文档并将文档
curl到其上下文中。 - 编译器反馈减少了语法错误和幻觉接口。
- 完整开发环境中的编译器还通过帮助LLM理解项目使用的特定版本依赖项的特性来改善依赖管理。(不过,这是LLM的一个持续弱点,它们会使用来自更新API版本的文档或仅适用于旧版本依赖项的假设。我们计划用sketch.dev解决这个问题)
- 测试失败有助于发现生成代码中的错误,并对LLM为新代码编写测试产生积极的强化效应。
- LLM可以处理比上下文窗口更大的代码库,因为它们选择性地选择要读取的代码库的哪些部分。
- 智能体可以自己尝试最终产品:运行代码,从浏览器截取页面的屏幕截图,将其反馈给模型,并根据端到端渲染不断调整CSS。当事情真的出错时,读取服务器日志,找到panic,修复它并添加测试。
智能体的缺点是时间。一个本来需要200个token响应的单句请求现在可以生成数万个中间token来驱动工具、一些网络搜索和多次运行项目测试套件。这需要几分钟时间,而我们一天只能健康地煮那么多杯咖啡。
今天它可能看起来也很昂贵(我上次重要的智能体驱动提交花费了我1.15美元的API积分!),但随着驱动模型的芯片继续改进,成本将迅速消失。GPU的未来进展远不如CPU受限,CPU在物理改进方面受到更大限制(编译器驱动的ILP只能走这么远)。我们仍然称LLM芯片为"图形"芯片这一事实表明,软件底层的经济机器在完全专注于LLM每瓦性能之前还有大量的重组要做。
最终,智能体花费CPU和GPU周期做中介工作,这样人类就不需要做了。任何时候我都可以将我的劳动机械化,我就能完成更多工作,所以智能体对我来说是一个巨大的进步。由于一天中的时间不足,我最终只编写了我想编写的程序的一小部分,而在智能体的支持下,我可以进一步完成我希望编写的程序列表。愿我们都能像我过去一年一样幸运。因此,我完全相信智能体值得大量的工程投资来解决它们的限制。
相对容易看到智能体产生有用工作的例子。将一个放入你的项目中,分解一个小任务并输入它,看看它做什么。让我给你两个例子。
4、示例:Github App认证
让我向你介绍一个我使用智能体在一个项目中完成大量工作的例子。我使用sketch为托管的sketch.dev实现了Github App认证的第一遍。它完成了整个过程,我点击界面发现错误时有3-4个反馈。那是一个令人惊叹的成就。傲慢的人很容易将粘合知名API的工作视为"不是真正的"编程,但根据我职业生涯的实际经验,对于我每小时可以做的真正有趣的编程,我必须做10小时或更多小时的枯燥工作,包括API、库、功能失调的编译器、构建系统或晦涩的包管理器,才能真正使那一小时有用。拥有一个工具,让我通过编写几个精心构造的句子来做30分钟的"不是真正的"编程,并在它工作时让我 vacuum 孩子的房间,保持了动力。
但现在来了工作。它实现了我想要的GitHub app认证流程。它甚至满足了我提出的严格要求:我问它是否可以避免保存每个用户的令牌,而是使用应用程序的全局私有凭据来驱动所有内容以保持数据库简单。它做到了!在这样做的过程中,它写了一些非常糟糕的代码。
首先,是它创建的巨大安全漏洞。因为它让任何授权该应用程序的人与该应用程序授权的任何存储库一起工作,即使他们没有访问权限。灾难。幸运的是,它太糟糕了,如果我们已经与其他团队成员一起测试,当我们的私有存储库开始出现在彼此的存储库列表中时,我们会很快发现它。(而且是如此明显的问题,我甚至在那之前就发现了它。)
向sketch.dev快速解释这个问题就让它修复了它,它为用户实现了授权检查并正确完成了。另一个惊人的成就,我写了一句话就得到了一个重新设计的功能提交在分支上。
下一个问题是性能。虽然新代码可以工作,但一旦有多个用户,它就会变得无法工作地慢。它通过以下方式生成用户有权访问的存储库列表:
for install := range allAppInstallations {
for r := range install.Repositories() {
if r.IsCollaborator(user) {
// 添加到可用存储库
}
}
}
这意味着每次我想向用户显示他们授权的存储库列表时,我必须为每个允许使用Sketch的GitHub组织进行API调用以获取其存储库列表,然后对每个存储库进行另一次API调用以检查此人是否是用户。这意味着API调用数量随着产品用户总数的增长而增长,这将不起作用。
事实证明,问题是我最初避免存储每个用户的GitHub令牌的天真要求。事实证明,他们没有任何有效的app-auth级别API调用来确定用户可以访问什么,唯一有效的方法是询问认证令牌具有什么访问权限,并为用户使用认证令牌。
一旦我意识到这一点,我告诉sketch回去并删除我的原始要求:保存每个用户的认证令牌并将其用于所有内容。它很快想出了高效的API调用。
讲述这个故事所花的字比我输入到Sketch中生成我想要的GitHub认证代码的总字数还要多,编写它所花的精力比捕获所有问题的代码审查还要多。我需要强调这一点,因为很容易从这样的轶事中取出一部分,并利用这些工具的限制和错误来宣布它们"无用"或"危险",而我的经验完全不是这样。我们今天拥有的显然不能取代我作为程序员,但它确实让我在一天内完成了一项我传统上需要一周才能完成的枯燥任务。而且我还在 vacuum 孩子的房间。
5、示例:围绕JSON的SQL约定
这是我的智能体需要定期做的一件事的例子,它一直在挣扎,直到我找到一种方法来帮助它(我相信这捕获了人们第一次尝试使用LLM时遇到的典型限制)。
我在Tailscale学到了一种奇怪的使用SQL的方法(来自Brad和Maisem):让每个表都是一个JSON对象。特别是,只有一个"真实"列,其余从JSON生成。所以典型的表看起来像:
CREATE TABLE IF NOT EXISTS Cookie (
Cookie TEXT NOT NULL AS (Data->>'cookie') STORED UNIQUE, -- 主键
UserID INTEGER NOT NULL AS (Data->>'user_id') STORED REFERENCES User (UserID),
Created INTEGER NOT NULL AS (unixepoch(Data->>'created')) STORED,
LastUsed INTEGER AS (unixepoch(Data->>'last_used')) CHECK (LastUsed>0),
Data JSONB NOT NULL
);
这有许多优点和缺点。它充当穷人的ORM,因为每个表都有一个与每个记录匹配的"明显"数据类型。它使添加到模式变得微不足道。你可以选择添加列,如果你喜欢,但你不必这样做。SQL列约束充当对JSON质量的良好动态检查。它大大增加了每行的存储数据量。你必须用JSON来构造所有的INSERT和UPDATE。一只脚踏在文档数据库世界中,但我仍然可以写老式的JOIN。等等。
撇开优缺点不谈(这可能是未来一篇有趣的博客文章),我们的智能体经常在这个风格上绊倒。在创建新表和列时,它有时(但不总是)遵循生成的列模式。当我们第一次添加一个不使用这种全生成列样式的表时,它变得更加困惑,似乎几乎随机地在样式之间选择。
事实证明,修复智能体的行为非常容易。在SQL模式文件的顶部,我尝试添加一个三句话的描述。关键行似乎是"每个表有一个具体的数据JSON列,所有其他列都是从它生成的",然后在不遵循此模式的表上添加注释,解释它们是规范的例外,行为显著改善。
这有点违反直觉。我对此类指令的生活经验是,工程师们大量忽视它们。它可能是广告(类似)的盲目性,它可能是保持注释最新的挑战,或者它可能是大多数注释不值得太多关注。相反,传达这种知识的行业规范是工程师不知道的令人不愉快的例行公事,写一个做错的PR,然后收到评论并告诉他们做更多工作。LLM似乎给了评论更多工作,希望会更好。
6、代码的"资产"和"债务"模型
反对LLM作为代码生成工具的论点之一是,生成代码只是代码总成本的一小部分。处理现有代码的持续工作是成本的绝大部分,论点是这样的。有些代码库这个说法显然是正确的。在用户群不断增长、添加使用程序新方式的重度使用产品中,工程师确实将大部分时间花在导航现有代码的未写明、误解的相互依赖关系上。对于工作是这样的的人来说,一台你可以与之交谈的计算机,为"在fortran中实现冒泡排序"产生可用结果,介于玩具和麻烦之间。有时试图将此与"资产"和"债务"等财务概念进行比较,但我将跳过它们,因为它们似乎从来不太合适。
这种对工程的理解对于某些项目是正确的,但对于整个工程是否正确是值得怀疑的。很少有程序能达到被重度使用和长寿的阶段。几乎一切都用户很少,或者寿命短,或者两者兼有。让我们不要从只接受维护大型现有产品工作的工程师的经验推断到整个行业。
幸运的是,我们不必回答整个编程是什么样子来判断智能体是否有价值,因为智能体甚至在现有产品的维护中也可能有用。智能体不仅仅是代码生成。它是一个LLM,有一系列工具来读取代码,并通过编辑文件来更改代码。
智能体删除代码和添加代码一样高兴。
结果是改变。是的,改变是"更多的工作",因为驱动智能体的人必须理解正在进行的更改。是的,智能体可能还没有足够的理解来更改大型产品。但更改是驱动工具的工程师的最终目标,智能体正在展示一些谨慎地编辑中等大型项目的能力。这使它们在编程行业的任何地方都可能成为有用的工具。如果智能体还不够好(这是一个很大的假设,非常值得测试),它们正在正确的轨道上,现在拥有了达到目标的所有基础知识。
一个相关但更棘手的话题是围绕更难使用的编程工具(例如,像C这样的编程语言,设施很少,构建系统复杂)的一个更安静的论点,即这些工具充当项目的守门人,阻止低质量的平庸开发。如果没有人能弄清楚如何添加依赖项,你就不能在项目上蔓延依赖项。如果你相信这样的论点,那么任何使编写代码更容易的东西:类型安全、垃圾收集、包管理和LLM驱动的智能体都会使事情变得更糟。如果你的目标是减速并避免改变,那么智能体就没有用。
7、为什么我们现在看到智能体?
与支撑LLM的transformer架构等有些神秘的想法不同,机械反馈到LLM似乎是"显而易见的"。对于我们这些思考开发工具的人来说,这是很清楚的:我已经研究了一年多了,但我们一月份的第一个版本的sketch.dev,尽管将go工具连接到LLM,但按照我们今天可以使用的标准,几乎不算作智能体。(sketch的第一个版本和当前的sketch开源项目之间的效用差异是惊人的。)反馈的效用对每个在ML领域工作的人来说也是清楚的,因为强化学习一直是该领域50年的核心租户之一。
答案是使智能体有用的关键部分工作在底层模型的训练过程中。**2023年的LLM无法驱动智能体,2025年的LLM针对它进行了优化。**模型必须稳健地调用它们被赋予的工具并很好地利用它们。我们现在才开始看到擅长这一点的前沿模型。虽然我们的目标是最终完全使用开放模型工作,但在我们的工具调用评估中,开放模型落后于前沿模型。我们相信六个月后情况会改变,但目前,有用的重复工具调用是底层模型的一个新功能。
8、下一步是什么
在一个快速发展的领域中思考下一步是具有挑战性的,因为今天大多数工程师甚至还没有使用这些工具。但我们这些构建这些工具的人需要思考它。
今天智能体的大多数使用要么在IDE中,要么在开发机器上的检出仓库中。这是一种简单的入门方式,安装一个vscode分支或命令行工具并运行它很容易。但它有两个重要的限制。
第一个主要限制是智能体需要内置很多安全措施以避免它们失控。我的一台机器上有生产凭据,我可以用来进行部署。作为运行命令的一部分,智能体会抓住这些凭据并为我的未提交更改运行我的部署脚本吗?避免这一点,如果你的智能体直接在你的真实计算机上运行,需要大量要求程序员监督工具调用并给智能体运行命令的权限。即使那样,也有危险。我可以说"全部同意"运行curl,却忘记我正在开发的在localhost上运行的Web服务器还没有任何身份验证,可以从磁盘读取任意文件。哎呀,我的生产凭据又暴露了。
第二个主要限制是要求开发者使用他们定制的手动配置的开发环境来运行智能体,这意味着我们实际上是在序列化智能体的执行。如前所述,智能体的一个主要弱点是每轮需要几分钟才能产生好结果(并且在可预见的将来可能仍然如此)。更好地利用我们时间的一种方式是让工程师同时驱动多个智能体,但这种智能体部署使这变得不切实际。
我们正在探索在sketch.dev中使用容器来解决这两个问题。默认情况下,sketch在容器中创建一个带有源代码副本的小开发环境,运行器能够从容器中提取git提交。这让你可以同时运行多个。(其他智能体也在探索这个领域,智能体总体上是一个非常繁忙的领域!)
为了给你一个这种并行性在实践中如何工作的例子,当我处理上面提到的GitHub认证时,我正要在一个群聊中抱怨我制作的表单有多丑。相反,我打开了Sketch的第二个副本,粘贴了表单的屏幕截图,并写道"这很丑,请让它不那么丑。"我回去思考认证,当我记得我输入的那个半小时后,我查看了结果并决定是的,这是一个改进。所以我要求它变基,它解决了合并冲突(我最不喜欢的编程任务之一!),我推送了更新。它远没有真正的设计师可以产生的质量,但它肯定比我测试期间创建的糟糕的无样式表单好。在过去,我会在问题跟踪器中创建一个问题,我的意思是在我的个人to-do.txt文件中记下创建一个问题,因为问题对其他程序员可见,并且需要输入比粘贴屏幕截图并说"这很丑,请修复"更连贯和建设性的东西。与智能体交谈的好处之一是,如果你只剩下一点点精神能量,你很有可能从30秒的工作中获得有价值的东西。我曾经将to-do.txt条目转变为真正问题的可能性,老实说,相当低。
所以我们在过去六个月探索智能体用户体验的结论是,我们可能终于有了"开发"容器的好用途。
9、IDE变成了什么?
我们正在花大量时间探索的一个开放问题是:在这个环境中IDE变成了什么?假设我们通过与智能体交谈开始工作。执行容器可以完全从GitHub派生,更改显示为差异并作为分支(或PR)推送。这是实际的工作流程吗?
在实践中,sketch生成的许多提交,或者我们尝试过的任何其他智能体,都需要一些人工清理。(我们的经验是,当程序员第一次使用智能体时,大多数提交需要手动干预,但练习编写提示可以减少必要的干预次数。)它可能像编辑注释或更改变量名一样简单。它可能更重要。我们如何在容器化世界中做到这一点?
到目前为止,我们有几个相当喜欢的工作流程,其他智能体还没有探索。一个是使差异视图可编辑。你可以在Sketch的差异视图的右侧输入,它最终会进入提交并为你推送。它非常适合单行编辑。
对于你想运行sed、或通过更改进行grep、或以有趣方式运行测试的那些修复,我们通过给予用户ssh访问容器取得了巨大成功。你不仅可以shell进去(我们在UI中也有一个小的Web终端),而且很容易将其转换为可以在传统IDE中直接打开的vscode:// URL,这有时正是我们想要的。
最后,我们让你在sketch.dev的差异视图上编写"代码审查"风格的注释,并将它们作为反馈发送回智能体。直接在差异行上注释可以大大减少我们必须输入的内容(并且从我们在代码审查过程中的长期实践中非常熟悉)。
总的来说,我们相信容器对编程是有用和必要的。这个想法已经存在很长时间了,但我个人从未想在容器中开始编程。但清理智能体为我编写的容器中的差异要有趣得多。
10、最后说明
学习和实验LLM衍生技术的过程是谦逊的练习。通常我喜欢在编程艺术改变时学习新事物:处理切换到多核编程,当SSD取代HDD并降低寻道延迟时重新思考软件设计,当一切突然可以在同一个网络上访问时,行业中的这些转变是处理的乐趣。(不要与最新的JavaScript框架、最新的云提供商服务或最新的集群编排软件等无用的苦工混淆。)这些挑战影响了我的程序工作方式:算法、语言、库等的选择。但LLM,更具体地说是智能体,以一种新的令人困惑的方式影响编写程序的过程。关于我工作方式的每一个基本假设都必须受到质疑,它波及我积累的所有经验。有些日子感觉如果我什么都不知道编程并从头开始会更好。而且它仍在改变。
今天这一切的工作方式与六个月前非常不同,我不相信我们已经处于稳定点。我相信围绕团队互动的许多规范也将改变。例如,整个行业采用的半心半意的代码审查的大多broken过程不再解决它之前勉强解决的问题。它需要被重新发明。"IDE",从未像它声称的那样集成,需要被撕毁并重新 purpose。行业现在似乎意识到了这一点,但还没有采取智能体优先的方法。有很多事情要做,我怀疑六个月后事情会再次非常不同。好奇心和谦逊会让我们度过它,但甚至比平时更建议远离人们围绕这项技术 circular 讨论的互联网论坛。那是智能体的工作。
原文链接:How I program with Agents
汇智网翻译整理,转载请标明出处