Skip to content

配置参考

本页是完整的配置项参考。所有默认值都直接取自 src/config.tsRECEIVE_DEFAULTSQUOTE_DEFAULTSresolveSnowLumaAccount 函数——如果这页文档和你实际观察到的行为对不上,以 src/config.ts 为准。

解析规则

在看具体的表格之前,先了解三条贯穿所有配置解析的规则(都实现在 src/config.ts):

  • 显式配置 > 环境变量 > 内置默认值,且环境变量只对 default 账号生效(见下方环境变量)。
  • 类型不匹配会静默退化为默认值,不会抛异常。布尔项用 typeof value === "boolean" 判断,不是布尔就用默认值;数字项统一转换为 Number(value),非有限数(NaN/Infinity)或不满足正负约束就退回默认值,否则 Math.floor() 取整。
  • accounts 之外都是 default 账号的字段。写在 channels.snowluma 顶层的字段(accounts 本身除外)构成 default 账号;额外账号必须写在 channels.snowluma.accounts.<id> 下,结构与 default 账号完全一致(详见多账号)。

顶层账号选项

写在 channels.snowlumadefault 账号)或 channels.snowluma.accounts.<id>(命名账号)下的字段。

Key类型默认值说明
enabledbooleantrue是否启用该账号。判定逻辑是 !== false,也就是说除了显式的 false,任何其他值(包括缺省)都视为启用。
namestring展示名,可选。
wsUrlstring""default 账号可回退到 SNOWLUMA_WS_URLSnowLuma 的 OneBot WebSocket 地址,例如 ws://127.0.0.1:3001/。命名账号没有环境变量回退,必须显式配置。
httpUrlstringundefineddefault 账号可回退到 SNOWLUMA_HTTP_URL可选的 HTTP API 地址。设置后,一次性 action 调用(Agent 工具、非网关进程里的发送)会优先走 HTTP 而不是新开一个 WebSocket(见 src/client.tsacquireActionClient)。
accessTokenstringundefineddefault 账号可回退到 SNOWLUMA_ACCESS_TOKEN,其次 SNOWLUMA_TOKENSnowLuma 的 access token。
selfIdnumber | stringundefineddefault 账号可回退到 SNOWLUMA_SELF_ID机器人自己的 QQ 号。省略时网关启动阶段会调用 get_login_info 自动探测;探测失败则群聊 @ 触发永远不会命中(详见三种接收模式)。字符串会被 Number() 转换,转换失败则视为未设置。
allowFromstring[]undefined(未设置 = 允许所有来源)来源白名单,元素形如 "private:123" / "group:456",或通配符 "*"
denyFromstring[]undefined来源黑名单,在 allowFrom 判定之后生效,始终优先——即使某个 peer 同时命中 allowFromdenyFrom,结果也是拒绝。
groupAutoReactbooleanfalse是否对触发了 Agent 的入站群消息自动加表情回应(groupAutoReact === true 才算启用,其余任何值都是 false)。
groupAutoReactEmojiIdnumber | string1groupAutoReact 使用的 QQ 表情 id。
replyToTriggerbooleantrue回复是否以 QQ 引用(quote-reply)触发消息的形式发送;对 realtime 批次引用开窗那条消息,对 /summary 批次引用命令那条消息(digest 批次没有单条"触发消息"可引用)。
textChunkLimitnumber4500出站文本按此字符数分块发送(正整数,非法值回退默认值)。
requestTimeoutMsnumber30000SnowLuma action 调用超时(毫秒)。
debugbooleanfalse调试模式:把每条出站消息的原始载荷(目标 + 渲染出的 OneBot 段)打进日志。网关回复路径经 log.info 输出,主机直发路径(channel.ts)经 console.info 输出。仅 true 才算启用。
reconnectobject见下表WebSocket 重连调优,直接传给 @snowluma/sdkSnowLumaWebSocketClientOptions.reconnect
receiveobject见下方各表接收模式的配置(mention / digest / summary / realtime / history)。
quoteobject见下表引用/合并转发主动解析配置。
toolsobject见下表Agent 工具注册开关。
accountsobject额外命名账号,仅在 default 账号(顶层 channels.snowluma)下有意义,见多账号

