真正投入生产的Agent需要状态持久化、子Agent委托、失败恢复沙箱、人类可审计轨迹等基础设施,这些与模型本身无关的运行时设计决定了Agent的实际行为上限。
"一切皆插件"架构如何重新定义 AI 智能体的本质——以及它对任何智能体技术栈构建者的启示。
在 AI 智能体工程领域,有一种持久已久的简写方式:拿一个能力强的 LLM,给它一个系统提示词和几个工具,然后就把结果称为"智能体"。这种简写对演示来说够用。一旦 AI 智能体需要运行超过几分钟、经历重启、调用子智能体、从失败的工具调用中恢复,或者让人类检查三小时前它实际做了什么,这套简写方式就崩溃了。
一旦越过这条线,AI 智能体就需要大量与模型本身无关的基础设施:安全执行工具的场所、跨轮次保持状态的方式、决定模型每次调用看到什么上下文的策略、向其他智能体委托工作的机制、限制智能体可触碰范围的沙箱、从部分故障中恢复的方式,以及可供人类或评估框架后续回放的事件记录。
整体而言,这些周边机制通常被称为智能体 harness(挽具):即位于模型与世界之间的运行时,它实际决定着 AI 智能体在实践中的行为方式。两个构建在同一底层模型上的 AI 智能体,可以因为围绕它的 harness 不同而表现得截然不同——如何管理上下文、暴露哪些工具、如何从错误中恢复、如何调度工作。
DeepSeek Harness(dsh)是 DeepSeek AI 以 MIT 许可证在开发者预览版中发布的一个开源项目,是将这种思维认真贯彻时会导致何种结果的一个有用的具体例子。它并非发明了智能体 harness 的概念——Anthropic 的 Claude Code、OpenAI 的 Codex CLI 以及各种开源智能体框架一直在向类似领域收敛。DeepSeek Harness 值得仔细阅读的原因在于它对单一架构承诺——"一切皆插件"——推进得有多远,以及这个承诺如何倒逼其余部分的设计呈现出如今的样子。
通俗地讲:DeepSeek Harness 是一个用于构建和运行编码/自动化 AI 智能体的运行时。安装后,指向一个模型提供商,就能得到一个开箱即用的可用智能体——包含文件编辑、shell 访问、网络搜索、子智能体和 Web UI。它明确地与模型解耦:除了 DeepSeek 自己的模型外,提供商目录还覆盖了 Anthropic、OpenAI、AWS Bedrock、Azure 和 Google 的 Gemini Enterprise Agent Platform,以及自定义的 OpenAI 兼容端点。设计上没有任何东西将 harness 绑定到 DeepSeek 自有模型上——这是一个颇有说明性的信号,表明这个项目实际上想要成为什么。
从技术上讲,这种分离比"代码恰好支持多个提供商"更严格。DeepSeek Harness 构建在一个名为 Cordis 的通用插件框架之上,其组合模型在一篇由北京大学和 DeepSeek 研究人员撰写的论文《A Programming Paradigm for Spatiotemporal Composability》中有描述。Cordis 通过共享上下文(ctx)为插件提供运行环境,插件通过它贡献服务、类型化事件,以及值得注意的是——可逆效应。根据项目自己的架构文档,"产品的每个部分都是一个插件,包括模型适配器、工具注册表、会话日志和智能体循环本身。"设计上不存在可以打补丁的特权核心:扩展 harness 意味着在现有插件旁挂载一个新插件,每条注册都是一条效应,当插件卸载时会干净地回滚。
这种分离之所以重要,原因很简单:它将模型变成了一个可互换的组件,而不是系统的组织原则。运行时不仅仅调用 LLM——它独立于当前应答的模型,拥有会话、工具管道和执行历史的所有权。这就是"模型与 harness 分离"的实际含义,也是使其余架构清晰可读的前提。
启动时,运行中的 dsh 实例是一个由有序层叠组成的插件树。一个 profile(文档附带了 web 和 headless 模板)列出了它堆叠哪些 bundle;bundle 是 Cordis 配置加上它所挂载代码的分发单元。dsh-base 是每个 profile 都包含的基础 bundle——模型适配器、工具、持久化、沙箱和审批策略、凭证、遥测——而 dsh-web-app 或 dsh-headless 则在其上添加浏览器 UI 或一次性运行器。层叠是确定性的且可检查的:可以运行 dsh --profile web --dump-config 查看本机将启动的确切插件树,然后用你自己的补丁文件覆盖任意行。
少数核心包锚定这棵树(每个拥有 ctx 这个共享插件上下文的某个独立部分):
从包表中放大视角,形状很直接:一个 Cordis 内核位于中心,智能体循环、模型适配器(ctx.llm)、工具注册表(ctx.tools)、技能、子智能体运行时(ctx.subagents)、沙箱和文件系统层以及会话日志(ctx.sessions)都作为同级插件挂在其上。工具注册表与沙箱通信以实际执行任何操作;会话日志从其他所有地方接收事件,fork、恢复和轨迹 UI 都读自它。关键的是,智能体循环本身只是那个列表中的又一个插件,不是其他插件向其报告的特权核心——这正是关键所在。运行时没有单一的"智能体类"供你继承;它只有一组独立可替换服务的组合。
很容易把"一切皆插件"读作一条工程口号。更有趣的问题是它实际解决的是什么问题。
单体智能体框架通常将工具列表、上下文管理策略和循环逻辑硬编码到一条执行路径中。在需要改变其中一个维度而不触动其他维度之前,这没问题——换掉沙箱用远程的、添加新的模型提供商,或给部分会话配置不同的工具集。在单体架构中,这些变更会波及共享代码路径,且难以独立测试。
DeepSeek Harness 的答案来自其文档所称的"能力接缝"(capability seam):一种由三个角色定义的可替换能力——服务定义(接口)、服务提供者(实现)和消费者(通常是面向模型的工具)。文件系统和子进程访问是一组接缝;因为 Bash、PTY 访问和代码导航工具都消费同一个接缝,将它指向远程沙箱就能让三者一起迁移,无需 fork 任何单独的工具。子智能体是另一组接缝——多个提供者实现在同一上下文中按名称共存(本地生成的子进程、共享对话历史分叉出的子进程、委托的 Claude Code 或 Codex 会话),因为不同的委托策略确实可以并肩使用,并非互相排斥。
这就是与典型单体框架的架构差异:能力不是通过条件编译实现的一个大类特性,而是独立加载的插件,它们向共享上下文做贡献,可以添加、移除或替换而无需触碰运行时的源码。项目自己的扩展指南对这点有具体说明——添加模型提供商意味着在 ctx.llm 上注册一个适配器;添加面向模型的能力意味着在 ctx.tools 上注册;限制派生进程意味着提供一个 ctx.sandbox 后端,工具消费者在派生前用它包裹。这些都不需要修改智能体循环。
DeepSeek Harness 的文档用"轮次"(turn)和"步骤"(step)来描述执行,而不是单一的扁平请求-响应周期。一个步骤是一次模型请求加上它调用的任何工具;一轮是零个或多个步骤,在输入首次被认领时开启,在没有欠模型的东西时关闭。
文档记录的轮次流程如下。一轮通过认领下一个输入加上任何排队的消息开启,然后组装插件已注册的所有提示词部分和工具 schema。那次认领会经过一个 agent/pre-step 事件,可以在模型看到它之前拒绝或重写它——而且即使被拒绝或空的第一条认领仍然会关闭一个持久化轮次,所以尝试会被记录而不是无声消失。一旦步骤开始,请求发出(agent/request → llm/stream),如果模型调用了一个工具,该调用会经过三阶段管道——tools/pre-execute、tools/execute、tools/post-execute——然后结果返回,步骤结束。如果还有欠下的工作,或新输入已到达,循环再次认领并开启另一个步骤;否则触发 agent/turn-stopping 并关闭该轮次。
有几个细节值得特别指出,因为它们解释了为什么循环要设计成这个样子,而不是一个更简单的 while 循环。agent/pre-step 是一个瀑布式事件:监听器可以重写或直接拒绝某个步骤即将看到的消息,这是注入上下文、护栏或压缩等扩展机制的插入点——完全不需要触碰循环本身的代码。即使第一条消息被拒绝,仍然会关闭一个持久化的对话轮次,因此这次尝试会被记录下来而不是默默消失。而三个工具管道事件(pre-execute、execute、post-execute)同样也是瀑布式的,意味着任何插件都可以拦截正在执行中的工具调用——通过挂载到一个定义良好的接口来实现审批门控、成本追踪或参数重写——而不是去分叉工具的实现。
这就是在生产级 AI 智能体中引入生命周期钩子的总体论据:可靠性工作——重试、护栏、成本上限、人工审批——几乎从来无法用"在主循环里再加一个 if 语句"来表达。它需要定义的拦截点,而不需要理解或修改整个控制流程。一个不暴露这些点的框架,会迫使每个运维关注点都变成围绕模型调用的大量临时包装代码。
DeepSeek Harness 中最不引人注目、但意义最深远的设计决策,可能是把会话当作一个仅追加的事件日志,而不是存储对话转录文本。deriveMessages() 通过从该日志中投影模型历史来重建模型实际看到的内容;独立的原始流式事件则纯粹为了回放和 UI 保真度而保留。文档声明了一个硬性不变量:任何到达模型请求的内容都必须能从日志中重建——新的模型可见输入需要新的会话事件类型,而不是通过旁路通道。
具体来说,日志按轮次累积严格有序的typed事件——用户/消息、一个或多个助手/消息事件、任意工具/调用和工具/结果对,以及一个结束的轮次/结束——下游所有模块都从同一个序列读取,而不是从独立缓存读取:deriveMessages() 从中投影出模型可见的历史,在其中的任意已完成轮次边界上分叉出分支,冷启动恢复通过回放日志来重启会话,轨迹视图则按来源逐事件检查它。
为什么要这样构建,而不是直接存储最终的消息历史?普通转录本回答的是"对话长什么样",但它无法回答"模型在第 12 步实际看到了什么"、"如果我们从这个点开始用不同的模型会发生什么"或"精确回放这个轨迹用于评估"。仅追加的typed事件日志可以回答以上所有问题,因为每一条事实——包括工具调用、子智能体调度和上下文注入——都是一条持久化的、可单独寻址的记录,而不是被压平的字符串。
这就是为什么分叉是可行的:例如,子智能体系统可以用父日志的"均衡已完成轮次前缀"——即到其最后一个已完成轮次为止的事件,有意排除任何进行中的、不均衡的轮次——来播种一个新的子会话,而运行时的自身不变量会接受该种子作为有效的回放输入。同样的机制支撑着进程重启后会话的恢复,以及项目 UI 暴露的"轨迹视图"功能,用于按来源逐事件检查一次运行。所有这些都不是事后追加的;它们直接源于把会话当作日志而非缓存来对待。
DeepSeek Harness 对这些抽象概念区分得很清晰,值得精确地说明它们之间的区别,因为这些术语在其他场合的 AI 智能体讨论中经常被混用:
工具是面向模型的、注册在 ctx.tools 上的单次调用能力——文件编辑、shell 执行、搜索。它们是模型调用的原子单位,也是管道护栏(pre-execute、execute、post-execute)的落脚点。
技能是智能体可以调用的可复用、可组合的指令或流程——更像是"如何出色地完成 X"的库,而不是可调用的函数。
子智能体是一种不同的能力插入点,用于将整个工作块委托给子智能体,它拥有自己的会话、自己的轮次循环,以及(根据文档)既可以是单次生命周期,也可以是能跨多次激活接收后续消息的可持续生命周期。
对于一个开发者预览阶段的项目来说,子智能体的设计异常深入。多个命名的提供者可以在同一接口背后共存——本地启动的子智能体、继承父会话历史的对 fork 子智能体,或通过各自的 SDK 运行在 Claude Code 或 Codex 内部的委托会话——调用方可以在启动时请求特定能力,如输出 schema、深度限制或受限工具集,运行时会根据所选提供者在启动子智能体之前验证这些能力,而不是默默忽略不支持的部分。可持续子智能体维护一个持久化会话,可以冷恢复、被中断,或通过一个独立的"报告"通道向其父智能体汇报,有意与普通对话分离,这样转录本永远不会把"运行时观察到的内容"和"子智能体实际说的话"混淆。
实战教训:工具、技能和子智能体解决的是不同问题——原子操作、可复用的专业知识、以及委托的自主性——将它们混为一谈(例如,把委托实现为"又一个没有状态的工具调用")往往会产生无法恢复、无法干净中断、无法区分子智能体自身措辞与运行时记账的系统。
标准工具调用意味着每个操作——读文件、再 grep、再编辑、再运行测试——都是一次独立的模型往返:模型看到一个结果,决定下一个调用,再为另一次完整的上下文传递付费。Code 模式是 DeepSeek Harness 四个预置之一(另有 Standard、Minimal 和 Creator),它通过将同一工具集作为生成的 TypeScript SDK 来暴露,从而改变了这一点。模型不再一次调用一个工具,而是针对该 SDK 编写一个短程序,由 harness 执行,这样一个原本需要五次往返的序列可以一次调用完成。
宣称的优点对于看过 AI 智能体在同一个下一步上反复燃烧 token 的人来说是显而易见的:减少模型往返次数、能够使用真正的控制流(循环、条件、批处理)而不是强迫每个分支都经过模型、以及对不需要每次新鲜判断的操作进行更确定性的组合。
代价同样真实,值得直说而不是一带而过。让模型编写并执行代码——即使是对着一个精心挑选的 SDK——相对于固定菜单中逐个验证的工具调用,会扩大攻击面并增加沙箱负担;它将部分调试负担从"哪个工具调用出了问题"转移到"生成的代码中哪一行出了问题";并且它依赖于模型可靠地产生正确且范围恰当的程序,这是一个不同于可靠地选择下一个工具调用、且不一定更简单的可靠性问题。有一点很说明问题:DeepSeek 自身发布的模型基准测试据报道使用的是 Minimal 模式——一个精简的双工具(bash 加 str_replace_editor)预置——而不是 Code 模式,这表明该项目本身将 Code 模式视为一种真正不同、而非严格更优的执行模式。
将两者并排比较很诱人,但它们运作在不同的层次上,这样理解才更有价值。
诚实的表述是:LangGraph 主要是一个编排和状态机框架——你提供模型调用和工具,它给你一个图结构、检查点和人机交互中断,用于显式控制执行流。DeepSeek Harness 的目标更为宏大——一个完整的 AI 智能体运行时,工具、沙箱、会话存储、子智能体传输和 UI 都已组装完毕,通过插件(而非图谱编写)来扩展。理论上,你可以构建一个调用 DeepSeek Harness 会话的 LangGraph 节点,反之亦然;它们并非互斥,而是回答不同的问题("我如何控制这个工作流的流向"与"我的 AI 智能体运行在什么基础设施中")。
几个架构选择经得起推敲,而不只是营销话术:
将 Harness 作为基础设施,而非提示词粘合剂。插件边界在框架层面(Cordis)强制执行,而不是留给开发者遵循的约定。
以事件日志作为单一事实来源。从日志派生模型可见的上下文,而非单独存储,这样就从根本上关闭了一整类「UI 显示的内容与模型看到的内容不同」的 bug。
能力校验式委托。子智能体系统在一个子级启动之前,会根据提供者验证请求的能力(Schema 支持、深度限制、工具过滤器),大声拒绝而不是静默忽略不支持的请求——这个小细节防止了一类常见的「看起来像成功了」的失败。
配置级扩展性。更换沙箱后端、添加模型提供者、或限定会话的工具集,这些都被记录为配置变更,而非源码补丁。
这些都不能让自主智能体默认变得可靠,项目本身也没有声称例外——它明确是一个开发者预览版,用自己的话说「仍有破坏兼容性的变更在前头」。
更好的 Harness 并不能解决模型可靠性问题:一个模型无论周围插件系统设计得多好,它幻觉出一个文件路径或误读一个 diff 依然会我行我素。长时间会话中上下文增长仍然是一个真实的成本和延迟问题,一个好的事件日志并不能消除它,只能让它可见。工具失败和部分状态仍然需要应用层处理——流水线给你的是钩子,而不是自动的正确性。沙箱化一个能写和执行任意代码的模型(如 Code 模式所做的)是一个比沙箱化固定工具菜单难得多的安全问题,目前尚不清楚这个领域已经收敛到一个令人满意的答案。长时间运行的多小时智能体任务仍然会以更好会话记账只能帮助你观察、而不能防止的方式积累成本和漂移。评估或调试一个复杂的多轮、多子智能体轨迹,即使有轨迹查看器仍然非常耗时——你还是得自己读。
其中几个原则在这个具体项目之外有很好的通用性:
将模型与运行时分离。把模型当作可交换的适配器,而不是系统的组织抽象。
把工具执行当作一等公民的子系统,有自己受保护的流水线——而不是放在智能体循环里的内联逻辑。
让智能体状态持久化、可重建,而不只是缓存在当前进程的内存中。
记录执行轨迹,而不只是最终输出——你无法调试或评估你没有记录的东西。
从一开始就为回放和恢复而设计;将其改造进无状态设计比从头构建要难得多。
使用插件或定义好的接口,而不是硬编码的集成,这样能力可以被替换而无需触碰核心逻辑。
把技能当作可组合的能力,与工具明确区分开来——可复用的know-how与可调用函数是不同的抽象。
把可观测性作为运行时的组成部分,而不是通过日志语句事后叠加的东西。
显式设计失败处理,有定义的声明周期钩子——不要依赖把整个循环包在一个 try/catch 里。
在实际可行的地方保持 Harness 与模型无关——这为你构建的其他所有东西提供了面向未来的保障。
如果把它精简到值得自己构建的最小版本,大概是这样的:一个可交换的模型适配器坐在一个智能体循环后面,该循环拥有轮次/步骤生命周期,并在每个阶段暴露钩子点。那个循环依赖三个并列的能力:从日志在每个步骤重新构建的上下文、受保护的工具执行流水线、以及可组合技能库。工具流水线依次依赖于一个隔离层——沙箱——用于任何接触文件系统或 shell 的东西,以及一个单独的委托层用于生成或恢复子智能体。这些组件每一个都写入一个单一追加写事件日志,那是系统的实际事实来源,正是这个日志使得事后回放、分叉、恢复崩溃的会话和一般可观测性成为可能——而不是你必须从分散的应用日志中重建的东西。
并非每个项目第一天就需要所有这些。但知道自己跳过了哪一块——以及跳过它你放弃了什么——要比在生产环境中才发现这个差距好得多。
DeepSeek Harness 不是一个新模型,也不需要当作模型来评估。它是一个关于 AI 智能体差异化将走向何方的赌注:不再关于哪个模型回答一个给定的调用,而是关于围绕模型构建的运行时——它决定模型看到什么、允许做什么、工作如何记录、以及如何从失败中恢复。这个赌注不是 DeepSeek 独有的——它在整个当前一代编程智能体 Harnesses 中都能看到,它们正在独立地收敛到 MCP 和 AGENTS.md 等共享协议——但「一切皆插件」的承诺由真实框架而非约定来强制执行,使 DeepSeek Harness 成为对此的一个清晰而相当严谨的例证。
下一代 AI 智能体的差异化可能不再取决于调用哪个模型,而是取决于围绕模型的运行时。如果这个方向是对的,那么有趣的工程工作根本不在模型 API 调用中——而是在这篇文章刚刚走过的所有内容中。
Sources & Further Reading
DeepSeek Harness — official GitHub repository
DeepSeek Harness — architecture documentation
DeepSeek Harness — subagen