NapCat(OneBot v11)到 OpenClaw 的 QQ 频道插件。
它的目标不是“给 QQ 接一个普通聊天机器人”,而是把 QQ 群聊接入一条稳定的、可审批、可反思、可沉淀记忆的 runtime 主链,让 persona-core 作为独立人格长期运行,voice-organ 作为唯一表达器官负责自然化输出。
- GitHub:https://github.com/saymyzj/openclaw-NapCatQQ
- English README: README.en.md
- 贡献说明:CONTRIBUTING.md
- 独立人格:QQ群聊主脑不是一次性 prompt,而是长期存在的
persona-core - runtime-first:正式主链走 OpenClaw runtime,不把 QQ 主链退回到临时
/v1/chat/completions - 双层表达:
persona-core决定“说什么”,voice-organ决定“怎么说” - 审批闭环:高风险
exec会走 OpenClaw 原生审批,再通过 NapCat 回到管理员私聊 - 记忆闭环:插件维护 world ledger,reflection 和 daily memory 交给人格体自己吸收
本插件参考了麦麦聊天机器人项目 MaiBot,在“面向真实社群关系的聊天机器人”这条方向上受益很多,特此致谢:
这个插件负责五件事:
- 把 NapCat / QQ 世界接到 OpenClaw
- 把群聊真实历史沉淀为 canonical ledger
- 把正式群聊回合稳定路由到
persona-core -> voice-organ - 把审批、控制面和异步补结果纳入同一个运行时边界
- 给 reflection 与 daily memory 提供样本和维护入口
正式群聊主链只有一条:
NapCat inbound -> persona-core runtime run -> voice-organ -> QQ outbound
persona-core:唯一正式群聊主脑,决定是否说话以及核心意思voice-organ:唯一表达器官,把核心意思改写得更自然,但不新增事实- NapCat 插件:世界适配层、canonical ledger、控制面入口、审批桥接层
- canonical ledger:插件维护,保存“世界真相” 内容包括群消息、图片摘要、最终发出的机器人消息、reply anchor、engagement state
- agent session:OpenClaw runtime 维护,保存人格连续性 内容包括最近几轮内部理解、工具调用、system events
- reflection / daily memory:位于两者之间 用来把真实聊天样本逐步沉淀为人格自己的可吸收材料
/approve ...:管理员私聊入口,用于处理exec审批/reflect [groupId] [limit]:管理员私聊入口,用于手动触发 reflection- 插件会在普通聊天流之前拦截这些命令,避免污染正式会话
- 自动 reflection:后台 heartbeat 批量消费 pending reflection samples
- daily memory:后台 heartbeat 按“日期 + 群”增量沉淀
memory/YYYY-MM-DD.md同一天仍是一个文件,但应按## group_id: <id>分段 - async followup:审批完成后的结果先进入 followup job,再尽量回到 persona/voice 语义
- 图片理解:问图与图片摘要走独立的 vision 子链路,优先复用稳定 session key,避免一张图生成一个新 session transcript
下面这套流程按“原生 OpenClaw + NapCat + 本插件”来写,目标是从头到尾跑通。
你至少需要准备好:
- 一套可运行的 OpenClaw
- Node.js
>= 22 - 一个可正常登录 QQ 的 NapCat 环境
- 一个用来收审批和控制面消息的管理员 QQ
- 至少 3 个 OpenClaw agent:
main、persona-core、voice-organ
如果你只装插件、不准备 persona-core / voice-organ,插件当然能被加载,但你得不到这套“独立人格主链”的核心价值。
先在 NapCat 侧完成 QQ 登录,并启用 OneBot v11 WebSocket 服务。
你需要确认这几个信息:
host推荐127.0.0.1port例如3001path默认/access token强烈建议设置,不要裸奔
推荐做法:
- 让 NapCat 只监听本机回环地址
- 如果 OpenClaw 与 NapCat 不在同一台机器上,只通过受控内网、Tailscale 或反向代理暴露
- 不要把 NapCat WebSocket 直接暴露到公网
推荐保留三个主体:
main负责可信私聊、控制面、日常维护persona-core负责正式群聊人格回合、reflection、daily memory 写入voice-organ负责群聊表达润色,不应该拥有读写/exec 等高权限工具
推荐目录布局:
~/.openclaw/
agents/
main/
persona-core/
voice-organ/
workspace/
workspace-persona-core/
workspace-voice-organ/
extensions/
napcat-qq/
mkdir -p ~/.openclaw/extensions
git clone https://github.com/saymyzj/openclaw-NapCatQQ ~/.openclaw/extensions/napcat-qq
cd ~/.openclaw/extensions/napcat-qq
npm install
npm run build原生安装方式优先推荐:
openclaw plugins install -l ~/.openclaw/extensions/napcat-qq这个命令通常会把插件写进你的 openclaw.json 的 plugins 段,包括:
plugins.allowplugins.load.pathsplugins.entriesplugins.installs
如果你更喜欢手动管理,也可以自己编辑 openclaw.json,但推荐先让原生命令落一版,再按需微调。
下面是一份推荐的单账号配置形态。它不是最小配置,而是更接近“独立人格运行时”的实际部署配置。
完成配置后,重启 OpenClaw gateway。插件正常加载时,你应该能在日志里看到:
[plugins] [napcat] plugin loaded
下面这一节以 channels.napcat 为准。
| 参数 | 是否必需 | 说明 |
|---|---|---|
host |
是 | NapCat WebSocket 地址,通常是 127.0.0.1 |
port |
是 | NapCat WebSocket 端口 |
accessToken |
强烈建议 | NapCat 访问令牌 |
path |
否 | WebSocket 路径,默认 / |
| 参数 | 是否必需 | 说明 |
|---|---|---|
monitorGroups |
否 | 白名单群号列表;这些群会启用 periodic patrol |
autoIntervene |
否 | 是否启用白名单群自动巡检 |
autoCheckIntervalMs |
否 | 巡检间隔,默认 30000 |
autoCheckMessageThreshold |
否 | 累积消息阈值,默认 10 |
requireMention |
否 | 是否强制只有被 @ 时才立即处理 |
historyLimit |
否 | 插件侧历史上下文保留条数 |
rateLimitMs |
否 | 发送节流,避免 QQ 侧限流 |
| 参数 | 是否必需 | 说明 |
|---|---|---|
renderMarkdownToPlain |
否 | 是否把 Markdown 压成纯文本再发 QQ |
multimodalImagesEnabled |
否 | 是否开启图片摘要 / 问图能力 |
multimodalImageMaxCount |
否 | 单轮最多处理几张图片 |
| 说明: | ||
| 图片理解会尽量复用稳定的 vision session key,减少零散 session transcript。 | ||
| 对 QQ/NapCat 这类图片源,插件会自动回退为内联图片内容,避免被 Gateway 的 URL 安全限制拦掉。 |
| 参数 | 是否必需 | 说明 |
|---|---|---|
persona.enabled |
否 | 是否启用人格主链 |
persona.coreAgentId |
否 | 正式群聊主脑,默认 persona-core |
persona.voiceAgentId |
否 | 表达器官,默认 voice-organ |
persona.voiceOnGroupOnly |
否 | 是否仅在群聊使用 voice-organ |
| 参数 | 是否必需 | 说明 |
|---|---|---|
maintenance.enabled |
否 | 是否启用后台维护循环 |
maintenance.reflectionEnabled |
否 | 是否自动跑 reflection backlog |
maintenance.reflectionIntervalMs |
否 | reflection 心跳间隔 |
maintenance.reflectionBatchSize |
否 | 每次处理多少条 reflection sample |
maintenance.dailyMemoryEnabled |
否 | 是否自动沉淀 daily memory |
maintenance.dailyMemoryIntervalMs |
否 | daily memory 心跳间隔,当前默认 4 小时 |
maintenance.dailyMemoryBatchSize |
否 | 每次处理多少个群的增量 |
| 说明: | ||
| daily memory 不再有“启动后 30 秒补跑”bootstrap;它只在下一个 heartbeat 或手动触发时运行。 | ||
同一天仍写入一个 memory/YYYY-MM-DD.md,但推荐严格按 ## group_id: <id> 分段维护。 |
| 参数 | 是否必需 | 说明 |
|---|---|---|
whitelistUserIds |
否 | 私聊白名单;空数组表示所有人可私聊 |
admins |
强烈建议 | 管理员 QQ 列表,用于审批和控制面 |
disableCommandsForAgents |
强烈建议 | 在 QQ 会话中对指定 agent 禁用 /status、!bash 等命令式输入 |
- 在任意群里
@机器人 - 插件会把这一轮正式路由给
persona-core - 如果
persona-core决定回复,再交给voice-organ
- 把群号放进
monitorGroups - 插件会在“时间到”或“消息数达到阈值”时做 periodic patrol
- 如果人格体判断值得参与,再正式发言
管理员私聊支持:
/approve ...处理 OpenClawexec审批/reflect [groupId] [limit]手动触发 reflection
- 当
persona-core触发需要审批的工具动作时,群里先收到一条等待提示 - 审批完成后,插件会把 followup 结果收进持久化 job
- 发送前会尽量重新走 persona / voice finalize
- 同一份 followup 结果会做去重,避免重复发群
风险:
- NapCat WebSocket 一旦裸露到公网,等同于把 QQ 机器人入口直接暴露出去
accessToken泄露后,攻击者可能伪造或读取频道流量
策略:
- 默认只监听
127.0.0.1 - 必须设置
accessToken - 跨机部署时只走受控内网、VPN、Tailscale 或零信任代理
风险:
- 插件会把群消息、图片摘要、reflection sample、followup job、图片缓存索引写到本地 sqlite
- 这意味着你要对磁盘、备份、日志和导出文件负责
策略:
- 只监控你明确同意纳入系统的群
- 对运行机器做磁盘加密和账户隔离
- 谨慎备份
group_chat.sqlite、workspace 和记忆文件 - 如果你依赖问图追问能力,要意识到 sqlite 中也会保留可复用的图片缓存记录
风险:
persona-core可以拥有exec- 如果审批边界太宽,模型有可能把高风险操作推到管理员确认链路上
策略:
- 只给
persona-core打开真正需要的工具 exec必须走 OpenClaw 原生审批- 管理员目标必须是你自己可控的私聊 QQ
- 审批前先看清命令和意图,不要机械同意
风险:
- 如果让
voice-organ拥有工具、读写或系统访问权限,它就不再只是表达器官 - 如果把所有群聊碎片无差别提升为长期记忆,人格会很快漂移
策略:
voice-organ保持无工具、无读写的窄权限- 长期记忆留给
persona-core在 reflection / daily memory 中慢慢吸收 - 不要让单次对话直接重写
SOUL.md
风险:
- 用户在 QQ 里直接发
/status、!bash、/model之类的字符串,可能污染会话或误触控制语义
策略:
- 把
persona-core、voice-organ加进disableCommandsForAgents - 控制面只走管理员私聊
- 群聊
@触发正式人格回合 - 白名单群 periodic patrol
- 图片摘要与问图上下文
persona-core -> voice-organ正式主链exec审批桥接到管理员私聊/approve与/reflect控制面拦截- 自动 reflection heartbeat
- daily memory 增量沉淀
- 审批 followup 持久化与去重
- 自动维护目前默认由插件内 heartbeat 驱动
- 如果你已经有成熟的 OpenClaw cron 体系,可以再把维护任务拆到 cron,但 README 这里先按插件原生维护循环说明
因为它解决的不是“QQ 上能不能发消息”,而是下面这件更难的事:
把 QQ 群聊接到一个持续存在、拥有会话连续性、能接受审批约束、能沉淀真实表达样本、还能逐步形成稳定人格记忆的 OpenClaw runtime 里。
如果你要的只是“QQ 自动回复”,这套东西会显得重。 如果你要的是“独立人格体在 QQ 里长期活着”,这套分层就是必要成本。
{ "agents": { "list": [ { "id": "main", "default": true, "workspace": "/path/to/workspace", "agentDir": "/path/to/agents/main/agent", "model": "openai-codex/gpt-5.4" }, { "id": "persona-core", "workspace": "/path/to/workspace-persona-core", "agentDir": "/path/to/agents/persona-core/agent", "model": "openai-codex/gpt-5.4", "tools": { "allow": [ "read", "write", "edit", "apply_patch", "exec", "web_fetch", "memory_search", "memory_get" ], "exec": { "host": "gateway", "security": "allowlist", "ask": "on-miss" } } }, { "id": "voice-organ", "workspace": "/path/to/workspace-voice-organ", "agentDir": "/path/to/agents/voice-organ/agent", "model": "openrouter/bytedance-seed/seed-2.0-mini", "tools": { "allow": [], "deny": [ "exec", "read", "write", "edit", "apply_patch", "web_search", "web_fetch", "memory_search", "memory_get", "group:runtime", "group:fs", "group:ui", "group:messaging", "gateway", "nodes", "cron", "browser" ] } } ] }, "bindings": [ { "agentId": "main", "match": { "channel": "napcat", "peer": { "kind": "direct", "id": "user:1234567890" } } } ], "approvals": { "exec": { "enabled": true, "mode": "targets", "agentFilter": ["persona-core"], "targets": [ { "channel": "napcat", "to": "napcat:1234567890" } ] } }, "channels": { "napcat": { "host": "127.0.0.1", "port": 3001, "accessToken": "<napcat_access_token>", "path": "/", "monitorGroups": [123456789], "autoIntervene": true, "autoCheckIntervalMs": 30000, "autoCheckMessageThreshold": 10, "historyLimit": 100, "rateLimitMs": 1000, "renderMarkdownToPlain": true, "multimodalImagesEnabled": true, "multimodalImageMaxCount": 3, "whitelistUserIds": ["1234567890"], "admins": ["1234567890"], "persona": { "enabled": true, "coreAgentId": "persona-core", "voiceAgentId": "voice-organ", "voiceOnGroupOnly": true }, "maintenance": { "enabled": true, "reflectionEnabled": true, "reflectionIntervalMs": 43200000, "reflectionBatchSize": 5, "dailyMemoryEnabled": true, "dailyMemoryIntervalMs": 14400000, "dailyMemoryBatchSize": 2 }, "disableCommandsForAgents": ["persona-core", "voice-organ"] } } }