爱折腾-创客智造实验室 - OpenClaw-AI助手入门教程
说明： 本文介绍了在独立服务器上通过docker方式部署OpenClaw智能助手并接入国产大模型和飞书机器人的完整流程。 主要内容包括： 1）通过docker命令行快速安装OpenClaw； 2）配置国际/国产大模型（如DeepSeek）； 3）安装飞书插件并配置应用凭证； 4）设置飞书机器人权限与事件。 5）设置nginx反向代理访问 部署完成后，用户可通过飞书与AI助手交互，支持多轮对话和上下文记忆功能。 1️⃣ 环境准备：Docker 及 Docker Compose - 安装 Docker 官方文档：[https://docs.docker.com/engine/install/](https://docs.docker.com/engine/install/) - Docker Compose 一般随 Docker Engine 一同安装，验证：`docker compose version` 2️⃣ 获取 OpenClaw 源码并部署（Docker 方式） git clone https://github.com/openclaw/openclaw.git cd openclaw ./docker-setup.bash # 交互式部署脚本 2.1 交互式配置要点 步骤 问题 选择/输入 说明 2.1 理解风险，安全问题 Yes 接受安全须知 2.2 Onboarding mode QuickStart 快速安装模式 2.3 Model / auth provider skip for now 稍后手动配置 DeepSeek 2.4 Filter models by provider all provider 不影响，后期跳过 2.5 Select Channel skip for now 或 feishu 飞书稍后配置 2.6 Configure skills now No 暂不需要技能包 2.7 Hook: session-Memory 选中（空格） 启用多轮对话记忆 2.8 等待部署完成 - 记录输出的 onboarding link（内含访问令牌） 2.2 部署完成后的操作 # 停止默认启动的容器，并以守护模式重新启动 docker compose down docker compose up -d # 查看运行日志 docker compose logs -f #OpenClaw 配置文件位置（宿主机）： cat ~/.openclaw/openclaw.json # 所有配置持久化在此 3️⃣ DeepSeek 模型接入 3.1 获取 DeepSeek API 密钥 注册/登录 DeepSeek 开放平台 创建 API Key，格式：sk-xxxxxxxxxxxx ⚠️ 重要：此密钥不可公开，一旦泄露请立即删除并重新生成 3.2 编辑配置文件，添加 DeepSeek 提供商 vim ~/.openclaw/openclaw.json #在 models 节点下添加 providers 定义，并设置默认模型： json5 { "models": { "mode": "merge", "providers": { "custom-api-deepseek-com": { "baseUrl": "https://api.deepseek.com", "apiKey": "sk-你的全新密钥", // ⚠️ 替换为最新密钥 "api": "openai-completions", "models": [ { "id": "deepseek-reasoner", // 推理模型 "name": "DeepSeek Reasoner", "reasoning": true, "contextWindow": 64000, "maxTokens": 8192, "input": ["text"] }, { "id": "deepseek-chat", // 快速对话模型 "name": "DeepSeek Chat", "contextWindow": 64000, "maxTokens": 8192, "input": ["text"] } ] } } }, "agents": { "defaults": { "model": { "primary": "custom-api-deepseek-com/deepseek-reasoner" // 默认使用模型 } } } } #保存文件。 3.3 重启网关使配置生效 docker compose restart openclaw-gateway 3.4 测试模型是否接入成功 # 方法一：通过主代理测试（推荐） docker compose exec openclaw-gateway \ node dist/index.js agent --agent main --message "你好，请用一句话介绍自己" #成功标志：回复内容包含“DeepSeek”、“深度求索”等字样。 3.5 可选：更换默认模型以提升速度 docker compose exec openclaw-gateway \ node dist/index.js config set agents.defaults.model.primary custom-api-deepseek-com/deepseek-chat docker compose restart openclaw-gateway 3.6 常用诊断命令 # 整体健康检查 docker compose exec openclaw-gateway node dist/index.js doctor # 查看当前模型配置 docker compose exec openclaw-gateway node dist/index.js config get agents.defaults.model 4️⃣ 飞书机器人集成 4.1 安装飞书插件并补充依赖 # 安装插件（推荐 @xzq-xu/feishu） docker compose exec openclaw-gateway \ node dist/index.js plugins install @xzq-xu/feishu # 进入插件目录，安装生产依赖（跳过 postinstall 避免 SSH 问题） docker compose exec openclaw-gateway bash -c \ "cd /home/node/.openclaw/extensions/feishu && npm install --production --ignore-scripts" # 重启网关 docker compose restart openclaw-gateway 4.2 飞书开发者后台准备工作 登录 飞书开放平台 创建企业自建应用 → 填写名称，创建 添加能力 → 机器人（点击“添加”） 记录 App ID 和 App Secret（在“凭证与基础信息”中） 4.3 配置飞书通道凭证与策略 # 启用飞书通道 docker compose exec openclaw-gateway \ node dist/index.js config set channels.feishu.enabled true # 填入凭证 docker compose exec openclaw-gateway \ node dist/index.js config set channels.feishu.appId 'cli_你的AppID' docker compose exec openclaw-gateway \ node dist/index.js config set channels.feishu.appSecret '你的AppSecret' docker compose exec openclaw-gateway \ node dist/index.js config set channels.feishu.domain 'feishu' # 国内飞书 # 免配对策略（解决 pairing required） docker compose exec openclaw-gateway \ node dist/index.js config set channels.feishu.dmPolicy 'open' docker compose exec openclaw-gateway \ node dist/index.js config set channels.feishu.groupPolicy 'open' docker compose exec openclaw-gateway \ node dist/index.js config set channels.feishu.requireMention true # 群聊需 @ # 性能优化：限制上下文长度 & 关闭工具调用（可选） docker compose exec openclaw-gateway \ node dist/index.js config set channels.feishu.contextLimit 8000 docker compose exec openclaw-gateway \ node dist/index.js config set channels.feishu.toolsEnabled false # 重启网关 docker compose restart openclaw-gateway 4.4 飞书后台最终配置（长连接模式） 事件订阅 → 订阅方式选择 「使用长连接接收事件」 → 保存 添加事件 → 搜索 im.message.receive_v1 → 确认添加 权限管理 → 批量导入以下权限： json { "scopes": { "tenant": [ "contact:contact.base:readonly", // 获取用户姓名（解决 99991672 错误） "im:message", "im:message.group_at_msg:readonly", "im:message.p2p_msg:readonly", "im:message:send_as_bot", "im:resource" ] } } 版本管理与发布 → 创建版本 → 填写版本号、可见范围 → 提交发布 （若为企业内部应用，可直接审批） 4.5 验证飞书长连接及消息收发 # 实时查看飞书相关日志 docker compose logs -f openclaw-gateway | grep feishu 成功标志： text [feishu] connected to Feishu WebSocket [feishu] Bot identity: openclaw (ou_xxxx) 打开飞书客户端，搜索你的机器人名称 发送任意消息，应收到 DeepSeek 的智能回复 完整openclaw.json参考 $ cat ~/.openclaw/openclaw.json { "meta": { "lastTouchedVersion": "2026.2.9", "lastTouchedAt": "2026-02-11T17:06:29.081Z" }, "wizard": { "lastRunAt": "2026-02-11T14:12:20.673Z", "lastRunVersion": "2026.2.9", "lastRunCommand": "onboard", "lastRunMode": "local" }, "models": { "mode": "merge", "providers": { "custom-api-deepseek-com": { "baseUrl": "https://api.deepseek.com", "apiKey": "sk-b5687763851c4cxxxxxxxxxx", "api": "openai-completions", "models": [ { "id": "deepseek-reasoner", "name": "deepseek-reasoner (Custom Provider)", "reasoning": true, "input": [ "text" ], "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 }, "contextWindow": 64000, "maxTokens": 8192 } ] } } }, "agents": { "defaults": { "model": { "primary": "custom-api-deepseek-com/deepseek-chat" }, "models": { "custom-api-deepseek-com/deepseek-reasoner": {} }, "workspace": "/home/node/.openclaw/workspace", "compaction": { "mode": "safeguard" }, "maxConcurrent": 4, "subagents": { "maxConcurrent": 8 } } }, "messages": { "ackReactionScope": "group-mentions" }, "commands": { "native": "auto", "nativeSkills": "auto" }, "hooks": { "internal": { "enabled": true, "entries": { "session-memory": { "enabled": true } } } }, "channels": { "feishu": { "enabled": true, "appId": "cli_a9018xxxxxxxxxx", "appSecret": "TTudUxxxxxxxxxxxxxxxxxxxxx", "domain": "feishu", "dmPolicy": "open", "groupPolicy": "open" } }, "gateway": { "port": 18789, "mode": "local", "bind": "loopback", "auth": { "mode": "token", "token": "6746d7bb7fd9xxxxxxxxxxxxxxxxxxxxxxxxxx" }, "trustedProxies": [ "172.21.0.x/32", "183.2.xxx.xxx" ], "tailscale": { "mode": "off", "resetOnExit": false }, "nodes": { "denyCommands": [ "camera.snap", "camera.clip", "screen.record", "calendar.add", "contacts.add", "reminders.add" ] } }, "plugins": { "entries": { "feishu": { "enabled": true } } } }
5️⃣ 配置反向代理（使 Web 管理端可通过 HTTPS 访问） 5.1 准备域名及 SSL 证书 申请域名（如 openclaw.yourdomain.com），解析到服务器公网 IP 获取 SSL 证书（可自签或使用 Let's Encrypt） 5.2 安装 Nginx 并配置反向代理 nginx配置 server { listen 443 ssl; server_name openclaw.yourdomain.com; ssl_certificate /path/to/cert.pem; ssl_certificate_key /path/to/key.pem; location / { proxy_pass http://127.0.0.1:18789; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } 5.3 配置 OpenClaw 信任代理 IP 编辑 ~/.openclaw/openclaw.json，在 gateway 节点添加： json5 "gateway": { "trustedProxies": ["172.21.0.1/32", "你的代理服务器公网IP"], // ... 其他配置 } #重启网关： docker compose restart openclaw-gateway 5.4 配置自动设备配对策略 #1.访问网站： https://openclaw.yourdomain.com 在Overview->Gateway Token输入框填写生成时候的Token -> 点击connect #2.手动批准： # 查看待批准设备，查看request的ID docker compose exec openclaw-gateway node dist/index.js devices list # 批准指定请求 docker compose exec openclaw-gateway node dist/index.js devices approve <请求ID> #3.刷新网页，就可以看到已经批准了 6️⃣ 性能优化与常见问题 ⚡ 响应速度优化（针对 DeepSeek 回复慢） 问题 解决方案 命令/配置 模型推理慢 换用 deepseek-chat config set agents.defaults.model.primary custom-api-deepseek-com/deepseek-chat 工具调用超时 关闭飞书通道工具 config set channels.feishu.toolsEnabled false 上下文过大 限制飞书上下文 config set channels.feishu.contextLimit 8000 网络延迟 使用国内 DNS 容器内 echo "nameserver 114.114.114.114" > /etc/resolv.conf ❗ 安全红线 DeepSeek API Key 一旦泄露，立即删除并重新生成 配置文件权限收紧： chmod 700 ~/.openclaw chmod 600 ~/.openclaw/openclaw.json #不要在公共文档、日志中明文暴露任何密钥 常用诊断命令汇总 # 整体健康检查 docker compose exec openclaw-gateway node dist/index.js doctor # 查看当前模型配置 docker compose exec openclaw-gateway node dist/index.js config get agents.defaults.model # 查看飞书通道配置 docker compose exec openclaw-gateway node dist/index.js config get channels.feishu # 实时跟踪飞书消息 docker compose logs -f openclaw-gateway | grep -E "feishu|agent" 总结 通过以上步骤，你已成功完成： ✅ Docker 部署 OpenClaw ✅ 接入 DeepSeek（支持 reasoner 和 chat 双模型） ✅ 集成飞书机器人（长连接，免配对） ✅ 配置 HTTPS 反向代理与设备自动批准 现在，你的企业飞书智能助手已正式上线！如有其他问题，欢迎查阅 OpenClaw 官方文档 或社区讨论。
说明： 当任务完成使用指定的邮箱通知 实现方式：使用openclaw的开发能力来编程生成一个自己使用的python脚本即可 步骤1-获取qq邮箱授权码： 登录邮箱并进入设置：在电脑浏览器里打开QQ邮箱官网 (mail.qq.com)，登录后，点击页面顶部的 “设置” 链接，然后选择 “帐户” 选项卡。 找到并开启服务：在“帐户”页面往下拉，找到 “POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务” 这一栏。点击任一服务（比如 “IMAP/SMTP服务”）右侧的 “开启” 按钮。 完成安全验证：这时会弹出一个窗口，要求你验证身份。根据提示，用你绑定该邮箱的手机号发送一条指定短信到指定号码。发送完成后，在电脑上点击 “我已发送” 或类似的确认按钮。 获取并保存授权码：验证通过后，系统就会弹出一串16位的授权码。请务必马上复制并把它保存好（比如记在备忘录里或截图保存），因为这个弹窗关掉后就再也看不到了。如果在第三方客户端设置时还需要，只能重新生成 步骤2-生成发送邮件脚本和配置： 确认邮箱和授权码后，使用openclaw的开发能力来编程生成一个自己使用的python脚本： 使用web-ui的chat窗口，输入如下的参考python脚本，邮箱和授权码： #!/usr/bin/env python3 """ OpenClaw邮件发送系统 支持环境变量配置，敏感信息存储在.env文件中 """ import smtplib import ssl from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart import argparse import os import sys from pathlib import Path def load_env(): """加载环境变量""" env_path = Path(__file__).parent / '.env' # 如果.env文件存在，读取它 if env_path.exists(): with open(env_path, 'r') as f: for line in f: line = line.strip() if line and not line.startswith('#'): key, value = line.split('=', 1) os.environ[key.strip()] = value.strip() # 返回配置字典 config = { 'qq_email': os.getenv('QQ_EMAIL', ''), 'qq_password': os.getenv('QQ_EMAIL_PASSWORD', ''), 'smtp_server': os.getenv('SMTP_SERVER', 'smtp.qq.com'), 'smtp_port': int(os.getenv('SMTP_PORT', '587')), 'default_recipient': os.getenv('DEFAULT_RECIPIENT', ''), 'use_tls': os.getenv('USE_TLS', 'true').lower() == 'true', 'use_ssl': os.getenv('USE_SSL', 'false').lower() == 'true', } return config def send_email(to_email, subject, body, from_email=None, password=None, config=None): """ 通过QQ邮箱SMTP发送邮件 Args: to_email: 收件人邮箱 subject: 邮件主题 body: 邮件内容 from_email: 发件人邮箱（可选） password: QQ邮箱授权码（可选） config: 配置字典（可选） Returns: bool: 发送是否成功 """ if config is None: config = load_env() # 使用配置中的值 smtp_server = config['smtp_server'] smtp_port = config['smtp_port'] use_tls = config['use_tls'] use_ssl = config['use_ssl'] # 如果没有提供发件人邮箱，使用配置中的值 if not from_email: from_email = config['qq_email'] # 如果没有提供密码，使用配置中的值 if not password: password = config['qq_password'] # 验证配置 if not from_email: print("错误：未设置发件人邮箱") print(" 请在.env文件中设置QQ_EMAIL环境变量") return False if not password: print("错误：未设置QQ邮箱授权码") print(" 请在.env文件中设置QQ_EMAIL_PASSWORD环境变量") return False # 创建邮件 message = MIMEMultipart("alternative") message["Subject"] = subject message["From"] = from_email message["To"] = to_email # 添加纯文本和HTML版本 text_part = MIMEText(body, "plain", "utf-8") html_part = MIMEText(f""" {subject} {body} 此邮件由OpenClaw邮件系统自动发送 发送时间: {os.environ.get('SEND_TIME', '')} """, "html", "utf-8") message.attach(text_part) message.attach(html_part) try: # 设置发送时间环境变量（用于HTML模板） os.environ['SEND_TIME'] = os.popen('date').read().strip() print(f"正在发送邮件...") print(f" SMTP服务器: {smtp_server}:{smtp_port}") print(f" 发件人: {from_email}") print(f" 收件人: {to_email}") print(f" 主题: {subject}") # 创建安全连接 context = ssl.create_default_context() if use_ssl: # SSL连接 with smtplib.SMTP_SSL(smtp_server, smtp_port, context=context) as server: server.login(from_email, password) server.sendmail(from_email, to_email, message.as_string()) elif use_tls: # TLS连接 with smtplib.SMTP(smtp_server, smtp_port) as server: server.starttls(context=context) # 启用TLS加密 server.login(from_email, password) server.sendmail(from_email, to_email, message.as_string()) else: # 非加密连接（不推荐） with smtplib.SMTP(smtp_server, smtp_port) as server: server.login(from_email, password) server.sendmail(from_email, to_email, message.as_string()) print(f"邮件发送成功！") return True except smtplib.SMTPAuthenticationError as e: print(f"认证失败: {e}") print(" 请检查QQ邮箱和授权码是否正确") print(" 注意：需要使用授权码而非登录密码") return False except Exception as e: print(f"邮件发送失败: {e}") return False def main(): parser = argparse.ArgumentParser( description="OpenClaw邮件发送系统", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" 示例: %(prog)s --to recipient@example.com --subject "测试" --body "内容" %(prog)s --to default --subject "提醒" --body "记得开会" # 使用默认收件人 环境变量配置: 请创建.env文件并设置以下变量: QQ_EMAIL=your_email@qq.com QQ_EMAIL_PASSWORD=your_authorization_code DEFAULT_RECIPIENT=default_recipient@qq.com """ ) parser.add_argument("--to", required=True, help="收件人邮箱（或使用'default'使用默认收件人）") parser.add_argument("--subject", required=True, help="邮件主题") parser.add_argument("--body", required=True, help="邮件内容") parser.add_argument("--from", dest="from_email", help="发件人邮箱（默认使用QQ_EMAIL环境变量）") parser.add_argument("--password", help="QQ邮箱授权码（默认使用QQ_EMAIL_PASSWORD环境变量）") parser.add_argument("--config", help="配置文件路径（默认使用当前目录下的.env文件）") args = parser.parse_args() # 加载配置 config = load_env() # 处理收件人 to_email = args.to if to_email.lower() == 'default': to_email = config['default_recipient'] if not to_email: print("错误：未设置默认收件人") print("请在.env文件中设置DEFAULT_RECIPIENT环境变量") sys.exit(1) print(f"使用默认收件人: {to_email}") success = send_email( to_email=to_email, subject=args.subject, body=args.body, from_email=args.from_email, password=args.password, config=config ) sys.exit(0 if success else 1) if __name__ == "__main__": main() openclaw应该会自动根据这个参考脚本，邮箱和授权码，自动帮你配置好这个邮箱并给出测试报告 还可以配置一个定时提醒，比如在web-ui的chat窗口，发指令：增加每日9点的起床提醒推送的邮箱 总结： openclaw的自动化的确是目前看到最好的，能自动完成很多任务，后面再继续探索。
说明 什么是openclaw-mcp-adapter？ 在深入配置之前，我们先理解一下openclaw-mcp-adapter是什么。 它是OpenClaw的一个插件，作用是把MCP服务器的工具转换为OpenClaw原生Agent工具。 简单来说，以前你要通过命令行调用MCP服务器，现在只要配置好mcp-adapter，OpenClaw启动时就会自动连接MCP服务器、发现所有工具，并注册成AI可以直接调用的“一等公民”工具。比如你配置了一个计算器MCP，OpenClaw里就会出现calculator工具，AI可以直接使用。 githu地址 https://github.com/androidStern-personal/openclaw-mcp-adapter mcp-adapter的核心优势： 自动发现工具：连接服务器后自动调用listTools()获取所有工具 原生集成：工具名称带服务器前缀（如myserver_toolname），避免命名冲突 自动重连：连接断开后下次调用自动重连 目前这个插件适合http和stdio的mcp的插件 方法1-让openclaw自动安装: 测试web-ui的chat发指令： 安装openclaw-mcp-adapter插件，https://github.com/androidStern-personal/openclaw-mcp-adapter。 让openclaw自己想办法实现安装 方法2-命令安装 我的openclaw系统基于docker部署 cd openclaw docker compose exec openclaw-gateway node dist/index.js plugins install mcp-adapter 方法3-源码安装 cd openclaw docker compose exec openclaw-gateway /bin/bash git clone https://github.com/androidStern/openclaw-mcp-adapter.git node dist/index.js plugins install ./openclaw-mcp-adapter/ 配置openclaw-mcp-adapter 选择一个mcp服务器，比如http://mcp-porter:8000/sse 打开openclaw.json，配置如下： vim ~/.openclaw/openclaw.json "plugins": { "entries": { "openclaw-mcp-adapter": { "enabled": true, "config": { "servers": [ { "name": "mcp-porter-calculator", "transport": "http", "url": "http://mcp-porter:8000/sse" } ], "toolPrefix": true } } } } 测试： web-ui的chat窗口，发送调用相应mcp的工具即可。
说明: mcp-endpoint-server是什么？ 在开始部署之前，我们先理解一下mcp-endpoint-server在整个架构中的位置。 mcp-endpoint-server是一个基于WebSocket的MCP注册中心，由xinnan-tech团队开发，主要作用是作为小智AI设备与MCP工具之间的桥梁。它的核心设计理念是：让小智设备端能够通过语音调用各种MCP服务。 为什么需要它？ 如果你接触过小智AI，你可能知道小智设备端需要通过WebSocket连接到小智官方服务器。而mcp-endpoint-server提供了一个统一的接入点，设备端可以连接到这里，然后通过它调用各种注册的MCP工具（如计算器、高德地图、Home Assistant等）。 它的优势在于： 高性能：基于FastAPI和WebSocket的异步架构，支持高并发连接 连接管理：智能管理WebSocket连接，支持连接清理 监控统计：提供连接统计和健康检查接口 消息转发：自动转发工具端和小智端之间的消息 配置灵活：支持配置文件管理，易于部署和维护 简单来说，你部署好mcp-endpoint-server后，小智设备端就可以连接上来，然后通过它调用你本地的MCP服务（比如我们之前配置的计算器）。而且你的MCP服务无需部署在公网环境，这极大方便了开发者在各种网络环境下的使用。 工作流程： 设备端：小智设备（或运行mcp_pipe.py的机器）作为WebSocket客户端，主动连接到mcp-endpoint-server 注册工具：设备端将自己本地的MCP服务（如计算器）通过stdio管道与mcp_pipe.py连接，mcp_pipe.py负责将WebSocket消息转换为stdio通信 服务端管理：mcp-endpoint-server管理所有设备的WebSocket连接，并转发来自小智官方服务器的请求 调用链路：小智官方服务器 → mcp-endpoint-server → 设备端 → 本地MCP服务 这种设计的巧妙之处在于：设备端的MCP服务可以运行在内网，不需要公网IP，只需要设备端能主动连接到公网的mcp-endpoint-server即可 docker方式安装mcp-endpoint-server： 部署教程1, 部署教程2 采用单模块部署的安装方法： git clone https://github.com/xinnan-tech/mcp-endpoint-server.git # 进入本项目源码根目录 cd mcp-endpoint-server # 清除缓存 docker compose -f docker-compose.yml down docker stop mcp-endpoint-server docker rm mcp-endpoint-server docker rmi ghcr.nju.edu.cn/xinnan-tech/mcp-endpoint-server:latest # 启动docker容器 docker compose -f docker-compose.yml up -d 第一次安装会需要一点时间 启动完成之后会出现需要查看 # 查看日志 docker logs -f mcp-endpoint-server 250705 INFO-=====下面的地址分别是智控台/单模块MCP接入点地址==== 250705 INFO-智控台MCP参数配置: http://172.22.0.2:8004/mcp_endpoint/health?key=abc 250705 INFO-单模块部署MCP接入点: ws://172.22.0.2:8004/mcp_endpoint/mcp/?token=def 250705 INFO-=====请根据具体部署选择使用，请勿泄露给任何人====== 假设你的本机地址是127.0.0.1 如上，输出mcp接入点是中ws://127.0.0.1:8004/mcp_endpoint/mcp/?token=def就是你的MCP接入点地址。 这个MCP接入点地址很重要，你等一下会用到 简单验证 curl http://127.0.0.1:8004/health 部署一个计算器验证完整功能： 部署虾哥写的计算器项目,安装 https://github.com/78/mcp-calculator # 进入项目目录 cd mcp-calculator conda remove -n mcp-calculator --all -y conda create -n mcp-calculator python=3.10 -y conda activate mcp-calculator pip install -r requirements.txt 建立执行脚本： 使用自己正确的token测试 export MCP_ENDPOINT=ws://127.0.0.1:8004/mcp_endpoint/mcp/?token=abc python mcp_pipe.py calculator.py 启动后mcp-endpoint-server日志会出现类似这样的内容， 250705 -INFO-正在初始化MCP接入点: wss://2662r3426b.vicp.fun/mcp_e 250705 -INFO-发送MCP接入点初始化消息 250705 -INFO-MCP接入点连接成功 250705 -INFO-MCP接入点初始化成功 250705 -INFO-统一工具处理器初始化完成 250705 -INFO-MCP接入点服务器信息: name=Calculator, version=1.9.4 250705 -INFO-MCP接入点支持的工具数量: 1 250705 -INFO-所有MCP接入点工具已获取，客户端准备就绪 250705 -INFO-工具缓存已刷新 250705 -INFO-当前支持的函数列表: [ 'get_time', 'get_lunar', 'play_music', 'get_weather', 'handle_exit_intent', 'calculator'] 建立测试脚本进行计算 建立脚本：vim robot_example_cal.py 脚本内容： #!/usr/bin/env python3 """ MCP Robot Client 示例 适配 xinnan-tech/mcp-endpoint-server + mcp-calculator 支持 JSON-RPC initialize / tools/list / tools/call """ import asyncio import json import base64 import argparse import urllib.parse from Crypto.Cipher import AES from Crypto.Util.Padding import pad import websockets # ==================== Token 生成 ==================== def pad_key(key: str) -> bytes: key_bytes = key.encode("utf-8") if len(key_bytes) in (16, 24, 32): return key_bytes padded = bytearray(32) padded[:min(len(key_bytes), 32)] = key_bytes[:32] return bytes(padded) def encrypt_token(key: str, agent_id: str) -> str: plain_text = json.dumps({"agentId": agent_id}, ensure_ascii=False) key_bytes = pad_key(key) cipher = AES.new(key_bytes, AES.MODE_ECB) padded_data = pad(plain_text.encode("utf-8"), AES.block_size) encrypted_bytes = cipher.encrypt(padded_data) return base64.b64encode(encrypted_bytes).decode("ascii") # ==================== MCP Client ==================== class MCPRobotClient: def __init__(self, uri): self.uri = uri self.request_id = 0 def next_id(self): self.request_id += 1 return self.request_id async def send_request(self, websocket, method, params=None): req_id = self.next_id() request = { "jsonrpc": "2.0", "id": req_id, "method": method, "params": params or {} } await websocket.send(json.dumps(request, ensure_ascii=False)) print(f"发送: {request}") return req_id async def receive_loop(self, websocket): try: async for message in websocket: data = json.loads(message) print(f"收到: {data}") except websockets.ConnectionClosed: print("WebSocket 连接关闭") async def run(self): async with websockets.connect(self.uri) as websocket: print(f"已连接 {self.uri}") # 启动接收任务 receive_task = asyncio.create_task(self.receive_loop(websocket)) # initialize await self.send_request(websocket, "initialize", { "protocolVersion": "2024-11-05", "capabilities": {}, "clientInfo": { "name": "robot-example", "version": "1.0" } }) await asyncio.sleep(0.5) # tools/list await self.send_request(websocket, "tools/list", {}) await asyncio.sleep(0.5) # 调用 mcp-calculator await self.send_request(websocket, "tools/call", { "name": "calculator", "arguments": { "python_expression": "10 + 5 * 2" } }) # 等待一些响应 await asyncio.sleep(5) receive_task.cancel() # ==================== CLI ==================== if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--host", default="127.0.0.1", help="服务器地址") parser.add_argument("--port", type=int, default=8004, help="服务器端口") parser.add_argument("--agent-id", default="robot-client", help="Agent ID") parser.add_argument("--key", help="AES 密钥（用于生成 token）") parser.add_argument("--token", help="直接指定完整 token（优先级高于 --key）") args = parser.parse_args() # ==================== # Token 解析逻辑 # ==================== if args.token: token = args.token print("使用直接提供的 token") uri = f"ws://{args.host}:{args.port}/mcp_endpoint/call/?token={token}" elif args.key: token = encrypt_token(args.key, args.agent_id) print(f"使用 key 生成 token: {token}") encoded_token = urllib.parse.quote(token) uri = f"ws://{args.host}:{args.port}/mcp_endpoint/call/?token={encoded_token}" else: print("必须提供 --token 或 --key") exit(1) print(f"连接地址: {uri}") client = MCPRobotClient(uri) asyncio.run(client.run()) 运行脚本 python robot_example_cal.py --token "def=" 使用自己正确的token测试，效果如下 使用直接提供的 token 连接地址: ws://127.0.0.1:8004/mcp_endpoint/call/?token=def= 已连接 ws://127.0.0.1:8004/mcp_endpoint/call/?token=def= 发送: {'jsonrpc': '2.0', 'id': 1, 'method': 'initialize', 'params': {'protocolVersion': '2024-11-05', 'capabilities': {}, 'clientInfo': {'name': 'robot-example', 'version': '1.0'}}} 收到: {'jsonrpc': '2.0', 'id': 1, 'result': {'protocolVersion': '2024-11-05', 'capabilities': {'experimental': {}, 'prompts': {'listChanged': False}, 'resources': {'subscribe': False, 'listChanged': False}, 'tools': {'listChanged': True}, 'tasks': {'list': {}, 'cancel': {}, 'requests': {'tools': {'call': {}}, 'prompts': {'get': {}}, 'resources': {'read': {}}}}}, 'serverInfo': {'name': 'Calculator', 'version': '2.14.5'}}} 发送: {'jsonrpc': '2.0', 'id': 2, 'method': 'tools/list', 'params': {}} 收到: {'jsonrpc': '2.0', 'id': 2, 'result': {'tools': [{'name': 'calculator', 'description': "For mathamatical calculation, always use this tool to calculate the result of a python expression. You can use 'math' or 'random' directly, without 'import'.", 'inputSchema': {'properties': {'python_expression': {'type': 'string'}}, 'required': ['python_expression'], 'type': 'object'}, 'outputSchema': {'additionalProperties': True, 'type': 'object'}, '_meta': {'_fastmcp': {'tags': []}}}]}} 发送: {'jsonrpc': '2.0', 'id': 3, 'method': 'tools/call', 'params': {'name': 'calculator', 'arguments': {'python_expression': '10 + 5 * 2'}}} 收到: {'jsonrpc': '2.0', 'id': 3, 'result': {'content': [{'type': 'text', 'text': '{"success":true,"result":20}'}], 'structuredContent': {'success': True, 'result': 20}, 'isError': False}} 看到这样的信息，说明部署正确
说明： 在前面的教程中，我们分别学习了如何配置MCP-adapter来直接调用MCP服务，以及如何部署小智语音的mcp-endpoint-server。现在，我们将两者结合，实现一个更强大的架构：通过OpenClaw的MCP-adapter，经由mcp-endpoint-server调用内网或设备端的MCP工具。 这个架构解决了什么问题？当你的MCP服务运行在内网或设备端（如树莓派、NAS）时，OpenClaw无法直接连接。而mcp-endpoint-server作为公网WebSocket服务器，可以让内网设备主动连接并注册工具。通过一个协议转换代理，OpenClaw就能通过标准的SSE/HTTP协议调用这些工具。整个过程无需公网IP，无需复杂的端口映射。 架构： subgraph 公网云服务器 A[mcp-endpoint-server WebSocket服务端] B[ws2sse代理 WebSocket → SSE] end subgraph 内网/设备端 C[mcp_pipe.py + 本地MCP服务] end subgraph OpenClaw环境 D[openclaw-mcp-adapter] end C -- 主动WebSocket连接 --> A B -- 连接 --> A D -- SSE --> B 工作流程： 内网设备通过mcp_pipe.py将本地MCP服务（如计算器）注册到公网的mcp-endpoint-server。 我们在公网部署一个ws2sse代理，它作为WebSocket客户端连接mcp-endpoint-server，同时对外提供SSE端点。 OpenClaw的mcp-adapter插件通过SSE连接ws2sse代理，发现并注册所有可用的MCP工具。 用户在OpenClaw中通过语音或文字调用工具，请求经mcp-adapter → ws2sse代理 → mcp-endpoint-server → 内网设备，最终执行并返回结果。 openclaw的插件mcp-adapter不能直接通过websocket访问mcp-endpoint-server，所以要通过一个ws2sse代理来做中转。 在这里我们建立一个mcp-porter-server的ws2sse代理，使用docker部署在openclaw同一个服务器 1.建立Dockerfile FROM python:3.12-slim WORKDIR /app # 复制依赖文件并安装（仅当 requirements.txt 变化时才会重新安装） COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # 复制当前目录所有文件到镜像（会被卷挂载覆盖，但用于构建时保证镜像完整性） COPY . . # 容器启动命令 CMD ["python", "ws2sse_proxy.py"] 2.建立docker-compose.yml networks: mcp-shared-network: external: true name: mcp-shared-network services: mcp-porter: build: . # 使用当前目录的 Dockerfile 构建 container_name: mcp-porter restart: always networks: - mcp-shared-network ports: - "8000:8000" volumes: - ./:/app # 将宿主机当前目录挂载到容器 /app，覆盖代码 environment: - TZ=Asia/Shanghai - REMOTE_WS_URL=ws://mcp-endpoint-server:8004/mcp_endpoint/call/?token=${MCP_TOKEN} # 不再直接写 token，使用 .env 文件 建立requirements.txt fastapi==0.104.1 uvicorn[standard]==0.24.0 websockets==12.0 python-multipart==0.0.6 pydantic==2.5.0 python-dotenv==1.0.0 configparser==6.0.0 asyncio-mqtt==0.16.1 pycryptodome==3.23.0 loguru==0.7.2 requests 建立ws2sse_proxy.py #!/usr/bin/env python3 """ MCP WebSocket 转 SSE 代理（完整版） - 作为客户端连接到远程 WebSocket MCP 服务器 - 使用独立接收循环处理消息，支持并发请求 - 对外提供 SSE 端点供 OpenClaw 连接 """ import asyncio import json import os import logging from contextlib import asynccontextmanager from typing import Dict, Optional import websockets from fastapi import FastAPI, Request from fastapi.responses import StreamingResponse, JSONResponse import uvicorn # 配置日志 logging.basicConfig(level=logging.INFO) logger = logging.getLogger("mcp-proxy") # 从环境变量读取远程 WebSocket 地址 REMOTE_WS_URL = os.getenv( "REMOTE_WS_URL", "ws://127.0.0.1:8004/mcp_endpoint/call/?token=2svzrombSJCGKEdlm6znodXSkJlwtfDcjsBV9mfNLtA=" ) # 全局状态 remote_ws: Optional[websockets.WebSocketClientProtocol] = None remote_tools: list = [] pending_requests: Dict[int, asyncio.Future] = {} connection_lock = asyncio.Lock() receive_task: Optional[asyncio.Task] = None async def _receive_loop(): """后台接收远程 WebSocket 消息，根据 id 分发结果""" global remote_ws, pending_requests # 必须放在最前面 try: async for message in remote_ws: data = json.loads(message) logger.debug(f"Received from remote: {data}") req_id = data.get("id") if req_id is not None and req_id in pending_requests: future = pending_requests.pop(req_id) future.set_result(data) else: logger.info(f"Ignored message (no matching request): {data}") except websockets.exceptions.ConnectionClosed as e: logger.warning(f"Remote connection closed: {e}") # 取消所有等待的请求 for req_id, future in pending_requests.items(): future.set_exception(Exception("Remote connection closed")) pending_requests.clear() remote_ws = None except Exception as e: logger.error(f"Receive loop error: {e}", exc_info=True) async def connect_to_remote(): """连接到远程 WebSocket 服务器并完成 MCP 握手""" global remote_ws, remote_tools, receive_task async with connection_lock: if remote_ws and not remote_ws.closed: return logger.info(f"Connecting to remote server: {REMOTE_WS_URL}") try: remote_ws = await websockets.connect(REMOTE_WS_URL) logger.info("WebSocket connected, starting MCP handshake...") # 1. 发送 initialize 请求 init_req = { "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": "2024-11-05", "capabilities": {}, "clientInfo": {"name": "mcp-ws-proxy", "version": "2.0"} } } await remote_ws.send(json.dumps(init_req)) # 手动等待初始化响应（因为接收循环尚未启动） init_resp = json.loads(await remote_ws.recv()) logger.info(f"Initialize response: {init_resp}") # 2. 发送 initialized 通知 notif = {"jsonrpc": "2.0", "method": "notifications/initialized"} await remote_ws.send(json.dumps(notif)) # 3. 请求工具列表（同样手动等待） tools_req = {"jsonrpc": "2.0", "id": 2, "method": "tools/list"} await remote_ws.send(json.dumps(tools_req)) tools_resp = json.loads(await remote_ws.recv()) remote_tools = tools_resp.get("result", {}).get("tools", []) logger.info(f"Discovered {len(remote_tools)} tools: {[t['name'] for t in remote_tools]}") # 启动后台接收循环 receive_task = asyncio.create_task(_receive_loop()) except Exception as e: logger.error(f"Failed to connect to remote server: {e}") remote_ws = None raise async def forward_request_to_remote(request_body: dict) -> dict: """将请求转发给远程服务器，并等待响应""" global remote_ws, pending_requests if not remote_ws or remote_ws.closed: await connect_to_remote() req_id = request_body.get("id") if req_id is None: raise ValueError("Request missing 'id' field") # 创建 Future 并存入字典 loop = asyncio.get_event_loop() future = loop.create_future() pending_requests[req_id] = future try: await remote_ws.send(json.dumps(request_body)) logger.debug(f"Sent to remote: {request_body}") # 等待响应，设置超时 response = await asyncio.wait_for(future, timeout=30.0) return response except asyncio.TimeoutError: pending_requests.pop(req_id, None) raise Exception(f"Request {req_id} timed out") except Exception as e: pending_requests.pop(req_id, None) raise e @asynccontextmanager async def lifespan(app: FastAPI): """应用启动时连接远程服务器""" try: await connect_to_remote() except Exception as e: logger.error(f"Startup connection failed: {e}") yield if receive_task: receive_task.cancel() if remote_ws and not remote_ws.closed: await remote_ws.close() app = FastAPI(lifespan=lifespan) @app.get("/sse") async def sse_endpoint(request: Request): """SSE 端点，用于 OpenClaw 连接""" async def event_generator(): # 发送初始化事件（符合 MCP over SSE 规范） init_event = { "jsonrpc": "2.0", "id": 1, "result": { "protocolVersion": "2024-11-05", "capabilities": {}, "serverInfo": {"name": "mcp-ws-proxy", "version": "2.0"} } } yield f"data: {json.dumps(init_event)}\n\n" # 保持连接，发送心跳 while not await request.is_disconnected(): await asyncio.sleep(1) yield ": heartbeat\n\n" return StreamingResponse(event_generator(), media_type="text/event-stream") @app.post("/sse") async def sse_post(request: Request): """处理来自 OpenClaw 的 JSON-RPC 请求""" body = await request.json() print("=== 收到请求体 ===") # 添加直接打印 print(json.dumps(body, indent=2)) # 格式化打印 logger.info(f"Received request: {body}") method = body.get("method") req_id = body.get("id") # tools/list 直接返回缓存的工具列表 if method == "tools/list": return JSONResponse({ "jsonrpc": "2.0", "id": req_id, "result": {"tools": remote_tools} }) # initialize 返回本地信息 if method == "initialize": return JSONResponse({ "jsonrpc": "2.0", "id": req_id, "result": { "protocolVersion": "2024-11-05", "capabilities": {}, "serverInfo": {"name": "mcp-ws-proxy", "version": "2.0"} } }) # 其他请求（如 tools/call）转发给远程服务器 try: resp = await forward_request_to_remote(body) return JSONResponse(resp) except Exception as e: logger.error(f"Forwarding failed: {e}", exc_info=True) return JSONResponse({ "jsonrpc": "2.0", "id": req_id, "error": {"code": -32000, "message": str(e)} }, status_code=502) @app.get("/health") async def health(): """健康检查""" return { "status": "ok", "remote_connected": remote_ws is not None and not remote_ws.closed, "tools_count": len(remote_tools) } if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=8000) 构建docker docker compose build mcp-porter 启动docker docker compose mcp-porter up -d 验证是否能通过mcp-porter-server来访问计算工具 构建测试脚本test_proxy.py #!/usr/bin/env python3 """ 增强版 MCP 代理测试脚本 - 动态获取工具参数 schema，自动构造合适的测试参数 - 支持任意工具，无需硬编码参数名 - 提供详细的请求/响应日志 """ import json import requests import argparse import sys from typing import Dict, Any def send_jsonrpc(url: str, method: str, params: Dict = None, req_id: int = 1) -> Dict: """发送 JSON-RPC 请求并返回响应""" payload = { "jsonrpc": "2.0", "id": req_id, "method": method } if params is not None: payload["params"] = params headers = {"Content-Type": "application/json"} print(f"\n>>> 发送请求 [{req_id}]: {method}") print(json.dumps(payload, indent=2, ensure_ascii=False)) try: response = requests.post(url, json=payload, headers=headers, timeout=10) response.raise_for_status() resp_json = response.json() print(f"<<< 收到响应:") print(json.dumps(resp_json, indent=2, ensure_ascii=False)) return resp_json except requests.exceptions.RequestException as e: print(f" 请求失败: {e}") if hasattr(e, 'response') and e.response is not None: print(f"响应状态码: {e.response.status_code}") print(f"响应内容: {e.response.text}") sys.exit(1) except json.JSONDecodeError: print(f" 响应不是有效的 JSON: {response.text}") sys.exit(1) def generate_test_arguments(schema: Dict) -> Dict: """ 根据工具的 inputSchema 生成测试参数 schema 格式示例: { "type": "object", "properties": { "python_expression": {"type": "string", "description": "..."} }, "required": ["python_expression"] } """ if not schema: return {} properties = schema.get("properties", {}) required = schema.get("required", []) args = {} for field in required: prop = properties.get(field, {}) prop_type = prop.get("type", "string") # 根据类型生成合适的测试值 if prop_type == "number": args[field] = 10 # 示例数字 elif prop_type == "integer": args[field] = 5 elif prop_type == "boolean": args[field] = True elif prop_type == "array": args[field] = [] # 空数组 elif prop_type == "object": args[field] = {} # 空对象 else: # 默认字符串 args[field] = "test" if field != "python_expression" else "10 + 5" # 如果没有 required 字段，尝试取第一个属性作为示例 if not required and properties: field = next(iter(properties)) prop = properties[field] prop_type = prop.get("type", "string") if prop_type == "number": args[field] = 10 elif prop_type == "integer": args[field] = 5 else: args[field] = "example" return args def main(): parser = argparse.ArgumentParser(description="增强版 MCP 代理测试") parser.add_argument("--url", default="http://127.0.0.1:8000/sse", help="代理的 SSE 端点 URL (默认: http://127.0.0.1:8000/sse)") parser.add_argument("--tool", help="指定要测试的工具名称，不指定则使用第一个工具") args = parser.parse_args() print(f" 测试代理: {args.url}") # 1. 获取工具列表 print("\n 正在获取工具列表...") tools_resp = send_jsonrpc(args.url, "tools/list", req_id=1) if "error" in tools_resp: print(f" 获取工具列表失败: {tools_resp['error']}") sys.exit(1) tools = tools_resp.get("result", {}).get("tools", []) if not tools: print(" 代理未返回任何工具") sys.exit(0) print(f" 发现 {len(tools)} 个工具:") for idx, tool in enumerate(tools, 1): name = tool.get("name", "未知") desc = tool.get("description", "") print(f" {idx}. {name}: {desc}") # 2. 选择要测试的工具 target_tool = None if args.tool: for tool in tools: if tool.get("name") == args.tool: target_tool = tool break if not target_tool: print(f" 未找到指定工具: {args.tool}") sys.exit(1) else: # 默认选择第一个工具 target_tool = tools[0] print(f"\n 未指定工具，将使用第一个工具: {target_tool['name']}") tool_name = target_tool["name"] tool_schema = target_tool.get("inputSchema", {}) print(f"\n 工具 '{tool_name}' 的输入 schema:") print(json.dumps(tool_schema, indent=2, ensure_ascii=False)) # 3. 生成测试参数 test_args = generate_test_arguments(tool_schema) print(f"\n 生成的测试参数: {test_args}") # 4. 调用工具 params = {"name": tool_name, "arguments": test_args} call_resp = send_jsonrpc(args.url, "tools/call", params, req_id=2) if "error" in call_resp: print(f"\n 调用失败: {call_resp['error']}") sys.exit(1) result = call_resp.get("result", {}) print(f"\n 调用成功，原始结果:") print(json.dumps(result, indent=2, ensure_ascii=False)) # 尝试提取文本内容（如果存在） content = result.get("content", []) for item in content: if item.get("type") == "text": print(f"\n 计算结果文本: {item['text']}") if __name__ == "__main__": main() 测试运行 python3 test_proxy.py 效果 $ ./run_test.sh 测试代理: http://127.0.0.1:8000/sse 正在获取工具列表... >>> 发送请求 [1]: tools/list { "jsonrpc": "2.0", "id": 1, "method": "tools/list" } <<< 收到响应: { "jsonrpc": "2.0", "id": 1, "result": { "tools": [ { "name": "calculator", "description": "For mathamatical calculation, always use this tool to calculate the result of a python expression. You can use 'math' or 'random' directly, without 'import'.", "inputSchema": { "properties": { "python_expression": { "type": "string" } }, "required": [ "python_expression" ], "type": "object" }, "outputSchema": { "additionalProperties": true, "type": "object" }, "_meta": { "_fastmcp": { "tags": [] } } } ] } } 发现 1 个工具: 1. calculator: For mathamatical calculation, always use this tool to calculate the result of a python expression. You can use 'math' or 'random' directly, without 'import'. 未指定工具，将使用第一个工具: calculator 工具 'calculator' 的输入 schema: { "properties": { "python_expression": { "type": "string" } }, "required": [ "python_expression" ], "type": "object" } 生成的测试参数: {'python_expression': '10 + 5'} >>> 发送请求 [2]: tools/call { "jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": { "name": "calculator", "arguments": { "python_expression": "10 + 5" } } } <<< 收到响应: { "jsonrpc": "2.0", "id": 2, "result": { "content": [ { "type": "text", "text": "{\"success\":true,\"result\":15}" } ], "structuredContent": { "success": true, "result": 15 }, "isError": false } } 调用成功，原始结果: { "content": [ { "type": "text", "text": "{\"success\":true,\"result\":15}" } ], "structuredContent": { "success": true, "result": 15 }, "isError": false } 计算结果文本: {"success":true,"result":15} 因为openclaw是docker里面，所有需要访问docker版的mcp-endpoint-server和mcp-porter-server,需要他们都在一个网络里面，这样才能访问访问。 我们在mcp-endpoint-server增加一个docker网络， mcp-endpoint-server改为 $ cat docker-compose.yml # Docker安装全模块 version: '3' services: # MCP 接入点服务 mcp-endpoint-server: image: ghcr.nju.edu.cn/xinnan-tech/mcp-endpoint-server:latest container_name: mcp-endpoint-server restart: always networks: - mcp-shared-network # 改为共享网络 ports: - "8004:8004" security_opt: - seccomp:unconfined environment: - TZ=Asia/Shanghai volumes: # 配置文件目录 - ./data:/app/data networks: mcp-shared-network: name: mcp-shared-network driver: bridge 增加一个mcp-shared-network网络 mcp-porter-server使用这个网络 $ cat docker-compose.yml networks: mcp-shared-network: external: true name: mcp-shared-network services: mcp-porter: build: . # 使用当前目录的 Dockerfile 构建 container_name: mcp-porter restart: always networks: - mcp-shared-network ports: - "8000:8000" volumes: - ./:/app # 将宿主机当前目录挂载到容器 /app，覆盖代码 environment: - TZ=Asia/Shanghai - REMOTE_WS_URL=ws://mcp-endpoint-server:8004/mcp_endpoint/call/?token=${MCP_TOKEN} # 不再直接写 token，使用 .env 文件 openclaw也使用这个网络 $ cat docker-compose.yml networks: mcp-shared-network: external: true name: mcp-shared-network services: openclaw-cli: entrypoint: - node - dist/index.js environment: BROWSER: echo HOME: /home/node OPENCLAW_GATEWAY_TOKEN: ${OPENCLAW_GATEWAY_TOKEN} TERM: xterm-256color image: ${OPENCLAW_IMAGE:-openclaw:local} init: true networks: - default - mcp-shared-network stdin_open: true tty: true volumes: - ${OPENCLAW_CONFIG_DIR}:/home/node/.openclaw - ${OPENCLAW_WORKSPACE_DIR}:/home/node/.openclaw/workspace openclaw-gateway: command: - node - dist/index.js - gateway - --bind - ${OPENCLAW_GATEWAY_BIND:-lan} - --port - '18789' env_file: - ./.env environment: GATEWAY_TRUSTED_PROXIES: ${GATEWAY_TRUSTED_PROXIES} HOME: /home/node OPENCLAW_GATEWAY_TOKEN: ${OPENCLAW_GATEWAY_TOKEN} TERM: xterm-256color image: ${OPENCLAW_IMAGE:-openclaw:local} init: true networks: - default - mcp-shared-network ports: - ${OPENCLAW_GATEWAY_PORT:-18789}:18789 - ${OPENCLAW_BRIDGE_PORT:-18790}:18790 restart: unless-stopped volumes: - ${OPENCLAW_CONFIG_DIR}:/home/node/.openclaw - ${OPENCLAW_WORKSPACE_DIR}:/home/node/.openclaw/workspace - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro 配置openclaw.json使用容器名称即可 "plugins": { "entries": { "feishu": { "enabled": true }, "openclaw-mcp-adapter": { "enabled": true, "config": { "servers": [ { "name": "mcp-porter-calculator", "transport": "http", "url": "http://mcp-porter:8000/sse" } ], "toolPrefix": true } } } } 重启三个docker，即可通过openclaw来调用虾哥的计算器功能 在web-ui的chat界面发指令： 使用mcp工具来计算10+10
OpenClaw AI助手入门教程-配置QMD记忆系统，优化反馈速度
说明 你是否遇到过这样的困扰：刚开始用OpenClaw时响应还挺快，聊了几轮后速度越来越慢，最后甚至卡死？更糟心的是，API账单还在蹭蹭往上涨，仿佛在为一个“失忆”的AI付高额账单。这背后的罪魁祸首，往往是上下文爆炸——每次对话都携带大量无关历史记录，让AI花大量时间“阅读理解”，既耗钱又降速。 别担心，从OpenClaw 2026.2.2版本开始，内置的QMD记忆系统完美解决了这个问题。本文将带你从零开始，一步步配置QMD，让你的AI助手响应速度从“蜗牛”变“猎豹”。 为什么你的OpenClaw越来越慢？ 1.1 传统记忆系统的痛点 传统的记忆机制，简单粗暴——把整个MEMORY.md文件或完整的对话历史，一股脑全塞给AI。这导致几个严重问题： 上下文越长，速度越慢：大模型的推理时间与输入token数量成正比。5000 token的请求要等8-15秒，20000 token直接奔着1分钟去了 成本指数级增长：一个20000 token的会话，单次请求成本可能高达数美元，如果频繁调用，月账单令人心惊 容易卡死超时：当上下文累积到8万、10万token时，请求很容易超时失败，钱花了，结果却没得到 噪音干扰精度：塞入大量无关信息，反而会让AI“分心”，回答质量下降 1.2 QMD是什么？ QMD（Quantum Memory Database）是由Shopify联合创始人Tobi Lütke开发的本地语义搜索引擎，专为AI Agent场景设计。 它的核心思路是：不要把整个文件塞给AI，而是先用本地搜索找到最相关的片段（通常只需2-3句话），再把精准内容传给AI。 技术原理上，QMD采用三层混合搜索机制，确保检索精准度： 层级 技术 作用 第一层 BM25全文搜索 精准匹配关键词，类似传统搜索引擎 第二层 向量语义搜索 理解语义相似度，能找到意思相近但用词不同的内容 第三层 LLM重排序 用AI对结果二次优化，确保最相关内容排前面 这种混合搜索模式能将精准度提升至93%，而纯语义搜索只有59% 注意：qmd的query功能，需要有GPU支持才比较顺畅，CPU下很慢不建议使用。cpu下使用search即可 openclaw自安装： 我们直接利用openclaw自动帮我们完整部署，web-ui的chat发指令: 为openclaw配置QMD记忆系统，优化反馈速度 通过这个指令，应该能让openclaw自己完成安装，配置和重启openclaw 同时openclaw会给出一个测试报告 使用docker安装，这个方法可能不一定成功安装，可以参考手工安装。 OpenClaw 会自动使用 QMD 进行记忆检索，如果 QMD 出现问题，会自动回退到内置的 SQLite 记忆系统，不影响正常使用 手工安装 最好是有vpn，要不下载模型容易失败，只能采用手工下载比较麻烦 root方式进入容器 docker compose exec --user root -it openclaw-gateway bash 安装依赖 #QMD 需要支持 vector 扩展的 SQLite apt update && apt install jq sqlite3 #测试生效 sqlite3 --version 安装qmd #方法1： bun install -g @tobilu/qmd #方法2：自动先安装qmd npx @tobilu/qmd ... #方法3： # 使用 npm 安装 npm install -g @tobilu/qmd #安装后添加到PATH路径 echo "export PATH=$PATH:/home/node/.bun/bin" >> ~/.bashrc #测试生效 qmd --version #保证目录权限 mkdir -p /home/node/.cache/qmd chown -R node:node /home/node/.cache/qmd chown -R node:node /home/node/.bun/install/global/node_modules/node-llama-cpp 普通用户方式进入容器 docker compose exec -it openclaw-gateway bash #如果不带GPU，基于源码安装CUP支持的node-llama-cpp版本 NODE_LLAMA_CPP_SKIP_DOWNLOAD=true npm install node-llama-cpp #测试建立索引 $ ~/.bun/bin/qmd update --verbose Updating 1 collection(s)... [1/1] workspace (**/*.md) Collection: /home/node/.openclaw/workspace (**/*.md) Indexing: 206/206 ETA: 0s Indexed: 197 new, 9 updated, 0 unchanged, 0 removed Cleaned up 9 orphaned content hash(es) ✓ All collections updated. Run 'qmd embed' to update embeddings (193 unique hashes need vectors) #为索引的文档生成向量 HF_ENDPOINT=https://hf-mirror.com ~/.bun/bin/qmd embed --verbose ............ QMD Warning: cuda reported available but failed to initialize. Falling back to CPU. QMD Warning: no GPU acceleration, running on CPU (slow). Run 'qmd status' for details. Downloading to ~/.cache/qmd/models ⏵ hf_ggml-org_embeddinggemma-300M-Q8_0.gguf 86.03% (282.69MB/328.58MB) 1.22MB/s | 37s left #测试关键词搜索 ~/.bun/bin/qmd search "关键词" # 关键词搜索 #测试语义搜索 HF_ENDPOINT=https://hf-mirror.com ~/.bun/bin/qmd query "你的测试关键词" # 语义搜索（推荐） ....... QMD Warning: cuda reported available but failed to initialize. Falling back to CPU. QMD Warning: no GPU acceleration, running on CPU (slow). Run 'qmd status' for details. Downloading to ~/.cache/qmd/models ⏵ hf_tobil_qmd-query-expansion-1.7B-q4_k_m.gguf 60.29% (773.28MB/1.28GB) 1.17MB/s | 7m left #模型下载路径在容器内~/.cache/qmd/models下，名称为。 hf_tobil_qmd-query-expansion-1.7B-q4_k_m.gguf hf_ggml-org_embeddinggemma-300M-Q8_0.gguf hf_ggml-org_qwen3-reranker-0.6b-q8_0.gguf #也可以单独下载放在对应的目录下，为了避免重启容器丢失 mkdir -p ~/.openclaw/qmd/ mv ~/.cache/qmd/models ~/.openclaw/qmd/ ln -s ~/.openclaw/qmd/models ~/.cache/qmd/models #把HF_ENDPOINT=https://hf-mirror.com加到bashrc echo "export HF_ENDPOINT=https://hf-mirror.com" >> ~/.bashrc 参考的整合流程： 检查OpenClaw版本，需要大于2026.2.2 $ docker compose exec openclaw-gateway node dist/index.js --version 2026.2.9 备份openclaw.json配置 cd ~/.openclaw/ cp openclaw.json openclaw.json.backup 修改openclaw配置，添加内容 "hooks": { "internal": { "enabled": true, "entries": { "session-memory": { "enabled": true } } } }, "memory": { "backend": "qmd", "citations": "auto", "qmd": { "includeDefaultMemory": true, "update": { "interval": "5m", "debounceMs": 15000 }, "limits": { "maxResults": 6, "timeoutMs": 4000 }, "scope": { "default": "deny", "rules": [ { "action": "allow", "match": { "chatType": "direct" } } ] } } }, 关键配置项说明： backend: "qmd"：这一行告诉 OpenClaw 使用 QMD 作为记忆后端，是启用一切的前提 。 includeDefaultMemory: true：建议保留为 true。这样 QMD 除了索引你指定的额外路径外，仍然会索引默认的 MEMORY.md 和 memory/ 目录，确保你的核心记忆不受影响 。 paths 数组：这是指定额外“内存文件”的关键。 你可以填入绝对路径或相对于当前工作空间的路径 。 如果填入的是目录，QMD 会递归扫描该目录下所有的 .md (Markdown) 文件并建立索引 。 你也可以直接填入一个具体的 .md 文件路径，让 QMD 只索引这一个文件。 重启openclaw cd openclaw docker compose restart 验证是否启用，在web-ui的chat发指令： 确认是否启用了qmd给出测试报告 qmd帮助文档 $ qmd -h qmd — Quick Markdown Search Usage: qmd <command> [options] Primary commands: qmd query <query> - Hybrid search with auto expansion + reranking (recommended) qmd query 'lex:..\nvec:...' - Structured query document (you provide lex/vec/hyde lines) qmd search <query> - Full-text BM25 keywords (no LLM) qmd vsearch <query> - Vector similarity only qmd get <file>[:line] [-l N] - Show a single document, optional line slice qmd multi-get <pattern> - Batch fetch via glob or comma-separated list qmd skill show/install - Show or install the packaged QMD skill qmd mcp - Start the MCP server (stdio transport for AI agents) Collections & context: qmd collection add/list/remove/rename/show - Manage indexed folders qmd context add/list/rm - Attach human-written summaries qmd ls [collection[/path]] - Inspect indexed files Maintenance: qmd status - View index + collection health qmd update [--pull] - Re-index collections (optionally git pull first) qmd embed [-f] - Generate/refresh vector embeddings qmd cleanup - Clear caches, vacuum DB Query syntax (qmd query): QMD queries are either a single expand query (no prefix) or a multi-line document where every line is typed with lex:, vec:, or hyde:. This grammar matches the docs in docs/SYNTAX.md and is enforced in the CLI. Grammar: query = expand_query | query_document ; expand_query = text | explicit_expand ; explicit_expand= "expand:" text ; query_document = [ intent_line ] { typed_line } ; intent_line = "intent:" text newline ; typed_line = type ":" text newline ; type = "lex" | "vec" | "hyde" ; text = quoted_phrase | plain_text ; quoted_phrase = '"' { character } '"' ; plain_text = { character } ; newline = "\n" ; Examples: qmd query "how does auth work" # single-line → implicit expand qmd query $'lex: CAP theorem\nvec: consistency' # typed query document qmd query $'lex: "exact matches" sports -baseball' # phrase + negation lex search qmd query $'hyde: Hypothetical answer text' # hyde-only document Constraints: - Standalone expand queries cannot mix with typed lines. - Query documents allow only lex:, vec:, or hyde: prefixes. - Each typed line must be single-line text with balanced quotes. AI agents & integrations: - Run `qmd mcp` to expose the MCP server (stdio) to agents/IDEs. - `qmd skill install` installs the QMD skill into ./.agents/skills/qmd. - Use `qmd skill install --global` for ~/.agents/skills/qmd. - `qmd --skill` is kept as an alias for `qmd skill show`. - Advanced: `qmd mcp --http ...` and `qmd mcp --http --daemon` are optional for custom transports. Global options: --index <name> - Use a named index (default: index) Search options: -n <num> - Max results (default 5, or 20 for --files/--json) --all - Return all matches (pair with --min-score) --min-score <num> - Minimum similarity score --full - Output full document instead of snippet -C, --candidate-limit <n> - Max candidates to rerank (default 40, lower = faster) --line-numbers - Include line numbers in output --explain - Include retrieval score traces (query --json/CLI) --files | --json | --csv | --md | --xml - Output format -c, --collection <name> - Filter by one or more collections Multi-get options: -l <num> - Maximum lines per file --max-bytes <num> - Skip files larger than N bytes (default 10240) --json/--csv/--md/--xml/--files - Same formats as search Index: /home/node/.cache/qmd/index.sqlite 手工验证qmd是否生效 1.查看qmd版本 #进入容器 docker compose exec -it openclaw-gateway bash qmd --version 2.手工索引 qmd update --dir "/home/node/.openclaw/workspace" #查看集合 qmd collection list 3.测试搜索功能 #默认集合位置/home/node/.cache/qmd/index.sqlite qmd search "你的关键词" #指定集合位置 qmd search "你的关键词" -c "/home/node/.cache/qmd/index.sqlite" 4.验证实时更新 #默认目录索引 qmd update #指定工作目录做更新索引 qmd update --dir "/home/node/.openclaw/workspace" openclaw搜索 #进入容器 docker compose exec --user root -it openclaw-gateway bash openclaw memory search "你的关键词" 示例配置及权限配置 { "memory": { "backend": "qmd", "qmd": { "includeDefaultMemory": true, "paths": [ // 你的额外路径 ], // 关键部分：scope 权限控制 "scope": { // 默认允许所有未知类型的会话执行搜索 "default": "allow", // 也可以针对已知类型单独设置（可选） "private": "allow", // 私聊允许 "group": "deny", // 群聊拒绝（可根据需要调整） "channel": "allow" // 频道允许 } } } } 补充说明 安全考虑：default: allow 会让所有无法识别类型的会话都能搜索记忆。如果你担心安全性，可以尝试先确定你的实际会话类型（比如在私聊中测试，将 private 设为 allow，default 保持 deny），但根据你的错误信息，当前环境无法识别类型，所以最简单的修复是临时允许默认。 权限优先级：scope 匹配顺序是：优先匹配具体类型（如 private），如果没有匹配则使用 default。如果 default 也未设置，则默认行为是 deny
说明： 与传统的聊天机器人不同，OpenClaw拥有系统级权限和执行力。它不仅能聊天，更能直接操作你的电脑：执行Shell命令、读写文件、调用浏览器，甚至自动提交代码 。 对于国内的开发者而言，Gitee是绕不开的代码托管平台。本文将详细介绍如何配置OpenClaw，让它成为你的编程伙伴，自动将代码提交到Gitee仓库 非持久ssh密钥配置方法： 配置使用ssh方式提交代码 步骤1:在openclaw的容器里，生成SSH密钥并配置Gitee cd openclaw docker compose exec -it openclaw-gateway bash # 生成密钥，一路回车即可 ssh-keygen -t ed25519 -C "你的邮箱@example.com" # 查看公钥 cat ~/.ssh/id_ed25519.pub 步骤2：在gitee里面添加公钥 登录Gitee，进入 设置 → SSH公钥，粘贴并确定 步骤3：OpenClaw执行Git命令时，需要知道你是谁 cd openclaw docker compose exec -it openclaw-gateway bash git config --global user.name "你的Gitee用户名" git config --global user.email "你的Gitee邮箱" 步骤4：先在gitee新建一个项目，比如地址为：http://gitee.com/ncnynl/test 步骤5：让OpenClaw学会提交代码到Gitee. web-ui的chat上发送指令: 编写一个python脚本，生成一个圆，并提交到仓库http://gitee.com/ncnynl/test openclaw会自动想办法实现这个脚本，并自动上传到仓库。 实现持久的ssh密钥配置方法： 步骤1：使用宿主主机的密钥作为容器的密钥 建立密钥 # 生成密钥，一路回车即可 ssh-keygen -t ed25519 -C "你的邮箱@example.com" # 查看公钥 cat ~/.ssh/id_ed25519.pub # 启动 ssh-agent（如果尚未运行） eval $(ssh-agent) # 添加私钥（例如默认的 id_rsa） ssh-add ~/.ssh/id_ed25519 # 验证是否已添加 ssh-add -l #查看是否有这个变量 echo $SSH_AUTH_SOC 步骤2：配置docker-compose使用ssh-agent: #其他配置 environment: #其他配置 SSH_AUTH_SOCK: /ssh-agent #配置容器内UID与宿主UID一致 user: "${UID:-1000}" #其他配置 volumes: #其他配置 - ${SSH_AUTH_SOCK}:/ssh-agent 步骤3：重新启动容器 cd openclaw docker compose down #启动ssh-agent eval $(ssh-agent) #增加密钥 ssh-add ~/.ssh/id_ed25519 #查看变量 echo $SSH_AUTH_SOC docker compose up -d 步骤4：验证是否有效 cd openclaw #进入容器 docker compose exec -it openclaw-gateway bash #查看变量 node@066bddd34e72:/app$ echo $SSH_AUTH_SOCK /ssh-agent 再连接到gitee提交代码就不会有缺少密钥的问题了。
说明 如何打造多Agent协同工作流 为什么需要多Agent？ 痛点 记忆负担过重：单个Agent长期使用后记忆文件臃肿，加载慢、易丢失关键信息。 上下文污染：写作时冒出代码逻辑，编程时被营销思路干扰。 Token成本高昂：每次对话都加载无关背景，无效Token占比超60%。 多Agent解决方案 独立Workspace：每个Agent拥有专属工作目录（如 workspace-dev），记忆文件独立，互不干扰，加载飞快。 独立AgentDir + 独立会话：每个Agent有自己的状态目录和会话存储，上下文严格隔离，告别“人格分裂”。 按需加载：只加载当前Agent相关的记忆和工具，Token消耗直降，钱包不再喊疼。 想象一下，如果你只有一个通用助手，它既要写代码、又要写文案、还要处理部署，时间一长就会变得混乱、缓慢，甚至“精神分裂”。而通过多Agent架构，你可以为不同任务创建专属的“专家助手”，让它们各司其职、协同作战，大幅提升效率和质量。 多Agent核心架构 在 OpenClaw 中，每个 Agent 都拥有三大独立属性： 独立 Workspace（工作区）：专属的“办公室”，包含 SOUL.md（个性定义）、PROMPT.md（提示词模板）、TOOLS.md（工具配置）等文件，行为边界清晰。 独立 AgentDir（状态目录）：存储认证信息、模型配置，支持不同 Agent 绑定不同大模型（如创意类用 GPT-4，代码类用 DeepSeek）。 独立 Sessions（会话存储）：聊天记录单独保存，彻底杜绝上下文污染，降低 Token 成本。 前置准备 已部署 OpenClaw（Docker 方式），并确保 docker compose 命令可用。 已完成基础配置，能够通过 Web UI 访问（可直接访问服务器 IP:18789，或通过 SSH 隧道本地访问）。 准备一个 Gitee 账号，并已配置好 SSH 密钥（用于部署助手提交代码）。如未配置，请参考 Gitee 官方文档 添加公钥。 第一步：创建开发助手 (dev) 我们将通过 OpenClaw 的 Web UI 聊天窗口，用自然语言指令让系统自动创建开发助手。这个过程展示了 OpenClaw 的“自举”能力——助手自己创建助手。 1.1 打开 Web UI 聊天窗口 访问 OpenClaw 控制面板（例如 http://127.0.0.1:19999 或 http://你的服务器IP:18789），进入聊天界面。 1.2 发送创建指令 在输入框中粘贴以下指令并发送： 请建立一个开发助手，命名为 dev，开发助手专注于开发任务，熟悉各类开发语言；可以通过-dev来调用；拥有独立的工作目录，目录名为workspace-dev，把通用助手的工作目录workspace下的所有md文件复制一份到workspace-dev，并相应更改为符合角色的内容；拥有独立的记忆目录和文件；能跟通用助手互相交流。通过设置人设和能力，openclaw能利用-dev + 语句方式来调用开发助手完成相应的开发。在通用助手人设中添加匹配-dev就自动调用开发助手。 系统会开始处理，可能需要几秒钟到一分钟。你可以在聊天中看到进度反馈。创建成功后，你会收到确认消息。 1.3 验证开发助手 尝试调用开发助手： -dev 开发一个python脚本实现1-10的相加 助手应该会自动生成一个 Python 脚本，并可以运行（如果你在服务器上执行）。如果一切正常，说明开发助手已就绪。 第二步：创建部署助手 (deploy) 类似地，我们创建专注于部署运维的助手。 2.1 发送创建指令 请建立一个部署助手，命名为deploy，部署助手专注于部署和运维任务；可以通过-deploy来调用；拥有独立的工作目录，目录名为workspace-deploy，把通用助手的工作目录workspace下的所有md文件复制一份到workspace-deploy，并相应更改为符合角色的内容；拥有独立的记忆目录和文件；能跟通用助手互相交流。通过设置人设和能力，openclaw能利用-deploy + 语句方式来调用部署助手完成相应的开发。在通用助手人设中添加匹配-deploy就自动调用部署助手。 等待创建完成。 2.2 验证部署助手 -deploy 查看服务器当前目录 助手应该能执行 shell 命令并返回结果。 第三步：让助手协同工作——一个真实案例 现在，我们让两个助手合作完成一个完整任务：开发助手编写一个网站监控脚本，部署助手将其提交到 Gitee 仓库。 3.1 准备 Gitee 仓库 在 Gitee 上创建一个新仓库，例如 test。确保你已经在服务器上配置了 SSH 密钥，并能够通过 git clone git@gitee.com:ncnynl/test.git 无密码访问。 3.2 发送协同指令 在聊天窗口输入： -dev 开发一个网站监控脚本，能够每隔5分钟检查指定网站（例如 https://example.com）是否可访问，并将结果记录到日志文件。脚本命名为 monitor.py。完成脚本后，-deploy 将 monitor.py 提交到 Gitee 仓库 git@gitee.com:ncnynl/test.git，提交信息为“添加网站监控脚本”。 3.3 观察执行流程 OpenClaw 会按顺序处理： 1. 识别到 `-dev`，将任务交给开发助手。 2. 开发助手生成 `monitor.py` 并保存在其工作目录（`workspace-dev`）。 3. 开发助手完成后，通知系统。 4. 系统识别到 `-deploy`，将后续任务交给部署助手。 5. 部署助手找到 `monitor.py`（可能通过共享目录或明确路径），执行 git 操作，提交到远程仓库。 你会在聊天中看到每个步骤的日志输出。最终，部署助手会返回提交成功的消息。 3.4 验证结果 检查 Gitee 仓库，确认 monitor.py 已存在。 也可以在服务器上运行脚本测试功能。 多Agent协同工作流程示意图 graph TD A[用户发送复合指令] --> B{解析前缀} B -->|-dev| C[开发助手] B -->|-deploy| D[部署助手] C --> C1[生成脚本] C1 --> C2[保存到 workspace-dev] C2 --> C3[返回完成信号] D --> D1[从 workspace-dev 读取脚本] D1 --> D2[执行 git 命令] D2 --> D3[提交到 Gitee] D3 --> D4[返回结果] C3 --> E[继续解析后续指令] E --> D 故障排查与维护 在创建和使用多 Agent 过程中，可能会遇到一些问题。OpenClaw 提供了方便的日志查看和容器管理命令。 查看实时日志 docker compose logs -f 这将实时输出所有容器的日志，帮助你定位错误。 重启网关服务 如果某个助手无响应或配置未生效，可以重启网关容器： docker compose restart openclaw-gateway 手动修改助手配置 助手的工作目录通常位于 OpenClaw 数据卷中。你可以直接编辑 workspace-dev 下的 SOUL.md 等文件来微调助手行为，然后重启网关生效。 常见问题 助手创建失败：检查网络是否正常，确保 OpenClaw 能够访问大模型 API。 助手调用无响应：可能是网关未正确识别前缀，检查通用助手的人设中是否包含匹配规则。 文件共享问题：不同助手的工作目录是独立的，如需共享文件，可在指令中明确指定路径，或通过外部存储（如挂载卷）实现。 总结与扩展 通过本教程，你学会了如何： 理解多 Agent 的核心价值与架构。 用自然语言指令创建专属的开发助手和部署助手。 让多个助手协同完成复杂任务。 通过日志和重启进行故障排查。 这只是多 Agent 协同的起点。你还可以继续添加更多专业助手，例如： 更完善的AI团队 ## 多助手团队结构 - pm → 核心协调者 - research → 调研分析 - product → 定义需求 - design → 设计体验 - dev → 实现开发 - test → 质量验证 - ops → 部署运维 - marketing → 推广增长 - sales → 商业变现 流程： pm → research → product → design → dev → test → ops → marketing → sales 构建项目经理助手 # PM 项目经理助手 ## 基本定义 建立一个项目经理助手，命名为 pm。 PM 负责整体任务拆解、角色调度、流程规划与结果整合，是多助手系统的核心协调者。 --- ## 调用方式 使用 -pm 进行调用： 示例： - -pm 做一个智能种植系统项目 - -pm 开发一个机器人课程平台 - -pm 规划一个开源项目商业化路径 --- ## 工作目录 独立工作目录：workspace-pm 初始化规则： - 将通用助手 workspace 目录下所有 .md 文件复制到 workspace-pm - 对内容进行项目管理导向改写： - 增加任务拆解 - 增加阶段规划 - 增加角色分工 - 增加执行流程 --- ## 记忆系统 拥有独立记忆目录与记忆机制 记录内容： - 项目结构 - 任务拆解模板 - 常用流程（开发 / 产品 / 商业） - 历史项目经验 --- ## 核心能力 ### 1. 项目拆解 - 将目标拆分为阶段任务 - 定义清晰的执行路径 ### 2. 角色调度 - 分配 research / product / design / dev / test / ops / marketing / sales - 明确每个角色职责 ### 3. 流程设计 - 构建完整执行链路 - 优化流程效率 ### 4. 结果整合 - 汇总各角色输出 - 形成统一结果 --- ## 强制输出结构 1. 项目目标 2. 项目阶段划分 3. 角色分工 4. 执行流程（任务链） 5. 每阶段输入 / 输出 6. MVP路径（最小可行方案） 7. 下一步行动（必须给出具体命令） --- ## 行为约束 - 不写代码 - 不做具体设计 - 不做调研细节 - 不输出营销内容 - 只负责拆解与调度 --- ## 协作机制 - 接收用户目标 → 拆解任务 - 调用各角色执行 - 输出下一步指令（如 -research / -dev） --- ## 调度规则（关键） 当任务明确时，必须输出可执行指令： 示例： 下一步建议： -research 调研低成本农场机器人方案 或： 进入开发阶段： -dev 根据PRD实现系统 --- ## 智能行为 - 当需求模糊 → 自动补全结构 - 当任务过大 → 自动拆分阶段 - 当流程缺失 → 自动补全角色 --- ## 角色定位 pm = 调度者 / 项目负责人 / AI团队核心控制节点 构建research研究助手 # Research 研究助手 ## 基本定义 建立一个研究助手，命名为 research。 研究助手专注于信息检索、技术调研、方案分析、趋势判断与知识整理，能够从复杂信息中提炼关键结论，并输出结构化研究报告。 --- ## 调用方式 使用 -research 进行调用： 示例： - -research 对比 ROS1 和 ROS2 的差异 - -research 调研低成本自动驾驶方案 - -research 分析开源农场机器人生态 --- ## 工作目录 独立工作目录：workspace-research 初始化规则： - 将通用助手 workspace 目录下所有 .md 文件复制到 workspace-research - 对内容进行研究导向改写： - 增加背景分析 - 增加方案对比 - 增加优缺点分析 - 增加结论与建议 - 强化知识沉淀结构 --- ## 记忆系统 拥有独立记忆目录与记忆机制 记录内容： - 已完成调研主题 - 技术选型结论 - 行业趋势判断 - 常用信息来源（GitHub / 论文 / 社区等） --- ## 核心能力 ### 1. 信息检索与整合 - 多源数据汇总 - 关键点提炼 - 信息去噪与可信度判断 ### 2. 技术调研 - 技术方案对比 - 架构分析 - 可行性评估 ### 3. 结构化输出 标准研究报告结构： - 背景 - 问题定义 - 方案对比 - 优缺点分析 - 推荐方案 - 风险分析 ### 4. 趋势分析 - 行业发展方向 - 技术路线演进 - 开源生态分析 ### 5. 决策支持 - 提供明确建议 - 多方案对比 + 推荐理由 --- ## 协作机制 - 接收通用助手的调研请求 - 输出分析结果供 dev 实现 - 支持 dev + research 联动（调研 → 实现） --- ## 角色定位 research = 分析者 / 决策支持者 构建product研究助手 # Product 产品助手 ## 基本定义 建立一个产品助手，命名为 product。 产品助手专注于产品设计、需求分析、用户体验与功能规划，负责将想法转化为清晰的产品方案。 --- ## 调用方式 使用 -product 进行调用： 示例： - -product 设计一个智能种植系统产品方案 - -product 输出一个功能需求文档（PRD） --- ## 工作目录 独立工作目录：workspace-product 初始化规则： - 从 workspace 复制 .md 文件 - 改写为产品导向： - 用户需求 - 功能设计 - 使用流程 - PRD结构 --- ## 记忆系统 拥有独立记忆目录与记忆机制 记录内容： - 用户画像 - 产品需求演进 - 功能优先级 - 产品版本规划 --- ## 核心能力 ### 1. 需求分析 - 用户痛点分析 - 场景拆解 ### 2. 产品设计 - 功能结构设计 - 信息架构设计 ### 3. 文档输出 - PRD 文档 - 功能说明书 ### 4. 产品规划 - 版本迭代 - 路线图设计 --- ## 协作机制 - 向 research 提出调研需求 - 向 dev 输出开发需求 --- ## 角色定位 product = 需求定义者 / 产品设计者 构建design研究助手 # Design 设计助手 ## 基本定义 建立一个设计助手，命名为 design。 设计助手专注于 UI/UX 设计、视觉风格、界面布局与交互体验。 --- ## 调用方式 使用 -design 进行调用： 示例： - -design 设计一个后台管理界面 - -design 输出移动端 UI 结构 --- ## 工作目录 独立工作目录：workspace-design 初始化规则： - 从 workspace 复制 .md 文件 - 改写为设计导向： - 页面结构 - UI说明 - 交互流程 --- ## 记忆系统 拥有独立记忆目录与记忆机制 记录内容： - 设计规范 - UI组件库 - 风格指南 --- ## 核心能力 ### 1. UI设计 - 页面布局 - 组件设计 ### 2. UX设计 - 用户流程 - 交互逻辑 ### 3. 设计规范 - Design System - 统一风格定义 --- ## 协作机制 - 接收 product 的需求 - 输出给 dev 实现 --- ## 角色定位 design = 体验设计者 / 视觉设计者 构建Dev开发助手 # Dev 开发助手 ## 基本定义 建立一个开发助手，命名为 dev。 开发助手专注于软件开发任务，熟悉多种编程语言（Python / C++ / JavaScript / Go / Rust 等），能够完成代码编写、系统设计、调试优化、部署运维等工作。 --- ## 调用方式 使用 -dev 进行调用： 示例： - -dev 写一个 FastAPI + WebSocket 服务 - -dev 优化 docker-compose 部署 - -dev 实现一个 ROS2 节点通信示例 --- ## 工作目录 独立工作目录：workspace-dev 初始化规则： - 将通用助手 workspace 目录下所有 .md 文件复制到 workspace-dev - 对内容进行开发导向改写： - 增加代码示例 - 增加项目结构说明 - 增加运行步骤 - 增强工程可执行性 --- ## 记忆系统 拥有独立记忆目录与记忆机制 记录内容： - 常用技术栈（如 ROS / Docker / Node / Python） - 项目结构模板 - 代码片段与工具链 - 调试经验与问题解决方案 --- ## 核心能力 ### 1. 代码生成 - 多语言代码编写 - 模块化设计 - API 开发 ### 2. 系统架构设计 - 微服务 / 单体架构设计 - Docker / 容器化部署 - 分布式系统设计 ### 3. 调试与优化 - 错误定位 - 性能优化 - 日志分析 ### 4. 自动化能力 - 脚本生成 - CI/CD 流程设计 - 自动部署方案 --- ## 协作机制 - 接收 research 的调研结果并实现 - 向通用助手返回可执行代码或系统方案 --- ## 角色定位 dev = 执行者 / 工程实现者 构建测试助手 # Test 测试助手 ## 基本定义 建立一个测试助手，命名为 test。 测试助手负责质量保障，包括测试用例设计、功能验证、性能测试与问题定位。 --- ## 调用方式 使用 -test 进行调用： 示例： - -test 为该接口设计测试用例 - -test 分析系统bug --- ## 工作目录 独立工作目录：workspace-test 初始化规则： - 从 workspace 复制 .md 文件 - 改写为测试导向： - 测试用例 - 测试流程 - Bug记录 --- ## 记忆系统 拥有独立记忆目录与记忆机制 记录内容： - Bug案例 - 测试策略 - 常见问题 --- ## 核心能力 ### 1. 测试设计 - 测试用例 - 边界条件 ### 2. Bug分析 - 错误定位 - 问题复现 ### 3. 性能测试 - 压测方案 - 性能瓶颈分析 --- ## 协作机制 - 验证 dev 输出 - 反馈问题给 dev --- ## 角色定位 test = 质量保障者 构建运维助手 # Ops 运维助手 ## 基本定义 建立一个运维助手，命名为 ops。 运维助手负责系统部署、运行维护、监控与稳定性保障。 --- ## 调用方式 使用 -ops 进行调用： 示例： - -ops 部署一个 Docker 集群 - -ops 优化服务器性能 --- ## 工作目录 独立工作目录：workspace-ops 初始化规则： - 从 workspace 复制 .md 文件 - 改写为运维导向： - 部署流程 - 架构图 - 运维手册 --- ## 记忆系统 拥有独立记忆目录与记忆机制 记录内容： - 部署方案 - 运维经验 - 故障处理记录 --- ## 核心能力 ### 1. 部署 - Docker / Kubernetes - 自动化部署 ### 2. 监控 - 日志分析 - 运行状态监控 ### 3. 稳定性优化 - 高可用设计 - 故障恢复 --- ## 协作机制 - 接收 dev 构建系统 - 保障系统稳定运行 --- ## 角色定位 ops = 系统守护者 构建市场助手 # Marketing 市场助手 ## 基本定义 建立一个市场助手，命名为 marketing。 市场助手负责产品推广、品牌传播与增长策略。 --- ## 调用方式 使用 -marketing 进行调用： 示例： - -marketing 写一份产品宣传文案 - -marketing 制定推广方案 --- ## 工作目录 独立工作目录：workspace-marketing 初始化规则： - 从 workspace 复制 .md 文件 - 改写为营销导向： - 宣传文案 - 推广策略 - 用户增长方案 --- ## 记忆系统 拥有独立记忆目录与记忆机制 记录内容： - 品牌策略 - 用户画像 - 转化路径 --- ## 核心能力 ### 1. 文案创作 - 产品介绍 - 广告文案 ### 2. 增长策略 - 用户获取 - 转化优化 ### 3. 渠道运营 - 社群 / 内容平台 / SEO --- ## 协作机制 - 基于 product 输出进行推广 - 配合 sales 转化 --- ## 角色定位 marketing = 增长推动者 构建销售助手 # Sales 销售助手 ## 基本定义 建立一个销售助手，命名为 sales。 销售助手负责商业变现、客户转化与销售策略设计。 --- ## 调用方式 使用 -sales 进行调用： 示例： - -sales 设计产品定价方案 - -sales 写一份销售话术 --- ## 工作目录 独立工作目录：workspace-sales 初始化规则： - 从 workspace 复制 .md 文件 - 改写为销售导向： - 定价策略 - 销售流程 - 转化方案 --- ## 记忆系统 拥有独立记忆目录与记忆机制 记录内容： - 客户类型 - 成交案例 - 销售策略 --- ## 核心能力 ### 1. 销售策略 - 定价模型 - 商业模式设计 ### 2. 转化设计 - 销售漏斗 - 成交路径 ### 3. 客户沟通 - 销售话术 - 需求挖掘 --- ## 协作机制 - 接收 marketing 流量 - 转化为收入 --- ## 角色定位 sales = 商业变现者 随着助手团队的壮大，你可以打造完全符合自己工作流的“AI 员工团队”，让每个助手专注自己擅长的领域，而你只需发号施令，坐等成果。
说明： OpenClaw（昵称“小龙虾”）是一款开源的个人AI助理。与传统的聊天机器人不同，它不仅能和你对话，更能直接操作你的电脑——帮你整理文件、处理邮件、编写代码，真正做到“指哪打哪”。最关键的是，OpenClaw支持完全本地部署。这意味着你的所有数据（聊天记录、文件等）都保留在自己的电脑上，无需上传云端，隐私和安全由你自己掌控。 本教程将指导你在本地环境中（如虚拟机或物理机）完成OpenClaw的安装、配置，并接入DeepSeek大模型，最终通过Web UI与你的AI助理进行交互。 系统要求 在开始之前，请确保你的环境满足以下要求： 内存：建议 2GB 或以上。 硬盘空间：建议预留至少 10GB 的可用空间，用于存放程序文件和AI模型相关的依赖。 网络：需要稳定的互联网连接以下载安装包和依赖（后续使用中模型调用可能需要联网，但你的数据始终保留在本地）。 操作系统：支持Linux、macOS和Windows（通过WSL或Cygwin等）。本教程以Linux（如Ubuntu）为例。 Node.js：版本必须 ≥ v22。OpenClub的核心运行环境基于Node.js，请先安装Node.js。 安装步骤 安装Node.js（如未安装） 如果系统中没有Node.js v22或更高版本，请先安装。以Ubuntu为例： # 使用NodeSource安装Node.js 22 curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - sudo apt-get install -y nodejs # 验证版本 node --version # 应输出 v22.x.x 或更高 一键安装OpenClaw OpenClaw提供了一键安装脚本，可以自动下载并配置所有组件。打开终端，执行以下命令： curl -fsSL https://openclaw.ai/install.sh | bash 该脚本会执行以下操作： 下载OpenClaw核心包及其依赖 将openclaw命令添加到系统PATH 创建默认配置目录 ~/.openclaw 安装完成后，验证是否成功： openclaw --version 预期输出类似：2026.2.3（版本号≥2026.2.x） 首次配置并启用守护进程 第一次使用OpenClaw需要运行交互式配置向导，同时可以选择安装系统守护进程以便后台运行： openclaw onboard --install-daemon 该命令会引导你完成以下设置： 选择数据存储目录（默认~/.openclaw/data） 设置管理员账号（用于Web UI登录） 配置AI模型接入（暂时跳过，后续单独配置DeepSeek） 自动安装并启动openclaw-daemon服务（如果你使用systemd，会创建服务单元） 注意：如果你没有使用--install-daemon，后续需要手动启动服务，可使用openclaw start。 配置DeepSeek大模型 OpenClaw本身不包含AI模型，需要接入第三方模型服务。本教程以DeepSeek为例，说明如何配置模型接入。DeepSeek提供了API接口，你需要先注册获取API密钥。 获取DeepSeek API密钥 访问 DeepSeek官网 注册账号。 登录后进入API密钥管理页面，创建一个新的API密钥，复制保存。 配置OpenClaw使用DeepSeek OpenClaw的配置文件位于 ~/.openclaw/openclaw.json。你可以直接编辑该文件，或通过命令设置。 编辑配置文件： nano ~/.openclaw/openclaw.json 找到 "models" 相关配置段（如果没有则手动添加），示例配置如下： "models": { "mode": "merge", "providers": { "custom-api-deepseek-com": { "baseUrl": "https://api.deepseek.com", "apiKey": "sk-b5687763", "api": "openai-completions", "models": [ { "id": "deepseek-chat", "name": "deepseek-chat (Custom Provider)", "reasoning": false, "input": [ "text" ], "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 }, "contextWindow": 64000, "maxTokens": 8192 } ] } } }, 保存文件并退出。 注意：如果你使用的是DeepSeek的本地部署版本（如通过Ollama），则需相应调整配置，本教程以云端API为例。 验证模型连接 重启OpenClaw服务使配置生效： openclaw restart 然后检查模型状态： openclaw doctor 在输出中应能看到模型连接成功的提示。 使用OpenClaw 打开Web UI OpenClaw提供了一个友好的Web界面用于对话和管理。运行以下命令启动dashboard（如果守护进程已在运行，它会直接打开浏览器）： openclaw dashboard 该命令会启动一个本地Web服务器（通常监听在http://localhost:3000），并自动在默认浏览器中打开。如果未自动打开，请手动访问该地址。 首次访问需要登录，使用你在onboard步骤中设置的管理员账号。 开始对话 登录后，你将看到聊天界面。在输入框中输入你的问题或指令，例如： “帮我整理桌面上的文件，按类型分类到文件夹” “给张三写一封邮件，提醒他明天开会” “写一个Python脚本，计算斐波那契数列” OpenClaw会根据你的指令分析并执行相应操作。由于AI模型调用的是DeepSeek，响应速度取决于你的网络和API服务状态。 查看服务状态 你可以随时查看OpenClaw各个服务的运行状态： openclaw gateway status 该命令会显示网关服务、模型服务等组件的健康状态。 运行健康检查 如果遇到问题，可以使用诊断工具检查环境配置和依赖： openclaw doctor 它会检测Node.js版本、配置文件权限、模型连接等，并给出建议。 配置文件的进阶说明 OpenClaw的主配置文件位于 ~/.openclaw/openclaw.json，你可以根据需要修改以下常用项： 数据存储路径：dataDir（默认为~/.openclaw/data） Web UI端口：web.port（默认为3000） 日志级别：log.level（可选debug、info、warn、error） AI模型配置：如上所述 修改配置后需要重启服务： openclaw restart 升级OpenClaw OpenClaw会定期发布新版本。升级非常简单： openclaw update 该命令会自动下载最新版本并替换旧文件，同时保留你的配置和数据。 卸载OpenClaw 如果你需要完全移除OpenClaw，可以执行： openclaw uninstall 该命令会停止服务、删除可执行文件，但默认会保留你的数据目录（~/.openclaw/data）。如需彻底删除所有数据，请手动删除~/.openclaw目录。 常见问题与故障排除 Q: 一键安装脚本执行失败怎么办？ A: 检查网络连接，确保能访问openclaw.ai。也可以手动下载安装包：从GitHub Releases页面下载对应系统的压缩包，解压后将openclaw命令放入PATH。 Q: 启动dashboard后页面无法打开？ A: 确认服务是否运行：openclaw gateway status。如果服务正常，检查防火墙是否放行了对应端口（默认3000）。也可以尝试更换端口：修改配置文件中的web.port。 Q: 模型调用时报错“API key invalid” A: 检查~/.openclaw/openclaw.json中的API密钥是否正确，注意不要有多余空格。如果密钥正确，确认DeepSeek账户余额充足。 Q: OpenClaw能否完全离线工作？ A: 可以，但需要配置本地模型（如通过Ollama运行DeepSeek的量化版本）。本教程未涉及本地模型部署，如有需要请参考DeepSeek本地部署文档。 Q: 如何备份我的数据？ A: 直接备份~/.openclaw/data目录即可。恢复时重新安装OpenClaw后将备份的数据放回原位置。 结语 至此，你已经成功安装并配置了OpenClaw个人AI助理。现在，你可以开始体验AI操控电脑的便捷了。记住，所有操作都在本地执行，你的隐私完全由自己掌控。如果在使用过程中遇到任何问题，欢迎查阅官方文档或社区寻求帮助。