# openClaw_plugin **Repository Path**: 1024335892/openClaw_plugin ## Basic Information - **Project Name**: openClaw_plugin - **Description**: openClaw的插件开发及其资料收集 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-05-14 - **Last Updated**: 2026-05-14 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Plugin 开发脚手架:从搭建项目结构开始(基本骨架) ## 初始化项目 ``` pnpm init pnpm i openclaw typescript pnpm i @types/node ``` ## 最小结构项目结构 ``` my-openclaw-plugin/ ├── openclaw.plugin.json # 清单文件 ├── src/ │ └── index.ts # 入口文件 ├── package.json └── tsconfig.json ``` ## `openclaw.plugin.json` ``` { "name": "my-plugin", "version": "1.0.0", "description": "我的第一个 OpenClaw 插件", "entry": "dist/index.js" } ``` ## `src/index.ts` ``` import { definePluginEntry } from "openclaw/plugin-sdk/core"; ​ export default definePluginEntry({ id: "my-plugin", name: "My Plugin", ​ register(api) { // 在这里注册能力(工具、Hook、HTTP 路由等) console.log("Plugin registered!"); }, ​ activate(api) { // 在这里启动后台服务(可选) console.log("Plugin activated!"); } }); ``` 这里的 register 和 activate 两阶段生命周期 - Register 阶段:Gateway 启动时调用。在这里通过 api.register\*() 方法声明能力,注册工具、注册 Hook、注册 HTTP 路由等。不能在 register 中做网络请求或启动服务,因为此时其他 Plugin 可能还没注册完毕,依赖关系可能不完整。 - Activate 阶段:所有 Plugin 都注册完毕后调用。在这里启动后台服务、建立外部连接。此时注册表是完整快照,你可以安全地检查其他 Plugin 是否存在。 ## `tsconfig.json` ``` { "compilerOptions": { "target": "ES2022", "module": "commonjs", "outDir": "./dist", "rootDir": "./src", "strict": true, "esModuleInterop": true, "declaration": true, "types": ["node"] }, "include": ["src/**/*"] } ``` ## 编译 ``` npx tsc # 应该在 dist/ 目录下生成 index.js ``` # 注册自定义工具:让 Agent 拥有新能力 Agent 的核心价值就在于“能执行操作”,而操作的载体就是工具。Agent 在推理循环中,LLM 输出“我要调用某个工具”的指令,Agent Loop 执行该工具并将结果返回给 LLM。你通过 registerTool 注册的工具,就会出现在 LLM 的可用工具列表中,LLM 可以根据用户请求主动调用它。 ## 企业知识库查询工具demo ``` import { definePluginEntry } from "openclaw/plugin-sdk/core"; ​ export default definePluginEntry({ id: "kb-search", name: "Enterprise Knowledge Base", ​ register(api) { api.registerTool( { name: "kb_search", description: "搜索企业知识库中的文档和FAQ", inputSchema: { type: "object", properties: { query: { type: "string", description: "搜索关键词" }, category: { type: "string", enum: ["技术", "产品", "运营", "HR"], description: "知识分类(可选)" } }, required: ["query"] }, async execute(params) { const { query, category } = params; // 调用企业知识库 API const response = await fetch( `https://kb.company.com/api/search?q=${encodeURIComponent(query)}&cat=${category || ""}`, { headers: { Authorization: `Bearer ${process.env.KB_API_TOKEN}` } } ); const results = await response.json(); return { type: "text", text: results.map((r: any) => `${r.title}\n${r.summary}` ).join("\n\n") }; } }, { names: ["kb_search"] } ); } }); ``` > 字段分析 - inputSchema 工具的参数定义,这是一个标准的 JSON Schema。LLM 通过这个 Schema 知道工具需要什么参数、参数的类型和约束。 - description 字段,LLM 依靠它来理解每个参数的含义,从而正确地从用户请求中提取参数。 | 字段 | 作用 | 实践建议 | | ----------- | -------- | ------------------------------------------ | | type | 参数类型 | 尽量用 string/number/boolean,避免嵌套对象 | | description | 参数描述 | 写清楚这个参数是什么,有什么限制 | | enum | 枚举值 | 限定取值范围,帮助 LLM 做选择 | | required | 必填参数 | 只标记真正必须的参数 | - execute 是工具的执行逻辑,这是工具被调用时实际执行的代码。它接收 LLM 传入的参数,执行业务逻辑(这里是调用企业 API),然后返回结果。返回值的格式是 { type: "text", text: "..." },这是最常用的文本返回格式。返回的文本会被传回 LLM,LLM 据此生成最终回复。 - { names: ["kb_search"] } 工具名注册,第二个参数指定了工具在系统中注册的名称列表。LLM 通过这个名称来调用工具。同时,这个名称也会出现在工具策略管道中,你可以在配置中用 allow/deny 控制哪些 Agent 能使用它。 > 工具注册环节需要几个事项 - 工具名的命名规范:使用小写字母和下划线,不使用连字符。因为工具名标准化机制会把连字符转为下划线(apply-patch → apply_patch),直接用下划线可以避免混淆。 - execute 函数的错误处理:如果 execute 抛出异常,Agent Loop 会捕获异常并将错误信息返回给 LLM,LLM 可能据此决定重试或放弃。所以建议在 execute 中做好错误处理,返回有意义的错误消息。 - 环境变量的使用:process.env.KB_API_TOKEN 直接从环境变量读取 API Token。安全审计会检查配置中是否有明文密钥。 # 使用 Hook 增强 Agent 行为:主动参与推理过程 OpenClaw 有 25 个生命周期 Hook,四种执行模式(Void、Modifying、Claiming、Sync)。Plugin 通过 api.on() 注册 Hook handler,在 Agent 处理消息的各个环节插入自定义逻辑。在所有 25 个 Hook 中,以下三个是开发者最常用的: | Hook | 触发时机 | 典型用途 | | ------------------- | ------------- | -------------- | | before_prompt_build | Prompt 构建前 | 注入上下文信息 | | before_tool_call | 工具调用前 | 拦截危险操作 | | message_sending | 消息发送前 | 内容审核/过滤 | > 在 Plugin 中同时注册这三个 Hook ``` register(api) { // Hook 1: 在 Prompt 构建前注入企业上下文 api.on("before_prompt_build", async (ctx) => { return { prependSystemContext: ` [企业上下文] 当前用户部门: ${await getUserDept(ctx.peer)} 公司最新公告: ${await getLatestAnnouncement()} ` }; }); // Hook 2: 在工具调用前进行安全检查 api.on("before_tool_call", async (ctx) => { if (ctx.toolName === "exec" && ctx.params.command?.includes("rm")) { return { block: true, blockReason: "安全策略:禁止执行删除命令" }; } return {}; }); // Hook 3: 在消息发送前进行内容审核 api.on("message_sending", async (ctx) => { const hasViolation = await contentModeration(ctx.text); if (hasViolation) { return { cancel: true }; } return {}; }); } ``` - Hook 1:before_prompt_build(Modifying Hook),这个 Hook 在 Agent 构建发给 LLM 的 Prompt 之前触发。你可以通过返回 prependSystemContext 来在 System Prompt 前面追加内容。Agent 的行为很大程度上取决于 System Prompt 中的上下文。如果你能在 Prompt 构建前动态注入“当前用户是销售部的”“公司刚发布了新产品”这类信息,Agent 的回复就会更加精准和个性化。 - Hook 2:before_tool_call,这个 Hook 在 LLM 决定调用某个工具后、实际执行之前触发。你可以检查工具名和参数,决定是否拦截。返回 { block: true, blockReason: "..." } 会阻止工具执行。第 21 课讲过,before_tool_call 的合并规则是 block 任一为 true 即生效,这是一票否决机制。即使其他 Plugin 允许执行,只要你的 Plugin block 了,工具调用就会被阻止。 - Hook 3:message_sending,这个 Hook 在 Agent 准备发送回复之前触发。你可以检查回复内容,决定是否取消发送或修改内容。返回 { cancel: true } 会取消消息发送。合并规则是“后覆盖前”,最后执行的 handler 有最终决定权。实际通常应用在敏感词过滤、内容合规检查、数据脱敏业务场景。比如 Agent 在回复中不小心包含了客户的手机号码,内容审核 Hook 可以自动检测并阻止发送或脱敏处理。 > Hook 的 priority 设置 当你知道系统中有其他 Plugin 也注册了同一个 Hook,并且你的逻辑需要在它们之前或之后执行时。比如安全检查应该设高优先级(先拦截危险操作),而日志记录可以设低优先级(最后执行,记录最终状态)。需要设置 Hook 的 priority,Modifying Hook 的 handler 按 priority 降序排序执行(数字越大越先执行,默认 priority = 0)。 ``` api.on("before_tool_call", async (ctx) => { // 安全检查逻辑 return { block: true, blockReason: "..." }; }, { priority: 100 }); // 高优先级,先于其他 handler 执行 ``` > 注册 Hook 有两种方式 api.on() 和 api.registerHook()。推荐使用 api.on(),因为 TypeScript 会根据 hookName 自动推导参数类型,减少类型错误。 ``` // ✅ 推荐:api.on() — TypeScript 自动推导 ctx 类型 api.on("before_tool_call", async (ctx) => { ctx.toolName; // TypeScript 知道这是 string ctx.params; // TypeScript 知道这是 object }); // ❌ 不推荐:registerHook() — 需要手动指定类型 api.registerHook({ hookName: "before_tool_call", handler: async (ctx: any) => { /* ... */ } }); ``` # 编译、安装与测试:让 Plugin 跑起来 ## 编译 ``` # 编译 TypeScript → JavaScript npx tsc # 验证输出 ls dist/ # 应该看到 index.js 和 index.d.ts ``` ## 安装到 OpenClaw,Plugin 有两种安装方式 > 方式一:复制到插件目录 ``` # 创建插件目录 mkdir -p ~/.openclaw/plugins/my-plugin # 复制编译产物和清单文件 cp -r dist/ ~/.openclaw/plugins/my-plugin/ cp openclaw.plugin.json ~/.openclaw/plugins/my-plugin/ ``` > 方式二:在配置中指定路径 ``` # openclaw.yaml plugins: load: paths: - /path/to/my-openclaw-plugin # 指向项目根目录 ``` 方式二的好处是,不需要每次修改代码后手动复制,只需要重新编译(npx tsc),然后重启 OpenClaw。在开发迭代阶段更方便。 > 验证加载 Plugin ``` # 查看已加载的插件列表 openclaw plugins list # 你应该看到类似输出: # my-plugin (v1.0.0) — 我的第一个 OpenClaw 插件 # Tools: kb_search # Hooks: before_prompt_build, before_tool_call, message_sending ``` # 测试 Plugin 功能 ## 测试工具注册 在聊天中输入:“帮我搜索知识库中关于休假政策的文档”。预期行为:Agent 应该调用 kb_search 工具,传入 query: "休假政策",然后返回知识库的搜索结果。 ## 测试 Hook 效果 - before_prompt_build 测试:询问 Agent “你知道我是哪个部门的吗?”,如果 Hook 正确注入了部门信息,Agent 应该能回答。 - before_tool_call 测试:让 Agent 执行 rm /tmp/test,应该被 Hook 拦截,Agent 回复安全策略提示。 - message_sending 测试:如果配置了内容审核,让 Agent 生成包含敏感词的内容,消息应该被取消或过滤。 # 总结整个流程 | 核心要点 | 核心内容 | | --------------- | ---------------------------------------------------------------------------- | | Plugin 项目结构 | 4 个核心文件:openclaw.plugin.json + index.ts + package.json + tsconfig.json | | 两阶段生命周期 | Register(声明能力)→ Activate(启动服务),大部分只需 Register | | 工具注册 | api.registerTool() — inputSchema 定义参数,execute 定义逻辑 | | Hook 接入 | api.on() — before_prompt_build / before_tool_call / message_sending | | 安装方式 | 复制到 ~/.openclaw/plugins/ 或配置 plugins.load.paths |