ai-agent-book第五章:Coding Agent 与通用 Agent 学习笔记

第五章:Coding Agent 与通用 Agent 学习笔记

学习来源:https://bojieli.github.io/ai-agent-book/book/chapter5/

本章的核心是说明如何把 模型、上下文、工具、约束、验证、纠错与文件系统 组合成一个可靠的 Coding Agent,并进一步说明为什么代码生成可以成为通用 Agent 的元能力。

一个成熟的 Coding Agent 不只是生成代码,而是能够理解项目、制定方案、修改文件、执行命令、运行测试、处理错误、回滚恢复,并最终交付可验证的结果。

一、Coding Agent 的整体架构与工作流程

1.1 Coding Agent 的基础能力

Coding Agent 的核心工作可以概括为对代码仓库进行"搜索、读取、修改、执行和验证"。基础工具通常包括 Code Interpreter、Bash Shell、文件读取、文件写入、文件编辑、Glob 文件名搜索和 Grep 内容搜索。这些工具看起来简单,但组合起来已经能够覆盖绝大多数编程任务。相比为每一种需求都提前开发专用工具,Coding Agent 可以直接通过代码和 Shell 动态解决问题,因此代码生成本身具有较强的通用性。

对于开放任务型通用 Agent,文件系统不仅用于保存文件,还承担了工作空间、长期记忆和中间状态存储的作用。Agent 可以从项目文件中读取上下文,把代码、数据、日志和生成结果写回文件,并将稳定经验沉淀为项目指令或长期记忆。也就是说,Coding Agent + 文件系统 可以构成通用 Agent 的基础执行框架。

1.2 Harness 在 Coding Agent 中的作用

Coding Agent 的稳定性不能只依赖模型能力,更依赖 Harness。Harness 一方面提供上下文和工具,让 Agent 有能力完成任务;另一方面通过约束、验证和纠错机制限制错误行为。软件工程本身已经具备完善的测试、Lint、类型检查、Git、CI 和沙盒环境,这些机制天然适合作为 Agent 的自动反馈系统。因此,Coding Agent 成熟度较高的重要原因,不只是模型会写代码,而是代码任务本身具有较强的可验证性。

Harness 的核心原则可以概括为:能用程序强制执行的规则,不要只写成自然语言提示;能自动验证的结果,不要完全依赖人工审查;错误反馈应尽量快速、结构化;任何高风险修改都应具有可靠的回退机制。 对 Agent 来说,"代码写完"不是任务结束,只有测试、检查和最终验证通过,才可以认为任务完成。

1.3 Coding Agent 的整体流程:按 Prompt 约束理解

这一部分可以直接理解成 Coding Agent 在执行任务时需要遵守的一组工程约束。复杂任务并不是"收到需求后直接开始写代码",而是依次完成项目理解、需求澄清、方案设计、实现、测试、自审查和文档同步。

  • 项目理解约束: 在修改代码之前,先阅读 README、CLAUDE.md、AGENTS.md、.cursorrules 等项目说明,理解目录结构、主要模块、构建方式、测试命令和禁止修改的区域。如果缺少关键文档,应先通过阅读代码建立项目结构认知,而不是直接开始修改。

  • 需求澄清约束: 对简单且边界明确的任务可以直接实现;对"优化性能""重构系统"等范围模糊的任务,应先明确目标、影响范围、允许的权衡和验收标准。在需求仍然存在关键歧义时,不应盲目编码。

  • 设计约束: 对影响多个模块或涉及重要架构变化的任务,在编码前先形成简洁设计方案,说明需要修改哪些模块、采用什么方案、是否增加依赖、可能影响哪些已有功能。复杂任务应先验证方案,再进入大规模代码修改。

  • 实现约束: 实现时优先复用项目已有抽象、工具和代码风格,避免无必要地重复造轮子。修改应尽量局部、可回滚,并避免为了让测试通过而采用破坏性捷径,例如删除已有逻辑、绕过权限或修改测试本身。

  • 测试约束: 修改完成后必须运行测试,而不是只检查代码是否生成成功。新增或修改功能应覆盖正常路径、边界情况和异常情况。如果测试失败,应分析错误、修复代码并重新测试,形成"测试---修复---再测试"的闭环。

  • 代码审查约束: 测试通过后还要检查代码可读性、重复逻辑、性能、安全问题、类型错误和代码规范。必要时运行 Linter、类型检查器或代码审查 Sub-Agent。测试通过并不意味着代码一定适合交付。

  • 文档同步约束: 如果修改影响架构、模块依赖、接口或核心行为,应同步更新项目文档。文档与代码必须共同演化,避免未来的 Agent 或开发者读取过时信息。