receive.mention —— 被 @ 或命中关键词时触发

Key类型默认值说明
enabledbooleantrue是否启用该模式。false 是绝对开关——为 false 时,无论消息内容如何,这个模式都不会触发。
requireMentionInGroupbooleantrue群聊中是否要求 @ 机器人(或命中关键词/回复自身消息)才触发。
keywordsstring[][]无需 @ 也能触发的关键词列表。传入非数组会被归一化为 [];数组元素统一 String() 化并去除首尾空白,空字符串被过滤掉。
keywordMatch"contains" | "prefix" | "exact" | "regex""contains"关键词匹配方式:contains 包含匹配、prefix 前缀匹配(忽略消息前导空白)、exact 全字匹配(忽略消息首尾空白)、regex 把关键词当正则表达式(无效正则会静默跳过,不会抛异常)。传入其他值一律回退为 contains
caseSensitivebooleanfalse关键词匹配是否大小写敏感。
triggerOnReplyToSelfbooleantrue群聊中回复机器人自己发过的消息是否算触发。
alwaysReplyInDirectbooleantrue私聊消息是否无条件触发(不需要关键词或其他条件)。

判定顺序、atAll 为什么不算命中、selfId 缺失时的行为,详见三种接收模式 · mention

receive.digest —— 定时/达量摘要

Key类型默认值说明
enabledbooleanfalse是否启用该模式(默认关闭,是三个模式里唯一默认关闭的)。
intervalMsnumber300000(5 分钟)窗口的最大存活时间;到期后如果消息数达到 minMessages 就 flush,否则重置计时器继续等。正整数,非法值回退默认值。
maxMessagesnumber50缓冲消息数达到此值立即 flush(不受 minMessages 约束——这是"够多了"而不是"够久了"的判定路径)。
minMessagesnumber3低于该消息数时,intervalMs 到期也不会 flush。内部会被夹到不超过 maxMessagesMath.min(minMessages, maxMessages)),避免配置出一个永远无法达到、因而永远无法 flush 的窗口。
promptstring见下方flush 时拼接在聊天记录前的指令文字。传入空字符串或纯空白会回退到默认值。
scope"group" | "direct" | "all""group"摘要引擎观察哪些聊天类型。传入其他值回退为 "group"
peersstring[][]只观察这些 peer(如 "group:123");为空表示 scope 范围内全部观察。
maxTranscriptCharsnumber20000交给 Agent 的聊天记录字符数硬上限。

默认 promptDEFAULT_DIGEST_PROMPT,逐字取自 src/config.ts):

以下是这段时间的群聊记录。请用简洁的中文归纳讨论的主题、结论和待办事项;如果没有值得汇报的内容,只回复 SKIP。

minMessages 的抑制-重试语义、SKIP 静默、命令注入防护,详见三种接收模式 · digest

receive.summary —— /summary 主动总结命令

Key类型默认值说明
enabledbooleantrue是否启用 /summary 命令(默认开启)。关掉后命令词会退回普通消息,照常走 mention/realtime 流程。
commandsstring[]["/summary", "/总结"]触发命令词,匹配消息去掉前导 @机器人 后的开头。大小写不敏感;传入空数组会回退到默认值(否则就没有任何词能触发了)。
countnumber100命令没带数字时,拉取并总结最近多少条消息。会被 maxCount 夹住。
maxCountnumber200用户通过 /summary <n> 能请求的条数上限。
promptstring见下方拼在聊天记录前的指令文字。传入空字符串或纯空白会回退到默认值。
scope"group" | "direct" | "all""all"哪些聊天类型可以使用该命令。传入其他值回退为 "all"
peersstring[][]只有这些 peer 可以使用(如 "group:123");为空表示 scope 范围内全部可用。
maxTranscriptCharsnumber20000交给 Agent 的聊天记录字符数硬上限。

默认 promptDEFAULT_SUMMARY_PROMPT,逐字取自 src/config.ts):

以下是这个会话最近的聊天记录。请用简洁的中文总结其中讨论的主题、结论和待办事项,必要时按话题分点列出。这是用户主动请求的总结,请直接给出总结内容,不要回复 SKIP。

digest 的区别、消息从哪里来、失败时会回什么,详见接收模式 · summary

