学网站建设的专业叫什么,wordpress提问模块,产品创新设计案例,做个app好还是做网站好原文#xff1a;David Crawshaw - 2025.06.08
这是我持续自学如何将编程经验应用到计算机能“对话”的世界中的第二部分。第一部分#xff0c;《我是如何用 LLM 编程的》#xff0c;探讨了如何将 LLM 融入我们现有的工具#xff08;基本上就是自动补全#xff09;#x…原文David Crawshaw - 2025.06.08
这是我持续自学如何将编程经验应用到计算机能“对话”的世界中的第二部分。第一部分《我是如何用 LLM 编程的》探讨了如何将 LLM 融入我们现有的工具基本上就是自动补全以及如何通过精心设计的提示prompting替代传统的网页搜索。现在我想谈谈更具挑战性但也更有回报的实践使用 Agent智能代理进行编程。
定义 Agent
有必要先定义一下在 LLM 语境下“Agent”这个词的含义。在 Agent 真正成为有用的构建工具之前围绕这个非常通用的词进行的“AI”炒作周期就已经开始了。因此这个词本身被包裹在不少营销噱头和神秘色彩之中需要我们拨开迷雾才能发现其价值所在。对于有工程背景的人来说现在有一个直白的定义一个 Agent 就是 9 行代码。也就是说Agent 是一个包含 LLM 调用的循环for loop。这个 LLM 可以在没有人类介入的情况下执行命令并查看其输出结果。 就是这样。就像所有简单的事物一样你的本能反应很可能是“那又怎样”它不过是一个循环。但其结果相比原始大语言模型的能力而言却带来了惊人的提升。
白板编程
想象你站在白板前用马克笔写一个 C 语言函数用于测试 UTF-8 字符串是否有效。这确实发生在我身上是标准的面试技巧。那次面试改变了我的职业生涯关系重大你在这个任务上的表现取决于你作为程序员的经验以及你掩饰无法使用外部资源的能力。你需要记住 UTF-8 的编码规则。你需要避免将 C 语言的语法与你职业生涯中使用过的其他类 C 编程语言混淆是“名后型”还是“型后名”。在日常工作中如果犯了错误你会得到编译器的反馈你可以查阅 UTF-8 的规范最重要的是你可以编写程序并插入一些printf语句来找出问题所在。
要求一个没有 Agent 的 LLM 写代码就相当于要求你在白板上写代码。这是一次挖掘半遗忘记忆的练习在一个极其低效的基质上运行解析器试图避免凭空臆造出实际上有用的编程接口。LLM 能够凭空生成程序这本身是一项令人惊叹的技术成就我很庆幸自己能活着看到它但同样不足为奇的是给虚拟白板挂上一块 GPU并不能完成多少有用的编程工作。
但是如果我们给 LLM 的不只是一个虚拟白板呢如果它能调用编译器查看编译错误并在我们看到结果之前有机会修复它们呢如果它能使用grep和cat来读取项目中现有的文件呢如果它能修改多个现有文件包括单元测试并反复运行测试呢Agent 就是基于反馈驱动的 LLM。
Agent 是能获得环境反馈的 LLM
正如人类在充满反馈的环境中茁壮成长一样当 LLM 被赋予一套程序员非常熟悉的、出奇精简的核心工具集时它们就能从漂亮的演示转变为有用的程序员
bash(cmd) (执行 Bash 命令)patch(hunks) (应用补丁)todo(tasks) (管理待办事项)web_nav(url), web_eval(script), web_logs(), web_screenshot(), etc (网页导航、执行脚本、查看日志、截图等)keyword_search(keywords) (关键词搜索)codereview() (代码审查)
Agent 非常擅长使用 Bash 工具如find, cat, grep -R来浏览代码库——这和我们这些在 IDE 出现之前就编程的人惯用的方式一样。我们预先指导它向 git 提交更改它也确实会使用 Bash 工具运行git add, git commit等命令。
与无法使用这些工具的 LLM 生成代码相比其结果大不相同。值得注意的是
API 使用大幅改进因为 Agent 可以搜索文档并通过curl将文档拉入其上下文窗口。编译器反馈减少了语法错误和臆造的接口。在完整的开发环境中编译器还能改善依赖管理帮助 LLM 理解项目所使用的特定依赖版本的特性。虽然这仍然是 LLM 的一个持续弱点它们可能会使用更新 API 版本的文档或者做出仅适用于旧版本依赖的假设。我们计划通过 sketch.dev 来解决这个问题。测试失败有助于发现生成代码中的错误并能产生积极强化的效果促使 LLM 为新代码编写测试。LLM 可以处理超出上下文窗口容量的大型代码库因为它们会选择性读取代码库的特定部分。Agent 可以亲自尝试最终产品运行代码从浏览器截取页面将其反馈给模型并根据端到端的渲染效果不断调整 CSS。当情况变得非常糟糕时读取服务器日志找到崩溃点修复它并添加测试。
Agent 的缺点是时间。一个原本只需生成 200 个 token 响应的简单句子请求现在可能会产生数万个驱动工具的中间 token包括一些网页搜索和项目测试套件的多次运行。这需要几分钟时间而我们一天能健康地冲几杯咖啡也是有限的。
目前它可能看起来成本较高我上一次由 Agent 驱动的重要提交花了我 1.15 美元的 API 额度但随着驱动模型的芯片不断改进成本将迅速消失。GPU 未来的进步空间远大于 CPUCPU 在物理层面的改进空间要受限得多编译器驱动的指令级并行ILP作用有限。我们至今仍称 LLM 芯片为“图形”芯片这表明软件底层的经济机器在完全聚焦于 LLM 的能效比之前还有大量的重组工作要做。
最终Agent 花费 CPU 和 GPU 周期来完成中间工作从而解放人类。任何时候只要我能将劳动机械化我就能完成更多工作因此 Agent 对我来说是一个巨大的进步。由于一天中的时间有限我最终只能编写我想写程序中极小的一部分。有了 Agent 的支持我能在愿望清单上更进一步。愿大家都像我过去一年这样幸运。因此我深信 Agent 值得投入大量工程精力来解决其局限性。
相对容易看到 Agent 产生有用工作的例子。在你的项目中放一个 Agent拆分一个小任务输入进去看看它会做什么。让我给你举两个例子。
示例 1GitHub App 认证
让我通过一个例子说明如何使用 Agent 在项目中完成大量工作。我使用 sketch.dev 为托管服务实现了 GitHub App 认证的第一版。整个过程我只在点击界面发现错误时给了 3-4 次反馈。这真是了不起的成就。傲慢者很容易将粘合知名 API 的工作贬低为“不是真正的”编程但在我职业生涯的实际经验中每做一小时真正有趣的编程我就不得不花 10 小时或更多的时间在 API、库、有问题的编译器、构建系统或晦涩的包管理器上进行枯燥的工作才能使那一小时变得有用。拥有一个工具让我只需写几句精心构思的句子就能完成 30 分钟的“非真正”编程并让我在它工作时能去打扫孩子的房间这保持了工作势头。
但现在问题来了。它实现了我想要的 GitHub App 认证流程。它甚至满足了我提出的严格需求我问它能否避免为每个用户保存令牌而是使用应用的全局私钥来驱动一切以简化数据库。它做到了但在实现过程中它写了一些非常糟糕的代码。
首先是它造成的巨大安全漏洞。因为它允许任何授权了该应用的用户操作任何授权给该应用的仓库即使他们无权访问该仓库。灾难幸运的是这个问题如此严重如果我们在团队中进行测试当彼此的私有仓库开始出现在对方的仓库列表中时我们很快就会发现它。而且问题如此明显我甚至在那之前就发现了。 向 sketch.dev 简单解释了一下问题它修复了它实现了用户授权检查并做对了。又一个惊人的成就我写了一个句子就得到了一个功能正常、经过修改的提交分支。
下一个问题是性能。虽然新代码能工作但一旦有多个用户它将慢得无法使用。它通过以下方式生成了用户有权访问的仓库列表
for install : range allAppInstallations {for r : range install.Repositories() {if r.IsCollaborator(user) {// 添加到可用仓库}}
}这意味着每次我想向用户显示他们已授权的仓库列表时我必须为每个允许使用 Sketch 的 GitHub 组织进行一次 API 调用来获取其仓库列表然后对每一个仓库再进行一次 API 调用来检查此人是否是用户。这意味着 API 调用次数会随着产品用户总数的增长而增长这根本行不通。
事实证明问题出在我最初那个天真的要求上避免存储来自 GitHub 的每个用户令牌。结果发现GitHub 在 App 认证层面没有任何高效的 API 调用来确定用户能访问什么。唯一有效的方法是询问认证令牌拥有什么权限并使用该用户的认证令牌。
意识到这点后我告诉 sketch 回去移除我最初的要求保存每个用户的认证令牌并用它们处理一切。它很快提出了高效的 API 调用方案。
讲述这个故事所用的字数比我输入到 Sketch 生成所需 GitHub 认证代码的总字数还多而写作所花的心思也比发现所有问题的代码审查还多。我需要强调这一点因为很容易抓住这类轶事的某个片段用这些工具的局限性和错误来宣称它们“无用”或“危险”而我的经验完全不是这样。我们今天拥有的工具显然还不能取代我作为程序员的角色但它确实让我在一天内完成了一项传统上可能需要一周才能艰难完成的枯燥任务。而且我还顺便打扫了孩子的房间。
示例 2围绕 JSON 的 SQL 约定
这里有一个例子说明我的 Agent 需要经常做某件事但在找到帮助它的方法之前它一直很吃力我相信这抓住了人们初次尝试使用 LLM 时遇到的典型限制。
我在 Tailscale从 Brad 和 Maisem 那里学到了一种使用 SQL 的奇特方式让每个表都是一个 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 (LastUsed0),Data JSONB NOT NULL
);这有很多优点也有很多缺点。它充当了一种简陋的 ORM因为每个表都有一个“显而易见”的、与每条记录匹配的数据类型。它使得添加模式变得微不足道。你可以选择ADD COLUMN但并非必须。SQL 列约束对 JSON 的质量起到了良好的动态检查作用。它大大增加了每行存储的数据量。你必须围绕 JSON 来构造所有的INSERT和UPDATE语句。算是半只脚踏入了文档数据库的世界但我仍然可以写一个老式的JOIN。等等。
撇开优缺点不谈这可以成为未来一篇有趣的博客文章我们的 Agent 经常在这种风格上栽跟头。在创建新表和列时它有时但并非总是会遵循生成列的模式。当我们第一次添加了一个不使用这种全生成列风格的表时它变得更加困惑似乎几乎是在不同风格之间随机选择。
结果修复 Agent 的行为出奇地容易。我在 SQL 模式文件的顶部尝试添加了一个三句话的描述。关键似乎是“每个表只有一个具体的 Data JSON 列所有其他列都由此生成”这句然后对那些不遵循此模式的表添加注释说明它们是例外情况之后行为就显著改善了。
这有点反直觉。我对此类指令的生活经验是工程师们非常不重视它们。可能是“广告盲区”ad blindness可能是保持注释更新的挑战也可能是因为大多数注释不值得太多关注……相反行业里传递这类知识的常规做法是让不了解情况的工程师写一个错误方式的 PR然后收到评论并被要求做更多工作。LLM 似乎让注释承担了更多工作希望这是好事。
代码的“资产”与“债务”模型
反对将 LLM 作为代码生成工具的一个论点是生成代码只占代码总成本的很小一部分。该论点认为处理现有代码的持续工作才是成本的大头。对于某些代码库来说这显然是正确的。在用户基础不断增长、不断添加新使用方式的成熟产品中工程师确实将大部分时间花在理清现有代码中那些未被记录、被误解的相互依赖关系上。对于从事此类工作的人来说一个能对话、能对“用 fortran 实现冒泡排序”给出可用结果的计算机其地位介于玩具和麻烦之间。有时人们会尝试将其与“资产”和“债务”等金融概念进行比较但我将跳过这些因为它们似乎总是不太贴切。
这种对工程的理解对某些项目是正确的是否适用于整个工程领域是值得怀疑的。很少有程序能达到被广泛使用且长期存在的程度。几乎所有东西都用户稀少或者生命周期短暂或者两者兼具。我们不要只从那些只维护大型现有产品的工程师的经验来推断整个行业。
幸运的是我们不需要回答整个编程领域是什么样子才能判断 Agent 是否有价值因为即使在维护现有产品方面Agent 也可能有用。Agent 不仅仅是代码生成。它是一个配备了一系列工具的 LLM能够读取代码并通过编辑文件来修改代码。
结果是改变。是的这种改变是“更多的工作”因为驱动 Agent 的人必须理解所做的更改。是的Agent 目前可能还没有足够的能力去修改大型产品。但改变正是驱动该工具的工程师的终极目标而 Agent 正在展现出能够谨慎编辑中等规模项目的能力。这使它们成为整个编程行业中潜在有用的工具。如果 Agent 目前还不够好这是个很大的“如果”非常值得测试它们也正走在正确的轨道上并且现在已经具备了达到目标的所有基本要素。
一个相关但更棘手的话题是围绕更难以使用的编程工具例如设施简陋、构建系统复杂的 C 语言有一个比较隐晦的论点这些工具充当了项目的守门员阻止了低质量、平庸的开发。如果没人知道如何添加依赖项项目就不可能有无节制的依赖。如果你相信这样的论点那么任何使编写代码更容易的东西类型安全、垃圾回收、包管理以及 LLM 驱动的 Agent都会让事情变得更糟。如果你的目标是减速和避免改变那么 Agent 就没有用处。
为什么我们现在才看到 Agent
与支撑 LLM 的 Transformer 架构这种有些神秘的概念不同给 LLM 引入机械反馈似乎“显而易见”。这对于我们这些思考开发工具的人来说很清楚我在这上面已经工作了一年多但我们在今年一月发布的 sketch.dev 的第一个版本尽管将 Go 工具链接入了 LLM按今天的标准几乎算不上是 Agent。最初版本的 sketch 与当前开源的sketch项目在实用性上的差异令人惊讶。反馈的效用对于在 ML 领域工作的每个人来说也很清楚因为强化学习作为该领域的核心原则之一已有 50 年历史。
答案是使 Agent 有用的关键部分工作在于底层模型的训练过程。2023 年的 LLM 无法驱动 Agent而 2025 年的 LLM 已为此优化。 模型必须能够稳健地调用所赋予的工具并有效利用它们。我们现在才开始看到擅长此道的前沿模型frontier models。虽然我们的目标是最终完全使用开源模型工作但在我们的工具调用评估中开源模型目前落后于前沿模型。我们相信六个月后情况会改变但就目前而言有效的重复工具调用对于底层模型来说是一项新特性。
下一步是什么
在一个发展迅速的领域思考下一步是充满挑战的更何况今天大多数工程师甚至还没开始使用这些工具。但我们这些构建这些工具的人需要思考它。
目前 Agent 的使用大多在 IDE 中或者在开发机上的代码库中。这是入门的简单方式安装一个 vscode 分支或命令行工具并运行它很容易。但它有两个显著的限制。
第一个主要限制是Agent 需要内置大量安全防护措施以避免它们失控。我的一台机器上藏着生产环境的凭据我可以用它来做部署。作为运行命令的一部分Agent 会不会抓取这些凭据并运行我的部署脚本去提交它未提交的更改如果你的 Agent 直接在真实的电脑上运行要避免这种情况就需要程序员大量地照看工具调用并给 Agent 运行命令的权限。即使这样危险依然存在。我可以对运行curl说“全部同意”却忘记了我正在本地主机localhost上开发的网络服务器还没有任何身份验证并且可以读取磁盘上的任意文件。糟糕我的生产凭据又暴露了。
第二个主要限制是要求开发者使用他们自己定制配置的手动开发环境来运行 Agent实际上意味着我们是在串行执行Agent 的运行。如前所述Agent 的一个主要弱点是每轮turn生成良好结果需要几分钟时间并且在可预见的未来很可能仍会如此。更好地利用我们时间的一种方式是让一个工程师同时驱动多个 Agent但这种 Agent 部署方式使得这变得不切实际。
我们正在sketch.dev探索使用**容器containers**来解决这两个问题。默认情况下sketch 会在容器中创建一个小的开发环境里面包含一份源代码副本运行器runner有能力从容器中提取 git 提交。这让你可以同时运行多个 Agent。其他 Agent 也在探索这个领域总体来说 Agent 是一个非常活跃的领域
给你举个这种并行性在实践中如何运作的例子当我在处理上面提到的 GitHub 认证时我正准备在一个群聊里抱怨我做的一个表单有多丑。相反我打开了第二个 Sketch 窗口粘贴了表单的截图并写下“这太丑了请让它好看点。”然后我回去思考认证问题。半小时后想起来时我查看了结果认为确实有改进。于是我叫它做 rebase 代码它解决了合并冲突这是我最不喜欢的编程任务之一然后我推送了更新。它的质量远不及真正的设计师但肯定比我测试时创建的丑陋无样式表单好多了。在过去我会在问题跟踪器上创建一个 issue——也就是说我会在我的个人待办事项.txt 文件里记一笔要创建 issue因为 issue 对其他程序员可见需要我打出比粘贴截图并说“这太丑了请修复”更连贯、更有建设性的内容。与 Agent 对话的一个巨大好处是即使你只剩一点点脑力也有很大机会从 30 秒的工作中获得一些有价值的东西。老实说我过去把待办事项.txt 条目变成真实 issue 的可能性相当低。
因此我们过去六个月探索 Agent 用户体验UX的收获是我们可能终于为“开发”容器找到了一个好用途。
IDE 会变成什么
一个我们投入大量时间探索的开放问题是在这种环境下IDE 会变成什么假设我们通过与 Agent 对话开始工作。执行容器可以完全从 GitHub 派生出来更改显示为差异diff并以分支或 PR形式推送。这就是实际的工作流程吗
在实践中由 sketch 或我们尝试过的任何其他 Agent 生成的提交许多都需要之后进行一些人工清理。我们的经验是程序员初次使用 Agent 时大多数提交都需要手动干预但练习写提示prompt可以减少必要的干预次数。可能简单到编辑一个注释或改变一个变量名也可能更复杂。我们如何让这在一个容器化的世界里运作
到目前为止我们有几个非常喜欢的工作流其他 Agent 尚未探索。一个是让差异视图可编辑。你可以在 Sketch 的差异视图右侧直接输入修改会进入提交并为你推送。这对单行编辑非常棒。
对于那些你想运行sed、grep更改内容或者以有趣方式运行测试的修复fixup工作我们通过给用户 SSH 访问容器的权限取得了巨大成功。你不仅能 shell 进去我们的 UI 里也有一个小型 Web 终端还能轻松将其变成一个vscode:// URL直接在传统 IDE 中打开这有时正是我们想要的。
最后我们让你在 sketch.dev 的差异视图上写“代码审查”风格的评论并将其作为反馈发回给 Agent。直接在差异的某一行上评论可以大大减少我们需要输入的内容并且这在我们长期的代码审查实践中非常熟悉。
总的来说我们相信容器对编程是有用且必要的。这个想法由来已久但我个人以前从未想过要在容器里开始编程。但在容器中清理一个 Agent 为我写好的差异这要有趣得多。
最后的思考
学习和试验 LLM 衍生技术的过程是一次谦逊的练习。总的来说当编程的艺术发生变化时我喜欢学习新事物应对向多核编程的转变当 SSD 取代 HDD 并消除了寻道延迟时重新思考软件设计当一切突然可以通过同一个互连网络访问时——这些行业转变是令人愉快的挑战。不要与无用的表面功夫混淆比如最新的 JavaScript 框架、最新的云服务商服务或最新的集群编排软件。这类挑战影响了我程序的工作方式算法、语言、库等的选择。但 LLM更具体地说是 Agent以一种新的、令人困惑的方式影响了编写程序的过程。关于我如何工作的每一个基本假设都必须受到质疑这波及了我积累的所有经验。有些时候感觉如果我完全不懂编程从零开始反而会更好。而且它仍在变化。
今天这一切的运作方式与六个月前大不相同我不相信我们已经达到了一个稳定的状态。我相信围绕团队互动的许多规范也将发生变化。例如那个在整个行业被采用、勉强解决问题但基本已失效的、半心半意的代码审查流程如今连它过去勉强解决的问题也解决不了了。它需要被重新发明。“IDE”从未如其声称的那样“集成”它需要被拆解并重新定位。行业现在似乎意识到了这一点但尚未采取“Agent 优先”agent-first的方法。还有很多事情要做我怀疑六个月后情况又会大不相同。好奇心和谦逊将带领我们度过难关但比以往任何时候都更要建议远离那些人们围绕这项技术无休止空谈的网络论坛。那是 Agent 该干的活。