生产级 AI Agent 写代码真正难的地方,不是怎么写一句更聪明的提示词,而是怎么管理它每一步看到的信息、能调用的工具、必须遵守的边界、以及做完以后怎么验证。

很多人第一次用 Coding Agent,都会经历一个很微妙的阶段。

一开始很惊艳。你丢给它一个需求,它会读文件、找依赖、改代码、跑测试,甚至自己修复报错。看起来像一个不知疲倦的初级程序员。

但多用几次以后,问题就来了。

它会把一个很小的 bug 修成一组重构;会为了让测试通过,顺手改掉测试本身;会读错项目里的旧实现;会把日志里的一行异常当成根因;更麻烦的是,它有时不是马上错,而是第一步小错,第二步补错,第三步把错误合理化。

很多人把这归因于一句话:Prompt 没写好。

这个判断不算错,但太浅。

生产级 AI Agent 写代码真正难的地方,不是怎么写一句更聪明的提示词,而是怎么管理它每一步看到的信息、能调用的工具、必须遵守的边界、以及做完以后怎么验证。

这件事现在有一个更准确的名字:上下文工程,Context Engineering。

Agent 和脚本的根本区别

脚本是确定性的。

你写一个脚本扫描目录、改文件、执行测试,它的控制逻辑在代码里。哪一步读文件,哪一步写文件,哪一步退出,都是人提前写死的。

Agent 不一样。

Agent 的控制逻辑有一部分交给了模型。它不是简单执行一段固定流程,而是在每一轮根据当前上下文决定下一步做什么。

一个最小的 Coding Agent,大概可以抽象成这样:

while task_not_done:
 context = build_context(goal, repo, memory, tool_results)
 action = model.decide(context)
 result = tools.execute(action)
 context.append(result)
 verify_or_continue()

这个循环看起来简单,但危险也在这里。

因为 Agent 每一次决策,都依赖当时的上下文。如果上下文里混进了旧日志、无关文件、过期设计、错误假设,模型就会基于这些材料做出下一步判断。工具执行后的结果又会被塞回上下文,成为下一轮判断依据。

所以 Agent 的错误不是一次性错误,而是循环系统里的状态污染。

这也是为什么 Agent 比普通脚本更难管。

脚本最多是你写错了逻辑。Agent 是它会自己生成下一步逻辑,而且每一步逻辑都被上一轮观察影响。

写脚本时,我们主要关心代码有没有 bug。设计 Agent 时,我们还要关心:它看到了什么、没看到什么、误解了什么、被什么工具输出带偏了。

Prompt 只是启动参数,Context 才是运行时

很多人以为 Prompt 是 Agent 的大脑。

更准确地说,Prompt 只是大脑收到的一部分输入。

在真实的 Coding Agent 里,模型每一轮看到的内容远不止用户那句话。它通常还会看到系统指令、项目规则、目录结构、最近打开过的文件、搜索结果、命令输出、测试失败日志、历史对话、工具定义、长期记忆、当前 diff。

这些东西加起来,才是 Agent 的运行时状态。

这就解释了一个常见现象:同一句 Prompt,在不同项目里效果完全不同。

不是模型性格变了,而是上下文变了。

如果一个仓库有清晰的 README、明确的测试入口、稳定的模块边界、规范的错误日志,Agent 会表现得像一个靠谱同事。

如果一个仓库里到处是历史包袱、重复实现、过期文档、没有测试、命名混乱,Agent 很容易像一个刚入职又不敢问人的新人:它会努力推理,但推理材料本身就是脏的。

这也是为什么“给 AI 多塞点资料”不一定能提升效果。

上下文窗口不是无限工作台,它更像模型的注意力预算。你塞进去的每一段内容,都会和其他内容竞争注意力。高信号材料会帮它判断,低信号材料会稀释判断。

一个成熟的 Agent 系统,不应该把整个仓库一股脑丢给模型,而应该回答三个问题:

第一,本轮任务必须知道哪些事实?

