日习一技
type: channel
link: https://i-study-everyday.online/
description: 每天学一点新技术，新框架，新工具，新名词。关于编程语言，数据库，数据结构，算法，机器学习，云计算。
items: turbovec 快速入门
items: 让小龙虾自己写手册：Skill Workshop
items: 带小龙虾逛 ClawHub：自定义 Skill 实战
items: 给小龙虾写本操作手册：Skills 系统
items: 让小龙虾给 Claude Code 派活：学习 OpenClaw 的 ACP 工具
items: 给小龙虾配个浏览器：学习 browser 工具（二）
items: 给小龙虾配齐工具箱：OpenClaw 的工具体系（二）
items: 给小龙虾配齐工具箱：OpenClaw 的工具体系
items: 把小龙虾装进口袋：iOS / Android Node 配对
items: 把小龙虾钉在菜单栏：OpenClaw 的 macOS app（二）

turbovec 快速入门
type: item
date: 2026-06-19T00:59:00+00:00
description: 做 RAG 的朋友多半都算过这样一笔账：一条 768 维的 embedding 向量，用 float32 存就是 768 × 4 = 3072 字节，正好 3 KB；语料攒到 1000 万条，光向量就要占 31 GB 内存。768 维是开源模型很常见的维度，BGE 的 bge-base-zh-v1.5、Sentence Transformers 的 all-mpnet-base-v2 都是这个规格。换更高维的模型，这笔账还要成倍往上涨：BGE-M3 是 1024 维（41 GB），OpenAI 的 text-embedding-3-small 是 1536 维（61 GB），text-embedding-3-large 到了 3072 维，直接奔 123 GB 去了。对于做 RAG 的团队来说，光是把向量塞进内存就是一笔不小的开销，更别提还要留余量给检索本身。常见的解法是向量量化（vector quantization），用更少的比特近似表示每个向量，FAISS 的 Product Quantization（PQ）就是这条路线的代表。不过 PQ 是有代价的：建索引前要先用一批代表性数据训练码本，语料分布变化大了还可能要重训。今天我们要介绍的主角 —— turbovec，走了一条不一样的路。它是一个用 Rust 实现、带 Python 绑定的向量索引库，核心算法来自 Google Research 的 TurboQuant 论文（arXiv:2504.19874，已被 ICLR 2026 接收），不需要训练，也不需要重建，向量加进去就能搜。它的项目首页放了一句颇有冲击力的话：A 10 million document corpus takes 31 GB of RAM as float32. turbovec fits it in 4 GB - and searches it faster than FAISS.这里的 31 GB 正是我们开头算的那笔账：1000 万条 768 维向量，用 float32 存要占 31 GB 内存，turbovec 能把它压到 4 GB，而且检索速度比 FAISS 还快。4 GB 是这么来的：TurboQuant 算法把每个坐标从 float32 的 32 bit 量化到 2~4 bit，压缩 8~16 倍，31 GB 的数据 4-bit 量化后约 3.9 GB；如果用 2-bit 还能再减半，只剩 2 GB 不到。正是由于它内存占用小、速度又快，最近几个月接连被几家海外科技媒体报道。它的几个核心特性是：在线量化（online quantization）：添加向量即建索引，没有单独的训练阶段，也不需要随语料增长重建索引比 FAISS 更快：手写 NEON（ARM）、AVX2 / AVX-512BW（x86）三路 SIMD 内核，运行时自动选择，ARM 上所有配置比 FAISS 的 IndexPQFastScan 快 10%~19%搜索时过滤：search() 支持传入 id allowlist 或 bitmask，量化内核直接在块级别处理过滤，做混合检索时不用先取后筛纯本地运行：不依赖托管服务，数据不出本机或 VPC，配合任意开源 embedding 模型就能搭一套完全离线的 RAG 栈与 FAISS 的对比定位FAISS 是向量检索领域的老牌选手，由 Meta 开源，功能非常全。turbovec 没有想做一个大而全的库去取代 FAISS，它的对标对象很明确，就是 FAISS 里走量化快速扫描路线的 IndexPQFastScan。两者最核心的差别在于有没有训练阶段。FAISS 的 PQ 是数据相关（data-dependent）的量化。它需要先用一批代表性数据跑 train()，学出一套码本（codebook），之后才能 add() 向量、search() 查询。码本学得好不好，直接影响召回质量；语料分布变化大了，码本还可能要重学。turbovec 用的 TurboQuant 是数据无关（data-oblivious）的量化。它的思路是：先对输入向量做一次随机正交旋转，旋转之后每个坐标会服从一个集中的 Beta 分布；在高维空间里不同坐标近似独立，于是可以对每个坐标独立套用一个最优标量量化器。整个过程不依赖具体数据，所以不需要训练、不需要学码本，向量加进去就直接建好了索引。它们两在使用上的主要差别如下表所示：数据无关并不意味着 turbovec 完全不看数据。它在第一次 add() 时会做一次 TQ+ 校准，从首批向量里学一组 per-coord 的微调参数，但只学这一次，之后不再变。这部分细节我们留到后面讲量化算法的文章里展开。安装turbovec 同时面向 Python 和 Rust 用户…

