Back to Journal
02 / Entry· 7 min read

从框架到应用:OpenClaw 源码拆解——当一个 Agent 要服务 20 个聊天渠道时

上一篇我们从 AgentScope 看到了 ReAct 循环的工程化。这篇换个视角:当一个 Agent 不是被嵌入到代码里,而是要作为一个独立服务、同时对接 WhatsApp/Telegram/Discord 等 20+ 渠道时,Loop 的设计会发生什么变化?

🔊 系统朗读

引子:同一个循环,两种截然不同的命运

上一篇文章里,我们从一个 20 行的 while True 循环出发,一路推演到 AgentScope 的生产级 ReAct 系统。那是一个框架的故事——你 import 它、用它构建 Agent、在你的应用里运行。

但还有另一种故事。

想象这样一个场景:你不是在写代码调用一个 Agent,而是启动一个进程,它自己就是一个 Agent。你通过 WhatsApp 给它发消息、在 Telegram 上问它问题、在 Discord 里让它帮你查资料——所有渠道共享同一个"大脑"、同一份记忆。

这就是 OpenClaw 做的事。它是一个自托管的个人 AI 助手网关,GitHub 上 160,000+ 星标,43 万行代码,不是框架,是一个开箱即用的 Agent 应用

把 AgentScope 和 OpenClaw 放在一起看,你会发现同一个 ReAct 循环在两种场景下长出了完全不同的"外壳"。


一、OpenClaw 的循环长什么样?

如果你读过上一篇文章,你应该还记得 AgentScope 的 ReAct 核心:

reasoning → acting → reasoning → acting → ... → exit

OpenClaw 的 Agent Loop 本质上也是这个循环。但它的入口和出口完全不同——不是函数调用,而是一条消息

一条消息的完整旅程

你在 WhatsApp 发了一条 "帮我查一下明天北京的天气"
  │
  ▼
┌─────────────────────────────────────────────────────┐
│ 1. WhatsApp Channel Adapter 收到消息                  │
│ 2. 路由引擎根据规则确定目标 Agent                       │
│ 3. Session Manager 找到/创建对应会话                   │
│ 4. 消息入队(per-session 队列,严格串行)                │
│ 5. 返回 { runId, acceptedAt } — 你看到"已收到"        │
└────────────────────┬────────────────────────────────┘
                     ▼
┌─────────────────────────────────────────────────────┐
│ 6. agentCommand 开始执行一个 turn                      │
│    ├── 解析 model + 默认参数                           │
│    ├── 加载 Skills 快照(SKILL.md 指令包)              │
│    └── 调用 runEmbeddedAgent                          │
│          ├── 组装 system prompt(基础 + skills + 上下文)│
│          ├── 加载历史 transcript                       │
│          └── 进入 ReAct 循环 ← 和 AgentScope 一样      │
│                ├── reasoning(调用 LLM)                │
│                ├── acting(执行工具)                    │
│                └── ... → 最终回复                      │
└────────────────────┬────────────────────────────────┘
                     ▼
┌─────────────────────────────────────────────────────┐
│ 7. 回复整形(Reply Shaping)                          │
│    ├── 过滤特殊 token(如 NO_REPLY)                   │
│    ├── 去除工具调用的重复确认                           │
│    └── 组装最终 payload                               │
│ 8. 通过原渠道(WhatsApp)回推消息                       │
│ 9. transcript 写入 SQLite                             │
└─────────────────────────────────────────────────────┘

第一个关键差异出现了:AgentScope 的循环入口是函数参数,OpenClaw 的循环入口是消息路由。 这看似微小的差异,导致了完全不同的架构。


二、Gateway 模式——为什么需要一个"网关"?

AgentScope 假设你在写代码。你可以直接 await agent.reply_stream("你好") 来启动循环。

OpenClaw 假设你不是在写代码。你在 WhatsApp 上打字、在 Telegram 上发语音、在 Discord 的群里 @ 它。所以它需要一个网关(Gateway)——一个单进程,同时持有 20+ 个渠道的连接。

         WhatsApp ─┐
         Telegram ─┤
          Discord ─┤
           Slack  ─┤  ┌────────────┐
          Signal  ─┼──┤  Gateway   ├──→ Agent Runtime
         iMessage ─┤  │ (单进程)    │
            Web   ─┤  └────────────┘
            CLI   ─┤
        微信/飞书  ─┘

这带来了一个 AgentScope 不需要操心的问题:并发控制

Per-Session 队列序列化

如果你在 WhatsApp 和 Telegram 上同时给同一个 Agent 发消息,怎么办?两个渠道同时触发循环,共享同一份对话历史——写冲突几乎是必然的。