因此,这套流程可以压缩成一句话:先理解,再规划;修改后立即验证;验证失败继续修复;重要变更同步文档;只有验证通过才结束任务。

二、故障错误与恢复

核心问题:生产级 Harness 会遇到哪些故障?如何检测和恢复?什么时候必须终止?

2.1 常见故障分为四层

  1. API 层故障: 包括 HTTP 429 限流、服务过载、请求超时、连接中断、流式输出被截断等。这类问题通常不是任务逻辑本身导致,而是模型服务或网络基础设施的问题。

  2. 工具层故障: 包括调用不存在的工具、参数格式不符合 Schema、工具执行抛出异常,以及模型面对同一个工具错误不断原样重试。其中"相同调用反复失败"尤其危险,因为它容易把 Agent 带入无限循环。

  3. 上下文层故障: 包括上下文窗口溢出、压缩失败,以及消息轨迹结构损坏。例如已经出现 tool_call,却缺少对应的 tool_result,会导致后续模型无法正确理解历史状态。

  4. 控制流层故障: 主要是死循环和死亡螺旋。死循环指 Agent 不断重复相同操作但没有产生新进展;死亡螺旋则是错误处理逻辑自身再次调用模型并继续报错,导致恢复机制不断触发自己。

2.2 如何检测故障

故障检测的第一原则不是"失败就重试",而是先判断错误类型是否值得重试。限流、网络抖动和短时过载通常可以重试;参数错误、权限不足、工具不存在则不能原样重试,必须修改调用参数、切换策略或停止当前路径。因此,生产级 Harness 应维护"错误类型 → 恢复策略"的明确映射,而不是统一使用重试。

除了单次错误,还必须检测重复模式。系统可以对"工具名 + 参数"生成调用指纹,如果完全相同的调用连续出现且结果没有变化,就说明 Agent 可能已经进入无进展循环。同时,应维护连续失败计数器,为后续熔断提供依据。

每个长连接都需要独立的活性信号,不能只依赖连接超时。 流式连接可能没有主动断开,但已经停止输出。此时 SDK 可能仍认为连接有效,所以 Harness 需要设置 watchdog timer。如果在指定时间内没有收到新 token 或事件,应主动终止当前流并触发恢复流程。对于消息轨迹,也需要完整性检查;发现工具调用缺少结果消息时,应在进入下一轮模型推理前修复结构。

2.3 如何恢复故障

故障恢复应遵循"从低成本、对用户透明的方法开始,逐步升级"的原则。

  1. 静默重试。 对限流、短时过载和网络抖动使用指数退避与随机抖动进行重试,并尊重服务端给出的等待时间。主任务链路值得重试,而标题生成、输入建议等辅助功能失败时通常可以直接放弃,避免后台任务大量重试挤占配额。

  2. 降级与接续。 如果单纯重试仍然失败,就改变请求方式。例如输出长度达到上限时,可以增加输出预算,或者让模型从已有内容继续生成;主模型持续不可用时,可以切换备用模型;高成本模式被限流时,可以降级到标准模式。

  3. 工具错误反馈给模型。 对"工具不存在""参数不合法""执行异常"等问题,不应直接终止会话,而应把错误包装成结构化 Tool Result 返回模型,让模型在下一轮根据明确的错误信息自行修正。错误反馈越具体,模型越容易纠正。

  4. 最终暴露给用户。 在自动重试、参数修复、降级、备用模型等方案都失败后,再把错误呈现给用户,并说明系统已经尝试过哪些恢复动作。中间恢复过程不应反复向用户暴露临时错误。