receive.realtime —— 亚秒级消息聚合

Key类型默认值说明
enabledbooleantrue是否启用聚合窗口。关闭后,触发消息会立即单独 flush(reason: "immediate"),不再等待聚合。
windowMsnumber800静默期:每条新消息都会重置这个计时器,静默期满即 flush。非负整数(允许 0),非法值回退默认值。注释里强调这个值应保持在 1000ms 以下才能算"realtime"。
maxWindowMsnumber3000窗口最长可以被撑开多久(硬上限),不受静默期计时器无限拖延。正整数。
maxMessagesnumber10缓冲消息数达到此值立即 flush。
maxCharsnumber8000缓冲文本(各消息 text 字段长度之和)超过此值立即 flush。

窗口如何按 ${peerId}::${senderId} 维度独立维护、为什么只有触发消息才能开窗,详见接收模式 · realtime

receive.history —— 回复时带入的历史聊天上下文

Key类型默认值说明
enabledbooleantrue是否累积近期消息,并在触发回复时作为历史上下文一并带入。这个队列与 digest(总结队列)分开存储,互不消费。
maxMessagesnumber20每个会话保留的历史消息条数上限(超出从最旧一端裁剪)。正整数。
maxCharsnumber4000每个会话保留、以及带入 Agent 正文时的历史文本字符上限。正整数。
maxAgeMsnumber0带入那一刻按消息的 QQ 时间戳丢弃早于该时长(毫秒)的历史消息;0 表示不按时间丢弃(只受条数/字符数约束)。非负整数。

历史如何累积、带入 body(而非 rawBody/commandBody)、并在带入后立即清空(drain-on-consume),详见接收模式 · history

quote —— 引用与合并转发解析

Key类型默认值说明
enabledbooleantrue是否主动通过 get_msg 拉取被引用(回复)的消息。
resolveForwardbooleantrue是否通过 get_forward_msg 展开合并转发的内容。
maxDepthnumber2合并转发可以嵌套展开多深(0 表示只展开顶层,不递归)。非负整数。
maxNodesnumber20单次展开渲染的转发节点数上限(跨越整棵嵌套转发树共享这个预算,不是每层各 20 个)。正整数。
maxCharsnumber4000注入到 Agent 可见正文中的引用/转发文本字符上限,超出会被截断并标记 truncated。正整数。
timeoutMsnumber10000每次 get_msg / get_forward_msg 调用的超时时间(毫秒)。

详细的解析算法、深度/节点/字符预算如何共同作用、环检测,见引用与合并转发

reconnect —— WebSocket 重连调优

Key类型默认值说明
enabledbooleantrue是否启用 @snowluma/sdk 内置的 WebSocket 自动重连。为 false 时,buildWebSocketOptions 会把 reconnect 整个设为 false 传给 SDK。
retriesnumber省略 ⇒ 无限重连(Number.POSITIVE_INFINITY最大重连次数。省略该字段(或显式 null)表示无限重连;任何显式数字都会被原样采纳,包括 0——0 意味着断线后不再重连。
minDelayMsnumber1000重连尝试之间的最小延迟(毫秒)。正整数。
maxDelayMsnumber30000重连尝试之间的最大延迟(毫秒)。正整数。

重连次数用尽后不会自恢复

retries 是有限值时,SDK 用尽重连次数后会停在断开状态,网关不会自行恢复,需要重启 OpenClaw。除非你确实想要"失败即停"的语义,否则建议省略该字段。

tools —— Agent 工具注册

Key类型默认值说明
enabledbooleantrue是否注册 snowluma_get_history / snowluma_get_group_members 两个 Agent 工具(见 Agent 工具)。为 falsesnowLumaPlugin.agentTools 返回空数组。

环境变量

只对 default 账号channels.snowluma 顶层字段,不含 accounts 下的命名账号)生效,实现在 src/env.ts

环境变量对应字段说明
SNOWLUMA_WS_URLwsUrl仅在 wsUrl 未显式配置时生效。
SNOWLUMA_HTTP_URLhttpUrl仅在 httpUrl 未显式配置时生效。
SNOWLUMA_ACCESS_TOKENaccessToken优先于 SNOWLUMA_TOKEN;仅在 accessToken 未显式配置时生效。
SNOWLUMA_TOKENaccessTokenSNOWLUMA_ACCESS_TOKEN 未设置时的次选。
SNOWLUMA_SELF_IDselfId会被 Number() 转换,转换失败视为未设置。仅在 selfId 未显式配置时生效。