OpenClaw 的解法是per-session 队列

typescript
// 同一个 session 内的 run 严格按顺序执行
// 不同 session 可以并行
class SessionQueue {
  private lanes = new Map<string, TaskQueue>();

  async enqueue(sessionKey: string, task: () => Promise<void>) {
    const lane = this.getOrCreateLane(sessionKey);
    return lane.push(task);  // FIFO,前一个 run 完成后才执行下一个
  }
}

同一个会话里的消息排队执行,不同会话互不阻塞。简单但关键——这是把 Agent 从"库函数"变成"服务"时必然面对的问题。

队列模式:不只是 FIFO

OpenClaw 还提供了四种队列策略,处理不同的消息场景:

模式语义典型场景
steer新消息直接中断当前 run用户说"停,换个方向"
followup排队等当前 run 完成正常追加消息
collect攒几条一起处理用户连续发了好几条
interrupt硬中断,丢弃当前 run紧急停止

在 AgentScope 里,循环中断靠的是 HITL 事件(USER_INTERRUPT)。而 OpenClaw 因为面对的是真实用户的聊天行为(人会连续发消息、会中途改主意),所以需要更细粒度的中断策略。


三、消息路由——"这条消息该给谁?"

AgentScope 里,你直接选择调用哪个 Agent。OpenClaw 里,消息自己找到正确的 Agent。

路由引擎按特异性从高到低匹配:

1. exact peer      → 精确匹配某个用户/群
2. parent peer     → 匹配父级
3. peer wildcard   → 通配符匹配
4. guild + roles   → 服务器 + 角色组合
5. guild           → 整个服务器
6. team            → 整个团队
7. account         → 整个账号
8. channel         → 整个渠道(如 "所有 Telegram 消息")
9. default agent   → 兜底

这让你可以:

  • 给某个 Discord 频道的消息分配一个"代码助手" Agent
  • 给 WhatsApp 上的私聊分配一个"个人管家" Agent
  • 其他所有消息走默认 Agent

每个 Agent 拥有独立的工作空间、会话存储、认证配置和记忆。一个 Gateway 进程下可以跑多个完全隔离的 Agent。


四、Session 与 Transcript——Agent 的"长期记忆"

AgentScope 的上下文管理聚焦于单次 reply 内的 context window 控制——压缩早期消息、截断工具结果。

OpenClaw 面对的是另一个尺度的问题:跨会话的持久化记忆

SQLite 作为 Transcript Store

每个 Agent 的对话历史存储在独立的 SQLite 数据库中:

agents/
├── default/
│   └── sessions.sqlite    ← 默认 Agent 的所有会话
├── code-assistant/
│   └── sessions.sqlite    ← 代码助手 Agent 的所有会话

关键设计决策:

  • 完整历史始终保留在磁盘上——Compaction 只改变模型看到的内容,不删除原始记录
  • 写锁 + Identity 验证——每次写入事务都验证 session identity,防止旧 run 覆盖新会话
  • DM 折叠——同一个用户的多个私聊渠道默认折叠到 Agent 的 main session(你在 WhatsApp 和 Telegram 上和它聊天,它记得上下文)

Session Key 决定隔离边界

场景Session Key 生成规则
私信(DM)折叠到 Agent 的 main session
群聊每个群独立 session
特定用户配置可自定义隔离规则

这意味着:你在 WhatsApp 上聊了一半的话题,换到 Telegram 上继续,Agent 能接上。但两个不同群的对话互不干扰。


五、Compaction——不是截断,是"总结后记住"

这是 OpenClaw 最有特色的设计之一。

AgentScope 的上下文压缩是被动触发的——context window 快满了才压缩。OpenClaw 的 Compaction 是主动的、有策略的、可插拔的

Compaction 的完整生命周期

上下文接近窗口限制(或模型返回 context-overflow 错误)
  │
  ▼
1. Memory Flush(记忆冲刷)
   ├── 提醒 Agent:"你即将进入压缩阶段"
   ├── "如果有重要的笔记/发现,现在保存到 memory 文件"
   └── Agent 可以调用 write 工具把关键信息写入持久文件
  │
  ▼
2. Compaction 执行
   ├── 旧 turn 被总结为紧凑条目
   ├── 最近 N 条消息保持完整
   ├── 保持 tool_call ↔ tool_result 配对完整
   └── 可插拔的 compaction provider(插件可自定义总结策略)
  │
  ▼
3. 质量审计(safeguard 模式)
   ├── 检查摘要是否丢失关键信息
   └── 不满足质量标准则重试

与 AgentScope 的对比:

