LLM辅助开发的实用工作流程
摘要
一本实用指南,介绍如何在软件开发中使用LLMs开发工作流程,专注于委派适当任务并有效管理智能代理循环。
<p><a href="https://lobste.rs/s/bx8zu1/practical_workflow_for_llm_assisted">评论</a></p>
查看缓存全文
缓存时间: 2026/08/18 00:12
# 大语言模型辅助开发的实用工作流
来源:https://yogthos.net/posts/2026-08-17-llm-workflow.html
当大语言模型发挥作用时,这种体验如同魔法般神奇;但当它们失效时,却仿佛在与一位自信满满却胡言乱语的诡辩家争论。经过数月的日常使用,我才逐渐培养出一种直觉——能判断大语言模型在哪些场景下更可能产出有用的代码,又在哪些情况下容易失误。我也花了不少时间摸索如何限定范围、提供足够的脚手架设计,从而稳定获得有用的结果。投入时间学会有效使用这个工具后,我真切感受到了其优势:现在我能完成那些曾经不敢尝试的大型项目开发。
从某种程度上说,这个过程与传统编程恰好相反。手工编写代码时,我们倾向于逐步构建程序,每添加一个函数都带有明确意图。而大语言模型则倾向于一次性输出大量代码,开发者的工作重心便转向筛选精简,从中提取真正需要的部分。
理解智能代理循环的一个好方法,是将其视为遗传算法。智能代理框架之所以有效,是因为其中存在一个进化过程:模型首先生成大致正确的代码,经过测试后获得反馈,然后模型基于反馈进行迭代。通过这个过程,它逐渐收敛到符合测试参数的解决方案。从这个意义上说,这其实与人类编写代码的方式并无太大差异——我们几乎不可能一次性完美解决复杂问题,通常都是先写出初版方案再反复优化。区别在于大语言模型能将这个过程加速数倍。
### 应该委托什么任务
大语言模型经过海量公开代码的训练,非常擅长完成典型任务。这些任务通常是已被重复执行过无数次的、构成项目基础的模板化工作。比如给模型提供一个示例JSON响应,让它编写服务端点;或是提供一组API接口,让它基于这些接口构建用户界面——这类常见任务模型往往掌握充分,能够一次性生成合理方案。它可能比你手动操作更加细致,会自动添加测试并处理所有明显的边界情况。
大语言模型还擅长执行探索性工作。比如识别特定的调用链、追踪执行步骤以理解某个服务端点的实现细节,或是梳理需要传递的参数。这类任务模型都能轻松完成,能节省大量追踪代码库和绘制特定工作流程的时间。
这些工具也特别适合处理语言相关的语法细节。如果你清楚想要实现的逻辑(例如遍历集合并按特定参数过滤),但正使用一门生疏的编程语言,大语言模型能很好地弥合差距。它们可以轻松地用符合语言习惯的语法表达你想要的逻辑。你只需用伪代码描述算法步骤,模型就能处理剩下的实现。
例如我最近需要参与一个JavaScript项目,而我已十多年没有接触这门语言。我对现代工具链、库和最佳实践都不熟悉,也没有时间重新学习所有这些。
使用DeepSeek让我能够像使用熟练的Clojure一样高效地使用JavaScript。它完全消除了理解语法、工具链等次要问题的摩擦。如果你是特定领域的专家并清楚要解决的问题,大语言模型可以成为你能力的巨大放大器。它们不会取代你的技能,但能让你更快速地推进工作,专注于问题的核心架构。
### 何时该亲自掌控
根据我的经验,智能代理最容易失误的领域在于处理上下文和发挥创造力。你需要记住AI并不了解你项目的具体特点。例如,如果你只是让它使用Clojure方言,它可能会调用学习Clojure时接触的JVM工具链(比如`clojure`和`lein`命令),而这些在当前环境中并不存在;或者它可能假设使用树遍历解释器并试图直接运行源代码。你需要提供精确的逻辑说明,比如明确告知运行时是纯Chez Scheme环境,所有构建都通过`chez --script`执行的make命令完成,同时指定权威源文件是`host/chez/*.ss`和`jolt-core/*.clj`,而非任何JVM风格的代码。
天真实现的陷阱也同样常见。当你给智能代理一个模糊的目标时,它往往会交付表面看似正确但结构错误的方案。例如代理可能决定将字符串方法调用通过通用分派表处理,结果导致每次调用都重新推导接收者类型。正确的解决方案应该是通过类型推导在编译时证明这些值确实是字符串,从而发出直接的原生调用并完全跳过分派过程。而被要求“让字符串方法更快”的代理,很可能只会重新排列几个条件分支而保持通用路径,永远不会设计出合理的方案。同样,当你要求实现序列的`count`函数时,它很可能会遍历整个集合并为每个元素分配新节点,而实际上该集合已内置可在常数时间内获取长度的方法。要求它连接字符串时,你可能会得到重复拼接而非单次遍历的方案。这就像遇到一个邪恶的精灵,它总用最糟糕的方式解读你的查询,导致解决方案完全偏离正确方向。关键在于你必须明确说明约束条件,这反过来也迫使你彻底思考问题本身。
有效使用大语言模型的关键在于:在开始之前,你必须对要构建的内容有清晰的认识。你必须在结构层面明确表达需求。你提供的初始脚手架越完善,智能代理偏离你设计的空间就越小。由此可推论,你必须深入理解领域才能有效使用大语言模型。如果你无法评估生成的代码是否以正确方式解决问题,那么你基本上就像在赌场拉动老虎机拉杆,祈祷能获得像样的解决方案。大语言模型擅长填补空白和处理模板代码,但设计和架构工作仍需你自己完成,与以往并无不同。
以下是我发现的一些保持开发方向有效的技巧。
首先务必规划任务。在考虑委托给大语言模型之前,你必须能回答这些问题:明确的目标、计划使用的算法、以及代码应如何融入现有架构。
当头脑中形成清晰图景后,就可以与智能代理进入规划阶段。将需求告诉它,在要求模型用Markdown编写分阶段计划前,先明确说明目标。更好的做法是让它生成Mermaid.js流程图(https://mermaid.live/)。
生成图表后,你可以直观检查逻辑。如果发现某个步骤有问题,直接要求修改该特定步骤。这比单纯用文本提示与它争论要高效得多。当步骤结构清晰后,很容易识别出不理想的组成部分。审查计划,让模型将其分解为独立任务,每个任务聚焦实现特定功能。让模型创建分支并为任务提交拉取请求,此时代码审查会变得容易,因为你能明确变更的范围和解决的具体问题。
让模型研究现有工作对确定方法很有帮助。完全新颖的问题很少见,智能代理擅长查找相关论文供你参考,以判断哪种方法更可行。同样重要的是,花时间熟悉各种可选路径并自主做出选择。
我还要强调,使用大语言模型时,低耦合的清晰架构变得极为重要。模型在处理没有依赖关系的较小任务时表现最佳,因为需要考虑的上下文较少。因此如果你能将项目分解为可独立工作的小模块,就能给代理提供边界清晰的任务。这也使得审查其输出容易得多。
我发现函数式编程风格特别适用,因为它强调上下文隔离和显式传递状态。那些使大型代码库易于人工维护的设计原则,同样能帮助大语言模型更好地工作。积极控制上下文是使用大语言模型的关键策略。
需要重申的是,绝不能给AI一张白纸。务必亲自设计脚手架结构。在让代理填充内容前,要有意识地建立文件结构并决定组件划分。
即使运用所有优秀的函数式工具,我们仍然容易将两类不同的代码混在一起:一类是关心数据含义的代码,另一类是决定数据如何在组件间传输的代码。传统软件设计结构将路由逻辑隐含在函数调用图中,控制逻辑常以临时方式与内部实现细节耦合。将事物分解为独立步骤有助于控制范围。
路由逻辑应在设计中被提升为一等公民。状态机是天然适配的方案,因为它强制分离“做什么”与“怎么做”。控制流逻辑可以大部分采用声明式表达,例如用前面提到的Mermaid图表,而实现细节则存在于流程的每个步骤中,成为智能代理处理的任务。
执行这些步骤迫使代理在你的架构内工作,而非自行发明结构,这基本避免了其偏离轨道的问题。一旦你让它构建图表并完成审查,就可以基于此创建初始项目结构。
### 将测试作为契约
在使用大语言模型时,将测试视为最终需求文档很有价值。如果先将期望功能定义为测试,你就可以让代理通过测试驱动开发来实现这些测试直至通过。它通常能很好地运行测试、分析失败原因并修改自身代码以满足规范。测试就是代理工作的契约。回到遗传算法的类比,这些测试就是驱动代码进化的选择压力。
预先编写测试能确保代码在功能层面按预期工作,也是防止回归的最佳防御。没有测试时,智能代理添加新功能很可能悄悄破坏三个旧功能。为现有功能建立契约能避免这个问题。
最有价值的测试类型是关注不同组件功能的测试以及端到端集成测试。它们不需要过于细粒度,因为整个工作流运行时问题会自然浮现。对于Web应用,我强烈推荐创建故事书并使用Playwright进行自动化测试,让测试像用户一样驱动页面完成整个工作流程。
此外,由于测试不反映性能特征,创建基准测试套件来检查CPU和内存使用等性能指标很有帮助。从项目伊始就建立基准测试,在指导我的Jolt项目开发过程中提供了重要信息。
### Git是你的安全网
可以将Git想象成游戏中的快速存档功能。每次智能代理达到稳定状态(测试通过且代码符合预期)时,你都应该立即提交代码。这让你能自由让代理尝试不同实验或进行复杂重构。如果代理搞砸了方案或想法不奏效,你无需手动清理,只需回滚到最后一个正常提交,换个方向继续。
我注意到,如果智能代理第一次就未能给出大致正确的解决方案,之后也很难修正。当你指出缺陷时,代理不会退一步重新理解根本问题,而是添加临时补丁来解决你提出的具体问题,这往往导致问题倍增。如果原始方案就不合适,堆砌更多补丁只会造成永远无法正常运行的混乱。当它开始陷入死循环时,就是重新阐述问题描述并从头开始的时候了。
相关的一点是,大语言模型使得探索代码库的成本变得极低。前面提到,在让代理开始工作前,你应该理解要保留的代码所涉及的问题,这依然成立。然而,通过解决问题的过程能更好地理解它。所以当你遇到不确定该做什么或哪种方法最佳的时刻,正是尝试不同思路并观察效果的好时机。由于有版本控制,回滚到已知的稳定提交并尝试新方向非常容易。
这类事情过去需要大量精力,但现在探索的门槛已大大降低。例如当我开始开发Jolt时,最初选择Janet作为运行时。理由是Janet在表面上类似Clojure,拥有紧凑运行时且可嵌入。但我很快意识到,缺乏分代垃圾回收与持久化数据结构产生的大量短生命周期对象不匹配。经过一些研究,我最终选择了Chez Scheme。整个Janet技术验证只用了一周时间,而没有大语言模型的情况下,这可能轻易变成耗时数月的项目。同样,在Chez Scheme上验证解决方案也只用了几天时间就明确其更优。
### 智能代理框架很重要
市面上有很多智能代理框架,各自针对不同用例优化。我发现重要的是框架能满足模型期望,并提供足够灵活性来定制工作流程以适应特定项目。
最终我构建了自己的框架,这在之前的文章(https://yogthos.net/posts/2026-06-08-dirge-code.html)中有所讨论。我花了些时间观察DeepSeek和GLM等模型在智能代理循环中的行为及易错点。Dirge(https://dirge-code.github.io/)也整合了官方deepseek-harness(https://github.com/deepseek-ai/deepseek-harness)等现有工具中的成熟技巧,避免重复造轮子。此外,我使用Janet来实现...
相似文章
LLM的有效用例
本文分享了LLM在软件工程中的实际应用案例,包括通过RAG搜索客户对话、从日志中排查API故障以及内容精简。重点强调了效率提升和减少手动筛选工作。
学习构建实用的智能体系统
本文提出了设计和优化实用智能体LLM系统的原则性方法,引入了一个包含伪工具和固定工作流的框架,以提高模块化、成本效益和跨多种任务的准确性。
LLM编码时代的软件工程最佳实践
一篇讨论软件工程最佳实践如何随着LLM编码工具的整合而发展的文章,为开发者提供指导。
@bibryam: 我作为高级工程师在2026年如何使用LLMs https://seangoedecke.com/how-i-use-llms-in-2026… 最大的AI工作流变化…
一位高级工程师描述了到2026年LLM代理如何演变成编码、调试和代码库研究的可靠协作者,而人类仍负责判断和审查。
你的LLM不应该是你的编码智能体工作流
主张在编码智能体工作流中,LLM应仅用于推理,而由确定性基础设施处理队列、状态、重试和恢复,这样即使达到使用限制,流程也不会中断。