拆解智能体:编码助手的六大组件

我写这篇文章,是想聊聊编码智能体(Coding Agent)及其执行框架(Harness)的整体设计:它们是什么,如何工作,各个部分在实践中如何协同。我的《从零构建大语言模型》和《从零构建大推理模型》的读者经常问起智能体,所以我觉得写一篇参考文章会很有用。
更广泛地说,智能体之所以成为重要话题,是因为近期大语言模型(LLM)系统的许多实际进展,不仅在于模型本身更好,更在于我们如何使用它们。在许多实际应用中,围绕模型的系统——比如工具调用(Tool Use)、上下文管理和记忆——所起的作用和模型本身一样大。这也能解释为什么像 Claude Code 或 Codex 这样的系统,感觉上比在普通聊天界面中使用相同模型要强大得多。
这篇文章,我将梳理编码智能体的六个主要构建模块。
你可能熟悉 Claude Code 或 Codex CLI,简单来说,它们本质上是将 LLM 包装在应用层(即所谓的智能体执行框架)中的智能体编码工具,旨在让编码任务更方便、性能更好。
编码智能体是为软件工作而设计的,其显著之处不仅在于模型选择,更在于围绕它的系统,包括仓库上下文、工具设计、提示缓存稳定性、记忆和长会话连续性。
这个区别很重要,因为当我们谈论 LLM 的编码能力时,人们常常把模型、推理行为和智能体产品混为一谈。但在深入编码智能体细节之前,让我先简要说明一下更广泛的概念——LLM、推理模型和智能体之间的区别。
LLM 是核心的下一个词元预测模型。推理模型本质上仍是 LLM,但通常经过训练和/或提示,会在推理时花费更多计算资源在中间推理、验证或候选答案搜索上。
智能体是位于其上的一层,可以理解为围绕模型的控制循环。通常,给定一个目标后,智能体层(或执行框架)会决定下一步检查什么、调用哪个工具、如何更新状态以及何时停止等。
粗略来说,我们可以这样理解它们的关系:LLM 是引擎,推理模型是强化版引擎(更强大,但使用成本更高),而智能体执行框架(Harness)则帮助我们驾驭这个模型。这个类比并不完美,因为我们也可以将常规 LLM 和推理 LLM 作为独立模型使用(在聊天界面或 Python 会话中),但我希望它能传达主要意思。

图 2:常规 LLM、推理 LLM(或推理模型)以及被智能体执行框架(Harness)包装的 LLM 之间的关系。
换句话说,智能体是在一个环境中反复调用模型的系统。
所以,简而言之,我们可以这样总结:
- LLM:原始模型
- 推理模型:经过优化以输出中间推理轨迹并进行更多自我验证的 LLM
- 智能体:使用模型加上工具、记忆和环境反馈的循环
- 智能体执行框架(Agent Harness):围绕智能体的软件脚手架,负责管理上下文、工具调用、提示、状态和控制流
- 编码执行框架(Coding Harness):智能体执行框架的一个特例;即针对软件工程的、管理代码上下文、工具、执行和迭代反馈的任务特定执行框架
如上所列,在智能体和编码工具的语境中,我们还有两个常用术语:智能体执行框架(Agent Harness) 和(智能体)编码执行框架(Coding Harness)。编码执行框架是围绕模型的软件脚手架,帮助其有效地编写和编辑代码。而智能体执行框架范围更广,不特指编码(例如 OpenClaw)。Codex 和 Claude Code 可以被视为编码执行框架。
总之,一个更好的 LLM 为推理模型(这涉及额外训练)提供了更好的基础,而执行框架则能从这个推理模型中榨取更多价值。
当然,LLM 和推理模型本身(无需执行框架)也能解决编码任务,但编码工作只有一部分是关于下一个词元生成。很大一部分是关于仓库导航、搜索、函数查找、差异应用、测试执行、错误检查,以及将所有相关信息保持在上下文中。(程序员可能知道这是很费脑力的工作,所以我们不喜欢在编码会话中被中断 :))。

