first commit

This commit is contained in:
2026-02-28 23:01:30 +08:00
commit 3956ee4806
415 changed files with 74538 additions and 0 deletions

View File

@@ -0,0 +1,146 @@
---
read_when:
- 你需要智能体循环或生命周期事件的详细说明
summary: 智能体循环生命周期、流和等待语义
title: 智能体循环
x-i18n:
generated_at: "2026-02-03T10:05:11Z"
model: claude-opus-4-5
provider: pi
source_hash: 0775b96eb3451e137297661a1095eaefb2bafeebb5f78123174a46290e18b014
source_path: concepts/agent-loop.md
workflow: 15
---
# 智能体循环OpenClaw
智能体循环是智能体的完整"真实"运行:接收 → 上下文组装 → 模型推理 → 工具执行 → 流式回复 → 持久化。这是将消息转化为操作和最终回复的权威路径,同时保持会话状态的一致性。
在 OpenClaw 中,循环是每个会话的单次序列化运行,在模型思考、调用工具和流式输出时发出生命周期和流事件。本文档解释了这个真实循环是如何端到端连接的。
## 入口点
- Gateway 网关 RPC`agent``agent.wait`
- CLI`agent` 命令。
## 工作原理(高层次)
1. `agent` RPC 验证参数解析会话sessionKey/sessionId持久化会话元数据立即返回 `{ runId, acceptedAt }`
2. `agentCommand` 运行智能体:
- 解析模型 + 思考/详细模式默认值
- 加载 Skills 快照
- 调用 `runEmbeddedPiAgent`pi-agent-core 运行时)
- 如果嵌入式循环未发出**生命周期结束/错误**事件,则发出该事件
3. `runEmbeddedPiAgent`
- 通过每会话 + 全局队列序列化运行
- 解析模型 + 认证配置文件并构建 pi 会话
- 订阅 pi 事件并流式传输助手/工具增量
- 强制执行超时 -> 超时则中止运行
- 返回有效负载 + 使用元数据
4. `subscribeEmbeddedPiSession` 将 pi-agent-core 事件桥接到 OpenClaw `agent` 流:
- 工具事件 => `stream: "tool"`
- 助手增量 => `stream: "assistant"`
- 生命周期事件 => `stream: "lifecycle"``phase: "start" | "end" | "error"`
5. `agent.wait` 使用 `waitForAgentJob`
- 等待 `runId` 的**生命周期结束/错误**
- 返回 `{ status: ok|error|timeout, startedAt, endedAt, error? }`
## 队列 + 并发
- 运行按会话键(会话通道)序列化,可选择通过全局通道。
- 这可以防止工具/会话竞争并保持会话历史的一致性。
- 消息渠道可以选择队列模式collect/steer/followup来馈送此通道系统。参见[命令队列](/concepts/queue)。
## 会话 + 工作区准备
- 解析并创建工作区;沙箱隔离运行可能会重定向到沙箱工作区根目录。
- 加载 Skills或从快照中复用并注入到环境和提示中。
- 解析引导/上下文文件并注入到系统提示报告中。
- 获取会话写锁;在流式传输之前打开并准备 `SessionManager`
## 提示组装 + 系统提示
- 系统提示由 OpenClaw 的基础提示、Skills 提示、引导上下文和每次运行的覆盖构建。
- 强制执行模型特定的限制和压缩保留令牌。
- 参见[系统提示](/concepts/system-prompt)了解模型看到的内容。
## 钩子点(可以拦截的位置)
OpenClaw 有两个钩子系统:
- **内部钩子**Gateway 网关钩子):用于命令和生命周期事件的事件驱动脚本。
- **插件钩子**:智能体/工具生命周期和 Gateway 网关管道中的扩展点。
### 内部钩子Gateway 网关钩子)
- **`agent:bootstrap`**:在系统提示最终确定之前构建引导文件时运行。用于添加/删除引导上下文文件。
- **命令钩子**`/new``/reset``/stop` 和其他命令事件(参见钩子文档)。
参见[钩子](/automation/hooks)了解设置和示例。
### 插件钩子(智能体 + Gateway 网关生命周期)
这些在智能体循环或 Gateway 网关管道内运行:
- **`before_agent_start`**:在运行开始前注入上下文或覆盖系统提示。
- **`agent_end`**:在完成后检查最终消息列表和运行元数据。
- **`before_compaction` / `after_compaction`**:观察或注释压缩周期。
- **`before_tool_call` / `after_tool_call`**:拦截工具参数/结果。
- **`tool_result_persist`**:在工具结果写入会话记录之前同步转换它们。
- **`message_received` / `message_sending` / `message_sent`**:入站 + 出站消息钩子。
- **`session_start` / `session_end`**:会话生命周期边界。
- **`gateway_start` / `gateway_stop`**Gateway 网关生命周期事件。
参见[插件](/tools/plugin#plugin-hooks)了解钩子 API 和注册详情。
## 流式传输 + 部分回复
- 助手增量从 pi-agent-core 流式传输并作为 `assistant` 事件发出。
- 分块流式传输可以在 `text_end``message_end` 时发出部分回复。
- 推理流式传输可以作为单独的流或作为块回复发出。
- 参见[流式传输](/concepts/streaming)了解分块和块回复行为。
## 工具执行 + 消息工具
- 工具开始/更新/结束事件在 `tool` 流上发出。
- 工具结果在记录/发出之前会对大小和图像有效负载进行清理。
- 消息工具发送会被跟踪以抑制重复的助手确认。
## 回复整形 + 抑制
- 最终有效负载由以下内容组装:
- 助手文本(和可选的推理)
- 内联工具摘要(当详细模式 + 允许时)
- 模型出错时的助手错误文本
- `NO_REPLY` 被视为静默令牌,从出站有效负载中过滤。
- 消息工具重复项从最终有效负载列表中移除。
- 如果没有剩余可渲染的有效负载且工具出错,则发出回退工具错误回复(除非消息工具已经发送了用户可见的回复)。
## 压缩 + 重试
- 自动压缩发出 `compaction` 流事件,可以触发重试。
- 重试时,内存缓冲区和工具摘要会重置以避免重复输出。
- 参见[压缩](/concepts/compaction)了解压缩管道。
## 事件流(当前)
- `lifecycle`:由 `subscribeEmbeddedPiSession` 发出(以及作为 `agentCommand` 的回退)
- `assistant`:从 pi-agent-core 流式传输的增量
- `tool`:从 pi-agent-core 流式传输的工具事件
## 聊天渠道处理
- 助手增量被缓冲到聊天 `delta` 消息中。
- 在**生命周期结束/错误**时发出聊天 `final`
## 超时
- `agent.wait` 默认30 秒(仅等待)。`timeoutMs` 参数可覆盖。
- 智能体运行时:`agents.defaults.timeoutSeconds` 默认 600 秒;在 `runEmbeddedPiAgent` 中止计时器中强制执行。
## 可能提前结束的情况
- 智能体超时(中止)
- AbortSignal取消
- Gateway 网关断开连接或 RPC 超时
- `agent.wait` 超时(仅等待,不会停止智能体)

View File

@@ -0,0 +1,219 @@
---
read_when:
- 你需要解释智能体工作区或其文件布局
- 你想备份或迁移智能体工作区
summary: 智能体工作区:位置、布局和备份策略
title: 智能体工作区
x-i18n:
generated_at: "2026-02-03T07:45:49Z"
model: claude-opus-4-5
provider: pi
source_hash: 84c550fd89b5f2474aeae586795485fd29d36effbb462f13342b31540fc18b82
source_path: concepts/agent-workspace.md
workflow: 15
---
# 智能体工作区
工作区是智能体的家。它是文件工具和工作区上下文使用的唯一工作目录。请保持其私密性并将其视为记忆。
这与 `~/.openclaw/` 是分开的,后者存储配置、凭证和会话。
**重要:** 工作区是**默认 cwd**,而不是硬性沙箱。工具会根据工作区解析相对路径,但绝对路径仍然可以访问主机上的其他位置,除非启用了沙箱隔离。如果你需要隔离,请使用
[`agents.defaults.sandbox`](/gateway/sandboxing)(和/或每智能体沙箱配置)。
当启用沙箱隔离且 `workspaceAccess` 不是 `"rw"` 时,工具在 `~/.openclaw/sandboxes` 下的沙箱工作区内操作,而不是你的主机工作区。
## 默认位置
- 默认:`~/.openclaw/workspace`
- 如果设置了 `OPENCLAW_PROFILE` 且不是 `"default"`,默认值变为
`~/.openclaw/workspace-<profile>`
-`~/.openclaw/openclaw.json` 中覆盖:
```json5
{
agent: {
workspace: "~/.openclaw/workspace",
},
}
```
`openclaw onboard``openclaw configure``openclaw setup` 将创建工作区并在缺失时填充引导文件。
如果你已经自己管理工作区文件,可以禁用引导文件创建:
```json5
{ agent: { skipBootstrap: true } }
```
## 额外的工作区文件夹
旧版安装可能创建了 `~/openclaw`。保留多个工作区目录可能会导致混乱的认证或状态漂移,因为同一时间只有一个工作区是活动的。
**建议:** 保持单个活动工作区。如果你不再使用额外的文件夹,请归档或移至废纸篓(例如 `trash ~/openclaw`)。
如果你有意保留多个工作区,请确保 `agents.defaults.workspace` 指向活动的那个。
`openclaw doctor` 在检测到额外工作区目录时会发出警告。
## 工作区文件映射(每个文件的含义)
这些是 OpenClaw 在工作区内期望的标准文件:
- `AGENTS.md`
- 智能体的操作指南以及它应该如何使用记忆。
- 在每个会话开始时加载。
- 适合放置规则、优先级和"如何行为"的详细信息。
- `SOUL.md`
- 人设、语气和边界。
- 每个会话加载。
- `USER.md`
- 用户是谁以及如何称呼他们。
- 每个会话加载。
- `IDENTITY.md`
- 智能体的名称、风格和表情符号。
- 在引导仪式期间创建/更新。
- `TOOLS.md`
- 关于你本地工具和惯例的注释。
- 不控制工具可用性;仅作为指导。
- `HEARTBEAT.md`
- 可选的心跳运行小型检查清单。
- 保持简短以避免 token 消耗。
- `BOOT.md`
- 当启用内部 hooks 时,在 Gateway 网关重启时执行的可选启动检查清单。
- 保持简短;使用 message 工具进行出站发送。
- `BOOTSTRAP.md`
- 一次性首次运行仪式。
- 仅为全新工作区创建。
- 仪式完成后删除它。
- `memory/YYYY-MM-DD.md`
- 每日记忆日志(每天一个文件)。
- 建议在会话开始时读取今天 + 昨天的内容。
- `MEMORY.md`(可选)
- 精选的长期记忆。
- 仅在主私密会话中加载(不在共享/群组上下文中)。
参见 [记忆](/concepts/memory) 了解工作流程和自动记忆刷新。
- `skills/`(可选)
- 工作区特定的 Skills。
- 当名称冲突时覆盖托管/捆绑的 Skills。
- `canvas/`(可选)
- 用于节点显示的 Canvas UI 文件(例如 `canvas/index.html`)。
如果任何引导文件缺失OpenClaw 会在会话中注入"缺失文件"标记并继续。大型引导文件在注入时会被截断;使用 `agents.defaults.bootstrapMaxChars` 调整限制默认20000
`openclaw setup` 可以重新创建缺失的默认值而不覆盖现有文件。
## 工作区中不包含的内容
这些位于 `~/.openclaw/` 下,不应提交到工作区仓库:
- `~/.openclaw/openclaw.json`(配置)
- `~/.openclaw/credentials/`OAuth token、API 密钥)
- `~/.openclaw/agents/<agentId>/sessions/`(会话记录 + 元数据)
- `~/.openclaw/skills/`(托管的 Skills
如果你需要迁移会话或配置,请单独复制它们并将它们排除在版本控制之外。
## Git 备份(推荐,私有)
将工作区视为私密记忆。将其放入**私有** git 仓库以便备份和恢复。
在运行 Gateway 网关的机器上执行这些步骤(工作区就在那里)。
### 1初始化仓库
如果安装了 git全新工作区会自动初始化。如果此工作区还不是仓库请运行
```bash
cd ~/.openclaw/workspace
git init
git add AGENTS.md SOUL.md TOOLS.md IDENTITY.md USER.md HEARTBEAT.md memory/
git commit -m "Add agent workspace"
```
### 2添加私有远程适合初学者的选项
选项 AGitHub 网页界面
1. 在 GitHub 上创建新的**私有**仓库。
2. 不要用 README 初始化(避免合并冲突)。
3. 复制 HTTPS 远程 URL。
4. 添加远程并推送:
```bash
git branch -M main
git remote add origin <https-url>
git push -u origin main
```
选项 BGitHub CLI`gh`
```bash
gh auth login
gh repo create openclaw-workspace --private --source . --remote origin --push
```
选项 CGitLab 网页界面
1. 在 GitLab 上创建新的**私有**仓库。
2. 不要用 README 初始化(避免合并冲突)。
3. 复制 HTTPS 远程 URL。
4. 添加远程并推送:
```bash
git branch -M main
git remote add origin <https-url>
git push -u origin main
```
### 3持续更新
```bash
git status
git add .
git commit -m "Update memory"
git push
```
## 不要提交密钥
即使在私有仓库中,也要避免在工作区中存储密钥:
- API 密钥、OAuth token、密码或私有凭证。
- `~/.openclaw/` 下的任何内容。
- 聊天的原始转储或敏感附件。
如果你必须存储敏感引用,请使用占位符并将真正的密钥保存在其他地方(密码管理器、环境变量或 `~/.openclaw/`)。
建议的 `.gitignore` 起始配置:
```gitignore
.DS_Store
.env
**/*.key
**/*.pem
**/secrets*
```
## 将工作区迁移到新机器
1. 将仓库克隆到所需路径(默认 `~/.openclaw/workspace`)。
2.`~/.openclaw/openclaw.json` 中将 `agents.defaults.workspace` 设置为该路径。
3. 运行 `openclaw setup --workspace <path>` 来填充任何缺失的文件。
4. 如果你需要会话,请单独从旧机器复制 `~/.openclaw/agents/<agentId>/sessions/`
## 高级注意事项
- 多智能体路由可以为每个智能体使用不同的工作区。参见
[渠道路由](/channels/channel-routing) 了解路由配置。
- 如果启用了 `agents.defaults.sandbox`,非主会话可以在 `agents.defaults.sandbox.workspaceRoot` 下使用每会话沙箱工作区。

115
content/concepts/agent.md Normal file
View File

@@ -0,0 +1,115 @@
---
read_when:
- 更改智能体运行时、工作区引导或会话行为时
summary: 智能体运行时(嵌入式 pi-mono、工作区契约和会话引导
title: 智能体运行时
x-i18n:
generated_at: "2026-02-03T10:04:53Z"
model: claude-opus-4-5
provider: pi
source_hash: 04b4e0bc6345d2afd9a93186e5d7a02a393ec97da2244e531703cb6a1c182325
source_path: concepts/agent.md
workflow: 15
---
# 智能体运行时 🤖
OpenClaw 运行一个源自 **pi-mono** 的嵌入式智能体运行时。
## 工作区(必需)
OpenClaw 使用单一智能体工作区目录(`agents.defaults.workspace`)作为智能体**唯一**的工作目录(`cwd`),用于工具和上下文。
建议:使用 `openclaw setup` 在缺失时创建 `~/.openclaw/openclaw.json` 并初始化工作区文件。
完整工作区布局 + 备份指南:[智能体工作区](/concepts/agent-workspace)
如果启用了 `agents.defaults.sandbox`,非主会话可以在 `agents.defaults.sandbox.workspaceRoot` 下使用按会话隔离的工作区覆盖此设置(参见 [Gateway 网关配置](/gateway/configuration))。
## 引导文件(注入)
`agents.defaults.workspace`OpenClaw 期望以下用户可编辑的文件:
- `AGENTS.md` — 操作指令 + "记忆"
- `SOUL.md` — 人设、边界、语气
- `TOOLS.md` — 用户维护的工具说明(例如 `imsg``sag`、约定)
- `BOOTSTRAP.md` — 一次性首次运行仪式(完成后删除)
- `IDENTITY.md` — 智能体名称/风格/表情
- `USER.md` — 用户档案 + 偏好称呼
在新会话的第一轮OpenClaw 将这些文件的内容直接注入智能体上下文。
空文件会被跳过。大文件会被修剪和截断并添加标记,以保持提示词精简(阅读文件获取完整内容)。
如果文件缺失OpenClaw 会注入一行"文件缺失"标记(`openclaw setup` 将创建安全的默认模板)。
`BOOTSTRAP.md` 仅在**全新工作区**(没有其他引导文件存在)时创建。如果你在完成仪式后删除它,后续重启不应重新创建。
要完全禁用引导文件创建(用于预置工作区),请设置:
```json5
{ agent: { skipBootstrap: true } }
```
## 内置工具
核心工具read/exec/edit/write 及相关系统工具)始终可用,受工具策略约束。`apply_patch` 是可选的,由 `tools.exec.applyPatch` 控制。`TOOLS.md` **不**控制哪些工具存在;它是关于*你*希望如何使用它们的指导。
## Skills
OpenClaw 从三个位置加载 Skills名称冲突时工作区优先
- 内置(随安装包提供)
- 托管/本地:`~/.openclaw/skills`
- 工作区:`<workspace>/skills`
Skills 可通过配置/环境变量控制(参见 [Gateway 网关配置](/gateway/configuration) 中的 `skills`)。
## pi-mono 集成
OpenClaw 复用 pi-mono 代码库的部分内容(模型/工具),但**会话管理、设备发现和工具连接由 OpenClaw 负责**。
- 无 pi-coding 智能体运行时。
- 不读取 `~/.pi/agent``<workspace>/.pi` 设置。
## 会话
会话记录以 JSONL 格式存储在:
- `~/.openclaw/agents/<agentId>/sessions/<SessionId>.jsonl`
会话 ID 是稳定的,由 OpenClaw 选择。
**不**读取旧版 Pi/Tau 会话文件夹。
## 流式传输中的引导
当队列模式为 `steer` 时,入站消息会注入当前运行。
队列在**每次工具调用后**检查;如果存在排队消息,当前助手消息的剩余工具调用将被跳过(工具结果显示错误"Skipped due to queued user message."),然后在下一个助手响应前注入排队的用户消息。
当队列模式为 `followup``collect` 时,入站消息会保留到当前轮次结束,然后使用排队的载荷开始新的智能体轮次。参见 [队列](/concepts/queue) 了解模式 + 防抖/上限行为。
分块流式传输在助手块完成后立即发送;默认为**关闭**`agents.defaults.blockStreamingDefault: "off"`)。
通过 `agents.defaults.blockStreamingBreak` 调整边界(`text_end``message_end`;默认为 text_end
使用 `agents.defaults.blockStreamingChunk` 控制软块分块(默认 8001200 字符;优先段落分隔,其次换行;最后是句子)。
使用 `agents.defaults.blockStreamingCoalesce` 合并流式块以减少单行刷屏(发送前基于空闲的合并)。非 Telegram 渠道需要显式设置 `*.blockStreaming: true` 以启用分块回复。
工具启动时发出详细工具摘要无防抖Control UI 在可用时通过智能体事件流式传输工具输出。
更多详情:[流式传输 + 分块](/concepts/streaming)。
## 模型引用
配置中的模型引用(例如 `agents.defaults.model``agents.defaults.models`)通过在**第一个** `/` 处分割来解析。
- 配置模型时使用 `provider/model`
- 如果模型 ID 本身包含 `/`OpenRouter 风格),请包含提供商前缀(例如:`openrouter/moonshotai/kimi-k2`)。
- 如果省略提供商OpenClaw 将输入视为别名或**默认提供商**的模型(仅在模型 ID 中没有 `/` 时有效)。
## 配置(最小)
至少需要设置:
- `agents.defaults.workspace`
- `channels.whatsapp.allowFrom`(强烈建议)
---
_下一篇[群聊](/channels/group-messages)_ 🦞

View File