维度AgentScopeOpenClaw
触发时机context 使用率 > 80%context 接近窗口 或 模型报错
压缩前直接压缩先 Memory Flush(让 Agent 主动存笔记)
压缩策略固定摘要可插拔 provider
质量保证safeguard 模式做质量审计
持久化仅内存SQLite 保留完整历史,压缩只影响模型视图

Memory Flush 是一个精妙的设计:在压缩之前,给 Agent 一个"临终遗言"的机会——"如果你有什么重要的发现,现在写下来"。这让 Agent 不会因为上下文压缩而丢失关键推理链条。


六、SKILL.md——用 Markdown 定义 Agent 行为

这是 OpenClaw 最"离经叛道"的设计。

AgentScope 里,你通过 Python 代码定义 Agent 的行为——写 system prompt、配置 toolkit、编写 middleware。这需要你是一个开发者。

OpenClaw 说:用 Markdown 就行。

一个 SKILL.md 长这样:

markdown
# Weather Checker

## When to Use
When the user asks about weather, temperature, or forecast.

## Instructions
1. Use the `web_search` tool to find current weather data
2. Summarize the temperature, humidity, and conditions
3. If the user asks about tomorrow, also search for forecast
4. Always mention the data source

## Examples
User: "北京明天天气怎么样?"
Action: web_search("北京 天气预报 明天")

这个 Markdown 文件会被加载到 Agent 的 system prompt 中,影响 Agent 的行为。

三层能力模型

层级定义方式谁可以创建
ToolsTypeScript 代码 + JSON Schema开发者
SkillsSKILL.md(Markdown)任何人
PluginsTypeScript 插件 SDK开发者

ClawHub 上已经有 13,700+ 社区发布的 Skill。这意味着非开发者也可以通过写 Markdown 来扩展 Agent 的能力——降低了一个数量级的门槛。

与 AgentScope 的对比

维度AgentScopeOpenClaw
行为定义system_prompt 字符串SKILL.md 文件
工具注册Python ToolBase 子类TypeScript 函数 + JSON Schema
扩展门槛需要写代码Skill 只需写 Markdown
动态切换ToolGroup + 元工具Skill 加载/卸载

七、Heartbeat——Agent 的"心跳",让它有主动行为能力

这是 AgentScope 完全没有的特性。

AgentScope 的 Agent 是被动的——你调 reply_stream() 它才动。OpenClaw 的 Agent 有心跳

typescript
// 每 30 分钟(可配置)唤醒一次 Agent
heartbeat: {
  interval: "30m",
  prompt: "检查一下有没有需要主动处理的事情"
}

每隔一段时间,Agent 会被自动唤醒,检查有没有需要主动处理的事项:

  • 检查邮箱有没有新的重要邮件
  • 检查日历上即将到来的会议
  • 检查某个监控指标是否触发了阈值
  • 主动给你发一条提醒

这让 Agent 从"问一句答一句"的工具,变成了一个有主动性的助手


八、插件钩子系统——比 AgentScope 更宽的拦截面

上一篇文章里,AgentScope 有 7 个中间件 hook 点。OpenClaw 的钩子系统覆盖面更广,因为它还需要拦截消息路由、渠道收发、session 生命周期等"服务层"的行为。

完整钩子清单

钩子时机AgentScope 有对应吗?
before_model_resolve确定用哪个模型之前
before_prompt_build构建 prompt 之前on_system_prompt
before_agent_replyLLM 调用之前on_reply
before_tool_call工具调用之前on_acting
after_tool_call工具调用之后on_acting
tool_result_persist工具结果写入磁盘之前
before_compaction上下文压缩之前on_compress_context
after_compaction上下文压缩之后
message_received渠道收到消息时
message_sending消息发送到渠道之前
message_sent消息已发送到渠道
session_start会话创建时
session_end会话结束时
gateway_startGateway 启动时
gateway_stopGateway 关闭时

多出来的钩子全部与**"服务"特性**相关:消息收发、session 管理、Gateway 生命周期。这正是"框架"和"应用"的分野。

决策规则

钩子的拦截行为采用终态短路模式:

typescript
// before_tool_call 返回 block → 停止后续处理器,工具不执行
{ block: true, reason: "This tool is not allowed" }

// message_sending 返回 cancel → 消息不发送
{ cancel: true }

与 AgentScope 的洋葱模式(next_handler 回调)不同,OpenClaw 用的是管道 + 短路模式——更简单,更适合"多个插件竞争决策"的场景。


九、回复整形——Agent 说的话要"适合聊天"

AgentScope 的输出是 AgentEvent 流——前端负责渲染。

OpenClaw 的输出是一条聊天消息——它要经过"回复整形"才能发出去:

