你正在走向 AI 倦怠吗?害怕被一个编程技能寥寥、对质量毫无追求、却拥有一个巨大 Claude 账户的人抢走工作?对项目中的代码质量感到失望,或者更糟——对“你自己”的代码感到失望?这篇文章就是为你写的。
关于大型科技公司运营的前沿 LLM,存在着重大且正当的伦理担忧,这些已被充分讨论,我了解并同意,本文不涉及这些。请不要把我误认为是一个支持 LLM 的技术兄弟。
另外:由于之前有人误以为我的文字是 LLM 生成的,我在此说明,本文是 100% 人类撰写,没有任何 AI 辅助。
伟大机器中的灵魂
有一本 Sean Mcmullen 写的同名好书,我十几岁时很喜欢读,乍看之下它描述的是与我们处境相反的情况:一台大型计算机,其各个组件是人,他们协同工作形成一个计算单元。另一方面,LLM 本身运行在真实的计算机上,并假装是(超级)人类。但再看一眼,故事与现实其实相距不远:我们在软件生产过程中的角色正慢慢从行动者降格为机器中的齿轮。规格驱动的反乌托邦就是:我们只是被下发一些规格,将其硬塞进 LLM,然后在 token 用完时哭泣,因为一位技术封建领主决定少发一些。
作为一名 Haskell 程序员,我享受编写 Haskell。是的,我非常喜欢我们在工作中打造的产品,我喜欢你能用我的开源库做的事情,但我真正享受的是用这门语言表达思想的过程本身。我假设你们大多数人也是如此,并且假设在许多其他语言中并非如此,这在一定程度上解释了为什么不同编程语言的爱好者对 LLM 辅助的未来是光明还是黑暗持有不同看法。
当我生成代码时,很多这种享受就面临风险。所以,也许不要这么做。我想继续编写(至少是令人愉快的部分)Haskell 程序,而不必阅读和审查(太多)生成的代码。同时,我想把这些 token 用在一些有益的地方,而不是慢慢烧掉我的大脑。
我想向你展示一种方法,让你继续享受编程,同时借助 LLM 适度提高生产力,而不是看起来生产力大增却失去所有乐趣。
如果你只想完全戒断 LLM,那也很好,你已经知道自己在做什么。但可能有些原因让你不想这么做,例如你确实需要带来一些真正的生产力提升,或者你不想在团队、公司、行业其他部分都迈向重度采用 LLM 时被落下。
继续写代码
如果你想继续拥有你的代码库,你需要继续写一些代码。如果你让它全部生成,它就会变成一片 LLM 荒地,只有你的编码代理才能在其中繁荣。所以你应该继续写代码,否则你的代码库最终会丢失。
继续写代码的另一个原因是保持自己是一名优秀的程序员。技能会因缺乏练习而丢失,而这里的风险尤其高,因为放弃编码工作并将其交给智能体的门槛真的很低。仅仅几周不写代码、把所有事情都交给智能体之后,你就会发现自己很难再回到亲自编码的状态。
LLM 在生成优秀的、人类可读的代码方面,远不如宣传的那样好。它们在生成自己后续会独家处理的代码方面还算可以。但我确信你已经体验过那种绝望:看着一个完全由 AI 生成的文件,里面某处必定藏着一个 bug,而你作为人类却觉得自己找不到它,因为整个代码景观对你来说太陌生了。
那么,如果不让智能体来做编码,又该如何获得那些生产力提升呢?让它们去做几乎其他所有事情。尤其是那些对你来说烦人的无聊工作。理想情况下,是那些不太难做对、且容易检查的任务。
规划
自计算机诞生之初,它们就一直被用作记账工具。以这种方式使用 LLM。除此之外,它还是一个你可以用自然语言而非正式接口来操作的记账工具。将领域专家之间的大量书面对话转化为可执行的待办事项。测试某些东西,写下测试结果,让它整理成一个如何修复缺陷的计划。
使用待办工具之类的工具,或者更好的做法是使用带 frontmatter 的 markdown 文件,以便正确跟踪规划事项。LLM 可以拥有巨大的上下文,但如果上下文太满,它仍然可能悄无声息地丢失信息。
但不要让它做任何关键决策。让它来问你。 如果你不理解问题,那是 LLM 的错,因为它没有给你相关的上下文(或者你可能已经精疲力竭,需要休息一下)。如果你一次又一次地回到同样的问题上,那就从屏幕前退一步,自己想一想,等你对想要什么有了清晰的图景再回来。
研究
当你把研究任务交给一个智能体时,很容易忍不住去看它查询和“思考”,或者去启动另一个项目中的其他智能体,或者去泡杯咖啡。这三者中,泡咖啡是最好的选择。还有一个更好的选择:用你熟悉的老式搜索引擎并行地自己研究。至少大致了解智能体将会知道的一切。
不要只是让它研究某些东西,就把它的结果当作事实,并以此为基础做规划。这会导致令人尴尬的技术债。
让智能体做研究的目的,不是让它把所有相关知识呈现给你,或者替你做出比你更好的决策。目的是你不需要对它说“让我帮你谷歌一下”。 你应该像智能体一样理解你所建模的领域,理想情况下甚至更好。
让你的智能体把研究结果写在某个地方,并附上所用资源的链接。当它稍后回来向你提出一个奇怪的方案时,问它研究结果怎么说、哪个资源这么说的。有 50% 的概率它会发现自己的错误。另外 50% 的情况下,读一读它,现在你就有能力自己做出好的决策了。
你才是编码者
这才是改变游戏规则的关键。
典型的编程辅助工具会引诱你进入“先规划,再让智能体编码”的模式。拒绝它。一起规划,但然后你来编码。 让大语言模型研究你的代码库,让它告诉你当前的待办事项,并列出所有你需要编辑的地方,让它指出潜在的陷阱,让它提醒你相关的背景研究。
我采用这种工作流程,而且它非常有趣。 我享受我的工作。有时甚至比大语言模型出现之前更享受。我总是有清晰的待办事项,我不需要担心整体规划,我可以专注,因为规划得当,我能很快完成待办事项。这就像敏捷开发,但没有那些烦人的流程。
让智能体围绕你的工作方式组织,而不是反过来。 也许你是一位经验丰富的程序员,已经知道自己最高效的工作方式。让智能体在你周围做那些你不那么喜欢的附带工作。
这种工作流程有多个优势:
*你继续做你喜欢的事。*如果你喜欢编程,就去做。*你始终清楚代码库的状态。*是否曾对自己氛围编程会话中生成的奇怪内容感到惊讶?需要重写大语言模型生成的垃圾代码?在会话中迷失了方向?用这种方式,你再也不会遇到这些问题。*你能及早发现糟糕的计划。*智能体编码者可能会一直沿着某个你很快就能识别为坏主意的方向走下去。*你持续磨练自己的技能。*显然如此。你会保持成为一名优秀的程序员,甚至不断进步。
编码智能体有用的罕见情况
理想情况下用于清理、小任务、常规工作、低风险重构。你在代码中留下了 FIXME(也许是为了节省时间和精力而故意留下的)?你写了 3 个有趣的案例,留下了 7 个类似的枯燥案例?你心里有一个模块重组计划,想要对它进行基准测试?需要用一个更好的库替换一个不再维护的库?这些都是有效的用例。从头设计复杂的东西可能就不是了。
有时你时间不够,但想要完成某些东西,也许你计划中剩余的待办事项都是明显的低风险任务。对你的监督智能体说“我离开一下,完成这个”是可以的,运气好的话,第二天早上你回来时功能就完成了。但重要的是,大部分编码工作要自己完成。
这里有一些小陷阱:
- 所以你写了那 3 个有趣的案例,然后告诉智能体完成剩下的 7 个,因为它们只是你所写案例的明显改编。
*很可能你应该进行抽象。*也许你真正在做的是应用一个透镜或某种光学?也许这实际上是一个流行类型类如
Traversable的实例
?大语言模型以不识别这一点而闻名,反而会复制大量代码。作为人类,你追求的是更好、更合理的可读代码。 - “让它指出潜在的陷阱”也是如此。是的,大语言模型可能非常擅长遍历整个调用链,并确保你收到的待办事项中列出了所有应该触及的地方。但与其依赖它来找到所有这些耦合,你应该考虑你的代码库是否组织得不好,迫使你首先使用大语言模型进行代码研究。
审查周期
你可能还记得,随着生成对抗网络的出现,AI 生成的图像突然变得逼真了许多。简而言之,你有一个模型(“生成器”)生成图像,另一个模型(“判别器”)告诉它表现如何。两者结合可以产生比单独使用生成器好得多的结果。将这个想法(其广义形式其实并不新鲜)应用于 LLM 辅助编码,你就得到了一个自动审查循环。
不要接受,甚至不要阅读任何由 LLM 生成且未经自动审查循环的产物。 这显然直接适用于代码(在那些你仍然让它生成代码的情况下),但尤其也适用于规划。当一个智能体编写代码时,工作并没有在它交付时完成,而是在审查智能体对其不再有发现时才算完成(即适合人类查看)。计划也是如此。梳理计划中的逻辑漏洞(在待办事项 2 中重构一个计划在待办事项 7 中编写的函数)并发现它们真的很累人,所以添加一个审查智能体来做这件事。
我发现让别人审查我的代码出奇地有帮助。有时它只会指出一些小问题,或者坚持要扩充 Haddock 注释,但很多时候它能发现真正的错误或遗漏,并让我专注于实际的待办事项。我建议在你自己的工作中加入审查循环,但如果你不想阅读 LLM 对你代码的审查,我完全理解。绕过这个问题的一种方法是,如果剩余的发现是次要的,就告诉它自己修复。
前沿
你完全没有充分利用前沿模型的能力。
互联网上的某个氛围编码者
是的。这是我观点的逻辑结果。
但依赖前沿模型功能是个坏主意,原因有多个。
- 环境成本。(尽管这篇文章本不打算触及这个话题。)前沿模型消耗大量能源。虽然这在某种程度上是猜测,因为 LLM 公司对其产品的工作原理并不十分透明。
- 很难对假装比你聪明得多的东西建立信任。最终,你要对你产出的代码负责。而不是你的机器。为自己的代码责怪他人是糟糕的管理者和同事对员工和同事做的事,用机器做这种事很荒谬。所以要以你能对结果负责的方式使用 LLM。只有当你让自己成为流程中不可或缺的一部分时,这才能实现。
- 最花哨的模型使用最多的 token,所以无法确定你是否能在给定会话中用它们完成任务。用较小模型能做的所有事情都是更安全的选择。
- 当你的工作流不需要前沿模型时,你就有机会最终用开放权重或开源模型替代它们,从而完全不再依赖技术封建领主。
最终,你在做一项复杂的工作。在某些方面,LLM 可能表现相同甚至稍好一些。但鉴于它们所有的缺点,这远远不足以向它们低头。要取代人类开发者的核心活动,LLM 必须比人类开发者好很多倍,使用更少的资源,在复杂的现实世界情境中更可靠,至少同样对齐,并以某种方式对其结果负责。
没有 token 了?
也许你经历过因为令牌耗尽而工作停滞的情况。这很烦人。你的工作流程现在围绕着一个你突然无法再访问的工具构建。在极端情况下,你什么都做不了。看看这个 xkcd 漫画,把“编译”想象成“没有令牌”,这是一种健康的处理方式。
如果这种情况发生几次,你可能会有被背叛的感觉。你是对的。你确实被背叛了。在特定计划下的给定会话中,你有多少令牌是一个不透明的数字,由某个大型科技封建领主随意决定。它不像你在公平市场上购买然后可以按计划使用的商品。
你需要停止将令牌耗尽视为“买得太少”,而应将其视为它本来的样子:服务中断。 你的 LLM 公司向你承诺你可以使用 LLM,但他们没有兑现。你会话中的最大令牌数可能会在你不知情或无法影响的情况下发生变化,因此你也无法真正计划。
当然,节省令牌是必要的。重新评估你的代理设置、使用情况、上下文大小、技能等,以节省令牌。但即使你这样做,即使你购买了更大的席位,也可能不够。
当你尽一切努力节省使用令牌后仍然用完时,不要将其视为“你的错”,而应将其视为你需要准备的技术故障。如果你曾经在火车或飞机上工作过很多,你知道你必须为没有互联网的情况做好准备。提前下载并缓存大型资源。总是有一些可以离线完成的工作。
对于 LLM 辅助工作,这意味着你必须让它为你需要处理的所有事情产生有形的工件。最重要的是,如果你适应了我概述的人类编码员工作流程,请确保你总是有一个计划好的待办事项列表可以处理。享受快速完成它们的过程,当你的代理回来时,告诉他们去做清理工作。
LLM 胡言乱语
LLM 生成的文本与人类文本不同。当 LLM 实际上并不真正知道它在说什么时,情况尤其糟糕。将 LLM 胡言乱语视为可能对你的心理健康有害。 不要消费太多。继续与人谈论你的代码,特别是关于更大的愿景和有趣的方面。只有在自动审查代理消除了边缘问题后,才阅读 LLM 工件。
当你头晕目眩时,休息一下。是的,即使当前没有代理在后台运行并“产生价值”。你的心理健康比工作产出更重要。
你的人类同胞
编程在很大程度上是一项社会性努力。例如,在公司或开源项目中,我们互相发送拉取请求、编写问题和提交消息。这是一种沟通形式。即使你是项目中唯一的人,你的过去自我也会为你的未来自我编写问题、提交消息和 PR。人与人之间的沟通是软件开发的支柱。
确保你始终以人为先。 不要向某人发送完全生成的 PR。我不小心这样做了,对方理所当然地受够了。我确保不再重复这个错误。
智能体会很乐意为你写出一份完整的 PR 正文,语法正确,细节一应俱全。这不是沟通,而是工具输出。 对待这类文本,就像对待基准测试数字或调试追踪一样:把它们附加到你手写的 PR 正文后面,或许放在一个 <details>
里,这样人们可以自行决定是否要读。你会发现,他们往往不会读。
我的历程
有了这一切,我比没有 LLM 辅助时更高效。很难说具体快了多少,也许是两倍?这比许多纯粹的 vibe 编程者所吹嘘的要少,但没关系。我把自己想象成一个采用一些温和有机肥料的园丁,而 vibe 编程者更像是把工业化学品倾倒在自己的田地里。我相信,就目前而言,我的方式更具可持续性。
我热爱用 Haskell 编程,以此为生是我的梦想工作。在过去一个月里,我担心这个梦想如今已成为过去。但我在这里所写的一切,正是我为了保持这项活动令人愉快而学到的东西。它似乎奏效了。