让小龙虾自己写手册：Skill Workshop
type: item
date: 2026-06-15T00:31:00+00:00
description: 上一篇我们围绕自定义 skill 做了三件事：从 ClawHub 上搜索并安装别人的 skill、从零开始自己手写一份 skill、再把它发布回 ClawHub 让别人也能安装。三件事做下来你应该感觉到了，手写 skill 终究是个体力活，触发短语得自己琢磨、正文得自己组织、哪些步骤值得沉淀也得自己判断。前面提过的 skill-creator 能稍微帮你减负，但本质还是一份「按步骤填空」的写作指导，触发权和判断权都在你手里。OpenClaw 有个实验性的内置插件叫 Skill Workshop，方向反过来：每轮成功的会话结束之后，它会自动扫一遍历史消息，把里面值得沉淀的可复用流程提议成一份 workspace skill，如果安全扫描通过就能直接写盘，让小龙虾把这一轮里学到的流程顺手写进下一轮自己能用的手册。开启与配置这是个实验性插件，默认关闭，需要你在配置文件里显式打开才会加载。打开 OpenClaw 的配置文件，在 plugins.entries 下加上这么一段：{ plugins: { entries: { "skill-workshop": { "enabled": true, "config": { "autoCapture": true, "approvalPolicy": "pending", "reviewMode": "hybrid" } } } } }改完保存后，再运行 openclaw gateway restart 重启网关让它生效。这几个配置参数逐个看一下：autoCapture: true —— 让它在每轮成功对话结束后，自动扫一遍历史消息找可沉淀的东西；关掉的话它就不主动扫了，只有你开口让小龙虾「把这个存成 skill」时才会存。approvalPolicy: "pending" —— 它扫到觉得该存的东西时，不会直接写成文件，而是先攒成一条「提议」排进待办队列，等你审批通过后才真正落到磁盘上。建议先使用该配置，等你确认它提议得靠谱了，再换成 auto 让它跳过审批、自己直接写。reviewMode: "hybrid" —— 用哪种方式去发现可沉淀的内容，一共四挡：off 完全不自动找，heuristic 只靠关键词扫描，llm 让模型回看一遍整段对话，hybrid 则是前两者一起上。新手用 hybrid 就行，几挡的差别后面讲。因为还在实验阶段，它的内部行为各版本之间可能会变，所以第一次用务必先 pending 模式，亲眼看几轮它都想存些什么，别一上来就让它自动写文件。这几个参数取不同的值，能搭出好几种不同的组合，对应不同的使用场景：// 保守：只接受模型显式调工具，关闭一切自动捕获 { autoCapture: false, approvalPolicy: "pending", reviewMode: "off" } // 评审优先（推荐）：自动捕获，但都先排队等审批 { autoCapture: true, approvalPolicy: "pending", reviewMode: "hybrid" } // 受信自动化：本地工作区里安全提案自动写盘 { autoCapture: true, approvalPolicy: "auto", reviewMode: "hybrid" } // 省钱：不跑回看模型，只认显式纠正短语 { autoCapture: true, approvalPolicy: "pending", reviewMode: "heuristic" }除了上面这四个，还有几个调阈值、限大小的配置项，第一次上手用不到，附在这里备查：键默认范围作用reviewInterval151..200累计多少轮成功对话后跑一次回看模型reviewMinToolCalls81..500累计多少次工具调用后跑回看模型reviewTimeoutMs450005000..180000回看模型单次运行的超时maxPending501..200每工作区最多保留多少提案maxSkillBytes400001024..200000生成的 skill / 支持文件单文件大小上限实战 Skill Workshop插件启用之后，这一节我们手动走一遍完整流程，亲眼看它怎么把一句话变成一份 skill。整个过程就三件事：在对话里说一句带「纠正口吻」的话、看看提案有没有进队列、再把它 apply 落地。说一句带「纠正口吻」的话第一步，造一段能被插件捕到的对话。插件会盯着你消息里有没有 next time / always / from now on 这类纠正口吻（完整短语列表后面会讲），命中了就尝试把它沉淀成 skill。注意这些短语得出现在用户消息里，小龙虾回复里出现不算。比如：next time we wo…

