一声棒喝,本不立文字
偏要著録,已是二义

sebastian-raschka

Coding Agent 的组成部分

Components of A Coding Agent

二〇二六年五月三日 · 英文原文

文章介绍 coding agent 与 agent harness 的关系,区分 LLM、reasoning model、agent 和 coding harness,并以 Mini Coding Agent 为例说明六个组件:Live Repo Context、prompt cache、structured tools、context reduction、session memory、bounded subagents,同时比较 Claude Code、Codex CLI 与 OpenClaw。

在本文中,我想介绍 coding agent 和 agent harness 的整体设计:它们是什么、如何工作,以及不同组件在实践中如何组合在一起。我的《Build a Large Language Model (From Scratch)》和《Build a Large Reasoning Model (From Scratch)》的读者经常问到 agent,因此我觉得写一篇可供引用的参考文章会有帮助。更一般地说,agent 已经成为一个重要话题,因为最近实用 LLM 系统的许多进展,不只是来自更好的模型,也来自我们如何使用这些模型。在许多真实应用中,外围系统,例如 tool use、context management 和 memory,所起的作用不亚于模型本身。这也有助于解释为什么 Claude Code 或 Codex 这样的系统,会让人感觉比同样模型在普通 chat interface 中使用时强得多。本文会梳理 coding agent 的六个主要构建块。

Claude Code、Codex CLI 和其他 Coding Agent

你可能已经熟悉 Claude Code 或 Codex CLI,不过为了先交代背景,它们本质上是 agentic coding tool:在 LLM 外包上一层应用层,也就是所谓的 agentic harness,使其在 coding task 中更方便、表现更好。

Figure 1: Claude Code CLI、Codex CLI,以及我的 Mini Coding Agent。

Coding agent 是为软件工作而工程化设计的系统,其中值得关注的不只是模型选择,还包括外围系统,例如 repo context、tool design、prompt-cache stability、memory 和 long-session continuity。这个区分很重要,因为当我们讨论 LLM 的 coding capabilities 时,人们往往会把模型、reasoning behavior 和 agent product 混成一件事。

不过在进入 coding agent 的细节之前,我先简单补充一点背景,说明更宽泛的几个概念之间的区别:LLM、reasoning model 和 agent。

关于 LLM、Reasoning Model 和 Agent 的关系

LLM 是核心的 next-token model。Reasoning model 仍然是 LLM,但通常是经过训练和/或 prompt,使其在中间推理、验证或候选答案搜索上花费更多 inference-time compute 的 LLM。

Agent 是其上的一层,可以理解为围绕模型的 control loop。通常,给定一个目标,agent layer(或 harness)会决定接下来检查什么、调用哪些工具、如何更新自身状态、何时停止,等等。

大致上,我们可以这样理解它们的关系:LLM 是引擎,reasoning model 是增强版引擎(更强,但使用成本更高),而 agent harness 帮助我们使用模型。这个类比并不完美,因为我们也可以把 conventional LLM 和 reasoning LLM 作为 standalone model 使用(在 chat UI 或 Python session 中),但我希望它能传达核心意思。

Figure 2: conventional LLM、reasoning LLM(或 reasoning model),以及包裹在 agent harness 中的 LLM 之间的关系。

换句话说,agent 是在一个环境中反复调用模型的系统。因此,简要总结如下:

LLM:原始模型

Reasoning model:经过优化的 LLM,用于输出 intermediate reasoning traces,并更多地进行自我验证

Agent:一个循环,使用模型加上工具、memory 和 environment feedback

Agent harness:围绕 agent 的软件脚手架,负责管理 context、tool use、prompt、state 和 control flow

Coding harness:agent harness 的一个特例;也就是面向软件工程的 task-specific harness,负责管理 code context、tools、execution 和 iterative feedback

如上所列,在 agent 和 coding tool 的语境中,我们还有两个常见术语:agent harness 和(agentic)coding harness。Coding harness 是围绕模型的软件脚手架,帮助模型有效地编写和编辑代码。而 agent harness 更宽泛一些,并不局限于 coding(例如可以想到 OpenClaw)。

Codex 和 Claude Code 可以被视为 coding harness。总之,更好的 LLM 为 reasoning model(需要额外训练)提供更好的基础,而 harness 能从这个 reasoning model 中榨取更多能力。

当然,LLM 和 reasoning model 本身也能解决 coding task(不使用 harness),但 coding work 只有一部分是 next-token generation。很大一部分工作是 repo navigation、search、function lookup、diff application、test execution、error inspection,以及让所有相关信息保持在 context 中。(写代码的人可能都知道,这是很耗费心力的工作,这也是我们不喜欢在 coding session 中被打断的原因 :)。)

