编码智能体(Coding Agent)的核心组件与设计
本文旨在探讨编码智能体(coding agent)及智能体执行框架(agent harness)的整体设计:它们是什么、如何工作,以及在实际应用中各部分如何协同运作。我的《从零构建大语言模型》和《从零构建大型推理模型》两本书的读者经常询问关于智能体的问题,因此我觉得有必要写一篇可供参考的文章。
更广泛地看,智能体之所以成为热点,是因为近期实用 LLM 系统的重大进展不仅在于模型本身的改进,更在于我们如何使用它们。在许多实际应用中,外围系统——如工具调用、上下文管理和记忆机制——的作用与模型本身同等重要。这也解释了为何像 Claude Code 或 Codex 这样的系统,比直接在普通聊天界面中使用相同模型,显得更加强大。
本文将阐述编码智能体的六个核心构建模块。
Claude Code、Codex CLI 及其他编码智能体
你可能已经熟悉 Claude Code 或 Codex CLI,但为了便于理解,需要说明它们本质上是智能体化的编码工具。它们通过应用层(即所谓的智能体执行框架)封装 LLM,使其在编码任务中更便捷、表现更优。

编码智能体专为软件工程工作设计,其关键不仅在于模型选型,还在于外围系统的设计,包括代码仓库上下文、工具设计、提示缓存稳定性、记忆机制以及长会话的连续性。
这一区别至关重要,因为当人们讨论 LLM 的编码能力时,往往容易将模型、推理行为和智能体产品混为一谈。但在深入探讨编码智能体的具体细节之前,我想先简要厘清 LLM、推理模型和智能体这几个更广泛概念之间的区别。
LLM、推理模型与智能体之间的关系
LLM 是核心的下一个词元预测模型。推理模型本质上仍是 LLM,但通常经过了专门训练或提示词设计,使其在中间推理、验证或候选答案搜索上投入更多的推理时计算资源。
Agent 是建立在模型之上的一层,可以理解为围绕模型的控制系统。给定一个目标,Agent 层(或 harness)负责决定下一步检查什么、调用哪些工具、如何更新自身状态,以及何时停止等。
粗略来看,它们的关系可以这样理解:LLM 是引擎,推理模型是强化版引擎(更强大但使用成本更高),而 Agent harness 则帮助我们利用模型。这个类比并不完美,因为无论是传统 LLM 还是推理 LLM,都可以作为独立模型使用(例如在聊天界面或 Python 会话中),但我希望这能传达核心要点。

换句话说,Agent 是一个在环境中反复调用模型的系统。
简而言之,我们可以这样总结:
LLM: 基础模型
推理模型:优化用于输出中间推理轨迹并加强自我验证的 LLM
Agent: 结合模型、工具、记忆和环境反馈的循环系统
Agent harness: 围绕 Agent 的软件脚手架,负责管理上下文、工具调用、提示词、状态和控制流
Coding harness: Agent harness 的特例,即面向软件工程的任务专用 harness,管理代码上下文、工具、执行及迭代反馈
如前所述,在 agent 和编程工具领域,还有两个常见术语:agent harness 和(agentic)coding harness。Coding harness 是围绕模型搭建的软件框架,帮助它高效地编写和修改代码。Agent harness 范围更广一些,不限于编程场景(比如 OpenClaw)。Codex 和 Claude Code 就可以看作是 coding harness。
不管怎样,更强的 LLM 能为推理模型打下更好的基础(这需要额外的训练),而 harness 则能让这个推理模型发挥出更大潜力。
当然,LLM 和推理模型本身也能解决编程任务(不借助 harness),但编程工作并不只是生成下一个 token,还涉及大量其他环节:浏览仓库、搜索代码、查找函数、应用 diff、执行测试、检查报错,以及把所有相关信息维持在上下文里。(写过代码的人都明白,这些脑力活有多累,所以我们不喜欢在写代码时被打断 :)。)