带小龙虾逛 ClawHub：自定义 Skill 实战
type: item
date: 2026-06-12T00:32:00+00:00
description: 上一篇我们把 Skills 系统的基本盘讲完了：一份 SKILL.md 就是一份操作手册，仓库自带 53 个 skill，加上内置插件捎来的 14 个，开箱就有 67 个能用；加载时按优先级合并、按声明的依赖条件筛选，再把每个 skill 的「名字 + 描述 + 路径」拼成一段索引塞进系统提示，正文则由模型在命中后自己用 Read 工具按需加载。但这 67 个终究是官方给的，对每个人的工作流来说都谈不上贴身。真正让 OpenClaw 生态贴近场景的，是自定义 skill，既包括别人写好放到第三方市场 ClawHub 上的，也包括你自己写出来的。今天我们就先带小龙虾去 ClawHub 上逛逛，挑一个 skill 装到本地试试；然后自己动手写一个最小可用的 sysinfo skill；最后把它发布回 ClawHub，让别人也能装来用。ClawHub 是什么ClawHub 是 OpenClaw 官方的 skill 和 plugin 注册中心，站点是 clawhub.ai。它在 OpenClaw 生态里的位置，类似 npm 之于 Node、PyPI 之于 Python：一个能公开浏览、版本化、可搜索的技能仓库。官方文档把 ClawHub 提供的能力归成下面这张特性表：特性说明公开浏览skill 目录和 SKILL.md 全文都可以匿名公开查看语义搜索走 embedding 向量匹配，而不是只对关键词版本管理用语义化版本号管理，每次发布生成一个新版本，配带变更说明和标签下载分发每个版本一份 zip 包社区反馈支持收藏（star）和评论安全扫描详情页展示 SkillSpector 和 VirusTotal 安全扫描结果，安装前一眼可见作者修复面板被扫描扣留的版本，作者能在 /dashboard 看到并申诉复扫复扫请求误判时作者可以申请有限次数的复扫人工审核申请审核和审计流程CLI 友好的 API适合自动化和脚本调用装一个 skill 试试ClawHub 上的 skill 怎么装到本地呢？官方提供了两套 CLI 命令行工具，我们可以使用 CLI 手动搜索和安装，也可以直接和小龙虾对话，让它给你安装。两个 CLI 工具ClawHub 有两套官方 CLI 入口：openclaw skills：OpenClaw 自家命令族里的一个子命令，装 OpenClaw 时一并就有。负责搜索、安装、更新 ClawHub 上的 skill，外加查看本地 skill 状态（list / info / check）。clawhub：独立的 CLI 工具，靠 npm i -g clawhub 单独安装，覆盖 ClawHub 全部功能，除了上面那些不用登录就能做的搜索、安装类操作，还包括登录、发布、删除、复扫、同步等需要鉴权的写操作。两者常用命令对照如下：操作openclaw skillsclawhub备注搜索openclaw skills search "<query>"clawhub search "<query>"走向量语义搜索，不只匹配关键词安装openclaw skills install <slug>clawhub install <slug>默认装 latest 标签的版本，拉到 workspace 的 skills/ 下安装指定版本openclaw skills install <slug> --version <ver>clawhub install <slug> --version <ver>想锁定某个旧版本时用更新openclaw skills update <slug>clawhub update <slug>按 .clawhub/lock.json 里的来源元数据重新拉新版本全部更新openclaw skills update --allclawhub update --all把 lock.json 里登记过的 ClawHub skill 一起升级查看列表openclaw skills listclawhub listopenclaw skills list 列本地全部 skill（bundled / managed / workspace）；clawhub list 只列从 ClawHub 装下来的查看详情openclaw skills info <slug>—看本地 skill 的来源、frontmatter 元数据、当前是否可用验证openclaw skills check—把本地所有 skill 的依赖条件挨个核对，不可用的列出来排障登录—clawhub login默认走浏览器授权；也可以 --token <token> 直接贴 API token发布—clawhub skill publish <path>把本地 skill 目录推…

给小龙虾写本操作手册：Skills 系统
type: item
date: 2026-06-08T07:20:00+00:00
description: 前面几篇我们把 OpenClaw 的工具挨个过了一遍，但工具终究只是零件。想象一下你跟小龙虾说一句「把 openclaw 仓库里带 bug 标签的 issue 都过一遍，能修的就开个 PR 修掉」，它得先用 exec 跑 gh issue list 拉 issue 列表，按 label 筛出要动的几个，再用 sessions_spawn 分头派子 agent 去改代码，修完再用 gh pr create 开 PR，最后还得盯着 review 评论看要不要再改一轮。这一串环节该按什么顺序走、被 rate-limit 怎么回退、PR 评审打回来怎么继续，光靠模型每次临场发挥并不稳。它需要一份写好的操作手册。这正是 Skill 这套机制要解决的问题。围绕 Skills 系统我们打算分三篇讲完：今天这一篇先把基本盘讲清楚，什么是 skill、从哪儿加载、如何参与对话；下一篇带大家逛一逛 ClawHub 市场，再动手写一个自己的 skill 并发布上去；第三篇看一个实验性玩法 Skill Workshop，让 agent 把自己干活时学到的流程自动写成 workspace skill。今天先从最基础的开始。什么是 Skill根据官方文档，OpenClaw 的 skill 用的是 Agent Skills 规范：每个 skill 就是磁盘上的一个目录，目录里至少有一份 SKILL.md，文件头是一段 YAML frontmatter，正文是 Markdown 写的操作说明。说白了，Skill 就是给 agent 备的一份操作手册：什么时候该触发、要调哪些底层工具、按什么步骤走、出错了怎么办。它和直接把这些步骤塞进系统提示词最大的区别是按需加载。OpenClaw 只在 agent 真有可能用到时才把它的存在告诉模型，平时不占用上下文；模型觉得用户问题和哪个 skill 相关了，再去把手册正文读出来。最小可用的 SKILL.md 长这样：--- name: hello-world description: A simple skill that says hello. --- # Hello World Skill When the user asks for a greeting, use the `echo` tool to say "Hello from your custom skill!".frontmatter 里 name 和 description 是必填的，如果缺一个，这份 skill 直接被丢掉。其中 description 是给模型看的一句话说明，模型靠它判断当前对话要不要使用这个 skill；正文则是给 agent 看的执行手册。除了 name 和 description 这两个必填字段，OpenClaw 还在 frontmatter 里加了不少自家扩展：声明依赖二进制 / 环境变量 / 配置项的 requires.bins / env / config、控制触发方式的 user-invocable / disable-model-invocation / command-dispatch、限定平台的 os、给桌面端 UI 用的 install 安装提示，以及配合密钥注入的 primaryEnv 等等。具体含义留到后面再细看。三类来源OpenClaw 把 skill 按来源分了好几类，信任级别和生命周期各不相同。优先级从高到低依次是：优先级来源路径1Workspace skills<workspace>/skills2Project agent skills<workspace>/.agents/skills3Personal agent skills~/.agents/skills4Managed / 本地 skills~/.openclaw/skills5Bundled skills随安装包发出6额外目录skills.load.extraDirs（配置里指定）如果几个来源里有同名 skill，高优先级覆盖低优先级。这套规则保证了用户工作区里自己定制的版本永远盖得住官方版本，方便做覆写和打补丁。中间那两层 .agents/skills 是给项目级或个人级的 agent profile 用的，平时不太会动，一般我们只关心三类：bundled：仓库自带，随安装包发出managed：~/.openclaw/skills，从 ClawHub 装下来的或本地打的补丁workspace：<workspace>/skills，用户自己写的定制版本除此之外，插件也可以自带 skill：在 openclaw.plugin.json 里声明一个 skills 目录，插件启用时这些 skill 就会按和 extraDirs 同等的最低优先…