2.4 跨模型接管

当主模型持续不可用时,可以由另一个模型继续未完成的轨迹,但前提是轨迹不能完全绑定某一家模型厂商的私有消息格式。工具调用的语义通常可以转换,而模型的 reasoning、签名或厂商私有凭证可能无法跨模型复用。因此,更合理的做法是把 Agent 轨迹保存成中立格式,将可迁移的文字内容、工具名称和参数保留下来,将厂商私有凭证单独存储或在切换时丢弃。

这说明 Agent 的运行轨迹本身也是重要基础设施。中立轨迹不仅可以用于模型故障接管,还可以用于后续的轨迹重放、Agent 评估、训练样本构造和经验提取。

2.5 什么时候必须终止

恢复机制不能无限执行。以下情况应触发熔断或人工接管:同一工具调用反复出现且无进展;同一恢复路径连续失败超过阈值;达到最大迭代轮数;会话 token、时间或调用预算耗尽;恢复逻辑自身开始递归触发;继续执行可能产生不可逆的危险操作。

特别需要防止"死亡螺旋"。错误处理路径中应尽量禁止再次触发不必要的 LLM 调用,例如任务因上下文溢出结束时,不应再调用 LLM 自动生成 commit message 或记忆摘要。对于无法完全避免的递归恢复,应设置恢复深度计数器并强制终止。最终原则是:可恢复错误自动恢复,无进展错误及时熔断,高风险错误交给人工。

三、Coding Agent 的实现技巧

3.1 并行工具调用、流式执行与级联中止

传统 Agent 经常采用"生成工具调用 → 等待工具执行 → 再生成下一步"的串行方式,因此会浪费大量等待时间。更高效的实现会结合流式模型输出:当第一个工具调用的参数已经完整生成并通过校验时,就立即开始执行,不必等待模型把后续所有工具调用都生成完。对于互不依赖的多个工具调用,还可以同时并行执行,从而让模型生成与工具执行发生重叠,显著降低整体延迟。

但并行执行必须明确故障边界。工具应声明是否允许并发,默认可以采用更保守的"禁止并发"策略。一个调用失败后,只应中止同一批中依赖该结果的后续调用,不能把无关调用一起取消,更不能因为一个文件读取失败就终止整个 Agent。这里的关键思想是:故障应该局部传播,而不是无限向上扩散。

3.2 上下文精细化管理

代码仓库通常远大于模型上下文,因此不能把整个项目一次性塞给模型。文件读取工具应支持按行号范围读取,并在返回结果中保留真实行号,便于 Agent 精确定位代码。对于编译、测试等产生的大量终端输出,也不能原样全部放入上下文,应保留关键的开头和结尾,将完整日志保存为文件,在需要时再按需读取。

这类设计的重点不是简单"压缩上下文",而是让 Agent 随时能够重新获取需要的信息。也就是说,文件系统负责保存完整状态,上下文只保留当前决策所需的最小信息。

3.3 环境信息动态注入

Coding Agent 的决策高度依赖当前运行环境,例如当前工作目录、Git 分支、最近提交、已暂存和未暂存修改。因此,这些信息应在每轮推理前以动态状态栏的方式追加到上下文,而不是长期写死在 System Prompt 中。这样既可以保持 Agent 对实时环境的准确感知,也可以避免频繁变化的状态破坏稳定前缀和 KV Cache。

3.4 命令执行状态持久化

很多开发操作依赖终端状态,例如 cd 切换目录、激活虚拟环境、设置环境变量和启动后台服务。如果每条命令都创建新的 Shell,会导致这些状态不断丢失。因此,Coding Agent 通常应维护一个长期存在的终端会话,在整个任务执行期间复用该 Shell。需要并行或隔离时,再额外创建独立终端。

3.5 即时语法反馈

