构建一个可靠的 Coding Agent
从最小工具箱开始,到架构、流程、Harness、故障恢复、实现细节、搜索、编辑与安全。
Coding 是每个 Agent 的基础能力
以“整理仓库里所有遗留的 TODO 注释,按优先级分类并生成 issue”为例,要用到五类操作:浏览目录(ls/glob)、读代码(read)、改文件(edit/write)、跑命令(bash)、查模式(grep/search)。几乎所有 Coding Agent 的核心动作都在这五类里。在当前 SOTA 模型下,支持这五类操作只需要七个工具。
七个简单工具组合起来就能完成很多不同的任务。遇到新需求时,Agent 可以自己写代码来扩展能力边界。
七个核心工具 · 看它们怎么配合
主要覆盖“感知”和“执行”两类为什么是七个
其实一个 Bash Shell 就够了。OpenAI Codex 只提供 Bash,文件读写和查找都靠它完成。书里列七个,是为了把 Coding Agent 需要的基本能力讲清楚。
和第四章的分类不同
第四章按调用方向和作用性质把工具分为感知、执行、协作、事件触发、用户沟通五类。这七个工具主要属于感知和执行,其余三类需要别的工具提供,但它们不是 Coding Agent 的核心。
写代码是通用解题手段
数学推理可以交给求解器算精确答案,业务规则写成代码比自然语言精确,缺工具可以临时写一个,数据格式变了可以现场生成解析逻辑。这几种用法在 5.2 展开。
从 Manus 到 OpenClaw:通用 Agent 的 Coding 内核
这类产品把 Deep Research、Computer Use 和 Coding 三种能力放进同一个系统。核心之所以是 Coding,是因为高效的内容生成几乎最后都要靠代码完成:
PPT / Word
本质是 OOXML 格式的代码。
PDF 报告
可以由 Markdown、HTML 或 LaTeX 生成。
数据与图表
用 Python 脚本完成。
调研与 GUI
搜索和综合靠代码发 Web 请求并解析。成功的浏览器操作序列可以固化成代码(第九章)。
Computer Use 更通用,但成本、延迟和稳定性都比不上直接用代码或 API 做同样的事。代码生成效率最高、成本最低、可复用性最强。
图 5-1 · OpenClaw 架构,以及一次“分析上季度销售数据”的执行过程
在整个过程里,文件系统都是信息流转的枢纽:记忆从文件读出,产物写进文件,经验也保存成文件。
为什么用 Markdown,不用向量数据库
- 用户能直接打开、修改 Agent 的记忆,记错了删掉那一行就行
- 天然保留时间顺序,不会出现语义检索里的时间混淆
- 可以用 Git 做版本控制和回滚
能写文件,就能改自己的外部产物
例如第一次给某家银行打电话时,发现对方要提供开户行地址才能验证身份,Agent 可以先把这个发现记下来。一条记录什么时候能当作可靠的知识、指令或程序,还要靠更多轨迹和结果来验证(第九章“持续进化”)。
适用边界
开放任务型(深度调研、内容生成、数据处理):需要的工具无法事先列全,代码是架构中枢。
垂直领域型(如客服):任务空间相对封闭,架构围绕业务流程、领域工具和对话策略来搭,代码只是工具箱里的一件工具。即使这样,它仍然是精确计算、数据处理和规则校验离不开的基础能力。
Coding Agent 的整体流程
推荐的工程化流程有五个阶段:项目文档化 → 需求理解 → 设计文档 → 编码与测试 → 审查与交付。每个阶段内部还有闭环反馈。实际产品(如 Claude Code、Codex)会按需裁剪:修简单 bug 时跳过设计文档,只有任务复杂、影响面大时才把各阶段完整走一遍。
完成的标准是“测试通过”,不是“代码写完”。Coding Agent 最常见的偷懒,就是写完代码不跑测试就报告完成。
图 5-2 · 五阶段工作流模拟器
计划先于行动 · 验证贯穿始终 · 文档与代码共同演化项目指令文件:项目级的系统提示词
CLAUDE.md、AGENTS.md、.cursorrules 已经是事实标准,每次会话开始时自动注入上下文。README 写给人看,指令文件写的是给 Agent 的行为约定:
# CLAUDE.md - 用 pnpm test 而不是 npm test - 禁用 any 类型 - 不要改动 migrations/ 目录
SOUL.md 约定“Agent 是谁”,项目指令文件约定“在这个项目里该怎么干活”。它的内容不随任务变化,是最省的稳定前缀,对 KV Cache 友好。
各阶段要点
- 文档化:关键文档缺失时,先读代码库,写出架构概览、目录结构和测试指南。知识显式写出来,协作才高效。
- 需求澄清:“优化性能”要先问清目标(响应时间、内存还是吞吐)、能接受什么代价、瓶颈在哪。
- 设计文档:审查一份简洁的设计比审查几百行代码容易得多,是人类最省力的介入点。
- 测试与自审:测试失败就分析、定位、修改,直到全部通过;再通过阅读代码、跑 lint 或调用审查子 Agent 自审。
- 文档同步:过时的文档比没有文档更糟。
AI-ready 的简单指标
一个远程新人只靠代码仓库和文档,能不能独立开展工作?决策记在文档里,上下文写在 issue 和 PR 描述里,经验沉淀在开发者指南里,Agent 才读得到。对远程工作友好的团队,往往也对 AI Agent 友好。
“先读多少再动手”是模型学到的策略
有的模型改之前会广泛阅读目录、实现、调用方和测试;有的只读几个最相关的文件就提交补丁,把编译和测试反馈当成调查的一部分。换 Harness 后这个阈值可能不变,同一 Harness 换模型时却可能改变。Harness 能放大或抑制它,但它不一定来自 Harness(第七、八章)。
Harness 工程在 Coding Agent 中的实践
Agent = Model + Harness。Harness 包括上下文与工具(让 Agent 能做事),以及约束、验证与纠正(让 Agent 不做错事)。Agent 能不能稳定运行,往往不取决于模型多强,而取决于这套基础设施是否扎实。在编码场景里,它落成四个组件:
验收基线
什么算做完:测试套件、CI 管道、代码审查标准。
执行边界
能碰什么、不能碰什么:模块边界、依赖规则、权限控制。
反馈信号
自动判断对错:Linter 输出、测试结果、类型检查错误。
回退手段
出了问题怎么恢复:Git、沙盒隔离、快照回滚。
表 5-1 · 任务清晰度 × 验证自动化程度
点击格子查看Coding Agent 是目前最成熟的一类 Agent,原因不是代码模型特别强,而是软件工程几十年积累下来的测试、类型检查、Linter、Git,本身就构成了一套强大的 Harness。
大规模代码迁移
一家大型科技公司的公开分享。关键在 Harness 做对三件事:知识存放在代码库里(Agent 看不到就等于不存在);约束写进 Linter 和 CI,不写在文档里;验证和纠正全链路自动化。
LangChain
只优化 Harness(系统提示词、工具中间件、自验证循环),就显著提升了基准表现。用 Agent 分析失败轨迹来改进 Harness,让这项工作从凭经验变成靠数据。
Anthropic
长任务分给两个角色:初始化 Agent 把任务拆成清单,执行 Agent 逐步推进,并把中间成果留给下一轮。这样解决了“一次想做太多”和“过早声称完成”。
可迁移到所有 Agent 的四条原则
- 约束优先于指导:能用代码强制的规则就不写成文档建议。前者是“做不了”,后者只是“建议别做”。
- 验证要自动化:人工审查无法扩展。
- 反馈越快、越结构化越好:错误信息越详细、离出错时刻越近,纠正效率越高。
- 回退要可靠:在安全网里,Agent 才能大胆试错。
约束动作,不只约束结果
修数据库故障时把库删了重建,“修复”确实生效了,数据却没了;修编译错误时把代码全删了重写,编译确实过了,实现却没了。这类破坏性捷径总是存在,即使写进评估指标也常被绕过(第八章 reward hacking)。所以生产级 Harness 要对 rm -rf、删生产数据、覆盖未读文件这类动作专门检查并审批。在训练侧,对应的是第八章的 RLVP:奖励结果,惩罚路径。
故障边界控制 · 同时读三个文件,其中一个找不到
点击某一行,切换成功 / 失败原则:故障只在同一批并行调用里传播,不上升到父级操作。依赖失败结果的调用会被级联中止,彼此独立的调用继续执行。每个工具要声明自己能否并发,默认不能,这样失败时是安全的。
故障与错误恢复
第一章的消融实验里,只少了一条工具结果反馈,Agent 就陷入了无限循环。生产环境的故障种类更多。本节回答三个问题:会遇到哪些故障,怎么检测和恢复,什么时候必须终止。下面的分析基于对 Claude Code 等生产级 Agent 源码的研究,只提炼其中稳定的工程原则。
API 层
限流(HTTP 429)、过载、超时、断连、输出触顶被截断。和任务内容无关,是基础设施的噪声。
工具层
幻觉调用、参数畸形、执行抛异常。最危险的是工具反复返回同一个错误,模型却原样反复重试。
上下文层
窗口溢出、压缩失败、轨迹结构损坏(例如工具调用缺少配对的结果)。
控制流层
死循环:反复做同样的事却没有进展。死亡螺旋:恢复逻辑自己调用 LLM 又出错,引起连锁反应。
先分类,再决定怎么恢复
错误 → 恢复策略映射表第一个问题不是“要不要重试”,而是“值不值得重试”。不可重试的错误重试多少次结果都一样,必须改变输入或策略。还要检测模式:对“工具名 + 参数”算指纹,同一指纹反复出现,说明陷入了无进展循环;每条恢复路径各自维护连续失败计数,作为熔断的依据。
恢复要分级升级:能在低一级解决,就不升级
恢复成功之前,错误不暴露给消费者为什么退避要叠加随机抖动
示意模拟 · 单位为时间片不加抖动时,所有客户端在同一时刻重试,形成一波波尖峰,造成二次拥塞。另外两个细节:要遵守服务端返回的等待时长提示;要区分前台和后台调用。主循环请求失败要重试,标题生成、输入建议这类后台辅助调用失败就直接放弃,否则会挤占主链路配额,形成“重试放大”。
静默卡死:通着但不出水
示意时间线SDK 的超时往往只覆盖初始连接,不覆盖传输过程。每个长连接都需要活性信号,不能只靠连接超时。
熔断阈值来自生产数据
Claude Code 压缩熔断 · 对数刻度第 3 次大约是两种情况的分界点:之前绝大多数故障已经恢复,之后再重试基本没有希望。同样,权限分类连续失败就回退到人工询问,输出接续最多尝试固定轮数。
死亡螺旋 · 一个真实发生过的连锁故障
宁可丢掉一次辅助功能(比如自动提取记忆),也不在错误路径上调用模型。所有自动化机制之上,还要有全局的终止与升级条件:最大迭代轮数、会话预算上限、连续失败超过阈值就升级到人工。
工具层错误:不终止,变成模型的输入
幻觉调用收到“工具不存在”的结构化错误;参数校验失败收到附带输入约束的提示;本该是对象却输出成字符串的畸形参数,在执行前先用程序修复。它们都作为普通工具结果进入上下文,由模型下一轮自行纠正。喂回的错误越具体,模型自我纠正的成功率越高。
产品模式宽容,训练模式严格
发现工具调用缺少配对的结果时,系统在注入上下文前自动修复,不把结构异常抛给模型或用户。有的生产级 Agent 同时跑产品模式和训练数据收集模式:产品模式下可以用占位符补上缺失的消息,训练模式下拒绝修复,因为合成的占位符会污染训练数据。
接管:把没跑完的轨迹交给另一家模型
实验 5-1 · 下表是正文推理,实测结果须如实记录真正的障碍不是接口地址不同,而是轨迹里有一部分只属于原厂商。工具调用和结果各家结构不同、语义一致,重新渲染就行。难办的是思考:可读的文字换一家仍然读得懂,厂商附在上面的凭证换一家就失效。凭证还可能附在工具调用上。
所以轨迹应当存成一份中立格式:思考拆成可移植的文字和不可移植的凭证,工具调用只记名称和参数,标识符在渲染请求时按目标厂商重新生成。这份中立轨迹也是第七章评估重放、第八章训练样本、第九章经验提取共用的产物。
Coding Agent 的实现技巧
以下五个技巧都是为了在不降低思考质量的前提下,提高响应速度、减少上下文消耗。它们共同指向一个目标:让 Agent 像经验丰富的开发者那样流畅地工作。
① 并行工具调用 + 流式执行
示意单位。模型在一次推理里依次输出“搜索代码、查配置文件、读日志”三个调用。第一个调用的参数一完整生成并通过校验就开始执行,和后两个调用的生成过程重叠进行;彼此独立的调用还可以并行。
② 长输出只留首尾
第四章的截断与持久化前几行通常包含错误上下文,后几行通常是错误总结。读文件也一样:支持按行号范围读取(比如只读第 100–150 行),并在每行前加上实际行号,模型就能精确引用“src/main.py 第 42 行”。
③ 环境信息放进 Agent 状态栏
追加在上下文末尾<agent_status> cwd: /home/dev/proj git branch: feature/rate-limit recent commits: a1b2c3d add retry middleware 9f8e7d6 split db module staged: src/api.py unstaged: tests/test_api.py </agent_status>
示意内容。四项关键信息:当前工作目录、git 分支、最近提交、已暂存和未暂存的变更。它们不能硬编码进静态系统提示词,否则会破坏 KV Cache,要作为动态的、追加式的状态栏,每次推理前实时生成。
④ 持久化终端会话
激活虚拟环境这类操作只在当前 shell 里有效。持久会话应作为默认模式,同时保留启动隔离终端的能力,用来跑并行任务。
⑤ 即时语法反馈
示意。写文件一完成,工具层就自动运行 linter,把结果放进工具返回值。就像在 IDE 里少打一个括号,编辑器马上画出红线。错误在引入的那一刻就被修正,不用等到跑测试才发现。
搜索工具:从粗到细,从语义到语法
图 5-3 · 四类互补的搜索工具
选一个需求,看该用哪一类路线之争:要不要建嵌入索引
以 Claude Code 为代表的终端型 Agent 刻意不建索引,靠 agentic 的 grep + glob 现场检索,既不用维护随代码变化而过时的索引,也省掉了一整套索引基础设施。Cursor 这类 IDE 早期走相反路线,为跨文件的语义召回付出建索引的成本。现在 Cursor 等 IDE 也改成了 grep + glob 现场检索。
语义搜索的两个关键
结构感知分块:按函数、类、方法这样完整的语义单元切分,而不是按固定字符数切。混合检索:向量嵌入找语义相近但用词不同的代码(搜“验证用户身份”能找到 check_credentials),关键词匹配精确命中函数名,两路并行,再用 reranker 合并排序。
文件编辑工具:怎么告诉系统“改哪里、怎么改”
难点不在编辑操作本身,而在人类语言的表达和机器的精确执行之间存在根本张力。图 5-4 比较了五种方案。下面的计算器估算每种方案下模型需要输出多少字符。
图 5-4 · 五种编辑方案的输出量与风险
示意估算:每行按 40 字符计| 方案 | 做法 | 优势 | 劣势 | 实际采用 |
|---|---|---|---|---|
| Old → New | 给出要替换的原文和新文本,框架做精确查找替换 | 可预测、无歧义:原文唯一就成功,否则失败 | 大段删除要完整输出原文,差一个字符就匹配失败 | Claude Code、Codex、今天的 Cursor |
| Diff + Apply Model | 输出 diff 或带“此处不变”的骨架,由更小更快的应用模型合并 | 关注点分离 | 描述和实际代码稍有出入,或有多段相似代码,就可能合并错位 | Cursor 早期,后来因基础模型变强而放弃 |
| 行号定位 | “删除第 X 到 Y 行,插入新内容” | 大段删除只要两个数字 | 每次编辑后行号都会变,多处编辑要统一用初始行号 | IDE 深度集成场景 |
| 首尾匹配 | 只给要删内容的开头几行和结尾几行 | 兼具文本替换的可靠和行号方案的效率 | 首尾组合在文件中必须唯一 | 部分定制方案 |
| 类 Vim 命令 | 42G、cw、dd、yy/p 等 | 移动、重组代码高效 | 学习负担大,弱模型错误率明显上升;一次输出多条命令时很难预算行号 | 实验性方案 |
一次思考里输出两处编辑:行号漂移
编辑 A:在第 3 行后插入 2 行 · 编辑 B:把第 8 行改成 return x + yVim 命令为什么不适合今天的模型
Vim 这类编辑器是为人类设计的:人要不断看到当前状态,再规划下一步的小操作。今天的模型是先长时间思考,再批量完成较复杂的操作(比如写几百行代码)。
Coding Agent 的安全
Coding Agent 能读写文件、执行命令、访问网络,一旦被注入恶意指令,就可能造成不可逆的损失。本节按四步展开:威胁模型 → 隔离兜底 → 执行期防御 → 信任与忠诚。
“致命三要素”+ 持久记忆
Simon Willison · 第四项是本书补充的放大器攻击路径:恶意指令藏在不受信任的内容里进入 Agent,驱使它读取私有数据,再从对外通道传出去。持久记忆不是并列的第四个必要条件,而是放大器,能把一次性攻击变成跨会话潜伏的长期威胁。四点对应四类边界:数据边界、输入信任边界、输出影响边界、跨会话边界。OpenClaw 这类全权限本地 Agent 四类全占。
单靠输入过滤基本挡不住提示注入。重点不是识别所有攻击,而是让 Agent 即使被注入,也没有机会把危险动作真正执行出去。
沿攻击链逐层加防线
打开防线,看攻击在哪一步被拦下网络出口控制
最容易被忽视,也最关键。默认断网,按需通过白名单代理放行少数目的地:包管理源、文档站点、任务明确需要的 API。即使恶意代码在沙盒里读到了敏感数据,没有出口也传不出去。
文件系统隔离
源码目录只读挂载,补丁经审查后落盘,或者把副本挂进可写工作区。生成物和中间文件放在单独的可写目录。~/.ssh、密钥、token 根本不挂载。
资源限额与超时
用 CPU、内存、磁盘配额和超时,防死循环、fork 炸弹和无限写盘。超时后要返回结构化错误,例如“执行超过 120 秒被终止,最后输出如下……”,不能静默杀掉进程。
语义解析,而不是关键字黑名单
黑名单:禁用命令名 rmShell 可以靠管道、子 shell、变量展开组合出无数写法,任何静态规则都能被绕过。语义解析要理解每个命令的参数类型和消费规则,比如哪个标志位会吃掉下一个参数,从而发现藏在里面的危险载荷。
Agent 为谁效忠:多方委托下的忠诚度光谱
替主人砍价时,对面坐着的是交涉对手一套行之有效的忠诚度守则
- 保护主人的私密信息,连这些信息是否存在都不透露
- 拒绝时不逐条念出拒绝清单,那本身就是泄露
- 私下的底线不等于对外的立场
- 只执行主人明确、具体的指令
- 顶住重复施压
仓库里读到的不可信内容、工具输出、第三方 MCP 服务器发来的指令,都是想策反 Agent 的“对手”。它们一律降级为“可参考,但不具备指令效力”的数据。
代码:通用 Agent 的元能力
普通能力是做某件具体的事。元能力是创造其他能力的能力:Agent 用它当场写出新工具(脚本、API 调用序列)、新约束(断言、校验规则)和新的表达形式(HTML 表单、PPT、视频帧),不必事先把所有能力都做好。下面六节按作用对象由内向外排列:思维本身 → 业务规则 → 内容呈现 → 系统接口 → 用户界面 → Agent 自身。
代码作为思考工具
模型的思考本质上是概率性的、近似的,数学和逻辑问题却要求确定、精确的答案。所以让 LLM 负责理解问题、写出代码,让解释器负责精确计算,两者各司其职。
例题 · 只选物理、没选数学的有多少人
默认值即书中题设纯自然语言推理
代码推理
Stephen Wolfram 的洞察:Wolfram Alpha 这类符号计算系统算得准,但自然语言理解脆弱,问法稍变就可能解析失败,也处理不了开放域的多步推理。LLM 恰好补上这个短板。
脚手架该多厚,取决于模型多强
实验 5-3 · 5-4实验 5-4 用 python-constraint 解骑士与无赖谜题(K&K 数据集),要求代码辅助模式准确率达到 90% 以上。实验刻意选用较弱的模型来放大对照。同一套脚手架配上不同能力的模型,结论可能截然相反,评估 Agent 技术时这个前提常被忽视。
代码作为业务规则的约束
“购买后 7 天内可退款”看似清楚:7 天是自然日还是工作日?“购买”指下单还是发货?代码要么跑通,要么报错,不会模棱两可。自然语言规则和代码化规则是互补的:写在提示词里,模型能向用户解释政策、找变通方案(例如改签而不是取消);编码成校验,结果精确、确定,适合多条件组合、时间计算和跨数据源验证。
代码化规则的真正价值不在省 token,而在防止取消订单、转出资金、删除数据这类不可逆的错误操作。
τ-bench 航空取消政策 · 校验与执行合并在一个工具里
政策事实一律查库,时间取服务端时钟第一重 · 提示词里的自然语言规则
帮模型理解政策、向用户解释。
第二重 · 参数即 checklist
为了填好 expected_*,模型得先查订单、逐条核对。查到经济舱没买保险时,它很可能在准备调用时就想起规则 5,于是不发起调用,直接建议“先购买保险再取消,或者改签”。这一层用来引导思考,不承担安全责任。
第三重 · 服务端真值校验守门
模型可能幻觉,也可能被注入操纵,同一上下文里的 Agent 难以自证清白。最后一道防线必须建在模型无法伪造的数据上。“独立验证”里的独立,既指独立的模型,也指独立的数据来源。
实验 5-5:在 τ-bench 航空场景,用 Qwen3-4B 对照“纯自然语言规则”和“三重保障”,看任务成功率、违规次数、无效调用次数,并统计 expected_* 自报值和数据库真值不一致的比例。
代码驱动的多媒体生成
演示文稿、报告、交互应用,底层都是代码:HTML 描述结构,CSS 控制样式,JS 实现交互。GUI 的所见即所得要求视觉理解和精确的坐标定位;改用代码,Agent 就绕开了视觉定位难题,每个元素的位置、样式和内容都有明确定义。
图 5-5 · PPT 生成的提议者—审核者机制
两类显式终止条件:审核者判定“质量达标”,或者达到最大轮数(例如 5 轮),用预算上限防止循环失控。审核者给出结构化建议,带页码、问题类型、严重程度,而不是一句模糊的“不好看”。
为什么拆成两个 Agent:上下文里要装多少张截图
Reviewer 每次只看最新版本的渲染图,不受旧版本干扰;Proposer 只积累结构化的文字反馈,token 少,也更好推理。它和第四章的事前审批同属提议者—审核者模式,区别是这里要多轮循环,而且审核者能看到提议者看不到的新信息(渲染结果)。建议让两者使用不同的模型家族,降低犯同类错误的概率。
图 5-6 · 论文 → 讲解视频的端到端流水线
实验 5-8 · 视频剪辑:两步定位“把冲浪部分剪出来”
示意素材:只有“冲浪在 40–110 秒”来自书中例子视频分析封装成子 Agent,大量截图就不会占用主 Agent 的上下文。定位之后生成 Blender Python API 脚本;Reviewer 先做快速预览、检查关键帧,达标后再完整渲染。验收要求起止点误差不超过 3 秒。
3D 与工业零件:代码生成还是生成模型
实验 5-9 · 法兰盘路线 A · CadQuery / OpenSCAD 代码
路线 B · 3D 生成模型(如混元 3D)
一看:有没有紧凑的精确描述
法兰盘有五六个参数就能完整定义,代码是它的无损表达。一盆绿植、一块太湖石、一张人脸的细节数不清,内在复杂度接近无限。
二看:精度要求和可验证性
孔径 5 mm、公差 ±0.05 mm,差一丝就是废品。代码生成的零件可以程序化验证,生成模型的产物没法直接和规格核对。
表示形式与可编辑性
制造需要 B-rep 参数化实体(STEP 文件里有特征树和尺寸参数,可以直接驱动 CNC),生成模型输出的是三角面片网格。两条路也能混着走:几何体用代码参数化,表面纹理交给生成模型。对照组生成一盆绿植时,优劣正好反过来。
代码作为系统适配器
外部服务常常没有现成的 SDK,文档缺失、返回格式不标准、字段随版本漂移。Agent 不必等人写好适配层,可以当场读接口文档或观察一两条真实响应,生成 HTTP 客户端、鉴权头、解析和格式转换代码。哪里接不上,就现场写一段胶水补上。对完全没有 API 的系统,可以先用 Computer Use 操作界面,再把成功的操作序列固化成 RPA 代码(第九章)。
实验 5-10 · 自适应日志解析:遇到新格式不报错,而是自我修复
图 5-7 · 生产日志智能诊断流水线
把人工诊断从小时级降到分钟级Agent 读生产轨迹,结合架构文档和 PRD 判断执行流程是否符合预期,定位出问题的环节和模块,生成结构化报告和回归测试(测试引用轨迹 ID 和交互轮次,由测试框架自动重放),最后通过 MCP 对接 GitHub,创建 Issue 并分配给相关开发者。
代码作为生成式 UI
纯文本对话是线性的:收集结构化信息要反复问答,呈现复杂数据关系时表达力有限,让用户在多个选项里选择时,文本列表不如界面直观。Agent 可以动态生成表单、交互图表甚至完整的 Web 应用,这叫生成式 UI。
A2UI 类声明式协议:只能点菜单上的菜
客户端用受信任的组件目录渲染如果 Agent 直接生成 HTML/JS,被提示注入后可能写出窃取数据的脚本。成因是提示注入,在浏览器里的效果类似 XSS,但不能把整个攻击都叫作 XSS。声明式协议只让 Agent 输出 JSON 清单,客户端用自己的原生组件渲染,因此能跨平台(React、Flutter、原生应用),也能用流式 JSONL 增量生成。注意 AG-UI(CopilotKit)不是界面描述语言,而是把消息、工具调用和状态补丁流式推到前端的事件/传输协议,甚至可以承载 A2UI 载荷。两者是互补关系,不是同类。表单、表格、卡片这类标准交互适合用声明式协议;自定义可视化、游戏界面仍然直接生成代码更灵活。
图 5-8 · 动态表单澄清意图:“我想订去北京的机票”
实验 5-12级联逻辑:只有选“往返”时才显示返程日期。表单代码由 LLM 动态生成。
图 5-9 · SQL 查询:数据绕过 LLM 直达前端
build_artifact(
type="sql",
code="SELECT dept, COUNT(*) as cnt
FROM employees GROUP BY dept")
build_artifact(type="chart", code="bar(data)")传统模式让 LLM 读几千行结果再用文字复述,耗 token、耗时间,而且 LLM“抄写”数据很容易出错。Artifact 模式下 LLM 只写代码,不参与数据传递,这就是“代码生成作为接口”的精髓。
生成的 SQL 也不能直接执行
Artifact 模式缩短了数据路径,但不能替代权限检查与执行隔离执行层还应当:使用只读数据库账号;限制查询时间、返回行数、可访问的表和时间范围;可视化代码在隔离网络和文件系统的沙盒里运行,只能输出规定格式的结果。
实验 5-13 · 自然语言交互的 ERP Agent:员工表 + 工资表,自动回答 10 个问题
员工表:员工 ID、姓名、部门、级别、入职日期、离职日期(为空表示在职);工资表:员工 ID、发薪日期、工资(每月一条)。① 平均每个员工在职多久?② 每个部门有多少在职员工?③ 哪个部门员工平均级别最高?④ 每个部门今年和去年各新入职多少人?⑤ 前年 3 月到去年 5 月,A 部门的平均工资?⑥ 去年 A、B 部门谁的平均工资高?⑦ 今年每个级别的平均工资?⑧ 入职一年内、一到两年、两到三年的员工最近一个月平均工资?⑨ 去年到今年涨薪幅度最大的 10 位员工?⑩ 有没有拖欠工资(某月在职但未发薪)?
用 HTML 交付成果,替代 Markdown 汇报
相比一页页翻读的 Markdown,HTML 有三个优势:用可操作的演示说明系统怎么运行;用图表和可下钻的组件呈现数据;网站可以随工作推进持续完善,而不是结束时一次性产出的死物。作者为每个研究项目维护一个交互式网站,用来追溯实验数据(prompt、LLM 原始回复、judge 打分)、监控训练的“内科指标”(损失、梯度范数、学习率、困惑度、奖励、KL 散度、策略熵),以及展示系统的运行原理。你正在看的这个页面就是这种交付形式。
动态生成软件:从“一刀切”到“千人千面”
Anthropic 的 “Imagine with Claude” 实时生成界面和交互逻辑,用户操作后 Claude 改代码、生成新界面。这种完全动态的模式成本和延迟较高,更适合作为展示能力边界的实验。更务实的是“半定制”:在已有框架上改,比如“把按钮改成蓝色”“在侧边栏加快捷菜单”,通过 HMR 热加载即时生效(实验 5-14:React + FastAPI,开发模式热加载)。
把信任边界下移到数据层
实验 5-15 · 招聘系统目标不是“保证 AI 每次都把权限检查写对”,而是“即使 AI 写错了代码,权限约束也绕不过去”。具体做法:行级安全策略按租户隔离;约束和校验器拒绝非法状态;受控视图、存储过程或数据访问服务只暴露允许的操作;每次读写都带上由受信任运行时绑定的访问上下文(用户、租户、角色或 Agent 身份)。生成代码不能伪造身份,拿不到高权限凭证,也不能绕过数据层直连数据库。应用层仍可以做预检查,以便早点给用户反馈,但最终裁决权在数据层。
代码创造代码:Agent 自举
DNA 自复制
随机变异 + 自然选择。不理解自身,不能定向修改,经历了 37 亿年盲目试错。
Agent 自举
理解代码 + 定向设计。理解自身机制,有目的地创造,继承最佳实践。
OpenClaw doctor:Agent 修复 Agent 的运行环境
确定性检查打底,LLM 兜底长尾不要夸大 Agent 在这里的作用:高频问题本来就有明确的检测规则和固定的修复动作,和传统运维脚本没有本质区别。真正体现 Agent 能力的是第二层,处理确定性规则覆盖不到的疑难问题。安全的修复(配置归一化、锁文件清理)自动执行;有风险的操作(重启服务、强制覆盖配置)需要用户确认。
图 5-10 · 复制 + 定向修改:航空客服 → 电商客服
架构框架完全继承,所以质量有保障从零写 Agent 时的常见缺陷
- 上下文管理随意:不用标准上下文格式,把轨迹转成纯文本塞进上下文,放弃了结构化消息带来的 KV Cache 优化,工具调用循环有边界 bug
- 工具设计不规范:描述简略,缺少使用边界和负面清单,参数没有具体示例
- 技术选型滞后:倾向于用训练数据里最常见、但已经过时的模型和 API。解决办法是维护 SOTA 知识库,或者让 Agent 能搜索
- 脱离外部生态:使用已废弃的 API、不再维护的库或有缺陷的模式
基于范例生成
最有效的办法不是在提示词里穷尽所有规则,而是给出高质量的 Agent 实现作为范例,让 Agent 在它的基础上改。范例本身就带着最佳实践,好的架构选择会自然保留下来。接到“开发新 Agent”的任务时,先复制自己(或另一个验证过的实现)的代码,再调整系统提示词、增删工具、改业务逻辑,保留架构框架。这就像基因的复制加变异。
图 5-11 · 能创造 Agent 的 Agent
实验 5-16:用 ch5/coding-agent 项目本身作为范例,对比“从零生成”和“基于范例修改”两种方式在质量和效率上的差异。验收时检查标准消息格式、工具调用协议、当前推荐的模型和 API,以及多轮对话中上下文和状态管理是否正确。
代码是 Agent 形式化思考和精确表达的语言
一、成熟度来自 Harness
Coding Agent 成熟度高,是因为测试套件、类型系统、版本控制天然构成了 Harness。这个结论可以推广到其他 Agent 场景。
二、可靠性来自故障路径
Agent 是否可靠,不取决于模型犯不犯错,而取决于每类故障是否都有对应的检测、恢复、接管与终止路径。
三、代码是元能力
代码既是完成任务的手段,也是积累知识、创造工具、优化自身的机制。
| 方向 | 代码做什么 | 本页的交互 |
|---|---|---|
| ① 思考工具 | 用符号计算和约束求解弥补概率思考的不足 | 选课例题 |
| ② 业务规则约束 | 无歧义地表达规则,在不可逆操作前提供确定性防线 | 取消政策守门员 |
| ③ 多媒体生成 | 提议者—审核者生成 PPT 和视频;按内在复杂度和精度要求选择代码或生成模型 | 截图计数、视频定位、法兰盘 |
| ④ 系统适配器 | 跟随格式演化,自动解析日志、诊断问题 | 自修复解析器 |
| ⑤ 生成式 UI | 动态表单、可视化,乃至可定制的完整应用 | 表单、SQL、数据层边界 |
| ⑥ Agent 自举 | 用代码修复和创造同类 Agent | doctor、复制 + 修改 |
到这里,上下文、知识、工具和代码能力已经组合成通用 Agent 的基础架构。但前五章仍然默认 Agent 和世界轮流行动。第六章会把观察与动作空间扩展到异步事件、语音、屏幕和物理世界,第七章再转向评估与持续改进。
容易想错的地方
$(echo rm)、find -exec、curl -o 都能绕过,要做语义解析思考题
1 ★★ 代码执行带来漏洞、死循环、资源耗尽等风险,沙盒又限制了能力(比如不能访问网络或文件系统)。安全和能力的最优平衡点在哪里?
可以从 5.1.9 的三项隔离出发:默认断网 + 白名单代理、源码只读 + 可写工作区 + 凭证不挂载、资源限额 + 结构化超时错误。思考哪些能力可以按任务放开,以及放开后由哪一层兜底。
2 ★★★ 每次自举都可能引入新的偏差或错误,这种错误会在代际间累积吗?怎样防止自举退化?
对比“从零生成”和“基于范例修改”,再想想:范例本身由谁验证?实验 5-16 的验收项(标准消息格式、工具调用协议、推荐的模型与 API、多轮状态管理)能否成为每一代的回归测试?
3 ★★ 日志解析能自动跟随格式演化。如果格式变化本身是个 bug,Agent 的适应性反而会掩盖问题。怎么区分“需要适应的变化”和“需要报告的异常”?
结合 5.2.4 的诊断流水线:适配之外,是否应把“格式发生了变化”本身也作为一条结构化报告,交给架构文档和 PRD 判断是否符合预期?
4 ★★ 如果 Reviewer 的审美和目标用户不一致,提议者—审核者循环会收敛到错误的局部最优。怎样让用户偏好也参与进来?
回顾提议者—审核者的设计原则:共享目标约束,反馈作为特殊事件加入 Proposer 的轨迹。用户反馈能否以同样的结构化形式进入循环,并改变 Reviewer 的审查维度?
5 ★★ 经验被沉淀进知识库文件、架构文档、项目指令文件和固化的代码。如果再提炼成提示词规则,规则集会不断膨胀。怎样对规则做“垃圾回收”?为什么一次成功的代码修改还不能直接算作持续进化?
参考 5.1.2:一条记录什么时候足以成为可靠的知识、指令或程序,还要结合更多轨迹和结果来验证(第九章)。
6 ★ 你的团队在知识文档化方面,离“AI-ready”还有多远?最大的障碍是什么?
用本章的指标自测:一个远程新人只靠代码仓库和文档,能不能独立开展工作?
7 ★★★ 在同时具备致命三要素和持久记忆的生产环境里,你会如何设计安全策略?
可以从本页“沿攻击链逐层加防线”的演示出发,把每条防线对应到验证、执行、数据三个层面,再考虑写入 MEMORY.md 的内容要经过与外部内容同等的信任审查。
8 ★★ 与“Agent 直接给出答案”相比,“Agent 生成代码、系统执行代码”的 Artifact 模式有哪些优劣?SQL 可能有破坏性、HTML 可能有漏洞,怎么保证安全?
对照 5.2.5:只读账号、只允许批准过的 SELECT、拒绝 DDL/DML 和多语句、服务端参数化绑定、限制时间/行数/表/时间范围,可视化代码在沙盒里运行;界面优先使用声明式组件目录。
9 ★★ 把业务规则编码进工具内部基于数据库真值的校验,并用参数设计引导模型调用前核对条件,相比自然语言规则有什么优势和局限?
回到 5.2.2 的三重保障:哪一重负责解释和变通,哪一重负责引导思考,哪一重负责安全。代码化规则精确、确定,那么它在解释政策和寻找变通方案上欠缺什么?