让小龙虾给 Claude Code 派活：学习 OpenClaw 的 ACP 工具
type: item
date: 2026-06-05T00:44:00+00:00
description: 用了好几篇把 OpenClaw 的内置工具箱挨个过完，又花了两篇专门讲浏览器，小龙虾「自己能干哪些活」到这里基本就讲齐了。今天换个方向，看它的另一项能力：收到任务后，它不亲自处理，而是把整个任务交给一个真正的外部编码 agent 去完成。这事其实和之前「让小龙虾分身」那篇里讲的 sub-agents 有点像。当时我们看过，主 agent 跑到一半可以用 sessions_spawn 把子任务派给后台的子 agent。但那些子 agent 都是 OpenClaw 自己的 agent，同一套运行时、同一批工具、同一份 system prompt，本质上还是小龙虾在跟自己的分身协作。ACP 要解决的是另一种诉求：派出去的不是 OpenClaw 子 agent，而是 Claude Code、Gemini CLI、Cursor、Codex 这些外部编码 harness。你在飞书上给小龙虾发一句「把这个仓库里的调试日志清理一下」，它接到之后并不自己写代码，而是在网关那台机器上拉起一个真正的 Claude Code 进程，让 Claude Code 去改文件、跑命令，干完再把结果播报回飞书。OpenClaw 的文档和命令里把这些外部 agent 统称为 harness，本文后面也沿用这个叫法。标准的 ACP 协议动手之前，先花点篇幅认识一下 ACP 这套协议本身，它并不是 OpenClaw 自创的东西。ACP 是 Agent Client Protocol（Agent 客户端协议） 的缩写，由开发 Zed 编辑器的团队提出并开源（Apache 许可），项目主页在 agentclientprotocol.com。它要解决的是一个典型的 N×M 问题：一边是越来越多的编辑器（Zed、JetBrains 系、各种 CLI），另一边是越来越多的 AI 编码 agent（Claude Code、Gemini CLI、Codex 等）。如果每个编辑器都要为每个 agent 单独写一套对接，每个 agent 又得反过来适配每个编辑器的私有接口，组合数量很快就会失控，用户也被绑死在某个特定的「编辑器 + agent」组合上。熟悉 LSP（Language Server Protocol，语言服务器协议） 的同学对这个套路应该不陌生。当年 LSP 用一套标准协议把「编辑器」和「语言能力」解耦，任何编辑器配任何语言服务器都能用；ACP 想做的是同一件事，只是解耦的两端换成了「编辑器」和「编码 agent」。协议本身定义了两个角色：Client：通常是编辑器，或者任何想接入 agent 的宿主程序，它掌握着工作区、权限和界面。Agent：真正干编码活的 AI 工具，比如 Claude Code。技术实现上，ACP 走的是 JSON-RPC 2.0 over stdio：Client 把 Agent 作为一个子进程拉起来，双方通过标准输入输出收发 JSON-RPC 消息。一次典型的交互大致是：Client 先 initialize 握手，再用 session/new 开一个会话，然后把用户的指令通过 session/prompt 发过去；Agent 干活的过程中，靠 JSON-RPC 的通知把进展实时流式推回 Client，需要读写文件或执行命令时，则用反向请求回头向 Client 申请权限。整个过程中，掌权的始终是 Client：权限给不给、界面怎么渲染、能碰工作区里的哪些东西，都由 Client 说了算，Agent 只能请求、不能擅自越界。把上面这条交互链画成时序图，大致是这样：如果你了解 MCP（Model Context Protocol，模型上下文协议），会觉得上面这套流程很眼熟：同样是 JSON-RPC 2.0、同样把对方作为子进程通过 stdio 拉起来、同样有 initialize 握手和流式通知。这并不是巧合，ACP 在设计上就刻意向 MCP 看齐、复用它的传输模型。两者的区别在于解决的问题不同：MCP 标准化的是「agent 如何连上工具和数据源」，这时 agent 是 client，MCP server 提供工具；ACP 标准化的是「编辑器或宿主如何连上一个 agent」，这时编辑器是 client，agent 负责编码。它们还能叠在一起用：使用 session/new 开新会话时，可以顺带把要连的 MCP server 一起声明掉，让被拉起来的 ACP Agent 自己再去当一回 MCP Client，连上它需要的工具。讲清楚标准协议，再看 OpenClaw 的位置就顺理成章了：在 ACP 这套协议里，OpenClaw 扮演的是 Client 角色。平时 Zed、JetBrains 是用 ACP 在编辑器里接入 Claude Code，Op…