文件修改完成后,不应等到整个任务结束才发现基础语法错误。更合理的方式是在编辑工具完成写入后自动运行语法检查、Linter 或类型检查,并把结果直接附加到工具返回值中。这样 Agent 可以在错误刚产生时立即修复,减少后期定位成本。这与 IDE 的即时错误提示类似,也是 Harness"快速反馈"原则的具体体现。

四、搜索、文件编辑与安全机制

4.1 Coding Agent 的搜索方式

代码搜索并不是只有一种方法。glob 适合快速理解文件结构和定位某类文件;grep/ripgrep 适合已经知道函数名、变量名、错误信息等具体文本时进行精确搜索;语义搜索适合"不知道代码具体叫什么,但知道它是做什么的"的探索任务;符号级搜索则用于追踪定义和引用关系。

实际使用时更重要的是组合,而不是追求单一搜索方案。常见策略是先快速扫目录,再通过关键词定位具体实现,必要时再追踪引用关系。近年来部分 Coding Agent 更倾向通过 Agent 自主组合 glob + grep 现场搜索,而不是长期维护代码向量索引,因为代码变化快,索引维护本身也有成本。

4.2 文件编辑工具

文件编辑工具的难点是如何让模型准确表达"修改哪一段、改成什么"。常见方案包括完整字符串替换、行号定位、首尾字符串匹配以及 Patch/Diff 等。当前实用性较高的方案通常强调可验证和可失败:例如 Old String → New String 要求旧文本在文件中真实存在且最好唯一,匹配失败时直接返回错误,而不是让系统猜测应该修改哪里。

对于 Agent 来说,编辑工具越确定越好。相比让模型输出模糊的自然语言修改建议,更可靠的方法是提供结构化编辑接口,让系统能够在真正落盘之前验证定位是否准确。大量编辑还应配合 Git diff、测试和回滚机制,避免一次错误修改污染整个代码库。

4.3 Coding Agent 的安全

Coding Agent 通常拥有文件读取、写入、Shell 和网络能力,因此其风险远高于普通聊天模型。真正有效的安全策略不是只过滤输入中的恶意关键词,而是限制 Agent 即使受到提示注入后也无法完成危险动作。重点包括网络出口控制、文件系统隔离、资源限制、危险命令语义分析和持久记忆安全。

代码执行环境应优先放在沙盒中,默认限制网络访问,仅放行明确需要的域名;源码、凭证和生成物应使用不同的挂载权限,SSH Key、Token 等敏感文件不应直接暴露给沙盒;CPU、内存、磁盘和运行时间都应设置上限。Shell 安全也不能只依赖 rm、curl 等关键字黑名单,因为危险操作可以隐藏在管道、子命令或参数组合中,应尽量根据命令真实语义进行判断。

此外,长期记忆也是安全边界。来自网页、邮件等不可信来源的内容不能未经检查直接写入 MEMORY.md 等长期记忆,否则一次提示注入可能跨会话持续影响 Agent。

五、代码是通用 Agent 的元能力

5.1 什么是"元能力"

普通工具解决的是预先定义好的任务,而代码生成能够在运行时创造新工具、新规则和新表达形式,因此可以被视为一种元能力。当 Agent 遇到工具箱中没有的能力时,可以临时生成脚本、API 适配器、校验器或界面,而不需要开发者提前枚举所有情况。这也是 Coding Agent 能成为开放任务型通用 Agent 核心的重要原因。

5.2 代码作为思考工具

LLM 擅长理解自然语言,但在精确计算、符号推导和复杂逻辑约束中仍可能出错。更合理的分工是让 LLM 负责理解问题和构造形式化表达,让 Python、SymPy、NumPy、SciPy 或约束求解器负责确定性计算。代码执行结果还可以直接作为验证信号,从而减少自然语言推理中的计算错误。

5.3 代码作为业务规则约束

自然语言规则容易存在歧义,而代码规则具有确定性。因此,对于退款、支付、删除数据等不可逆操作,不能只依赖 System Prompt 告诉模型"请遵守政策",还应该在执行工具内部加入程序化校验。自然语言规则适合帮助模型理解和解释,代码校验则负责最终的强制执行,两者应同时存在。