@@ -0,0 +1,123 @@
---
read_when:
- 正在开发 Gateway 网关协议、客户端或传输层
summary: WebSocket Gateway 网关架构、组件和客户端流程
title: Gateway 网关架构
x-i18n:
generated_at: "2026-02-03T07:45:55Z"
model: claude-opus-4-5
provider: pi
source_hash: c636d5d8a5e628067432b30671466309e3d630b106d413f1708765bf2a9399a1
source_path: concepts/architecture.md
workflow: 15
---
# Gateway 网关架构
最后更新2026-01-22
## 概述
- 单个长期运行的 **Gateway 网关**拥有所有消息平台(通过 Baileys 的 WhatsApp、通过 grammY 的 Telegram、Slack、Discord、Signal、iMessage、WebChat
- 控制平面客户端macOS 应用、CLI、Web 界面、自动化)通过配置的绑定主机(默认 `127.0.0.1:18789`)上的 **WebSocket** 连接到 Gateway 网关。
- **节点**macOS/iOS/Android/无头设备)也通过 **WebSocket** 连接,但声明 `role: node` 并带有明确的能力/命令。
- 每台主机一个 Gateway 网关;它是唯一打开 WhatsApp 会话的位置。
- **canvas 主机**(默认 `18793`)提供智能体可编辑的 HTML 和 A2UI。
## 组件和流程
### Gateway 网关(守护进程)
- 维护提供商连接。
- 暴露类型化的 WS API请求、响应、服务器推送事件
- 根据 JSON Schema 验证入站帧。
- 发出事件如 `agent``chat``presence``health``heartbeat``cron`
### 客户端mac 应用 / CLI / web 管理)
- 每个客户端一个 WS 连接。
- 发送请求(`health``status``send``agent``system-presence`)。
- 订阅事件(`tick``agent``presence``shutdown`)。
### 节点macOS / iOS / Android / 无头设备)
-`role: node` 连接到**同一个 WS 服务器**。
-`connect` 中提供设备身份;配对是**基于设备**的(角色为 `node`),批准存储在设备配对存储中。
- 暴露命令如 `canvas.*``camera.*``screen.record``location.get`
协议详情:
- [Gateway 网关协议](/gateway/protocol)
### WebChat
- 静态界面,使用 Gateway 网关 WS API 获取聊天历史和发送消息。
- 在远程设置中,通过与其他客户端相同的 SSH/Tailscale 隧道连接。
## 连接生命周期(单个客户端)
```
Client Gateway
| |
|---- req:connect -------->|
|<------ res (ok) ---------| (or res error + close)
| (payload=hello-ok carries snapshot: presence + health)
| |
|<------ event:presence ---|
|<------ event:tick -------|
| |
|------- req:agent ------->|
|<------ res:agent --------| (ack: {runId,status:"accepted"})
|<------ event:agent ------| (streaming)
|<------ res:agent --------| (final: {runId,status,summary})
| |
```
## 线路协议(摘要)
- 传输WebSocket带 JSON 载荷的文本帧。
- 第一帧**必须**是 `connect`
- 握手后:
- 请求:`{type:"req", id, method, params}``{type:"res", id, ok, payload|error}`
- 事件:`{type:"event", event, payload, seq?, stateVersion?}`
- 如果设置了 `OPENCLAW_GATEWAY_TOKEN`(或 `--token``connect.params.auth.token` 必须匹配,否则套接字关闭。
- 有副作用的方法(`send``agent`)需要幂等键以安全重试;服务器保持短期去重缓存。
- 节点必须在 `connect` 中包含 `role: "node"` 以及能力/命令/权限。
## 配对 + 本地信任
- 所有 WS 客户端(操作员 + 节点)在 `connect` 时包含**设备身份**。
- 新设备 ID 需要配对批准Gateway 网关为后续连接颁发**设备令牌**。
- **本地**连接loopback 或 Gateway 网关主机自身的 tailnet 地址)可以自动批准以保持同主机用户体验流畅。
- **非本地**连接必须签名 `connect.challenge` nonce 并需要明确批准。
- Gateway 网关认证(`gateway.auth.*`)仍适用于**所有**连接,无论本地还是远程。
详情:[Gateway 网关协议](/gateway/protocol)、[配对](/channels/pairing)、[安全](/gateway/security)。
## 协议类型和代码生成
- TypeBox 模式定义协议。
- 从这些模式生成 JSON Schema。
- 从 JSON Schema 生成 Swift 模型。
## 远程访问
- 推荐Tailscale 或 VPN。
- 替代方案SSH 隧道
```bash
ssh -N -L 18789:127.0.0.1:18789 user@host
```
- 相同的握手 + 认证令牌适用于隧道连接。
- 远程设置中可以为 WS 启用 TLS + 可选的证书固定。
## 操作快照
- 启动:`openclaw gateway`(前台,日志输出到 stdout
- 健康检查:通过 WS 的 `health`(也包含在 `hello-ok` 中)。
- 监控:使用 launchd/systemd 自动重启。
## 不变量
- 每台主机恰好一个 Gateway 网关控制单个 Baileys 会话。
- 握手是强制的;任何非 JSON 或非 connect 的第一帧都会导致硬关闭。
- 事件不会重放;客户端必须在出现间隙时刷新。

View File

@@ -0,0 +1,67 @@
---
read_when:
- 你想了解自动压缩和 /compact
- 你正在调试长会话触及上下文限制的问题
summary: 上下文窗口 + 压缩OpenClaw 如何将会话保持在模型限制内
title: 压缩
x-i18n:
generated_at: "2026-02-01T20:22:17Z"
model: claude-opus-4-5
provider: pi
source_hash: e1d6791f2902044b5798ebf9320a7d055d37211eff4be03caa35d7e328ae803c
source_path: concepts/compaction.md
workflow: 14
---
# 上下文窗口与压缩
每个模型都有一个**上下文窗口**(可见的最大 token 数。长时间运行的对话会累积消息和工具结果一旦窗口空间紧张OpenClaw 会**压缩**较早的历史记录以保持在限制范围内。
## 什么是压缩
压缩会将**较早的对话总结**为一条紧凑的摘要条目,并保持近期消息不变。摘要存储在会话历史中,因此后续请求使用的是:
- 压缩摘要
- 压缩点之后的近期消息
压缩会**持久化**到会话的 JSONL 历史记录中。
## 配置
有关 `agents.defaults.compaction` 设置,请参阅[压缩配置与模式](/concepts/compaction)。
## 自动压缩(默认开启)
当会话接近或超过模型的上下文窗口时OpenClaw 会触发自动压缩,并可能使用压缩后的上下文重试原始请求。
你会看到:
- 详细模式下显示 `🧹 Auto-compaction complete`
- `/status` 显示 `🧹 Compactions: <count>`
在压缩之前OpenClaw 可以运行一次**静默记忆刷写**轮次,将持久化笔记写入磁盘。详情及配置请参阅[记忆](/concepts/memory)。
## 手动压缩
使用 `/compact`(可选附带指令)强制执行一次压缩:
```
/compact Focus on decisions and open questions
```
## 上下文窗口来源
上下文窗口因模型而异。OpenClaw 使用已配置提供商目录中的模型定义来确定限制。
## 压缩与修剪
- **压缩**:总结并**持久化**到 JSONL 中。
- **会话修剪**:仅裁剪旧的**工具结果****在内存中**按请求进行。
有关修剪的详情,请参阅 [/concepts/session-pruning](/concepts/session-pruning)。
## 提示
- 当会话感觉过时或上下文臃肿时,使用 `/compact`
- 大型工具输出已被截断;修剪可以进一步减少工具结果的堆积。
- 如果你需要全新开始,`/new``/reset` 会启动一个新的会话 ID。

168
content/concepts/context.md Normal file
View File

@@ -0,0 +1,168 @@
---
read_when:
- 你想了解 OpenClaw 中"上下文"的含义
- 你在调试为什么模型"知道"某些内容(或忘记了)
- 你想减少上下文开销(/context、/status、/compact
summary: 上下文:模型看到的内容、如何构建以及如何检查
title: 上下文
x-i18n:
generated_at: "2026-02-03T07:46:15Z"
model: claude-opus-4-5
provider: pi
source_hash: b32867b9b93254fdd1077d0d97c203cabfdba3330bb941693c83feba8e5db0cc
source_path: concepts/context.md
workflow: 15
---
# 上下文
"上下文"是 **OpenClaw 在一次运行中发送给模型的所有内容**。它受模型的**上下文窗口**token 限制)约束。
新手心智模型:
- **系统提示词**OpenClaw 构建规则、工具、Skills 列表、时间/运行时,以及注入的工作区文件。
- **对话历史**:你的消息 + 助手在此会话中的消息。
- **工具调用/结果 + 附件**:命令输出、文件读取、图片/音频等。
上下文与"记忆"_不是同一回事_记忆可以存储在磁盘上并稍后重新加载上下文是模型当前窗口内的内容。
## 快速开始(检查上下文)
- `/status` → 快速查看"我的窗口有多满?" + 会话设置。
- `/context list` → 注入了什么 + 大致大小(每个文件 + 总计)。
- `/context detail` → 更深入的分解:每个文件、每个工具 schema 大小、每个 Skills 条目大小和系统提示词大小。
- `/usage tokens` → 在正常回复后附加每次回复的使用量页脚。
- `/compact` → 将较旧的历史总结为紧凑条目以释放窗口空间。
另请参阅:[斜杠命令](/tools/slash-commands)、[Token 使用与成本](/reference/token-use)、[压缩](/concepts/compaction)。
## 示例输出
数值因模型、提供商、工具策略和工作区内容而异。
### `/context list`
```
🧠 Context breakdown
Workspace: <workspaceDir>
Bootstrap max/file: 20,000 chars
Sandbox: mode=non-main sandboxed=false
System prompt (run): 38,412 chars (~9,603 tok) (Project Context 23,901 chars (~5,976 tok))
Injected workspace files:
- AGENTS.md: OK | raw 1,742 chars (~436 tok) | injected 1,742 chars (~436 tok)
- SOUL.md: OK | raw 912 chars (~228 tok) | injected 912 chars (~228 tok)
- TOOLS.md: TRUNCATED | raw 54,210 chars (~13,553 tok) | injected 20,962 chars (~5,241 tok)
- IDENTITY.md: OK | raw 211 chars (~53 tok) | injected 211 chars (~53 tok)
- USER.md: OK | raw 388 chars (~97 tok) | injected 388 chars (~97 tok)
- HEARTBEAT.md: MISSING | raw 0 | injected 0
- BOOTSTRAP.md: OK | raw 0 chars (~0 tok) | injected 0 chars (~0 tok)
Skills list (system prompt text): 2,184 chars (~546 tok) (12 skills)
Tools: read, edit, write, exec, process, browser, message, sessions_send, …
Tool list (system prompt text): 1,032 chars (~258 tok)
Tool schemas (JSON): 31,988 chars (~7,997 tok) (counts toward context; not shown as text)
Tools: (same as above)
Session tokens (cached): 14,250 total / ctx=32,000
```
### `/context detail`
```
🧠 Context breakdown (detailed)
Top skills (prompt entry size):
- frontend-design: 412 chars (~103 tok)
- oracle: 401 chars (~101 tok)
… (+10 more skills)
Top tools (schema size):
- browser: 9,812 chars (~2,453 tok)
- exec: 6,240 chars (~1,560 tok)
… (+N more tools)
```
## 什么计入上下文窗口
模型接收的所有内容都计入,包括:
- 系统提示词(所有部分)。
- 对话历史。
- 工具调用 + 工具结果。
- 附件/转录(图片/音频/文件)。
- 压缩摘要和修剪产物。
- 提供商"包装器"或隐藏头部(不可见,仍然计数)。
## OpenClaw 如何构建系统提示词
系统提示词由 **OpenClaw 拥有**,每次运行时重建。它包括:
- 工具列表 + 简短描述。
- Skills 列表(仅元数据;见下文)。
- 工作区位置。
- 时间UTC + 如果配置了则转换为用户时间)。
- 运行时元数据(主机/操作系统/模型/思考)。
- 在**项目上下文**下注入的工作区引导文件。
完整分解:[系统提示词](/concepts/system-prompt)。
## 注入的工作区文件(项目上下文)
默认情况下OpenClaw 注入一组固定的工作区文件(如果存在):
- `AGENTS.md`
- `SOUL.md`
- `TOOLS.md`
- `IDENTITY.md`
- `USER.md`
- `HEARTBEAT.md`
- `BOOTSTRAP.md`(仅首次运行)
大文件按文件使用 `agents.defaults.bootstrapMaxChars`(默认 `20000` 字符)截断。`/context` 显示**原始 vs 注入**大小以及是否发生了截断。
## Skills注入的内容 vs 按需加载的内容
系统提示词包含一个紧凑的 **Skills 列表**(名称 + 描述 + 位置)。此列表有实际开销。
Skill 指令默认*不*包含。模型应该**仅在需要时**`read` Skill 的 `SKILL.md`
## 工具:有两种成本
工具以两种方式影响上下文:
1. 系统提示词中的**工具列表文本**(你看到的"Tooling")。
2. **工具 schema**JSON。这些发送给模型以便它可以调用工具。它们计入上下文即使你看不到它们作为纯文本。
`/context detail` 分解最大的工具 schema以便你可以看到什么占主导。
## 命令、指令和"内联快捷方式"
斜杠命令由 Gateway 网关处理。有几种不同的行为:
- **独立命令**:仅为 `/...` 的消息作为命令运行。
- **指令**`/think``/verbose``/reasoning``/elevated``/model``/queue` 在模型看到消息之前被剥离。
- 仅指令消息会持久化会话设置。
- 正常消息中的内联指令作为每条消息的提示。
- **内联快捷方式**(仅允许列表中的发送者):正常消息中的某些 `/...` token 可以立即运行(例如:"hey /status"),并在模型看到剩余文本之前被剥离。
详情:[斜杠命令](/tools/slash-commands)。
## 会话、压缩和修剪(什么会持久化)
什么在消息之间持久化取决于机制:
- **正常历史**在会话记录中持久化,直到被策略压缩/修剪。
- **压缩**将摘要持久化到记录中,并保持最近的消息不变。
- **修剪**从运行的*内存中*提示词中删除旧的工具结果,但不重写记录。
文档:[会话](/concepts/session)、[压缩](/concepts/compaction)、[会话修剪](/concepts/session-pruning)。
## `/context` 实际报告什么
`/context` 在可用时优先使用最新的**运行构建的**系统提示词报告:
- `System prompt (run)` = 从最后一次嵌入式(具有工具能力的)运行中捕获,并持久化在会话存储中。
- `System prompt (estimate)` = 当没有运行报告存在时(或通过不生成报告的 CLI 后端运行时)即时计算。
无论哪种方式,它都报告大小和主要贡献者;它**不会**转储完整的系统提示词或工具 schema。

View File

@@ -0,0 +1,129 @@
---
read_when:
- 你正在更改向模型或用户展示时间戳的方式
- 你正在调试消息或系统提示词输出中的时间格式问题
summary: 信封、提示词、工具和连接器中的日期与时间处理
title: 日期与时间
x-i18n:
generated_at: "2026-02-01T20:24:52Z"
model: claude-opus-4-5
provider: pi
source_hash: 753af5946a006215d6af2467fa478f3abb42b1dff027cf85d5dc4c7ba4b58d39
source_path: date-time.md
workflow: 14
---
# 日期与时间
OpenClaw 默认使用**主机本地时间作为传输时间戳**,并且**仅在系统提示词中使用用户时区**。
提供商时间戳会被保留,因此工具保持其原生语义(当前时间可通过 `session_status` 获取)。
## 消息信封(默认为本地时间)
入站消息会附带一个时间戳(分钟精度):
```
[Provider ... 2026-01-05 16:26 PST] message text
```
此信封时间戳**默认为主机本地时间**,与提供商时区无关。
你可以覆盖此行为:
```json5
{
agents: {
defaults: {
envelopeTimezone: "local", // "utc" | "local" | "user" | IANA 时区
envelopeTimestamp: "on", // "on" | "off"
envelopeElapsed: "on", // "on" | "off"
},
},
}
```
- `envelopeTimezone: "utc"` 使用 UTC。
- `envelopeTimezone: "local"` 使用主机时区。
- `envelopeTimezone: "user"` 使用 `agents.defaults.userTimezone`(回退到主机时区)。
- 使用显式 IANA 时区(例如 `"America/Chicago"`)指定固定时区。
- `envelopeTimestamp: "off"` 从信封头中移除绝对时间戳。
- `envelopeElapsed: "off"` 移除已用时间后缀(`+2m` 样式)。
### 示例
**本地时间(默认):**
```
[WhatsApp +1555 2026-01-18 00:19 PST] hello
```
**用户时区:**
```
[WhatsApp +1555 2026-01-18 00:19 CST] hello
```
**启用已用时间:**
```
[WhatsApp +1555 +30s 2026-01-18T05:19Z] follow-up
```
## 系统提示词:当前日期与时间
如果已知用户时区,系统提示词会包含一个专门的**当前日期与时间**部分,其中仅包含**时区**(不含时钟/时间格式),以保持提示词缓存的稳定性:
```
Time zone: America/Chicago
```
当智能体需要获取当前时间时,请使用 `session_status` 工具;状态卡中包含时间戳行。
## 系统事件行(默认为本地时间)
插入到智能体上下文中的排队系统事件会带有时间戳前缀,使用与消息信封相同的时区选择(默认:主机本地时间)。
```
System: [2026-01-12 12:19:17 PST] Model switched.
```
### 配置用户时区和格式
```json5
{
agents: {
defaults: {
userTimezone: "America/Chicago",
timeFormat: "auto", // auto | 12 | 24
},
},
}
```
- `userTimezone` 设置提示词上下文中的**用户本地时区**。
- `timeFormat` 控制提示词中的 **12 小时/24 小时显示格式**`auto` 跟随操作系统偏好设置。
## 时间格式检测auto
`timeFormat: "auto"`OpenClaw 会检查操作系统偏好设置macOS/Windows并回退到区域格式。检测到的值会**按进程缓存**,以避免重复的系统调用。
## 工具载荷 + 连接器(原始提供商时间 + 标准化字段)
渠道工具返回**提供商原生时间戳**,并添加标准化字段以保持一致性:
- `timestampMs`纪元毫秒数UTC
- `timestampUtc`ISO 8601 UTC 字符串
原始提供商字段会被保留,不会丢失任何数据。
- Slack来自 API 的类纪元字符串
- DiscordUTC ISO 时间戳
- Telegram/WhatsApp提供商特定的数字/ISO 时间戳
如果需要本地时间,请使用已知时区在下游进行转换。
## 相关文档
- [系统提示词](/concepts/system-prompt)
- [时区](/concepts/timezone)
- [消息](/concepts/messages)

View File

@@ -0,0 +1,59 @@
---
read_when:
- 你想了解 OpenClaw 支持的完整功能列表
summary: OpenClaw 在渠道、路由、媒体和用户体验方面的功能。
title: 功能
x-i18n:
generated_at: "2026-02-04T17:53:22Z"
model: claude-opus-4-5
provider: pi
source_hash: 1b6aee0bfda751824cb6b3a99080b4c80c00ffb355a96f9cff1b596d55d15ed4
source_path: concepts/features.md
workflow: 15
---
## 亮点
<Columns>
<Card title="渠道" icon="message-square">
通过单个 Gateway 网关支持 WhatsApp、Telegram、Discord 和 iMessage。
</Card>
<Card title="插件" icon="plug">
通过扩展添加 Mattermost 等更多平台。
</Card>
<Card title="路由" icon="route">
多智能体路由,支持隔离会话。
</Card>
<Card title="媒体" icon="image">
支持图片、音频和文档的收发。
</Card>
<Card title="应用与界面" icon="monitor">
Web 控制界面和 macOS 配套应用。
</Card>
<Card title="移动节点" icon="smartphone">
iOS 和 Android 节点,支持 Canvas。
</Card>
</Columns>
## 完整列表
- 通过 WhatsApp WebBaileys集成 WhatsApp
- Telegram 机器人支持grammY
- Discord 机器人支持channels.discord.js
- Mattermost 机器人支持(插件)
- 通过本地 imsg CLI 集成 iMessagemacOS
- Pi 的智能体桥接,支持 RPC 模式和工具流式传输
- 长响应的流式传输和分块处理
- 多智能体路由,按工作区或发送者隔离会话
- 通过 OAuth 进行 Anthropic 和 OpenAI 的订阅认证
- 会话:私信合并为共享的 `main`;群组相互隔离
- 群聊支持,通过提及激活
- 图片、音频和文档的媒体支持
- 可选的语音消息转录钩子
- WebChat 和 macOS 菜单栏应用
- iOS 节点,支持配对和 Canvas 界面
- Android 节点支持配对、Canvas、聊天和相机
<Note>
旧版 Claude、Codex、Gemini 和 Opencode 路径已被移除。Pi 是唯一的编程智能体路径。
</Note>

View File

@@ -0,0 +1,117 @@
---
read_when:
- 你正在更改出站渠道的 Markdown 格式化或分块逻辑
- 你正在添加新的渠道格式化器或样式映射
- 你正在调试跨渠道的格式化回归问题
summary: 出站渠道的 Markdown 格式化管道
title: Markdown 格式化
x-i18n:
generated_at: "2026-02-01T20:22:42Z"
model: claude-opus-4-5
provider: pi
source_hash: f9cbf9b744f9a218860730f29435bcad02d3db80b1847fed5f17c063c97d4820
source_path: concepts/markdown-formatting.md
workflow: 14
---
# Markdown 格式化
OpenClaw 通过将出站 Markdown 转换为共享的中间表示IR然后再渲染为特定渠道的输出来进行格式化。IR 保留源文本不变,同时携带样式/链接跨度信息,使分块和渲染在各渠道间保持一致。
## 目标
- **一致性:**一次解析,多个渲染器。
- **安全分块:**在渲染前拆分文本,确保行内格式不会跨块断裂。
- **渠道适配:**将同一 IR 映射到 Slack mrkdwn、Telegram HTML 和 Signal 样式范围,无需重新解析 Markdown。
## 管道
1. **解析 Markdown -> IR**
- IR 是纯文本加上样式跨度(粗体/斜体/删除线/代码/剧透)和链接跨度。
- 偏移量使用 UTF-16 代码单元,以便 Signal 样式范围与其 API 对齐。
- 仅当渠道启用了表格转换时才会解析表格。
2. **分块 IR格式优先**
- 分块在渲染前对 IR 文本进行操作。
- 行内格式不会跨块拆分;跨度按块进行切片。
3. **按渠道渲染**
- **Slack** mrkdwn 标记(粗体/斜体/删除线/代码),链接格式为 `<url|label>`
- **Telegram** HTML 标签(`<b>``<i>``<s>``<code>``<pre><code>``<a href>`)。
- **Signal** 纯文本 + `text-style` 范围;当标签与 URL 不同时,链接变为 `label (url)`
## IR 示例
输入 Markdown
```markdown
Hello **world** — see [docs](https://docs.openclaw.ai).
```
IR示意
```json
{
"text": "Hello world — see docs.",
"styles": [{ "start": 6, "end": 11, "style": "bold" }],
"links": [{ "start": 19, "end": 23, "href": "https://docs.openclaw.ai" }]
}
```
## 使用场景
- Slack、Telegram 和 Signal 的出站适配器从 IR 进行渲染。
- 其他渠道WhatsApp、iMessage、Microsoft Teams、Discord仍使用纯文本或各自的格式化规则启用时会在分块前应用 Markdown 表格转换。
## 表格处理
Markdown 表格在各聊天客户端中的支持并不一致。使用 `markdown.tables` 按渠道(和按账户)控制转换方式。
- `code`:将表格渲染为代码块(大多数渠道的默认设置)。
- `bullets`将每行转换为项目符号列表Signal + WhatsApp 的默认设置)。
- `off`:禁用表格解析和转换;原始表格文本直接透传。
配置键:
```yaml
channels:
discord:
markdown:
tables: code
accounts:
work:
markdown:
tables: off
```
## 分块规则
- 分块限制来自渠道适配器/配置,应用于 IR 文本。
- 代码围栏作为单个块保留,并带有尾部换行符,以确保渠道正确渲染。
- 列表前缀和引用块前缀是 IR 文本的一部分,因此分块不会在前缀中间拆分。
- 行内样式(粗体/斜体/删除线/行内代码/剧透)不会跨块拆分;渲染器会在每个块内重新打开样式。
如需了解更多跨渠道的分块行为,请参见[流式传输 + 分块](/concepts/streaming)。
## 链接策略
- **Slack** `[label](url)` -> `<url|label>`;裸 URL 保持原样。解析时禁用自动链接以避免重复链接。
- **Telegram** `[label](url)` -> `<a href="url">label</a>`HTML 解析模式)。
- **Signal** `[label](url)` -> `label (url)`,除非标签与 URL 匹配。
## 剧透
剧透标记(`||spoiler||`)仅为 Signal 解析,映射为 SPOILER 样式范围。其他渠道将其视为纯文本。
## 如何添加或更新渠道格式化器
1. **解析一次:**使用共享的 `markdownToIR(...)` 辅助函数,传入适合渠道的选项(自动链接、标题样式、引用块前缀)。
2. **渲染:**使用 `renderMarkdownWithMarkers(...)` 和样式标记映射(或 Signal 样式范围)实现渲染器。
3. **分块:**在渲染前调用 `chunkMarkdownIR(...)`;逐块渲染。
4. **接入适配器:**更新渠道出站适配器以使用新的分块器和渲染器。
5. **测试:**添加或更新格式测试,如果渠道使用分块,还需添加出站投递测试。
## 常见陷阱
- Slack 尖括号标记(`<@U123>``<#C123>``<https://...>`)必须保留;安全转义原始 HTML。
- Telegram HTML 要求对标签外的文本进行转义,以避免标记损坏。
- Signal 样式范围依赖 UTF-16 偏移量;不要使用码点偏移量。
- 保留代码围栏的尾部换行符,以确保闭合标记独占一行。

412
content/concepts/memory.md Normal file
View File

