Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
重构所有代码和文档中的项目名称,将OpenFang统一更新为ZCLAW。包括: - 配置文件中的项目名称 - 代码注释和文档引用 - 环境变量和路径 - 类型定义和接口名称 - 测试用例和模拟数据 同时优化部分代码结构,移除未使用的模块,并更新相关依赖项。
27 KiB
27 KiB
ZCLAW 线上知识库
版本: 1.0.0 最后更新: 2026-03-12 目的: 为 ZClaw 项目提供全面、结构化的 ZCLAW 抷术参考
目录
核心概念
ZCLAW 是什么?
ZCLAW 是一个 自托管的 AI Agent 硴关,不是简单的"聊天 UI + 模型接入器"。
核心定位:
- 自托管: 运行在你自己的硬件上,你的规则
- 多通道: 一个 Gateway 同时服务 WhatsApp、Telegram、Discord、飞书等多个渠道
- Agent 原生: 为编码 Agent 构建,支持工具调用、会话、记忆、多 Agent 路由
- 开源: MIT 许可,社区驱动
关键洞察: ZCLAW 的核心价值是 执行 + 持续性 + 可控性:
- 执行: 能真正读写文件、跑命令、控浏览器、发消息
- 持续性: 不只是一次性问答,而是可长期运转的 Agent
- 可控性: 用户能看到配置、文本指令、工作区与约束,而不是黑盒
Agent 的真正含义
在 ZCLAW 中,一个 Agent 包含:
- 一个
agentId - 一个独立 workspace / agentDir
- 一组 bootstrap 文件 (
AGENTS.md、SOUL.md、USER.md、IDENTITY.md) - 一套工具与 sandbox 规则
- 一套 session 历史
- 一组可能的 channel bindings
- 一种人格 / 工作方式 / 角色定位
Bootstrap 文件职责
| 文件 | 职责 | 内容示例 |
|---|---|---|
AGENTS.md |
操作规范与行为准则 | 会话启动 checklist、安全规范、工具使用规则 |
SOUL.md |
身份、气质、边界 | Core Truths、Boundaries、Vibe、Continuity |
USER.md |
关于用户的信息 | 用户习惯、上下文、沟通偏好、时区 |
IDENTITY.md |
Agent 外显身份 | Name、Emoji、Avatar、Vibe |
HEARTBEAT.md |
心跳任务指令 | 定时检查任务、触发条件、投递目标 |
系统架构
四层架构
┌─────────────────────────────────────────────────────────────┐
│ 应用层 (Application) │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │WhatsApp │ │Telegram │ │ Discord │ │ 飞书 │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │
│ │ │ │ │ │
│ └────────────┴────────────┴────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Gateway (中枢) │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ WebSocket │ │ HTTP API │ │ Config │ │ │
│ │ │ Server │ │ Server │ │ Manager │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ └───────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Agent Runtime │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Skills │ │ Tools │ │ Memory │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ └───────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ LLM Providers │ │
│ │ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │ │
│ │ │ Claude │ │ GPT-4 │ │ GLM │ │ Qwen │ │ │
│ │ └────────┘ └────────┘ └────────┘ └────────┘ │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Gateway 职责
Gateway 是 ZCLAW 的真正控制面板:
- WebSocket 协议握手与会话维持
- Agent 运行时管理
- Session/stream 事件分发
- Channels 消息收发
- 配置热加载与配置 RPC
- Skills / Tools / Plugins / Heartbeat 协调
- Device auth / pairing / scopes
Workspace 结构
~/.zclaw/
├── zclaw.json # 主配置文件
├── .env # 环境变量
├── workspace/ # 默认工作区
│ ├── AGENTS.md
│ ├── SOUL.md
│ ├── USER.md
│ ├── IDENTITY.md
│ ├── memory.md
│ └── memory/
│ └── YYYY-MM-DD.md
├── agents/ # 多 Agent 状态目录
│ └── <agentId>/
│ ├── agent/
│ │ └── auth-profiles.json
│ └── sessions/
│ └── <sessionKey>.jsonl
└── skills/ # 托管技能目录
Gateway 协议
WebSocket 帧类型
// 请求帧
interface GatewayRequest {
type: 'req';
id: string;
method: string;
params?: Record<string, any>;
}
// 响应帧
interface GatewayResponse {
type: 'res';
id: string;
ok: boolean;
payload?: any;
error?: any;
}
// 事件帧
interface GatewayEvent {
type: 'event';
event: string;
payload?: any;
seq?: number;
}
握手流程
客户端 Gateway
│ │
│────── WebSocket Connect ────▶│
│ │
│◀───── connect.challenge ─────│ (包含 nonce)
│ │
│────── connect request ──────▶│ (包含 device 签名)
│ │
│◀───── connect response ──────│ (成功/失败)
│ │
│◀═══════ 事件流 ═══════════════│ (agent, chat, etc.)
│ │
Device 认证
// 签名载荷格式 (v2)
const payload = [
'v2',
deviceId,
clientId,
clientMode,
role,
scopes.join(','),
String(signedAt),
token || '',
nonce,
].join('|');
// 使用 Ed25519 签名
const signature = nacl.sign.detached(messageBytes, secretKey);
连接参数
interface ConnectParams {
minProtocol: 3;
maxProtocol: 3;
client: {
id: string; // 客户端标识
version: string; // 客户端版本
platform: string; // Win32/Darwin/Linux
mode: 'operator' | 'node';
};
role: 'operator' | 'node';
scopes: string[]; // ['operator.read', 'operator.write']
auth?: { token?: string };
device: {
id: string; // 设备 ID (公钥指纹)
publicKey: string; // Base64 编码的公钥
signature: string; // 签名
signedAt: number; // 签名时间戳
nonce: string; // 服务器提供的 nonce
};
}
核心 RPC 方法
| 方法 | 描述 | 参数 |
|---|---|---|
agent |
发送消息给 Agent | message, sessionKey?, model? |
health |
获取健康状态 | - |
status |
获取 Gateway 状态 | - |
config.get |
获取配置 | path |
config.patch |
更新配置 | path, value |
send |
通过渠道发送消息 | channel, chatId, text |
Agent 流事件
interface AgentStreamEvent {
stream: 'assistant' | 'tool' | 'lifecycle';
delta?: string; // 增量文本
content?: string; // 完整内容
tool?: string; // 工具名称
phase?: 'start' | 'end' | 'error';
runId?: string; // 运行 ID
error?: string; // 错误信息
}
配置系统
配置文件位置
~/.zclaw/zclaw.json # 主配置
~/.zclaw/.env # 环境变量
配置层级与优先级
agents.defaults.* # 全局默认
↓ 覆盖
agents.list[].* # 每个 Agent 的覆盖
↓ 覆盖
channels.defaults.* # 全渠道默认
↓ 覆盖
channels.<channel>.* # 单渠道覆盖
↓ 覆盖
channels.<channel>.accounts.<id>.* # 账号级覆盖
热加载模式
| 模式 | 行为 |
|---|---|
hybrid (默认) |
安全更改即时生效,关键更改自动重启 |
hot |
只热应用安全更改,需重启时记录警告 |
restart |
任何更改都重启 Gateway |
off |
禁用文件监控,手动重启生效 |
CLI 配置命令
# 查看配置
zclaw config get agents.defaults.workspace
# 设置配置
zclaw config set agents.defaults.heartbeat.every "2h"
# 删除配置
zclaw config unset tools.web.search.apiKey
# 配置向导
zclaw configure
# 完整设置向导
zclaw onboard
环境变量引用
{
"gateway": {
"auth": {
"token": "${ZCLAW_GATEWAY_TOKEN}"
}
},
"models": {
"providers": {
"openai": {
"apiKey": "${OPENAI_API_KEY}"
}
}
}
}
Skills 与 Tools
Skills 加载位置与优先级
- Bundled skills: 安装包自带
- Managed/local skills:
~/.zclaw/skills - Workspace skills:
<workspace>/skills - Extra dirs:
skills.load.extraDirs配置
优先级: workspace > managed > bundled > extraDirs
SKILL.md 格式
---
name: my-skill
description: 技能描述
homepage: https://example.com
user-invocable: true
disable-model-invocation: false
---
# 技能标题
技能说明内容...
Use {baseDir} to reference skill folder path.
Skills vs Tools 区别
| 概念 | 描述 | 示例 |
|---|---|---|
| Skills | 任务说明 + 规则 + 可选脚本的组合 | 代码审查、文档生成 |
| Tools | 类型化的可执行能力 | exec, read, write, browser |
内置 Tools
{
"tools": {
"exec": { "shell": true },
"web": {
"search": { "enabled": true }
},
"browser": { "enabled": true },
"read": {},
"write": {},
"edit": {}
}
}
MCP 支持
ZCLAW 原生支持 MCP (Model Context Protocol):
- 给 Agent 扩展新的上下文来源与工具面
- 让技能可以调用标准化外部能力
- 让模型在不写死工具的情况下复用第三方协议能力
插件开发
插件结构
my-plugin/
├── zclaw.plugin.json # 必需: 插件清单
├── index.ts # 入口文件
├── package.json
└── dist/
zclaw.plugin.json
{
"id": "my-plugin",
"name": "My Plugin",
"version": "1.0.0",
"description": "Plugin description",
"main": "dist/index.js",
"skills": ["./skills"],
"config": {
"enabled": {
"type": "boolean",
"default": true
}
}
}
插件 API
interface PluginAPI {
config: Record<string, any>;
// 注册 Gateway RPC 方法
registerGatewayMethod(
method: string,
handler: (ctx: RpcContext) => void
): void;
// 注册钩子
registerHook(
event: string,
handler: (...args: any[]) => any,
meta?: Record<string, any>
): void;
}
interface RpcContext {
params: Record<string, any>;
respond(ok: boolean, payload: any): void;
}
ZClaw 插件示例
// plugins/zclaw-ui/index.ts
export default function register(api: PluginAPI) {
// 注册自定义 RPC 方法
api.registerGatewayMethod('zclaw.clones.list', ({ respond }) => {
const data = readZclawData();
respond(true, { clones: data.clones });
});
// 注册启动钩子
api.registerHook('gateway:startup', async () => {
console.log('[ZCLAW] Plugin loaded');
});
}
多 Agent 路由
路由规则 (按优先级)
peer精确匹配 (DM/group/channel id)parentPeer继承匹配 (thread 继承)guildId + roles(Discord 角色路由)guildId(Discord)teamId(Slack)accountId规则- channel-level 匹配 (
accountId: "*") - fallback 到默认 Agent
Binding 配置
{
"bindings": [
{
"agentId": "work",
"match": {
"channel": "whatsapp",
"accountId": "personal",
"peer": { "kind": "direct", "id": "+15551234567" }
}
},
{
"agentId": "main",
"match": { "channel": "whatsapp" }
}
]
}
多 Agent 配置示例
{
"agents": {
"list": [
{
"id": "home",
"default": true,
"workspace": "~/.zclaw/workspace-home"
},
{
"id": "work",
"workspace": "~/.zclaw/workspace-work",
"model": "anthropic/claude-opus-4-6"
}
]
},
"bindings": [
{ "agentId": "home", "match": { "channel": "whatsapp", "accountId": "personal" } },
{ "agentId": "work", "match": { "channel": "whatsapp", "accountId": "biz" } }
]
}
安全与沙箱
沙箱模式
| 模式 | 描述 |
|---|---|
off |
无沙箱,直接执行 |
write |
只沙箱写操作 |
all |
所有操作都在沙箱中执行 |
工具策略
{
"agents": {
"list": [
{
"id": "family",
"sandbox": { "mode": "all" },
"tools": {
"allow": ["read", "exec"],
"deny": ["write", "browser"]
}
}
]
}
}
安全检查清单
- 无硬编码密钥 (使用 env 引用)
- DM 访问控制已配置
- 群聊 mention 规则已设置
- 工具权限最小化
- 沙箱模式适当
- Gateway 端口不对外暴露
Heartbeat 机制
概念
Heartbeat 不是简单的 cron,而是 定期触发一个完整 Agent turn:
- 默认读取
HEARTBEAT.md - 如果没事做,返回
HEARTBEAT_OK - 可以配置投递目标 (
none、last或具体渠道) - 可以设置 active hours
- 支持 per-agent 覆盖
配置
{
"agents": {
"defaults": {
"heartbeat": {
"every": "1h",
"activeHours": { "start": "09:00", "end": "18:00" },
"deliverTo": "last"
}
}
}
}
HEARTBEAT.md 示例
# 心跳任务
每小时检查:
1. 是否有待处理的提醒
2. 是否需要发送日报
3. 日历事件提醒
如果无事可做,回复 HEARTBEAT_OK
Channels 通道系统
支持的通道
| 通道 | 多账号 | 描述 |
|---|---|---|
| ✅ | 通过 Web WhatsApp | |
| Telegram | ✅ | Bot API |
| Discord | ✅ | Bot + Guild |
| 飞书 | ✅ | 企业自建应用 |
| Slack | ✅ | Bot + Workspace |
| iMessage | ❌ | macOS only |
| Signal | ✅ | 通过 signald |
通道配置结构
{
"channels": {
"whatsapp": {
"enabled": true,
"dmPolicy": "pairing",
"allowFrom": ["+15555550123"],
"accounts": {
"personal": {
"authDir": "~/.zclaw/credentials/whatsapp/personal"
},
"biz": {
"authDir": "~/.zclaw/credentials/whatsapp/biz"
}
}
}
}
}
访问控制
{
"channels": {
"whatsapp": {
"dmPolicy": "allowlist",
"allowFrom": ["+15555550123"],
"groups": {
"*": { "requireMention": true }
}
}
},
"messages": {
"groupChat": {
"mentionPatterns": ["@zclaw", "小龙虾"]
}
}
}
最佳实践
1. 配置管理
# 使用 CLI 而非直接编辑 JSON
zclaw config set agents.defaults.model "anthropic/claude-sonnet-4-6"
# 验证配置
zclaw doctor
# 查看日志
zclaw logs --follow
2. Agent 隔离
- 每个 Agent 使用独立 workspace
- 不共享
agentDir(会导致 auth/session 冲突) - 敏感 Agent 启用沙箱
3. 密钥管理
// 使用环境变量引用
{
"models": {
"providers": {
"openai": {
"apiKey": "${OPENAI_API_KEY}"
}
}
}
}
4. 错误处理
- Gateway 连接是协议适配工程,不是简单的 ws 连接
- 实现指数退避重连
- 正确处理
connect.challenge
ZClaw 映射指南
设置页面对应关系
| ZClaw 页面 | ZCLAW 子系统 | 真实目标 |
|---|---|---|
| 通用 | 系统级设置 | 控制连接状态、系统级行为开关 |
| 模型与 API | providers / model defaults | 管理 provider 配置、主模型与 fallback |
| MCP 服务 | Tools / MCP | 定义 Agent 可接入的外部能力 |
| 技能 | Skills | 管理 Agent 可调用的工作流知识库 |
| IM 频道 | Channels | 管理消息来源和路由规则 |
| 工作区 | Workspace / Sandbox | 确定 Agent 执行边界 |
| 数据与隐私 | Data / Telemetry | 明确数据存储位置和隐私设置 |
| 分身/快速配置 | Agents / Bindings | 创建/配置新的 Agent 实例 |
ZClaw 自定义 RPC 方法
// plugins/zclaw-ui 注册的方法
client.listClones() // zclaw.clones.list
client.createClone(opts) // zclaw.clones.create
client.updateClone(id, opts) // zclaw.clones.update
client.deleteClone(id) // zclaw.clones.delete
client.getUsageStats() // zclaw.stats.usage
client.getSessionStats() // zclaw.stats.sessions
client.getWorkspaceInfo() // zclaw.workspace.info
client.getPluginStatus() // zclaw.plugins.status
client.getQuickConfig() // zclaw.config.quick
client.listSkills() // zclaw.skills.list
分身 (Clone) = Agent 实例
interface CloneConfig {
id: string;
name: string;
role?: string;
nickname?: string;
scenarios?: string[];
model?: string;
workspaceDir?: string;
workspaceResolvedPath?: string;
restrictFiles?: boolean;
privacyOptIn?: boolean;
userName?: string;
userRole?: string;
bootstrapReady?: boolean;
bootstrapFiles?: Array<{ name: string; path: string; exists: boolean }>;
}
判断标准
如果一个页面改动之后,没有改变 ZCLAW Runtime 的真实行为、真实配置、真实路由、真实工作区或真实 Agent 上下文,那它大概率还只是"演示 UI",不是系统能力。
ZCLAW 桌面 Gateway 握手排障案例(2026-03)
症状演进
- 初始表现为桌面端长时间停留在“握手中...”
- 修正握手客户端身份后,错误表象变成
WebSocket connection failed - 修复候选地址 fallback 的错误覆盖后,暴露出真实错误
origin not allowed - 自动补齐
gateway.controlUi.allowedOrigins后,错误继续推进为pairing required
已确认的排查结论
gateway.auth.token已正确从zclaw.json读取并注入桌面端连接- Tauri 调试版实际运行的是
target/debug/resources/zclaw-runtime - Gateway WebSocket 握手客户端身份需满足当前 schema:
client.id=cliclient.mode=clirole=operator
- 浏览器 / WebView 环境与 Node 探针的关键差异是会附带
Origin - Tauri WebView 需要被加入:
gateway.controlUi.allowedOriginshttp://tauri.localhosttauri://localhost
- 当
origin not allowed被解决后,Gateway 会继续要求对当前设备完成 pairing
有效的排障方法
1. 先分离“网络失败”和“协议失败”
如果 UI 只显示 WebSocket connection failed,先检查连接代码是否在多个候选地址之间 fallback,并把更早的握手错误覆盖掉。
ZCLAW 的处理方式是:
- 仅对以下错误继续尝试下一个候选地址:
WebSocket connection failedGateway handshake timed outWebSocket closed before handshake completed
- 对握手 / 鉴权 / schema 错误立即停止 fallback,原样暴露给 UI
2. 用独立协议探针验证 Gateway 真正接受的握手参数
在本案例中,Node 探针证明了:
cli/cli/operator是可接受的客户端身份- 设备
deviceId必须和publicKey的派生规则一致 - 仅靠终端探针成功并不能证明 Tauri WebView 一定能连通,因为 WebView 会额外带
Origin
3. 优先检查本地 Gateway 的 pending / paired devices
可用命令:
zclaw devices list --json
本案例中,pairing required 发生时,devices list 已能看到当前桌面端的 pending 请求,说明:
- 连接已到达 Gateway
- 当前缺的是“批准这台设备”,不是 token 或网络
ZCLAW 当前修复策略
A. 连接前自动准备本地 Gateway
桌面端在 Tauri 运行时连接前,先调用本地准备逻辑:
- 确保
gateway.controlUi.allowedOrigins包含:http://tauri.localhosttauri://localhost
- 如果配置被修改且 Gateway 正在运行,自动重启 Gateway 使配置生效
B. 握手遇到 pairing required 时自动批准本机桌面设备
当前策略只在本地 loopback Gateway 下启用:
- 仅匹配
ws://127.0.0.1:*或ws://localhost:* - 前端读取当前桌面端持久化的
deviceId/publicKey - Tauri 侧调用:
zclaw devices list --json
zclaw devices approve <requestId> --json --token <token> --url <url>
- 只批准同时匹配以下条件的 pending request:
deviceIdpublicKey
- 批准成功后立即重试连接
后续遇到同类问题时的最短排障顺序
- 确认当前运行的是目标
desktop.exe - 确认
zclaw.json中有gateway.auth.token - 确认 WebView localStorage 已持久化
zclaw_gateway_url/zclaw_gateway_token - 把握手错误原样暴露,不要让 fallback 覆盖
- 若报
origin not allowed:- 检查
gateway.controlUi.allowedOrigins
- 检查
- 若报
pairing required:- 检查
zclaw devices list --json - 看当前桌面设备是否进入
pending
- 检查
- 如果 pending 存在,优先做“只批准本机当前设备”的自动化,而不是直接放宽所有设备
ZCLAW 桌面聊天 / 模型配置协议错配案例(2026-03)
症状
- 桌面端显示 Gateway 已连接,但发送消息立即失败
- 常见错误文案为:
invalid agent params: must have required property 'idempotencyKey'invalid agent params: must NOT have additional properties: model
- 模型与 API 页面可以切换本地显示值,但不会改变 Gateway 的真实默认模型
根因
- ZCLAW 桌面端此前仍按旧协议调用
agent:- 发送了顶层
model - 没有发送必填
idempotencyKey
- 发送了顶层
- 当前 ZCLAW runtime 的
agentschema 已变更为:message必填idempotencyKey必填model不是允许的顶层字段
- 桌面端“模型切换”之前只是本地 Zustand 状态,没有写回 Gateway 配置,因此不会影响真实运行时行为
有效排查方法
- 不要只看仓库里的旧 client 封装,要直接核对当前实际 runtime 的 schema
- 如果仓库源码里搜不到新字段(如
idempotencyKey),优先检查打包后的zclaw-runtime - 在本案例中,真实约束来自 runtime 中的
AgentParamsSchema:message: NonEmptyStringidempotencyKey: NonEmptyStringagentId/sessionKey/...可选additionalProperties: false
- 对模型配置,不要只改前端本地状态;应优先确认 runtime 是否已暴露:
models.listconfig.getconfig.apply
ZCLAW 当前修复策略
desktop/src/lib/gateway-client.tschat()改为发送idempotencyKey- 停止发送非法顶层
model - 新增
models.list/config.get/config.apply客户端接口
desktop/src/store/chatStore.ts- 发送消息时不再把本地
clone_*直接当作 runtimeagentId - 继续保留前端会话与分身关联信息,避免 UI 上下文丢失
- 发送消息时不再把本地
desktop/src/components/Settings/ModelsAPI.tsx- 改为基于真实 Gateway 配置读写默认模型与中文模型插件 Provider 配置
desktop/src/components/ChatArea.tsx- 聊天输入区模型下拉改为通过
config.apply更新 Gateway 默认模型,而不是只切本地显示
- 聊天输入区模型下拉改为通过
当前结论
- 如果错误同时出现
missing idempotencyKey和unexpected property model,优先判断为“桌面端协议版本落后于当前 runtime” - 如果模型切换只改变 UI 文案、不会改变新会话的实际模型,说明它仍是“演示态”,应改为落到
config.get/config.apply
参考资料
官方文档
社区资源
ZClaw 内部参考
docs/zclaw-deep-dive.md- 深度分析config/zclaw.default.json- 默认配置plugins/zclaw-ui/index.ts- 插件实现desktop/src/lib/gateway-client.ts- 客户端实现