first commit
This commit is contained in:
146
content/concepts/agent-loop.md
Normal file
146
content/concepts/agent-loop.md
Normal 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` 超时(仅等待,不会停止智能体)
|
||||
219
content/concepts/agent-workspace.md
Normal file
219
content/concepts/agent-workspace.md
Normal 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)添加私有远程(适合初学者的选项)
|
||||
|
||||
选项 A:GitHub 网页界面
|
||||
|
||||
1. 在 GitHub 上创建新的**私有**仓库。
|
||||
2. 不要用 README 初始化(避免合并冲突)。
|
||||
3. 复制 HTTPS 远程 URL。
|
||||
4. 添加远程并推送:
|
||||
|
||||
```bash
|
||||
git branch -M main
|
||||
git remote add origin <https-url>
|
||||
git push -u origin main
|
||||
```
|
||||
|
||||
选项 B:GitHub CLI(`gh`)
|
||||
|
||||
```bash
|
||||
gh auth login
|
||||
gh repo create openclaw-workspace --private --source . --remote origin --push
|
||||
```
|
||||
|
||||
选项 C:GitLab 网页界面
|
||||
|
||||
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
115
content/concepts/agent.md
Normal 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` 控制软块分块(默认 800–1200 字符;优先段落分隔,其次换行;最后是句子)。
|
||||
使用 `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)_ 🦞
|
||||
123
content/concepts/architecture.md
Normal file
123
content/concepts/architecture.md
Normal 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 的第一帧都会导致硬关闭。
|
||||
- 事件不会重放;客户端必须在出现间隙时刷新。
|
||||
67
content/concepts/compaction.md
Normal file
67
content/concepts/compaction.md
Normal 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
168
content/concepts/context.md
Normal 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。
|
||||
129
content/concepts/date-time.md
Normal file
129
content/concepts/date-time.md
Normal 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 的类纪元字符串
|
||||
- Discord:UTC ISO 时间戳
|
||||
- Telegram/WhatsApp:提供商特定的数字/ISO 时间戳
|
||||
|
||||
如果需要本地时间,请使用已知时区在下游进行转换。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [系统提示词](/concepts/system-prompt)
|
||||
- [时区](/concepts/timezone)
|
||||
- [消息](/concepts/messages)
|
||||
59
content/concepts/features.md
Normal file
59
content/concepts/features.md
Normal 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 Web(Baileys)集成 WhatsApp
|
||||
- Telegram 机器人支持(grammY)
|
||||
- Discord 机器人支持(channels.discord.js)
|
||||
- Mattermost 机器人支持(插件)
|
||||
- 通过本地 imsg CLI 集成 iMessage(macOS)
|
||||
- Pi 的智能体桥接,支持 RPC 模式和工具流式传输
|
||||
- 长响应的流式传输和分块处理
|
||||
- 多智能体路由,按工作区或发送者隔离会话
|
||||
- 通过 OAuth 进行 Anthropic 和 OpenAI 的订阅认证
|
||||
- 会话:私信合并为共享的 `main`;群组相互隔离
|
||||
- 群聊支持,通过提及激活
|
||||
- 图片、音频和文档的媒体支持
|
||||
- 可选的语音消息转录钩子
|
||||
- WebChat 和 macOS 菜单栏应用
|
||||
- iOS 节点,支持配对和 Canvas 界面
|
||||
- Android 节点,支持配对、Canvas、聊天和相机
|
||||
|
||||
<Note>
|
||||
旧版 Claude、Codex、Gemini 和 Opencode 路径已被移除。Pi 是唯一的编程智能体路径。
|
||||
</Note>
|
||||
117
content/concepts/markdown-formatting.md
Normal file
117
content/concepts/markdown-formatting.md
Normal 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
412
content/concepts/memory.md
Normal 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 个 token,80 个 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 默认值。
|
||||
141
content/concepts/messages.md
Normal file
141
content/concepts/messages.md
Normal 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)和渠道文档。
|
||||
145
content/concepts/model-failover.md
Normal file
145
content/concepts/model-failover.md
Normal 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)了解更广泛的模型选择和回退概述。
|
||||
320
content/concepts/model-providers.md
Normal file
320
content/concepts/model-providers.md
Normal 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 Gemini(API 密钥)
|
||||
|
||||
- 提供商:`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 ADC;Antigravity/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` 配置,因为它使用自定义端点:
|
||||
|
||||
- MiniMax(Anthropic 兼容):`--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
196
content/concepts/models.md
Normal file
@@ -0,0 +1,196 @@
|
||||
---
|
||||
read_when:
|
||||
- 添加或修改模型 CLI(models 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 Code(Codex)订阅**(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`,否则此文件默认会被合并。
|
||||
372
content/concepts/multi-agent.md
Normal file
372
content/concepts/multi-agent.md
Normal 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
151
content/concepts/oauth.md
Normal 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 Codex(ChatGPT 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` 中实现,并集成到向导/命令中。
|
||||
|
||||
### Anthropic(Claude Pro/Max)setup-token
|
||||
|
||||
流程概要:
|
||||
|
||||
1. 运行 `claude setup-token`
|
||||
2. 将令牌粘贴到 OpenClaw
|
||||
3. 作为令牌认证配置文件存储(无刷新)
|
||||
|
||||
向导路径为 `openclaw onboard` → 认证选择 `setup-token`(Anthropic)。
|
||||
|
||||
### OpenAI Codex(ChatGPT 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
619
content/concepts/pi.md
Normal 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`
|
||||
99
content/concepts/presence.md
Normal file
99
content/concepts/presence.md
Normal 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`:最后更新时间戳(自纪元以来的毫秒数)
|
||||
|
||||
## 生产者(在线状态来源)
|
||||
|
||||
在线状态条目由多个来源生成并**合并**。
|
||||
|
||||
### 1)Gateway 网关自身条目
|
||||
|
||||
Gateway 网关始终在启动时植入一个"self"条目,这样即使在任何客户端连接之前,UI 也能显示 Gateway 网关主机。
|
||||
|
||||
### 2)WebSocket 连接
|
||||
|
||||
每个 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
94
content/concepts/queue.md
Normal 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 队列以可配置的并发上限排空每个通道(未配置的通道默认为 1;main 默认为 4,subagent 为 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
76
content/concepts/retry.md
Normal 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.1(10%)
|
||||
- 提供商默认值:
|
||||
- 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,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 重试按请求应用(消息发送、媒体上传、表情回应、投票、贴纸)。
|
||||
- 组合流程不会重试已完成的步骤。
|
||||
129
content/concepts/session-pruning.md
Normal file
129
content/concepts/session-pruning.md
Normal 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)
|
||||
200
content/concepts/session-tool.md
Normal file
200
content/concepts/session-tool.md
Normal 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`(默认 >0;0 = 即发即忘)
|
||||
|
||||
行为:
|
||||
|
||||
- `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`(0–5,默认 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
166
content/concepts/session.md
Normal 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`)来实现。
|
||||
17
content/concepts/sessions.md
Normal file
17
content/concepts/sessions.md
Normal 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)。
|
||||
133
content/concepts/streaming.md
Normal file
133
content/concepts/streaming.md
Normal 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`(800–2500ms)、`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 消息发送。
|
||||
101
content/concepts/system-prompt.md
Normal file
101
content/concepts/system-prompt.md
Normal 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`(仅在无法访问时询问用户)。
|
||||
96
content/concepts/timezone.md
Normal file
96
content/concepts/timezone.md
Normal 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
284
content/concepts/typebox.md
Normal 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 Schema(draft‑07)写入 `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 模型。
|
||||
74
content/concepts/typing-indicators.md
Normal file
74
content/concepts/typing-indicators.md
Normal 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 秒。
|
||||
42
content/concepts/usage-tracking.md
Normal file
42
content/concepts/usage-tracking.md
Normal 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 凭据,使用量信息将被隐藏。
|
||||
Reference in New Issue
Block a user