给小龙虾配个浏览器：学习 browser 工具（二）
type: item
date: 2026-06-01T00:36:00+00:00
description: 上一篇我们把 browser 工具的运行环境从头捋了一遍：首先学习了它的参数定义，然后使用 host / sandbox / node 确定浏览器运行在哪里，以及使用 profile 确定运行哪个浏览器。环境备齐，这一篇书接上文，来看 agent 怎么驱动浏览器在页面上点点点，以及这套动作背后的原理。标签页管理browser 一次只在一个标签页上干活，所以为了保证后续的动作没问题，我们首先要认准操作哪个标签页。我们可以使用 openclaw browser <动作> 命令行来管理它，开新标签页用 open，网址作参数，再用 --label 顺手贴个标签：$ openclaw browser open https://www.aneasystone.com --label blog opened: https://www.aneasystone.com/ tab: t1 label: blog id: 7E73979D64CBF48058818D78D50FB94B该命令返回了几个参数：tab 是形如 t1 的稳定 tabId，label 是我们自己起的 blog 标签，id 则是 CDP 协议的原始 targetId。这几个参数都可以作为后续操作这个标签页时的标识。比如之后想再操作它，用 focus 把它切到前台就行，tabId、label、targetId 这三种都认，下面三条指向的都是同一个页面：$ openclaw browser focus t1 # 用 tabId $ openclaw browser focus blog # 用 label $ openclaw browser focus 7E73979D # 用 targetId（前缀也认）顺带分清一个常被搞混的点：open 每次都新开一个标签页，而 navigate 是让当前标签页原地跳到另一个网址，所以常见用法是先 focus 选中、再 navigate 到某个网址；也可以加上可选的 --target-id 直接跳转：$ openclaw browser navigate https://www.aneasystone.com/about-me.html --target-id blog再开一个不带 label 的，然后用 tabs 列出所有的标签页：$ openclaw browser open https://www.baidu.com opened: https://www.baidu.com/ tab: t2 id: 9B0D7E3F1A2C4856E7F0A1B2C3D4E5F6 $ openclaw browser tabs 1. aneasystone's blog [t1 label:blog] https://www.aneasystone.com/ id: 7E73979D64CBF48058818D78D50FB94B 2. 百度一下，你就知道 [t2] https://www.baidu.com/ id: 9B0D7E3F1A2C4856E7F0A1B2C3D4E5F6我们还可以加一个 --json 参数，看到完整的数据结构：$ openclaw browser --json tabs { "tabs": [ { "targetId": "7E73979D64CBF48058818D78D50FB94B", "title": "aneasystone's blog", "url": "https://www.aneasystone.com/", "wsUrl": "ws://127.0.0.1:18800/devtools/page/7E73979D64CBF48058818D78D50FB94B", "type": "page", "suggestedTargetId": "blog", "tabId": "t1", "label": "blog" } ] }收尾要关掉标签页就用 close 参数：$ openclaw browser close blog closed tab如果忘记关也没有关系，OpenClaw 给主 agent 的浏览器会话配了一套自动清理机制：空闲超过一定时间（默认 120 分钟）的标签页会被回收，每个会话还有个标签页数量上限（默认 8 个），后台每隔几分钟扫一遍；子 agent、cron、ACP 这类任务跑完时，也会顺手把自己开的标签页关掉，不至于在后台留一堆的孤儿窗口。SSRF 防护刚才用 open 和 navigate 打开网址，看着什么链接都能打开，其实不然。之前学习工具箱时就提到过，web_fetch 自带一套 SSRF 防护：凡是能由外部输入决定去访问哪个地址的工具，都得防着被人诱导去访问内网。浏…