Figure 3. Coding harness 结合了三层:model family、agent loop 和 runtime supports。模型提供“engine”,agent loop 驱动 iterative problem solving,而 runtime supports 提供基础管线。在循环中,“observe”从环境收集信息,“inspect”分析这些信息,“choose”选择下一步,“act”执行它。

这里的要点是:一个好的 coding harness 可以让 reasoning model 和 non-reasoning model 都显得比在普通 chat box 中强得多,因为它帮助处理 context management 等问题。

Coding Harness

如上一节所述,当我们说 harness 时,通常指模型周围的软件层:它组装 prompt、暴露工具、跟踪文件状态、应用编辑、运行命令、管理权限、缓存稳定前缀、存储 memory,等等。

今天使用 LLM 时,相比直接 prompt 模型或使用 web chat UI(更接近“与上传文件聊天”),这一层塑造了大部分用户体验。由于在我看来,如今 LLM 的 vanilla 版本能力非常接近(例如 GPT-5.4、Opus 4.6、GLM-5 等的 vanilla 版本),harness 往往会成为让一个 LLM 比另一个更好用的区分因素。

这带有推测成分,但我怀疑,如果把最新、最强的 open-weight LLM 之一,例如 GLM-5,放进类似的 harness,它很可能可以达到 Codex 中 GPT-5.4 或 Claude Code 中 Claude Opus 4.6 的水平。

话虽如此,一些 harness-specific post-training 通常是有益的。例如,OpenAI 历史上维护过独立的 GPT-5.3 和 GPT-5.3-Codex 变体。

下一节中,我想进一步进入细节,使用我的 Mini Coding Agent 讨论 coding harness 的核心组件:https://github.com/rasbt/mini-coding-agent

Figure 4: Coding agent / coding harness 的主要 harness features,后续章节会讨论这些特性。

顺便说一句,本文中为简化起见,我会在一定程度上交替使用“coding agent”和“coding harness”这两个术语。(严格来说,agent 是模型驱动的 decision-making loop,而 harness 是外围软件脚手架,提供 context、tools 和 execution support。)

Figure 5: 极简但完整可运行、from-scratch 的 Mini Coding Agent(用纯 Python 实现)

总之,下面是 coding agent 的六个主要组件。你可以查看我的极简但完整可运行、from-scratch 的 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
  1. Live Repo Context

这可能是最明显的组件,但也是最重要的组件之一。当用户说“fix the tests”或“implement xyz”时,模型应该知道自己是否处在 Git repo 中、当前在哪个 branch、哪些项目文档可能包含指令,等等。原因是这些细节经常会改变,或者影响什么才是正确动作。

例如,“Fix the tests”并不是一个自包含的指令。如果 agent 看到了 AGENTS.md 或项目 README,它可能会知道应该运行哪个 test command,等等。如果它知道 repo root 和 layout,就能去正确的位置查找,而不是猜测。此外,git branch、status 和 commits 可以提供更多 context,说明当前正在进行哪些改动、应该关注哪里。

Figure 6: Agent harness 首先构建一个小的 workspace summary,然后将其与用户请求结合,为项目提供额外 context。

这里的要点是,coding agent 在开始任何工作之前,先收集信息(作为 workspace summary 的“stable facts”),这样它就不会在每次 prompt 时都从零开始、没有 context。

  1. Prompt Shape And Cache Reuse

一旦 agent 获得了 repo view,下一个问题就是如何把这些信息提供给模型。前一张图展示了一个简化视图(“Combined prompt: prefix + request”),但在实践中,如果每次用户查询都重新组合并处理 workspace summary,就会相对浪费。也就是说,coding session 是重复性的,而 agent rules 通常保持不变。tool descriptions 通常也保持不变。甚至 workspace summary 通常也(大体)保持不变。主要变化通常是最新的用户请求、近期 transcript,以及也许还有 short-term memory。

“Smart” runtime 不会在每一轮都把所有东西重新构造成一个巨大的、不加区分的 prompt,如下图所示。

Figure 7: Agent harness 构建稳定的 prompt prefix,添加变化的 session state,然后把组合后的 prompt 输入模型。

与第 1 节的主要区别是,第 1 节关注收集 repo facts。这里我们关注如何高效地打包和缓存这些 facts,以便反复调用模型。

“stable”的“Stable prompt prefix”意思是其中包含的信息变化不大。它通常包含 general instructions、tool descriptions 和 workspace summary。如果没有重要内容变化,我们不希望在每次交互中都浪费 compute 从头重建它。

其他组件更新更频繁(通常每一轮都会更新)。这包括 short-term memory、recent transcript 和最新的用户请求。

简言之,“Stable prompt prefix”的 caching 方面就是:smart runtime 会尝试复用这一部分。

  1. Tool Access and Use