关键在于:一个好的 coding harness 能让推理模型和非推理模型在普通聊天框里显得弱很多的能力被充分释放,因为它能帮忙做好上下文管理等各项工作。
什么是 Coding Harness
如上一节所说,所谓 harness,通常指的是围绕模型的那层软件,负责组装 prompt、暴露工具、跟踪文件状态、应用修改、执行命令、管理权限、缓存稳定前缀、存储记忆等等。
如今在使用 LLM 时,这一层相比直接提示模型或网页聊天 UI,更能决定用户的大部分体验(后者更接近“与上传文件聊天”)。
在我看来,当前原生 LLM 的能力大同小异(例如 GPT-5.4、Opus 4.6 和 GLM-5 的原生版本),因此 harness(工具框架)往往是区分一个 LLM 比另一个更好的关键因素。
这纯属猜测,但我怀疑如果将最新的顶级开源权重 LLM(如 GLM-5)放入类似的 harness,其表现很可能与 Codex 中的 GPT-5.4 或 Claude Code 中的 Claude Opus 4.6 相当。当然,针对特定 harness 的后训练通常也是有益的,例如 OpenAI 历史上就维护过 GPT-5.3 和 GPT-5.3-Codex 两个不同的变体。
在下一节,我将深入细节,使用我的Mini Coding Agent(https://github.com/rasbt/mini-coding-agent)来讨论 coding harness 的核心组件。

顺便提一下,为了简单起见,本文有时互换使用“coding agent”和“coding harness”这两个词。(严格来说,agent 是指由模型驱动的决策循环,而 harness 是提供上下文、工具和执行支持的外部软件脚手架。)

总之,编码智能体主要由以下六个组件构成。你可以查看我那个极简但功能完整、从零编写的迷你编码智能体(Mini Coding Agent)(采用纯 Python 实现)的源代码,以获取更具体的代码示例。代码通过注释标注了下方讨论的六个组件:
##############################
#### Six Agent Components ####
##############################
# 1) Live Repo Context -> WorkspaceContext
# 2) Prompt Shape And Cache Reuse -> build_prefix, memory_text, prompt
# 3) Structured Tools, Validation, And Permissions -> build_tools, run_tool, validate_tool, approve, parse, path, tool_*
# 4) Context Reduction And Output Management -> clip, history_text
# 5) Transcripts, Memory, And Resumption -> SessionStore, record, note_tool, ask, reset
# 6) Delegation And Bounded Subagents -> tool_delegate1. 实时仓库上下文
这可能是最显而易见的组件,但同时也是最重要的组件之一。
当用户说“修复测试”或“实现 xyz”时,模型应当知道当前是否处于 Git 仓库中、处于哪个分支、哪些项目文档可能包含操作说明等等。
这是因为这些细节经常变化,且会影响正确的行动方针。例如,“修复测试”并不是一条自包含的指令。如果智能体看到 AGENTS.md 或项目 README,它可能会学到该运行哪个测试命令等。如果它知道仓库根目录和布局,它就能去正确的地方查找,而不是盲目猜测。
此外,Git 分支、状态和提交记录有助于提供更多上下文,以便了解当前正在进行的变更以及应聚焦之处。

关键在于:coding agent 会在正式干活之前,先把信息收集好(以工作区摘要的形式存下这些“稳定事实”),这样每次收到新的 prompt 时就不会毫无上下文、从零开始。
2. Prompt 的组织与缓存复用
有了代码仓库的全貌之后,下一个问题是如何把这些信息喂给模型。上一张图展示的是简化版流程(“合并 prompt:前缀 + 请求”),但实际中,如果每次用户提问都重新拼接并重新处理整个工作区摘要,浪费就太大了。
原因很简单:coding 会话是重复性的,agent 规则基本不变,工具描述也基本不变,工作区摘要通常也(大体上)保持稳定。真正变化的往往只是最新的用户请求、最近的对话记录,以及短期记忆。
因此,“聪明”的运行时不会每一轮都把所有内容重新拼成一大段不加区分的 prompt,如下图所示。

这里与第 1 节的主要区别在于:第 1 节讲的是如何收集仓库信息,而这一节关心的是如何高效地打包和缓存这些信息,以便模型反复调用时可以复用。
“稳定” 的“稳定 Prompt 前缀”意味着其中的内容变化不大。它通常包含通用指令、工具描述和工作区摘要。如果没有重要变更,我们不希望为每次交互都重新构建这部分内容,从而浪费算力。 其他组件的更新频率更高(通常每个回合一次),包括短期记忆、最近的对话记录以及最新用户请求。 简而言之,“稳定 Prompt 前缀”的缓存机制在于智能运行时(runtime)会尝试复用该部分数据。3. 工具访问与使用
工具访问与使用让体验从聊天更像智能体(Agent)。 普通模型只能用文字建议命令,但在编码框架内的 LLM 应该执行更狭窄且更有用的操作,即实际执行命令并获取结果(而非手动调用命令并将结果粘贴回聊天中)。 但框架不会让模型随意杜撰语法,而是提供一份预先定义、命名且输入输出边界清晰的允许工具列表。(当然,类似 Pythonsubprocess.call 的工具可以作为其中一部分,使智能体能够执行广泛的任意 Shell 命令。)
下图展示了工具使用的流程。


这里,模型需要选择一个 框架(harness)能够识别的操作,例如列出文件、读取文件、搜索、运行 shell 命令、写入文件等。同时,它必须以 框架可校验的格式提供参数。
因此,当模型请求执行某项操作时,运行时(runtime)会先暂停,执行一系列程序化检查,例如:
“这是一个已知的工具吗?”
“参数是否合法?”
“这需要用户批准吗?”
“请求的路径确实在工作区内吗?”
只有所有这些检查都通过后,操作才会真正执行。
运行 coding agent 固然存在一定的风险,但 框架的这些检查机制也提升了可靠性,因为模型不会去执行完全随意的命令。
此外,除了拒绝格式错误的操作和设置审批门控, 框架还可以通过校验文件路径,将文件访问限制在仓库范围内。
从某种角度看, 框架限制了模型的操作自由度,但同时也提高了易用性。
4. 最小化上下文膨胀
上下文膨胀并非 coding agent 独有的问题,而是 LLM 的通用难题。当然,如今 LLM 支持的上下文窗口越来越长(我最近写过关于让长上下文在计算上更可行的 注意力机制变体),但长上下文不仅成本高昂,还可能引入额外的噪声(如果其中包含大量无关信息的话)。
现代 LLM 中注意力机制变体的可视化指南
Sebastian Raschka, PhD·3月22日阅读全文由于反复读取文件、冗长的工具输出和日志等原因,coding agent 比普通的 LLM 多轮对话更容易受到上下文膨胀的影响。
如果运行时把这些信息都完整保留,可用的 context tokens 很快就会耗尽。因此,一个好的 coding harness 在处理上下文膨胀时通常相当讲究,而不只是像普通聊天界面那样简单地裁剪或总结信息。
从概念上讲,coding agent 中的上下文压缩过程大致如下图所示。具体来说,这里是对上一节图 8 中 clip(步骤 6)部分的进一步展开。

一个最简的 harness 至少会采用两种压缩策略来解决这个问题。
第一种是裁剪(clipping),即缩短过长的文档片段、大型工具输出、记忆笔记和会话记录条目。换句话说,它防止任何一段文本仅因为自身冗长就占据过多的 prompt 空间。
第二种策略是会话记录缩减或总结,即把完整的会话历史(下一节会详细讲)压缩成一份更小、可放入 prompt 的摘要。
这里的关键技巧是保留较新事件的更多细节,因为它们更可能影响当前步骤;而对较旧的事件则更激进地压缩,因为它们的相关性较低。
此外,还会对较早的文件读取进行去重,避免模型仅仅因为某个文件在会话早些时候被读过多次,就反复看到同样的内容。
总的来说,我认为这是优秀 coding agent 设计中被低估、也不起眼的一环。很多表面上的“模型质量”,实际上取决于上下文质量。
5. 结构化会话记忆
实际上,前文介绍的六个核心概念彼此高度交织,文章的不同章节和图示会针对不同侧重点或不同的“缩放级别”来展开讲解。上一节我们讨论了在提示阶段如何利用历史记录,以及如何构建一份紧凑的 transcript。那里的核心问题是:下一轮对话中,应该将多少历史信息重新喂给模型?因此,该节的重点在于压缩、裁剪、去重和近期性。
而本节讨论的结构化会话记忆,关注的则是历史记录在存储阶段的组织方式。这里的问题是:Agent 长期保留的永久性记录究竟是什么?因此,重点在于运行时会将一份更完整的 transcript 作为持久化状态保存下来,同时配套一个更轻量的记忆层。这个记忆层体积更小,会被修改和压缩,而不是单纯地追加内容。
总而言之,编码 Agent 至少将状态分为两层:
工作记忆:Agent 明确维护的小型、提炼后的状态
完整 transcript:涵盖所有用户请求、工具输出和 LLM 响应
上图展示了两个主要的会话文件——完整 transcript 和工作记忆,它们通常以 JSON 文件的形式存储在磁盘上。如前所述,完整 transcript 保存了全部历史,即使关闭 Agent,后续也可以恢复会话。工作记忆则更像是一种提炼版本,包含当前最重要的信息,这与紧凑 transcript 有一定关联。
压缩后的会话记录和短工作记忆的用途略有不同。前者服务于提示词重建,其核心任务是为模型提供近期历史的压缩视图,使其无需每轮都读取完整日志即可延续对话;后者则更侧重任务连续性,其作用是通过维护一个简短的显式摘要,追踪跨轮次的关键信息,如当前任务、涉及的重要文件及近期笔记。
按照上图中第四步的流程,最新的用户请求、LLM 响应及工具输出会被记录为“新事件”,同步写入完整会话记录与短工作记忆。为了简化上图,后续轮次未一一展示。
6. 带(有界)子智能体的委派
当智能体具备了工具调用与状态管理能力后,下一步有价值的增强就是委派。
之所以强调委派,是因为它允许我们通过子智能体将部分工作拆分为子任务并行执行,从而加速主任务进程。举例来说,当主智能体在处理任务的中途需要顺带查一个旁支信息——如某个符号定义在哪个文件、某项配置的具体值,或测试为何失败——将这类查询剥离成一个有界的子任务,远比在单一循环里混处理多条工作流更高效。
(在我的迷你编程智能体中,实现更简洁,子进程仍以同步方式运行,但底层思路一致。)
子智能体只有在继承了足够上下文时才有实际价值;但若不加以限制,便容易出现多智能体重复劳动、争抢修改同一文件,甚至递归生成更多子智能体的失控局面。
因此,设计难题不在于如何启动子智能体,而在于如何把它管住 :)。