图 3. 编码执行框架结合了三层:模型家族、智能体循环和运行时支持。模型提供“引擎”,智能体循环驱动迭代问题解决,运行时支持提供底层管道。在循环内部,“观察”从环境收集信息,“检查”分析该信息,“选择”决定下一步,“执行”则实施它。
这里的要点是,一个好的编码执行框架可以让推理模型和非推理模型感觉比在普通聊天框中强大得多,因为它有助于上下文管理等。
执行框架:区分用户体验的关键
如前所述,当我们说_执行框架(Harness)_时,通常指的是围绕模型的软件层,它负责组装提示、暴露工具、跟踪文件状态、应用编辑、运行命令、管理权限、缓存稳定前缀、存储记忆等等。
如今,在使用 LLM 时,与直接提示模型或使用网页聊天界面(更接近“与上传的文件聊天”)相比,这一层在很大程度上塑造了用户体验。
在我看来,如今原始版本 LLM 的能力已经非常相似(例如 GPT-5.4、Opus 4.6 和 GLM-5 等的原始版本),执行框架往往是决定一个 LLM 比另一个更好用的区分因素。
这有点推测性,但我怀疑,如果我们把最新、能力最强的开源权重 LLM(比如 GLM-5)放入一个类似的执行框架中,它很可能在 Codex 中达到与 GPT-5.4 相当的性能,或在 Claude Code 中与 Claude Opus 4.6 相当。话虽如此,针对特定执行框架的后训练通常是有益的。例如,OpenAI 历史上就维护着独立的 GPT-5.3 和 GPT-5.3-Codex 变体。
在下一节,我想更具体地讨论编码执行框架的核心组件,并以我的 Mini Coding Agent 为例:https://github.com/rasbt/mini-coding-agent。

图 4:编码智能体/编码执行框架的主要功能,将在后续章节讨论。
顺便说一下,在本文中,为简便起见,我有时会交替使用“编码智能体”和“编码执行框架”这两个术语。(严格来说,智能体是模型驱动的决策循环,而执行框架是提供上下文、工具和执行支持的周围软件脚手架。)

图 5:最小化但完全可用的、从零实现的 Mini Coding Agent(纯 Python 实现)
总之,以下是编码智能体的六个主要组件。你可以查看我最小化但完全可用的、从零实现的 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_delegate
组件一:实时仓库上下文
这也许是最明显的组件,但也是最重要的之一。
当用户说“修复测试”或“实现 xyz”时,模型应该知道它是否在 Git 仓库内、在哪个分支上、哪些项目文档可能包含说明等等。
因为这些细节常常会改变或影响正确的操作。例如,“修复测试”不是一个自包含的指令。如果智能体看到了 AGENTS.md 或项目 README,它可能会了解到应该运行哪个测试命令等。如果它知道仓库根目录和布局,它就可以查看正确的地方,而不是猜测。
此外,Git 分支、状态和提交信息有助于提供更多关于当前正在进行哪些更改以及应关注何处的上下文。

图 6:智能体执行框架首先构建一个简短的工作空间摘要,该摘要与用户请求结合,以提供额外的项目上下文。
这里的要点是,编码智能体在开始任何工作之前,会先收集信息(作为工作空间摘要的“稳定事实”),这样它就不会在每次提示时都从零开始、毫无上下文。
组件二:提示结构与缓存复用
一旦智能体有了仓库视图,下一个问题就是如何将这些信息提供给模型。上一张图展示了这方面的简化视图(“组合提示:前缀 + 请求”),但在实践中,在每次用户查询时都组合并重新处理工作空间摘要会相对浪费。
也就是说,编码会话是重复性的,智能体规则通常保持不变。工具描述通常也保持不变。甚至工作空间摘要通常也(大部分)保持不变。主要的变化通常是用户的最新请求、最近的会话记录,可能还有短期记忆。
“智能”的运行时不会在每一轮都把所有东西重建为一个巨大的、无差别的提示,如下图所示。