Tool access 和 tool use 是它开始不像 chat、而更像 agent 的地方。普通模型可以用文字建议命令,但 coding harness 中的 LLM 应该做更窄、更有用的事情,并且确实能够执行命令并取回结果(而不是由我们手动调用命令,再把结果粘回 chat)。

不过,与其让模型即兴生成任意语法,harness 通常会提供一个预定义的 allowed and named tools 列表,带有清晰的输入和边界。(当然,像 Python subprocess.call 这样的东西也可以是其中一部分,这样 agent 也能执行任意范围较广的 shell commands。)

Tool-use flow 如下图所示。

Figure 8: 模型发出 structured action,harness 验证它,必要时请求 approval,执行它,并把有边界的结果反馈回循环。

为了说明这一点,下面是使用我的 Mini Coding Agent 时,用户通常会看到的例子。(它没有 Claude Code 或 Codex 那么美观,因为它非常极简,并且使用纯 Python,没有任何外部依赖。)

Figure 9: Mini Coding Agent 中 tool call approval request 的示例。

这里,模型必须选择 harness 能识别的 action,例如列出文件、读取文件、搜索、运行 shell command、写入文件等。它还必须以 harness 能检查的形式提供参数。因此,当模型请求做某件事时,runtime 可以停下来运行程序化检查,例如“这是已知工具吗?”、“参数有效吗?”、“这需要用户 approval 吗?”、“请求的路径是否在 workspace 内?”只有这些检查通过之后,才会真正执行任何操作。

当然,运行 coding agent 会带来一定风险,但 harness checks 也会提高可靠性,因为模型不会执行完全任意的命令。此外,除了拒绝格式错误的 action 和 approval gating 之外,还可以通过检查文件路径,将文件访问限制在 repo 内。

从某种意义上说,harness 给了模型更少的自由,但同时也提升了可用性。

  1. Minimizing Context Bloat

Context bloat 不是 coding agent 独有的问题,而是 LLM 的通用问题。当然,如今 LLM 支持越来越长的 context(我最近也写过让其在计算上更可行的 attention variants),但长 context 仍然昂贵,也可能引入额外噪声(如果其中有大量无关信息)。

在 multi-turn chat 期间,coding agent 比普通 LLM 更容易出现 context bloat,因为会反复读取文件、产生很长的 tool outputs、logs 等。如果 runtime 以完整保真度保留所有这些内容,可用的 context tokens 很快就会耗尽。

因此,一个好的 coding harness 通常在处理 context bloat 方面相当复杂,不只是像普通 chat UI 那样截断或总结信息。

概念上,coding agent 中的 context compaction 可能如下图所示。具体来说,我们对上一节 Figure 8 中的 clip(step 6)部分进一步放大。

Figure 10: 大输出会被 clipped,较旧的读取会被 deduplicated,transcript 会在重新进入 prompt 前被压缩。

一个最小 harness 至少会使用两种 compaction strategies 来处理这个问题。第一种是 clipping,它会缩短长文档片段、大型 tool outputs、memory notes 和 transcript entries。换句话说,它防止某一段文本只是因为啰嗦,就占据 prompt budget。

第二种策略是 transcript reduction 或 summarization,它把完整 session history(下一节会进一步讨论)转换成更小、可放入 prompt 的摘要。这里的一个关键技巧是保留更丰富的近期事件,因为它们更可能与当前步骤相关。而对更早的事件,则更激进地压缩,因为它们相关性可能较低。

此外,我们还会对较旧的 file reads 进行 deduplicate,这样模型就不会仅仅因为同一文件在 session 早些时候被读取了多次,就反复看到相同的文件内容。

总体而言,我认为这是优秀 coding-agent design 中被低估的、略显乏味的一部分。很多表面上的“model quality”实际上是 context quality。

  1. Structured Session Memory

实践中,这里讨论的 6 个核心概念彼此高度交织,不同小节和图只是以不同焦点或缩放层级来覆盖它们。

上一节中,我们讨论了 history 的 prompt-time use,以及如何构建 compact transcript。那里的问题是:过去有多少内容应该在下一轮重新进入模型?因此重点是 compression、clipping、deduplication 和 recency。

现在,本节的 structured session memory 关注的是 history 的 storage-time structure。这里的问题是:agent 随着时间推移,会保留什么作为永久记录?因此重点是,runtime 会保留一份更完整的 transcript 作为 durable state,同时还有一层更轻量的 memory layer;后者更小,会被修改和压缩,而不是只追加内容。

总结一下,coding agent 会把 state 分成(至少)两层:

working memory:agent 显式保留的小型、提炼后的 state

full transcript:覆盖所有用户请求、tool outputs 和 LLM responses

