本文作者 Sebastian Raschka 是 AI 领域的知名学者,曾任威斯康星大学麦迪逊分校的统计学教授。
在本文中,我将探讨编码智能体(coding agents)及其智能体编排(agent harnesses)的整体设计:它们究竟是什么、工作原理如何,以及在实际应用中各组件是如何协同运作的。
广泛地讲,智能体之所以成为一个备受关注的重要议题,是因为近期实用型大语言模型(LLM)系统所取得的诸多进展,不仅仅归功于模型本身的性能提升,更在于我们如何去运用这些模型。在许多实际应用场景中,智能体周边的支撑系统 —— 例如工具调用、上下文管理及记忆机制 —— 所发挥的作用,其重要性丝毫不亚于模型本身。这也解释了为何像 Claude Code 或 Codex 这类系统,往往给人的感觉要比单纯在普通聊天界面中使用的同款基础模型显得更为强大、更具能力。
在本文中,我将详细阐述构成编码智能体的六大核心构建模块。
Claude Code、Codex CLI 及其他编程智能体
如果你是开发者的话,你可能已经很熟悉 Claude Code 或 Codex CLI 了,但为了铺垫背景,不妨先说明一下:它们本质上属于「智能体式」编程工具,通过在应用层 —— 即所谓的「智能体编排」(agentic harness)—— 中对大型语言模型(LLM)进行封装,从而使编程任务变得更加便捷且高效。
图 1:Claude Code CLI、Codex CLI 和我自建的代码智能体。
编程智能体(Coding agents)专为软件开发工作而设计;在这一领域,关键要素不仅在于模型的选择,更在于其周边的系统架构 —— 包括代码仓库的上下文环境、工具的设计、提示缓存的稳定性、记忆机制以及长会话的连续性。
这种区分至关重要,因为当我们探讨大型语言模型(LLM)的编程能力时,人们往往会将模型本身、其推理行为以及作为产品的智能体混为一谈。不过,在深入探讨编程智能体的具体细节之前,请允许我先简要补充一些背景信息,阐明「大型语言模型」、「推理模型」和「智能体」这三个更宏观概念之间的区别。
大模型、推理模型与智能体的关系
大型语言模型(LLM)本质上是一种核心的「预测下一个 token」的模型。而「推理模型」虽然仍属于 LLM 的范畴,但通常经过了专门的训练和 / 或提示工程优化,使其在推理过程中投入更多的计算资源,用于执行中间步骤的逻辑推理、结果验证,或在候选答案集合中进行搜索。
智能体(Agent)则是一个构建在模型之上的抽象层,可以将其理解为围绕核心模型运作的一个「控制回路」。通常情况下,当接收到一个既定目标后,智能体层(或称「编排框架」)会负责决策下一步需要检查哪些信息、应当调用哪些工具、如何更新自身的状态,以及何时终止任务等一系列操作。
粗略地讲,我们可以用这样一个类比来理解它们之间的关系:LLM 相当于引擎,推理模型则相当于经过强化的引擎(性能更强,但运行成本也更高),而智能体编排框架则扮演着辅助我们操控这一引擎的角色。尽管这个类比并不完美 —— 毕竟我们也可以将常规 LLM 和推理型 LLM 作为独立的模型直接使用(例如在聊天界面或 Python 交互会话中)—— 但我希望它能有效地传达出其中的核心要义。
图 2:传统大语言模型(LLM)、推理型大语言模型(或推理模型),以及封装在智能体编排框架中的大语言模型之间的关系。
换句话说,智能体就是一个在特定环境中反复调用模型的系统。
因此,简而言之,我们可以将其归纳如下:
-
大型语言模型:原始模型。
-
推理模型:一种经过优化的 LLM,旨在输出中间推理过程(痕迹),并具备更强的自我验证能力。
-
智能体:一个循环执行的实体,它结合使用了模型、工具、记忆模块以及环境反馈。
-
智能体编排框架(Agent Harness):围绕智能体构建的软件支架,负责管理上下文、工具调用、提示词、状态以及控制流。
-
代码编排框架(Coding Harness):智能体框架的一种特例;即专用于软件工程任务的框架,负责管理代码上下文、工具、代码执行以及迭代反馈。
如上所述,在智能体和编码工具的语境下,我们还会遇到两个流行术语:智能体编排框架和(智能体驱动的)代码编排框架。后者是围绕模型构建的软件支架,旨在辅助模型高效地编写和编辑代码。而智能体编排框架的范畴则更为广泛,并不局限于代码领域(例如,可以参考 OpenClaw 项目)。Codex 和 Claude Code 均可被视为代码编排框架的实例。
总之,更优秀的大模型能为推理模型(通常需要经过额外的训练)奠定更坚实的基础;而智能体框架则能进一步挖掘并充分发挥该推理模型的潜能。
诚然,LLM 和推理模型本身也具备独立解决编码任务的能力(即无需借助编排框架辅助);但实际上,编码工作远不止是单纯的「下一个 token 生成」(Next-token generation)。编码工作很大程度上还涉及代码仓库导航、搜索、函数查找、代码差异应用(Diff)、测试执行、错误排查,以及在整个过程中维持所有相关信息的上下文连贯性。
程序员们想必深知这是一项极耗心力的脑力劳动 —— 这也正是我们在专心编码时最不愿被打扰的原因所在 :))
图 3。一个编码编排框架由三个层级构成:模型族、智能体循环以及运行时支持。其中,模型充当「引擎」;智能体循环驱动迭代式的问题求解过程;而运行时支持则提供底层的「管道」基础设施。在循环内部,「观察」环节从环境中收集信息;「审视」环节对这些信息进行分析;「决策」环节选定下一步行动;最后由「执行」环节付诸实施。
这里的关键要点在于,一套优秀的「编码辅助框架」能让具备推理能力和不具备推理能力的模型,在实际表现上显得比单纯置于普通聊天框中强大得多 —— 因为它有助于处理上下文管理等一系列关键任务。
编码辅助编排框架
正如上一节所提到的,当我们提及「辅助编排框架」(Harness)时,通常是指围绕在模型外部的那一层软件层。这一层负责组装提示词(Prompts)、调用外部工具、追踪文件状态、应用代码修改、执行终端命令、管理权限、缓存稳定前缀、存储记忆信息,以及处理更多其他任务。
如今,在使用大模型时,相比于直接向模型输入提示词,或是使用简单的网页聊天界面(后者更接近于「上传文件后进行对话」的模式),正是这一层辅助框架塑造了用户体验的绝大部分。
在我看来,当前各类大模型的「原生版本」在能力上已趋于同质化(例如,GPT-5.4、Opus 4.6 和 GLM-5 等模型的原生版本之间),因此,这套辅助框架往往就成了决定某款大模型表现优于另一款的关键差异化因素。
这纯属推测,但我猜想:如果我们把当前最新、能力最强的开源权重大型语言模型之一(例如 GLM-5)置入一套类似的辅助框架之中,它的表现很可能就能与 Codex 环境下的 GPT-5.4,或是 Claude Code 环境下的 Claude Opus 4.6 达到同一水平。话虽如此,针对特定的辅助框架进行一些「后训练」通常还是有益的。举例来说,OpenAI 历史上就曾针对 GPT-5.3 模型维护过两个不同的变体版本:GPT-5.3 和 GPT-5.3-Codex。
在下一节中,我将深入探讨具体细节,并以我开发的「Mini Coding Agent」项目为例(GitHub 链接:github.com/rasbt/mini-…
图 4:编码智能体 / 编码编排框架的主要特性,将在后续章节中讨论。
顺便一提,为了行文简洁,本文中「编码智能体」和「编码编排框架」这两个术语有时会互换使用。(严格来说,智能体是模型驱动的决策循环,而框架是提供上下文、工具和执行支持的外部软件框架。)
图 5:一个极简但功能完备、从零构建的「迷你编程智能体」(纯 Python 实现)。
言归正传,下方列出了编程智能体的六大核心组件。你可以查阅我所构建的那个极简但功能完备、从零构建的「迷你编程智能体」(纯 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、实时代码库上下文
这或许是最显而易见的组件,但同时也是至关重要的组件之一。
当用户发出「修复测试」或「实现 xyz 功能」之类的指令时,模型应当能够识别当前是否处于 Git 代码库环境中,正处于哪条分支上,哪些项目文档可能包含相关指引,诸如此类。
之所以如此,是因为这些细节往往会改变或影响模型应采取的正确行动。例如,「修复测试」这一指令本身并非一条自足的指令。如果智能体能够查阅诸如 AGENTS.md 或项目 README 之类的文档,它便能获知应当执行哪条测试命令等具体信息。若能知晓代码库的根目录位置及整体布局,它便能直奔目标位置进行查找,而非盲目猜测。
此外,Git 分支信息、当前状态及提交记录等数据,也能提供更为丰富的上下文信息,帮助智能体了解当前正在进行哪些变更,以及应当将工作重心聚焦于何处。
图 6:智能体运行框架首先构建一份简要的「工作区摘要」,随后将其与用户请求合并,以此为项目提供额外的上下文信息。
核心要点在于:该编程代理在着手执行任何任务之前,会预先收集相关信息(即作为工作区摘要的「稳定事实」);这样一来,它就不必在处理每一个提示词(Prompt)时都从零开始,陷入缺乏上下文的窘境。
2、提示词结构与缓存复用
一旦智能体获得了代码仓库的概览视图,接下来的问题便是:如何将这些信息有效地输入给语言模型?上一幅图中展示了这一过程的简化视图(即「组合提示词:前缀 + 请求」);但在实际操作中,若针对用户的每一次查询都重新合并并处理一遍工作区摘要,将是相对低效且浪费资源的做法。
换言之,编程会话往往具有重复性,且智能体所遵循的规则通常保持不变。各类工具的功能描述通常也维持原样。甚至连工作区摘要的内容,通常也(在很大程度上)保持稳定。真正发生变化的主要因素,通常仅限于最新的用户请求、近期的交互对话记录,以及可能涉及的短期记忆信息。
如图下方所示,「智能」运行时环境并不会在每一个交互回合中,都将所有信息重新打包成一个庞大且未作区分的整体提示词。
图 7:智能体编排框架构建一个「稳定提示前缀」,随后加入不断变化的会话状态,并将合并后的完整提示输入给模型。
与第 1 节的主要区别在于:第 1 节侧重于收集代码仓库的相关事实信息;而在本节中,我们的重点是如何高效地封装并缓存这些事实,以便在后续对模型进行反复调用时加以复用。
所谓「稳定提示前缀」中的「稳定」,意指其中包含的信息变动极小。它通常涵盖了通用指令、工具描述以及工作空间的概览信息。如果其中未发生任何实质性变化,我们便不希望在每一次交互过程中都耗费计算资源将其从头重建。
而其他组件的更新频率则要高得多(通常是每一轮交互更新一次)。这些组件包括短期记忆、近期的对话记录以及最新的用户请求。
简而言之,针对「稳定提示前缀」所实施的缓存机制,其核心理念就在于:通过智能的运行时环境,尽可能地对这部分内容进行复用。
3、工具访问与使用
工具访问与使用环节让交互体验从聊天逐渐转变为智能体。
普通模型可以生成文字形式的命令建议,但编码框架中的大模型应该执行更精准、更有用的任务,并能够实际执行命令并获取结果(而不是让我们手动调用命令并将结果粘贴回聊天窗口)。
但编排框架通常不会让模型随意生成语法,而是提供一个预定义的、包含明确输入和边界的可用工具列表。(当然,像 Python 的 subprocess.call 这样的工具也可以包含在内,这样智能体就可以执行任意范围的 shell 命令。)
工具使用流程如下图所示。
图 8:模型发出一个结构化操作,框架对其进行验证,并可选择性地请求批准,然后执行该操作,并将有界结果反馈回循环。
为了说明这一点,以下示例展示了用户使用迷你编码智能体时通常看到的界面。(由于它非常简洁,并且使用纯 Python 编写,没有任何外部依赖,因此不如 Claude Code 或 Codex 那样美观。)
图 9:Mini Coding Agent 中工具调用审批请求的示意图。
在此环节中,模型必须选择一个编排框架(harness)能够识别的动作,例如列出文件、读取文件、执行搜索、运行 Shell 命令、写入文件等。同时,它还必须以一种编排框架能够进行校验的格式提供相应的参数。
因此,当模型请求执行某项操作时,运行时环境会暂停执行,并运行一系列程序化检查,例如:
-
「这是一个已知的工具吗?」
-
「提供的参数是否有效?」
-
「此操作是否需要用户的审批?」
-
「请求访问的路径是否位于工作区(workspace)范围之内?」
只有在通过了上述所有检查之后,实际的操作指令才会被真正执行。
诚然,运行编码智能体确实伴随着一定的风险;但得益于编排框架的这些检查机制,系统的可靠性也得到了提升,因为模型无法执行完全随意、不受控的指令。
此外,除了能够拒绝格式错误的动作请求并实施审批门控机制外,通过对文件路径进行校验,还可以确保文件访问操作始终被限制在代码仓库(repo)的范围之内。
从某种意义上说,编排框架虽然限制了模型的自由度,却显著提升了其实用性。
4、最小化上下文膨胀
上下文膨胀并非编码智能体所独有的问题,而是大模型普遍面临的一个难题。诚然,如今的 LLM 已能支持越来越长的上下文窗口,但长上下文依然成本高昂,且若其中夹杂大量无关信息,还可能引入额外的「噪声」。
在多轮对话过程中,编码智能体比普通 LLM 更易受上下文膨胀之苦 —— 这主要是由于频繁的文件读取、冗长的工具输出、系统日志等因素所致。
如果运行时环境对上述所有信息都采取「全保真」的存储策略,那么可用的上下文 Token 额度很快就会被耗尽。因此,一套优秀的代码智能体编排框架(Coding Harness)通常会采取相当精妙的策略来应对上下文膨胀问题,其处理手段远比普通聊天界面那种单纯的「截断」或「摘要」操作要复杂得多。
从概念层面来看,编码智能体中的「上下文压缩」机制大致可按下方图示所概括的流程运作。具体而言,我们在此将视角进一步聚焦于上一节图 8 中所展示的「截取」(Clip,即第 6 步)环节。
图 10:在重新送入提示词之前,系统会对大篇幅的输出进行截断,对较早的读取内容进行去重,并对交互记录(Transcript)进行压缩。
一个极简的编排框架至少会采用两种「紧凑化」策略来应对这一问题。
第一种策略是「截断」(Clipping),即缩短冗长的文档片段、大型工具输出、内存笔记以及交互记录条目。换言之,它能防止任何单段文本仅仅因为内容冗长,就独占了大部分的提示词预算。
第二种策略是「记录缩减」或「摘要化」,即将完整的会话历史(关于这一点,下一节会有更详细的阐述)转化为一段篇幅更小、更适合作为提示词输入的摘要。
这里的关键技巧在于:保留近期事件的更多细节,因为它们往往与当前步骤的关联度更高;而对于较早期的事件,我们会采取更激进的压缩手段,因为它们的相关性通常较低。
此外,我们还会对较早期的文件读取操作进行去重处理,从而避免模型因同一文件在会话早期被反复读取,而不得不一遍又一遍地重复接收相同的文件内容。
总而言之,我认为这正是优秀「编码智能体」设计中那些往往被低估、看似枯燥乏味的关键环节之一。许多表面上归功于「模型质量」的优异表现,实际上恰恰源自于「上下文质量」的提升。
5、结构化会话记忆
在实践中,此处涵盖的所有这 6 个核心概念都是高度交织在一起的;不同的章节和图示会以不同的侧重点或粒度来阐述这些概念。在上一节中,我们探讨了在提示阶段(prompt-time)如何利用历史信息,以及如何构建一份紧凑的会话记录(transcript)。当时的核心问题是:在下一轮交互中,究竟应该将多少过往的历史信息回传给模型?因此,那一节的重点在于信息的压缩、截断、去重以及时效性。
而本节 —— 即「结构化会话记忆」—— 所探讨的则是历史信息在存储阶段(storage-time)的内部结构。本节的核心问题在于:作为智能体,它应当随着时间的推移保留哪些信息,并将其作为永久性的记录?因此,本节的重点在于:运行时环境应维护一份更为完整的会话记录作为「持久化状态」;与此同时,它还应维护一个更为轻量级的「记忆层」—— 该记忆层体积较小,且其内容会经历修改与压缩处理,而非仅仅进行简单的追加。
综上所述,一个编程智能体会将其状态拆分为(至少)两个层级:
-
工作记忆(Working Memory):即智能体显式维护的一份经过高度提炼、体积较小的状态信息。
-
完整会话记录(Full Transcript):这份记录涵盖了所有的用户请求、工具输出结果以及大模型的响应内容。
图 11:新事件会被追加到「完整对话记录」中,并被归纳总结至「工作记忆」里。磁盘上的会话文件通常以 JSON 格式存储。
上图展示了两个主要的会话文件 ——「完整对话记录」和「工作记忆」—— 它们通常以 JSON 文件的形式存储在硬盘上。正如前文所述,「完整对话记录」保存了完整的历史信息,即使智能体程序被关闭,该记录也能支持后续的会话恢复。「工作记忆」则更像是一个经过提炼的精简版本,其中包含了当前最重要的信息;这一点在功能上与「精简对话记录」有着一定的关联。
不过,「精简对话记录」与「工作记忆」各自承担的职责略有不同。「精简对话记录」主要用于提示词的重构。其作用是向模型提供近期历史信息的压缩视图,从而使模型无需在每一轮对话中都查阅完整的对话记录,便能顺畅地延续对话。「工作记忆」则更侧重于保障任务的连续性。其作用是维护一份小巧且经过显式管理的摘要,专门用于记录跨轮次对话中那些至关重要的信息,例如当前正在执行的任务、重要的相关文件以及近期的备忘记录等。
参照上图中的第 4 步,在随后的下一轮对话中(为避免上图显得过于繁杂,该轮次并未在图中展示),最新的用户请求 —— 连同大模型的响应及工具的输出 —— 将被作为一条「新事件」,同时记录并追加到「完整对话记录」与「工作记忆」之中。
6、使用(有界)子智能体进行任务委托
一旦智能体拥有了工具和状态,下一个有用的功能之一就是任务委托。
原因在于,它允许我们通过子智能体将某些工作并行化为子任务,从而加速主任务的执行。例如,主智能体可能正在执行某个任务,但仍然需要一些辅助信息,例如哪个文件定义了某个符号、某个配置的含义是什么,或者某个测试失败的原因。将这些信息拆分成一个有界子任务会很有用,而不是强制一个循环同时处理所有线程的工作。
(在我的迷你编码代理中,实现更简单,子智能体仍然同步运行,但基本思想是相同的。)
子智能体只有在继承足够的上下文来执行实际工作时才有用。但如果我们不加以限制,就会出现多个智能体重复工作、访问相同文件或生成更多子智能体等问题。
所以,棘手的设计问题不仅在于如何生成子智能体,还在于如何绑定子智能体。
图 12:子智能体继承了足以使其发挥作用的上下文,但其运行边界比主智能体更为严格。
这里的关键在于:子智能体既继承了足够的上下文以确保其有效性,同时也受到了一定的限制(例如,被设定为只读模式,且递归深度受到限制)。
Claude Code 早在很久以前便已支持子智能体功能,而 Codex 则是近期才引入这一特性的。通常情况下,Codex 并不会强制将子智能体设定为只读模式;相反,它们往往会继承主智能体的大部分沙箱环境与审批配置。因此,这里的「边界」更多是指任务的范围界定、上下文环境以及执行深度等方面的限制。
总结
上一节旨在概述编码智能体系统的主要组成部分。正如前文所述,从实现层面来看,这些组件之间或多或少都存在着深度交织与耦合。不过,我仍希望通过逐一剖析这些组件的方式,能有助于读者构建起一套关于编码智能体编排框架运作机制的整体心智模型,并理解为何相比于简单的多轮对话模式,代码智能体能够显著提升大语言模型的实用价值。
图 13:前文讨论过的编码编排框架(Coding Harness)的六大主要特征。
如果你有兴趣看到这些特征是如何通过简洁、极简风格的 Python 代码实现的,不妨看看「Mini Coding Agent」。
它与 OpenClaw 相比有何不同?
OpenClaw 确实是一个值得拿来对比的有趣案例,但它与本文所探讨的系统并非完全属于同一类型。
OpenClaw 更像是一个运行于本地的通用型智能体平台 —— 尽管它也具备编码能力 —— 而非一款专精于(终端环境下的)编码辅助工具。
不过,它与编码辅助框架之间仍存在诸多重叠之处:
-
它会在工作区内利用提示词和指令文件,例如 AGENTS.md、SOUL.md 和 TOOLS.md;
-
它会保存 JSONL 格式的会话记录文件,并具备会话记录压缩与会话管理功能;
-
它能够衍生出辅助会话及子智能体;
-
等等。
然而,正如上文所述,两者的侧重点截然不同。编码智能体的设计初衷,是为了优化个人开发者在代码仓库中工作时的体验 —— 即高效地指挥辅助工具去检查文件、修改代码并执行本地工具。相比之下,OpenClaw 的设计重心则在于跨聊天窗口、频道及工作区运行大量「长生命周期」的本地智能体,而编码工作仅仅是其众多重要任务负载之一。
参考原文: