ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

手写代码时代落幕?Agent重塑软件工程的实战拆解

手写代码时代落幕?Agent重塑软件工程的实战拆解 DHH在公开场合反复强调过同一个判断手写代码的时代已经落幕Agent正在重塑软件工程。这话一出争论就没停过。有人觉得这是大佬在制造焦虑有人觉得他说的不过是既成事实——代码还是那堆代码但生产过程真的变了。作为这两年深度使用Agent跑过完整项目、也带着团队在真实业务里落地过Agent工作流的人我的感受是DHH不是在预言他是在描述已经发生的事。这篇文章我不打算复述他的原话也不会站在“AI取代程序员”这种口水战的角度去聊。我想做的是把这话拆开Agent到底是什么、它凭什么说重塑软件工程、当前主流的Agent框架怎么选、从0到1搭一个能用的开发Agent需要几步、以及真实工程里那些文档里不会写的坑该怎么排。适合两类人看一类是想搞清楚Agent不是简单“自动补全”的开发者另一类是已经在尝试用Agent但被上下文丢失、任务执行中断、权限失控折磨过的实践者。1. DHH的发言到底在说什么软件工程的分水岭1.1 事件背景与“手写代码”的准确含义DHH是个有争议的人从Ruby on Rails到Basecamp再到现在的Turbo他每一次发声都能引发一波行业争论。他说“手写代码时代落幕”时很多人理解成了“程序员要失业”但细看他的论述他指的其实是开发生产关系的转变过去工程师的核心产出是亲手敲出的代码行未来工程师的核心产出是对Agent的指令设计和对Agent产出的审查决策。这里有个容易混淆的点。DHH说的“手写代码”不是指“代码这种东西消失了”而是指“逐行从零实现逻辑”不再是人类工程师的主要时间去向。就像建筑行业蓝图依然存在但画图工具从手动制图变成了CAD再到参数化设计。结构工程师不用再一笔笔画每一根梁而是定义规则、审核模型。代码还在写的方式变了。他敢说“落幕”而不是“减弱”是因为过去两年他主导开发的Turbo和Rails相关项目里大量样板代码已经由Agent代劳他更多时间花在描述需求、审查差异、修正方向上。对于普通的软件工程师这个信号的实际意义是如果你还在把大量时间花在“把接口从A拷贝到B然后改字段名”这种体力活上那么这段路不会太长。而如果你能把精力移到“把业务需求拆清楚、把验收标准写明白、把Agent生成的代码审明白”上那么你在团队里的价值反而更高了。1.2 从1.0到2.0三组肉眼可见的变化我在团队里推行Agent工作流半年之后回头对比传统开发方式发现最根本的变化不是“代码生成速度变快”而是整个开发链路的重心发生了迁移。我总结了三个可观察的变化。第一组变化产出单位从“提交的代码行”变成了“完成的任务卡片”。以前一个功能开发工程师要花半天写代码、半天调试代码行数是可量化的产出。现在一个Agent可以在几分钟内生成成体系的代码工程师的工作变成了把任务拆分清楚、描述清楚输入输出、定义边界和约束然后等Agent产出再审查。任务卡片的清晰度直接决定了Agent产出的质量这个环节成了真正的瓶颈。第二组变化质量控制的重点从“写对”转向“审对”。过去编译器会在你写代码时拦住大量低级错误现在Agent替你写了代码编译器依然能拦住语法错误但语义层的问题——比如某个业务规则被悄悄漏掉了、某个边界条件没处理——需要人来审。这意味着审查不再是走过场而是最高权重的主职工作。我见过不少团队在初期用Agent最不顺利的团队恰恰是那些还指望“生成完就不用管了”的团队。第三组变化开发瓶颈从“知识储备”变成了“上下文管理”。传统开发中一个资深工程师的价值在于他脑子里积累了大量的领域知识和项目上下文。Agent的模型参数里塞进了海量公共知识但项目的私有上下文——历史决策、代码风格、技术债、隐含规则——依然需要人来喂给它。谁能高效地把项目上下文组织起来谁就能让Agent产生最大价值。维度软件工程1.0手写为主软件工程2.0Agent驱动核心产出亲手编写的代码任务拆解、指令设计、审查决策主要瓶颈人的编码速度与知识储备上下文的组织质量与审查效率质量控制编译期拦截代码走查语义审查定向测试运行时沙箱验证典型协作模式人写代码人来审人设计分工Agent执行人来验收1.3 为什么是这一刻技术条件已经凑齐AI辅助编程不是新概念从早期的代码补全到GitHub Copilot这条路走了很多年。但Agent这个形态能够在这两年集中爆发靠的是几股力量同时到位。首先是模型能力的跨越当前主流的模型在长上下文理解、指令跟随、多步推理上的表现已经能支撑“让模型自己规划并执行多步骤任务”。其次是工具调用标准的成型函数调用、结构化输出、JSON Schema这些机制让模型能稳定地“使用”外部工具而不是只在对话框里自说自话。第三是落地生态的成熟开源的Agent框架、沙箱运行环境、向量数据库、可观测性与日志工具把“实现一个Agent”的成本从几个月降到了几天。用生活化的类比来说过去几年我们面对的是一个“知识丰富但不爱动手”的顾问他能告诉你代码应该怎么写但不会真的帮你改。现在这个顾问不仅有了手工具调用有了记事本记忆系统还有了执行流程编排循环于是他从“建议者”变成了“执行者”。这就是Agent和过去的代码补全工具最本质的区别。2. Agent不是Copilot的升级版核心组件拆解2.1 Agent与自动补全工具的边界在哪里在实际讨论中我经常遇到一个混淆把Agent和Copilot类的工具混为一谈。这俩虽然都是大模型驱动的开发工具但工作模式完全不同。Copilot类工具是“建议者”你输入上下文它给你补全代码你决定是否接受。整个决策循环中人始终在环里每一步都需要人来判断。Agent是“执行者”你给它一个目标它自己拆解步骤、调用工具、查看结果、修正策略循环往复直到完成任务或者宣告失败。人在这个过程中的角色从“每一步都决策”变成了“设定目标和边界然后检查最终结果”。用做菜打个比方Copilot像是一个站在旁边的厨师朋友你炒菜时他给你建议“该放盐了”“火候小了”。Agent更像是你把菜谱、食材、厨房设备的使用权限都交给了一个帮厨他按你的要求独立完成整个流程你最后来品尝验收。前者是辅助后者是委托。理解了这一点就能理解为什么DHH敢说手写代码落幕——因为越来越多的工作可以“委托”了。对比维度Copilot类工具Agent工作模式建议-采纳目标-任务拆解-执行-反馈人在循环中的角色每一步都参与决策设定目标、边界与最终审查工具调用基本不调用外部工具主动调用Shell、浏览器、API、文件系统多步推理弱依赖人引导强能自主规划与调整失败处理人接管自主重试、降级或上报2.2 四个核心组件缺一不可要理解Agent为何能重塑软件工程得先拆开它看。一个能真正干活的Agent至少包含模型、工具、记忆、编排四个部分。模型是Agent的“大脑”负责推理、规划、理解指令。开发场景里模型的代码生成能力和长上下文能力直接决定了Agent的天花板。工具是Agent的“手脚”让它能碰真实系统——执行Shell命令、读写文件、调用API、操作浏览器。一个只有模型没有工具的Agent充其量是个高级对话框有了工具它才能从“输出代码建议”变成“真正修改代码并验证效果”。记忆分两层短期记忆是对话上下文让Agent记得本轮任务里做过什么长期记忆则是向量库、文件、知识库让Agent跨会话记住项目约束和历史决策。编排负责把大脑、手脚和记忆串成一个循环观察当前状态-思考下一步-决定调用哪个工具-执行-根据结果再观察如此往复。这个循环听起来简单但工程实现上有大量细节。比如你让Agent“修复这个测试失败”它不能只是嘴上说要改哪个文件而必须真的跑测试、看报错、改代码、再跑测试验证。没有编排逻辑它就做不了这个闭环。2.3 主流开发Agent框架怎么选现在市面上的Agent框架多到让人眼花缭乱。我按实际用途把它们分成几个流派。LangChain是最成熟的生产级框架组件齐全文档丰富适合做完整的Agent编排应用。AutoGPT走的是通用自主Agent路线更偏研究实验不适合直接用在生产代码库上。CrewAI主打角色扮演式多Agent协作适合模拟团队分工。MetaGPT更进一步把标准化的软件开发流程产品、架构、开发、测试固化成多Agent流水线偏研究性质。OpenAI的Swarm和AgentKit则适合深度依赖OpenAI生态的团队Swarm的设计更轻灵活度高。还有一堆公司内部的私有Agent系统没有开源但思路基本一致。选型建议很简单如果你是想学Agent原理不要一上来就套框架先手写一个最小循环如果是要在产品里稳定跑选LangChain这种成熟框架因为坑被踩得差不多了如果只是做实验和教学CrewAI和MetaGPT能让你快速看到多Agent协作的样子。3. 从0到1搭建一个能跑通的开发Agent3.1 准备模型通道与基础环境动手之前先把环境搭好。需要一个支持工具调用的大模型API国内外的各家大模型基本都兼容OpenAI接口协议建议直接用一个OpenAI兼容的接口方便切换模型。环境里需要Python 3.10以上版本最好用venv或者conda隔离环境别污染系统Python。再安装openai库如果打算用LangChain框架就装langchain和langchain-openai。切记模型的上下文长度直接影响Agent完成长任务的能力。一个工具调用循环会在每次交互时消耗大量上下文选模型时尽量不要低于32K上下文我自己实际用下来128K以上会舒服很多。另外工具调用Function Calling能力是刚需纯文本对话模型没法稳定地让Agent执行命令。3.2 写一个最小Agent循环我建议所有初学者从手写一个最小循环开始核心不超过一百行。看这个循环怎么运转import json import subprocess from openai import OpenAI client OpenAI() TOOLS [ { type: function, function: { name: run_shell, description: 在本地Shell中执行命令并返回输出结果, parameters: { type: object, properties: { command: {type: string, description: 要执行的Shell命令} }, required: [command], }, } } ] def run_shell(command: str) - str: result subprocess.run(command, shellTrue, capture_outputTrue, textTrue, timeout30) return result.stdout (\n[STDERR]\n result.stderr if result.stderr else ) def agent_loop(user_goal: str, max_iterations: int 10): messages [{role: system, content: 你是一个软件工程助手。请自主规划并执行任务使用工具验证结果。}] messages.append({role: user, content: f任务目标{user_goal}}) for i in range(max_iterations): response client.chat.completions.create( modelgpt-4o, messagesmessages, toolsTOOLS, tool_choiceauto, ) message response.choices[0].message messages.append(message) if not message.tool_calls: print(Agent完成, message.content) return message.content for tool_call in message.tool_calls: if tool_call.function.name run_shell: args json.loads(tool_call.function.arguments) print(f[第{i1}轮] 执行命令{args[command]}) output run_shell(args[command]) messages.append({ role: tool, tool_call_id: tool_call.id, content: json.dumps({output: output}, ensure_asciiFalse)[:4000], }) print(达到最大迭代次数未完成任务。) return None if __name__ __main__: agent_loop(查看当前目录结构并在当前目录下创建一个README.md文件内容简要描述该目录下的文件。)这段代码的逻辑就是那个核心循环把系统提示和用户目标放进消息队列调用模型如果模型返回的是工具调用就执行工具并把结果回填给模型模型根据结果决定下一步行动直到模型认为任务完成返回最终回答。我刻意省略了错误处理和上下文压缩但正是这个最小的骨架能让初学者一眼看清Agent的本质——它不是魔法就是一个“思考-行动-观察”的自动循环。3.3 让Agent真正干活的调优要点骨架跑通之后决定Agent能不能在生产里用的全是细节。第一个细节是系统提示词。不能只写“你是助手”要写清楚角色、任务边界、输出要求。比如我常用的写法是“你是软件工程Agent你能在沙箱目录内执行Shell命令。每次执行命令前先说明计划。不允许修改沙箱目录之外的任何文件。生成代码时必须同时给出验证步骤。”这些约束能防止Agent胡来。第二个细节是工具粒度。工具不是越多越好也不是越细越好。工具太粗比如一个“execute_any_command”灵活性高但风险大模型容易失控工具太细比如一个“read_a_file_line_by_line”模型调用成本高上下文消耗快。合理做法是提供几个中等粒度的工具读文件、写文件、执行Shell、搜代码、跑测试。每个工具的描述要写清楚参数含义和返回结构模型对描述的理解程度直接影响调用准确率。第三个细节是迭代上限。Agent经常陷入“执行-失败-再执行”的循环里不给迭代上限会烧掉大量Token。我一般设置8到15轮超过就停止并输出当前状态让人接管。第四个细节是日志与可观测性。每一轮Agent的思考、调用、返回值都要记录到结构化日志里。没有日志Agent出问题你只能抓瞎有了日志你才能复盘是模型判断错了、工具错误、还是上下文截断了。我在生产环境里会把每轮的工具调用和关键决策写入JSONL文件出了问题直接对着日志时间线排查。3.4 部署与验证清单一个Agent写完后不要直接扔到生产代码库里应该按照从易到难的顺序验证。先给它一个只读任务比如“分析项目里哪个文件最长说出它的复杂度并给出建议”看看它能不能正确读文件并给出有理有据的分析。再给一个写入任务比如“在指定的临时目录里生成一个符合PEP8规范的小工具模块并写一个unittest测试它”检验生成代码的能力。最后再给一个修改任务比如“修复一个指定的bug并运行测试验证通过”校验闭环能力。每个阶段都要检查同一个东西Agent是否理解了约束是否在执行前有计划是否在验证后给出可信结论。只要一个环节不过关就回到提示词或工具设计上去调整而不是盲目增加模型能力。4. 当Agent进入真实工程手写工作流被重塑后的样子4.1 任务拆解从“师傅带徒弟”变成“指令设计”传统开发流程里一个功能需求下来资深工程师心里会自然地把大需求拆成小任务再分配到人。Agent进入之后这个拆解能力从“团队管理能力”变成了“日常表达能力”。因为Agent不会主动理解你的业务逻辑你必须把需求写成它能执行的指令而指令的质量直接决定产出质量。我在团队里总结过一个“任务卡片五要素”模板目标这个任务要交付什么结果、约束不能碰哪些文件、遵守什么代码规范、输入哪些文件或接口可以依赖、验收标准怎么验证完成比如跑哪个测试、输出什么格式、边界超出什么范围必须停下问人。写清楚这五点并按照团队知识库里的项目说明一起投喂给Agent效果比只扔一句“帮我写个登录接口”好十倍。这个转变对工程师的影响是深远的那些善于把模糊需求变成精确描述的人在Agent时代会如鱼得水那些只会照着需求文档默默写代码的人反而会感到落差。因为“写”可以被替代“描述清楚”却很难被替代。4.2 代码审查成为工程师的主职工作Agent生成代码的速度越快审查压力就越大。一个Agent一天能生成的代码量人肉审查可能根本跟不上。如果审查跟不上质量退化就是必然。所以这里的关键不是“审得更多”而是“审得更高效”。我把审查分成三层。第一层是机器审查先让静态检查工具如ruff、ESLint和测试套件自动过一遍凡是机器能拦下的问题就不要浪费人的眼睛。第二层是结构化审查检查Agent的方案是否匹配任务卡的验收标准、是否引入了新的不必要依赖、是否处理了边界条件和错误路径。第三层才是人工深度审查只针对关键逻辑和安全敏感部分逐行确认。很多人一上来就想逐行看Agent生成的每一段代码这在中等规模项目里根本不可持续必须把火力集中在真正有风险的地方。我实际用过的一个有效流程是Agent在自己的分支里改代码、跑测试、生成提交说明工程师做code review时用diff工具看变更只检查语义正确性不纠结缩进和命名。这样审查效率至少提升了一倍。4.3 多Agent协作怎么落地单个Agent能完成的任务是有上限的复杂功能往往需要多个Agent协作。当前实践里最常用的是两种模式。流水线模式一个Agent负责生成需求分析下一个Agent基于分析写代码再下一个Agent做测试和审查上游的输出是下游的输入像工厂流水线。黑板模式多个Agent共享一个工作区比如一个代码仓库或者一个共享的“任务黑板”各自认领任务同步更新状态像一群人围着一块白板分工协作。MetaGPT就是把这两种模式固化成框架的典型它定义了产品经理、架构师、工程师、QA等多个角色Agent每个角色的输出用标准文档格式传递。真实业务里不必这么重但可以借鉴它的核心思路把角色、职责边界、交接物格式定义清楚。我自己的简化做法是一个“规划Agent”负责拆任务卡2到3个“执行Agent”并行干活一个“审查Agent”负责交叉检查。注意多Agent不是越多越好协调开销随着Agent数量非线性增长3到5个是性价比比较高的区间。4.4 团队落地路径从小步快跑到全面替代给想在企业里推行Agent工作流的团队一个建议千万不要想象一个全自动的“第二天就没有人写代码”的世界那既不现实也不安全。分成三步走。第一步低风险辅助让Agent先干那些错误成本低的活比如生成单元测试、写提交信息、整理代码注释、辅助代码审查扫描。这些工作质量只要不差即使偶尔出错影响也不大。第二步有限授权让Agent在独立分支上处理功能开发但合并前必须经过完整的人工代码审查和CI检查。这个阶段可以观察它的失败模式制定应对策略。第三步构建专属体系把团队的开发规范、项目历史决策、架构约束整理成知识库接入Agent的长期记忆再给Agent定制专属工具集比如公司内部的服务SDK、数据库访问工具此时Agent才真正变成了“懂这个项目的数字工程师”而不再是一个通用的“代码生成器”。5. 实测中常见问题与排查实录5.1 Agent执行被中断的常见原因我在使用过程中最常碰到的问题就是Agent跑到一半就停。日志里经常出现类似“execution terminated due to error”的报错。根据我的排查经验这背后往往是三类原因。第一类是工具返回错误但Agent没有正确处理。比如它执行了一个不存在的文件路径工具返回非零退出码Agent把它当成“任务已经做完了”于是输出一个虚假的成功结论。解决方法是把工具返回结果里加上明确的错误标记并且在系统提示里写下“如果命令执行失败优先猜测原因并修复而不是直接终止”。第二类是上下文超过模型窗口被截断导致Agent丢失了早期目标开始做无关的事情。解决方法是在工具结果回填时做截断和摘要不要一股脑把巨大输出塞进上下文。第三类是对手配置了迭代上限Agent还在合理探索时就因为达到上限被强制终止。这种情况需要区分如果Agent的每一步都有效就放宽上限如果它反复尝试相同方案那属于“死循环”上限应该越紧越好。我在本地开发Agent工具时还遇到过一个常见的坑Shell命令的编码问题。Windows下执行中文路径或者输出中文内容时编码不统一容易导致解析错误最好在工具实现里统一用UTF-8编码并设置PYTHONIOENCODING环境变量。5.2 上下文丢失与记忆错乱的处理长任务里上下文丢失是最隐蔽的敌人。Agent前半段规划得挺好后半段突然忘了需求里的某个关键约束生成出完全偏离方向的东西。根因通常是对话消息过长早期的关键信息被截断或稀释了。对策我个人用下来最有效的是“上下文外置”。不要指望模型记住所有事把关键信息写到文件里。具体做法任务开始时让Agent把拆解后的计划与约束写到/workspace/plan.md每完成一个重要步骤让它更新这个文件后续每轮对话开始前让Agent先读一遍这个文件再决策。这样即使对话上下文丢了文件还在Agent还能找回方向。另一个温柔但有效的办法是“摘要接力”每执行几轮让模型把到目前为止的进度、决定、遗留问题总结成一个结构化摘要替换掉冗长的历史消息。这个技巧在处理超长任务时几乎是必须的。5.3 权限边界与安全上的硬性教训安全这个问题我踩过的坑最大。早期为了让Agent干活痛快我给了它一个几乎无限制的Shell工具结果它自作主张往错误的目录里写了一大堆文件还有一个测试环境数据库的修改因为权限太宽险酿大祸。从那以后我定了几条硬规矩至今没再出过大问题。第一条是目录隔离Agent的沙箱目录必须和真实项目目录分离每次任务开始的初始工作目录固定且只读挂载项目副本Agent的写入只能发生在临时工作区。通过diff的方式让人类审查后再合入主项目。第二条是命令白名单Shell工具收到的命令必须经过一个校验器只允许白名单内的命令比如git、python、pytest、ls、cat危险命令如rm -rf、磁盘管理类、网络监听类强制拦截。在需要的时候才逐步扩大权限而且扩大操作必须人工审批。第三条是敏感信息防护系统提示和工具里都不允许访问带密钥的文件测试环境变量也要屏蔽真实凭证。有同事遇到过Agent在日志里打印出API密钥的情况所以必须在工具返回内容里对敏感字段做脱敏。可能有人觉得这些限制让Agent变“笨”了干活不痛快。我的经验恰恰相反权限边界清晰之后Agent的错误率明显下降因为很多“半个项目被夷平”的惨剧发生正是因为它同时拥有了理解力和破坏力而给它套上约束相当于给一个能干的大力士戴上手套——效率不变但不会划伤你手上的宝贝。5.4 Agent幻觉与不诚实行为幻觉是另一个高频问题而且最危险的是“看起来很诚实的幻觉”。Agent在无法获得真实结果时会编造一个看起来合理的输出。比如它说“已修复”但其实根本没跑测试它说“检查了三个文件”但其实只看了目录列表。对策依然是机制性的。第一所有工具返回都带上原始输出的哈希摘要Agent生成结论时强制引用工具输出如果结论与工具输出矛盾审查时一眼就能发现。第二对Agent的关键声称要求提供证据链比如“运行了测试并全部通过”就要要求在回复中带上测试命令和关键输出。第三不信任“完成”声明用CI和测试来做最终裁决。一句话Agent的“自我报告”是参考信息不是验收标准。我在这个原则下几乎没有再被Agent的“假成功”骗到过。我自己的体会写到最后我想说一句真心话。这一年多我亲手把项目里的很多体力活交给了Agent说完全没有“职业危机感”是假的。但真正把工作流跑顺之后我发现那份危机感变成了另外一个东西——一种对“什么才是真正有价值的工程能力”的更清醒认识。DHH说手写代码落幕不是否定工程经验的价值而是把价值的天平从“写的速度和量”拨向了“拆解的清晰度、审查的敏锐度、边界的控制力”。今天这篇东西里所有建议都是我踩坑后沉淀下来的。如果你刚接触Agent先不要急着做复杂框架把那个最小循环跑明白如果你已经踩过上下文丢失和执行中断的坑回到记忆管理和权限隔离这两个基本功上来大部分问题都能解决。这条路还在快速变化但这几个底层能力短期内不会过时。
返回列表