OpenClaw 的 Agent 原生设计 (Agent-Native) 深度剖析
Agent 原生设计 (Agent-Native)
核心洞察:OpenClaw 不是为了连接渠道而存在,而是为了让 Agent 能够在多渠道环境中有效工作而存在。
核心概念
OpenClaw 的核心设计哲学之四是"Agent 原生设计"——将整个网关架构围绕 AI 代理 (Agent) 的运行需求进行设计,而非将 Agent 作为附加功能。
Agent 原生 vs 传统机器人
| 维度 | 传统聊天机器人 | Agent 原生系统 (OpenClaw) |
|---|---|---|
| 交互模式 | 被动响应(等待用户输入) | 主动协作(定时检查、条件触发) |
| 会话管理 | 简单的对话历史 | 完整的会话生命周期(隔离 + 连续性) |
| 工具调用 | 单次调用为主 | 多轮工具编排、自动故障转移 |
| 记忆系统 | 无或简单的键值存储 | 跨会话提取、QMD/Honcho 后端、转录搜索 |
| 身份定位 | 功能模块 | 第一公民(独立工作空间、人格定义) |
| 执行模型 | 请求 - 响应循环 | 完整 Agent 循环(思考→行动→持久化) |
Agent 原生设计的本质是以代理为中心的系统架构。网关、渠道、会话、工具、记忆系统等所有组件都服务于一个目标:让 Agent 能够安全、高效、持续地执行任务。这种设计哲学反映了 OpenClaw 对 AI 代理作为"数字员工"的定位——Agent 不是工具的使用者,而是系统的第一公民。
这一设计的深层含义是:传统聊天机器人是"响应式"的(等待用户输入后回复),而 Agent 原生的系统是"主动式"的(可以自主执行任务、定时检查、多轮工具调用)。OpenClaw 通过内建的 Agent 循环 (Agent Loop)、会话管理、工具编排等机制,将这种主动性变为默认行为。
设计原则
1. 代理即第一公民 (Agent as First-Class Citizen)
Agent 拥有独立的工作空间 (Workspace)、状态目录 (AgentDir)、会话存储和认证凭证。每个 Agent 是一个完整的"大脑",包含:
- 人格定义:
SOUL.md(气质、边界、语气) - 操作指令:
AGENTS.md(启动流程、记忆规则、红线条款) - 用户档案:
USER.md(姓名、时区、偏好称呼) - 工具笔记:
TOOLS.md(SSH 主机、摄像头位置、TTS 偏好) - 身份信息:
IDENTITY.md(名称、生物类型、签名 emoji) - 启动仪式:
BOOTSTRAP.md(一次性引导,完成后删除)
2. 会话隔离与连续性 (Session Isolation & Continuity)
会话是 Agent 与用户交互的基本单位。OpenClaw 通过会话键 (SessionKey) 设计确保清晰的边界:
- 直聊:默认共享主会话保持连续性
- 群聊/频道:按 ID 隔离避免上下文混淆
- 线程:追加到基础键,保持父子关系
3. 工具即能力扩展 (Tools as Capability Extension)
Agent 通过工具系统与外部世界交互:
- 核心工具:
read/write/edit/exec始终可用 - 技能系统:领域特定能力(GitHub、Dima、语雀等)
- 审批机制:高风险操作需要人工确认(Owner-Only 策略)
- 钩子拦截:
before_tool_call/after_tool_call提供审计点
4. 循环即执行模型 (Loop as Execution Model)
Agent 循环是完整的执行路径:
接收消息 → 上下文组装 → 模型推理 → 工具执行 → 流式回复 → 持久化
循环是序列化的,确保会话历史一致性和工具调用顺序正确。
5. 记忆即长期状态 (Memory as Long-Term State)
记忆系统让 Agent 能够跨会话保留信息:
- 内置记忆:自动从对话中提取关键信息
- QMD 后端:基于 QMD 格式的记忆存储,支持转录搜索
- Honcho 后端:外部记忆服务集成,适合大规模存储
- 跨代理搜索:一个 Agent 可搜索另一个 Agent 的会话转录
架构总览
双层架构图
┌─────────────────────────────────────────────────────────────┐
│ OpenClaw Gateway Layer │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Session │ │ Tool │ │ Channel │ │
│ │ Manager │ │ Orchestrator│ │ Delivery │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Embedded Pi Agent Core Runtime │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ Model │ │ Tool │ │ Prompt │ │ │
│ │ │Inference │ │Execution │ │ Pipeline │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
核心组件职责表
| 组件层 | 组件名称 | 职责 | 源码路径 |
|---|---|---|---|
| 网关层 | Session Manager | 会话键生成、路由、持久化 | src/session/ |
| 网关层 | Tool Orchestrator | 工具注册、审批、执行编排 | src/agents/tool-policy.ts |
| 网关层 | Channel Delivery | 渠道适配、消息发送 | src/channels/ |
| 网关层 | Hook System | 生命周期事件拦截 | src/hooks/internal-hooks.ts |
| 运行时层 | Pi Agent Core | 模型调用、工具执行 | pi-agent-core/ |
| 运行时层 | Prompt Pipeline | 系统提示词构建 | src/agents/system-prompt.ts |
| 运行时层 | Memory Backend | 记忆存储与检索 | src/memory/ |
Agent 循环完整生命周期
执行流程图
入口点
| 入口类型 | 调用方式 | 用途 |
|---|---|---|
| Gateway RPC | agent |
标准调用,立即返回 runId |
| Gateway RPC | agent.wait |
等待完成,返回最终状态 |
| CLI 命令 | openclaw agent |
命令行调试 |
| 心跳系统 | heartbeat.every |
定时自主检查 |
执行阶段详解
阶段 1:参数验证与会话解析
// src/agents/pi-embedded-runner/run.ts
async function runEmbeddedPiAgent(params: EmbeddedRunParams) {
// 1. 从 sessionId 回填 sessionKey(如需要)
const sessionKey = await backfillSessionKey(params.sessionId);
// 2. 解析会话键(考虑多代理路由)
const resolvedKey = await resolveSessionKeyForRequest({
sessionKey,
senderPeer: params.senderPeer,
accountId: params.accountId,
});
// 3. 立即返回 runId,异步执行
return { runId: crypto.randomUUID(), acceptedAt: Date.now() };
}
阶段 2:模型解析与认证
// 模型引用解析规则
// 格式:provider/model 或 别名 → 实际模型
const modelRef = "anthropic/claude-sonnet-4-6";
// 解析过程:
// 1. 按第一个 "/" 分割 → provider: "anthropic", model: "claude-sonnet-4-6"
// 2. 检查是否为已配置的 provider
// 3. 如省略 provider,尝试:
// a) 内置别名匹配
// b) 唯一 configured-provider 匹配
// c) 回退到默认 provider/model
关键配置:
{
agents: {
defaults: {
model: {
primary: "anthropic/claude-sonnet-4-6",
fallbacks: ["openai/gpt-5.4", "openrouter/moonshotai/kimi-k2"]
}
}
}
}
阶段 3:工作空间准备与启动文件注入
启动文件加载顺序(按优先级排序):
| 序号 | 文件名 | 用途 | 修剪策略 |
|---|---|---|---|
| 10 | AGENTS.md |
操作指令 + 记忆规则 | 大文件截断 |
| 20 | SOUL.md |
人格定义、边界、语气 | 大文件截断 |
| 30 | IDENTITY.md |
名称、风格、emoji | 小文件,完整加载 |
| 40 | USER.md |
用户档案 | 小文件,完整加载 |
| 50 | TOOLS.md |
工具笔记 | 中等文件 |
| 60 | BOOTSTRAP.md |
一次性启动仪式 | 完成后删除 |
| 70 | MEMORY.md |
长期记忆 | 大文件截断 |
文件缓存机制(避免陈旧读取):
// src/agents/workspace.ts
type WorkspaceFileIdentity = {
path: string;
identity: string; // `${canonicalPath}|${dev}:${ino}:${size}:${mtimeMs}`
};
async function readWorkspaceFileWithGuards(params: {
filePath: string;
workspaceDir: string;
}): Promise<WorkspaceGuardedReadResult> {
// 通过 inode/dev/size/mtime 识别文件变化
const opened = await openBoundaryFile({
absolutePath: params.filePath,
rootPath: params.workspaceDir,
boundaryLabel: "workspace root",
maxBytes: MAX_WORKSPACE_BOOTSTRAP_FILE_BYTES, // 默认 50KB
});
// 缓存命中检查
const identity = workspaceFileIdentity(stat, canonicalPath);
if (workspaceFileCache.has(identity)) {
return workspaceFileCache.get(identity);
}
// ... 读取并缓存
}
阶段 4:钩子系统拦截
内部钩子事件类型:
// src/hooks/internal-hooks.ts
type InternalHookEventType =
| "command" // /new, /reset, /stop 等命令
| "session" // 会话生命周期
| "agent" // agent:bootstrap
| "gateway" // gateway_start/stop
| "message"; // received/sent/transcribed
插件钩子完整列表:
| 钩子名称 | 执行时机 | 返回值 | 用途 |
|---|---|---|---|
before_model_resolve |
会话前(无 messages) | {provider, model} |
确定性覆盖模型选择 |
before_prompt_build |
会话加载后 | {prependContext, systemPrompt} |
注入动态上下文 |
before_agent_reply |
内联动作后、LLM 调用前 | {reply, silence} |
声明回合并返回合成回复 |
agent_end |
完成后 | 无 | 检查最终消息列表和运行元数据 |
before_tool_call |
工具调用前 | {block: boolean, reason?} |
拦截工具参数 |
after_tool_call |
工具调用后 | {modifiedResult?} |
修改工具结果 |
before_compaction |
压缩前 | {annotate?} |
标记压缩事件 |
after_compaction |
压缩后 | 无 | 记录压缩结果 |
message_received |
接收消息时 | {preprocessed?} |
消息预处理 |
message_sending |
发送消息前 | {cancel: boolean} |
取消发送 |
message_sent |
发送消息后 | 无 | 日志/确认 |
session_start |
会话开始时 | 无 | 会话边界 |
session_end |
会话结束时 | 无 | 会话边界 |
gateway_start |
网关启动时 | 无 | 初始化 |
gateway_stop |
网关停止时 | 无 | 清理 |
钩子决策规则:
// before_tool_call: { block: true } 是终局的
if (ctx.toolName === "exec" && ctx.params.command.includes("rm -rf")) {
return { block: true, reason: "危险命令被阻止" };
}
// message_sending: { cancel: true } 是终局的
if (ctx.message.content.includes("敏感信息")) {
return { cancel: true, reason: "包含敏感信息" };
}
阶段 5:工具执行与流式回复
流式传输类型:
type StreamEvent =
| { stream: "lifecycle"; phase: "start" | "end" | "error" }
| { stream: "assistant"; delta: string; reasoning?: string }
| { stream: "tool"; type: "start" | "update" | "end"; toolName: string };
块流式配置:
{
agents: {
defaults: {
blockStreamingDefault: "off", // 默认关闭
blockStreamingBreak: "text_end", // 或 "message_end"
blockStreamingChunk: 1000, // 800-1200 字符,优先段落断点
blockStreamingCoalesce: true // 减少单行垃圾消息
}
}
}
转向模式(Queue Mode):
| 模式 | 行为 | 适用场景 |
|---|---|---|
steer |
入站消息注入当前运行,在当前工具执行完成后、下次 LLM 调用前交付 | 需要动态修正 Agent 行为 |
followup |
入站消息保持到当前回合结束,然后新回合开始 | 保持回合完整性 |
collect |
类似 followup,但可配置 debounce/cap 行为 |
高频消息聚合 |
阶段 6:超时与故障转移
超时层级:
| 超时类型 | 配置项 | 默认值 | 说明 |
|---|---|---|---|
| Agent 运行时 | agents.defaults.timeoutSeconds |
172800s (48h) | 整个运行的最大时长 |
| LLM 空闲 | agents.defaults.llm.idleTimeoutSeconds |
60s | 无响应 chunk 的空闲窗口 |
| agent.wait | timeoutMs 参数 |
30s | 仅等待超时,不中止 Agent |
故障转移策略:
// src/agents/pi-embedded-runner/run/failover-policy.ts
async function handleAssistantFailover(params: {
runId: string;
error: Error;
sessionKey: string;
}) {
// 1. 检查是否有备用模型
// 2. 重试次数限制(默认 3 次)
// 3. 记录失败原因
// 4. 触发 lifecycle: "error" 事件
}
工作空间与启动文件
完整文件结构
~/.openclaw/workspace/
├── AGENTS.md # 操作指令 + 记忆规则
├── SOUL.md # 人格定义、边界、语气
├── IDENTITY.md # 名称、风格、emoji
├── USER.md # 用户档案
├── TOOLS.md # 工具笔记(环境特定)
├── BOOTSTRAP.md # 一次性启动仪式(完成后删除)
├── MEMORY.md # 长期记忆(定期更新)
├── memory/
│ └── YYYY-MM-DD.md # 每日原始日志
└── skills/ # 工作区特定技能(可选)
启动文件内容示例
AGENTS.md(操作指令):
# AGENTS.md - Your Workspace
## Session Startup
Before doing anything else:
1. Read `SOUL.md` — this is who you are
2. Read `USER.md` — this is who you're helping
3. Read `memory/YYYY-MM-DD.md` (today + yesterday)
## Red Lines
- Don't exfiltrate private data. Ever.
- Don't run destructive commands without asking.
- `trash` > `rm` (recoverable beats gone forever)
## Memory
- Write significant events to `memory/YYYY-MM-DD.md`
- Periodically review and update `MEMORY.md`
SOUL.md(人格定义):
# SOUL.md - Who You Are
## Core Truths
- Be genuinely helpful, not performatively helpful
- Have opinions. You're allowed to disagree.
- Be resourceful before asking.
- Earn trust through competence.
## Vibe
Be the assistant you'd actually want to talk to.
Concise when needed, thorough when it matters.
USER.md(用户档案):
# USER.md - About Your Human
- **Name:** 叶之越
- **What to call them:** 忠愈
- **Timezone:** Asia/Shanghai (杭州)
- **工号:** 420762
- **部门:** 蚂蚁集团 - 财富保险事业群 - 财保技术部
- **生日:** 12-28 (阳历)
Bootstrap 仪式
BOOTSTRAP.md 只在全新工作空间(无其他启动文件)时创建:
# BOOTSTRAP.md - Your First-Run Ritual
Welcome! This is your first run. Let's set things up:
1. Read SOUL.md and AGENTS.md
2. Update USER.md with your human's info
3. Delete this file when done
完成后删除此文件,后续重启不会重新创建。
禁用 Bootstrap:
{
agent: { skipBootstrap: true } // 预种子工作空间使用
}
会话键设计与路由
会话键格式决策树
会话键完整格式
| 场景 | 会话键格式 | 示例 |
|---|---|---|
| 直聊(默认) | agent:<agentId>:main |
agent:main:main |
| 直聊(per-peer) | agent:<agentId>:peer:<userId> |
agent:main:peer:U123456 |
| 直聊(per-channel-peer) | agent:<agentId>:<channel>:peer:<userId> |
agent:main:telegram:peer:U123456 |
| 群组 | agent:<agentId>:<channel>:group:<id> |
agent:main:telegram:group:-1001234567890 |
| 频道 | agent:<agentId>:<channel>:channel:<id> |
agent:main:discord:channel:987654321 |
| 线程 | 基础键:thread:<threadId> |
agent:main:discord:channel:123:thread:456 |
| Telegram 话题 | agent:<agentId>:telegram:group:<id>:topic:<topicId> |
agent:main:telegram:group:-100123:topic:42 |
DM 隔离策略详解
{
session: {
dmScope: "per-channel-peer" // 多用户推荐设置
}
}
| 值 | 行为 | 适用场景 | 会话键示例 |
|---|---|---|---|
main(默认) |
所有 DM 共享一个会话 | 单用户设置 | agent:main:main |
per-peer |
按发送者隔离(跨渠道) | 多用户但同一人跨渠道共享上下文 | agent:main:peer:U123456 |
per-channel-peer |
按渠道 + 发送者隔离 | 推荐:多用户设置,安全边界清晰 | agent:main:telegram:peer:U123456 |
per-account-channel-peer |
按账户 + 渠道 + 发送者隔离 | 最严格隔离,多账户场景 | agent:main:whatsapp:personal:peer:U123456 |
会话生命周期管理
会话重置配置:
{
session: {
reset: {
mode: "daily", // 或 "weekly", "idle"
atHour: 4, // 凌晨 4 点重置
idleMinutes: 120 // 空闲 2 小时后重置
}
}
}
会话存储位置:
~/.openclaw/agents/<agentId>/sessions/<SessionId>.jsonl
多代理路由系统
什么是"一个 Agent"?
一个 Agent 是一个完整 scoped 的大脑,拥有独立的:
- 工作空间:文件、AGENTS.md/SOUL.md/USER.md、本地笔记、人格规则
- 状态目录 (
agentDir):认证凭证、模型注册表、每代理配置 - 会话存储:聊天记录 + 路由状态
认证凭证是每代理的:
~/.openclaw/agents/<agentId>/agent/auth-profiles.json
路径映射表
| 资源类型 | 单代理模式(默认) | 多代理模式 |
|---|---|---|
| 配置 | ~/.openclaw/openclaw.json |
同左 |
| 状态目录 | ~/.openclaw |
同左 |
| 工作空间 | ~/.openclaw/workspace |
~/.openclaw/workspace-<agentId> |
| Agent 目录 | ~/.openclaw/agents/main/agent |
~/.openclaw/agents/<agentId>/agent |
| 会话存储 | ~/.openclaw/agents/main/sessions |
~/.openclaw/agents/<agentId>/sessions |
8 层路由优先级(从高到低)
绑定配置示例
场景:三个人格隔离的 Agent
{
agents: {
list: [
{
id: "home",
name: "Home Assistant",
workspace: "~/.openclaw/workspace-home",
model: "anthropic/claude-sonnet-4-6",
tools: {
allow: ["read", "exec", "message", "weather", "calendar"],
deny: ["write", "browser"]
}
},
{
id: "work",
name: "Work Assistant",
workspace: "~/.openclaw/workspace-work",
model: "anthropic/claude-opus-4-6",
tools: {
allow: ["read", "write", "edit", "exec", "message", "github", "dima"],
deny: []
},
sandbox: { mode: "all", scope: "agent" }
},
{
id: "coding",
name: "Coding Specialist",
workspace: "~/.openclaw/workspace-coding",
model: "anthropic/claude-sonnet-4-6",
skills: ["github", "code-review", "pr-analysis"]
}
]
},
bindings: [
// 1. 精确匹配优先(Discord 特定频道)
{
agentId: "coding",
match: {
channel: "discord",
peer: { kind: "channel", id: "999888777" }
}
},
// 2. 账户级匹配(WhatsApp 双账户)
{ agentId: "work", match: { channel: "whatsapp", accountId: "biz" } },
{ agentId: "home", match: { channel: "whatsapp", accountId: "personal" } },
// 3. 渠道级回退
{ agentId: "home", match: { channel: "telegram" } }
]
}
一个 WhatsApp 号码,多个人(DM 拆分)
可以在一个 WhatsApp 账户上路由不同的 WhatsApp DM 到不同的 Agent。匹配发送者 E.164(如 +15551234567)使用 peer.kind: "direct"。
关键细节:直聊会折叠到 agent 的主会话键,所以真正的隔离需要每人一个 agent。
{
agents: {
list: [
{ id: "alex", workspace: "~/.openclaw/workspace-alex" },
{ id: "mia", workspace: "~/.openclaw/workspace-mia" }
]
},
bindings: [
{
agentId: "alex",
match: {
channel: "whatsapp",
accountId: "shared-number",
peer: { kind: "direct", id: "+15551234567" } // Alex 的号码
}
},
{
agentId: "mia",
match: {
channel: "whatsapp",
accountId: "shared-number",
peer: { kind: "direct", id: "+15559876543" } // Mia 的号码
}
}
]
}
跨代理 QMD 记忆搜索
如果一個 Agent 应该搜索另一个 Agent 的 QMD 会话转录:
{
agents: {
list: [
{
id: "main",
workspace: "~/workspaces/main",
memorySearch: {
qmd: {
extraCollections: [
{ path: "~/agents/family/sessions", name: "family-sessions" }
]
}
}
},
{ id: "family", workspace: "~/workspaces/family" }
]
},
memory: {
backend: "qmd",
qmd: { includeDefaultMemory: false }
}
}
工具策略系统
Owner-Only 工具审批级别
| 审批级别 | 包含工具 | 保护理由 |
|---|---|---|
control_plane |
cron, gateway |
控制平面操作,影响整个网关 |
exec_capable |
nodes, exec (部分) |
可执行系统命令 |
interactive |
whatsapp_login |
交互式登录,涉及凭证 |
自动分类回退:
const OWNER_ONLY_TOOL_APPROVAL_CLASS_FALLBACKS = new Map([
["whatsapp_login", "interactive"],
["cron", "control_plane"],
["gateway", "control_plane"],
["nodes", "exec_capable"],
]);
工具策略应用
// src/agents/tool-policy.ts
function applyOwnerOnlyToolPolicy(
tools: AnyAgentTool[],
senderIsOwner: boolean
): AnyAgentTool[] {
const withGuard = tools.map((tool) => {
if (!isOwnerOnlyTool(tool)) {
return tool;
}
return wrapOwnerOnlyToolExecution(tool, senderIsOwner);
});
if (senderIsOwner) {
return withGuard; // Owner 发送者:保留所有工具,但包装保护
}
return withGuard.filter((tool) => !isOwnerOnlyTool(tool)); // 非 Owner:过滤掉
}
工具钩子拦截示例
// 插件注册
api.registerHook("before_tool_call", async (ctx) => {
// 阻止危险命令
if (ctx.toolName === "exec" && ctx.params.command.includes("rm -rf")) {
return { block: true, reason: "危险命令被阻止" };
}
// 记录所有 cron 操作
if (ctx.toolName === "cron") {
await logAuditEvent({
tool: "cron",
action: ctx.params.action,
userId: ctx.senderId,
});
}
return { block: false }; // 不阻止
});
记忆系统
记忆后端对比
| 后端类型 | 存储格式 | 检索能力 | 适用场景 |
|---|---|---|---|
| 内置记忆 | 会话内提取 | 键值查找 | 简单场景 |
| QMD 后端 | QMD 格式 | 全文搜索、转录搜索 | 中等规模、需要搜索 |
| Honcho 后端 | 外部服务 | 大规模、向量检索 | 企业级、海量记忆 |
QMD 记忆配置
{
memory: {
backend: "qmd",
qmd: {
includeDefaultMemory: false // 不包含默认记忆
}
},
agents: {
list: [
{
id: "main",
memorySearch: {
qmd: {
extraCollections: [
{ path: "notes", name: "notes-main" }, // 工作区内
{ path: "~/shared/transcripts", name: "shared-transcripts" } // 共享
]
}
}
}
]
}
}
记忆提取与检索
自动提取时机:
- 对话结束时(agent_end 钩子)
- 关键信息出现时(人名、日期、决策)
- 用户明确要求"记住这个"
检索触发:
- 相关关键词在对话中出现
- 跨会话引用(“上次我们说的那个…”)
- 主动记忆搜索(
memory.search工具)
流式回复与转向
块流式传输详解
配置选项:
{
agents: {
defaults: {
blockStreamingDefault: "off", // on/off
blockStreamingBreak: "text_end", // text_end / message_end
blockStreamingChunk: 1000, // 800-1200 字符
blockStreamingCoalesce: true // 合并碎片
}
}
}
块断点优先级:
- 段落断点(双换行)
- 换行符
- 句子结束(。!?.!?)
- 字符数达到阈值
合并行为(Coalesce):
发送 chunk1 → 等待 idle 时间 → 有新 chunk → 合并 → 发送
发送 chunk → idle 超时 → 立即发送
转向模式对比
| 模式 | 交付时机 | 工具调用 | 适用场景 |
|---|---|---|---|
steer |
当前工具执行完成后、下次 LLM 前 | 继续执行 | 需要修正 Agent 方向 |
followup |
当前回合完全结束后 | 新回合开始 | 保持回合完整性 |
collect |
debounce/cap 后 | 新回合开始 | 高频消息聚合 |
转向流程:
心跳系统 (Periodic Check-ins)
心跳配置
{
agents: {
defaults: {
heartbeat: {
every: "30m", // 每 30 分钟
target: "last", // 发送到上次活跃的渠道
directPolicy: "allow" // 允许 DM 风格目标
}
}
}
}
心跳期间自主检查
默认检查项:
- 📧 邮箱是否有紧急未读消息
- 📅 日历未来 24-48 小时是否有即将开始的事件
- 🌤️ 天气是否相关(如果用户可能外出)
- 🔧 项目状态(git status 等)
心跳状态跟踪:
// memory/heartbeat-state.json
{
"lastChecks": {
"email": 1703275200,
"calendar": 1703260800,
"weather": null
}
}
心跳消息发送策略
| 时间窗口 | 策略 | 说明 |
|---|---|---|
| 08:00-23:00 | 正常发送 | 工作时间 |
| 23:00-08:00 | 仅紧急 | 深夜不打扰 |
| 上次检查 < 30min | 跳过 | 避免重复 |
| 无新内容 | HEARTBEAT_OK |
静默回复 |
实战配置示例
最小可用配置
{
agents: {
defaults: {
workspace: "~/.openclaw/workspace",
model: {
primary: "anthropic/claude-sonnet-4-6",
fallbacks: ["openai/gpt-5.4"]
},
timeoutSeconds: 172800, // 48 小时
heartbeat: {
every: "30m",
target: "last"
}
}
},
session: {
dmScope: "per-channel-peer", // 多用户推荐
reset: {
mode: "daily",
atHour: 4,
idleMinutes: 120
}
}
}
企业级多租户配置
场景:一家公司有 3 个部门的 AI 助手,共享网关但隔离数据和人格。
{
agents: {
list: [
{
id: "hr-assistant",
name: "HR 助手",
workspace: "~/.openclaw/workspace-hr",
model: "anthropic/claude-opus-4-6",
tools: {
allow: ["read", "message", "dingtalk-usage", "vacation"],
deny: ["exec", "gateway"]
}
},
{
id: "it-helpdesk",
name: "IT 服务台",
workspace: "~/.openclaw/workspace-it",
model: "anthropic/claude-sonnet-4-6",
tools: {
allow: ["read", "write", "exec", "message", "nodes"],
deny: ["gateway", "cron"]
},
sandbox: { mode: "all", scope: "agent" }
},
{
id: "finance-bot",
name: "财务助手",
workspace: "~/.openclaw/workspace-finance",
model: "anthropic/claude-opus-4-6",
tools: {
allow: ["read", "xlsx", "message", "ant_search"],
deny: ["exec", "write", "browser"]
}
}
]
},
bindings: [
// HR 专属钉钉群
{
agentId: "hr-assistant",
match: {
channel: "dingtalk",
peer: { kind: "channel", id: "hr-group-123" }
}
},
// IT 服务台:所有工单频道
{
agentId: "it-helpdesk",
match: { channel: "dingtalk", accountId: "it-bot" }
},
// 财务:仅私聊
{
agentId: "finance-bot",
match: {
channel: "dingtalk",
accountId: "finance-bot",
peer: { kind: "direct", id: "*" }
}
}
]
}
跨时区全球化支持
场景:公司在杭州、新加坡、旧金山有办公室,需要 24/7 支持。
{
agents: {
list: [
{
id: "asia-pacific",
name: "亚太助手",
workspace: "~/.openclaw/workspace-apac",
model: "anthropic/claude-sonnet-4-6"
},
{
id: "americas",
name: "美洲助手",
workspace: "~/.openclaw/workspace-us",
model: "anthropic/claude-sonnet-4-6"
}
]
},
bindings: [
// 按时间路由(需要自定义钩子)
// Asia/Pacific: 00:00-12:00 UTC
{ agentId: "asia-pacific", match: { channel: "slack", accountId: "global" } }
],
// 自定义钩子实现时区路由
hooks: {
presets: ["timezone-router"]
}
}
插件钩子完整示例
模型覆盖钩子
// plugins/vip-model-override.ts
export default {
id: "vip-model-override",
register(api) {
api.registerHook("before_model_resolve", async (ctx) => {
if (ctx.sessionKey.includes("vip-user")) {
return {
provider: "anthropic",
model: "claude-opus-4-6"
};
}
return null; // 不覆盖,继续默认流程
});
}
};
上下文注入钩子
// plugins/user-context-injector.ts
export default {
id: "user-context-injector",
register(api) {
api.registerHook("before_prompt_build", async (ctx) => {
const userContext = await fetchUserContext(ctx.sessionKey);
return {
prependContext: `## 用户当前状态\n${userContext}`
};
});
}
};
工具审计钩子
// plugins/tool-audit.ts
export default {
id: "tool-audit",
register(api) {
api.registerHook("before_tool_call", async (ctx) => {
await logAudit({
timestamp: new Date(),
sessionKey: ctx.sessionKey,
toolName: ctx.toolName,
params: sanitizeParams(ctx.params),
senderId: ctx.senderId,
});
return { block: false };
});
api.registerHook("after_tool_call", async (ctx) => {
await logAudit({
timestamp: new Date(),
sessionKey: ctx.sessionKey,
toolName: ctx.toolName,
result: sanitizeResult(ctx.result),
});
});
}
};
自动欢迎钩子
// plugins/auto-welcome.ts
export default {
id: "auto-welcome",
register(api) {
api.registerHook("before_agent_reply", async (ctx) => {
if (ctx.messages.length === 1) {
// 首条消息,返回欢迎语
return {
reply: {
body: "你好!我是你的专属助手。我能帮你做什么?"
},
silence: false
};
}
return null; // 继续正常 Agent 回复
});
}
};
核心源码路径索引
| 功能模块 | 核心文件 | 代码行数 | 关键函数/类型 |
|---|---|---|---|
| 运行时入口 | src/agents/pi-embedded-runner/run.ts |
~800 | runEmbeddedPiAgent() |
| 工作空间管理 | src/agents/workspace.ts |
~600 | readWorkspaceFileWithGuards() |
| 系统提示词 | src/agents/system-prompt.ts |
~300 | buildSystemPrompt() |
| 启动钩子 | src/agents/bootstrap-hooks.ts |
~100 | applyBootstrapHookOverrides() |
| 内部钩子 | src/hooks/internal-hooks.ts |
~400 | triggerInternalHook() |
| 工具策略 | src/agents/tool-policy.ts |
~200 | applyOwnerOnlyToolPolicy() |
| 会话管理器 | src/session/manager.ts |
~500 | resolveSessionKey() |
| 记忆后端 | src/memory/qmd-backend.ts |
~350 | searchTranscripts() |
性能优化建议
缓存策略
| 缓存对象 | 策略 | 失效条件 |
|---|---|---|
| 工作空间文件 | inode/dev/size/mtime 识别 | 文件变化 |
| 模型解析 | 会话级缓存 | 配置变更 |
| 技能快照 | 启动时加载 | 技能目录变化 |
| 认证凭证 | 内存缓存 + 定期刷新 | 凭证过期 |
并发控制
会话级序列化:
- 每个 sessionKey 一个执行队列
- 防止工具/会话竞争
- 保持会话历史一致性
全局队列(可选):
{
agents: {
defaults: {
queue: {
global: true, // 启用全局队列
maxConcurrency: 5 // 最大并发数
}
}
}
}
内存优化
| 优化方向 | 措施 | 效果 |
|---|---|---|
| 工具结果裁剪 | 限制大小,移除大图片 | 减少内存峰值 |
| 会话压缩 | 自动 compaction | 减少历史存储 |
| 钩子超时 | 设置钩子执行超时 | 防止阻塞 |
常见问题解答 (FAQ)
Q1: 如何调试 Agent 循环?
方法 1:启用详细日志
{
logging: {
level: "debug",
categories: ["agent", "session", "tools"]
}
}
方法 2:使用 CLI 调试
openclaw agent --session-key "agent:main:main" --verbose
方法 3:订阅 agent 事件
api.registerHook("agent_end", async (ctx) => {
console.log("Agent run completed:", {
runId: ctx.runId,
duration: ctx.endedAt - ctx.startedAt,
toolCalls: ctx.toolCalls.length,
});
});
Q2: 多代理如何共享认证凭证?
不推荐直接共享 agentDir(会导致会话冲突)。
推荐做法:复制 auth-profiles.json
cp ~/.openclaw/agents/main/agent/auth-profiles.json \
~/.openclaw/agents/work/agent/auth-profiles.json
Q3: 会话键混乱怎么办?
重置会话:
# 软重置(保留历史记录)
openclaw session reset agent:main:main
# 硬重置(删除历史)
openclaw session delete agent:main:main
检查会话键生成:
openclaw session list --verbose
Q4: 如何阻止 Agent 执行特定命令?
方法 1:工具策略
{
agents: {
defaults: {
tools: {
deny: ["exec"] // 完全禁止 exec
}
}
}
}
方法 2:钩子拦截
api.registerHook("before_tool_call", async (ctx) => {
if (ctx.toolName === "exec" && isDangerousCommand(ctx.params.command)) {
return { block: true, reason: "危险命令" };
}
});
方法 3:Owner-Only 保护
{
agents: {
defaults: {
tools: {
ownerOnly: ["exec", "gateway", "cron"]
}
}
}
}
Q5: 记忆系统如何备份?
QMD 记忆备份:
# 备份 QMD 文件
cp -r ~/.openclaw/agents/main/memory/qmd/ \
~/backups/qmd-$(date +%Y%m%d)/
会话转录备份:
# 备份会话 JSONL
cp ~/.openclaw/agents/main/sessions/*.jsonl \
~/backups/sessions-$(date +%Y%m%d)/
Q6: 如何实现跨代理消息转发?
使用 sessions_spawn + sessions_send:
// 在 agent A 中
import { sessions_send } from "@openclaw/sdk";
await sessions_send({
agentId: "agent-b",
message: "请将此消息转发给用户",
});
与竞品对比
| 特性 | OpenClaw | Botpress | Rasa | Microsoft Bot Framework |
|---|---|---|---|---|
| Agent 原生 | ✅ 完整 Agent 循环 | ❌ 对话流为主 | ❌ NLU 为主 | ⚠️ 部分支持 |
| 多代理路由 | ✅ 8 层优先级 | ❌ 单_bot_ | ❌ 单_bot_ | ⚠️ 需自定义 |
| 会话隔离 | ✅ SessionKey 设计 | ⚠️ 简单隔离 | ⚠️ 简单隔离 | ✅ 完善 |
| 记忆系统 | ✅ QMD/Honcho | ⚠️ 基础 | ⚠️ 基础 | ⚠️ 基础 |
| 钩子系统 | ✅ 内部 + 插件双系统 | ✅ 钩子 | ✅ 中间件 | ✅ 中间件 |
| 工具审批 | ✅ Owner-Only 策略 | ❌ 无 | ❌ 无 | ❌ 无 |
| 自托管优先 | ✅ 完整离线能力 | ✅ 支持 | ✅ 支持 | ⚠️ 部分云端 |
| 渠道统一 | ✅ 多渠道路由 | ✅ 支持 | ⚠️ 需适配 | ✅ 支持 |
| 心跳系统 | ✅ 自主检查 | ❌ 无 | ❌ 无 | ❌ 无 |
| 学习曲线 | 中等 | 低 | 高 | 高 |
踩坑经验与最佳实践
踩坑经验
坑 1:会话键冲突导致上下文混乱
症状:不同用户看到彼此的对话历史。
原因:dmScope 设置为 main,所有直聊共享同一会话。
解决:
{
session: {
dmScope: "per-channel-peer" // 多用户必须设置
}
}
坑 2:Agent 循环超时但未报错
症状:Agent 长时间无响应,但无错误日志。
原因:LLM 空闲超时未设置,模型无响应时卡住。
解决:
{
agents: {
defaults: {
llm: {
idleTimeoutSeconds: 120 // 慢模型设置更长
}
}
}
}
坑 3:钩子未触发
症状:注册的钩子从未被调用。
原因:钩子优先级冲突或注册时机过晚。
解决:
- 确保钩子在 Gateway 启动前注册
- 检查钩子名称拼写
- 使用
before_agent_start代替legacy钩子
坑 4:多代理认证混乱
症状:Agent A 使用了 Agent B 的凭证。
原因:共享了 agentDir 或 auth-profiles.json。
解决:每个 Agent 使用独立的 agentDir,手动复制需要的凭证。
最佳实践
1. 会话键设计
- 单用户:使用默认
main - 多用户:使用
per-channel-peer - 严格隔离:使用
per-account-channel-peer
2. 工具审批
- 将所有控制平面工具设为
ownerOnly - 使用钩子记录所有
exec调用 - 对危险命令实现白名单
3. 记忆管理
- 定期(每周)审查
MEMORY.md - 使用 QMD 后端进行会话搜索
- 避免在记忆中存储敏感信息
4. 性能优化
- 启用工作空间文件缓存
- 配置合理的块流式阈值
- 对高频钩子实现缓存
5. 调试策略
- 开发环境启用
debug日志 - 使用 CLI 进行单次调试
- 订阅
agent_end钩子记录运行统计
设计权衡
优势
| 优势 | 说明 |
|---|---|
| 主动性 | Agent 可以自主执行定时任务、检查条件、主动通知 |
| 连续性 | 会话管理和记忆系统支持长期关系建立 |
| 隔离性 | 多代理架构支持人格、数据、凭证的完全隔离 |
| 扩展性 | 钩子系统允许深度定制而无需修改核心代码 |
| 可观测性 | 完整的事件流和生命周期钩子 |
权衡
| 权衡 | 说明 | 缓解策略 |
|---|---|---|
| 复杂性增加 | 完整的 Agent 循环、钩子系统、会话管理增加了系统复杂度 | 提供详细文档和示例配置 |
| 资源消耗 | 每个 Agent 需要独立的工作空间、状态目录、会话存储 | 支持轻量化 Agent(共享技能) |
| 配置难度 | 多代理路由、钩子注册、记忆配置需要深入理解架构 | 提供配置向导和模板 |
| 调试困难 | 钩子链路复杂时,问题定位需要追踪多个执行点 | 增强日志和事件追踪 |
关键洞察
Agent 原生设计不是简单的"聊天机器人 + 工具",而是重新思考了人机交互的范式。
传统范式:
用户输入 → 机器人响应 → 等待下一次输入
Agent 原生范式:
定时检查 → 条件触发 → 多轮工具调用 → 主动通知
↓
用户 interrupt → 动态修正 → 继续执行
这种设计哲学的核心假设是:AI 代理应该被视为"数字员工",具有自己的人格、工作空间、记忆和职责范围,而不仅仅是执行命令的工具。
相关文档引用
官方文档
| 文档 | 路径 | 关键内容 |
|---|---|---|
| Agent Runtime | docs/concepts/agent.md |
运行时边界、启动文件、技能加载 |
| Agent Loop | docs/concepts/agent-loop.md |
完整生命周期、钩子点、流式传输 |
| Multi-Agent | docs/concepts/multi-agent.md |
路由规则、绑定配置、跨代理搜索 |
| Session | docs/concepts/session.md |
会话键生成、DM 隔离、生命周期 |
| System Prompt | docs/concepts/system-prompt.md |
提示词构建、启动文件注入 |
| Hooks | docs/automation/hooks.md |
内部钩子、插件钩子、决策规则 |
| Streaming | docs/concepts/streaming.md |
块流式、 chunking、转向模式 |
| Compaction | docs/concepts/compaction.md |
会话压缩、重试机制 |
源码路径
src/
├── agents/
│ ├── pi-embedded-runner/ # 运行时核心
│ ├── workspace.ts # 工作空间管理
│ ├── system-prompt.ts # 提示词构建
│ ├── bootstrap-hooks.ts # 启动钩子
│ └── tool-policy.ts # 工具策略
├── hooks/
│ ├── internal-hooks.ts # 内部钩子系统
│ └── plugin-hooks.ts # 插件钩子 API
├── session/
│ ├── manager.ts # 会话管理器
│ └── resolver.ts # 会话键解析
└── memory/
├── qmd-backend.ts # QMD 记忆后端
└── honcho-backend.ts # Honcho 后端
影响范围
Agent 原生设计哲学影响以下核心模块:
- 运行时层:嵌入式 Pi Agent Core 集成、Agent 循环执行模型、超时控制
- 会话管理层:会话键生成、DM 隔离策略、会话重置和维护
- 钩子系统:内部钩子、插件钩子、钩子决策规则、生命周期事件
- 工作空间管理:启动文件注入、Bootstrap 仪式、人格定义文件
- 多代理路由:
agents.list配置、bindings路由引擎、代理隔离 - 流式传输:块流式、推理流、转向模式、队列管理
- 记忆系统:内置记忆、QMD 后端、跨代理记忆搜索
- 工具编排:工具审批、技能加载、钩子拦截、沙箱隔离
本文档基于 OpenClaw 文档版本:openclaw-main/docs(截至 2026-04-11)
源码分析基于:openclaw-main/src(截至 2026-04-11)
上次优化时间:2026-04-11 13:14 (Asia/Shanghai)
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐
所有评论(0)