执行框架(Harness)与缓存
图 7:执行框架构建一个稳定的提示前缀,添加变化的会话状态,然后将组合后的提示输入模型。
与第一部分的主要区别在于,第一部分是关于收集仓库事实。而这里,我们关注的是如何高效地打包和缓存这些事实,以便在多次模型调用中重复使用。
“稳定提示前缀”意味着其中包含的信息不会频繁变动。它通常包含通用指令、工具描述和工作空间摘要。如果重要内容没有变化,我们就不想在每次交互中都从头重建这部分内容,浪费计算资源。
其他组件更新得更频繁(通常是每轮)。这包括短期记忆、最近的对话记录和最新的用户请求。
简而言之,对“稳定提示前缀”的缓存,就是智能运行时尝试复用这部分内容。
工具调用(Tool Use)
工具访问和工具调用是让体验从聊天转向智能体的关键。
一个普通的模型可以用文字描述命令,但置于编码执行框架中的 LLM 应该做更具体、更有用的事——它应该能够实际执行命令并获取结果(而不是由我们手动调用命令,再把结果粘贴回聊天窗口)。
不过,执行框架通常不会让模型即兴发挥任意语法,而是提供一个预定义的、允许的工具列表,每个工具都有清晰的输入和边界。(当然,像 Python 的 subprocess.call 也可以包含在内,这样智能体也能执行范围广泛的任意 shell 命令。)
工具调用的流程如下图所示。
图 8:模型输出一个结构化动作,执行框架验证它,可选地请求批准,执行它,并将有界的结果反馈回循环中。
为了说明这一点,下面是一个使用我的 Mini Coding Agent 时,用户通常看到的示例。(它不像 Claude Code 或 Codex 那么漂亮,因为它非常精简,使用纯 Python,没有外部依赖。)
图 9:Mini Coding Agent 中工具调用批准请求的示意图。
在这里,模型必须选择一个执行框架能识别的动作,比如列出文件、读取文件、搜索、运行 shell 命令、写入文件等。它还必须提供执行框架可以检查的参数格式。
所以当模型请求做某事时,运行时可以停下来运行程序化检查,例如:
- “这是一个已知的工具吗?”
- “参数有效吗?”
- “这需要用户批准吗?”
- “请求的路径是否在工作空间内?”
只有通过这些检查后,才会实际执行任何操作。
运行编码智能体当然有一定风险,但执行框架的检查也提高了可靠性,因为模型不会执行完全任意的命令。
此外,除了拒绝格式错误的动作和设置批准关卡,还可以通过检查文件路径来将文件访问限制在仓库内。
从某种意义上说,执行框架给了模型更少的自由,但同时也提高了可用性。
上下文膨胀(Context Bloat)与压缩
上下文膨胀并非编码智能体独有的问题,而是 LLM 普遍面临的挑战。虽然 LLM 现在支持越来越长的上下文(我最近写过关于注意力变体的文章,使其在计算上更可行),但长上下文仍然昂贵,并且可能引入额外的噪音(如果有很多不相关信息)。
编码智能体在多轮聊天中比普通 LLM 更容易受到上下文膨胀的影响,因为会重复读取文件、产生冗长的工具输出、日志等。
如果运行时以全保真度保留所有这些内容,很快就会耗尽可用的上下文 token。因此,一个好的编码执行框架在处理上下文膨胀方面通常相当复杂,不仅仅像常规聊天界面那样简单地截断或总结信息。
从概念上讲,编码智能体中的上下文压缩可能如下图的总结所示。具体来说,我们进一步放大了上一节图 8 中“裁剪”(步骤 6)的部分。
图 10:大型输出被裁剪,较早的读取被去重,对话记录在返回提示前被压缩。
一个最小的执行框架至少使用两种压缩策略来管理这个问题。
第一种是裁剪,它缩短冗长的文档片段、大型工具输出、记忆笔记和对话记录条目。换句话说,它防止任何一段文本仅仅因为冗长而占用过多的提示预算。
第二种策略是对话记录缩减或总结,它将完整的会话历史(下一节会详细讨论)转化为一个更小的、可放入提示的摘要。
这里的一个关键技巧是保持近期事件更丰富,因为它们更可能与当前步骤相关。而对较早的事件进行更激进的压缩,因为它们可能不那么相关。
此外,我们还会对较早的文件读取进行去重,这样模型就不会仅仅因为之前在会话中多次读取了同一文件内容而反复看到它。
总的来说,我认为这是优秀编码智能体设计中一个被低估的、看似枯燥的部分。很多表面上的“模型质量”实际上取决于上下文质量。
结构化会话记忆
实际上,这里涵盖的这 6 个核心概念是高度交织的,不同的章节和图表从不同的焦点或缩放级别来介绍它们。在上一节中,我们介绍了历史记录的提示时使用,以及如何构建一个紧凑的对话记录。那里的问题是:下一轮应该将多少过去的信息反馈给模型?所以重点是压缩、裁剪、去重和近期性。
现在,本节的结构化会话记忆,是关于历史记录的存储时结构。这里的问题是:智能体随时间保留哪些内容作为永久记录?所以重点是运行时将更完整的对话记录作为持久状态保存,同时维护一个更轻量级的记忆层,这个层更小,会被修改和压缩,而不仅仅是追加。
总结一下,一个编码智能体将状态分离为(至少)两层:
- 工作记忆:智能体明确保留的小型、精炼的状态
- 完整对话记录:涵盖所有用户请求、工具输出和 LLM 响应
图 11:新事件被追加到完整对话记录中,并在工作记忆中总结。会话文件通常以 JSON 文件形式存储在磁盘上。
上图说明了两个主要的会话文件——完整对话记录和工作记忆,它们通常以 JSON 文件形式存储在磁盘上。如前所述,完整对话记录存储整个历史,如果我们关闭智能体,它是可恢复的。工作记忆更像是一个精炼版本,包含当前最重要的信息,这与紧凑对话记录有些关联。
但紧凑对话记录和工作记忆的职责略有不同。紧凑对话记录用于提示重建。它的职责是给模型一个近期历史的压缩视图,以便它可以在不每轮都看到完整对话记录的情况下继续对话。工作记忆更多是为了任务连续性。它的职责是跨轮次保留一个小的、明确维护的关于重要事项的摘要,比如当前任务、重要文件和近期笔记。
按照上图中的步骤 4,最新的用户请求,连同 LLM 响应和工具输出,将在下一轮中作为“新事件”记录在完整对话记录和工作记忆中(图中未显示以减少杂乱)。
委托与子智能体
一旦智能体拥有了工具和状态,下一个有用的能力就是委托。
原因在于,它允许我们通过子智能体将某些工作并行化为子任务,从而加速主要任务。例如,主智能体可能正在执行一个任务,但仍然需要一个侧面的答案,比如哪个文件定义了一个符号、配置说了什么,或者为什么一个测试失败了。将其拆分为一个有界的子任务,而不是强迫一个循环同时处理所有工作线程,是很有用的。
(在我的 mini coding agent 中,实现更简单,子智能体仍然是同步运行的,但基本思想是一样的。)
子智能体只有在继承了足够的上下文来做实际工作时才有用。但如果我们不加以限制,就会出现多个智能体重复工作、操作相同文件或产生更多子智能体等情况。
所以,棘手的设计问题不仅在于如何生成子智能体,还在于如何约束它。
图 12:子智能体继承了足够的上下文以发挥作用,但它在比主智能体更严格的边界内运行。
这里的技巧在于,子智能体继承了足够的上下文以发挥作用,但也受到约束(例如,只读和限制递归深度)。
Claude Code 支持子智能体已经很久了,Codex 最近也添加了它们。Codex 通常不强制子智能体进入只读模式。相反,它们通常继承了主智能体的大部分沙盒和批准设置。所以,边界更多是关于任务范围、上下文和深度。
前面几节梳理了编程智能体的核心组件。虽然这些模块在实际实现中往往紧密耦合,但逐一拆解有助于理解执行框架(Harness)的工作原理,以及为什么它们能让 LLM 比简单的多轮对话更有用。
图 13:前文讨论的编程执行框架六大核心功能。
如果你想看这些功能如何用简洁的 Python 代码实现,可以看看我的 Mini Coding Agent 项目。
OpenClaw 可能是个有趣的对比对象,但它和这类系统不太一样。
OpenClaw 更像一个本地的通用智能体平台,也能写代码,但它并非专门针对终端编程场景优化。
不过它和编程执行框架仍有不少重叠之处:
- 它会在工作空间中使用提示和指令文件,比如 AGENTS.md、SOUL.md 和 TOOLS.md
- 它会维护 JSONL 格式的会话文件,支持对话记录压缩和会话管理
- 它能创建辅助会话和子智能体
- 等等
但如前所述,两者的侧重点不同。编程智能体专为开发者在仓库中工作、让 AI 助手高效查看文件、编辑代码、运行本地工具而设计。OpenClaw 则更侧重于在聊天、频道和工作空间中运行多个长期存活的本地智能体,编码只是其众多重要任务之一。
另外分享一个消息:我的新书《Build A Reasoning Model (From Scratch)》已经完稿,所有章节均已开放早期访问。出版社正在排版,预计今年夏天正式出版。
这大概是我迄今为止最雄心勃勃的一本书,花了大约一年半时间写作,书中融入了大量实验。无论是投入的时间、精力还是打磨的精细度,它都算是我最下功夫的一本,希望你会喜欢。
书中主要探讨以下主题:
- 推理模型评估
- 推理时扩展(Inference-time Scaling)
- 自我优化(Self-refinement)
- 强化学习(Reinforcement Learning)
- 知识蒸馏(Distillation)
当前 LLM 领域关于“推理”的讨论很多,我认为理解它在 LLM 语境下真正含义的最好方式,就是亲手从头实现一个!
觉得有用?分享给更多人