这再次体现 Harness 的核心思想:重要规则应该编码化,而不是只文档化。 对高风险操作,应以服务端真实数据作为判断依据,而不是相信模型自己传入的"我已经检查过"。

5.4 代码驱动的多媒体生成

PPT、PDF、网页、视频等内容都可以通过代码描述,因此 Agent 可以把多媒体生成转化为代码生成问题。真正困难的地方不是生成代码,而是验证最终渲染效果。因此,本章反复采用 Proposer--Reviewer 机制:一个 Agent 负责生成代码,另一个 Agent 或 Vision 模型负责查看渲染结果并提出结构化修改意见,直到结果满足要求。

代码生成和生成模型并不是互相替代。具有明确尺寸、规则和强验证要求的对象更适合代码生成;复杂度极高、难以用少量参数精确描述的视觉对象更适合生成模型。选择哪一种路线,应取决于产物是否容易形式化,以及是否需要精确验证和后续可编辑性。

5.5 代码作为系统适配器

真实系统中的 API、日志格式和数据结构会不断变化,Agent 可以读取接口文档或观察真实样本,然后临时生成解析和转换代码。这使代码成为连接不同系统的适配层。对于没有 API 的系统,也可以先通过 Computer Use 完成操作,再把成功的操作过程固化成 RPA 脚本,提高后续执行速度和稳定性。

在 Agent 运维中,这种能力还可以用于日志解析和问题诊断:读取轨迹日志,自动定位异常模块,生成问题报告、回归测试,甚至通过 MCP 创建 GitHub Issue。代码生成因此不只是"完成用户任务",也可以成为 Agent 系统自身的维护工具。

5.6 代码作为生成式 UI

纯文本对话在复杂信息收集和数据展示场景中效率较低。Agent 可以动态生成表单、图表和交互页面,让用户一次性填写多个相关字段,或者把查询结果直接交给前端渲染。对于 SQL 等场景,更好的模式通常是让 LLM 只负责生成查询 Artifact,而不是让大量数据库结果经过 LLM 再转述,这样可以减少 token 消耗和抄写错误。

但动态代码不能直接获得无限权限。数据库应采用只读账号,限制 SQL 类型、可访问表和返回规模;前端生成代码也应运行在隔离环境中。生成式 UI 提升了交互能力,但仍然需要 Harness 在执行层完成权限和安全约束。

5.7 Agent 自举:用代码创造 Agent

代码生成的进一步应用是让 Agent 创建或修复另一个 Agent。例如 Doctor 类工具可以先用确定性规则检测 Token 过期、端口冲突、依赖缺失等常见问题,再让 LLM 处理规则无法覆盖的长尾故障。这里最合理的架构不是"全部交给 LLM",而是确定性检查负责高频问题,LLM 负责复杂语义分析。

让 Agent 创建新 Agent 时,也不应该完全从零开始生成。更可靠的做法是提供高质量 Agent 范例,让模型复制成熟框架后进行适应性修改,包括调整 Prompt、增删工具和业务逻辑,而保留已经验证过的上下文管理、工具调用和错误恢复机制。换句话说,好的代码范例本身就是一种比长 Prompt 更强的工程约束。

六、实验

实验 5-1 ★★★:跨厂商的轨迹接管

实验目标:验证一份中立的轨迹格式能否让跑到一半的 Agent 轨迹换一家模型接着跑完,并量化"原样直传"与"一刀切剥光"两种做法的代价。

技术方案:任务需要多轮工具调用,跑到中途对当前厂商注入连续的限流与过载响应,熔断后切换到另一家继续。轨迹按中立格式保存,思考分为可移植的文字与不可移植的凭证两部分,工具调用只记录名称与参数。三种做法对照:直传把原厂商返回的消息原样搬进新厂商的结构;剥离删掉全部思考与凭证;中立丢弃凭证,把文字或厂商给出的思考摘要以普通内容的身份带入,标识符按目标厂商重新生成,遇到强制要求凭证的接收端则把历史调用改写为文字叙述。选三家线上格式互不相同的厂商,两两切换。