@@ -0,0 +1,412 @@
---
read_when:
- 你想了解记忆文件布局和工作流程
- 你想调整自动压缩前的记忆刷新
summary: OpenClaw 记忆的工作原理(工作空间文件 + 自动记忆刷新)
title: 记忆
x-i18n:
generated_at: "2026-02-03T07:47:38Z"
model: claude-opus-4-5
provider: pi
source_hash: f3a7f5d9f61f9742eb3a8adbc3ccaddeadb7e48ceccdfb595327d6d1f55cd00e
source_path: concepts/memory.md
workflow: 15
---
# 记忆
OpenClaw 记忆是**智能体工作空间中的纯 Markdown 文件**。这些文件是唯一的事实来源;模型只"记住"写入磁盘的内容。
记忆搜索工具由活动的记忆插件提供(默认:`memory-core`)。使用 `plugins.slots.memory = "none"` 禁用记忆插件。
## 记忆文件Markdown
默认工作空间布局使用两个记忆层:
- `memory/YYYY-MM-DD.md`
- 每日日志(仅追加)。
- 在会话开始时读取今天和昨天的内容。
- `MEMORY.md`(可选)
- 精心整理的长期记忆。
- **仅在主要的私人会话中加载**(绝不在群组上下文中加载)。
这些文件位于工作空间下(`agents.defaults.workspace`,默认 `~/.openclaw/workspace`)。完整布局参见[智能体工作空间](/concepts/agent-workspace)。
## 何时写入记忆
- 决策、偏好和持久性事实写入 `MEMORY.md`
- 日常笔记和运行上下文写入 `memory/YYYY-MM-DD.md`
- 如果有人说"记住这个",就写下来(不要只保存在内存中)。
- 这个领域仍在发展中。提醒模型存储记忆会有帮助;它会知道该怎么做。
- 如果你想让某些内容持久保存,**请要求机器人将其写入**记忆。
## 自动记忆刷新(压缩前触发)
当会话**接近自动压缩**时OpenClaw 会触发一个**静默的智能体回合**,提醒模型在上下文被压缩**之前**写入持久记忆。默认提示明确说明模型*可以回复*,但通常 `NO_REPLY` 是正确的响应,因此用户永远不会看到这个回合。
这由 `agents.defaults.compaction.memoryFlush` 控制:
```json5
{
agents: {
defaults: {
compaction: {
reserveTokensFloor: 20000,
memoryFlush: {
enabled: true,
softThresholdTokens: 4000,
systemPrompt: "Session nearing compaction. Store durable memories now.",
prompt: "Write any lasting notes to memory/YYYY-MM-DD.md; reply with NO_REPLY if nothing to store.",
},
},
},
},
}
```
详情:
- **软阈值**:当会话 token 估计超过 `contextWindow - reserveTokensFloor - softThresholdTokens` 时触发刷新。
- 默认**静默**:提示包含 `NO_REPLY`,因此不会发送任何内容。
- **两个提示**:一个用户提示加一个系统提示附加提醒。
- **每个压缩周期刷新一次**(在 `sessions.json` 中跟踪)。
- **工作空间必须可写**:如果会话以 `workspaceAccess: "ro"``"none"` 在沙箱中运行,则跳过刷新。
完整的压缩生命周期参见[会话管理 + 压缩](/reference/session-management-compaction)。
## 向量记忆搜索
OpenClaw 可以在 `MEMORY.md``memory/*.md`(以及你选择加入的任何额外目录或文件)上构建小型向量索引,以便语义查询可以找到相关笔记,即使措辞不同。
默认值:
- 默认启用。
- 监视记忆文件的更改(去抖动)。
- 默认使用远程嵌入。如果未设置 `memorySearch.provider`OpenClaw 自动选择:
1. 如果配置了 `memorySearch.local.modelPath` 且文件存在,则使用 `local`
2. 如果可以解析 OpenAI 密钥,则使用 `openai`
3. 如果可以解析 Gemini 密钥,则使用 `gemini`
4. 否则记忆搜索保持禁用状态直到配置完成。
- 本地模式使用 node-llama-cpp可能需要运行 `pnpm approve-builds`
- 使用 sqlite-vec如果可用在 SQLite 中加速向量搜索。
远程嵌入**需要**嵌入提供商的 API 密钥。OpenClaw 从身份验证配置文件、`models.providers.*.apiKey` 或环境变量解析密钥。Codex OAuth 仅涵盖聊天/补全,**不**满足记忆搜索的嵌入需求。对于 Gemini使用 `GEMINI_API_KEY``models.providers.google.apiKey`。使用自定义 OpenAI 兼容端点时,设置 `memorySearch.remote.apiKey`(以及可选的 `memorySearch.remote.headers`)。
### 额外记忆路径
如果你想索引默认工作空间布局之外的 Markdown 文件,添加显式路径:
```json5
agents: {
defaults: {
memorySearch: {
extraPaths: ["../team-docs", "/srv/shared-notes/overview.md"]
}
}
}
```
说明:
- 路径可以是绝对路径或工作空间相对路径。
- 目录会递归扫描 `.md` 文件。
- 仅索引 Markdown 文件。
- 符号链接被忽略(文件或目录)。
### Gemini 嵌入(原生)
将提供商设置为 `gemini` 以直接使用 Gemini 嵌入 API
```json5
agents: {
defaults: {
memorySearch: {
provider: "gemini",
model: "gemini-embedding-001",
remote: {
apiKey: "YOUR_GEMINI_API_KEY"
}
}
}
}
```
说明:
- `remote.baseUrl` 是可选的(默认为 Gemini API 基础 URL
- `remote.headers` 让你可以在需要时添加额外的标头。
- 默认模型:`gemini-embedding-001`
如果你想使用**自定义 OpenAI 兼容端点**OpenRouter、vLLM 或代理),可以使用 `remote` 配置与 OpenAI 提供商:
```json5
agents: {
defaults: {
memorySearch: {
provider: "openai",
model: "text-embedding-3-small",
remote: {
baseUrl: "https://api.example.com/v1/",
apiKey: "YOUR_OPENAI_COMPAT_API_KEY",
headers: { "X-Custom-Header": "value" }
}
}
}
}
```
如果你不想设置 API 密钥,使用 `memorySearch.provider = "local"` 或设置 `memorySearch.fallback = "none"`
回退:
- `memorySearch.fallback` 可以是 `openai``gemini``local``none`
- 回退提供商仅在主嵌入提供商失败时使用。
批量索引OpenAI + Gemini
- OpenAI 和 Gemini 嵌入默认启用。设置 `agents.defaults.memorySearch.remote.batch.enabled = false` 以禁用。
- 默认行为等待批处理完成;如果需要可以调整 `remote.batch.wait``remote.batch.pollIntervalMs``remote.batch.timeoutMinutes`
- 设置 `remote.batch.concurrency` 以控制我们并行提交多少个批处理作业默认2
- 批处理模式在 `memorySearch.provider = "openai"``"gemini"` 时适用,并使用相应的 API 密钥。
- Gemini 批处理作业使用异步嵌入批处理端点,需要 Gemini Batch API 可用。
为什么 OpenAI 批处理快速又便宜:
- 对于大型回填OpenAI 通常是我们支持的最快选项,因为我们可以在单个批处理作业中提交许多嵌入请求,让 OpenAI 异步处理它们。
- OpenAI 为 Batch API 工作负载提供折扣定价,因此大型索引运行通常比同步发送相同请求更便宜。
- 详情参见 OpenAI Batch API 文档和定价:
- https://platform.openai.com/docs/api-reference/batch
- https://platform.openai.com/pricing
配置示例:
```json5
agents: {
defaults: {
memorySearch: {
provider: "openai",
model: "text-embedding-3-small",
fallback: "openai",
remote: {
batch: { enabled: true, concurrency: 2 }
},
sync: { watch: true }
}
}
}
```
工具:
- `memory_search` — 返回带有文件 + 行范围的片段。
- `memory_get` — 按路径读取记忆文件内容。
本地模式:
- 设置 `agents.defaults.memorySearch.provider = "local"`
- 提供 `agents.defaults.memorySearch.local.modelPath`GGUF 或 `hf:` URI
- 可选:设置 `agents.defaults.memorySearch.fallback = "none"` 以避免远程回退。
### 记忆工具的工作原理
- `memory_search``MEMORY.md` + `memory/**/*.md` 语义搜索 Markdown 块(目标约 400 个 token80 个 token 重叠)。它返回片段文本(上限约 700 个字符)、文件路径、行范围、分数、提供商/模型,以及我们是否从本地回退到远程嵌入。不返回完整文件内容。
- `memory_get` 读取特定的记忆 Markdown 文件(工作空间相对路径),可选从起始行开始读取 N 行。`MEMORY.md` / `memory/` 之外的路径仅在明确列在 `memorySearch.extraPaths` 中时才允许。
- 两个工具仅在智能体的 `memorySearch.enabled` 解析为 true 时启用。
### 索引内容(及时机)
- 文件类型:仅 Markdown`MEMORY.md``memory/**/*.md`,以及 `memorySearch.extraPaths` 下的任何 `.md` 文件)。
- 索引存储:每个智能体的 SQLite 位于 `~/.openclaw/memory/<agentId>.sqlite`(可通过 `agents.defaults.memorySearch.store.path` 配置,支持 `{agentId}` 令牌)。
- 新鲜度:监视器监视 `MEMORY.md``memory/``memorySearch.extraPaths`,标记索引为脏(去抖动 1.5 秒)。同步在会话开始时、搜索时或按间隔安排,并异步运行。会话记录使用增量阈值触发后台同步。
- 重新索引触发器:索引存储嵌入的**提供商/模型 + 端点指纹 + 分块参数**。如果其中任何一个发生变化OpenClaw 会自动重置并重新索引整个存储。
### 混合搜索BM25 + 向量)
启用时OpenClaw 结合:
- **向量相似度**(语义匹配,措辞可以不同)
- **BM25 关键词相关性**(精确令牌如 ID、环境变量、代码符号
如果你的平台上全文搜索不可用OpenClaw 会回退到纯向量搜索。
#### 为什么使用混合搜索?
向量搜索擅长"这意味着同一件事"
- "Mac Studio gateway host" vs "运行 gateway 的机器"
- "debounce file updates" vs "避免每次写入都索引"
但它在精确的高信号令牌上可能较弱:
- ID`a828e60``b3b9895a…`
- 代码符号(`memorySearch.query.hybrid`
- 错误字符串("sqlite-vec unavailable"
BM25全文正好相反擅长精确令牌弱于释义。
混合搜索是务实的中间地带:**同时使用两种检索信号**,这样你可以在"自然语言"查询和"大海捞针"查询上都获得好结果。
#### 我们如何合并结果(当前设计)
实现概述:
1. 从双方检索候选池:
- **向量**:按余弦相似度取前 `maxResults * candidateMultiplier` 个。
- **BM25**:按 FTS5 BM25 排名取前 `maxResults * candidateMultiplier` 个(越低越好)。
2. 将 BM25 排名转换为 0..1 范围的分数:
- `textScore = 1 / (1 + max(0, bm25Rank))`
3. 按块 id 合并候选并计算加权分数:
- `finalScore = vectorWeight * vectorScore + textWeight * textScore`
说明:
- 在配置解析中 `vectorWeight` + `textWeight` 归一化为 1.0,因此权重表现为百分比。
- 如果嵌入不可用(或提供商返回零向量),我们仍然运行 BM25 并返回关键词匹配。
- 如果无法创建 FTS5我们保持纯向量搜索不会硬失败
这不是"IR 理论完美"的,但它简单、快速,并且往往能提高真实笔记的召回率/精确率。
如果我们以后想要更复杂的方案常见的下一步是倒数排名融合RRF或在混合之前进行分数归一化最小/最大或 z 分数)。
配置:
```json5
agents: {
defaults: {
memorySearch: {
query: {
hybrid: {
enabled: true,
vectorWeight: 0.7,
textWeight: 0.3,
candidateMultiplier: 4
}
}
}
}
}
```
### 嵌入缓存
OpenClaw 可以在 SQLite 中缓存**块嵌入**,这样重新索引和频繁更新(特别是会话记录)不会重新嵌入未更改的文本。
配置:
```json5
agents: {
defaults: {
memorySearch: {
cache: {
enabled: true,
maxEntries: 50000
}
}
}
}
```
### 会话记忆搜索(实验性)
你可以选择性地索引**会话记录**并通过 `memory_search` 呈现它们。
这由实验性标志控制。
```json5
agents: {
defaults: {
memorySearch: {
experimental: { sessionMemory: true },
sources: ["memory", "sessions"]
}
}
}
```
说明:
- 会话索引是**选择加入**的(默认关闭)。
- 会话更新被去抖动并在超过增量阈值后**异步索引**(尽力而为)。
- `memory_search` 永远不会阻塞索引;在后台同步完成之前,结果可能略有延迟。
- 结果仍然只包含片段;`memory_get` 仍然仅限于记忆文件。
- 会话索引按智能体隔离(仅索引该智能体的会话日志)。
- 会话日志存储在磁盘上(`~/.openclaw/agents/<agentId>/sessions/*.jsonl`)。任何具有文件系统访问权限的进程/用户都可以读取它们,因此将磁盘访问视为信任边界。对于更严格的隔离,在单独的操作系统用户或主机下运行智能体。
增量阈值(显示默认值):
```json5
agents: {
defaults: {
memorySearch: {
sync: {
sessions: {
deltaBytes: 100000, // ~100 KB
deltaMessages: 50 // JSONL 行数
}
}
}
}
}
```
### SQLite 向量加速sqlite-vec
当 sqlite-vec 扩展可用时OpenClaw 将嵌入存储在 SQLite 虚拟表(`vec0`)中,并在数据库中执行向量距离查询。这使搜索保持快速,无需将每个嵌入加载到 JS 中。
配置(可选):
```json5
agents: {
defaults: {
memorySearch: {
store: {
vector: {
enabled: true,
extensionPath: "/path/to/sqlite-vec"
}
}
}
}
}
```
说明:
- `enabled` 默认为 true禁用时搜索回退到对存储嵌入的进程内余弦相似度计算。
- 如果 sqlite-vec 扩展缺失或加载失败OpenClaw 会记录错误并继续使用 JS 回退(无向量表)。
- `extensionPath` 覆盖捆绑的 sqlite-vec 路径(对于自定义构建或非标准安装位置很有用)。
### 本地嵌入自动下载
- 默认本地嵌入模型:`hf:ggml-org/embeddinggemma-300M-GGUF/embeddinggemma-300M-Q8_0.gguf`(约 0.6 GB
-`memorySearch.provider = "local"` 时,`node-llama-cpp` 解析 `modelPath`;如果 GGUF 缺失,它会**自动下载**到缓存(或 `local.modelCacheDir`,如果已设置),然后加载它。下载在重试时会续传。
- 原生构建要求:运行 `pnpm approve-builds`,选择 `node-llama-cpp`,然后运行 `pnpm rebuild node-llama-cpp`
- 回退:如果本地设置失败且 `memorySearch.fallback = "openai"`,我们自动切换到远程嵌入(`openai/text-embedding-3-small`,除非被覆盖)并记录原因。
### 自定义 OpenAI 兼容端点示例
```json5
agents: {
defaults: {
memorySearch: {
provider: "openai",
model: "text-embedding-3-small",
remote: {
baseUrl: "https://api.example.com/v1/",
apiKey: "YOUR_REMOTE_API_KEY",
headers: {
"X-Organization": "org-id",
"X-Project": "project-id"
}
}
}
}
}
```
说明:
- `remote.*` 优先于 `models.providers.openai.*`
- `remote.headers` 与 OpenAI 标头合并;键冲突时 remote 优先。省略 `remote.headers` 以使用 OpenAI 默认值。

View File

@@ -0,0 +1,141 @@
---
read_when:
- 解释入站消息如何转化为回复
- 阐明会话、队列模式或流式传输行为
- 记录推理可见性和使用影响
summary: 消息流程、会话、队列和推理可见性
title: 消息
x-i18n:
generated_at: "2026-02-03T10:05:22Z"
model: claude-opus-4-5
provider: pi
source_hash: 147362b61bee21ee6e303654d970a052325f076ddb45814306053f70409737b5
source_path: concepts/messages.md
workflow: 15
---
# 消息
本页汇总了 OpenClaw 如何处理入站消息、会话、队列、流式传输和推理可见性。
## 消息流程(高层概述)
```
入站消息
-> 路由/绑定 -> 会话密钥
-> 队列(如果有运行中的任务)
-> 智能体运行(流式传输 + 工具)
-> 出站回复(渠道限制 + 分块)
```
关键配置项在配置中:
- `messages.*` 用于前缀、队列和群组行为。
- `agents.defaults.*` 用于分块流式传输和分块默认值。
- 渠道覆盖(`channels.whatsapp.*``channels.telegram.*` 等)用于上限和流式传输开关。
完整 schema 参见[配置](/gateway/configuration)。
## 入站去重
渠道可能在重新连接后重复投递同一消息。OpenClaw 保持一个短期缓存,以渠道/账户/对端/会话/消息 ID 为键,因此重复投递不会触发另一次智能体运行。
## 入站防抖
来自**同一发送者**的快速连续消息可以通过 `messages.inbound` 批量合并为单个智能体轮次。防抖按渠道 + 会话为范围,并使用最近的消息进行回复线程/ID 处理。
配置(全局默认 + 单渠道覆盖):
```json5
{
messages: {
inbound: {
debounceMs: 2000,
byChannel: {
whatsapp: 5000,
slack: 1500,
discord: 1500,
},
},
},
}
```
注意事项:
- 防抖仅适用于**纯文本**消息;媒体/附件会立即刷新。
- 控制命令会绕过防抖,保持独立。
## 会话和设备
会话由 Gateway 网关拥有,而非客户端。
- 直接聊天合并到智能体主会话密钥。
- 群组/渠道获得各自的会话密钥。
- 会话存储和记录保存在 Gateway 网关主机上。
多个设备/渠道可以映射到同一会话,但历史记录不会完全同步回每个客户端。建议:对长对话使用一个主设备,以避免上下文分歧。控制 UI 和 TUI 始终显示 Gateway 网关支持的会话记录,因此它们是事实来源。
详情:[会话管理](/concepts/session)。
## 入站正文和历史上下文
OpenClaw 将**提示正文**与**命令正文**分开:
- `Body`:发送给智能体的提示文本。这可能包括渠道信封和可选的历史包装器。
- `CommandBody`:用于指令/命令解析的原始用户文本。
- `RawBody``CommandBody` 的旧别名(为兼容性保留)。
当渠道提供历史记录时,使用共享包装器:
- `[Chat messages since your last reply - for context]`
- `[Current message - respond to this]`
对于**非直接聊天**(群组/渠道/房间),**当前消息正文**会加上发送者标签前缀(与历史条目使用的样式相同)。这使智能体提示中的实时消息和队列/历史消息保持一致。
历史缓冲区是**仅待处理的**:它们包含*未*触发运行的群组消息(例如,提及门控的消息),并**排除**已在会话记录中的消息。
指令剥离仅适用于**当前消息**部分,因此历史记录保持完整。包装历史记录的渠道应将 `CommandBody`(或 `RawBody`)设置为原始消息文本,并将 `Body` 保留为组合提示。历史缓冲区可通过 `messages.groupChat.historyLimit`(全局默认)和单渠道覆盖(如 `channels.slack.historyLimit``channels.telegram.accounts.<id>.historyLimit`)进行配置(设置 `0` 表示禁用)。
## 队列和后续消息
如果运行已在进行中,入站消息可以排队、导入当前运行,或收集用于后续轮次。
- 通过 `messages.queue`(和 `messages.queue.byChannel`)配置。
- 模式:`interrupt``steer``followup``collect`,以及积压变体。
详情:[队列](/concepts/queue)。
## 流式传输、分块和批处理
分块流式传输在模型生成文本块时发送部分回复。分块遵循渠道文本限制,避免拆分围栏代码。
关键设置:
- `agents.defaults.blockStreamingDefault``on|off`,默认 off
- `agents.defaults.blockStreamingBreak``text_end|message_end`
- `agents.defaults.blockStreamingChunk``minChars|maxChars|breakPreference`
- `agents.defaults.blockStreamingCoalesce`(基于空闲的批处理)
- `agents.defaults.humanDelay`(块回复之间的拟人化暂停)
- 渠道覆盖:`*.blockStreaming``*.blockStreamingCoalesce`(非 Telegram 渠道需要显式设置 `*.blockStreaming: true`
详情:[流式传输 + 分块](/concepts/streaming)。
## 推理可见性和 token
OpenClaw 可以显示或隐藏模型推理:
- `/reasoning on|off|stream` 控制可见性。
- 当模型产生推理内容时,它仍计入 token 使用量。
- Telegram 支持将推理流式传输到草稿气泡中。
详情:[思考 + 推理指令](/tools/thinking)和 [Token 使用](/reference/token-use)。
## 前缀、线程和回复
出站消息格式在 `messages` 中集中配置:
- `messages.responsePrefix`(出站前缀)和 `channels.whatsapp.messagePrefix`WhatsApp 入站前缀)
- 通过 `replyToMode` 和单渠道默认值进行回复线程
详情:[配置](/gateway/configuration#messages)和渠道文档。

View File

@@ -0,0 +1,145 @@
---
read_when:
- 诊断认证配置文件轮换、冷却时间或模型回退行为
- 更新认证配置文件或模型的故障转移规则
summary: OpenClaw 如何轮换认证配置文件并在模型之间进行回退
title: 模型故障转移
x-i18n:
generated_at: "2026-02-03T07:46:17Z"
model: claude-opus-4-5
provider: pi
source_hash: eab7c0633824d941cf0d6ce4294f0bc8747fbba2ce93650e9643eca327cd04a9
source_path: concepts/model-failover.md
workflow: 15
---
# 模型故障转移
OpenClaw 分两个阶段处理故障:
1. 在当前提供商内进行**认证配置文件轮换**。
2. **模型回退**到 `agents.defaults.model.fallbacks` 中的下一个模型。
本文档解释运行时规则及其背后的数据。
## 认证存储(密钥 + OAuth
OpenClaw 对 API 密钥和 OAuth 令牌都使用**认证配置文件**。
- 密钥存储在 `~/.openclaw/agents/<agentId>/agent/auth-profiles.json`(旧版:`~/.openclaw/agent/auth-profiles.json`)。
- 配置 `auth.profiles` / `auth.order` **仅用于元数据和路由**(不含密钥)。
- 旧版仅导入 OAuth 文件:`~/.openclaw/credentials/oauth.json`(首次使用时导入到 `auth-profiles.json`)。
更多详情:[/concepts/oauth](/concepts/oauth)
凭证类型:
- `type: "api_key"``{ provider, key }`
- `type: "oauth"``{ provider, access, refresh, expires, email? }`(某些提供商还有 `projectId`/`enterpriseUrl`
## 配置文件 ID
OAuth 登录创建不同的配置文件,以便多个账户可以共存。
- 默认:当没有电子邮件可用时为 `provider:default`
- 带电子邮件的 OAuth`provider:<email>`(例如 `google-antigravity:user@gmail.com`)。
配置文件存储在 `~/.openclaw/agents/<agentId>/agent/auth-profiles.json``profiles` 下。
## 轮换顺序
当一个提供商有多个配置文件时OpenClaw 按以下顺序选择:
1. **显式配置**`auth.order[provider]`(如果设置)。
2. **已配置的配置文件**:按提供商过滤的 `auth.profiles`
3. **已存储的配置文件**`auth-profiles.json` 中该提供商的条目。
如果没有配置显式顺序OpenClaw 使用轮询顺序:
- **主键:** 配置文件类型(**OAuth 优先于 API 密钥**)。
- **次键:** `usageStats.lastUsed`(每种类型中最旧的优先)。
- **冷却/禁用的配置文件**会移到末尾,按最早过期时间排序。
### 会话粘性(缓存友好)
OpenClaw **为每个会话固定所选的认证配置文件**以保持提供商缓存热度。它**不会**在每个请求时轮换。固定的配置文件会被重用直到:
- 会话被重置(`/new` / `/reset`
- 压缩完成(压缩计数递增)
- 配置文件处于冷却/禁用状态
通过 `/model …@<profileId>` 手动选择会为该会话设置**用户覆盖**,在新会话开始之前不会自动轮换。
自动固定的配置文件(由会话路由器选择)被视为**偏好**:它们会优先尝试,但 OpenClaw 可能在速率限制/超时时轮换到另一个配置文件。用户固定的配置文件会锁定到该配置文件如果失败且配置了模型回退OpenClaw 会移动到下一个模型而不是切换配置文件。
### 为什么 OAuth 可能"看起来丢失"
如果你为同一个提供商同时拥有 OAuth 配置文件和 API 密钥配置文件,除非固定,否则轮询可能在消息之间切换它们。要强制使用单个配置文件:
- 使用 `auth.order[provider] = ["provider:profileId"]` 固定,或
- 通过 `/model …` 使用每会话覆盖并指定配置文件覆盖(当你的 UI/聊天界面支持时)。
## 冷却时间
当配置文件因认证/速率限制错误或看起来像速率限制的超时而失败时OpenClaw 将其标记为冷却状态并移动到下一个配置文件。格式/无效请求错误(例如 Cloud Code Assist 工具调用 ID 验证失败)被视为值得故障转移的情况,使用相同的冷却时间。
冷却时间使用指数退避:
- 1 分钟
- 5 分钟
- 25 分钟
- 1 小时(上限)
状态存储在 `auth-profiles.json``usageStats` 下:
```json
{
"usageStats": {
"provider:profile": {
"lastUsed": 1736160000000,
"cooldownUntil": 1736160600000,
"errorCount": 2
}
}
}
```
## 账单禁用
账单/额度失败(例如"insufficient credits"/"credit balance too low"被视为值得故障转移的情况但它们通常不是暂时性的。OpenClaw 不使用短冷却时间,而是将配置文件标记为**禁用**(使用更长的退避时间)并轮换到下一个配置文件/提供商。
状态存储在 `auth-profiles.json` 中:
```json
{
"usageStats": {
"provider:profile": {
"disabledUntil": 1736178000000,
"disabledReason": "billing"
}
}
}
```
默认值:
- 账单退避从 **5 小时**开始,每次账单失败翻倍,上限为 **24 小时**
- 如果配置文件 **24 小时**内没有失败,退避计数器会重置(可配置)。
## 模型回退
如果某个提供商的所有配置文件都失败OpenClaw 会移动到 `agents.defaults.model.fallbacks` 中的下一个模型。这适用于认证失败、速率限制和耗尽配置文件轮换的超时(其他错误不会推进回退)。
当运行以模型覆盖(钩子或 CLI开始时在尝试任何配置的回退之后回退仍会在 `agents.defaults.model.primary` 处结束。
## 相关配置
参阅 [Gateway 网关配置](/gateway/configuration) 了解:
- `auth.profiles` / `auth.order`
- `auth.cooldowns.billingBackoffHours` / `auth.cooldowns.billingBackoffHoursByProvider`
- `auth.cooldowns.billingMaxHours` / `auth.cooldowns.failureWindowHours`
- `agents.defaults.model.primary` / `agents.defaults.model.fallbacks`
- `agents.defaults.imageModel` 路由
参阅[模型](/concepts/models)了解更广泛的模型选择和回退概述。

View File

@@ -0,0 +1,320 @@
---
read_when:
- 你需要按提供商分类的模型设置参考
- 你需要模型提供商的示例配置或 CLI 新手引导命令
summary: 模型提供商概述,包含示例配置和 CLI 流程
title: 模型提供商
x-i18n:
generated_at: "2026-02-03T07:46:28Z"
model: claude-opus-4-5
provider: pi
source_hash: 14f73e5a9f9b7c6f017d59a54633942dba95a3eb50f8848b836cfe0b9f6d7719
source_path: concepts/model-providers.md
workflow: 15
---
# 模型提供商
本页介绍 **LLM/模型提供商**(不是 WhatsApp/Telegram 等聊天渠道)。
关于模型选择规则,请参阅 [/concepts/models](/concepts/models)。
## 快速规则
- 模型引用使用 `provider/model` 格式(例如:`opencode/claude-opus-4-5`)。
- 如果设置了 `agents.defaults.models`,它将成为允许列表。
- CLI 辅助工具:`openclaw onboard``openclaw models list``openclaw models set <provider/model>`
## 内置提供商pi-ai 目录)
OpenClaw 附带 pi-ai 目录。这些提供商**不需要** `models.providers` 配置;只需设置认证 + 选择模型。
### OpenAI
- 提供商:`openai`
- 认证:`OPENAI_API_KEY`
- 示例模型:`openai/gpt-5.2`
- CLI`openclaw onboard --auth-choice openai-api-key`
```json5
{
agents: { defaults: { model: { primary: "openai/gpt-5.2" } } },
}
```
### Anthropic
- 提供商:`anthropic`
- 认证:`ANTHROPIC_API_KEY``claude setup-token`
- 示例模型:`anthropic/claude-opus-4-5`
- CLI`openclaw onboard --auth-choice token`(粘贴 setup-token`openclaw models auth paste-token --provider anthropic`
```json5
{
agents: { defaults: { model: { primary: "anthropic/claude-opus-4-5" } } },
}
```
### OpenAI Code (Codex)
- 提供商:`openai-codex`
- 认证OAuth (ChatGPT)
- 示例模型:`openai-codex/gpt-5.2`
- CLI`openclaw onboard --auth-choice openai-codex``openclaw models auth login --provider openai-codex`
```json5
{
agents: { defaults: { model: { primary: "openai-codex/gpt-5.2" } } },
}
```
### OpenCode Zen
- 提供商:`opencode`
- 认证:`OPENCODE_API_KEY`(或 `OPENCODE_ZEN_API_KEY`
- 示例模型:`opencode/claude-opus-4-5`
- CLI`openclaw onboard --auth-choice opencode-zen`
```json5
{
agents: { defaults: { model: { primary: "opencode/claude-opus-4-5" } } },
}
```
### Google GeminiAPI 密钥)
- 提供商:`google`
- 认证:`GEMINI_API_KEY`
- 示例模型:`google/gemini-3-pro-preview`
- CLI`openclaw onboard --auth-choice gemini-api-key`
### Google Vertex、Antigravity 和 Gemini CLI
- 提供商:`google-vertex``google-antigravity``google-gemini-cli`
- 认证Vertex 使用 gcloud ADCAntigravity/Gemini CLI 使用各自的认证流程
- Antigravity OAuth 作为捆绑插件提供(`google-antigravity-auth`,默认禁用)。
- 启用:`openclaw plugins enable google-antigravity-auth`
- 登录:`openclaw models auth login --provider google-antigravity --set-default`
- Gemini CLI OAuth 作为捆绑插件提供(`google-gemini-cli-auth`,默认禁用)。
- 启用:`openclaw plugins enable google-gemini-cli-auth`
- 登录:`openclaw models auth login --provider google-gemini-cli --set-default`
- 注意:你**不需要**将客户端 ID 或密钥粘贴到 `openclaw.json` 中。CLI 登录流程将令牌存储在 Gateway 网关主机的认证配置文件中。
### Z.AI (GLM)
- 提供商:`zai`
- 认证:`ZAI_API_KEY`
- 示例模型:`zai/glm-4.7`
- CLI`openclaw onboard --auth-choice zai-api-key`
- 别名:`z.ai/*``z-ai/*` 规范化为 `zai/*`
### Vercel AI Gateway
- 提供商:`vercel-ai-gateway`
- 认证:`AI_GATEWAY_API_KEY`
- 示例模型:`vercel-ai-gateway/anthropic/claude-opus-4.5`
- CLI`openclaw onboard --auth-choice ai-gateway-api-key`
### 其他内置提供商
- OpenRouter`openrouter``OPENROUTER_API_KEY`
- 示例模型:`openrouter/anthropic/claude-sonnet-4-5`
- xAI`xai``XAI_API_KEY`
- Groq`groq``GROQ_API_KEY`
- Cerebras`cerebras``CEREBRAS_API_KEY`
- Cerebras 上的 GLM 模型使用 ID `zai-glm-4.7``zai-glm-4.6`
- OpenAI 兼容的基础 URL`https://api.cerebras.ai/v1`
- Mistral`mistral``MISTRAL_API_KEY`
- GitHub Copilot`github-copilot``COPILOT_GITHUB_TOKEN` / `GH_TOKEN` / `GITHUB_TOKEN`
## 通过 `models.providers` 配置的提供商(自定义/基础 URL
使用 `models.providers`(或 `models.json`)添加**自定义**提供商或 OpenAI/Anthropic 兼容的代理。
### Moonshot AI (Kimi)
Moonshot 使用 OpenAI 兼容端点,因此将其配置为自定义提供商:
- 提供商:`moonshot`
- 认证:`MOONSHOT_API_KEY`
- 示例模型:`moonshot/kimi-k2.5`
Kimi K2 模型 ID
{/_ moonshot-kimi-k2-model-refs:start _/ && null}
- `moonshot/kimi-k2.5`
- `moonshot/kimi-k2-0905-preview`
- `moonshot/kimi-k2-turbo-preview`
- `moonshot/kimi-k2-thinking`
- `moonshot/kimi-k2-thinking-turbo`
{/_ moonshot-kimi-k2-model-refs:end _/ && null}
```json5
{
agents: {
defaults: { model: { primary: "moonshot/kimi-k2.5" } },
},
models: {
mode: "merge",
providers: {
moonshot: {
baseUrl: "https://api.moonshot.ai/v1",
apiKey: "${MOONSHOT_API_KEY}",
api: "openai-completions",
models: [{ id: "kimi-k2.5", name: "Kimi K2.5" }],
},
},
},
}
```
### Kimi Coding
Kimi Coding 使用 Moonshot AI 的 Anthropic 兼容端点:
- 提供商:`kimi-coding`
- 认证:`KIMI_API_KEY`
- 示例模型:`kimi-coding/k2p5`
```json5
{
env: { KIMI_API_KEY: "sk-..." },
agents: {
defaults: { model: { primary: "kimi-coding/k2p5" } },
},
}
```
### Qwen OAuth免费层级
Qwen 通过设备码流程提供对 Qwen Coder + Vision 的 OAuth 访问。
启用捆绑插件,然后登录:
```bash
openclaw plugins enable qwen-portal-auth
openclaw models auth login --provider qwen-portal --set-default
```
模型引用:
- `qwen-portal/coder-model`
- `qwen-portal/vision-model`
参见 [/providers/qwen](/providers/qwen) 了解设置详情和注意事项。
### Synthetic
Synthetic 通过 `synthetic` 提供商提供 Anthropic 兼容模型:
- 提供商:`synthetic`
- 认证:`SYNTHETIC_API_KEY`
- 示例模型:`synthetic/hf:MiniMaxAI/MiniMax-M2.1`
- CLI`openclaw onboard --auth-choice synthetic-api-key`
```json5
{
agents: {
defaults: { model: { primary: "synthetic/hf:MiniMaxAI/MiniMax-M2.1" } },
},
models: {
mode: "merge",
providers: {
synthetic: {
baseUrl: "https://api.synthetic.new/anthropic",
apiKey: "${SYNTHETIC_API_KEY}",
api: "anthropic-messages",
models: [{ id: "hf:MiniMaxAI/MiniMax-M2.1", name: "MiniMax M2.1" }],
},
},
},
}
```
### MiniMax
MiniMax 通过 `models.providers` 配置,因为它使用自定义端点:
- MiniMaxAnthropic 兼容):`--auth-choice minimax-api`
- 认证:`MINIMAX_API_KEY`
参见 [/providers/minimax](/providers/minimax) 了解设置详情、模型选项和配置片段。
### Ollama
Ollama 是提供 OpenAI 兼容 API 的本地 LLM 运行时:
- 提供商:`ollama`
- 认证:无需(本地服务器)
- 示例模型:`ollama/llama3.3`
- 安装https://ollama.ai
```bash
# Install Ollama, then pull a model:
ollama pull llama3.3
```
```json5
{
agents: {
defaults: { model: { primary: "ollama/llama3.3" } },
},
}
```
当 Ollama 在本地 `http://127.0.0.1:11434/v1` 运行时会自动检测。参见 [/providers/ollama](/providers/ollama) 了解模型推荐和自定义配置。
### 本地代理LM Studio、vLLM、LiteLLM 等)
示例OpenAI 兼容):
```json5
{
agents: {
defaults: {
model: { primary: "lmstudio/minimax-m2.1-gs32" },
models: { "lmstudio/minimax-m2.1-gs32": { alias: "Minimax" } },
},
},
models: {
providers: {
lmstudio: {
baseUrl: "http://localhost:1234/v1",
apiKey: "LMSTUDIO_KEY",
api: "openai-completions",
models: [
{
id: "minimax-m2.1-gs32",
name: "MiniMax M2.1",
reasoning: false,
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 200000,
maxTokens: 8192,
},
],
},
},
},
}
```
注意事项:
- 对于自定义提供商,`reasoning``input``cost``contextWindow``maxTokens` 是可选的。
省略时OpenClaw 默认为:
- `reasoning: false`
- `input: ["text"]`
- `cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }`
- `contextWindow: 200000`
- `maxTokens: 8192`
- 建议:设置与你的代理/模型限制匹配的显式值。
## CLI 示例
```bash
openclaw onboard --auth-choice opencode-zen
openclaw models set opencode/claude-opus-4-5
openclaw models list
```
另请参阅:[/gateway/configuration](/gateway/configuration) 了解完整配置示例。

196
content/concepts/models.md Normal file
View File

@@ -0,0 +1,196 @@
---
read_when:
- 添加或修改模型 CLImodels list/set/scan/aliases/fallbacks
- 更改模型回退行为或选择用户体验
- 更新模型扫描探测(工具/图像)
summary: 模型 CLI列表、设置、别名、回退、扫描、状态
title: 模型 CLI
x-i18n:
generated_at: "2026-02-03T10:05:42Z"
model: claude-opus-4-5
provider: pi
source_hash: e8b54bb370b4f63a9b917594fb0f6ff48192e168196d30c713b8bbe72b78fef6
source_path: concepts/models.md
workflow: 15
---
# 模型 CLI
参见 [/concepts/model-failover](/concepts/model-failover) 了解认证配置文件轮换、冷却时间及其与回退的交互。
快速提供商概述 + 示例:[/concepts/model-providers](/concepts/model-providers)。
## 模型选择工作原理
OpenClaw 按以下顺序选择模型:
1. **主要**模型(`agents.defaults.model.primary``agents.defaults.model`)。
2. `agents.defaults.model.fallbacks` 中的**回退**(按顺序)。
3. **提供商认证故障转移**在移动到下一个模型之前在提供商内部发生。
相关:
- `agents.defaults.models` 是 OpenClaw 可使用的模型白名单/目录(加上别名)。
- `agents.defaults.imageModel` **仅在**主要模型无法接受图像时使用。
- 每个智能体的默认值可以通过 `agents.list[].model` 加绑定覆盖 `agents.defaults.model`(参见 [/concepts/multi-agent](/concepts/multi-agent))。
## 快速模型推荐(经验之谈)
- **GLM**:在编程/工具调用方面稍好。
- **MiniMax**:在写作和氛围方面更好。
## 设置向导(推荐)
如果你不想手动编辑配置,请运行新手引导向导:
```bash
openclaw onboard
```
它可以为常见提供商设置模型 + 认证,包括 **OpenAI CodeCodex订阅**OAuth**Anthropic**(推荐使用 API 密钥;也支持 `claude setup-token`)。
## 配置键(概述)
- `agents.defaults.model.primary``agents.defaults.model.fallbacks`
- `agents.defaults.imageModel.primary``agents.defaults.imageModel.fallbacks`
- `agents.defaults.models`(白名单 + 别名 + 提供商参数)
- `models.providers`(写入 `models.json` 的自定义提供商)
模型引用会规范化为小写。提供商别名如 `z.ai/*` 会规范化为 `zai/*`
提供商配置示例(包括 OpenCode Zen在 [/gateway/configuration](/gateway/configuration#opencode-zen-multi-model-proxy)。
## "Model is not allowed"(以及为什么回复停止)
如果设置了 `agents.defaults.models`,它将成为 `/model` 和会话覆盖的**白名单**。当用户选择不在该白名单中的模型时OpenClaw 返回:
```
Model "provider/model" is not allowed. Use /model to list available models.
```
这发生在正常回复生成**之前**,所以消息可能感觉像"没有响应"。修复方法是:
- 将模型添加到 `agents.defaults.models`,或
- 清除白名单(删除 `agents.defaults.models`),或
-`/model list` 中选择一个模型。
白名单配置示例:
```json5
{
agent: {
model: { primary: "anthropic/claude-sonnet-4-5" },
models: {
"anthropic/claude-sonnet-4-5": { alias: "Sonnet" },
"anthropic/claude-opus-4-5": { alias: "Opus" },
},
},
}
```
## 在聊天中切换模型(`/model`
你可以在不重启的情况下切换当前会话的模型:
```
/model
/model list
/model 3
/model openai/gpt-5.2
/model status
```
注意事项:
- `/model`(和 `/model list`)是紧凑的编号选择器(模型系列 + 可用提供商)。
- `/model <#>` 从该选择器中选择。
- `/model status` 是详细视图(认证候选项,以及配置时的提供商端点 `baseUrl` + `api` 模式)。
- 模型引用通过在**第一个** `/` 处分割来解析。输入 `/model <ref>` 时使用 `provider/model`
- 如果模型 ID 本身包含 `/`OpenRouter 风格),你必须包含提供商前缀(例如:`/model openrouter/moonshotai/kimi-k2`)。
- 如果省略提供商OpenClaw 将输入视为别名或**默认提供商**的模型(仅在模型 ID 中没有 `/` 时有效)。
完整命令行为/配置:[斜杠命令](/tools/slash-commands)。
## CLI 命令
```bash
openclaw models list
openclaw models status
openclaw models set <provider/model>
openclaw models set-image <provider/model>
openclaw models aliases list
openclaw models aliases add <alias> <provider/model>
openclaw models aliases remove <alias>
openclaw models fallbacks list
openclaw models fallbacks add <provider/model>
openclaw models fallbacks remove <provider/model>
openclaw models fallbacks clear
openclaw models image-fallbacks list
openclaw models image-fallbacks add <provider/model>
openclaw models image-fallbacks remove <provider/model>
openclaw models image-fallbacks clear
```
`openclaw models`(无子命令)是 `models status` 的快捷方式。
### `models list`
默认显示已配置的模型。有用的标志:
- `--all`:完整目录
- `--local`:仅本地提供商
- `--provider <name>`:按提供商筛选
- `--plain`:每行一个模型
- `--json`:机器可读输出
### `models status`
显示已解析的主要模型、回退、图像模型,以及已配置提供商的认证概述。它还显示认证存储中找到的配置文件的 OAuth 过期状态(默认在 24 小时内警告)。`--plain` 仅打印已解析的主要模型。
OAuth 状态始终显示(并包含在 `--json` 输出中)。如果已配置的提供商没有凭证,`models status` 会打印 **Missing auth** 部分。
JSON 包括 `auth.oauth`(警告窗口 + 配置文件)和 `auth.providers`(每个提供商的有效认证)。
使用 `--check` 进行自动化(缺失/过期时退出 `1`,即将过期时退出 `2`)。
首选的 Anthropic 认证是 Claude Code CLI setup-token在任何地方运行如需要在 Gateway 网关主机上粘贴):
```bash
claude setup-token
openclaw models status
```
## 扫描OpenRouter 免费模型)
`openclaw models scan` 检查 OpenRouter 的**免费模型目录**,并可选择性地探测模型的工具和图像支持。
关键标志:
- `--no-probe`:跳过实时探测(仅元数据)
- `--min-params <b>`:最小参数量(十亿)
- `--max-age-days <days>`:跳过较旧的模型
- `--provider <name>`:提供商前缀筛选
- `--max-candidates <n>`:回退列表大小
- `--set-default`:将 `agents.defaults.model.primary` 设置为第一个选择
- `--set-image`:将 `agents.defaults.imageModel.primary` 设置为第一个图像选择
探测需要 OpenRouter API 密钥(来自认证配置文件或 `OPENROUTER_API_KEY`)。没有密钥时,使用 `--no-probe` 仅列出候选项。
扫描结果按以下顺序排名:
1. 图像支持
2. 工具延迟
3. 上下文大小
4. 参数数量
输入
- OpenRouter `/models` 列表(筛选 `:free`
- 需要来自认证配置文件或 `OPENROUTER_API_KEY` 的 OpenRouter API 密钥(参见 [/environment](/help/environment)
- 可选筛选器:`--max-age-days``--min-params``--provider``--max-candidates`
- 探测控制:`--timeout``--concurrency`
在 TTY 中运行时,你可以交互式选择回退。在非交互模式下,传递 `--yes` 接受默认值。
## 模型注册表(`models.json`
`models.providers` 中的自定义提供商会写入智能体目录下的 `models.json`(默认 `~/.openclaw/agents/<agentId>/models.json`)。除非 `models.mode` 设置为 `replace`,否则此文件默认会被合并。

View File

@@ -0,0 +1,372 @@
---
read_when: You want multiple isolated agents (workspaces + auth) in one gateway process.
status: active
summary: 多智能体路由:隔离的智能体、渠道账户和绑定
title: 多智能体路由
x-i18n:
generated_at: "2026-02-03T07:47:38Z"
model: claude-opus-4-5
provider: pi
source_hash: 1848266c632cd6c96ff99ea9eb9c17bbfe6d35fa1f90450853083e7c548d5324
source_path: concepts/multi-agent.md
workflow: 15
---
# 多智能体路由
目标:多个*隔离的*智能体(独立的工作区 + `agentDir` + 会话),加上多个渠道账户(例如两个 WhatsApp在一个运行的 Gateway 网关中。入站消息通过绑定路由到智能体。
## 什么是"一个智能体"
一个**智能体**是一个完全独立作用域的大脑,拥有自己的:
- **工作区**文件、AGENTS.md/SOUL.md/USER.md、本地笔记、人设规则
- **状态目录**`agentDir`)用于认证配置文件、模型注册表和每智能体配置。
- **会话存储**(聊天历史 + 路由状态)位于 `~/.openclaw/agents/<agentId>/sessions` 下。
认证配置文件是**每智能体独立的**。每个智能体从自己的位置读取:
```
~/.openclaw/agents/<agentId>/agent/auth-profiles.json
```
主智能体凭证**不会**自动共享。切勿在智能体之间重用 `agentDir`(这会导致认证/会话冲突)。如果你想共享凭证,请将 `auth-profiles.json` 复制到另一个智能体的 `agentDir`
Skills 通过每个工作区的 `skills/` 文件夹实现每智能体独立,共享的 Skills 可从 `~/.openclaw/skills` 获取。参见 [Skills每智能体 vs 共享](/tools/skills#per-agent-vs-shared-skills)。
Gateway 网关可以托管**一个智能体**(默认)或**多个智能体**并行。
**工作区注意事项:** 每个智能体的工作区是**默认 cwd**,而不是硬性沙箱。相对路径在工作区内解析,但绝对路径可以访问主机的其他位置,除非启用了沙箱隔离。参见 [沙箱隔离](/gateway/sandboxing)。
## 路径(快速映射)
- 配置:`~/.openclaw/openclaw.json`(或 `OPENCLAW_CONFIG_PATH`
- 状态目录:`~/.openclaw`(或 `OPENCLAW_STATE_DIR`
- 工作区:`~/.openclaw/workspace`(或 `~/.openclaw/workspace-<agentId>`
- 智能体目录:`~/.openclaw/agents/<agentId>/agent`(或 `agents.list[].agentDir`
- 会话:`~/.openclaw/agents/<agentId>/sessions`
### 单智能体模式(默认)
如果你什么都不做OpenClaw 运行单个智能体:
- `agentId` 默认为 **`main`**。
- 会话键为 `agent:main:<mainKey>`
- 工作区默认为 `~/.openclaw/workspace`(或当设置了 `OPENCLAW_PROFILE` 时为 `~/.openclaw/workspace-<profile>`)。
- 状态默认为 `~/.openclaw/agents/main/agent`
## 智能体助手
使用智能体向导添加新的隔离智能体:
```bash
openclaw agents add work
```
然后添加 `bindings`(或让向导完成)来路由入站消息。
验证:
```bash
openclaw agents list --bindings
```
## 多个智能体 = 多个人、多种人格
使用**多个智能体**,每个 `agentId` 成为一个**完全隔离的人格**
- **不同的电话号码/账户**(每渠道 `accountId`)。
- **不同的人格**(每智能体工作区文件如 `AGENTS.md``SOUL.md`)。
- **独立的认证 + 会话**(除非明确启用,否则无交叉通信)。
这让**多个人**共享一个 Gateway 网关服务器,同时保持他们的 AI"大脑"和数据隔离。
## 一个 WhatsApp 号码,多个人(私信分割)
你可以将**不同的 WhatsApp 私信**路由到不同的智能体,同时保持**一个 WhatsApp 账户**。使用 `peer.kind: "dm"` 匹配发送者 E.164(如 `+15551234567`)。回复仍然来自同一个 WhatsApp 号码(无每智能体发送者身份)。
重要细节:直接聊天折叠到智能体的**主会话键**,因此真正的隔离需要**每人一个智能体**。
示例:
```json5
{
agents: {
list: [
{ id: "alex", workspace: "~/.openclaw/workspace-alex" },
{ id: "mia", workspace: "~/.openclaw/workspace-mia" },
],
},
bindings: [
{ agentId: "alex", match: { channel: "whatsapp", peer: { kind: "dm", id: "+15551230001" } } },
{ agentId: "mia", match: { channel: "whatsapp", peer: { kind: "dm", id: "+15551230002" } } },
],
channels: {
whatsapp: {
dmPolicy: "allowlist",
allowFrom: ["+15551230001", "+15551230002"],
},
},
}
```
注意事项:
- 私信访问控制是**每 WhatsApp 账户全局的**(配对/允许列表),而不是每智能体。
- 对于共享群组,将群组绑定到一个智能体或使用 [广播群组](/channels/broadcast-groups)。
## 路由规则(消息如何选择智能体)
绑定是**确定性的****最具体的优先**
1. `peer` 匹配(精确私信/群组/频道 id
2. `guildId`Discord
3. `teamId`Slack
4. 渠道的 `accountId` 匹配
5. 渠道级匹配(`accountId: "*"`
6. 回退到默认智能体(`agents.list[].default`,否则列表中的第一个条目,默认:`main`
## 多账户/电话号码
支持**多账户**的渠道(如 WhatsApp使用 `accountId` 来识别每个登录。每个 `accountId` 可以路由到不同的智能体,因此一个服务器可以托管多个电话号码而不混合会话。
## 概念
- `agentId`:一个"大脑"(工作区、每智能体认证、每智能体会话存储)。
- `accountId`:一个渠道账户实例(例如 WhatsApp 账户 `"personal"` vs `"biz"`)。
- `binding`:通过 `(channel, accountId, peer)` 以及可选的 guild/team id 将入站消息路由到 `agentId`
- 直接聊天折叠到 `agent:<agentId>:<mainKey>`(每智能体"主"`session.mainKey`)。
## 示例:两个 WhatsApp → 两个智能体
`~/.openclaw/openclaw.json`JSON5
```js
{
agents: {
list: [
{
id: "home",
default: true,
name: "Home",
workspace: "~/.openclaw/workspace-home",
agentDir: "~/.openclaw/agents/home/agent",
},
{
id: "work",
name: "Work",
workspace: "~/.openclaw/workspace-work",
agentDir: "~/.openclaw/agents/work/agent",
},
],
},
// 确定性路由:第一个匹配获胜(最具体的优先)。
bindings: [
{ agentId: "home", match: { channel: "whatsapp", accountId: "personal" } },
{ agentId: "work", match: { channel: "whatsapp", accountId: "biz" } },
// 可选的每对等方覆盖(示例:将特定群组发送到 work 智能体)。
{
agentId: "work",
match: {
channel: "whatsapp",
accountId: "personal",
peer: { kind: "group", id: "1203630...@g.us" },
},
},
],
// 默认关闭:智能体到智能体的消息必须明确启用 + 加入允许列表。
tools: {
agentToAgent: {
enabled: false,
allow: ["home", "work"],
},
},
channels: {
whatsapp: {
accounts: {
personal: {
// 可选覆盖。默认:~/.openclaw/credentials/whatsapp/personal
// authDir: "~/.openclaw/credentials/whatsapp/personal",
},
biz: {
// 可选覆盖。默认:~/.openclaw/credentials/whatsapp/biz
// authDir: "~/.openclaw/credentials/whatsapp/biz",
},
},
},
},
}
```
## 示例WhatsApp 日常聊天 + Telegram 深度工作
按渠道分割:将 WhatsApp 路由到快速日常智能体Telegram 路由到 Opus 智能体。
```json5
{
agents: {
list: [
{
id: "chat",
name: "Everyday",
workspace: "~/.openclaw/workspace-chat",
model: "anthropic/claude-sonnet-4-5",
},
{
id: "opus",
name: "Deep Work",
workspace: "~/.openclaw/workspace-opus",
model: "anthropic/claude-opus-4-5",
},
],
},
bindings: [
{ agentId: "chat", match: { channel: "whatsapp" } },
{ agentId: "opus", match: { channel: "telegram" } },
],
}
```
注意事项:
- 如果你有一个渠道的多个账户,请在绑定中添加 `accountId`(例如 `{ channel: "whatsapp", accountId: "personal" }`)。
- 要将单个私信/群组路由到 Opus 而保持其余在 chat 上,请为该对等方添加 `match.peer` 绑定;对等方匹配始终优先于渠道级规则。
## 示例:同一渠道,一个对等方到 Opus
保持 WhatsApp 在快速智能体上,但将一个私信路由到 Opus
```json5
{
agents: {
list: [
{
id: "chat",
name: "Everyday",
workspace: "~/.openclaw/workspace-chat",
model: "anthropic/claude-sonnet-4-5",
},
{
id: "opus",
name: "Deep Work",
workspace: "~/.openclaw/workspace-opus",
model: "anthropic/claude-opus-4-5",
},
],
},
bindings: [
{ agentId: "opus", match: { channel: "whatsapp", peer: { kind: "dm", id: "+15551234567" } } },
{ agentId: "chat", match: { channel: "whatsapp" } },
],
}
```
对等方绑定始终获胜,因此将它们放在渠道级规则之上。
## 绑定到 WhatsApp 群组的家庭智能体
将专用家庭智能体绑定到单个 WhatsApp 群组,使用提及限制和更严格的工具策略:
```json5
{
agents: {
list: [
{
id: "family",
name: "Family",
workspace: "~/.openclaw/workspace-family",
identity: { name: "Family Bot" },
groupChat: {
mentionPatterns: ["@family", "@familybot", "@Family Bot"],
},
sandbox: {
mode: "all",
scope: "agent",
},
tools: {
allow: [
"exec",
"read",
"sessions_list",
"sessions_history",
"sessions_send",
"sessions_spawn",
"session_status",
],
deny: ["write", "edit", "apply_patch", "browser", "canvas", "nodes", "cron"],
},
},
],
},
bindings: [
{
agentId: "family",
match: {
channel: "whatsapp",
peer: { kind: "group", id: "120363999999999999@g.us" },
},
},
],
}
```
注意事项:
- 工具允许/拒绝列表是**工具**,不是 Skills。如果 skill 需要运行二进制文件,请确保 `exec` 被允许且二进制文件存在于沙箱中。
- 对于更严格的限制,设置 `agents.list[].groupChat.mentionPatterns` 并为渠道保持群组允许列表启用。
## 每智能体沙箱和工具配置
从 v2026.1.6 开始,每个智能体可以有自己的沙箱和工具限制:
```js
{
agents: {
list: [
{
id: "personal",
workspace: "~/.openclaw/workspace-personal",
sandbox: {
mode: "off", // 个人智能体无沙箱
},
// 无工具限制 - 所有工具可用
},
{
id: "family",
workspace: "~/.openclaw/workspace-family",
sandbox: {
mode: "all", // 始终沙箱隔离
scope: "agent", // 每智能体一个容器
docker: {
// 容器创建后的可选一次性设置
setupCommand: "apt-get update && apt-get install -y git curl",
},
},
tools: {
allow: ["read"], // 仅 read 工具
deny: ["exec", "write", "edit", "apply_patch"], // 拒绝其他
},
},
],
},
}
```
注意:`setupCommand` 位于 `sandbox.docker` 下,在容器创建时运行一次。
当解析的 scope 为 `"shared"` 时,每智能体 `sandbox.docker.*` 覆盖会被忽略。
**好处:**
- **安全隔离**:限制不受信任智能体的工具
- **资源控制**:沙箱隔离特定智能体同时保持其他智能体在主机上
- **灵活策略**:每智能体不同的权限
注意:`tools.elevated` 是**全局的**且基于发送者;不能按智能体配置。
如果你需要每智能体边界,使用 `agents.list[].tools` 拒绝 `exec`
对于群组定向,使用 `agents.list[].groupChat.mentionPatterns` 使 @提及清晰地映射到目标智能体
参见 [多智能体沙箱和工具](/tools/multi-agent-sandbox-tools) 了解详细示例。

151
content/concepts/oauth.md Normal file
View File

@@ -0,0 +1,151 @@
---
read_when:
- 你想全面了解 OpenClaw 的 OAuth 流程
- 你遇到了令牌失效/登出问题
- 你想了解 setup-token 或 OAuth 认证流程
- 你想使用多账户或配置文件路由
summary: OpenClaw 中的 OAuth令牌交换、存储和多账户模式
title: OAuth
x-i18n:
generated_at: "2026-02-01T20:23:29Z"
model: claude-opus-4-5
provider: pi
source_hash: af714bdadc4a89295a18da1eba5f5b857c8d533ebabe9b0758b722fe60c36124
source_path: concepts/oauth.md
workflow: 14
---
# OAuth
OpenClaw 支持通过 OAuth 进行"订阅认证",适用于提供此功能的提供商(特别是 **OpenAI CodexChatGPT OAuth**)。对于 Anthropic 订阅,请使用 **setup-token** 流程。本页说明:
- OAuth **令牌交换**的工作原理PKCE
- 令牌**存储**在哪里(以及原因)
- 如何处理**多账户**(配置文件 + 按会话覆盖)
OpenClaw 还支持**提供商插件**,它们自带 OAuth 或 API 密钥流程。通过以下命令运行:
```bash
openclaw models auth login --provider <id>
```
## 令牌汇聚点(为什么需要它)
OAuth 提供商通常在登录/刷新流程中发放**新的刷新令牌**。某些提供商(或 OAuth 客户端)在为同一用户/应用发放新令牌时,可能会使旧的刷新令牌失效。
实际症状:
- 你通过 OpenClaw _和_ Claude Code / Codex CLI 登录 → 其中一个稍后会随机"登出"
为减少这种情况OpenClaw 将 `auth-profiles.json` 视为**令牌汇聚点**
- 运行时从**同一个位置**读取凭据
- 我们可以保留多个配置文件并确定性地路由它们
## 存储(令牌存放位置)
密钥按**智能体**存储:
- 认证配置文件OAuth + API 密钥):`~/.openclaw/agents/<agentId>/agent/auth-profiles.json`
- 运行时缓存(自动管理;请勿编辑):`~/.openclaw/agents/<agentId>/agent/auth.json`
仅用于导入的旧版文件(仍然支持,但不是主存储):
- `~/.openclaw/credentials/oauth.json`(首次使用时导入到 `auth-profiles.json`
以上所有路径也遵循 `$OPENCLAW_STATE_DIR`(状态目录覆盖)。完整参考:[/gateway/configuration](/gateway/configuration#auth-storage-oauth--api-keys)
## Anthropic setup-token订阅认证
在任意机器上运行 `claude setup-token`,然后将其粘贴到 OpenClaw 中:
```bash
openclaw models auth setup-token --provider anthropic
```
如果你在其他地方生成了令牌,可以手动粘贴:
```bash
openclaw models auth paste-token --provider anthropic
```
验证:
```bash
openclaw models status
```
## OAuth 交换(登录工作原理)
OpenClaw 的交互式登录流程在 `@mariozechner/pi-ai` 中实现,并集成到向导/命令中。
### AnthropicClaude Pro/Maxsetup-token
流程概要:
1. 运行 `claude setup-token`
2. 将令牌粘贴到 OpenClaw
3. 作为令牌认证配置文件存储(无刷新)
向导路径为 `openclaw onboard` → 认证选择 `setup-token`Anthropic
### OpenAI CodexChatGPT OAuth
流程概要PKCE
1. 生成 PKCE 验证器/质询 + 随机 `state`
2. 打开 `https://auth.openai.com/oauth/authorize?...`
3. 尝试在 `http://127.0.0.1:1455/auth/callback` 捕获回调
4. 如果回调无法绑定(或你在远程/无头环境中),手动粘贴重定向 URL/代码
5.`https://auth.openai.com/oauth/token` 进行交换
6. 从访问令牌中提取 `accountId` 并存储 `{ access, refresh, expires, accountId }`
向导路径为 `openclaw onboard` → 认证选择 `openai-codex`
## 刷新 + 过期
配置文件存储 `expires` 时间戳。
运行时:
- 如果 `expires` 在未来 → 使用已存储的访问令牌
- 如果已过期 → 刷新(在文件锁下)并覆盖已存储的凭据
刷新流程是自动的;你通常不需要手动管理令牌。
## 多账户(配置文件)+ 路由
两种模式:
### 1推荐独立智能体
如果你希望"个人"和"工作"永远不交叉,请使用隔离的智能体(独立的会话 + 凭据 + 工作区):
```bash
openclaw agents add work
openclaw agents add personal
```
然后按智能体配置认证(向导),并将聊天路由到正确的智能体。
### 2高级单个智能体中的多个配置文件
`auth-profiles.json` 支持同一提供商的多个配置文件 ID。
选择使用哪个配置文件:
- 通过配置顺序全局设置(`auth.order`
- 通过 `/model ...@<profileId>` 按会话设置
示例(会话覆盖):
- `/model Opus@anthropic:work`
如何查看存在哪些配置文件 ID
- `openclaw channels list --json`(显示 `auth[]`
相关文档:
- [/concepts/model-failover](/concepts/model-failover)(轮换 + 冷却规则)
- [/tools/slash-commands](/tools/slash-commands)(命令界面)

619
content/concepts/pi.md Normal file
View File

@@ -0,0 +1,619 @@
---
title: Pi 集成架构
x-i18n:
generated_at: "2026-02-03T07:53:24Z"
model: claude-opus-4-5
provider: pi
source_hash: 98b12f1211f70b1a25f58e68c7a4d0fe3827412ca53ba0ea2cd41ac9c0448458
source_path: pi.md
workflow: 15
---
# Pi 集成架构
本文档描述了 OpenClaw 如何与 [pi-coding-agent](https://github.com/badlogic/pi-mono/tree/main/packages/coding-agent) 及其相关包(`pi-ai``pi-agent-core``pi-tui`)集成以实现其 AI 智能体能力。
## 概述
OpenClaw 使用 pi SDK 将 AI 编码智能体嵌入到其消息 Gateway 网关架构中。OpenClaw 不是将 pi 作为子进程生成或使用 RPC 模式,而是通过 `createAgentSession()` 直接导入并实例化 pi 的 `AgentSession`。这种嵌入式方法提供了:
- 对会话生命周期和事件处理的完全控制
- 自定义工具注入(消息、沙箱、渠道特定操作)
- 每个渠道/上下文的系统提示自定义
- 支持分支/压缩的会话持久化
- 带故障转移的多账户认证配置文件轮换
- 与提供商无关的模型切换
## 包依赖
```json
{
"@mariozechner/pi-agent-core": "0.49.3",
"@mariozechner/pi-ai": "0.49.3",
"@mariozechner/pi-coding-agent": "0.49.3",
"@mariozechner/pi-tui": "0.49.3"
}
```
| 包 | 用途 |
| ----------------- | ------------------------------------------------------------------------------------------ |
| `pi-ai` | 核心 LLM 抽象:`Model``streamSimple`、消息类型、提供商 API |
| `pi-agent-core` | 智能体循环、工具执行、`AgentMessage` 类型 |
| `pi-coding-agent` | 高级 SDK`createAgentSession``SessionManager``AuthStorage``ModelRegistry`、内置工具 |
| `pi-tui` | 终端 UI 组件(用于 OpenClaw 的本地 TUI 模式) |
## 文件结构
```
src/agents/
├── pi-embedded-runner.ts # Re-exports from pi-embedded-runner/
├── pi-embedded-runner/
│ ├── run.ts # Main entry: runEmbeddedPiAgent()
│ ├── run/
│ │ ├── attempt.ts # Single attempt logic with session setup
│ │ ├── params.ts # RunEmbeddedPiAgentParams type
│ │ ├── payloads.ts # Build response payloads from run results
│ │ ├── images.ts # Vision model image injection
│ │ └── types.ts # EmbeddedRunAttemptResult
│ ├── abort.ts # Abort error detection
│ ├── cache-ttl.ts # Cache TTL tracking for context pruning
│ ├── compact.ts # Manual/auto compaction logic
│ ├── extensions.ts # Load pi extensions for embedded runs
│ ├── extra-params.ts # Provider-specific stream params
│ ├── google.ts # Google/Gemini turn ordering fixes
│ ├── history.ts # History limiting (DM vs group)
│ ├── lanes.ts # Session/global command lanes
│ ├── logger.ts # Subsystem logger
│ ├── model.ts # Model resolution via ModelRegistry
│ ├── runs.ts # Active run tracking, abort, queue
│ ├── sandbox-info.ts # Sandbox info for system prompt
│ ├── session-manager-cache.ts # SessionManager instance caching
│ ├── session-manager-init.ts # Session file initialization
│ ├── system-prompt.ts # System prompt builder
│ ├── tool-split.ts # Split tools into builtIn vs custom
│ ├── types.ts # EmbeddedPiAgentMeta, EmbeddedPiRunResult
│ └── utils.ts # ThinkLevel mapping, error description
├── pi-embedded-subscribe.ts # Session event subscription/dispatch
├── pi-embedded-subscribe.types.ts # SubscribeEmbeddedPiSessionParams
├── pi-embedded-subscribe.handlers.ts # Event handler factory
├── pi-embedded-subscribe.handlers.lifecycle.ts
├── pi-embedded-subscribe.handlers.types.ts
├── pi-embedded-block-chunker.ts # Streaming block reply chunking
├── pi-embedded-messaging.ts # Messaging tool sent tracking
├── pi-embedded-helpers.ts # Error classification, turn validation
├── pi-embedded-helpers/ # Helper modules
├── pi-embedded-utils.ts # Formatting utilities
├── pi-tools.ts # createOpenClawCodingTools()
├── pi-tools.abort.ts # AbortSignal wrapping for tools
├── pi-tools.policy.ts # Tool allowlist/denylist policy
├── pi-tools.read.ts # Read tool customizations
├── pi-tools.schema.ts # Tool schema normalization
├── pi-tools.types.ts # AnyAgentTool type alias
├── pi-tool-definition-adapter.ts # AgentTool -> ToolDefinition adapter
├── pi-settings.ts # Settings overrides
├── pi-extensions/ # Custom pi extensions
│ ├── compaction-safeguard.ts # Safeguard extension
│ ├── compaction-safeguard-runtime.ts
│ ├── context-pruning.ts # Cache-TTL context pruning extension
│ └── context-pruning/
├── model-auth.ts # Auth profile resolution
├── auth-profiles.ts # Profile store, cooldown, failover
├── model-selection.ts # Default model resolution
├── models-config.ts # models.json generation
├── model-catalog.ts # Model catalog cache
├── context-window-guard.ts # Context window validation
├── failover-error.ts # FailoverError class
├── defaults.ts # DEFAULT_PROVIDER, DEFAULT_MODEL
├── system-prompt.ts # buildAgentSystemPrompt()
├── system-prompt-params.ts # System prompt parameter resolution
├── system-prompt-report.ts # Debug report generation
├── tool-summaries.ts # Tool description summaries
├── tool-policy.ts # Tool policy resolution
├── transcript-policy.ts # Transcript validation policy
├── skills.ts # Skill snapshot/prompt building
├── skills/ # Skill subsystem
├── sandbox.ts # Sandbox context resolution
├── sandbox/ # Sandbox subsystem
├── channel-tools.ts # Channel-specific tool injection
├── openclaw-tools.ts # OpenClaw-specific tools
├── bash-tools.ts # exec/process tools
├── apply-patch.ts # apply_patch tool (OpenAI)
├── tools/ # Individual tool implementations
│ ├── browser-tool.ts
│ ├── canvas-tool.ts
│ ├── cron-tool.ts
│ ├── discord-actions*.ts
│ ├── gateway-tool.ts
│ ├── image-tool.ts
│ ├── message-tool.ts
│ ├── nodes-tool.ts
│ ├── session*.ts
│ ├── slack-actions.ts
│ ├── telegram-actions.ts
│ ├── web-*.ts
│ └── whatsapp-actions.ts
└── ...
```
## 核心集成流程
### 1. 运行嵌入式智能体
主入口点是 `pi-embedded-runner/run.ts` 中的 `runEmbeddedPiAgent()`
```typescript
import { runEmbeddedPiAgent } from "./agents/pi-embedded-runner.js";
const result = await runEmbeddedPiAgent({
sessionId: "user-123",
sessionKey: "main:whatsapp:+1234567890",
sessionFile: "/path/to/session.jsonl",
workspaceDir: "/path/to/workspace",
config: openclawConfig,
prompt: "Hello, how are you?",
provider: "anthropic",
model: "claude-sonnet-4-20250514",
timeoutMs: 120_000,
runId: "run-abc",
onBlockReply: async (payload) => {
await sendToChannel(payload.text, payload.mediaUrls);
},
});
```
### 2. 会话创建
`runEmbeddedAttempt()`(由 `runEmbeddedPiAgent()` 调用)内部,使用 pi SDK
```typescript
import {
createAgentSession,
DefaultResourceLoader,
SessionManager,
SettingsManager,
} from "@mariozechner/pi-coding-agent";
const resourceLoader = new DefaultResourceLoader({
cwd: resolvedWorkspace,
agentDir,
settingsManager,
additionalExtensionPaths,
});
await resourceLoader.reload();
const { session } = await createAgentSession({
cwd: resolvedWorkspace,
agentDir,
authStorage: params.authStorage,
modelRegistry: params.modelRegistry,
model: params.model,
thinkingLevel: mapThinkingLevel(params.thinkLevel),
tools: builtInTools,
customTools: allCustomTools,
sessionManager,
settingsManager,
resourceLoader,
});
applySystemPromptOverrideToSession(session, systemPromptOverride);
```
### 3. 事件订阅
`subscribeEmbeddedPiSession()` 订阅 pi 的 `AgentSession` 事件:
```typescript
const subscription = subscribeEmbeddedPiSession({
session: activeSession,
runId: params.runId,
verboseLevel: params.verboseLevel,
reasoningMode: params.reasoningLevel,
toolResultFormat: params.toolResultFormat,
onToolResult: params.onToolResult,
onReasoningStream: params.onReasoningStream,
onBlockReply: params.onBlockReply,
onPartialReply: params.onPartialReply,
onAgentEvent: params.onAgentEvent,
});
```
处理的事件包括:
- `message_start` / `message_end` / `message_update`(流式文本/思考)
- `tool_execution_start` / `tool_execution_update` / `tool_execution_end`
- `turn_start` / `turn_end`
- `agent_start` / `agent_end`
- `auto_compaction_start` / `auto_compaction_end`
### 4. 提示
设置完成后,会话被提示:
```typescript
await session.prompt(effectivePrompt, { images: imageResult.images });
```
SDK 处理完整的智能体循环:发送到 LLM、执行工具调用、流式响应。
## 工具架构
### 工具管道
1. **基础工具**pi 的 `codingTools`read、bash、edit、write
2. **自定义替换**OpenClaw 将 bash 替换为 `exec`/`process`,为沙箱自定义 read/edit/write
3. **OpenClaw 工具**消息、浏览器、画布、会话、定时任务、Gateway 网关等
4. **渠道工具**Discord/Telegram/Slack/WhatsApp 特定的操作工具
5. **策略过滤**:工具按配置文件、提供商、智能体、群组、沙箱策略过滤
6. **Schema 规范化**:为 Gemini/OpenAI 的特殊情况清理 Schema
7. **AbortSignal 包装**:工具被包装以尊重中止信号
### 工具定义适配器
pi-agent-core 的 `AgentTool` 与 pi-coding-agent 的 `ToolDefinition` 有不同的 `execute` 签名。`pi-tool-definition-adapter.ts` 中的适配器桥接了这一点:
```typescript
export function toToolDefinitions(tools: AnyAgentTool[]): ToolDefinition[] {
return tools.map((tool) => ({
name: tool.name,
label: tool.label ?? name,
description: tool.description ?? "",
parameters: tool.parameters,
execute: async (toolCallId, params, onUpdate, _ctx, signal) => {
// pi-coding-agent signature differs from pi-agent-core
return await tool.execute(toolCallId, params, signal, onUpdate);
},
}));
}
```
### 工具拆分策略
`splitSdkTools()` 通过 `customTools` 传递所有工具:
```typescript
export function splitSdkTools(options: { tools: AnyAgentTool[]; sandboxEnabled: boolean }) {
return {
builtInTools: [], // Empty. We override everything
customTools: toToolDefinitions(options.tools),
};
}
```
这确保 OpenClaw 的策略过滤、沙箱集成和扩展工具集在各提供商之间保持一致。
## 系统提示构建
系统提示在 `buildAgentSystemPrompt()``system-prompt.ts`中构建。它组装一个完整的提示包含工具、工具调用风格、安全护栏、OpenClaw CLI 参考、Skills、文档、工作区、沙箱、消息、回复标签、语音、静默回复、心跳、运行时元数据等部分以及启用时的记忆和反应还有可选的上下文文件和额外系统提示内容。部分内容在子智能体使用的最小提示模式下会被裁剪。
提示在会话创建后通过 `applySystemPromptOverrideToSession()` 应用:
```typescript
const systemPromptOverride = createSystemPromptOverride(appendPrompt);
applySystemPromptOverrideToSession(session, systemPromptOverride);
```
## 会话管理
### 会话文件
会话是具有树结构id/parentId 链接)的 JSONL 文件。Pi 的 `SessionManager` 处理持久化:
```typescript
const sessionManager = SessionManager.open(params.sessionFile);
```
OpenClaw 用 `guardSessionManager()` 包装它以确保工具结果安全。
### 会话缓存
`session-manager-cache.ts` 缓存 SessionManager 实例以避免重复的文件解析:
```typescript
await prewarmSessionFile(params.sessionFile);
sessionManager = SessionManager.open(params.sessionFile);
trackSessionManagerAccess(params.sessionFile);
```
### 历史限制
`limitHistoryTurns()` 根据渠道类型(私信 vs 群组)裁剪对话历史。
### 压缩
自动压缩在上下文溢出时触发。`compactEmbeddedPiSessionDirect()` 处理手动压缩:
```typescript
const compactResult = await compactEmbeddedPiSessionDirect({
sessionId, sessionFile, provider, model, ...
});
```
## 认证与模型解析
### 认证配置文件
OpenClaw 维护一个认证配置文件存储,每个提供商有多个 API 密钥:
```typescript
const authStore = ensureAuthProfileStore(agentDir, { allowKeychainPrompt: false });
const profileOrder = resolveAuthProfileOrder({ cfg, store: authStore, provider, preferredProfile });
```
配置文件在失败时轮换,并带有冷却跟踪:
```typescript
await markAuthProfileFailure({ store, profileId, reason, cfg, agentDir });
const rotated = await advanceAuthProfile();
```
### 模型解析
```typescript
import { resolveModel } from "./pi-embedded-runner/model.js";
const { model, error, authStorage, modelRegistry } = resolveModel(
provider,
modelId,
agentDir,
config,
);
// Uses pi's ModelRegistry and AuthStorage
authStorage.setRuntimeApiKey(model.provider, apiKeyInfo.apiKey);
```
### 故障转移
`FailoverError` 在配置了回退时触发模型回退:
```typescript
if (fallbackConfigured && isFailoverErrorMessage(errorText)) {
throw new FailoverError(errorText, {
reason: promptFailoverReason ?? "unknown",
provider,
model: modelId,
profileId,
status: resolveFailoverStatus(promptFailoverReason),
});
}
```
## Pi 扩展
OpenClaw 加载自定义 pi 扩展以实现特殊行为:
### 压缩安全护栏
`pi-extensions/compaction-safeguard.ts` 为压缩添加护栏,包括自适应令牌预算以及工具失败和文件操作摘要:
```typescript
if (resolveCompactionMode(params.cfg) === "safeguard") {
setCompactionSafeguardRuntime(params.sessionManager, { maxHistoryShare });
paths.push(resolvePiExtensionPath("compaction-safeguard"));
}
```
### 上下文裁剪
`pi-extensions/context-pruning.ts` 实现基于缓存 TTL 的上下文裁剪:
```typescript
if (cfg?.agents?.defaults?.contextPruning?.mode === "cache-ttl") {
setContextPruningRuntime(params.sessionManager, {
settings,
contextWindowTokens,
isToolPrunable,
lastCacheTouchAt,
});
paths.push(resolvePiExtensionPath("context-pruning"));
}
```
## 流式传输与块回复
### 块分块
`EmbeddedBlockChunker` 管理将流式文本分成离散的回复块:
```typescript
const blockChunker = blockChunking ? new EmbeddedBlockChunker(blockChunking) : null;
```
### 思考/最终标签剥离
流式输出被处理以剥离 `<think>`/`<thinking>` 块并提取 `<final>` 内容:
```typescript
const stripBlockTags = (text: string, state: { thinking: boolean; final: boolean }) => {
// Strip <think>...</think> content
// If enforceFinalTag, only return <final>...</final> content
};
```
### 回复指令
回复指令如 `[[media:url]]``[[voice]]``[[reply:id]]` 被解析和提取:
```typescript
const { text: cleanedText, mediaUrls, audioAsVoice, replyToId } = consumeReplyDirectives(chunk);
```
## 错误处理
### 错误分类
`pi-embedded-helpers.ts` 对错误进行分类以进行适当处理:
```typescript
isContextOverflowError(errorText) // Context too large
isCompactionFailureError(errorText) // Compaction failed
isAuthAssistantError(lastAssistant) // Auth failure
isRateLimitAssistantError(...) // Rate limited
isFailoverAssistantError(...) // Should failover
classifyFailoverReason(errorText) // "auth" | "rate_limit" | "quota" | "timeout" | ...
```
### 思考级别回退
如果思考级别不受支持,它会回退:
```typescript
const fallbackThinking = pickFallbackThinkingLevel({
message: errorText,
attempted: attemptedThinking,
});
if (fallbackThinking) {
thinkLevel = fallbackThinking;
continue;
}
```
## 沙箱集成
当启用沙箱模式时,工具和路径受到约束:
```typescript
const sandbox = await resolveSandboxContext({
config: params.config,
sessionKey: sandboxSessionKey,
workspaceDir: resolvedWorkspace,
});
if (sandboxRoot) {
// Use sandboxed read/edit/write tools
// Exec runs in container
// Browser uses bridge URL
}
```
## 提供商特定处理
### Anthropic
- 拒绝魔术字符串清除
- 连续角色的回合验证
- Claude Code 参数兼容性
### Google/Gemini
- 回合排序修复(`applyGoogleTurnOrderingFix`
- 工具 schema 清理(`sanitizeToolsForGoogle`
- 会话历史清理(`sanitizeSessionHistory`
### OpenAI
- Codex 模型的 `apply_patch` 工具
- 思考级别降级处理
## TUI 集成
OpenClaw 还有一个本地 TUI 模式,直接使用 pi-tui 组件:
```typescript
// src/tui/tui.ts
import { ... } from "@mariozechner/pi-tui";
```
这提供了与 pi 原生模式类似的交互式终端体验。
## 与 Pi CLI 的主要区别
| 方面 | Pi CLI | OpenClaw 嵌入式 |
| -------- | ----------------------- | ----------------------------------------------------------------------------------------------- |
| 调用方式 | `pi` 命令 / RPC | 通过 `createAgentSession()` 的 SDK |
| 工具 | 默认编码工具 | 自定义 OpenClaw 工具套件 |
| 系统提示 | AGENTS.md + prompts | 按渠道/上下文动态生成 |
| 会话存储 | `~/.pi/agent/sessions/` | `~/.openclaw/agents/<agentId>/sessions/`(或 `$OPENCLAW_STATE_DIR/agents/<agentId>/sessions/` |
| 认证 | 单一凭证 | 带轮换的多配置文件 |
| 扩展 | 从磁盘加载 | 编程方式 + 磁盘路径 |
| 事件处理 | TUI 渲染 | 基于回调onBlockReply 等) |
## 未来考虑
可能需要重构的领域:
1. **工具签名对齐**:目前在 pi-agent-core 和 pi-coding-agent 签名之间适配
2. **会话管理器包装**`guardSessionManager` 增加了安全性但增加了复杂性
3. **扩展加载**:可以更直接地使用 pi 的 `ResourceLoader`
4. **流式处理器复杂性**`subscribeEmbeddedPiSession` 已经变得很大
5. **提供商特殊情况**许多提供商特定的代码路径pi 可能可以处理
## 测试
所有涵盖 pi 集成及其扩展的现有测试:
- `src/agents/pi-embedded-block-chunker.test.ts`
- `src/agents/pi-embedded-helpers.buildbootstrapcontextfiles.test.ts`
- `src/agents/pi-embedded-helpers.classifyfailoverreason.test.ts`
- `src/agents/pi-embedded-helpers.downgradeopenai-reasoning.test.ts`
- `src/agents/pi-embedded-helpers.formatassistanterrortext.test.ts`
- `src/agents/pi-embedded-helpers.formatrawassistanterrorforui.test.ts`
- `src/agents/pi-embedded-helpers.image-dimension-error.test.ts`
- `src/agents/pi-embedded-helpers.image-size-error.test.ts`
- `src/agents/pi-embedded-helpers.isautherrormessage.test.ts`
- `src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts`
- `src/agents/pi-embedded-helpers.iscloudcodeassistformaterror.test.ts`
- `src/agents/pi-embedded-helpers.iscompactionfailureerror.test.ts`
- `src/agents/pi-embedded-helpers.iscontextoverflowerror.test.ts`
- `src/agents/pi-embedded-helpers.isfailovererrormessage.test.ts`
- `src/agents/pi-embedded-helpers.islikelycontextoverflowerror.test.ts`
- `src/agents/pi-embedded-helpers.ismessagingtoolduplicate.test.ts`
- `src/agents/pi-embedded-helpers.messaging-duplicate.test.ts`
- `src/agents/pi-embedded-helpers.normalizetextforcomparison.test.ts`
- `src/agents/pi-embedded-helpers.resolvebootstrapmaxchars.test.ts`
- `src/agents/pi-embedded-helpers.sanitize-session-messages-images.keeps-tool-call-tool-result-ids-unchanged.test.ts`
- `src/agents/pi-embedded-helpers.sanitize-session-messages-images.removes-empty-assistant-text-blocks-but-preserves.test.ts`
- `src/agents/pi-embedded-helpers.sanitizegoogleturnordering.test.ts`
- `src/agents/pi-embedded-helpers.sanitizesessionmessagesimages-thought-signature-stripping.test.ts`
- `src/agents/pi-embedded-helpers.sanitizetoolcallid.test.ts`
- `src/agents/pi-embedded-helpers.sanitizeuserfacingtext.test.ts`
- `src/agents/pi-embedded-helpers.stripthoughtsignatures.test.ts`
- `src/agents/pi-embedded-helpers.validate-turns.test.ts`
- `src/agents/pi-embedded-runner-extraparams.live.test.ts`(实时)
- `src/agents/pi-embedded-runner-extraparams.test.ts`
- `src/agents/pi-embedded-runner.applygoogleturnorderingfix.test.ts`
- `src/agents/pi-embedded-runner.buildembeddedsandboxinfo.test.ts`
- `src/agents/pi-embedded-runner.createsystempromptoverride.test.ts`
- `src/agents/pi-embedded-runner.get-dm-history-limit-from-session-key.falls-back-provider-default-per-dm-not.test.ts`
- `src/agents/pi-embedded-runner.get-dm-history-limit-from-session-key.returns-undefined-sessionkey-is-undefined.test.ts`
- `src/agents/pi-embedded-runner.google-sanitize-thinking.test.ts`
- `src/agents/pi-embedded-runner.guard.test.ts`
- `src/agents/pi-embedded-runner.limithistoryturns.test.ts`
- `src/agents/pi-embedded-runner.resolvesessionagentids.test.ts`
- `src/agents/pi-embedded-runner.run-embedded-pi-agent.auth-profile-rotation.test.ts`
- `src/agents/pi-embedded-runner.sanitize-session-history.test.ts`
- `src/agents/pi-embedded-runner.splitsdktools.test.ts`
- `src/agents/pi-embedded-runner.test.ts`
- `src/agents/pi-embedded-subscribe.code-span-awareness.test.ts`
- `src/agents/pi-embedded-subscribe.reply-tags.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.calls-onblockreplyflush-before-tool-execution-start-preserve.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.does-not-append-text-end-content-is.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.does-not-call-onblockreplyflush-callback-is-not.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.does-not-duplicate-text-end-repeats-full.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.does-not-emit-duplicate-block-replies-text.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.emits-block-replies-text-end-does-not.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.emits-reasoning-as-separate-message-enabled.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.filters-final-suppresses-output-without-start-tag.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.includes-canvas-action-metadata-tool-summaries.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.keeps-assistanttexts-final-answer-block-replies-are.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.keeps-indented-fenced-blocks-intact.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.reopens-fenced-blocks-splitting-inside-them.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.splits-long-single-line-fenced-blocks-reopen.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.streams-soft-chunks-paragraph-preference.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.subscribeembeddedpisession.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.suppresses-message-end-block-replies-message-tool.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.waits-multiple-compaction-retries-before-resolving.test.ts`
- `src/agents/pi-embedded-subscribe.tools.test.ts`
- `src/agents/pi-embedded-utils.test.ts`
- `src/agents/pi-extensions/compaction-safeguard.test.ts`
- `src/agents/pi-extensions/context-pruning.test.ts`
- `src/agents/pi-settings.test.ts`
- `src/agents/pi-tool-definition-adapter.test.ts`
- `src/agents/pi-tools-agent-config.test.ts`
- `src/agents/pi-tools.create-openclaw-coding-tools.adds-claude-style-aliases-schemas-without-dropping-b.test.ts`
- `src/agents/pi-tools.create-openclaw-coding-tools.adds-claude-style-aliases-schemas-without-dropping-d.test.ts`
- `src/agents/pi-tools.create-openclaw-coding-tools.adds-claude-style-aliases-schemas-without-dropping-f.test.ts`
- `src/agents/pi-tools.create-openclaw-coding-tools.adds-claude-style-aliases-schemas-without-dropping.test.ts`
- `src/agents/pi-tools.policy.test.ts`
- `src/agents/pi-tools.safe-bins.test.ts`
- `src/agents/pi-tools.workspace-paths.test.ts`

View File

@@ -0,0 +1,99 @@
---
read_when:
- 调试实例标签页
- 排查重复或过期的实例行
- 更改 Gateway 网关 WS 连接或系统事件信标
summary: OpenClaw 在线状态条目如何生成、合并和显示
title: 在线状态
x-i18n:
generated_at: "2026-02-03T07:46:37Z"
model: claude-opus-4-5
provider: pi
source_hash: c752c76a880878fed673d656db88beb5dbdeefff2491985127ad791521f97d00
source_path: concepts/presence.md
workflow: 15
---
# 在线状态
OpenClaw"在线状态"是以下内容的轻量级、尽力而为的视图:
- **Gateway 网关**本身,以及
- **连接到 Gateway 网关的客户端**mac 应用、WebChat、CLI 等)
在线状态主要用于渲染 macOS 应用的**实例**标签页,并为运维人员提供快速可见性。
## 在线状态字段(显示的内容)
在线状态条目是具有以下字段的结构化对象:
- `instanceId`(可选但强烈推荐):稳定的客户端身份(通常是 `connect.client.instanceId`
- `host`:人类友好的主机名
- `ip`:尽力而为的 IP 地址
- `version`:客户端版本字符串
- `deviceFamily` / `modelIdentifier`:硬件提示
- `mode``ui``webchat``cli``backend``probe``test``node`...
- `lastInputSeconds`"自上次用户输入以来的秒数"(如果已知)
- `reason``self``connect``node-connected``periodic`...
- `ts`:最后更新时间戳(自纪元以来的毫秒数)
## 生产者(在线状态来源)
在线状态条目由多个来源生成并**合并**。
### 1Gateway 网关自身条目
Gateway 网关始终在启动时植入一个"self"条目这样即使在任何客户端连接之前UI 也能显示 Gateway 网关主机。
### 2WebSocket 连接
每个 WS 客户端都以 `connect` 请求开始。在成功握手后Gateway 网关为该连接更新插入一个在线状态条目。
#### 为什么一次性 CLI 命令不会显示
CLI 经常为短暂的一次性命令进行连接。为避免实例列表被刷屏,`client.mode === "cli"` **不会**被转换为在线状态条目。
### 3`system-event` 信标
客户端可以通过 `system-event` 方法发送更丰富的周期性信标。mac 应用使用此方法报告主机名、IP 和 `lastInputSeconds`
### 4节点连接role: node
当节点通过 Gateway 网关 WebSocket 以 `role: node` 连接时Gateway 网关为该节点更新插入一个在线状态条目(与其他 WS 客户端流程相同)。
## 合并 + 去重规则(为什么 `instanceId` 很重要)
在线状态条目存储在单个内存映射中:
- 条目以**在线状态键**为索引。
- 最佳键是稳定的 `instanceId`(来自 `connect.client.instanceId`),它在重启后仍然有效。
- 键不区分大小写。
如果客户端在没有稳定 `instanceId` 的情况下重新连接,它可能会显示为**重复**行。
## TTL 和有界大小
在线状态是有意设计为短暂的:
- **TTL** 超过 5 分钟的条目会被修剪
- **最大条目数:** 200最旧的优先删除
这使列表保持新鲜并避免无限制的内存增长。
## 远程/隧道注意事项(回环 IP
当客户端通过 SSH 隧道/本地端口转发连接时Gateway 网关可能会看到远程地址为 `127.0.0.1`。为避免覆盖客户端报告的有效 IP回环远程地址会被忽略。
## 消费者
### macOS 实例标签页
macOS 应用渲染 `system-presence` 的输出,并根据最后更新的时间应用一个小的状态指示器(活跃/空闲/过期)。
## 调试技巧
- 要查看原始列表,对 Gateway 网关调用 `system-presence`
- 如果你看到重复项:
- 确认客户端在握手中发送稳定的 `client.instanceId`
- 确认周期性信标使用相同的 `instanceId`
- 检查连接派生的条目是否缺少 `instanceId`(这种情况下重复是预期的)

94
content/concepts/queue.md Normal file
View File

@@ -0,0 +1,94 @@
---
read_when:
- 更改自动回复执行或并发设置时
summary: 用于序列化入站自动回复运行的命令队列设计
title: 命令队列
x-i18n:
generated_at: "2026-02-03T10:05:28Z"
model: claude-opus-4-5
provider: pi
source_hash: 2104c24d200fb4f9620e52a19255cd614ababe19d78f3ee42936dc6d0499b73b
source_path: concepts/queue.md
workflow: 15
---
# 命令队列2026-01-16
我们通过一个小型进程内队列序列化入站自动回复运行(所有渠道),以防止多个智能体运行发生冲突,同时仍允许跨会话的安全并行。
## 为什么需要
- 自动回复运行可能开销很大LLM 调用),当多条入站消息接近同时到达时可能发生冲突。
- 序列化可以避免竞争共享资源会话文件、日志、CLI stdin并降低上游速率限制的可能性。
## 工作原理
- 一个支持通道感知的 FIFO 队列以可配置的并发上限排空每个通道(未配置的通道默认为 1main 默认为 4subagent 为 8
- `runEmbeddedPiAgent` 按**会话键**入队(通道 `session:<key>`),以保证每个会话只有一个活动运行。
- 然后每个会话运行被排入**全局通道**(默认为 `main`),因此整体并行度受 `agents.defaults.maxConcurrent` 限制。
- 启用详细日志时,如果排队运行在开始前等待超过约 2 秒,会发出简短通知。
- 输入指示器仍在入队时立即触发(当渠道支持时),因此在等待轮次时用户体验不受影响。
## 队列模式(按渠道)
入站消息可以引导当前运行、等待后续轮次,或两者兼顾:
- `steer`:立即注入当前运行(在下一个工具边界后取消待处理的工具调用)。如果未在流式传输,则回退到 followup。
- `followup`:在当前运行结束后为下一个智能体轮次入队。
- `collect`:将所有排队消息合并为**单个**后续轮次(默认)。如果消息针对不同的渠道/线程,它们会单独排空以保留路由。
- `steer-backlog`(又名 `steer+backlog`):现在引导**并**保留消息用于后续轮次。
- `interrupt`(旧版):中止该会话的活动运行,然后运行最新消息。
- `queue`(旧版别名):与 `steer` 相同。
steer-backlog 意味着你可以在被引导的运行之后获得后续响应,因此流式传输界面可能看起来像重复。如果你希望每条入站消息只有一个响应,请优先使用 `collect`/`steer`
发送 `/queue collect` 作为独立命令(按会话)或设置 `messages.queue.byChannel.discord: "collect"`
默认值(配置中未设置时):
- 所有界面 → `collect`
通过 `messages.queue` 全局或按渠道配置:
```json5
{
messages: {
queue: {
mode: "collect",
debounceMs: 1000,
cap: 20,
drop: "summarize",
byChannel: { discord: "collect" },
},
},
}
```
## 队列选项
选项适用于 `followup``collect``steer-backlog`(以及当 `steer` 回退到 followup 时):
- `debounceMs`:在开始后续轮次前等待静默(防止"继续,继续")。
- `cap`:每个会话的最大排队消息数。
- `drop`:溢出策略(`old``new``summarize`)。
summarize 保留被丢弃消息的简短要点列表,并将其作为合成的后续提示注入。
默认值:`debounceMs: 1000``cap: 20``drop: summarize`
## 按会话覆盖
- 发送 `/queue <mode>` 作为独立命令,为当前会话存储该模式。
- 选项可以组合:`/queue collect debounce:2s cap:25 drop:summarize`
- `/queue default``/queue reset` 清除会话覆盖。
## 范围和保证
- 适用于所有使用 Gateway 网关回复管道的入站渠道的自动回复智能体运行WhatsApp 网页版、Telegram、Slack、Discord、Signal、iMessage、网页聊天等
- 默认通道(`main`)对入站 + 主心跳是进程范围的;设置 `agents.defaults.maxConcurrent` 以允许多个会话并行。
- 可能存在额外的通道(例如 `cron``subagent`),以便后台任务可以并行运行而不阻塞入站回复。
- 按会话通道保证一次只有一个智能体运行触及给定会话。
- 无外部依赖或后台工作线程;纯 TypeScript + promises。
## 故障排除
- 如果命令似乎卡住,启用详细日志并查找"queued for …ms"行以确认队列正在排空。
- 如果你需要查看队列深度,启用详细日志并观察队列计时行。

76
content/concepts/retry.md Normal file
View File

@@ -0,0 +1,76 @@
---
read_when:
- 更新提供商重试行为或默认值
- 调试提供商发送错误或速率限制
summary: 出站提供商调用的重试策略
title: 重试策略
x-i18n:
generated_at: "2026-02-01T20:23:37Z"
model: claude-opus-4-5
provider: pi
source_hash: 55bb261ff567f46ce447be9c0ee0c5b5e6d2776287d7662762656c14108dd607
source_path: concepts/retry.md
workflow: 14
---
# 重试策略
## 目标
- 按 HTTP 请求重试,而非按多步骤流程重试。
- 通过仅重试当前步骤来保持顺序。
- 避免重复执行非幂等操作。
## 默认值
- 尝试次数3
- 最大延迟上限30000 毫秒
- 抖动0.110%
- 提供商默认值:
- Telegram 最小延迟400 毫秒
- Discord 最小延迟500 毫秒
## 行为
### Discord
- 仅在速率限制错误HTTP 429时重试。
- 可用时使用 Discord `retry_after`,否则使用指数退避。
### Telegram
- 在瞬态错误时重试429、超时、连接/重置/关闭、暂时不可用)。
- 可用时使用 `retry_after`,否则使用指数退避。
- Markdown 解析错误不会重试;会回退为纯文本。
## 配置
`~/.openclaw/openclaw.json` 中按提供商设置重试策略:
```json5
{
channels: {
telegram: {
retry: {
attempts: 3,
minDelayMs: 400,
maxDelayMs: 30000,
jitter: 0.1,
},
},
discord: {
retry: {
attempts: 3,
minDelayMs: 500,
maxDelayMs: 30000,
jitter: 0.1,
},
},
},
}
```
## 注意事项
- 重试按请求应用(消息发送、媒体上传、表情回应、投票、贴纸)。
- 组合流程不会重试已完成的步骤。

View File

@@ -0,0 +1,129 @@
---
read_when:
- 你想减少工具输出导致的 LLM 上下文增长
- 你正在调整 agents.defaults.contextPruning
summary: 会话剪枝:工具结果修剪以减少上下文膨胀
x-i18n:
generated_at: "2026-02-03T07:46:35Z"
model: claude-opus-4-5
provider: pi
source_hash: 9b0aa2d1abea7050ba848a2db038ccc3e6e2d83c6eb4e3843a2ead0ab847574a
source_path: concepts/session-pruning.md
workflow: 15
---
# 会话剪枝
会话剪枝在每次 LLM 调用之前从内存上下文中修剪**旧的工具结果**。它**不会**重写磁盘上的会话历史(`*.jsonl`)。
## 运行时机
- 当启用 `mode: "cache-ttl"` 且该会话的最后一次 Anthropic 调用早于 `ttl` 时。
- 仅影响该请求发送给模型的消息。
- 仅对 Anthropic API 调用(和 OpenRouter Anthropic 模型)生效。
- 为获得最佳效果,请将 `ttl` 与你的模型 `cacheControlTtl` 匹配。
- 剪枝后TTL 窗口会重置,因此后续请求会保持缓存直到 `ttl` 再次过期。
## 智能默认值Anthropic
- **OAuth 或 setup-token** 配置文件:启用 `cache-ttl` 剪枝并将心跳设置为 `1h`
- **API 密钥**配置文件:启用 `cache-ttl` 剪枝,将心跳设置为 `30m`,并将 Anthropic 模型的 `cacheControlTtl` 默认为 `1h`
- 如果你显式设置了这些值中的任何一个OpenClaw **不会**覆盖它们。
## 改进内容(成本 + 缓存行为)
- **为什么要剪枝:** Anthropic 提示缓存仅在 TTL 内适用。如果会话空闲超过 TTL下一个请求会重新缓存完整提示除非你先修剪它。
- **什么变得更便宜:** 剪枝减少了 TTL 过期后第一个请求的 **cacheWrite** 大小。
- **为什么 TTL 重置很重要:** 一旦剪枝运行,缓存窗口会重置,因此后续请求可以重用新缓存的提示,而不是再次重新缓存完整历史。
- **它不做什么:** 剪枝不会添加 token 或"双倍"成本;它只改变该 TTL 后第一个请求缓存的内容。
## 可以剪枝的内容
-`toolResult` 消息。
- 用户 + 助手消息**永远不会**被修改。
- 最后 `keepLastAssistants` 条助手消息受保护;该截止点之后的工具结果不会被剪枝。
- 如果没有足够的助手消息来确定截止点,则跳过剪枝。
- 包含**图像块**的工具结果会被跳过(永不修剪/清除)。
## 上下文窗口估算
剪枝使用估算的上下文窗口(字符 ≈ token × 4。基础窗口按以下顺序解析
1. `models.providers.*.models[].contextWindow` 覆盖。
2. 模型定义 `contextWindow`(来自模型注册表)。
3. 默认 `200000` token。
如果设置了 `agents.defaults.contextTokens`,它将被视为解析窗口的上限(最小值)。
## 模式
### cache-ttl
- 仅当最后一次 Anthropic 调用早于 `ttl`(默认 `5m`)时才运行剪枝。
- 运行时:与之前相同的软修剪 + 硬清除行为。
## 软剪枝 vs 硬剪枝
- **软修剪**:仅用于过大的工具结果。
- 保留头部 + 尾部,插入 `...`,并附加一个包含原始大小的注释。
- 跳过包含图像块的结果。
- **硬清除**:用 `hardClear.placeholder` 替换整个工具结果。
## 工具选择
- `tools.allow` / `tools.deny` 支持 `*` 通配符。
- 拒绝优先。
- 匹配不区分大小写。
- 允许列表为空 => 允许所有工具。
## 与其他限制的交互
- 内置工具已经截断自己的输出;会话剪枝是一个额外的层,防止长时间运行的聊天在模型上下文中累积过多的工具输出。
- 压缩是独立的:压缩进行总结并持久化,剪枝是每个请求的临时操作。参阅 [/concepts/compaction](/concepts/compaction)。
## 默认值(启用时)
- `ttl``"5m"`
- `keepLastAssistants``3`
- `softTrimRatio``0.3`
- `hardClearRatio``0.5`
- `minPrunableToolChars``50000`
- `softTrim``{ maxChars: 4000, headChars: 1500, tailChars: 1500 }`
- `hardClear``{ enabled: true, placeholder: "[Old tool result content cleared]" }`
## 示例
默认(关闭):
```json5
{
agent: {
contextPruning: { mode: "off" },
},
}
```
启用 TTL 感知剪枝:
```json5
{
agent: {
contextPruning: { mode: "cache-ttl", ttl: "5m" },
},
}
```
限制剪枝到特定工具:
```json5
{
agent: {
contextPruning: {
mode: "cache-ttl",
tools: { allow: ["exec", "read"], deny: ["*image*"] },
},
},
}
```
参阅配置参考:[Gateway 网关配置](/gateway/configuration)

View File

@@ -0,0 +1,200 @@
---
read_when:
- 添加或修改会话工具时
summary: 用于列出会话、获取历史记录和发送跨会话消息的智能体会话工具
title: 会话工具
x-i18n:
generated_at: "2026-02-03T07:46:54Z"
model: claude-opus-4-5
provider: pi
source_hash: cb6e0982ebf507bcf9de4bb17719759c2b6d3e519731c845580a55279084e4c8
source_path: concepts/session-tool.md
workflow: 15
---
# 会话工具
目标:小型、不易误用的工具集,使智能体能够列出会话、获取历史记录并向另一个会话发送消息。
## 工具名称
- `sessions_list`
- `sessions_history`
- `sessions_send`
- `sessions_spawn`
## 键模型
- 主直接聊天桶始终是字面键 `"main"`(解析为当前智能体的主键)。
- 群聊使用 `agent:<agentId>:<channel>:group:<id>``agent:<agentId>:<channel>:channel:<id>`(传递完整键)。
- 定时任务使用 `cron:<job.id>`
- Hooks 使用 `hook:<uuid>`,除非明确设置。
- Node 会话使用 `node-<nodeId>`,除非明确设置。
`global``unknown` 是保留值,永远不会被列出。如果 `session.scope = "global"`,我们会将其别名为 `main` 用于所有工具,这样调用者永远不会看到 `global`
## sessions_list
将会话列为行数组。
参数:
- `kinds?: string[]` 过滤器:`"main" | "group" | "cron" | "hook" | "node" | "other"` 中的任意一个
- `limit?: number` 最大行数(默认:服务器默认值,限制如 200
- `activeMinutes?: number` 仅在 N 分钟内更新的会话
- `messageLimit?: number` 0 = 无消息(默认 0>0 = 包含最后 N 条消息
行为:
- `messageLimit > 0` 获取每个会话的 `chat.history` 并包含最后 N 条消息。
- 工具结果在列表输出中被过滤;使用 `sessions_history` 获取工具消息。
- 在**沙箱隔离**的智能体会话中运行时,会话工具默认为**仅生成的可见性**(见下文)。
行结构JSON
- `key`:会话键(字符串)
- `kind``main | group | cron | hook | node | other`
- `channel``whatsapp | telegram | discord | signal | imessage | webchat | internal | unknown`
- `displayName`(如果可用的群组显示标签)
- `updatedAt`(毫秒)
- `sessionId`
- `model``contextTokens``totalTokens`
- `thinkingLevel``verboseLevel``systemSent``abortedLastRun`
- `sendPolicy`(如果设置的会话覆盖)
- `lastChannel``lastTo`
- `deliveryContext`(可用时的规范化 `{ channel, to, accountId }`
- `transcriptPath`(从存储目录 + sessionId 派生的尽力路径)
- `messages?`(仅当 `messageLimit > 0` 时)
## sessions_history
获取一个会话的记录。
参数:
- `sessionKey`(必填;接受会话键或来自 `sessions_list``sessionId`
- `limit?: number` 最大消息数(服务器限制)
- `includeTools?: boolean`(默认 false
行为:
- `includeTools=false` 过滤 `role: "toolResult"` 消息。
- 以原始记录格式返回消息数组。
- 当给定 `sessionId`OpenClaw 将其解析为相应的会话键(缺失的 id 会报错)。
## sessions_send
向另一个会话发送消息。
参数:
- `sessionKey`(必填;接受会话键或来自 `sessions_list``sessionId`
- `message`(必填)
- `timeoutSeconds?: number`(默认 >00 = 即发即忘)
行为:
- `timeoutSeconds = 0`:入队并返回 `{ runId, status: "accepted" }`
- `timeoutSeconds > 0`:等待最多 N 秒完成,然后返回 `{ runId, status: "ok", reply }`
- 如果等待超时:`{ runId, status: "timeout", error }`。运行继续;稍后调用 `sessions_history`
- 如果运行失败:`{ runId, status: "error", error }`
- 通告投递在主运行完成后运行,且为尽力而为;`status: "ok"` 不保证通告已投递。
- 通过 Gateway 网关 `agent.wait`(服务器端)等待,因此重连不会丢失等待。
- 智能体到智能体的消息上下文会为主运行注入。
- 主运行完成后OpenClaw 运行**回复循环**
- 第 2 轮及以后在请求者和目标智能体之间交替。
- 精确回复 `REPLY_SKIP` 以停止来回。
- 最大轮数为 `session.agentToAgent.maxPingPongTurns`05默认 5
- 循环结束后OpenClaw 运行**智能体到智能体通告步骤**(仅目标智能体):
- 精确回复 `ANNOUNCE_SKIP` 以保持静默。
- 任何其他回复都会发送到目标渠道。
- 通告步骤包括原始请求 + 第 1 轮回复 + 最新的来回回复。
## Channel 字段
- 对于群组,`channel` 是会话条目上记录的渠道。
- 对于直接聊天,`channel``lastChannel` 映射。
- 对于 cron/hook/node`channel``internal`
- 如果缺失,`channel``unknown`
## 安全 / 发送策略
基于策略的按渠道/聊天类型阻止(不是按会话 id
```json
{
"session": {
"sendPolicy": {
"rules": [
{
"match": { "channel": "discord", "chatType": "group" },
"action": "deny"
}
],
"default": "allow"
}
}
}
```
运行时覆盖(按会话条目):
- `sendPolicy: "allow" | "deny"`(未设置 = 继承配置)
- 可通过 `sessions.patch` 或仅所有者的 `/send on|off|inherit`(独立消息)设置。
执行点:
- `chat.send` / `agent`Gateway 网关)
- 自动回复投递逻辑
## sessions_spawn
在隔离会话中生成子智能体运行,并将结果通告回请求者聊天渠道。
参数:
- `task`(必填)
- `label?`(可选;用于日志/UI
- `agentId?`(可选;如果允许,在另一个智能体 id 下生成)
- `model?`(可选;覆盖子智能体模型;无效值会报错)
- `runTimeoutSeconds?`(默认 0设置时在 N 秒后中止子智能体运行)
- `cleanup?``delete|keep`,默认 `keep`
允许列表:
- `agents.list[].subagents.allowAgents`:通过 `agentId` 允许的智能体 id 列表(`["*"]` 允许任意)。默认:仅请求者智能体。
发现:
- 使用 `agents_list` 发现哪些智能体 id 允许用于 `sessions_spawn`
行为:
- 使用 `deliver: false` 启动新的 `agent:<agentId>:subagent:<uuid>` 会话。
- 子智能体默认使用完整工具集**减去会话工具**(可通过 `tools.subagents.tools` 配置)。
- 子智能体不允许调用 `sessions_spawn`(无子智能体 → 子智能体生成)。
- 始终非阻塞:立即返回 `{ status: "accepted", runId, childSessionKey }`
- 完成后OpenClaw 运行子智能体**通告步骤**并将结果发布到请求者聊天渠道。
- 在通告步骤中精确回复 `ANNOUNCE_SKIP` 以保持静默。
- 通告回复规范化为 `Status`/`Result`/`Notes``Status` 来自运行时结果(不是模型文本)。
- 子智能体会话在 `agents.defaults.subagents.archiveAfterMinutes` 后自动归档默认60
- 通告回复包含统计行运行时间、token 数、sessionKey/sessionId、记录路径和可选成本
## 沙箱会话可见性
沙箱隔离的会话可以使用会话工具,但默认情况下它们只能看到通过 `sessions_spawn` 生成的会话。
配置:
```json5
{
agents: {
defaults: {
sandbox: {
// 默认:"spawned"
sessionToolsVisibility: "spawned", // 或 "all"
},
},
},
}
```

166
content/concepts/session.md Normal file
View File

@@ -0,0 +1,166 @@
---
read_when:
- 修改会话处理或存储
summary: 聊天的会话管理规则、键和持久化
title: 会话管理
x-i18n:
generated_at: "2026-02-03T07:47:44Z"
model: claude-opus-4-5
provider: pi
source_hash: 147c8d1a4b6b4864cb16ad942feba80181b6b0e29afa765e7958f8c2483746b5
source_path: concepts/session.md
workflow: 15
---
# 会话管理
OpenClaw 将**每个智能体的一个直接聊天会话**视为主会话。直接聊天折叠为 `agent:<agentId>:<mainKey>`(默认 `main`),而群组/频道聊天获得各自的键。`session.mainKey` 会被遵循。
使用 `session.dmScope` 控制**私信**如何分组:
- `main`(默认):所有私信共享主会话以保持连续性。
- `per-peer`:跨渠道按发送者 ID 隔离。
- `per-channel-peer`:按渠道 + 发送者隔离(推荐用于多用户收件箱)。
- `per-account-channel-peer`:按账户 + 渠道 + 发送者隔离(推荐用于多账户收件箱)。
使用 `session.identityLinks` 将带提供商前缀的对等 ID 映射到规范身份,这样在使用 `per-peer``per-channel-peer``per-account-channel-peer` 时,同一个人可以跨渠道共享私信会话。
## Gateway 网关是唯一数据源
所有会话状态都**由 Gateway 网关拥有**"主" OpenClaw。UI 客户端macOS 应用、WebChat 等)必须向 Gateway 网关查询会话列表和令牌计数,而不是读取本地文件。
- 在**远程模式**下,你关心的会话存储位于远程 Gateway 网关主机上,而不是你的 Mac 上。
- UI 中显示的令牌计数来自 Gateway 网关的存储字段(`inputTokens``outputTokens``totalTokens``contextTokens`)。客户端不会解析 JSONL 对话记录来"修正"总数。
## 状态存储位置
- 在 **Gateway 网关主机**上:
- 存储文件:`~/.openclaw/agents/<agentId>/sessions/sessions.json`(每个智能体)。
- 对话记录:`~/.openclaw/agents/<agentId>/sessions/<SessionId>.jsonl`Telegram 话题会话使用 `.../<SessionId>-topic-<threadId>.jsonl`)。
- 存储是一个映射 `sessionKey -> { sessionId, updatedAt, ... }`。删除条目是安全的;它们会按需重新创建。
- 群组条目可能包含 `displayName``channel``subject``room``space` 以在 UI 中标记会话。
- 会话条目包含 `origin` 元数据(标签 + 路由提示),以便 UI 可以解释会话的来源。
- OpenClaw **不**读取旧版 Pi/Tau 会话文件夹。
## 会话修剪
默认情况下OpenClaw 在 LLM 调用之前从内存上下文中修剪**旧的工具结果**。
这**不会**重写 JSONL 历史记录。参见 [/concepts/session-pruning](/concepts/session-pruning)。
## 压缩前记忆刷新
当会话接近自动压缩时OpenClaw 可以运行一个**静默记忆刷新**轮次,提醒模型将持久性笔记写入磁盘。这仅在工作区可写时运行。参见[记忆](/concepts/memory)和[压缩](/concepts/compaction)。
## 传输到会话键的映射
- 直接聊天遵循 `session.dmScope`(默认 `main`)。
- `main``agent:<agentId>:<mainKey>`(跨设备/渠道的连续性)。
- 多个电话号码和渠道可以映射到同一个智能体主键;它们作为进入同一个对话的传输通道。
- `per-peer``agent:<agentId>:dm:<peerId>`
- `per-channel-peer``agent:<agentId>:<channel>:dm:<peerId>`
- `per-account-channel-peer``agent:<agentId>:<channel>:<accountId>:dm:<peerId>`accountId 默认为 `default`)。
- 如果 `session.identityLinks` 匹配带提供商前缀的对等 ID例如 `telegram:123`),则规范键替换 `<peerId>`,这样同一个人可以跨渠道共享会话。
- 群组聊天隔离状态:`agent:<agentId>:<channel>:group:<id>`(房间/频道使用 `agent:<agentId>:<channel>:channel:<id>`)。
- Telegram 论坛话题在群组 ID 后附加 `:topic:<threadId>` 以进行隔离。
- 旧版 `group:<id>` 键仍被识别以进行迁移。
- 入站上下文可能仍使用 `group:<id>`;渠道从 `Provider` 推断并规范化为规范的 `agent:<agentId>:<channel>:group:<id>` 形式。
- 其他来源:
- 定时任务:`cron:<job.id>`
- Webhooks`hook:<uuid>`(除非由 hook 显式设置)
- 节点运行:`node-<nodeId>`
## 生命周期
- 重置策略:会话被重用直到过期,过期在下一条入站消息时评估。
- 每日重置:默认为 **Gateway 网关主机本地时间凌晨 4:00**。当会话的最后更新早于最近的每日重置时间时,会话即为过期。
- 空闲重置(可选):`idleMinutes` 添加一个滑动空闲窗口。当同时配置每日和空闲重置时,**先过期者**强制新会话。
- 旧版仅空闲模式:如果你设置了 `session.idleMinutes` 而没有任何 `session.reset`/`resetByType` 配置OpenClaw 会保持仅空闲模式以保持向后兼容。
- 按类型覆盖(可选):`resetByType` 允许你覆盖 `dm``group``thread` 会话的策略thread = Slack/Discord 线程、Telegram 话题、连接器提供的 Matrix 线程)。
- 按渠道覆盖(可选):`resetByChannel` 覆盖渠道的重置策略(适用于该渠道的所有会话类型,优先于 `reset`/`resetByType`)。
- 重置触发器:精确的 `/new``/reset`(加上 `resetTriggers` 中的任何额外项)启动新的会话 ID 并传递消息的其余部分。`/new <model>` 接受模型别名、`provider/model` 或提供商名称(模糊匹配)来设置新会话模型。如果单独发送 `/new``/reset`OpenClaw 会运行一个简短的"问候"轮次来确认重置。
- 手动重置:从存储中删除特定键或删除 JSONL 对话记录;下一条消息会重新创建它们。
- 隔离的定时任务总是每次运行生成新的 `sessionId`(没有空闲重用)。
## 发送策略(可选)
阻止特定会话类型的投递,无需列出单个 ID。
```json5
{
session: {
sendPolicy: {
rules: [
{ action: "deny", match: { channel: "discord", chatType: "group" } },
{ action: "deny", match: { keyPrefix: "cron:" } },
],
default: "allow",
},
},
}
```
运行时覆盖(仅所有者):
- `/send on` → 为此会话允许
- `/send off` → 为此会话拒绝
- `/send inherit` → 清除覆盖并使用配置规则
将这些作为独立消息发送以使其生效。
## 配置(可选重命名示例)
```json5
// ~/.openclaw/openclaw.json
{
session: {
scope: "per-sender", // keep group keys separate
dmScope: "main", // DM continuity (set per-channel-peer/per-account-channel-peer for shared inboxes)
identityLinks: {
alice: ["telegram:123456789", "discord:987654321012345678"],
},
reset: {
// Defaults: mode=daily, atHour=4 (gateway host local time).
// If you also set idleMinutes, whichever expires first wins.
mode: "daily",
atHour: 4,
idleMinutes: 120,
},
resetByType: {
thread: { mode: "daily", atHour: 4 },
dm: { mode: "idle", idleMinutes: 240 },
group: { mode: "idle", idleMinutes: 120 },
},
resetByChannel: {
discord: { mode: "idle", idleMinutes: 10080 },
},
resetTriggers: ["/new", "/reset"],
store: "~/.openclaw/agents/{agentId}/sessions/sessions.json",
mainKey: "main",
},
}
```
## 检查
- `openclaw status` — 显示存储路径和最近的会话。
- `openclaw sessions --json` — 导出每个条目(使用 `--active <minutes>` 过滤)。
- `openclaw gateway call sessions.list --params '{}'` — 从运行中的 Gateway 网关获取会话(使用 `--url`/`--token` 进行远程 Gateway 网关访问)。
- 在聊天中单独发送 `/status` 消息可查看智能体是否可达、会话上下文使用了多少、当前的思考/详细模式开关,以及你的 WhatsApp Web 凭证上次刷新时间(有助于发现重新链接需求)。
- 发送 `/context list``/context detail` 查看系统提示中的内容和注入的工作区文件(以及最大的上下文贡献者)。
- 单独发送 `/stop` 消息可中止当前运行、清除该会话的排队后续操作,并停止从中生成的任何子智能体运行(回复包含已停止的数量)。
- 单独发送 `/compact`(可选指令)消息可总结旧上下文并释放窗口空间。参见 [/concepts/compaction](/concepts/compaction)。
- 可以直接打开 JSONL 对话记录查看完整轮次。
## 提示
- 将主键专用于 1:1 通信;让群组保留各自的键。
- 自动清理时,删除单个键而不是整个存储,以保留其他地方的上下文。
## 会话来源元数据
每个会话条目记录其来源(尽力而为)在 `origin` 中:
- `label`:人类可读标签(从对话标签 + 群组主题/频道解析)
- `provider`:规范化的渠道 ID包括扩展
- `from`/`to`:入站信封中的原始路由 ID
- `accountId`:提供商账户 ID多账户时
- `threadId`:渠道支持时的线程/话题 ID
来源字段为私信、频道和群组填充。如果连接器仅更新投递路由(例如,保持私信主会话新鲜),它仍应提供入站上下文,以便会话保留其解释器元数据。扩展可以通过在入站上下文中发送 `ConversationLabel``GroupSubject``GroupChannel``GroupSpace``SenderName` 并调用 `recordSessionMetaFromInbound`(或将相同上下文传递给 `updateLastRoute`)来实现。

View File

@@ -0,0 +1,17 @@
---
read_when:
- 你查找了 docs/sessions.md规范文档位于 docs/session.md
summary: 会话管理文档的别名
title: 会话
x-i18n:
generated_at: "2026-02-01T20:23:55Z"
model: claude-opus-4-5
provider: pi
source_hash: 7f1e39c3c07b9bb5cdcda361399cf1ce1226ebae3a797d8f93e734aa6a4d00e2
source_path: concepts/sessions.md
workflow: 14
---
# 会话
规范的会话管理文档位于[会话管理](/concepts/session)。

View File

@@ -0,0 +1,133 @@
---
read_when:
- 解释流式传输或分块在渠道上如何工作
- 更改分块流式传输或渠道分块行为
- 调试重复/提前的块回复或草稿流式传输
summary: 流式传输 + 分块行为(块回复、草稿流式传输、限制)
title: 流式传输和分块
x-i18n:
generated_at: "2026-02-03T10:05:41Z"
model: claude-opus-4-5
provider: pi
source_hash: f014eb1898c4351b1d6b812223226d91324701e3e809cd0f3faf6679841bc353
source_path: concepts/streaming.md
workflow: 15
---
# 流式传输 + 分块
OpenClaw 有两个独立的"流式传输"层:
- **分块流式传输(渠道):** 在助手写入时发出已完成的**块**。这些是普通的渠道消息(不是令牌增量)。
- **类令牌流式传输(仅限 Telegram** 在生成时用部分文本更新**草稿气泡**;最终消息在结束时发送。
目前**没有真正的令牌流式传输**到外部渠道消息。Telegram 草稿流式传输是唯一的部分流式传输界面。
## 分块流式传输(渠道消息)
分块流式传输在助手输出可用时以粗粒度块发送。
```
模型输出
└─ text_delta/events
├─ (blockStreamingBreak=text_end)
│ └─ 分块器在缓冲区增长时发出块
└─ (blockStreamingBreak=message_end)
└─ 分块器在 message_end 时刷新
└─ 渠道发送(块回复)
```
图例:
- `text_delta/events`:模型流事件(对于非流式模型可能稀疏)。
- `chunker`:应用最小/最大边界 + 断点偏好的 `EmbeddedBlockChunker`
- `channel send`:实际的出站消息(块回复)。
**控制项:**
- `agents.defaults.blockStreamingDefault``"on"`/`"off"`(默认关闭)。
- 渠道覆盖:`*.blockStreaming`(以及每账户变体)可为每个渠道强制设置 `"on"`/`"off"`
- `agents.defaults.blockStreamingBreak``"text_end"``"message_end"`
- `agents.defaults.blockStreamingChunk``{ minChars, maxChars, breakPreference? }`
- `agents.defaults.blockStreamingCoalesce``{ minChars?, maxChars?, idleMs? }`(发送前合并流式块)。
- 渠道硬上限:`*.textChunkLimit`(例如 `channels.whatsapp.textChunkLimit`)。
- 渠道分块模式:`*.chunkMode`(默认 `length``newline` 在长度分块之前按空行(段落边界)分割)。
- Discord 软上限:`channels.discord.maxLinesPerMessage`(默认 17分割高度较大的回复以避免 UI 裁剪。
**边界语义:**
- `text_end`:分块器发出时立即流式传输块;在每个 `text_end` 时刷新。
- `message_end`:等到助手消息完成,然后刷新缓冲的输出。
如果缓冲文本超过 `maxChars``message_end` 仍然使用分块器,因此可能在最后发出多个块。
## 分块算法(低/高边界)
块分块由 `EmbeddedBlockChunker` 实现:
- **低边界:** 在缓冲区 >= `minChars` 之前不发出(除非强制)。
- **高边界:** 优先在 `maxChars` 之前分割;如果强制,则在 `maxChars` 处分割。
- **断点偏好:** `paragraph``newline``sentence``whitespace` → 硬断点。
- **代码围栏:** 从不在围栏内分割;当在 `maxChars` 处强制分割时,关闭 + 重新打开围栏以保持 Markdown 有效。
`maxChars` 被限制在渠道 `textChunkLimit` 内,因此你无法超过每渠道的上限。
## 合并(合并流式块)
启用分块流式传输时OpenClaw 可以在发送前**合并连续的块分块**。这减少了"单行刷屏",同时仍提供渐进式输出。
- 合并在**空闲间隙**`idleMs`)后刷新。
- 缓冲区受 `maxChars` 限制,超过时将刷新。
- `minChars` 防止微小片段发送,直到累积足够文本(最终刷新始终发送剩余文本)。
- 连接符从 `blockStreamingChunk.breakPreference` 派生(`paragraph``\n\n``newline``\n``sentence` → 空格)。
- 渠道覆盖通过 `*.blockStreamingCoalesce` 可用(包括每账户配置)。
- 除非覆盖Signal/Slack/Discord 的默认合并 `minChars` 提高到 1500。
## 块之间的类人节奏
启用分块流式传输时,你可以在块回复之间添加**随机暂停**(在第一个块之后)。这使多气泡响应感觉更自然。
- 配置:`agents.defaults.humanDelay`(通过 `agents.list[].humanDelay` 按智能体覆盖)。
- 模式:`off`(默认)、`natural`8002500ms`custom``minMs`/`maxMs`)。
- 仅适用于**块回复**,不适用于最终回复或工具摘要。
## "流式传输块或全部内容"
这映射到:
- **流式传输块:** `blockStreamingDefault: "on"` + `blockStreamingBreak: "text_end"`(边生成边发出)。非 Telegram 渠道还需要 `*.blockStreaming: true`
- **最后流式传输全部内容:** `blockStreamingBreak: "message_end"`(刷新一次,如果很长可能有多个块)。
- **无分块流式传输:** `blockStreamingDefault: "off"`(只有最终回复)。
**渠道说明:** 对于非 Telegram 渠道,分块流式传输**默认关闭**,除非 `*.blockStreaming` 明确设置为 `true`。Telegram 可以在没有块回复的情况下流式传输草稿(`channels.telegram.streamMode`)。
配置位置提醒:`blockStreaming*` 默认值位于 `agents.defaults` 下,而不是根配置。
## Telegram 草稿流式传输(类令牌)
Telegram 是唯一支持草稿流式传输的渠道:
- 在**带主题的私聊**中使用 Bot API `sendMessageDraft`
- `channels.telegram.streamMode: "partial" | "block" | "off"`
- `partial`:用最新的流式文本更新草稿。
- `block`:以分块方式更新草稿(相同的分块器规则)。
- `off`:无草稿流式传输。
- 草稿分块配置(仅用于 `streamMode: "block"``channels.telegram.draftChunk`(默认值:`minChars: 200``maxChars: 800`)。
- 草稿流式传输与分块流式传输分开;块回复默认关闭,仅在非 Telegram 渠道上通过 `*.blockStreaming: true` 启用。
- 最终回复仍然是普通消息。
- `/reasoning stream` 将推理写入草稿气泡(仅限 Telegram
当草稿流式传输活跃时OpenClaw 会为该回复禁用分块流式传输以避免双重流式传输。
```
Telegram私聊 + 主题)
└─ sendMessageDraft草稿气泡
├─ streamMode=partial → 更新最新文本
└─ streamMode=block → 分块器更新草稿
└─ 最终回复 → 普通消息
```
图例:
- `sendMessageDraft`Telegram 草稿气泡(不是真正的消息)。
- `final reply`:普通 Telegram 消息发送。

View File

@@ -0,0 +1,101 @@
---
read_when:
- 编辑系统提示词文本、工具列表或时间/心跳部分
- 更改工作区引导或 Skills 注入行为
summary: OpenClaw 系统提示词包含的内容及其组装方式
title: 系统提示词
x-i18n:
generated_at: "2026-02-03T07:46:58Z"
model: claude-opus-4-5
provider: pi
source_hash: bef4b2674ba0414ce28fd08a4c3ead0e0ebe989e7df3c88ca8a0b2abfec2a50b
source_path: concepts/system-prompt.md
workflow: 15
---
# 系统提示词
OpenClaw 为每次智能体运行构建自定义系统提示词。该提示词由 **OpenClaw 拥有**,不使用 pi-coding-agent 默认提示词。
该提示词由 OpenClaw 组装并注入到每次智能体运行中。
## 结构
该提示词设计紧凑,使用固定部分:
- **Tooling**:当前工具列表 + 简短描述。
- **Safety**:简短的防护提醒,避免追求权力的行为或绕过监督。
- **Skills**(如果可用):告诉模型如何按需加载 Skill 指令。
- **OpenClaw Self-Update**:如何运行 `config.apply``update.run`
- **Workspace**:工作目录(`agents.defaults.workspace`)。
- **Documentation**OpenClaw 文档的本地路径(仓库或 npm 包)以及何时阅读它们。
- **Workspace Files (injected)**:表示下方包含引导文件。
- **Sandbox**(启用时):表示沙箱隔离运行时、沙箱路径,以及是否可用提权执行。
- **Current Date & Time**:用户本地时间、时区和时间格式。
- **Reply Tags**:支持的提供商的可选回复标签语法。
- **Heartbeats**:心跳提示词和确认行为。
- **Runtime**主机、操作系统、node、模型、仓库根目录检测到时、思考级别一行
- **Reasoning**:当前可见性级别 + /reasoning 切换提示。
系统提示词中的安全防护是建议性的。它们指导模型行为但不强制执行策略。使用工具策略、执行审批、沙箱隔离和渠道允许列表进行硬性执行;运维人员可以按设计禁用这些。
## 提示词模式
OpenClaw 可以为子智能体渲染更小的系统提示词。运行时为每次运行设置一个 `promptMode`(不是面向用户的配置):
- `full`(默认):包含上述所有部分。
- `minimal`:用于子智能体;省略 **Skills**、**Memory Recall**、**OpenClaw Self-Update**、**Model Aliases**、**User Identity**、**Reply Tags**、**Messaging**、**Silent Replies** 和 **Heartbeats**。Tooling、**Safety**、Workspace、Sandbox、Current Date & Time已知时、Runtime 和注入的上下文仍然可用。
- `none`:仅返回基本身份行。
`promptMode=minimal` 时,额外注入的提示词标记为 **Subagent Context** 而不是 **Group Chat Context**
## 工作区引导注入
引导文件被修剪后附加在 **Project Context** 下,这样模型无需显式读取即可看到身份和配置上下文:
- `AGENTS.md`
- `SOUL.md`
- `TOOLS.md`
- `IDENTITY.md`
- `USER.md`
- `HEARTBEAT.md`
- `BOOTSTRAP.md`(仅在全新工作区上)
大文件会带截断标记被截断。每个文件的最大大小由 `agents.defaults.bootstrapMaxChars` 控制默认20000。缺失的文件会注入一个简短的缺失文件标记。
内部钩子可以通过 `agent:bootstrap` 拦截此步骤以修改或替换注入的引导文件(例如将 `SOUL.md` 替换为其他角色)。
要检查每个注入文件贡献了多少(原始 vs 注入、截断,加上工具 schema 开销),使用 `/context list``/context detail`。参见[上下文](/concepts/context)。
## 时间处理
当用户时区已知时,系统提示词包含专用的 **Current Date & Time** 部分。为保持提示词缓存稳定,它现在只包含**时区**(没有动态时钟或时间格式)。
当智能体需要当前时间时使用 `session_status`;状态卡片包含时间戳行。
配置方式:
- `agents.defaults.userTimezone`
- `agents.defaults.timeFormat``auto` | `12` | `24`
完整行为详情参见[日期和时间](/date-time)。
## Skills
当存在符合条件的 Skills 时OpenClaw 注入一个紧凑的**可用 Skills 列表**`formatSkillsForPrompt`),其中包含每个 Skill 的**文件路径**。提示词指示模型使用 `read` 加载列出位置(工作区、托管或内置)的 SKILL.md。如果没有符合条件的 Skills则省略 Skills 部分。
```
<available_skills>
<skill>
<name>...</name>
<description>...</description>
<location>...</location>
</skill>
</available_skills>
```
这使基础提示词保持小巧,同时仍然支持有针对性的 Skill 使用。
## 文档
如果可用,系统提示词包含一个 **Documentation** 部分,指向本地 OpenClaw 文档目录(仓库工作区中的 `docs/` 或打包的 npm 包文档),并注明公共镜像、源仓库、社区 Discord 和 ClawHub (https://clawhub.com) 用于 Skills 发现。提示词指示模型首先查阅本地文档了解 OpenClaw 行为、命令、配置或架构,并尽可能自己运行 `openclaw status`(仅在无法访问时询问用户)。

View File

@@ -0,0 +1,96 @@
---
read_when:
- 需要了解时间戳如何为模型进行规范化
- 为系统提示词配置用户时区
summary: 智能体、信封和提示词的时区处理
title: 时区
x-i18n:
generated_at: "2026-02-01T20:24:13Z"
model: claude-opus-4-5
provider: pi
source_hash: 9ee809c96897db1126c7efcaa5bf48a63cdcb2092abd4b3205af224ebd882766
source_path: concepts/timezone.md
workflow: 14
---
# 时区
OpenClaw 对时间戳进行标准化,使模型看到**单一的参考时间**。
## 消息信封(默认为本地时间)
入站消息被包装在如下信封中:
```
[Provider ... 2026-01-05 16:26 PST] message text
```
信封中的时间戳**默认为主机本地时间**,精确到分钟。
你可以通过以下配置进行覆盖:
```json5
{
agents: {
defaults: {
envelopeTimezone: "local", // "utc" | "local" | "user" | IANA 时区
envelopeTimestamp: "on", // "on" | "off"
envelopeElapsed: "on", // "on" | "off"
},
},
}
```
- `envelopeTimezone: "utc"` 使用 UTC。
- `envelopeTimezone: "user"` 使用 `agents.defaults.userTimezone`(回退到主机时区)。
- 使用显式 IANA 时区(例如 `"Europe/Vienna"`)可设置固定偏移量。
- `envelopeTimestamp: "off"` 从信封头中移除绝对时间戳。
- `envelopeElapsed: "off"` 移除已用时间后缀(`+2m` 样式)。
### 示例
**本地时间(默认):**
```
[Signal Alice +1555 2026-01-18 00:19 PST] hello
```
**固定时区:**
```
[Signal Alice +1555 2026-01-18 06:19 GMT+1] hello
```
**已用时间:**
```
[Signal Alice +1555 +2m 2026-01-18T05:19Z] follow-up
```
## 工具负载(原始提供商数据 + 规范化字段)
工具调用(`channels.discord.readMessages``channels.slack.readMessages` 等)返回**原始提供商时间戳**。我们还附加规范化字段以保持一致性:
- `timestampMs`UTC 纪元毫秒数)
- `timestampUtc`ISO 8601 UTC 字符串)
原始提供商字段保持不变。
## 系统提示词的用户时区
设置 `agents.defaults.userTimezone` 来告知模型用户的本地时区。如果未设置OpenClaw 会在运行时**解析主机时区**(无需写入配置)。
```json5
{
agents: { defaults: { userTimezone: "America/Chicago" } },
}
```
系统提示词包含:
- `Current Date & Time` 部分,显示本地时间和时区
- `Time format: 12-hour``24-hour`
你可以通过 `agents.defaults.timeFormat``auto` | `12` | `24`)控制提示词格式。
详情参见[日期与时间](/date-time)的完整行为和示例。

284
content/concepts/typebox.md Normal file
View File

@@ -0,0 +1,284 @@
---
read_when:
- 更新协议模式或代码生成
summary: TypeBox 模式作为 Gateway 网关协议的唯一事实来源
title: TypeBox
x-i18n:
generated_at: "2026-02-03T07:47:23Z"
model: claude-opus-4-5
provider: pi
source_hash: 233800f4f5fabe8ed0e1b3d8aded2eca27252e08c9b0b24ea9c6293e9282c918
source_path: concepts/typebox.md
workflow: 15
---
# TypeBox 作为协议的事实来源
最后更新2026-01-10
TypeBox 是一个 TypeScript 优先的模式库。我们用它来定义 **Gateway 网关 WebSocket 协议**(握手、请求/响应、服务器事件)。这些模式驱动**运行时验证**、**JSON Schema 导出**和 macOS 应用的 **Swift 代码生成**。一个事实来源;其他一切都是生成的。
如果你想了解更高层次的协议上下文,请从 [Gateway 网关架构](/concepts/architecture)开始。
## 心智模型30 秒)
每个 Gateway 网关 WS 消息都是以下三种帧之一:
- **Request**`{ type: "req", id, method, params }`
- **Response**`{ type: "res", id, ok, payload | error }`
- **Event**`{ type: "event", event, payload, seq?, stateVersion? }`
第一个帧**必须**是 `connect` 请求。之后,客户端可以调用方法(例如 `health``send``chat.send`)并订阅事件(例如 `presence``tick``agent`)。
连接流程(最小):
```
Client Gateway
|---- req:connect -------->|
|<---- res:hello-ok --------|
|<---- event:tick ----------|
|---- req:health ---------->|
|<---- res:health ----------|
```
常用方法 + 事件:
| 类别 | 示例 | 说明 |
| ---- | --------------------------------------------------------- | ------------------------------- |
| 核心 | `connect``health``status` | `connect` 必须是第一个 |
| 消息 | `send``poll``agent``agent.wait` | 有副作用的需要 `idempotencyKey` |
| 聊天 | `chat.history``chat.send``chat.abort``chat.inject` | WebChat 使用这些 |
| 会话 | `sessions.list``sessions.patch``sessions.delete` | 会话管理 |
| 节点 | `node.list``node.invoke``node.pair.*` | Gateway 网关 WS + 节点操作 |
| 事件 | `tick``presence``agent``chat``health``shutdown` | 服务器推送 |
权威列表在 `src/gateway/server.ts``METHODS``EVENTS`)中。
## 模式所在位置
- 源码:`src/gateway/protocol/schema.ts`
- 运行时验证器AJV`src/gateway/protocol/index.ts`
- 服务器握手 + 方法分发:`src/gateway/server.ts`
- 节点客户端:`src/gateway/client.ts`
- 生成的 JSON Schema`dist/protocol.schema.json`
- 生成的 Swift 模型:`apps/macos/Sources/OpenClawProtocol/GatewayModels.swift`
## 当前流程
- `pnpm protocol:gen`
- 将 JSON Schemadraft07写入 `dist/protocol.schema.json`
- `pnpm protocol:gen:swift`
- 生成 Swift Gateway 网关模型
- `pnpm protocol:check`
- 运行两个生成器并验证输出已提交
## 模式在运行时的使用方式
- **服务器端**:每个入站帧都用 AJV 验证。握手仅接受参数匹配 `ConnectParams``connect` 请求。
- **客户端**JS 客户端在使用之前验证事件和响应帧。
- **方法表面**Gateway 网关在 `hello-ok` 中公布支持的 `methods``events`
## 示例帧
Connect第一条消息
```json
{
"type": "req",
"id": "c1",
"method": "connect",
"params": {
"minProtocol": 2,
"maxProtocol": 2,
"client": {
"id": "openclaw-macos",
"displayName": "macos",
"version": "1.0.0",
"platform": "macos 15.1",
"mode": "ui",
"instanceId": "A1B2"
}
}
}
```
Hello-ok 响应:
```json
{
"type": "res",
"id": "c1",
"ok": true,
"payload": {
"type": "hello-ok",
"protocol": 2,
"server": { "version": "dev", "connId": "ws-1" },
"features": { "methods": ["health"], "events": ["tick"] },
"snapshot": {
"presence": [],
"health": {},
"stateVersion": { "presence": 0, "health": 0 },
"uptimeMs": 0
},
"policy": { "maxPayload": 1048576, "maxBufferedBytes": 1048576, "tickIntervalMs": 30000 }
}
}
```
请求 + 响应:
```json
{ "type": "req", "id": "r1", "method": "health" }
```
```json
{ "type": "res", "id": "r1", "ok": true, "payload": { "ok": true } }
```
事件:
```json
{ "type": "event", "event": "tick", "payload": { "ts": 1730000000 }, "seq": 12 }
```
## 最小客户端Node.js
最小可用流程connect + health。
```ts
import { WebSocket } from "ws";
const ws = new WebSocket("ws://127.0.0.1:18789");
ws.on("open", () => {
ws.send(
JSON.stringify({
type: "req",
id: "c1",
method: "connect",
params: {
minProtocol: 3,
maxProtocol: 3,
client: {
id: "cli",
displayName: "example",
version: "dev",
platform: "node",
mode: "cli",
},
},
}),
);
});
ws.on("message", (data) => {
const msg = JSON.parse(String(data));
if (msg.type === "res" && msg.id === "c1" && msg.ok) {
ws.send(JSON.stringify({ type: "req", id: "h1", method: "health" }));
}
if (msg.type === "res" && msg.id === "h1") {
console.log("health:", msg.payload);
ws.close();
}
});
```
## 实践示例:端到端添加方法
示例:添加一个新的 `system.echo` 请求,返回 `{ ok: true, text }`
1. **模式(事实来源)**
添加到 `src/gateway/protocol/schema.ts`
```ts
export const SystemEchoParamsSchema = Type.Object(
{ text: NonEmptyString },
{ additionalProperties: false },
);
export const SystemEchoResultSchema = Type.Object(
{ ok: Type.Boolean(), text: NonEmptyString },
{ additionalProperties: false },
);
```
将两者添加到 `ProtocolSchemas` 并导出类型:
```ts
SystemEchoParams: SystemEchoParamsSchema,
SystemEchoResult: SystemEchoResultSchema,
```
```ts
export type SystemEchoParams = Static<typeof SystemEchoParamsSchema>;
export type SystemEchoResult = Static<typeof SystemEchoResultSchema>;
```
2. **验证**
`src/gateway/protocol/index.ts` 中,导出一个 AJV 验证器:
```ts
export const validateSystemEchoParams = ajv.compile<SystemEchoParams>(SystemEchoParamsSchema);
```
3. **服务器行为**
`src/gateway/server-methods/system.ts` 中添加处理程序:
```ts
export const systemHandlers: GatewayRequestHandlers = {
"system.echo": ({ params, respond }) => {
const text = String(params.text ?? "");
respond(true, { ok: true, text });
},
};
```
`src/gateway/server-methods.ts` 中注册(已合并 `systemHandlers`),然后将 `"system.echo"` 添加到 `src/gateway/server.ts` 中的 `METHODS`
4. **重新生成**
```bash
pnpm protocol:check
```
5. **测试 + 文档**
`src/gateway/server.*.test.ts` 中添加服务器测试,并在文档中记录该方法。
## Swift 代码生成行为
Swift 生成器输出:
- 带有 `req``res``event``unknown` 情况的 `GatewayFrame` 枚举
- 强类型的 payload 结构体/枚举
- `ErrorCode` 值和 `GATEWAY_PROTOCOL_VERSION`
未知的帧类型保留为原始 payload 以实现向前兼容。
## 版本控制 + 兼容性
- `PROTOCOL_VERSION``src/gateway/protocol/schema.ts` 中。
- 客户端发送 `minProtocol` + `maxProtocol`;服务器拒绝不匹配的。
- Swift 模型保留未知帧类型以避免破坏旧客户端。
## 模式模式和约定
- 大多数对象使用 `additionalProperties: false` 以实现严格的 payload。
- `NonEmptyString` 是 ID 和方法/事件名称的默认值。
- 顶层 `GatewayFrame``type` 上使用**鉴别器**。
- 有副作用的方法通常需要在 params 中包含 `idempotencyKey`(示例:`send``poll``agent``chat.send`)。
## 实时 schema JSON
生成的 JSON Schema 在仓库的 `dist/protocol.schema.json` 中。发布的原始文件通常可在以下位置获取:
- https://raw.githubusercontent.com/openclaw/openclaw/main/dist/protocol.schema.json
## 当你更改模式时
1. 更新 TypeBox 模式。
2. 运行 `pnpm protocol:check`
3. 提交重新生成的 schema + Swift 模型。

View File

@@ -0,0 +1,74 @@
---
read_when:
- 更改输入指示器的行为或默认设置
summary: OpenClaw 何时显示输入指示器以及如何调整它们
title: 输入指示器
x-i18n:
generated_at: "2026-02-01T20:24:47Z"
model: claude-opus-4-5
provider: pi
source_hash: 8ee82d02829c4ff58462be8bf5bb52f23f519aeda816c2fd8a583e7a317a2e98
source_path: concepts/typing-indicators.md
workflow: 14
---
# 输入指示器
在运行活跃期间,输入指示器会发送到聊天渠道。使用
`agents.defaults.typingMode` 控制输入指示器**何时**开始显示,使用 `typingIntervalSeconds`
控制**刷新频率**。
## 默认行为
`agents.defaults.typingMode` **未设置**时OpenClaw 保持旧版行为:
- **私聊**:模型循环开始后立即显示输入指示器。
- **群聊中被提及**:立即显示输入指示器。
- **群聊中未被提及**:仅在消息文本开始流式传输时显示输入指示器。
- **心跳运行**:输入指示器禁用。
## 模式
`agents.defaults.typingMode` 设置为以下值之一:
- `never` — 永远不显示输入指示器。
- `instant` — **模型循环开始后立即**显示输入指示器,即使运行最终只返回静默回复令牌。
- `thinking` — 在**第一个推理增量**时开始显示输入指示器(需要运行时设置
`reasoningLevel: "stream"`)。
- `message` — 在**第一个非静默文本增量**时开始显示输入指示器(忽略
`NO_REPLY` 静默令牌)。
触发时机从晚到早的顺序:
`never``message``thinking``instant`
## 配置
```json5
{
agent: {
typingMode: "thinking",
typingIntervalSeconds: 6,
},
}
```
可以按会话覆盖模式或刷新频率:
```json5
{
session: {
typingMode: "message",
typingIntervalSeconds: 4,
},
}
```
## 注意事项
- `message` 模式不会为纯静默回复显示输入指示器(例如用于抑制输出的 `NO_REPLY`
令牌)。
- `thinking` 仅在运行流式传输推理时触发(`reasoningLevel: "stream"`)。
如果模型未产生推理增量,则不会显示输入指示器。
- 无论使用何种模式,心跳运行都不会显示输入指示器。
- `typingIntervalSeconds` 控制的是**刷新频率**,而非开始时间。
默认值为 6 秒。

View File

@@ -0,0 +1,42 @@
---
read_when:
- 你正在对接提供商使用量/配额界面
- 你需要解释使用量跟踪行为或认证要求
summary: 使用量跟踪界面及凭据要求
title: 使用量跟踪
x-i18n:
generated_at: "2026-02-01T20:24:46Z"
model: claude-opus-4-5
provider: pi
source_hash: 6f6ed2a70329b2a6206c327aa749a84fbfe979762caca5f0e7fb556f91631cbb
source_path: concepts/usage-tracking.md
workflow: 14
---
# 使用量跟踪
## 功能简介
- 直接从提供商的使用量端点拉取使用量/配额数据。
- 不提供估算费用;仅展示提供商报告的时间窗口数据。
## 展示位置
- 聊天中的 `/status`:包含会话 token 数和估算费用的表情符号丰富的状态卡片(仅限 API 密钥)。当可用时,会显示**当前模型提供商**的使用量。
- 聊天中的 `/usage off|tokens|full`每次响应的使用量页脚OAuth 仅显示 token 数)。
- 聊天中的 `/usage cost`:从 OpenClaw 会话日志汇总的本地费用摘要。
- CLI`openclaw status --usage` 打印完整的按提供商分类的详细信息。
- CLI`openclaw channels list` 在提供商配置旁打印相同的使用量快照(使用 `--no-usage` 跳过)。
- macOS 菜单栏:上下文菜单下的"使用量"部分(仅在可用时显示)。
## 提供商及凭据
- **Anthropic (Claude)**:认证配置中的 OAuth 令牌。
- **GitHub Copilot**:认证配置中的 OAuth 令牌。
- **Gemini CLI**:认证配置中的 OAuth 令牌。
- **Antigravity**:认证配置中的 OAuth 令牌。
- **OpenAI Codex**:认证配置中的 OAuth 令牌(存在时使用 accountId
- **MiniMax**API 密钥(编程计划密钥;`MINIMAX_CODE_PLAN_KEY``MINIMAX_API_KEY`);使用 5 小时编程计划时间窗口。
- **z.ai**:通过环境变量/配置/认证存储提供的 API 密钥。
如果没有匹配的 OAuth/API 凭据,使用量信息将被隐藏。