给小龙虾配齐工具箱：OpenClaw 的工具体系（二）
type: item
date: 2026-05-25T02:21:00+00:00
description: 上一篇我们把 OpenClaw 的内置工具分成了八大类，并且把前四类一一看了一遍，这一篇书接上文，继续后面四类的学习。设备控制这一类管的是小龙虾对配对进来的设备 / node 的操控，两个工具配合着用：nodes 负责发现、配对、指定要遥控哪个 node，以及对它干什么（拍照、录屏、取定位、推送通知等），canvas 负责在某个 node 的屏幕上画东西（显示页面、跑 JS 脚本、截屏画面）。我们先来看 nodes 的参数，它和昨天学习的 message 类似，也是个按 action 分派的工具：完整的 action 一共 19 个，每个 action 还各带一批专属的细分参数（定位精度、通知优先级、录屏的 screen 选择等等），一张表列不下，用到时再查文档即可。这 19 个动作按功能分组大致如下：图里的分组命名，正好牵出我们之前曾学习过的一组概念：设备（Device） 和 节点（Node）。一台 Mac、一部手机这样的物理设备，在 OpenClaw 里其实分两层看：Device 是它的「身份证」，管它是谁、准不准进来；Node 是它以 role: node 连进来之后对外贴出的「能力清单」，管它能被调用哪些功能。nodes 这个工具名字虽叫 nodes，实际上横跨了这两层：图里的「配对审批」和「设备信息」管的是 Device（这台设备是谁、批不批），「摄像头 / 屏幕 / 定位 / 通知 / 自定义命令」几组才是真正在调 Node 的能力。说白了它就是一台「设备遥控器」，node 参数选遥控哪一台，action 选按哪个键。接下来，我们再来看下 canvas 工具的核心参数：这个工具其实我们之前在 macOS app 篇里已经见过了，那块吸在菜单栏边、给小龙虾当可视化工作区的面板，就是它的画布；它和 nodes 一样，每个 action 底层都对应一条 canvas.* 节点命令，发给目标 node 上的画布去执行。运行时管理这一类是小龙虾用来管理自身运行时的两个工具：cron 管定时任务（比如每天早上八点提醒我打卡），对应前面 cron / heartbeat 那篇；而 gateway 则能查看和修改自己的运行时配置，甚至重启和自我升级。我们先看 cron，它的参数也是按 action 分派，外加一个嵌套的 job 对象，核心部分如下：job 对象里最关键的是 schedule（什么时候跑）和 payload（跑的时候干什么）：光看参数还是有点抽象，举个具体例子：开头说的「每天早上八点提醒打卡」，用 add 建出来就是这么一个 job：{ action: "add", job: { name: "打卡提醒", schedule: { kind: "cron", expr: "0 8 * * *", tz: "Asia/Shanghai" }, // 何时跑：每天上海时间 8:00 payload: { kind: "systemEvent", text: "提醒主人：该打卡了" }, // 跑时干啥：推一条提醒 delivery: { mode: "announce" }, // 结果去哪：播报到当前通道 }, }这个 job 拆开看其实就三块：先是 schedule，它指的是任务什么时候跑，有三种写法：at 是「某个时间点只跑一次」，给一个具体时刻就行（比如下周一上午十点），适合一次性的提醒；every 是「每隔多久跑一次」，给一个毫秒间隔（比如每两小时一次）；cron 最灵活，用一条 cron 表达式配上时区来描述周期，上面例子里的 0 8 * * * 加 Asia/Shanghai，意思就是「每天上海时间早上八点」。再是 payload，它说的是到点之后具体干什么，分两种。一种是 systemEvent，只往通道里推一条提醒文本，内容写在 text 字段里，上面打卡那个例子就是它，跑起来无非是按时吼你一嗓子。另一种是 agentTurn，到点会触发一整轮完整的 agent 任务，要它干的事写在 message 字段里。比如把上面的 payload 换成：payload: { kind: "agentTurn", message: "拉一下我今天的日程，挑出最重要的三件发我" }到了八点，小龙虾就不只是提醒你，而是真的去翻日程、筛选、再把结果发过来。注意这两种类型的字段不一样：systemEvent 用 text，agentTurn 用 message。最后是 delivery，它管干完之后结果往哪儿送，有三档：none 是不往外送，任务在后台默默跑完就算（常配那些不需要回话的任务）；announce 是把结果播报到当前通道，你在 Telegram、飞书里就能直接收到，也是最常用的一档；webhook 则是把结果 …

给小龙虾配齐工具箱：OpenClaw 的工具体系
type: item
date: 2026-05-22T00:54:00+00:00
description: 在上一篇的最后，我们举了一个场景，对着手机喊一句让小龙虾查附近的咖啡馆，它先调 location.get 拿坐标、再用 web 搜索查咖啡馆、最后把结果合成语音念回来。这一连串动作里，location.get、web 搜索、语音合成，其实都是 OpenClaw 里的工具。除此之外，小龙虾的工具箱里还有很多内置工具，我们今天就来系统的学习一遍。工具 vs. 技能 vs. 插件在正式学习之前，我们先看下 OpenClaw 里三个容易混淆的概念：工具（Tool）是 agent 实际调用的东西。一个工具就是一个带类型签名的函数，比如 exec、browser、web_search、message。OpenClaw 自带一批内置工具，还能通过插件注册新工具。对模型来说，工具是以结构化函数定义的形式发给模型 API 的。技能（Skill）教 agent 什么时候、怎么用工具。它是一份注入到 system prompt 的 SKILL.md，给 agent 提供上下文、约束和分步指引。插件（Plugin）把这些打包到一起。一个插件可以同时注册通道、模型 provider、工具、技能等任意组合。三者的关系大致是这样：简单说，工具是手脚，技能是手册，插件是打包。今天这篇只讲最底下那层手脚，手册和打包留到后面再讲。内置工具清单按官方文档，下面这个表格列出了 OpenClaw 所有的内置工具：工具干什么exec跑 shell 命令process管后台进程code_execution跑沙箱化的远程 Python 分析browser控制 Chromium 浏览器（导航、点击、截图）web_search搜网页x_search搜 X 帖子web_fetch抓页面内容read读工作区内的文件write写工作区内的文件edit编辑工作区内的文件apply_patch多块（multi-hunk）文件补丁message把消息发到各个通道canvas驱动 node 上的 Canvas（演示、求值、截屏）nodes发现并指定已配对的设备cron管定时任务gateway查看、改配置、重启或自更新网关image分析图片image_generate生成或编辑图片music_generate生成音乐video_generate生成视频tts一次性文本转语音memory_search按语义/关键词检索记忆笔记memory_get读取指定记忆文件或行区间sessions_*列出会话、读会话历史、给别的会话发消息、派生子会话subagents子 agent 编排agents_list列出可用 agentsession_status轻量的 /status 式回读，可临时切换会话模型这张表看着很多，其实可以分为八类：命令执行、网络访问、文件读写、消息收发、设备控制、运行时管理、媒体生成、子 agent 编排。下面我们一类一类来看，重点挑前面几篇文章里反复出现过的工具展开讲。每个工具我都先附一张参数表，直接从 OpenClaw 源码里的 schema 定义整理出来的，列清楚它收哪些参数、每个参数什么意思、哪些必填。看懂一个工具的参数表，基本就摸清了它能干什么、边界在哪。你会发现，决定一个工具好不好用、安不安全的，往往不是它能干什么，而是那些藏在参数里的细节。第一次接触的话，不用每个参数都背下来，先扫一眼有个印象，建立「哪类活找哪个工具」的直觉就够了。命令执行这一类管的是「让小龙虾运行命令或代码」，一共三个工具：exec 在你的机器上跑 shell 命令，几乎是整个工具箱里用得最多的一个：装依赖、跑测试、提交代码，几乎都靠它；process 和 exec 配对，专管那些转入后台还没退出的命令；code_execution 则只需你说清「要算什么」，由 xAI 的 Grok 自己写 Python 并在远程沙箱里跑，全程不碰你的机器。下面一个个看它们的参数。先看 exec 的参数定义：一眼看过去，必填的只有 command 一个，丢一条命令进去就能跑。不过剩下那一长串可选参数，才是这个工具真正的门道所在：跑在前台还是后台？ 默认前台跑，命令执行完结果就回来了。但有些命令（比如启动一个开发服务器）会一直挂着不退出，总不能让小龙虾干等着。所以 OpenClaw 设了个超时：默认跑超过 10 秒（参数 yieldMs）就自动转后台，先把控制权交还给对话，命令继续在后台跑。你也可以一上来就 background: true 直接丢后台。命令转后台之后，再想看它输出了什么、给它敲个回车或粘点东西进去，就得换用配套的 process 工具，它的 action 有 poll（看最新输出）、send-keys（发按键）、submit（回车）、paste（粘贴）四种。可以把这一对工具想象成：exec 开一个会话…

把小龙虾装进口袋：iOS / Android Node 配对
type: item
date: 2026-05-21T00:39:00+00:00
description: 在上一篇里，我们装好并体验了 macOS app，它横跨 operator 和 node 两类身份，既能当控制台，又能把这台 Mac 的屏幕、摄像头、命令行暴露给 agent。今天我们把目光转到手机上：iOS Node 和 Android Node，两个纯 node 客户端。它们身上没有 operator 那半边，唯一的任务就是当小龙虾的远程感官，把手机的麦克风、摄像头、GPS、屏幕暴露给家里的 agent 调用。配合上一篇讲的 Gateway 远程访问，出门在外也能随时唤起家里的小龙虾。配对 iOS Node在 Gateway 协议里 iOS Node 的 role 是 node，走的是和 Telegram bot 那种 channel 完全不同的握手路径。channel 那套叫 DM 配对（DM pairing），陌生人第一次给 bot 发消息时，bot 回一段 8 位的一次性 pairing code，你在 CLI 里 openclaw pairing approve 批准这个码，它管的是谁能跟 bot 说话。iOS Node 这套叫 设备配对（device pairing），手机带着自己的 ED25519 设备身份连上 Gateway 的 WebSocket，Gateway 挂起一条 pending 请求（用 requestId 标识），你在 CLI 里 openclaw devices approve 批准这台设备，Gateway 随即签发一个会轮换的 token，它管的是哪台设备能进网络。这套握手流程里还有另一组容易混的词，Node 和 Device，我们先了解下它们的区别。Node vs. Device明明连进来的是一台 Node（iOS 这台手机），批准用的却是 openclaw devices approve，查状态又换回 openclaw nodes status。它俩在 OpenClaw 里是上下两层：Device（设备身份） 管这是谁。可以把它理解成这台设备的身份证。设备自己生成一对 ED25519 密钥，由公钥算出一个 deviceId 当唯一编号，再配一份授权记录，记下它被谁批准过、能用哪些角色。Gateway 靠它做身份识别和准入：没见过的 deviceId 必须经操作员批准，批准后按角色发 token（这个 token 可以随时换发新的、把旧的作废，也可以直接吊销，但不管怎么换都不能超出配对时批准的 role / scope 范围）。一个 Device 还能同时持有多个角色的 token，比如上一篇那台 Mac，既是 operator 又是 node。Node（能力主机） 管它能干啥。可以把它理解成这台设备对外贴出的一张能力清单。设备以 role: node 连进 Gateway 时，会报上自己的 caps（有哪些硬件能力，比如摄像头、屏幕、定位、麦克风）和 commands（允许别人调用的命令白名单，像 camera.snap、canvas.snapshot、location.get）。Gateway 把它记进一张运行时的 node 名册，别的客户端就能用 node.invoke 把命令派给它执行。拿到 app值得注意的是，iOS app 暂时还没上架 App Store，官方给它贴的标签是「极早期 alpha」，因此把它当成尝鲜，别当成生产工具。能拿到 app 的途径只有两个：TestFlight 内测：如果你在内测名单里自己编译：根据官方文档，从源码用 Xcode 编译，我走的就是这条开发路径首先确认几个前置工具装齐了：Xcode 16+：低于这个版本工程文件可能打不开Node 的 pnpm：OpenClaw 整个仓库的包管理器，iOS 这边的脚本也靠它xcodegen：这个工程不把 .xcodeproj 提交进仓库，而是用一份 YAML 描述、靠 xcodegen 现场生成，所以这步不能省（brew install xcodegen 装一下）Xcode 里配好 Apple 开发者签名：哪怕只是个人免费 Apple ID 也行，但得先在 Xcode 的 Settings → Accounts 里登进去一切就绪之后，在仓库根目录依次运行如下命令：$ pnpm install # 装 monorepo 依赖 $ ./scripts/ios-configure-signing.sh # 给你这台机器生成一套唯一的本地 bundle ID $ cd apps/ios $ xcodegen generate # 用 YAML 现场生成 OpenClaw.xcodeproj $ open OpenClaw.xcodeproj # 用 Xcode 打开这几步等价于一条 pnpm ios:open 快捷命…

把小龙虾钉在菜单栏：OpenClaw 的 macOS app（二）
type: item
date: 2026-05-20T08:56:00+00:00
description: 上一篇我们把 OpenClaw 的 macOS app 装上了，弄清了它「菜单栏伴侣」的定位，看了它作为 operator 那半边的功能，结尾停在了它的另一重身份上：它启动后还会以 role: node 连进同一个 Gateway，把自己的一组 caps 和 commands 报上去、登记进 node 注册表，别的客户端就能用 node.invoke 把命令转过来。这一篇就接着上一篇的结尾，详细学习下 macOS app 作为 node 的几个能力 —— Canvas 画板、摄像头与录屏、system.run 和 Exec approvals、Voice Wake 与 Push-to-talk，最后再把它们串起来，跑一个实战的例子。CanvasCanvas 是 macOS app 内嵌的一块 WKWebView 面板，专门给 agent 当轻量可视化工作区用，渲染 HTML/CSS/JS、A2UI、小的交互 UI。它的状态存在 ~/Library/Application Support/OpenClaw/canvas/<session>/ 下，面板通过一个自定义 URL scheme openclaw-canvas://<session>/<path> 来读这些文件（这样不用起 loopback server，也天生挡掉了目录穿越）。如果某个 session 根目录下没有 index.html，app 会显示一个内置的脚手架页。面板本身是无边框、可缩放的，吸在菜单栏附近（或鼠标位置），按 session 记住大小和位置，本地 canvas 文件一改它自动重载，同一时刻只显示一个 Canvas 面板。可以在 Settings 配置页面的 Allow Canvas 选项来整个关掉，关掉之后所有 canvas 命令返回 CANVAS_DISABLED。平时是 agent 在对话里通过 Gateway WebSocket 驱动 Canvas，但 CLI 也能手动操作来验证。最直接的就是让它弹出来：$ openclaw nodes canvas present --node <node-id>跑完之后，菜单栏附近会弹出一块画板，但里面几乎是空白的：这个空白页是正常现象，canvas.present 只管「把面板显示出来」，不管往里塞内容。macOS 第一次打开 Canvas 会跳到 Gateway 托管的 A2UI host 页，这个页面本身就是个空壳：它只是 A2UI 的渲染运行时，在 agent 真正推内容之前页面上当然什么都没有。注意：画板里有时会显示一行 Unauthorized 错误。这不是 macOS 的 TCC 权限问题，而是 WebView 去拉 A2UI host 页时 token 失效了（token 有 10 分钟 TTL，Gateway 重启过、连接断过、或长时间没动过画板都会让它过期）。可以从菜单栏退出 app 再重开，让它重新握手拿到新鲜的 token 就好了；要么在 Gateway 配置里把 canvasHost.enabled 设成 false 彻底关掉这个托管页（直接的 canvas.* 命令和本地脚手架页照常用）。这里顺便介绍下 A2UI。A2UI（Agent-to-UI） 是一套声明式的 UI 协议，agent 不用手写 HTML，而是发一串 JSONL 消息来描述「界面长什么样」：用 Column、Text 这类组件搭出一棵界面树，配上 usageHint（标题 / 正文之类）这种语义提示，由客户端的 A2UI 渲染器统一把它画成真正的控件；用户在界面上的点击、输入再作为事件反向回传给 agent，是双向的。OpenClaw 把这个渲染器（A2UI host）做成了 Canvas 面板里的内嵌页，上面的 canvas.present 默认跳过去的就是它。目前 Canvas 只认 A2UI v0.8 的 server→client 消息（beginRendering / surfaceUpdate / dataModelUpdate / deleteSurface），v0.9 的 createSurface 还不支持。除了 canvas.present 命令，也可以用 canvas.navigate 打开一个本地 canvas 路径，或者 http(s) 或 file:// URL，或者直接传 "/" 返回本地脚手架页：$ openclaw nodes canvas navigate "/" --node <node-id>显示如下：如果想看到自定义内容，可以用 canvas.a2ui push 推一段 A2UI 协议：$ openclaw nodes canvas a2ui push --text …