LLM 原始输出
  │
  ├── 过滤 NO_REPLY token(Agent 决定不回复)
  ├── 去除重复确认(Agent 调了 message 工具后又文字确认了一遍)
  ├── 内联工具摘要(搜索结果的简要总结)
  ├── 可选附加 reasoning 摘要
  └── 最终 payload → 通过渠道发送

一个具体的例子:

# LLM 原始输出:
"我来帮你查一下天气。
[调用 web_search 工具]
根据搜索结果,明天北京晴,25°C,适合出行。"

# 整形后(去除工具调用确认):
"明天北京晴,25°C,适合出行。"

用户在 WhatsApp 上不需要看到"我来帮你查一下"这种过程性文字——他们只要结果。这种面向聊天体验的回复优化是框架(AgentScope)不需要操心的,因为那是前端的事。但在应用(OpenClaw)里,它是 Agent 循环的最后一步。


十、对比总结:框架 vs 应用

把两篇文章的核心发现放在一起:

维度AgentScope(框架)OpenClaw(应用)
本质Python 库,你 import 它Node.js 进程,你启动它
循环入口agent.reply_stream()消息路由 → per-session 队列
循环核心_next_action() 三选一决策标准 ReAct + 回复整形
并发模型asyncio + 工具并发安全标记per-session 队列序列化 + 4 种队列模式
上下文管理被动压缩(trigger_ratio)主动 Compaction + Memory Flush
扩展方式Python 代码(ToolBase, Middleware)SKILL.md + 插件 SDK
持久化无(你自行处理)SQLite transcript + 完整历史
多渠道无(你自行处理)20+ 渠道内置适配
主动行为Heartbeat 定时唤醒
中间件7 hook,洋葱模式16+ hook,管道短路模式
多 AgentLeader-Worker + Message Bus路由引擎 + 独立 workspace 隔离
HITL事件暂停 + 恢复队列模式(steer/interrupt)

设计哲学的根本分歧

AgentScope 的哲学是:提供最好的积木块,让开发者搭建自己的 Agent。

  • 它的 ReAct 循环是高度可配置的(ReActConfigContextConfigInjectionConfig
  • 它的中间件是洋葱模式——每一层都可以精确控制上下层的行为
  • 它的多 Agent 编排是 Leader-Worker——让 LLM 自己决定流程

OpenClaw 的哲学是:给你一个已经搭好的 Agent,你用 Markdown 调教它。

  • 它的循环你几乎改不了(也没必要改)
  • 它的扩展点是 SKILL.md——非开发者也能参与
  • 它的多渠道、记忆、心跳都是开箱即用的
  • 它的 Compaction 带 Memory Flush——Agent 自己知道什么时候该"记笔记"

尾声:从理解循环到选择工具

两篇文章走下来,你应该对 Agent Loop 有了一个完整的认知地图:

                        Agent Loop 的认知地图

  ┌─────────────────────────────────────────────────────────┐
  │                     核心循环层                           │
  │          while True: reasoning → acting → exit           │
  │    (两篇文章的共识:所有 Agent 框架的内核都是 ReAct)       │
  └───────────────────────┬─────────────────────────────────┘
                          │
            ┌─────────────┼─────────────┐
            ▼                           ▼
  ┌─────────────────┐          ┌─────────────────┐
  │   框架路线        │          │   应用路线        │
  │  (AgentScope)    │          │  (OpenClaw)      │
  ├─────────────────┤          ├─────────────────┤
  │ 洋葱中间件        │          │ 管道钩子 + 短路   │
  │ 工具并发策略       │          │ per-session 队列  │
  │ 决策器三选一       │          │ 回复整形          │
  │ 上下文压缩        │          │ Compaction +      │
  │                  │          │   Memory Flush    │
  │ Leader-Worker    │          │ Heartbeat 心跳     │
  │ 事件流生成器       │          │ 路由引擎          │
  └─────────────────┘          │ SKILL.md          │
                               │ 多渠道网关          │
                               └─────────────────┘

选哪个?

  • 如果你要构建自己的 Agent 产品,需要深度控制循环行为 → AgentScope(或类似的框架)
  • 如果你要快速拥有一个跨渠道的个人 AI 助手,不想写太多代码 → OpenClaw
  • 如果你要学习 Agent 架构设计 → 两个都看,它们的差异本身就是最好的教材

最后记住一件事:不管外壳多么复杂,Agent 的核心就是一个循环。所有的工程努力,都是为了让这个循环在生产环境中稳定、可控、可观察地运行。

分享
← 返回博客列表
🎁 有邀请福利哦,点击查看
🎁