Figure 11: 新事件会被追加到 full transcript,并被总结进 working memory。磁盘上的 session files 通常以 JSON files 存储。

上图展示了两个主要的 session files:full transcript 和 working memory,它们通常会作为 JSON files 存储在磁盘上。如前所述,full transcript 存储完整历史;如果我们关闭 agent,它可以恢复。working memory 更像是提炼版本,包含当前最重要的信息,这与 compact transcript 有些相关。

但 compact transcript 和 working memory 的职责略有不同。

Compact transcript 用于 prompt reconstruction。它的职责是给模型一个近期历史的压缩视图,使其可以继续对话,而不需要每一轮都看到 full transcript。

Working memory 更面向 task continuity。它的职责是保留一个小型、显式维护的摘要,记录跨轮次重要的内容,例如当前任务、重要文件和近期 notes。

按照上图中的 step 4,最新用户请求连同 LLM response 和 tool output,会在下一轮作为“new event”记录到 full transcript 和 working memory 中;为了减少图中的杂乱,上图没有展示这一轮。

  1. Delegation With (Bounded) Subagents

一旦 agent 具备工具和状态,下一个有用能力之一就是 delegation。原因是它允许我们通过 subagents 将某些工作并行化为 subtasks,从而加快主任务。

例如,main agent 可能正处在某个任务中间,但仍然需要一个旁支答案,比如某个 symbol 由哪个文件定义、某个 config 说明了什么,或者某个 test 为什么失败。将其拆分成一个 bounded subtask 很有用,而不是强迫一个循环同时承载每一条工作线。(在我的 mini coding agent 中,实现更简单,child 仍然同步运行,但底层思想相同。)

Subagent 只有在继承足够 context、能够真正工作时才有用。但如果我们不限制它,就会有多个 agents 重复工作、触碰相同文件、生成更多 subagents,等等。因此,棘手的设计问题不只是如何 spawn 一个 subagent,也包括如何 bind 一个 subagent :)。

Figure 12: Subagent 继承足够 context 以发挥作用,但在比 main agent 更严格的边界内运行。

这里的技巧是,subagent 继承足够 context 以保持有用,同时也受到约束(例如 read-only,并限制 recursion depth)。

Claude Code 很早就支持 subagents,Codex 最近也加入了它们。Codex 通常不会强制 subagents 进入 read-only mode。相反,它们通常继承 main agent 的大部分 sandbox 和 approval setup。因此,边界更多体现在 task scoping、context 和 depth 上。

组件总结

上一节试图覆盖 coding agent 的主要组件。如前所述,它们在实现中或多或少是深度交织的。不过,我希望逐一讨论它们有助于建立 coding harness 如何工作的整体 mental model,以及为什么它们能让 LLM 相比简单的 multi-turn chat 更有用。

Figure 13: 前面各节讨论的 coding harness 六个主要特性。

如果你有兴趣看这些特性如何用简洁、极简的 Python 代码实现,可能会喜欢我的 Mini Coding Agent。

这与 OpenClaw 相比如何?

OpenClaw 是一个有趣的比较对象,但它并不是完全同一类系统。OpenClaw 更像是一个本地的通用 agent platform,也可以 coding,而不是一个专门的(terminal)coding assistant。

它仍然与 coding harness 有一些重叠:

它使用 workspace 中的 prompt 和 instruction files,例如 AGENTS.md、SOUL.md 和 TOOLS.md

它保留 JSONL session files,并包含 transcript compaction 和 session management

它可以生成 helper sessions 和 subagents 等

不过,如上所述,重点不同。Coding agent 优化的是一个人在 repository 中工作,并让 coding assistant 高效检查文件、编辑代码、运行本地工具。OpenClaw 则更优化于在 chats、channels 和 workspaces 中运行许多 long-lived local agents,而 coding 是其中几个重要 workload 之一。

我很高兴地分享,我已经写完了《Build A Reasoning Model (From Scratch)》,所有章节都已进入 early access。出版社目前正在处理版式,应该会在今年夏天发布。这可能是我迄今为止最有野心的一本书。我花了大约 1.5 年写它,并进行了大量实验。按时间、精力和打磨程度来说,这也可能是我最用心的一本书,希望你会喜欢。

《Build a Reasoning Model (From Scratch)》在 Manning 和 Amazon 上提供。

主要主题包括:

evaluating reasoning models

inference-time scaling

self-refinement

reinforcement learning

distillation

围绕 LLM 中“reasoning”的讨论很多,而我认为,理解它在 LLM 语境中真正含义的最佳方式,就是从零实现一个!

Amazon(pre-order)

Manning(complete book in early access,pre-final layout,528 pages)

译自 sebastian-raschka · 录于 二〇二六年五月三日