验收标准:切换后的首个请求都要留下原始响应,直传的失败必须是厂商真实返回的错误,不得以模拟错误代替。要求中立做法在全部厂商组合上都不出现接口错误,另外两种在哪些组合上失败、报什么错,如实记录。三者对比任务完成率、切换后重复调用同一工具的次数(按"工具名 + 参数"指纹计),以及切换后到完成所需的额外轮数与 token。若中立做法在重复调用上并未优于剥离,同样如实记录。

方案 接管成功 数据及总额正确
原样直传 3/6 3/6
删除思考 4/6 4/6
中立格式 6/6 6/6

实验 5-2 ★★:输出到一半断掉之后的接续

实验目标:比较"整轮重发"与"以半截输出为前缀续写"两种恢复方式在成本、正确性与副作用上的差异。

技术方案:在流式响应上取三个断点------思考中途、正文中途、工具调用参数中途------切断连接。三种恢复方式:丢弃半截整轮重发;把半截内容作为末尾的 assistant 消息要求模型接着写(有的厂商原生支持,有的要求显式标注这是一条待续写的消息,没有这条接口的则退回下一种);追加一条元指令说明从断点继续。半截的工具调用无法以原生结构回传,需先转成文字再让模型补完,拼接后重新解析校验。若半截输出里已有工具因流式提前执行,续写前按调用指纹去重,避免重复副作用。

验收标准:三类断点各重复若干次,报告每种方式的恢复成功率、相对整轮重发节省的输出 token、补完后参数的合法率与语义正确率(拼接处容易多出空白或重复字符,合法不等于正确),以及重复副作用次数。同时记录哪些断点在某些厂商上无法复现,以及降级路径是否可用。

七、本章总结

第五章真正想说明的是:Coding Agent 的能力来源于模型,但可靠性来源于 Harness。 模型负责理解需求、生成方案和代码,Harness 通过项目指令、工具边界、测试、Lint、CI、Git、沙盒和错误恢复机制,把模型的开放生成能力限制在一个可验证、可恢复的工程闭环中。

从通用 Agent 的角度看,代码生成之所以重要,是因为它不仅能写程序,还能用于精确计算、业务规则校验、多媒体生成、系统适配、生成式 UI,以及创建和修复 Agent 自身。代码因此不是普通工具,而是一种可以继续创造工具、规则和系统的"元能力"。

本章最值得记住的是:计划先于行动,验证决定完成;错误处理要覆盖检测、恢复、接管和终止整个循环;能编码化的约束,不要只写在 Prompt 里。

相关推荐
http阿拉丁神猫5 小时前
Go语言入门笔记:从基础到开发环境搭建(二)
开发语言·笔记·golang
sell-cloud6 小时前
Prompt Engineering要凉了?Fable 5.5能“猜“意图,但银行正在封杀AI Agent——企业AI的真正护城河是什么
人工智能·prompt
Navicat中国7 小时前
Navicat 18 内置 AI 助手让数据库开发从繁琐操作迈向智能协作
大数据·人工智能·数据库开发
4SAPI8 小时前
多模型请求路由怎么设计?成本、速度和能力如何平衡
人工智能·python·gateway·php
魔乐社区8 小时前
翼芯・智算测试与适配优化专区,让国产算力“选得准、跑得通、用得好“
人工智能·开源·大模型
HRBorderInsight8 小时前
马来西亚人均收入一个月多少钱,个人薪资和家庭收入要分开看
经验分享·笔记
又見山10 小时前
本地 Markdown 与数据主权:你的笔记到底属于谁
笔记·obsidian
XCDoris10 小时前
郴州logo设计机构
大数据·人工智能·python
Goboy10 小时前
一个人带三个Agent,我用全新TRAE从0到1搭了个资讯周刊站
人工智能·trae
俊哥V10 小时前
每日 AI 研究简报 · 2026-10-19
人工智能·ai