第二,哪些材料只是可能有用,应该通过工具按需检索?

第三,哪些历史信息已经过期,继续保留只会制造噪声?

这就是上下文工程和提示词工程的差别。

提示词工程关心“怎么说”。上下文工程关心“让模型在什么时候看到什么”。

为什么 Agent 会越改越乱

Agent 写代码越改越乱,通常不是因为它不会写代码,而是因为循环系统缺少刹车。

一个典型失控链路是这样的。

第一步,Agent 读到不完整上下文。

比如你让它修一个接口超时问题,它只读到了 controller 和 service,没有读到网关超时配置,也没有读到调用方重试逻辑。于是它把问题判断成 service 内部性能问题。

第二步,它生成一个局部合理、全局错误的计划。

它可能会加缓存、改线程池、改 SQL,甚至重构方法。但真正问题可能只是网关配置和客户端超时不一致。

第三步,工具权限过宽。

如果 Agent 可以随意改文件、删文件、运行命令,它就会用“完成任务”的视角行动。模型不会天然知道哪些文件是业务核心,哪些脚本不能碰,哪些测试不能为了通过而修改。

第四步,观察结果被误读。

它跑了一部分测试,看到通过,就以为问题解决了。但局部测试通过不代表行为正确。更糟的是,测试失败时,它可能不是回到根因,而是继续修补刚刚引入的新错误。

第五步,错误被写回上下文。

一旦 Agent 把“我已经确认这是缓存问题”这样的错误判断带进下一轮,后面的每一步都会围绕这个假设展开。它会越来越自洽,也越来越偏。

这就是为什么你会看到一种很熟悉的翻车方式:Agent 很努力,日志很多,diff 很大,解释也很完整,但方向错了。

技术上看,这不是“AI 不认真”,而是闭环控制系统缺少独立验证。

如果生成计划的是它,执行工具的是它,解释结果的是它,判断完成的还是它,那整个系统其实只有一个脑子。这个脑子一旦进入错误轨道,很难自己跳出来。

工具边界比 Prompt 更重要

很多团队在接入 Agent 时,第一反应是写一大段系统提示:

不要乱改代码。

不要删除文件。

不要修改测试。

先理解项目再动手。

这些要求有用,但不够。

因为提示词是软约束,工具权限才是硬边界。

如果你不希望 Agent 删除数据库,就不要给它直接执行危险 SQL 的能力。如果你不希望它改生产配置,就不要把生产配置写权限暴露给它。如果你不希望它大范围重构,就把写操作限制在明确文件集合里。

一个技术判断很简单:凡是你不敢让一个实习生直接做的动作,也不应该直接开放给 Agent。

真正的 Agent 工具设计,应该按风险分级。

只读工具风险最低,比如搜索文件、读取文件、查看 git diff、查询日志。

可逆写工具风险中等,比如在工作区生成 patch、修改临时分支、创建草稿文件。

不可逆或高影响工具风险最高,比如删除文件、执行数据库变更、发布、改权限、触发生产任务。

低风险工具可以自动执行。中风险工具要有 diff 和验证。高风险工具必须有人确认,或者根本不暴露给通用 Agent。

MCP 这类协议的价值也在这里。它不只是“让模型连更多工具”,更重要的是把资源、提示模板、工具能力用统一接口暴露出来。接口统一以后,团队才有机会做权限、审计、灰度和隔离。

但要注意,工具越多不等于 Agent 越强。

工具集合臃肿以后,模型会面临选择困难。两个工具功能重叠、参数含义模糊、返回结果很长,都会增加误用概率。一个人类工程师都分不清该用哪个工具,Agent 通常不会表现得更稳定。

所以生产级 Agent 的工具设计,应该追求三个标准:

工具职责单一。

参数含义明确。

返回结果短而有信息量。

不要让工具返回几千行日志,然后期待模型自己抓重点。更好的做法是让工具先结构化:错误摘要、关键堆栈、失败测试、相关文件、建议下一步。

Agent 不怕工具少,怕工具乱。