诀窍在于让子智能体获得足够的上下文以发挥效用,同时对它施加约束(例如限制为只读、限定递归深度)。
Claude Code 很早就支持 subagent,Codex 最近也加上了。不过 Codex 一般不会强制让 subagent 处于只读模式,它们通常会继承主 agent 的大部分沙箱和审批设置。因此,这个边界的重点更多在于任务范围、上下文和深度的划分。
组件小结
上文尝试覆盖了 coding agent 的主要组件。如前所述,这些组件在实现上或多或少是深度交织在一起的。不过,我希望逐个讲解的方式有助于建立整体的心智模型,理解 coding harness 是如何工作的,以及相比简单的多轮对话,它为什么能让 LLM 更有用。

如果你想看到这些内容用干净、极简的 Python 代码实现,可以看看我的 Mini Coding Agent。
它与 OpenClaw 相比如何?
OpenClaw 是个有趣的对比对象,但它并不是同一类系统。
OpenClaw 更像一个也能写代码的本地通用 agent 平台,而不是专门的(终端)编程助手。
它与 coding harness 仍有不少重叠之处:
使用工作区中的 prompt 和指令文件,比如 AGENTS.md、SOUL.md 和 TOOLS.md
保留 JSONL 会话文件,并支持对话记录压缩和会话管理
可以启动辅助会话和 subagent
等等
不过,如前所述,两者的侧重点不同。Coding agent 是为在代码仓库里工作的人优化的——让编程助手高效地检查文件、编辑代码、运行本地工具。而 OpenClaw 更侧重于跨聊天、频道和工作区运行多个长期驻留的本地 agent,编程只是其中一项重要工作负载。
很高兴宣布,我已完成《Build A Reasoning Model (From Scratch)》的写作,所有章节目前均处于预览阶段。出版方正在处理排版,预计今夏正式出版。
这可能是我迄今最雄心勃勃的一部作品。我花了大约一年半时间撰写,其中包含大量实验。在时间、精力和打磨程度上,这或许也是我投入最多的书,希望你会喜欢。

主要主题包括:
评估推理模型
推理时扩展(inference-time scaling)
自我精炼(self-refinement)
强化学习
蒸馏(distillation)
关于大语言模型中的“推理”有很多讨论,我认为理解其在 LLM 语境下真实含义的最佳方式,就是从零开始实现一个!
Amazon(预售)
