这篇文章想讨论的是编程 Agent(Coding Agent)和 Agent Harness 的整体设计:它们是什么、如何运作,以及各个部分在实践中是怎样组合起来的。
读过我《Build a Large Language Model (From Scratch)》和《Build a Large Reasoning Model (From Scratch)》的读者,经常会问到 Agent 相关的话题,所以我觉得写一篇可以随时拿来引用的参考文章会很有帮助。
更广泛地看,Agent 之所以变得重要,是因为近年来实用型 LLM 系统的进步,并不只是来自模型本身更强,还来自我们使用模型的方式。在很多真实应用里,围绕模型的系统层,比如工具使用、上下文管理和记忆机制,和模型本身同样关键。这也解释了为什么 Claude Code 或 Codex 往往会让人感觉明显强于同一模型在普通聊天界面中的表现。
这篇文章里,我会梳理编程 Agent 的六个核心构件。
你大概已经接触过 Claude Code 或 Codex CLI。简单来说,它们本质上都是带有 agentic 特征的编程工具:在 LLM 外面包上一层应用层,也就是所谓的 Agent Harness,从而让模型在编码任务上更方便、表现也更好。
图 1:Claude Code CLI, Codex CLI, 和我的 Mini Coding Agent.。
编程 Agent 是为软件工作专门设计的。这里真正关键的,不只是选用了哪个模型,还有围绕模型的一整套系统,包括代码仓库上下文、工具设计、提示缓存稳定性、记忆机制,以及长会话的连续性等。
这个区分很重要,因为一谈到 LLM 的编程能力,人们常常会把模型本身、推理行为,以及 Agent 产品混成一回事。不过在进入编程 Agent 的细节之前,我想先简要说明一下 LLM、推理模型和 Agent 这几个更广义概念之间的区别。
LLM 是核心的 next-token 模型。推理模型本质上仍然是 LLM,只不过通常经过了额外训练和/或提示优化,使其在推理时愿意投入更多 inference-time compute 到中间推理、结果校验或候选答案搜索上。
Agent 则是模型之上的一层,可以理解为围绕模型运转的控制循环。通常在给定一个目标后,Agent 层(或 Harness)会决定下一步检查什么、调用哪些工具、如何更新状态,以及何时停止。
粗略来说,它们的关系可以这样理解:LLM 是引擎,推理模型是加强版引擎,动力更强,但使用成本也更高;而 Agent Harness 则帮助我们把模型用得更好。这个类比并不完全严谨,因为普通 LLM 和推理 LLM 也都可以被单独使用,比如在聊天界面里或 Python 会话里,但我希望它能传达出核心意思。
Figure 2
图 2:普通 LLM、推理 LLM(推理模型)和包裹在 Agent Harness 中的 LLM 之间的关系。
换句话说,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。但编程工作只有一部分是在做 next-token 生成,更大的一部分其实是代码仓库导航、搜索、函数定位、应用 diff、执行测试、检查错误,以及把所有相关信息都维持在上下文里。(写代码的人都知道,这些都是很费脑力的事,所以我们才讨厌在编码时被打断。)
图 3
图 3:Coding Harness 由三层构成:模型家族、Agent 循环和运行时支持。模型提供“引擎”,Agent 循环驱动迭代式问题求解,运行时支持则提供底层基础设施。在循环内部,“observe”负责从环境收集信息,“inspect”负责分析这些信息,“choose”决定下一步,而“act”执行具体动作。
这里的关键结论是:一个好的 Coding Harness,能让推理模型和非推理模型都比在普通聊天框里显得强得多,因为它在上下文管理等方面提供了大量帮助。
前面提到过,当我们说 Harness 时,通常指的是围绕模型的软件层。它负责组装提示、暴露工具、跟踪文件状态、应用编辑、运行命令、管理权限、缓存稳定前缀、存储记忆,等等。
在今天的 LLM 使用场景里,这一层在很大程度上塑造了用户体验,相比直接提示模型,或者使用更接近“上传文件后聊天”的网页聊天界面,它的影响要大得多。
在我看来,如今各家 LLM 的基础版能力其实已经相当接近了,比如 GPT-5.4、Opus 4.6 和 GLM-5 的基础版。很多时候,真正拉开使用体验差距的,反而是 Harness。
说一点带推测性质的话:如果把当前最新、能力最强的一批开源权重 LLM 之一,比如 GLM-5,放进一个类似的 Harness 里,它很可能可以和 Codex 中的 GPT-5.4、或者 Claude Code 中的 Claude Opus 4.6 打到差不多的水平。当然,一些面向 Harness 的后训练通常仍然有帮助。比如 OpenAI 过去就长期维护过独立的 GPT-5.3 和 GPT-5.3-Codex 变体。
下一节里,我会更具体地讨论 Coding Harness 的核心组件,并以我的 Mini Coding Agent 为例:https://github.com/rasbt/mini-coding-agent。
图 4
图 4:编程 Agent / Coding Harness 的主要特性,将在下面各节中讨论。
顺便说明一下,为了叙述方便,本文会在一定程度上混用“编程 Agent”和“Coding Harness”这两个词。(严格来说,Agent 指的是模型驱动的决策循环,而 Harness 指的是为它提供上下文、工具和执行支持的外围软件脚手架。)
图 5
图 5:极简但完整可用的从零构建的 Mini Coding Agent(纯 Python 实现)
下面是编程 Agent 的六个主要组件。你也可以直接查看我用纯 Python 从零实现的、极简但完整可用的 Mini Coding Agent 源码,里面有更具体的代码示例。代码里通过注释标出了下文讨论的六个组件:
################################## Six Agent Components ################################### 1) 实时代码仓库上下文 -> WorkspaceContext# 2) 提示结构设计与缓存复用 -> build_prefix, memory_text, prompt# 3) 结构化工具、校验和权限 -> build_tools, run_tool, validate_tool, approve, parse, path, tool_*# 4) 上下文压缩与输出管理 -> clip, history_text# 5) 转录、记忆与会话恢复 -> SessionStore, record, note_tool, ask, reset# 6) 委派与有界子 Agent -> tool_delegate1)实时仓库上下文
这可能是最显而易见的组件,但也是最重要的之一。
当用户说“修复测试”或“实现 xyz”时,模型应该知道自己是不是处在一个 Git 仓库中、当前在哪个分支、项目文档里是否包含相关说明,等等。
这是因为这些细节往往会改变或影响正确的行动方式。比如“修复测试”本身并不是一条自包含的指令。如果 Agent 能看到 AGENTS.md 或项目 README,它就可能知道该运行哪个测试命令;如果它知道仓库根目录和目录结构,也就能去正确的位置查找,而不是靠猜。
此外,Git 分支、工作区状态和提交记录,也能帮助系统补充上下文,理解当前正在推进哪些改动,以及应该重点关注哪里。
图 6
图 6:Agent Harness 会先构建一个简短的工作区摘要,再把它与用户请求结合起来,为模型补充额外的项目上下文。
这里的关键点在于:编程 Agent 会在真正开始干活之前,先收集一批信息,把它们作为工作区摘要中的“稳定事实”,这样后续每次提示时,就不必在毫无上下文的情况下从零开始。
2)提示结构与缓存复用
当 Agent 已经拿到了代码仓库视图,接下来的问题就是:如何把这些信息喂给模型。上一张图展示的是一个简化版的过程(“组合提示:前缀 + 请求”),但在真实系统里,如果每次用户发来请求都重新组合并处理整份工作区摘要,其实是比较浪费的。
也就是说,编码会话本身具有很强的重复性。Agent 规则通常不变,工具描述通常也不变,工作区摘要往往也大体稳定。真正会变化的,通常只是最新的用户请求、最近的转录,也许再加上一点短期记忆。
“聪明”的运行时不会在每一轮都把所有内容重新拼成一个巨大而未分层的提示,如下图所示。
图 7
图 7:Agent Harness 构建一个稳定的提示前缀,添加变化的会话状态,然后将组合后的提示喂给模型。
这一节和第 1 节最大的区别在于:第 1 节关注的是如何收集仓库事实,而这里关注的是如何高效地打包并缓存这些事实,以便反复调用模型时复用。
所谓“稳定提示前缀”,是指其中包含的信息变化不大。它通常会包含通用指令、工具描述和工作区摘要。如果没有发生什么重要变化,我们就不希望在每次交互时都从头重建这一部分。
其他组件则更新得更频繁,通常每轮都会变化,包括短期记忆、最近的转录,以及最新的用户请求。
简而言之,“稳定提示前缀”的缓存,本质上就是让足够聪明的运行时尽可能复用这部分内容。
3)工具访问、校验与权限
工具访问和工具使用,是系统开始从“聊天”变得更像“Agent”的关键所在。
普通模型可以在文字里建议某个命令,但处在 Coding Harness 中的 LLM 应该做一件更窄、却也更有用的事:它应该能够真正执行命令并拿回结果,而不是让我们手动跑完命令再把结果贴回聊天窗口。
不过,Harness 通常不会让模型随意发挥、临时编造语法,而是提供一组预先定义好的、具名的、允许使用的工具,并为它们规定清晰的输入格式和边界。(当然,也可以把类似 Pythonsubprocess.call这样的能力包含进去,这样 Agent 仍然可以执行很宽泛的一类 shell 命令。)
工具调用流程如下图所示。
图 8
图 8:模型发出一个结构化动作,Harness 对其进行校验,可选地请求用户批准,执行之后再把有界结果反馈回循环。
为了说明这一点,下面是用我的 Mini Coding Agent 时的一个示例。(没有 Claude Code 或 Codex 那么漂亮,因为它非常极简,用的是纯 Python,没有任何外部依赖。)
图 9
图 9:Mini Coding Agent 中一次工具调用审批请求的示意。
在这里,模型必须选择一个 Harness 能识别的动作,比如列出文件、读取文件、搜索、运行 shell 命令、写入文件等等。它还必须以 Harness 可校验的参数形式提供输入。
所以,当模型请求执行某个动作时,运行时就可以先暂停下来,执行一些程序化检查:
- “这是已知的工具吗?”
- “参数合法吗?”
- “需要用户批准吗?”
- “请求的路径在工作区内吗?”
只有这些检查都通过了,才会真正执行。
当然,运行编程 Agent 本身始终带有一定风险,但 Harness 的这些检查也会提升可靠性,因为模型无法执行完全任意的命令。
此外,除了拒绝格式不合法的动作、加上审批门槛之外,还可以通过检查文件路径,把文件访问限制在仓库内部。
从某种意义上说,Harness 的确减少了模型的自由度,但与此同时,它也提升了整个系统的可用性。
4)控制上下文膨胀
上下文膨胀并不是编程 Agent 独有的问题,而是 LLM 普遍都会遇到的问题。当然,如今 LLM 支持的上下文已经越来越长了(我最近还写过一篇介绍实现这一点所依赖的注意力变体),但长上下文依然昂贵,而且如果夹杂了大量无关信息,还会引入额外噪音。
在多轮交互里,编程 Agent 往往比普通 LLM 更容易出现上下文膨胀,因为它会反复读取文件、产生很长的工具输出,还会积累日志等信息。
如果运行时把这些内容全都原样保留下来,很快就会把可用的上下文 token 消耗殆尽。所以,一个好的 Coding Harness 在处理上下文膨胀这件事上,通常会做得相当精细,而不只是像普通聊天界面那样简单裁剪或概括一下。
从概念上看,编程 Agent 中的上下文压缩大致会像下图这样工作。更具体地说,这里是在继续放大上一节图 8 中clip(步骤 6)对应的部分。
图 10
图 10:大的输出会被裁剪,较早的读取会被去重,转录在回到提示之前会先被压缩。
一个最基础的 Harness,至少会使用两种压缩策略来处理这个问题。
第一种是裁剪(clipping),也就是缩短过长的文档片段、大型工具输出、记忆笔记和转录条目。换句话说,它要防止某一段文本仅仅因为特别冗长,就独占了整个提示预算。
第二种策略是转录压缩或总结,也就是把完整的会话历史(下一节会详细讲)变成一个更小、但仍可用于提示的摘要。
这里有一个关键技巧:近期事件要保留得更丰富一些,因为它们更可能与当前步骤相关;而更早的事件则可以被更激进地压缩,因为它们大概率没那么重要。
此外,我们还会对较早的文件读取做去重,这样模型就不会因为在会话早期多次读取过同一个文件,而反复看到完全相同的内容。
总的来说,我觉得这恰恰是优秀编程 Agent 设计里一个经常被低估、也略显“无聊”的部分。很多看起来像是“模型质量”的差异,本质上其实是上下文质量的差异。
5)结构化会话记忆
在实践中,这 6 个核心概念是高度交织在一起的。前面的不同小节和配图,只是从不同的关注点和缩放层级去讨论它们。上一节里,我们讨论的是历史在提示阶段如何被使用,以及如何构建紧凑转录。当时关注的问题是:过去的信息中,到底有多少内容应该在下一轮重新送回模型?所以重点放在压缩、裁剪、去重和时效性上。
而这一节谈的“结构化会话记忆”,关注的是历史在存储阶段的结构。这里的问题变成了:随着时间推移,Agent 会把什么内容保留下来,作为持久记录?重点在于,运行时会维护一份更完整的转录作为持久状态,同时再维护一层更轻量的记忆,这层记忆更小,会不断被修改和压缩,而不是只是单纯往后追加。
总结一下,编程 Agent 至少会把状态拆成两层:
- 工作记忆(working memory):Agent 显式维护的精简状态
- 完整转录(full transcript):覆盖所有用户请求、工具输出和 LLM 响应
图 11
图 11:新事件会被追加到完整转录中,同时被总结进工作记忆。磁盘上的会话文件通常会以 JSON 文件形式保存。
上图展示了两个主要的会话文件:完整转录和工作记忆,它们通常会以 JSON 文件的形式保存在磁盘上。如前所述,完整转录保存的是全部历史,因此即使关闭 Agent,之后也可以恢复。工作记忆则更像一份提炼过的版本,只保留当前最重要的信息,它与紧凑转录有关,但两者并不完全相同。
不过,紧凑转录和工作记忆的职责还是略有不同。紧凑转录是为提示重建服务的,它的职责是给模型提供一个压缩过的近期历史视图,让模型在每一轮都不需要看到完整转录也能继续对话。工作记忆则更偏向任务连续性,它的职责是跨轮次维护一份小而精的、显式管理的摘要,其中包含当前任务、重要文件、近期笔记等内容。
按照上图中步骤 4 的描述,最新的用户请求,以及对应的 LLM 响应和工具输出,会在下一轮中作为“新事件”被记录下来,并同时写入完整转录和工作记忆。为了避免图里过于拥挤,这一步没有画出来。
6)委派与受约束的子 Agent
一旦 Agent 具备了工具和状态,接下来一个很有用的能力就是委派(delegation)。
原因在于,它允许我们借助子 Agent,把一部分工作并行拆成子任务,从而加快主任务的推进。比如,主 Agent 正在处理一个任务,但中途还需要某个旁支答案,例如:哪个文件定义了某个符号、某个配置文件里写了什么、或者某个测试为什么会失败。把这些问题拆到一个受约束的子任务里,往往比强迫同一个循环同时背负所有工作线程要好得多。
(在我的 Mini Coding Agent 里,这部分实现更简单,子 Agent 仍然是同步运行的,但底层思路是一样的。)
子 Agent 只有在继承了足够上下文的前提下才真正有用。但如果不给它加限制,就很容易出现多个 Agent 重复劳动、同时修改同一个文件、甚至继续生成更多子 Agent 等问题。
所以,真正棘手的设计问题不只是“怎么生成子 Agent”,还包括“怎么把它约束住”。
图 12
图 12:子 Agent 会继承足够完成任务的上下文,但它运行在比主 Agent 更严格的边界之内。
这里的关键技巧在于:子 Agent 要继承足够的上下文,才能真正完成任务;但与此同时,它也必须受到约束,比如运行在只读模式下,或者限制递归深度。
Claude Code 很早就支持子 Agent,而 Codex 则是后来才加入这一能力。Codex 通常不会强制子 Agent 进入只读模式;相反,它们往往会继承主 Agent 的大部分沙盒和审批设置。所以这里所谓的边界,更多是通过任务范围、上下文和递归深度来体现的。
上面这些小节试图覆盖编程 Agent 的主要组件。正如前面提到的,它们在实现里往往是深度交织在一起的。不过,我还是希望这样逐一拆开讲,能帮助大家建立起一个关于编程 Harness 如何工作的整体心智模型,也更容易理解为什么它们能让 LLM 比简单的多轮聊天有用得多。
图 13
图 13:前面各节讨论的 Coding Harness 的六个主要特性。
如果你想看看这些机制是如何用干净、极简的 Python 代码实现出来的,可以去看看我的 Mini Coding Agent。
OpenClaw 可能是一个很有意思的对照对象,但它和这里讨论的系统并不完全是同一类东西。
OpenClaw 更像是一个本地的通用 Agent 平台,它也能做编码,但并不是一个专门面向终端场景的编程助手。
不过,它和 Coding Harness 之间仍然有不少重叠点:
- 使用工作区中的提示与指令文件,比如 AGENTS.md、SOUL.md 和 TOOLS.md
- 保存 JSONL 会话文件,并包含转录压缩与会话管理
- 可以生成辅助会话和子 Agent
- 等等
不过,正如前面所说,两者的侧重点并不一样。编程 Agent 优化的对象,是在代码仓库中工作、需要编程助手高效检查文件、编辑代码和运行本地工具的人;而 OpenClaw 更偏向于在聊天、频道和工作空间中运行多个长期存活的本地 Agent,编码只是它众多重要工作负载中的一项。
学AI大模型的正确顺序,千万不要搞错了
🤔2026年AI风口已来!各行各业的AI渗透肉眼可见,超多公司要么转型做AI相关产品,要么高薪挖AI技术人才,机遇直接摆在眼前!
有往AI方向发展,或者本身有后端编程基础的朋友,直接冲AI大模型应用开发转岗超合适!
就算暂时不打算转岗,了解大模型、RAG、Prompt、Agent这些热门概念,能上手做简单项目,也绝对是求职加分王🔋
📝给大家整理了超全最新的AI大模型应用开发学习清单和资料,手把手帮你快速入门!👇👇
学习路线:
✅大模型基础认知—大模型核心原理、发展历程、主流模型(GPT、文心一言等)特点解析
✅核心技术模块—RAG检索增强生成、Prompt工程实战、Agent智能体开发逻辑
✅开发基础能力—Python进阶、API接口调用、大模型开发框架(LangChain等)实操
✅应用场景开发—智能问答系统、企业知识库、AIGC内容生成工具、行业定制化大模型应用
✅项目落地流程—需求拆解、技术选型、模型调优、测试上线、运维迭代
✅面试求职冲刺—岗位JD解析、简历AI项目包装、高频面试题汇总、模拟面经
以上6大模块,看似清晰好上手,实则每个部分都有扎实的核心内容需要吃透!
我把大模型的学习全流程已经整理📚好了!抓住AI时代风口,轻松解锁职业新可能,希望大家都能把握机遇,实现薪资/职业跃迁~