近期研读OpenClaw源码,发现其Harness层设计精炼,核心目标明确:将大语言模型(LLM)的不确定性控制在可控环境中,同时保持系统灵活性。
本文基于此整理关键洞见,供LLM工程化从业者参考。
一、Agent开发的核心痛点
Agent开发的最大挑战并非模型能力不足,而是调用过程中的不确定性:可能遭遇任务阻塞、无限循环或上下文窗口耗尽。LLM本质为黑盒,输入Prompt后结果不可预测,有时正常返回,有时持续调用工具无法终止。
OpenClaw Harness层作为结构化运行壳,专门在LLM与真实世界间建立可控屏障,统一管理关键环节。
二、主链路执行机制
入口函数runEmbeddedPiAgent()(run.ts:255)流程简洁:
runEmbeddedPiAgent() [run.ts:255]
├── 模型解析与认证
├── 上下文窗口评估
├── 运行尝试循环(最大160次重试)
│ └── runEmbeddedAttempt() [run/attempt.ts:749]
│ ├── SessionManager初始化
│ ├── 系统Prompt构建
│ ├── 工具集创建
│ └── 流式订阅
└── 错误处理与Failover
- 解析模型配置及API Key认证
- 评估上下文窗口使用状态
- 执行重试循环,上限160次
- 每次调用
runEmbeddedAttempt(),失败时触发failover机制
160次上限经实践验证:复杂编程任务常需20-30次工具调用,结合重试余量设计合理。
runEmbeddedAttempt()构成Agent执行生命周期核心,包含四要素:
- SessionManager初始化:管理执行状态生命周期
- 系统Prompt构建:通过
buildEmbeddedSystemPrompt生成指令框架 - 工具集创建:依托
createOpenClawCodingTools加载模型能力 - 流式订阅启动:
subscribeEmbeddedPiSession处理LLM输出流
三、Prompt分层装配机制
系统Prompt由src/agents/system-prompt.ts分层组装,核心结构如下:
function buildAgentSystemPrompt(params: {...}) {
// 1. 基础身份声明
// 2. Tooling章节(工具列表)
// 3. Tool Call Style指导
// 4. Safety安全约束
// 5. CLI快速参考
// 6. Skills技能系统
// 7. Memory Recall记忆检索
// 8. Workspace工作目录
// 9. Sandbox沙箱环境
// 10. Runtime运行时信息
// 11. Project Context项目上下文
// 12. 心跳机制控制
}
分层设计避免信息过载:子Agent仅需获取必备工具、目录及时间信息,减少token消耗与行为干扰。
组装流程严格遵循职责分离原则,确保各层级功能独立清晰。
四、流式处理的分层封装
为适配30+ LLM Provider,流式处理采用链式封装架构:
activeSession.agent.streamFn = streamSimple
→ wrapOllamaCompatNumCtx() // Ollama兼容处理
→ wrapStreamTrimToolCallNames() // 工具名标准化
→ wrapStreamDecodeXaiToolCallArguments() // xAI参数解码
→ cacheTrace.wrapStreamFn() // 缓存追踪
每层专注单一职责,新增Provider适配仅需扩展包装层,避免主逻辑污染,显著提升扩展性。
五、Tool Call的状态机管理
工具调用通过事件驱动状态机处理(src/agents/pi-embedded-subscribe.handlers.tools.ts):
subscribeEmbeddedPiSession() [pi-embedded-subscribe.ts:34]
├── 创建事件处理器
└── 处理事件流:
├── text_delta → 累积响应文本
├── tool_execution_start → 启动工具记录
├── tool_execution_update → 处理执行更新
└── tool_execution_end → 结果存储与钩子触发
after_tool_call钩子提供插件扩展点,支持日志记录、审计等操作,确保核心链路简洁高效。
六、工程级循环防护机制
针对工具调用循环风险,src/agents/tool-loop-detection.ts实施三重防护:
- generic_repeat:同一工具重复调用检测
- known_poll_no_progress:无效轮询行为识别
- ping_pong:工具间交互循环预警
- global_circuit_breaker:全局兜底熔断
known_poll_no_progress机制基于实际进展判定,超越简单频次统计。
七、三级记忆系统架构
记忆系统划分为三层协同运作:
持久层:工作区文件MEMORY.md与memory/*.md提供长期记忆存储。
工具层:通过memory_search与memory_get实现运行时记忆检索。
引擎层:src/memory/manager.ts维护双索引表:
chunks_vec:向量表,支持语义相似度搜索chunks_fts:全文检索表,基于BM25关键词匹配
混合检索公式:finalScore = vectorWeight × vectorScore + textWeight × textScore
混合模式兼顾语义理解与关键词精度,嵌入后端兼容OpenAI、本地Ollama等方案。
八、JITI驱动的插件系统
插件加载采用动态JITI机制:
配置规范化 → 插件扫描 → Manifest加载 → JITI动态加载 → 插件注册 → 激活注册表
支持直接加载.ts文件,免除预编译步骤。30+渠道SDK(飞书、Discord等)均通过标准适配器接口接入,核心Harness无需感知渠道细节。
ChannelPlugin接口定义:
id, meta, capabilities, config,
security?, outbound?, messaging?
九、八级精准路由系统
路由实现基于src/routing/resolve-route.ts,支持8级优先级匹配:
peer → peer.parent → guild+roles → guild → team → account → channel → default
Session Key多维度结构:<agentId>:<mainKey>:<channel>:<accountId>:<peerKind>:<peerId>
六级组合精确定位会话上下文,如"Telegram频道中用户私聊",满足多租户场景细粒度需求。
十、架构启示
核心价值在于严格的边界划分:
- Harness专注执行结构,解耦业务逻辑
- 插件系统承载扩展能力,保护核心链路
- 路由系统负责分发,独立于具体执行
- Provider适配器封装模型差异
清晰边界设计在LLM工程化中尤为珍贵。尽管系统复杂度(160行重试逻辑、500行Prompt构建器等)对小团队构成维护挑战,但对于多渠道、多模型、多租户Agent产品,其架构价值显著。
建议重点研读tool-loop-detection.ts与memory/manager.ts,实践细节远超文档描述。

