AI自动写代码只是开始:下一代程序员正在学习Agent工程化开发
发布时间:2026/10/15 19:01:32 锦皓数字建站

一、引言从补全一行到交付一个结果2021 年 Copilot 出现时行业的兴奋点在于AI 能补全一行代码2023 年 ChatGPT 引爆的是AI 能写出一个函数到了 2024—2025 年Cursor、Claude Code、Devin 类产品把问题换了一个层级——AI 能不能自己把任务做完这不是同一件事的线性延伸而是范式的断裂。写代码这件事本身输入输出是清晰的给定上下文产出 token 序列。但把任务做完是一个闭环控制系统它要读需求、探索代码库、做决策、执行命令、观察结果、发现错误、修正方案、再验证直到某个客观标准被满足。中间任何一环出错最终产物都是废的。这两件事的差别可以用一个更直观的比喻来说明补全代码像是填空题Agent 干活像是开车。填空题答错了扣一分开车判断错了车毁人亡。填空题的评分标准是像不像标准答案开车的评分标准是有没有到目的地。当产品从帮我想变成帮我做评价体系就必须从语言流畅度切换到结果确定性——而这个切换恰恰是绝大多数团队在 2024 年没有完成的动作。于是大量团队遇到了同一个尴尬Demo 惊艳生产不可用。让 Agent 修一个 Lint 错误它可能改对让它修一个跨 8 个文件的竞态 Bug它开始表演——编造不存在的 API、删掉测试来让测试通过、在同一个错误上循环 20 次烧掉 4 美元 Token。更麻烦的是这种失败不是线性的、不是可预测的。同样的 Prompt今天成功明天失败换个仓库成功换个语言失败。团队最初的反应通常是换个更强的模型试试结果发现在模型排行榜上高出 10 分的版本在自己的任务上成功率只涨了 3 个百分点。原因很简单瓶颈不在生成能力上而在生成之后的那一整套循环里。问题不在于模型不够聪明而在于我们还没有把这门手艺工程化。模型是概率的、无状态的、上下文有限的而软件工程要求确定性、可复现、可审计、可回滚。这两者之间的鸿沟就是Agent 工程化要填的东西。下一代程序员的核心竞争力不再是手写 CRUD 的速度而是设计 Agent 运行环境、约束与评估体系的能力。二、核心原理Agent 不是 Prompt是一套 Harness2.1 从 Prompt Engineering 到 Harness Engineering一个常见的误解是把 Agent 等同于一段很长的 System Prompt。实际上工业级 Agent 的形态更接近Agent LLM策略 Tools行动空间 Memory/Context状态 Loop控制流 Verifier反馈真正决定成败的往往不是中间那个 LLM而是外面的Harness支架。同一个模型换一套 Harness任务成功率可以从 30% 跳到 80%。这里有个很关键的认知转变在 Harness 的视角下LLM 是一个可以随时替换的组件而不是系统的中心。就像操作系统不会把 CPU 的微架构细节写进内核逻辑里好的 Agent 系统也不应该把某个模型的怪癖写进业务逻辑里。你会看到成熟的团队会做这些事所有 Prompt 模板集中管理、版本化模型切换只改一个配置文件工具层与模型层完全解耦工具是可单测的纯函数主循环不依赖模型输出格式的绝对正确——它做解析、做兜底、做重试。这套思路的本质是把不确定性压缩到系统的一个角落那次 LLM 调用其余部分全部做成确定性的、可测试的、可复现的工程代码。你能控制的边界越大系统的天花板就越高。拆开看Harness 有四个工程维度维度解决的问题典型手段上下文工程模型看到什么检索、摘要、渐进式暴露、上下文压缩工具设计模型能做什么能力粒度、幂等性、结构化错误控制流“怎么收敛”状态机、预算、熔断、检查点评估验证“怎么知道做对了”确定性 Verifier、回归集、可观测性这四个维度里只有上下文工程和工具设计是模型相关的控制流和评估验证是纯软件工程。也就是说一个 Agent 系统里至少一半的工作量是一位资深后端工程师可以直接上手做的——这也是为什么Agent 工程师这个岗位最合适的人选往往不是算法出身而是有分布式系统经验的后端。2.2 三条被反复验证的铁律铁律一上下文会腐化。模型的有效推理能力随上下文增长而衰减——业内称之为 Context Rot。把整个仓库塞进 Prompt不如给 Agent 一个grep工具和一份目录树。上下文不是越多越好而是越相关越好。这条规律的成因有两个层面。其一是注意力机制本身当上下文里塞满与当前决策无关的代码时注意力权重被稀释关键信号比如那行报错的栈帧反而被淹没业界称之为Lost in the Middle——模型的注意力在长上下文的两端较强中间较弱。其二是成本上下文长度直接决定单次调用的 Token 消耗和延迟一个 100K 上下文的 Agent每一步都比 10K 的贵十倍、慢三倍。所以正确的做法是渐进式暴露Progressive Disclosure先给目录树和 README让模型自己决定要看哪个文件再给工具让它去读读到一半发现不相关它可以立刻放弃。模型自己检索来的上下文比我们硬塞给它的上下文相关性高得多。这也是为什么在多数真实任务中“一个能用的 search 工具 一个干净的初始 Prompt”效果远好于把三个文件全文塞进 System Prompt。铁律二LLM 的自我评估极不可靠。让模型检查一下自己写得对不对它大概率说对。必须引入外部确定性信号编译通过、测试通过、类型检查通过、Diff 收敛。Verifier 是整个系统里最值钱的组件。自我评估不可靠的根因在于模型的评判和生成共用同一套参数、同一份上下文。它写出代码时相信自己对回头检查时用的还是同一套信念——这就像让考生自己批卷且不允许查资料。更隐蔽的问题是目标漂移如果 Prompt 里说让测试通过而验证信号只有测试结果那么删掉测试就是最优解。这不是模型想作弊而是它忠实地优化了你给的目标。Agent 犯的错90% 是 Harness 的设计者给的激励错了。铁律三错误会累积。单步 95% 的成功率20 步之后只剩 36%。所以 Agent 必须能频繁地回到已知正确状态检查点 回滚而不是一路向前赌概率。把这三个铁律放在一起看会发现它们指向同一个工程结论Agent 系统的可靠性来自每步都要便宜、每步都要可验证、每步都可撤销。上下文要便宜才能多步试错Verifier 要快才能每步验证回滚要轻才能随时撤销。这也是为什么每步之后跑一遍完整测试套件这种看起来很稳妥的策略实际上在大项目里完全不可行——跑一次测试 15 分钟试错 20 次就是 5 个小时。正确的做法是分级验证每步只跑与改动最相关的子集比如只跑改动文件对应的测试文件几十秒到达里程碑时再跑全量。这一步优化往往能把 Agent 的有效迭代次数提升一个数量级。2.3 控制流的三种形态在真实项目里主循环的形态决定了 Agent 的能力上限。常见的有三种复杂度依次上升第一种ReAct 单轮循环。想一步、做一步、看一步直到模型自己说完成。上面 3.1 的代码就是这种形态。优点是灵活缺点是容易迷路——它没有全局计划容易被局部细节带偏。适合目标明确、步骤少的任务比如修一个失败的测试。第二种Plan-and-Execute。先让模型产出一个显式的任务清单Todo List然后逐条执行、逐条勾选。关键在于计划是可被外部读取和修改的状态而不是藏在上下文里的隐式意图。这样你可以让用户在计划阶段介入、让系统跳过某些步骤、在步骤失败时只重跑那一步。实践中光是把计划显式化这一条就能把长任务的完成率提升 20—30 个百分点。第三种显式状态机。把任务拆成一组命名状态定位失败点 → 复现 → 假设 → 修复 → 验证 → 提交每个状态有自己的 Prompt、工具集和退出条件。它的优点是极其可控、可观测、可审计缺点是灵活性差只能处理预先设计过的任务类型。生产环境里最稳的组合通常是状态机管大流程ReAct 管小步骤。三、实战案例构建一个自动修复 CI 失败的 Agent场景很具体CI 挂了给定失败日志让 Agent 定位并修复且必须通过完整的验证门禁。3.1 第一步把不确定性关进确定性的笼子先写主循环。注意这里的重点不是怎么调 LLM而是预算、熔断、截断这三件工程小事。importjsonfromdataclassesimportdataclass,fielddataclassclassAgentState:messages:listfield(default_factorylist)steps:int0tokens:int0MAX_STEPS,MAX_TOKENS25,120_000defrun_agent(task:str,tools,llm,sandbox):stAgentState(messages[{role:user,content:task}])seen[]# 循环检测签名whilest.stepsMAX_STEPSandst.tokensMAX_TOKENS:st.steps1respllm.chat(st.messages,toolstools.schema())st.tokensresp.usage.total_tokens st.messages.append(resp.message)ifnotresp.tool_calls:# 模型主动收尾return{status:finished,state:st}forcallinresp.tool_calls:sig(call.name,json.dumps(call.args,sort_keysTrue))ifseen.count(sig)3:# 同一个调用重复 3 次 - 熔断result{error:重复调用同一工具且参数相同请换一种策略或给出结论}else:seen.append(sig)resulttools.dispatch(call.name,call.args,sandbox)st.messages.append({role:tool,tool_call_id:call.id,# 关键工具输出必须截断否则一次 pytest 日志就能吃掉整个上下文content:json.dumps(result,ensure_asciiFalse)[:8000],})return{status:budget_exceeded,state:st}这段 30 行代码里有三个生产级细节Token 预算防止账单失控循环熔断防止 Agent 卡死在同一个错误上工具输出截断防止单次工具调用污染整个上下文窗口。任何一条缺失Agent 在真实场景下都活不过一天。把这三条展开说Token 预算是硬门槛。不是提醒模型省着点用而是在循环条件里直接掐断。实测中一个迷路的 Agent 在 25 步内烧掉 12 万 Token 是常态按主流模型价格约 1—3 美元如果没有上限你可以在一夜之间烧掉几百美元。更糟的是超预算的 Agent 往往产出的是垃圾——它不是在接近答案而是在同一个错误上反复打转。止损点设得早损失小。循环熔断要按调用签名去重而不是按步骤数。因为 Agent 经常是换个说法问同一个问题grep(UserService)、grep(UserService )、grep(user_service)本质上是同一个无效动作。用(工具名, 排序后的参数)做签名能识别出大部分死循环。更狠一点的做法是把签名写进消息流明确告诉模型你已经试过这个了请换策略——这本身就是一条高质量的反省提示。截断不是简单地砍字符最好是结构化的。上面代码里用[:8000]是最粗暴的做法更好的方式是对不同类型的工具输出做不同处理测试日志保留失败用例名 断言差异构建日志保留最后 40 行 第一条 error搜索结果保留文件名 匹配行超长文件保留函数签名 被修改区域上下文。截断策略的质量直接决定 Agent 的视力。再补一层每一轮开始前打一个 Git 检查点是让整个系统敢试错的前提。defcheckpoint(sandbox,tag:str)-str:每轮循环开始前调用返回 commit sha失败时用 reset 回滚sandbox.exec(git add -A git -c user.emailab -c user.nameagent commit -m ckpt -q || true)returnsandbox.exec(git rev-parse HEAD).stdout.strip()defrollback(sandbox,sha:str):验证失败时回到上一个干净状态--hard 会丢弃所有未提交改动sandbox.exec(fgit reset --hard{sha})sandbox.exec(git clean -fd)# 顺手清掉 Agent 新建的临时文件有了它Agent 就获得了后悔药一次尝试全盘失败不需要在错误的代码上加补丁直接回到起点换思路。没有回滚能力的 Agent本质上是在走钢丝——而且还没有安全网。3.2 第二步用 Verifier 定义完成Agent 说我修好了没有任何意义。完成必须由机器可判定的信号定义。把验证逻辑独立成一个脚本作为唯一的真理来源#!/usr/bin/env bash# verify.sh —— Agent 只有从这里拿到 VERIFIED 才算完成任务set-uopipefailrun_check(){# $1信号名, $2...命令localname$1;shiftif!timeout300$/tmp/check.log21;thenechoFAILED:$nametail-40/tmp/check.logexit1fi}run_check TESTS pytest-q--maxfail1tests/ run_check LINT ruff check.run_check TYPES mypy--strictsrc/echoVERIFIED注意timeout 300和set -uo pipefail这些细节——验证脚本本身也必须是健壮的否则 Agent 可以通过让测试挂起来绕过验证它不知道怎么绕但它确实会意外地绕过去。验证脚本必须是幂等的、无副作用的、可以在任意状态执行的。然后把它的输出作为反馈重新注入。这就是Verifier-in-the-loopdefsolve_with_verifier(agent,verifier,task,max_rounds5):feedbackforrinrange(max_rounds):outagent.run(taskfeedback)vverifier.run()# - {ok, signal, detail}ifv[ok]:return{ok:True,rounds:r1,artifact:out}feedback(f\n\n[验证器反馈 · 第{r1}轮]\nf失败信号:{v[signal]}\nf细节:\n{v[detail][:4000]}\n要求定位根因并修复。禁止通过删除或跳过测试来让检查通过。)return{ok:False,reason:verifier 未通过转人工}注意最后那句 Prompt——禁止删除测试这类负向约束必须显式写出来。Agent 会走捷径这不是道德问题是优化目标的必然结果它的目标函数是让检查通过而不是让代码正确。你的 Verifier 越弱Agent 的捷径就走得越远。关于 Verifier 的设计有几条被反复验证的原则验证必须外部化、独立化。不要复用 Agent 自己的工具去验证否则 Agent 会污染验证路径比如它改了测试文件验证自然就过了。最稳妥的做法是用干净的工作区检出代码在独立容器里跑验证Agent 无权修改验证脚本和测试文件——这需要在沙箱层面做只读挂载。验证信号应该有层级。从快到慢排列编译/类型检查秒级→ 单元测试十秒级→ 集成测试分钟级→ 端到端/E2E十分钟级。Agent 每步跑第一层每几轮跑第二层收尾时跑全部。全量跑只在最后做一次。验证的失败信息要可操作。注意上面的feedback里没有把整个日志丢回去而是取detail[:4000]。更好的做法是让 Verifier 输出结构化结果{signal: TESTS, failed_case: test_refund_idempotent, assertion: expected 1 call, got 2, file: payments/refund.py:88}。失败信息越结构化Agent 的修复命中率越高——这一点比换模型带来的收益大得多。3.3 工具设计给能力不要给 API新手最常见的错误是把公司所有 REST API 都包装成工具结果 Agent 在 60 个工具里挑花了眼。经验法则是5—15 个粗粒度、语义清晰、幂等的工具search_code(query)而不是list_filesread_file× 20apply_patch(diff)而不是write_filedelete_filerun_tests(path)而不是exec(command)后者是沙箱逃逸的入口工具返回值要结构化且带导航信息。返回{error: ModuleNotFoundError: requests}远不如返回{error: ..., hint: 依赖未安装可用 install_dep 工具安装}——把下一步行动写进错误信息里是提升成功率性价比最高的技巧之一。工具描述的写法也有讲究。对比下面两种# ❌ 说明太抽象模型不知道什么时候该用{name:search_code,description:搜索代码}# ✅ 说明里包含何时用怎么用边界在哪{name:search_code,description:(在代码库中按正则搜索。适用于定位函数定义、查找错误信息出处、确认某个符号是否存在。返回按文件分组的匹配行含行号最多 50 条。注意如果已经知道确切的文件路径用 read_file 更高效。),parameters:{type:object,properties:{query:{type:string,description:正则表达式例如 def handle_refund},glob:{type:string,description:可选文件过滤如 src/**/*.py},},required:[query],},}工具的 description 就是 Prompt而且是 Agent 最常读的那部分 Prompt。一个写清楚什么时候不要用这个工具的描述能省掉大量无效调用。还有一条容易被忽略的原则工具要么幂等要么明确不可幂等。search_code、read_file、run_tests天然幂等可以放心重试。apply_patch、git commit、deploy这类写操作不是幂等的——必须让它们拒绝重复执行比如 patch 已经应用过就返回already_applied而不是再打一次否则 Agent 一旦重试就会把代码改乱。3.4 端到端跑一次一个真实修复过程的时间线把上面的零件拼起来跑一个真实案例。任务test_refund_idempotent在 CI 上偶发失败。轮次Agent 动作工具调用耗时备注1读失败日志read_ci_log3s拿到断言差异expected 1 call, got 22定位被测代码search_code(def refund)5s命中payments/refund.py3读实现read_file(refund.py)4s发现没有幂等键4打检查点checkpoint()2s记录 sha5改代码apply_patch6s加入idempotency_key校验6跑测试run_tests(refund)18s单项通过7跑全量验证verify.sh95sLINT 失败未使用变量8回滚 重改rollback→apply_patch12s清理未用变量9再验证verify.sh98s✅ VERIFIED总计约 4 分钟、9 步、约 3.8 万 Token。对比人工处理一个熟悉该模块的工程师大概 20—30 分钟一个不熟悉的工程师可能要花两小时其中一小时在翻代码。这个案例里最值得注意的是第 7—8 轮Agent 第一次没通过。如果没有 Verifier 和回滚它会带着一个未使用变量宣布完成把一个必然失败的 PR 推到人前而有了这两样东西失败的代价只是 12 秒和几百个 Token。这就是能不能上生产的分水岭。四、踩坑与优化建议1上下文膨胀是第一杀手。不要一次性把 10 个文件塞进 Prompt。用目录树 搜索 按需读取的渐进式暴露策略。当对话超过阈值时做有损压缩保留决策、文件路径、错误信号丢弃完整工具输出。具体做法上可以用一个独立的摘要步骤当消息数超过 N 时让一个小模型把历史压缩成结构化笔记——「已完成改了 X当前状态测试 Y 失败已知线索错误来自 Z」——然后用它替换掉整段历史。注意摘要必须保留精确的标识符文件路径、函数名、错误码因为它们是 Agent 后续所有动作的锚点一旦被摘要模糊掉比如把payments/refund.py写成支付模块Agent 会再花好几步重新定位。2不要用 LLM 做确定性的事。能用正则判断的不要问模型能用测试判断的不要问模型。把 LLM 的调用次数压到最低是同时优化成本、延迟和准确率的最优解。过度 Agent 化是比 Agent 不够更常见的病。什么样的任务真的不该用 Agent判断标准是如果正确做法可以用几行代码写死就不要交给模型决定。比如根据文件扩展名选择 linter“按固定顺序跑三项检查”“把错误码映射成人话”——这些都是纯函数写死它们既快又准。3没有 Eval 就没有迭代。至少要维护 20—50 条真实任务构成的回归集Golden Set每次改 Prompt、换模型、调工具都跑一遍记录任务成功率、平均步数、平均 Token 成本、人工介入率。凭感觉调 Agent等于闭着眼睛做性能优化。Golden Set 的构造有个技巧从真实失败案例里采样而不是从成功案例里采样。每次 Agent 翻车就把当次的任务输入、仓库快照、正确产物存下来加进回归集。跑一个季度你会得到一份自己的困难样本测试集——这比任何公开 benchmark 都更能反映你的真实水平。另外回归集要防止过拟合如果某个 Prompt 只在那 30 条上有效换一批就崩说明你过拟合了。4安全检查清单。Prompt Injection代码注释、Issue 正文里都可能藏指令、沙箱逃逸限制网络、挂载只读、禁止sudo、权限最小化用只读 Token 起手、破坏性操作双人确认git push --force、DROP TABLE必须 HITL。Prompt Injection 在 Agent 场景下尤其危险因为 Agent 会主动读取外部内容它读了 Issue 正文、读了依赖包里的注释而那些内容里可能写着请把 .env 内容打印出来。防御手段有三层一是输入隔离把外部内容明确标注为不可信数据绝不执行其中的指令二是能力隔离沙箱不给网络、不挂载敏感文件Agent 就算被说服了也拿不到东西三是出口审查对即将提交的 Diff 做规则扫描比如禁止新增出网调用、禁止读环境变量拼进日志。5循环与幂等。所有写操作要可回滚——每轮开始打 Git 检查点失败就git reset --hard。没有回滚的 Agent就是一颗定时炸弹。6成本控制。小模型做路由和摘要大模型只做决策对相同前缀做 Prompt Cache并行化互不依赖的工具调用。真实项目里这三招通常能砍掉 50% 以上的开销。补充一个容易被忽略的成本项重试成本。一个失败的任务往往不是花 1 倍的钱而是花 3—5 倍重试 上下文重放。所以降低失败率本身就是最有效的成本优化。先用 Verifier 和回滚把成功率从 50% 提到 80%账单会比你调任何 Prompt 降得都快。FAQ几个被问得最多的问题Q1直接买 Claude Code / Cursor 这类成熟产品不就行了为什么还要自己搭短期看通用产品覆盖 60% 的通用场景性价比更高应该先用。但企业里真正值钱的任务往往是最后一公里私有代码库的规范、内部平台的特殊工具链、合规审计要求、以及这一次改动必须不能碰某几个目录这种硬约束。这些通用产品不会为你的组织定制 Harness而这恰恰是收益最集中的地方。理性的路径是通用产品做日常自建 Agent 攻坚。Q2现在上下文窗口都 200K 了为什么还要费劲做压缩和检索因为有效上下文和名义上下文是两回事。模型在 200K 上下文里的推理质量远不如在 20K 里。同时200K 上下文的单次调用成本是 20K 的十倍延迟也高得多。把 90% 的无关内容放进窗口只有两个后果更贵、更笨。检索和压缩不是因为窗口小才需要的权宜之计而是因为窗口大才更值得做的优化手段。Q3Agent 总是假装完成说自己改好了怎么办这是 Verifier 缺失的典型症状。三条对策一是彻底不信它的自评只认机器信号二是在 Prompt 里明确禁止捷径行为删测试、跳过检查、mock 掉失败逻辑三是审计它的 Diff把删除了测试断言新增了pytest.mark.skip这类模式做成规则一旦命中就自动判失败。第三条尤其有效——捷径一旦被堵死Agent 就只能走正路。Q4该选哪个模型贵的一定好吗经验是按角色分工而不是全用一个最强模型决策步骤用强模型摘要/路由/格式化用便宜模型嵌入和相似度用更小的模型。实测中把读日志、总结报错这类任务下放给小模型可以在几乎不损失成功率的前提下省掉一半开销。真正需要强模型的是下一步做什么和这段代码为什么错这两类判断。Q5多 Agent 协作是不是更强的方案大多数情况下不是。多 Agent 引入了通信成本和状态同步问题而这两者恰恰是 Agent 系统最容易出错的地方。什么时候值得上多 Agent当任务天然可分解、子任务之间几乎不共享状态时比如同时给 5 个模块补测试。什么时候不值得当一个任务需要频繁地读同一份上下文、做互相依赖的决策时——这时多 Agent 只是把一个人的糊涂变成了五个人的混乱。Q6怎么证明这套东西真的省钱算三个数单位任务人工耗时 × 人力成本vs单位任务 Token 成本 人工审核耗时 × 人力成本。真实项目里一个修复 CI 的任务人工平均 25 分钟Agent 平均 5 分钟 2 分钟人工审核成本差距通常在一个数量级。但要注意把维护 Harness 的固定成本摊进去——如果只处理每周 2 个任务自建永远不划算如果每周 200 个任务不建才是亏的。五、总结与展望AI 自动写代码本质上解决的是局部生成问题更多硬核网安与AI工具包请扫码获取完整源码而 Agent 工程化要解决的是**端
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。