记忆不是聊天记录,而是状态压缩

很多人一听 Agent 记忆,就想到“它能记住我说过的话”。

对 Coding Agent 来说,这个理解太消费级了。

生产级 Agent 的记忆,不是完整聊天记录,而是可复用、可检索、可更新的工程事实。

比如:

这个项目的测试入口是什么。

某个模块为什么不能直接改。

上一次排查已经排除了哪些方向。

一次迁移任务当前完成到哪一步。

哪个设计决策已经被团队确认。

这些信息如果每次都靠模型从历史对话里捞,不稳定,也浪费上下文。更好的方式是让 Agent 在长任务中维护结构化笔记。

你可以把它理解成 Agent 版本的工作日志:

任务目标:修复订单导出超时
已确认事实:
- 超时主要发生在导出超过 5 万条记录时
- Controller 层没有分页流式输出
- 网关超时为 60s,客户端重试 2 次

已排除方向:
- 不是数据库连接池耗尽
- 不是权限校验接口阻塞

下一步:
- 检查导出链路是否支持异步任务
- 补充大数据量导出测试

    这种记忆的价值,不是“模型更像人”,而是把长任务拆成可延续的工程状态。

    长任务一定会遇到上下文窗口限制。即使窗口越来越大,也不能指望把所有历史都塞进去。更可控的方式是压缩。

    压缩不是简单总结,而是保留对后续决策有影响的信息,丢掉已经不需要的原始输出。

    比如一次测试失败的完整日志可能有 2000 行,但下一轮真正需要的只有:

    失败用例名。

    异常类型。

    关键堆栈。

    疑似相关文件。

    是否由本轮改动引入。

    这就是上下文工程里的一个核心动作:把原始观察变成决策材料。

    生产级 Agent 必须有 Verifier

    如果只能给 Coding Agent 加一个模块,我会优先加 Verifier。

    原因很直接:生成器不能同时充当最终裁判。

    Verifier 的职责不是继续写代码,而是判断当前结果是否满足验收条件。它可以是自动化测试、静态检查、类型检查、安全扫描、规则校验,也可以是另一个更窄职责的模型。

    关键是它必须独立于生成链路。

    比如 Agent 修改接口后,Verifier 不能只问一句“你觉得修好了吗”。它应该检查:

    相关测试是否运行。

    失败测试是否和本次改动相关。

    公开接口是否发生破坏性变化。

    配置文件是否被意外修改。

    新增代码是否绕过权限、日志、事务、幂等。

    diff 是否超出任务范围。

    这一步越工程化,Agent 越稳定。

    很多 Agent 翻车的本质,不是没有写出正确代码,而是没有判断“什么时候该停”。

    没有停止条件的 Agent,会把每一次失败都当成继续修改的理由。跑测试失败,继续改;改完又失败,再继续改;最后它可能已经偏离原任务很远。

    生产级系统必须设置硬停止条件:

    最多迭代几轮。

    最多修改多少文件。

    最多扩大多少 diff。

    遇到哪些文件必须暂停。

    测试连续失败几次必须交还给人。

    这些限制听起来会降低 Agent 自主性,但恰恰是让它可用的前提。

    自主性不是没有边界。没有边界的自主性,在工程系统里通常叫事故源。

    一个能落地的 Coding Agent 分层

    如果把生产级 Coding Agent 拆开,我建议至少分成七层。

    第一层是任务入口。

    它要把用户需求转成明确任务,不明确就追问。比如“优化一下代码”不是好任务,“把订单导出超过 5 万条时的接口超时问题定位并给出最小修复方案”才是好任务。

    第二层是 Router。

    它判断任务类型。是解释代码、生成测试、修 bug、小重构、跨模块迁移,还是只需要查资料。不同任务应该进入不同流程。不是所有任务都需要 Agent 模式,小任务用一次模型调用甚至更稳。

    第三层是 Planner。

    它先输出计划,不直接写代码。计划里要包含读取哪些文件、验证什么假设、预计改哪些位置、什么情况需要暂停。

    第四层是 Context Manager。

    它负责把项目规则、相关文件、历史决策、工具结果组织成高信号上下文。这里最忌讳“全量塞入”。正确做法是先少量核心上下文,再通过搜索、文件读取、测试结果逐步展开。

    第五层是 Tool Policy。

    它决定当前阶段允许哪些工具。探索阶段只读;实现阶段允许受限写;验证阶段允许测试和检查;发布或数据库变更必须人工确认。

    第六层是 Verifier。

    它不写业务代码,只做验收。它可以跑测试,也可以检查 diff 是否越界。它的输出应该是结构化的:通过、失败、阻塞、需要人工判断。

    第七层是 Rollback 和 Audit。

    所有高影响动作都要能追踪。改了哪些文件,为什么改,哪一轮改,验证结果是什么,失败时如何回退。这些东西不是“企业流程”,而是 Agent 时代的基本安全设施。

    把这七层放在一起,你会发现:模型只是其中一个组件。

    真正的 Agent 系统,是模型、上下文、工具、权限、验证和审计共同组成的工程系统。

    程序员应该怎么改变用法

    如果你只是个人用 Cursor、Claude Code、Codex 或其他 AI 编程工具,也不需要一下子搭完整平台。

    但你可以立刻改掉几个习惯。

    第一,不要上来就让它改代码。

    先让它读项目、列假设、给计划。你要看的不是它写得快不快,而是它理解得对不对。

    第二,把任务切小。

    “重构用户系统”这种任务很适合翻车。“把登录失败的错误码从字符串改成枚举,并补充对应测试”就稳定很多。

    第三,明确禁止范围。

    比如告诉它:不要改数据库脚本,不要改公共 API,不要修改测试期望,不要碰支付模块。更好的做法是在工具或工作流层面限制,而不是只写在 Prompt 里。

    第四,要求它先给 diff 摘要。

    你不要只看最终代码,要看它改了哪些文件、每个文件为什么改、有没有超出任务范围。

    第五,用测试结果约束它,而不是用感觉约束它。

    让它跑具体测试,让它解释失败原因,让它说明哪些失败和本次修改相关。没有测试的项目,Agent 的价值会下降很多,因为它缺少环境反馈。

    第六,长任务要让它维护笔记。

    尤其是迁移、排障、跨模块改造,不要把所有历史都留在聊天里。让它维护一个任务笔记,记录已确认事实、已排除方向、待办项、风险点。

    第七,发现它开始大面积改动时,停下来。

    大 diff 不是能力强的表现。很多时候,大 diff 只是 Agent 失去边界感的信号。

    最后说一句实话

    AI 编程工具正在从补全代码,走向自己读仓库、自己做计划、自己调用工具、自己验证结果。

    这个方向不会停。

    但越是走向 Agent,越不能只讨论“哪个模型更强”。模型能力当然重要,但在真实工程里,决定可用性的往往是更朴素的东西:

    上下文是否干净。

    工具是否清晰。

    权限是否收敛。

    验证是否独立。

    失败是否能回滚。

    日志是否能追踪。

    很多人用 AI 写代码越用越累,不是因为 AI 没价值,而是他们把 Agent 当成一个更聪明的输入框。

    Agent 不是输入框。

    它更像一个会行动的工程参与者。

    你不能只给它一句话,然后期待它天然懂项目边界、业务风险、团队规范和生产事故代价。

    真正成熟的 AI 编程工作流,不是让 Agent 替你做所有决定,而是把它放进一个设计良好的工程系统里:让它在该探索时探索,在该执行时执行,在该停下时停下,在该交还给人时交还给人。

    未来程序员的竞争力,也不会只是“会不会写 Prompt”。

    更重要的是,你能不能把一个不稳定但强大的模型,组织成一个可控、可验证、可交付的工程流程。

    这才是 AI Agent 写代码真正的门槛。

    Logo

    Agent 垂直技术社区,欢迎活跃、内容共建。

    更多推荐