刚接触 Agent 相关工程的时候,很多人都被Agent Harness和Agent Runtime这两个词绕晕过。尤其当终端里出现一条类似error: agent harness runtime "codex" is unavailable because its plugin registration is incomplete的报错时,第一反应往往是:这俩到底谁出了问题?是运行时崩了,还是载体没搭好?如果你也遇到过这种困惑,这篇内容就是为你准备的。我会从概念边界、职责分工、配置实操和常见报错四个维度,把这两个容易混淆的组件彻底讲清楚。适合正在接触 Agent 框架源码、做智能体二次开发,或者只是被 CLI 工具报错卡住但想弄明白原因的朋友。
先说结论,方便你带着框架往下看:Harness 管的是“怎么想怎么决策”,Runtime 管的是“怎么落地怎么执行”。一个是控制大脑的驾驶舱,一个是真正把动作做出来的机械臂。下面展开讲。
1. 从一个报错开始:Harness 和 Runtime 到底谁出了错
1.1 报错现场:unavailable 的含义
我自己第一次认真去查这两个概念,就是因为一条报错。当时在配置一个类似 Codex 的 AI 编程代理工具,启动后直接抛了这么一句:
error: agent harness runtime "codex" is unavailable because its plugin registration is incomplete翻译成人话就是:Agent 的外层载体(Harness)尝试去加载一个叫codex的底层执行运行时(Runtime),结果发现这个 Runtime 没有在插件注册表中完成注册,或者注册信息不完整,于是拒绝启动。
这里的unavailable不是“运行时崩溃了”,而是“运行时压根没有被正确登记”。打个比方,就像你到了公司发现工牌刷不开门,不是门锁坏了,而是行政那边根本没给你录入工牌权限。报错信息里的plugin registration指的就是这种“登记动作”——Harness 和 Runtime 之间不是直接硬编码绑定的,而是通过一个插件注册表来互相发现。
1.2 报错背后的组件边界:谁在“调用”谁
要读懂这条报错,得先搞清楚一句话:肯定是 Harness 去找 Runtime,而不是反过来。这既是工程上的架构约定,也是理解二者关系的第一把钥匙。
在一个典型的 Agent 系统里,Harness 是启动入口。它负责加载配置、初始化会话、维护上下文、调度模型调用,还要处理工具调用的循环。而 Runtime 是 Harness 手里的一把“执行工具”,负责真正把动作做出来:执行一段 Shell 命令、跑一段 Python 脚本、读写文件、发一个 HTTP 请求。所以报错出现时,实际上是 Harness 启动流程走到“初始化执行环境”这一步,发现它点名的 Runtime 不在服务名单里。
理解了这个调用方向,你再去看各种 Agent 框架的源码,就会觉得清晰很多。Harness 通常有一个 runtime registry 或者 plugin manager,Runtime 则对外暴露统一的接口,比如execute_command、run_python、read_file。Harness 通过接口调用 Runtime,Runtime 通过注册实现被 Harness 发现。
1.3 为什么要分清这两个概念
有人会问:不就是一个工具吗,我随便用用,搞清楚这两个概念有啥实际价值?我的体会是:分不清它们的区别,你连一个配置文件都改不明白。
绝大多数 Agent 工装的配置界面都会同时出现 harness 和 runtime 相关的配置项。比如你会看到harness.max_iterations这种参数,也会看到runtime.sandbox_mode这种参数。如果你不知道前者控制的是“Agent 最多思考多少轮”,后者控制的是“命令在哪种沙箱里执行”,那就很容易把参数填到错误的区块里,导致配置不生效。
更进一步,当你开始设计自己的 Agent 应用时,这个边界会直接影响你的架构选型。你是想让 Agent 跑在本地 Docker 沙箱里,还是跑在远端容器里?你是想用官方默认的执行后端,还是想接入自己团队内部的任务执行系统?这些决策本质上都是在回答同一个问题:你要换哪个 Runtime,或者你自己写一个 Runtime,但它必须满足 Harness 的接口约定。
2. 核心概念拆解:Harness 是“驾驶舱”,Runtime 是“机械臂”
2.1 Agent Harness:编排者与治理者
Agent Harness 承担的角色,用一句话概括就是:负责让 Agent “有脑子地工作”。它不关心一条命令具体是怎么被操作系统执行的,它关心的是这条命令该不该执行、什么时候执行、执行之后的结果如何回到模型上下文里。
具体来说,Harness 至少包含以下几块职责:
- 会话与状态管理:维护多轮对话的历史记录,保存和恢复 Agent 的执行上下文。你做一次任务中途断了,能不能接着上次的状态继续跑,就是 Harness 的事。
- 模型调用与上下文组装:把系统提示、工具描述、历史消息编排成模型可接受的格式,处理流式输出和函数调用结果返回。
- 工具与权限治理:定义哪些工具可以用、哪些路径可以访问、哪些命令需要人工审批。AI 编程工具里常见的“执行危险命令前询问用户”,就是 Harness 层实现的安全策略。
- 循环控制:决定 Agent 是继续思考还是结束任务。
max_iterations、early_stop这类参数都归 Harness 管。
用一个驾驶舱做类比:Harness 是那个握着方向盘的飞行员。他负责看仪表盘(读状态)、定航线(做计划)、决定什么时候拉操纵杆(发起工具调用),但他自己并不直接产生推力——那是发动机的事。发动机就是 Runtime。
还有一个容易忽略的点:Harness 往往决定了一个 Agent 工具给开发者呈现出来的“体感”。同一个底层执行 Runtime,可以有不同的 Harness。有的 Harness 偏命令行交互,适合快速验证;有的 Harness 偏 API 服务化,适合集成到 CI/CD 流水线;有的 Harness 偏图形界面,适合非技术用户操作。你选择哪种 Harness,就是选择哪种编程模型和交互方式,这是 Runtime 层面不关心的。
2.2 Agent Runtime:真正的执行后端
如果说 Harness 是大脑,那 Agent Runtime 就是小脑加四肢。它直接对接到操作系统、容器或远端执行环境,负责把 Harness 下发的“意图”变成系统里的真实动作。
Runtime 的核心能力包括:
- 命令执行:在指定的工作目录里执行 Shell 命令,捕获标准输出、标准错误和退出码。看似简单,但背后的进程管理、环境变量注入、PATH 解析、超时杀进程都是 Runtime 的活。
- 代码解释执行:比如提供一个内置 Python 解释器,让模型可以边写代码边执行,拿到运行结果后修正方案。这个能力在数据分析、脚本生成类 Agent 里尤其关键。
- 文件系统访问:读写相对路径和绝对路径的文件,并遵循 Harness 下发的权限边界。一个合格的文件访问实现还需要处理软链接、权限位、路径穿越等安全问题。
- 沙箱隔离:很多 Runtime 会把命令执行放到 Docker 容器、Firecracker 微虚拟机或者独立的用户命名空间里,避免 Agent 的误操作影响宿主机。
Runtime 和 Harness 的另一个关键区别是:Runtime 往往是无状态的,或者说它的状态管理更简单。每一条命令的执行结果都是独立的,运行完就返回一堆输出字节;而 Harness 需要把这些输出再整理进上下文,让模型判断下一步怎么走。这也是为什么很多运行时抽象都长得特别像“函数”:输入一段代码或命令,返回执行结果。这种设计让 Runtime 很容易被替换、测试和横向扩展。
2.3 一张表看清 Harness 与 Runtime 的职责边界
为了让你更直观地对照,我把常见的职责项整理成表格:
| 能力维度 | Agent Harness | Agent Runtime |
|---|---|---|
| 核心使命 | 编排决策流程 | 执行实际动作 |
| 是否调用大模型 | 是,负责模型推理和上下文管理 | 否,只负责动作落地 |
| 会话状态 | 负责保存和恢复多轮状态 | 通常无状态或轻量状态 |
| 工具定义 | 负责声明可用工具及参数格式 | 负责实现具体工具逻辑 |
| 权限审批 | 负责人工审批、安全策略 | 负责在底层遵守隔离约束 |
| 典型故障表现 | 会话丢失、上下文溢出、循环卡死 | 命令超时、依赖缺失、沙箱创建失败 |
| 类比 | 驾驶舱/指挥官 | 发动机/机械臂 |
2.4 为什么插件注册机制如此关键
回到文章开头那条报错,你会发现有一个反复出现的关键词:plugin registration。为什么 Harness 和 Runtime 之间非要加一层插件注册的间接层,而不是直接写死调用关系?
我的理解是,这跟 Agent 生态的快速迭代有关。Runtime 是变化最频繁的部分:今天用官方沙箱,明天想换成云上容器,后天想接入内部任务调度系统。如果 Harness 代码里硬编码了所有 Runtime,那每接入一个新执行后端都要改 Harness 主程序,风险太大,迭代也太慢。插件机制让 Runtime 可以独立开发、独立发布,只要实现了约定好的接口,并在注册表中登记自己的名称和元数据,Harness 就能在运行时动态发现并加载。
这套思路其实在 IDE、构建工具、浏览器扩展里已经很成熟了。比如你在 VS Code 里装语言插件,本质上就是向编辑器注册一个“我能处理这种语言”的信号。Agent 框架把同样的模式搬过来,只不过注册的对象从“语法高亮器”变成了“执行运行时”。
了解了这一点,再遇到registration is incomplete这类报错时,你就不会再把它当成一个黑盒错误,而是会自然地去检查:注册表里到底有没有这个条目?条目里的字段是不是齐全?版本是否兼容?
3. 实操对比:配置一个可用的 Harness + Runtime
3.1 一个典型的插件式架构长什么样
纸上谈兵告一段落,下面进入动手环节。虽然不同的 Agent 框架在具体配置语法上有差异,但底层逻辑是共通的。我用一个简化的 YAML 配置示例来展示 Harness 和 Runtime 是如何在工程里配合的,这个结构你可以直接迁移到自己用的工具上。
# agent.yaml agent: name: my-coding-agent harness: type: "@agentkit/terminal-harness" options: interactive: true max_iterations: 20 auto_approve: false workspace: "./project" runtime: type: "codex" options: sandbox: docker image: "python:3.11-slim" timeout_seconds: 120 workdir: "/workspace"上面对应关系非常明确:harness区块定义的是决策控制层的参数,runtime区块定义的是执行后端层的参数。你以后照这个结构去查文档、改配置,心里就有数了。
3.2 关键配置字段逐个看
我挑几个经常被搞混的字段展开说,它们最能体现 Harness 与 Runtime 的差异。
先看harness.max_iterations。这个字段的意思是:Agent 在结束任务前,最多允许执行多少轮“思考-行动-观察”循环。它控制的是决策深度,而不是执行时间。就算单条命令执行只要一秒,如果模型一直在循环里反复试错,任务也可能很久才结束。调大这个值能让 Agent 更耐心,但也更烧 token;调小则偏向快速收敛,适合任务很明确的场景。
再看runtime.sandbox。这个是 Runtime 层的核心配置。docker表示每条命令都在独立的 Docker 容器里运行,资源隔离和文件系统隔离都相对干净。备选值通常还有local(直接在宿主机上跑,快但有风险)和remote(连接远端执行服务,适合大型计算任务)。选择哪一种,考验的是你对执行速度、安全性和资源消耗三者平衡的把握。
千万不要把harness.interactive和runtime.sandbox搞混。前者决定要不要在每一步之前弹窗征求用户确认,例如执行rm -rf之前先问一嘴;后者决定命令实际跑在哪里。一个管流程,一个管环境,互相独立。
3.3 实操:查看当前可用的 Runtime 列表
配置文件里写明了要使用某个 Runtime,但实际能不能用,取决于这个 Runtime 有没有成功注册到 Harness 的插件系统里。绝大多数框架都会提供一个 CLI 命令来查看注册状态,我习惯把它当作“体检”的第一步。命令大概是这样的:
# 列出当前 Harness 能发现的所有已注册 Runtime agent harness runtime list # 检查某个 Runtime 的注册元数据是否完整 agent runtime validate codex如果输出显示codex不在列表里,或者validate命令报告注册信息缺失,那配置文件写得再漂亮也白搭——Harness 根本找不到执行后端。这时候就要进入下一节的排查流程了。
如果validate通过了,但实际启动还是报错,那就要考虑运行环境层面的问题。比如 Runtime 依赖了本机的 Python 解释器,但当前PATH里没有;或者 Runtime 需要 Docker 守护进程,但 Docker 没有启动。记住一个原则:Harness 层面的问题看日志,Runtime 层面的问题看环境。日志会告诉你注册和发现的流程走到哪一步断了,环境检查会告诉你执行依赖缺了什么。
4. 排查实录:Runtime 不可用时怎么处理
4.1 常见报错速查表
在实际使用中,unavailable只是众多 Runtime 相关报错的一种。我把最常见的几种整理成一张速查表,你遇到类似报错可以对照着看:
| 报错现象 | 可能原因 | 排查方向 |
|---|---|---|
runtime "codex" is unavailable because its plugin registration is incomplete | Runtime 插件的注册元数据缺失或校验失败 | 检查插件安装目录、重新运行注册脚本 |
runtime "codex" not found | 配置里写了 codex,但注册表里根本没有这个条目 | 列出已注册 Runtime,确认插件是否安装 |
plugin registration failed: duplicate name | 同一个 Runtime 名被注册了两次 | 检查多个插件目录是否存在重复安装 |
runtime initialization failed | 注册是成功的,但启动时报环境依赖错误 | 检查 Docker 进程、Python 路径、系统资源 |
version mismatch between harness and runtime | Harness 主程序与 Runtime 插件版本不兼容 | 升级或降级其中一方到锁定的兼容版本 |
4.2 三步定位法:从日志到环境
拿到一条 Runtime 类报错,我建议按以下三步来排查,避免上来就乱试。
第一步,开启详细日志,确认报错发生的阶段。大多数框架都支持--verbose或--debug参数。你要重点看报错之前那几行日志:是发生在“加载插件清单”阶段,还是“解析插件元数据”阶段,还是“初始化运行时进程”阶段?三个阶段对应的处理方式完全不同。注册元数据有问题就重装插件,运行时进程起不来就检查系统服务。
第二步,用验证命令直接探测注册状态。像前文提到的agent runtime validate codex,这种命令会主动检查插件入口文件、注册表条目、接口实现是否齐全。它能立刻告诉你到底是“插件没装”还是“装了但没注册”还是“注册了但格式不对”。
第三步,检查 Runtime 的执行依赖。如果注册没有问题,那大概率是 Runtime 真正要启动执行环境的时候缺了东西。最常见的三类依赖是:Docker 守护进程、特定版本的编程语言解释器、网络访问权限。逐一确认,速度很快。
4.3 独家避坑经验
这里分享几个我在实际项目里踩过、也帮朋友排查过的坑,希望能帮你省点时间。
避坑一:重装插件时不要只删目录。有些框架的插件安装会往全局注册表里写元数据。你只删了插件目录,注册表里的残留条目还在,下一次安装会提示重复,甚至直接拒绝注册。正确的做法是先卸载(uninstall),再安装(install),让工具清理注册表。
避坑二:全局工具链和项目级依赖要分清。我遇到过一次特别迷的报错:同一个项目在 A 机器上正常,在 B 机器上死活跑不起来。后来发现 B 机器上存在两个 Python 版本,Runtime 在全局目录里找到的是一个旧版本解释器,缺了某个依赖包。这个问题的本质是 Runtime 的依赖搜索路径和 Harness 的当前工作目录没有对齐。解决方法是把 Runtime 路径或虚拟环境路径显式写进配置,不要让工具自动探测。
避坑三:升级要谨慎,锁定版本更安心。“这周还能用,升级完就废了”是 Agent 工具链里最常见的悲剧。因为 Harness 和 Runtime 的接口耦合度很高,升级 Harness 往往需要同步升级 Runtime 插件,甚至迁移配置格式。我在生产环境里的做法是:把 Harness 主程序和 Runtime 插件的版本写进项目的 lock 文件,统一升级,而不是单独升某一个。这样能大幅降低莫名其妙的兼容性 bug。
避坑四:权限审批配错,表现骗人。还有一个容易被误判为 Runtime 故障的场景:Harness 卡在某条命令前等待用户确认,如果你设置了auto_approve: false但终端没有触发交互提示,看起来就像“Agent 假死”。实际上不是 Runtime 挂了,是 Harness 的交互通道没打通。检查一下是不是在非交互式终端里运行了需要交互的命令,要么改成auto_approve: true,要么换到能弹出交互提示的终端。
5. 认清边界后的一些实际体会
5.1 这个边界如何指导日常开发
弄清楚 Harness 和 Runtime 的区别,不只是为了看懂报错,它对你在真实项目里做技术决策很有帮助。
当你的 Agent 任务执行变得很慢时,你可以先判断瓶颈在哪个层:如果是模型在反复思考,那是 Harness 的循环策略和上下文管理需要优化,比如减少遗留的历史消息、降低max_iterations;如果是命令执行本身慢,那是 Runtime 的问题,可以考虑换更强的沙箱机器、优化执行路径,或者用并行执行能力更强的 Runtime。
当你想给 Agent 增加新能力时,这个边界帮你决定代码应该写在哪。让模型多一个“决策维度”,比如能根据代码结构选择不同编译命令,这是 Harness 层的工具定义问题;让 Agent 能在全新的运行环境里干活,比如接入一个 GPU 计算集群,这是 Runtime 层的对接问题。想清楚这件事,团队协作时就不会出现“我让你加沙箱能力,你怎么去改提示词”的尴尬。
5.2 如果自己设计 Agent,怎么选型
最后给想自己动手搭 Agent 的朋友一些选型建议。如果你只是想在本地快速验证想法,优先选一个默认配置开箱即用、Harness 内置了交互终端的工具,把 Runtime 先用本地无沙箱模式跑通逻辑,再考虑安全加固。
如果你的目标是做一个长期运行的自动化服务,那从一开始就要把 Harness 的会话恢复能力和 Runtime 的沙箱隔离能力当成硬指标。宁可前期慢一点,也要确保任务中断后能恢复,命令执行不会污染宿主机。
如果你的团队里有成熟的执行平台,比如内部的任务调度系统或云端沙箱服务,那最合适的路线是:保留现成的 Harness,自己封装一个满足接口约定的 Runtime 插件。这也是插件注册机制最值钱的地方——不用从零造轮子,只要实现好统一接口,就能把整套执行后端替换成自己团队的方案。
我在实际接触这套体系的过程中最大的体会是:很多看似晦涩的 Agent 工具都只是把过去工程界的优秀实践搬了过来,Harness 和 Runtime 的分离就是典型的“控制面与数据面分离”思想在智能体领域的一次迁移。理解了它,你以后读任何一个 Agent 框架的源代码,都会觉得路径清晰、不再迷茫。