从 OpenClaw 到 Agent Harness:Agent 的「壳」到底是怎么设计的
今年最出圈的 Agent 项目是 OpenClaw——一个奥地利开发者的周末作品,半年长成 GitHub 史上增长最快的仓库。但比「它火」更值得研究的是「它是什么」:OpenClaw 本质上不是一个更聪明的 Agent,而是一层精心设计的「壳」。这层壳在行业里有个正式的名字:Agent Harness。这篇把两件事放在一起讲透:OpenClaw 这层壳的实现细节,以及 Harness 这个通用概念的区别、实现和场景选择。
先从一个饭桌上聊出来的共识说起。
朋友是做 SaaS 的技术负责人,用 LangChain 搭了个自动写周报的 Agent。跑了两周卡住了:同样的输入,一会儿输出三段式一会儿输出流水账,改 prompt 改到怀疑人生。我问他模型用的什么,他说 GPT-4o。我说模型没问题,问题在你的 Harness。
他愣了:「Harness 是什么?我用了 LangChain 啊。」
我说:LangChain 给你的是零件,Harness 是你把零件拼成什么样的机器。你用了零件,但没设计机器——没有循环控制,没有上下文装配规则,没有工具调用的约束,全指望模型自己发挥。这不是模型的问题,是壳没搭好。
几乎同一时间,OpenClaw 在另一个极端证明了「壳」的价值:它的模型是可以换的(Claude、GPT、Gemini、本地模型都行),但护城河不在模型,全在壳的设计上。一个靠「没搭壳」翻车,一个靠「壳搭得好」封神。这层壳,就是今天的主角。
一、先分清三个概念:Framework、Harness、Gateway
这三个词经常被混用,先画清边界。
Framework(框架)是零件箱。LangChain 给你 Chain、Tool、Memory 这些组件,但怎么用它们搭出一个稳定的 Agent,框架不管,是你自己的事。
Harness(脚手架)是你用零件拼出来的那台机器。LLM 和最终产品之间的运行时层,负责四件事:循环控制、上下文装配、工具调度、状态管理。模型只决定「说什么」,Harness 决定「怎么做」。
Gateway(网关)是 Harness 的一种特殊形态。当 Harness 的核心职责是「多渠道接入 + 会话路由 + 权限控制」时,它就更像一个网关。OpenClaw 就是这类:它不是普通的 Harness,是一个以 Gateway 为中心组织的 Harness。
三者的关系一句话说清:Framework 给你零件,Harness 是拼好的机器,Gateway 是专门用来接渠道的机器。OpenClaw = Gateway 形态的 Harness,LangChain = Framework,你用 LangChain 搭出来的东西 = 你自己的 Harness。
二、OpenClaw 的壳:一个进程扛起所有
OpenClaw 的架构初看会让人不适应:这么大的名气,居然就一个常驻 Node.js 进程。这个进程一头连着将近 30 个聊天渠道(WhatsApp、Telegram、Discord、iMessage、飞书……),另一头连着带工具、记忆、技能的 Agent 运行时。
一条消息的完整旅程:渠道适配器把各家平台的消息格式归一化 → 会话管理器找到你对应的上下文 → 队列把并发请求串行化 → 进 Agent 循环(模型推理、调工具、拿结果、再推理)→ 回复沿原路返回。
这个「单进程控制面」的选择,场景匹配度极高。OpenClaw 是个人助手,瓶颈不是并发量,是「我在任何设备上都能找到我的 Agent」。单进程意味着部署零依赖(一个 Node 进程加一份配置文件),升级就重启,备份就打包目录。
Gateway 和 Agent:一个进程里的两个角色
很多人第一次看会困惑:Gateway 单独维护了路由、会话、权限,那 Agent 是怎么「接入」的?要单独配置吗?
答案是:不用。它们是同一个进程里的两个代码模块,逻辑分离、物理一体。
│ ┌──────────┐ ┌──────────────────────┐ │
│ │ Gateway │ │ Agent 运行时 │ │
│ │ 路由 │ │ ReAct 循环 │ │
│ │ 会话 │ │ 工具调度 │ │
│ │ 队列 │ │ 技能加载 │ │
│ │ 权限 │ │ 模型调用 │ │
│ │ 记忆持久化 │ │ │ │
│ └──────────┘ └──────────────────────┘ │
│ ~/.openclaw/openclaw.json │
└─────────────────────────────────────────┘
Gateway 管「消息怎么进来、给谁、按什么顺序」,Agent 管「拿到消息之后怎么想、怎么做」。默认安装什么都不用配,就用自带的 Agent 运行时。只有两种场景需要单独配置:多 Agent(一个 Gateway 养多个分身,每个独立 workspace 和模型)和外接 Agent(理论上可对接外部 Agent 服务,但官方建议没特别需求别折腾)。
所以准确地说:OpenClaw 的本体是 Gateway,不是 Agent。Agent 是可以换的——换模型、换运行时、加实例,Gateway 还是 Gateway。
配置实录:openclaw.json 长什么样
所有配置收在一个文件里,JSON5 格式。装完 openclaw onboard 向导自动生成最小配置,你只需要在环境变量里放 API key。
模型配置——主模型加降级备选:
agents: {
defaults: {
model: {
primary: "anthropic/claude-sonnet-4-6",
fallbacks: ["openai/gpt-5.4"], // 主模型挂了自动切
},
},
},
env: { ANTHROPIC_API_KEY: "sk-ant-...", OPENAI_API_KEY: "sk-..." },
}
Agent 循环行为——心跳、技能、沙箱:
agents: {
defaults: {
workspace: "~/.openclaw/workspace",
skills: ["github", "weather"],
sandbox: { mode: "non-main" },
heartbeat: { every: "30m", target: "last" },
},
},
}
多 Agent 路由——一个网关养多个分身:
agents: {
list: [
{ id: "home", default: true, workspace: "~/.openclaw/workspace-home" },
{ id: "work", workspace: "~/.openclaw/workspace-work",
model: { primary: "openai/gpt-5.4" } },
],
},
bindings: [
{ agentId: "home", match: { channel: "whatsapp", accountId: "personal" } },
{ agentId: "work", match: { channel: "whatsapp", accountId: "biz" } },
],
}
三、Harness 的四个核心职责
OpenClaw 的壳之所以稳,是因为它把 Harness 的四件事都做实了。这四件事是任何 Harness 的通用职责。光说概念不够,我们把每件事的内部机制拆开看。
循环控制的内部:一个 while 循环里的状态机
LLM 调用不是一次性的,是循环:推理→调工具→拿结果→再推理。但这个循环的每一圈,Harness 内部其实在做一组精确的状态转移:
├─ 检查保险丝:步数超了吗?时间超了吗?token 超预算了吗?
│ └─ 任一触发 → 跳出循环,走降级返回
├─ 装配上下文(见下一节)
├─ 调 LLM,拿到两种返回之一:
│ ├─ 纯文本 → 模型认为任务完成 → 跳出循环,返回结果
│ └─ tool_calls → 模型想调工具 → 进工具调度
├─ 工具结果作为 tool message 追加进消息列表
├─ 状态落盘(checkpoint)
└─ 步数 +1,进入第 N+1 圈
注意一个细节:模型只能通过「不返回 tool_calls」这一种方式主动结束循环。除此之外的所有退出路径——超时、超步数、超预算、LLM 报错——都由 Harness 裁决。这就是「模型决定说什么,Harness 决定怎么做」在代码层的含义:循环的终止权被刻意设计成了不对称的。
三重保险丝同时生效:
state = {"steps": 0, "tokens_used": 0, "messages": [task]}
while state["steps"] < self.max_steps:
if self._is_timeout(state) or self._over_budget(state):
break
resp = await self._call_llm(state["messages"])
state["tokens_used"] += resp.usage.total_tokens
if not resp.tool_calls:
return resp.content # 模型说「做完了」
for tc in resp.tool_calls:
result = await self._dispatch_tool(tc)
state["messages"].append({"role": "tool", "content": result})
state["steps"] += 1
return self._fallback(state) # 超限,返回兜底
还有一个容易漏的机制:慢速死循环检测。如果模型每圈都调同一个工具、参数只差一点点(比如 offset 每次加 1),步数保险丝根本拦不住——它在「正常工作」,只是永远工作不完。Harness 级的解法是记录最近 N 圈的工具调用签名(工具名+参数哈希),发现重复模式就强制中断。OpenClaw 的心跳任务配独立小模型,也是这个思路:心跳触发的循环天然是后台无人盯的,必须用更保守的循环参数。
上下文装配的内部:一个 token 预算分配问题
上下文装配是 Harness 最被低估的职责。你以为模型在「看」对话历史,实际上它看到的是 Harness 精心裁剪过的版本。装配的本质是一个预算分配问题:假设模型的上下文窗口是 128K token,Harness 要在调用前决定怎么分这块蛋糕:
parts = []
# 固定开销:system prompt + 工具定义,先扣掉
parts.append({"role": "system", "content": self.system_prompt})
parts.extend(self._tool_definitions())
used = self._count_tokens(parts)
# 记忆注入:检索相关记忆,但有单独的预算上限(比如 8K)
memories = self.memory_store.retrieve(current_input, top_k=5)
mem_budget = min(8_000, budget - used)
parts.append({"role": "system",
"content": f"相关记忆:\n{self._truncate(memories, mem_budget)}"})
used = self._count_tokens(parts)
# 历史对话:拿剩下的预算,从最近的消息往前装,装不下就摘要压缩
history_budget = budget - used - self._count_tokens(current_input)
parts.extend(self._fit_history(session.messages, history_budget))
parts.append({"role": "user", "content": current_input})
return parts
_fit_history 的内部逻辑值得单独说:它不是什么「留最近 10 轮」这么简单。生产级的做法是三段式——最近 3-5 轮原文保留(保证对话连贯)、更早的内容让模型生成滚动摘要(保留信息但压缩体积)、工具返回的大块结果只留结论(一个 5000 字的网页抓取结果,只留模型当时提取的那 200 字结论)。模型根本不知道还有更早的对话、还有没被检索到的记忆——它的世界就是 Harness 喂给它的这一块。装配得好,模型事半功倍;装配得烂,模型再强也白搭。
工具调度的内部:提议与裁决的分离
模型只是「提议」调用某个工具,Harness 要裁决三个问题:这个工具存在吗?当前上下文有权限吗?参数格式对吗?三个都过了才真的执行。这个「提议-裁决」分离是安全模型的根基——第八篇讲的权限闸门,在 Harness 层面就是这个 dispatcher。OpenClaw 的 sandbox.mode、渠道白名单,本质都是这个裁决器的产品化。
状态管理的内部:无状态函数之间的记忆接力
LLM 每次调用都是无状态的函数调用,是 Harness 在调用之间维护「发生了什么」。关键机制是 checkpoint 的时机:不是任务结束才存,是每圈循环结束就落盘。这样进程在任何一圈挂了,重启后都能从最近一圈恢复,只丢当前这一圈的进度。模型不记得上一轮说了什么,不记得刚调了哪个工具——这些都是 Harness 的 SessionState 在记。模型只是被一次次叫醒、喂上下文、吐结果、再被放回去。
四、流程原理:一条消息在 OpenClaw 里的完整时序
概念讲完,跟一条真实消息走一遍。你在 Telegram 里给 OpenClaw 发「帮我看看今天有什么会,下午两点前提醒我出发」,从消息发出到收到回复,壳里发生了什么:
T+10ms 渠道适配器把 Telegram 消息格式归一化成内部 Message 对象
{sender: "you", text: "...", channel: "telegram"}
T+15ms 权限检查:sender 在 channels.telegram.allowFrom 白名单里吗?
└─ 不在 → 静默丢弃或回复配对码,流程终止
T+20ms 会话管理器:按 sender 找到你的主会话,加载 SessionState
(消息历史、之前沉淀的 Markdown 记忆文件列表)
T+25ms 队列:你手机刚发的上一条还在处理 → 这条排队等
└─ 队列的意义:同一会话内的消息严格串行,防止上下文竞争
T+30ms Agent 循环启动,第一轮上下文装配:
system prompt + 技能元数据 + 相关记忆(你的通勤偏好、
常用地址)+ 压缩后的历史 + 当前输入
T+40ms LLM 第一轮推理:判断需要调 calendar.list_events 工具
T+900ms 工具调度器裁决:calendar 工具存在、有权限、参数合法 → 执行
T+1200ms 日历结果回来,追加进消息列表,checkpoint 落盘
T+1250ms LLM 第二轮推理:发现有 15:30 的会,需要再调
traffic.estimate 算路程时间 → 再走一轮调度
T+2400ms 模型不再返回 tool_calls,生成回复文本:
「今天一个会,15:30 在 XX。按路况 14:45 出门合适,
我 14:30 提醒你」
T+2450ms 循环退出。同时模型写了一条记忆(你的通勤偏好有更新)
和一条心跳待办(14:30 提醒你)到 HEARTBEAT.md
T+2500ms 回复经 Gateway 路由回 Telegram 渠道适配器,发到你的聊天窗口
两秒后你收到回复。但流程还没完——14:30 心跳定时器触发,Agent 被唤醒,读 HEARTBEAT.md 发现有条待办,主动给你发消息「该出发了」。整条链路里,LLM 只参与了「推理」那几百毫秒,剩下的全是壳在工作。
四、实现对照:OpenClaw 是怎么做这四件事的
把通用职责和 OpenClaw 的具体实现摆在一起看,会更直观。
sandbox.mode 隔离 + 细粒度权限开关 + openclaw doctor 体检 | |
~/.openclaw/),备份即打包目录 |
有几个设计特别值得说。文件化记忆:不用数据库,记忆写成一堆 Markdown 文件躺在配置目录里,你随时能翻开看它记了你什么,记错了直接改,搬家整个目录拷走。个人助手场景,透明度比检索效率重要得多。心跳机制:每半小时醒一次读 HEARTBEAT.md,检查有没有该干的活——早报、截止提醒、后台任务收尾。这让 OpenClaw 从「被动工具」变成了「主动助手」,是「养了个员工」的关键体验。Tools/Skills 分层:工具是函数级原子能力,技能是工作流级能力包,可以从 ClawHub 市场下载,也可以让 Agent 自己写。
五、场景选择:六种情况对号入座
理解了壳的设计,最后一个问题:你的场景该用什么壳?
你要个人助手,跑在聊天软件里 → OpenClaw。它的 Gateway 架构把最难的事(渠道适配、会话管理、权限控制)都做完了,别重复造轮子。注意三个坑:WhatsApp 接入走逆向协议有封号风险、权限别一路全开、心跳别配旗舰模型(一天 48 次调用的账单不小)。
你是企业团队,用 OpenAI 技术栈,快速上线第一优先 → OpenAI Agents SDK。四个 Harness 职责打包成开箱即用的抽象(Agent、Runner、Guardrail、Session),代价是绑死生态。
你的场景需要多 Agent 角色协作 → CrewAI。角色驱动的编排对这种场景是天然适配,但单 Agent 场景显得重。
你需要最大灵活性,不介意自己设计 → LangGraph。它把 Agent 循环显式建模成图(StateGraph),你画节点和边,它负责执行。最强的「Harness 设计师工具」,前提是你能把四件事想清楚。
你在做编程类 Agent → Claude Code。文件限项目目录、命令按语义分级放行、危险操作展示参数确认。在编程窄域里目前没人做得比它好,但通用性弱。
上面都不满足,核心逻辑太特殊 → 自己写。一个最精简的 Harness 核心约 200 行:循环 + 上下文装配 + 工具调度 + 状态管理。自己写的三个正当理由:核心逻辑框架装不下(80% 代码在跟框架搏斗)、性能敏感(每层抽象都是开销)、学习目的(亲手写过才真正理解框架在帮你做什么)。
六、什么时候 OpenClaw 不够用
吹了这么多,也得说清楚边界。OpenClaw 不适合的场景:
团队协作。它的设计假设是「一个用户配一个 Agent」,多人共用、权限隔离的企业场景硬套会很难受。高并发。单进程扛不了几百人同时用,面向公众的服务回分布式那套。合规敏感行业。金融、医疗对数据审计有强要求,「记忆写在 Markdown 文件里、接入用逆向协议」过不了合规评审。完全不想动手的人。装只要五分钟,但权限配置、模型选择、心跳调优都需要你理解它在干什么,想要永远不用管的,去买 SaaS。
这几个「不适合」恰好反证了 Harness 的场景匹配原则:壳没有绝对优劣,只有场景匹配。OpenClaw 的单进程对个人助手是最优解,对企业服务就是错配。
TL;DR
三个概念分清楚:Framework 是零件箱,Harness 是拼好的机器,Gateway 是专门接渠道的机器。OpenClaw = Gateway 形态的 Harness OpenClaw 的架构就一句话:单进程 Gateway 收敛路由、会话、队列、权限、记忆,Agent 运行时和模型都是可替换的 Gateway 和 Agent 是一个进程里的两个代码模块,逻辑分离、物理一体,默认安装不用单独配 Harness 四个职责:循环控制(三重保险丝)、上下文装配(模型看到的世界是裁剪过的)、工具调度(模型提议、Harness 裁决)、状态管理(模型无状态,Harness 扛一切) OpenClaw 的三大特色实现:文件化记忆(透明可迁移)、心跳机制(被动变主动)、Tools/Skills 分层(函数级 vs 工作流级) 选型看场景:个人助手 OpenClaw、OpenAI 栈用 SDK、多角色 CrewAI、深度定制 LangGraph、编程 Claude Code、太特殊就自己写 200 行 OpenClaw 的边界:团队协作、高并发、合规敏感、不想动手,四种情况别用它
— END —
夜雨聆风