空字符串或纯空白的环境变量值会被视为未设置(src/env.tsreadEnvtrim() 并在结果为空时返回 undefined)。

多账号

listSnowLumaAccountIdssrc/config.ts)的判定规则:

  • channels.snowluma 顶层(不含 accounts)只要设置了 wsUrlhttpUrl 中任意一个,就会被识别为 id 为 "default" 的账号。
  • channels.snowluma.accounts 下每个 key,只要对应的值设置了 wsUrlhttpUrl,就会被识别为一个独立账号,账号 id 就是那个 key。

命名账号的字段结构和顶层完全一致(reconnect/receive/quote/tools 都可以独立配置),但不会读取环境变量回退——环境变量回退只在 resolveSnowLumaAccount 判定"这是 default 账号"时才生效。

json
{
  "channels": {
    "snowluma": {
      "enabled": true,
      "wsUrl": "ws://127.0.0.1:3001/",
      "accessToken": "token-for-default-account",

      "accounts": {
        "secondary": {
          "enabled": true,
          "wsUrl": "ws://127.0.0.1:3002/",
          "accessToken": "token-for-secondary-account",
          "allowFrom": ["*"],
          "receive": {
            "digest": { "enabled": true, "scope": "all" }
          }
        }
      }
    }
  }
}

Agent 工具(snowluma_get_history / snowluma_get_group_members)和 react 动作都接受可选的 accountId 参数来指定操作哪个账号,省略时使用 "default"

完整示例

以下示例覆盖 SnowLumaAccountConfig 的每一个选项(jsonc 仅用于加注释,实际 openclaw.json 不支持注释,写入前请去掉 // 行):

jsonc
{
  "channels": {
    "snowluma": {
      "enabled": true,
      "name": "主账号",
      "wsUrl": "ws://127.0.0.1:3001/",
      "httpUrl": "http://127.0.0.1:3001",
      "accessToken": "your-snowluma-token",
      "selfId": 123456789,
      "allowFrom": ["private:10000001", "group:20000002"],
      "denyFrom": ["private:99999999"],
      "groupAutoReact": true,
      "groupAutoReactEmojiId": 76,
      "replyToTrigger": true,
      "textChunkLimit": 4500,
      "requestTimeoutMs": 30000,
      "debug": false,
      "reconnect": {
        "enabled": true,
        "retries": 20,
        "minDelayMs": 1000,
        "maxDelayMs": 30000
      },
      "receive": {
        "mention": {
          "enabled": true,
          "requireMentionInGroup": true,
          "keywords": ["机器人", "小助手"],
          "keywordMatch": "contains",
          "caseSensitive": false,
          "triggerOnReplyToSelf": true,
          "alwaysReplyInDirect": true
        },
        "digest": {
          "enabled": true,
          "intervalMs": 300000,
          "maxMessages": 50,
          "minMessages": 3,
          "prompt": "请用简洁的中文归纳这段时间的讨论主题、结论和待办事项,没有值得汇报的内容请只回复 SKIP。",
          "scope": "group",
          "peers": ["group:20000002"],
          "maxTranscriptChars": 20000
        },
        "realtime": {
          "enabled": true,
          "windowMs": 800,
          "maxWindowMs": 3000,
          "maxMessages": 10,
          "maxChars": 8000
        },
        "history": {
          "enabled": true,
          "maxMessages": 20,
          "maxChars": 4000,
          "maxAgeMs": 0
        }
      },
      "quote": {
        "enabled": true,
        "resolveForward": true,
        "maxDepth": 2,
        "maxNodes": 20,
        "maxChars": 4000,
        "timeoutMs": 10000
      },
      "tools": { "enabled": true },
      "accounts": {
        "secondary": {
          "enabled": true,
          "wsUrl": "ws://127.0.0.1:3002/",
          "accessToken": "another-token",
          "allowFrom": ["*"]
        }
      }
    }
  }
}