# 配置项速查

全部配置都写在 profile 的 `cordis.patch.yml` 里 `dsh-qq` 那一行的 `config:` 下面；也可以用 DSH 设置页里「QQ」那节点着改（详见 [README](../README.md#在-dsh-设置里调它)）。

> 一行 patch 是**整行替换、不是深合并**：后写的 `config` 会把前面的整块盖掉，所以改的时候把要保留的键一起写全。

## 连接

| 配置 | 默认 | 说明 |
| --- | --- | --- |
| `wsUrl` | 空 | NapCat 的 WebSocket 服务端地址，如 `ws://127.0.0.1:3001`。**留空就不连接**（不会偷偷去连 3001）；填好保存约 0.8 秒自动接上，不用重启 |
| `accessToken` | 空 | NapCat 那边设的 Token，留空就不带鉴权头 |
| `selfId` | 空 | 机器人 QQ 号。留空会自动 `get_login_info` 查 |
| `reconnectMs` | `5000` | 断线重连间隔（毫秒） |

## 会话与工作区

| 配置 | 默认 | 说明 |
| --- | --- | --- |
| `provider` / `model` / `preset` | 空 | 指定模型与预设；留空跟随宿主当前设置 |
| `cwd` | 空 | Agent 工作目录 |
| `workspaceRoot` | 空 | 每个 QQ 会话的独立工作目录放在哪；留空用 `<cwd>/会话` |
| `isolateByPeer` | `true` | 每个私聊 / 每个群各自一个工作目录 + 记忆区，互不可见 |
| `sessionIdleTimeout` | `1800000` | 会话闲置多久回收（毫秒） |

## 群里怎么说话

| 配置 | 默认 | 说明 |
| --- | --- | --- |
| `requireMention` | `true` | 群聊必须 @ 才理 |
| `groupWakeMs` | `60000` | 被某人 @ 过一次后，多久内不用再 @ 也能接话（毫秒，0 = 关闭）。窗口按「群 + 人」算，别人蹭不到 |
| `groupReplyGapMs` | `20000` | 同一个「群 + 人」两次回复之间至少隔多久（毫秒）：均匀地回，间隔里的消息攒着下次一起看 |
| `groupRepliesPerWindow` | `0` | 一个保活窗口内最多回几次（0 = 不限）。它和「窗口内说话续期」会打架，一般别开 |
| `groupMinGapMs` | `4000` | 群聊任意两条回复之间的最小间隔（毫秒），防风控 |
| `historyLimit` | `20` | 群聊带多少条最近消息当上下文 |
| `quoteReply` | `true` | 消息带引用时，把「被引用的人 + 原文」一起交给 Agent |

## 谁能跟它说话

| 配置 | 默认 | 说明 |
| --- | --- | --- |
| `groupMode` | `open` | `open` / `allowlist` / `disabled` |
| `groupAllow` | `[]` | `allowlist` 模式下允许的群号 |
| `privateMode` | `allowlist` | `open` / `allowlist` / `disabled`；默认只认白名单，白名单空着 = 谁都不理 |
| `privateAllow` | `[]` | `allowlist` 模式下允许的 QQ 号；**空着就没人能私聊进来** |
| `replyToPrivate` | `true` | 私聊总开关 |
| `ownerIds` | `[]` | 主人的 QQ 号（可多个）；群聊里这些人会被标出来，其他群友当陌生人 |
| `ownerLabel` | `主人` | 群聊里怎么称呼这位主人，例如「老板」 |

## 同类机器人

| 配置 | 默认 | 说明 |
| --- | --- | --- |
| `botChatEnabled` | `false` | 群里另一个机器人发言时要不要接；关着 = 同类说啥都不理，连 @ 也不接 |
| `peerBots` | `[]` | 已知同类机器人的 QQ 号；群历史里会给它们标「· 同类机器人」 |

## 图片 · 语音 · 表情包

| 配置 | 默认 | 说明 |
| --- | --- | --- |
| `imageCacheDir` | 空 | 收到的图片下载到哪；留空用 `<cwd>/图片缓存` |
| `imageMaxMB` | `8` | 单张图片体积上限 |
| `memeDb` | 空 | 查梗词库（`memes.db`）的路径；留空读环境变量 `DSH_QQ_MEME_DB`，两个都空则 `qq_meme` 回「没配词库」 |
| `faceCollectHint` | 空 | 什么样的图值得收进表情包收藏；留空 = 一张都不收 |
| `faceSendGapMs` | `90000` | 两次发图/表情之间至少隔多少毫秒；`0` = 不限 |
| `faceSendMaxPerHour` | `6` | 一小时最多发几张图/表情（`qq_mface` + `qq_send_image` 合计）；`0` = 不限 |
| `asrEnabled` | `true` | 语音自动转文字（本地 vosk，离线） |
| `asrPython` | `python` | 跑 ASR 脚本的 python（默认要求它在 PATH 里） |
| `asrScript` | 空 | ASR 脚本路径；留空用插件自带的 `tools/asr.py` |
| `asrModel` | 空 | vosk 模型目录（纯 ASCII 路径）；留空按 `VOSK_MODEL` → 插件旁 `models/` 找 |
| `asrTimeoutMs` | `60000` | 单条语音转写超时 |

## 发消息的节奏

| 配置 | 默认 | 说明 |
| --- | --- | --- |
| `textChunkLimit` | `1500` | 单条消息最长多少字，超了自动切 |
| `splitDelayMs` | `400` | 分段发送之间的间隔，别设 0 太急 |
| `rateLimitPerMin` | `12` | 每个会话每分钟最多发几条（0 = 关闭） |
| `streamReply` | `true` | 私聊是否边生成边发（群聊始终整段发） |
| `streamMaxChunks` | `3` | 一轮里最多流式发几条 |
| `maxTurnMs` | `600000` | 单轮最长等待时间，超时只提示不打断 |
| `showToolResults` | `false` | 要不要把工具调用过程也发到 QQ 里 |

## 没人说话的时候（主动开口）

它是「来消息才动」的：没人说话，它就永远不出声。这一组配置给会话加一个冷场闹钟 —— 安静够久了，就往会话里投一句「可以主动开口了」。**说不说仍然由它自己决定**（不想说就 `[SILENT]`），这一层不替它说话。

| 配置 | 默认 | 说明 |
| --- | --- | --- |
| `proactiveEnabled` | `true` | 冷场主动开口的总开关 |
| `proactiveSilentMs` | `1200000` | 安静多久算冷场（毫秒，默认 20 分钟）。**必须小于 `sessionIdleTimeout`**（默认 30 分钟），否则会话先被回收，就轮不到它开口了 |
| `proactiveMaxPerDay` | `6` | 每个会话每天最多主动说几次；`0` = 不主动 |
| `proactiveQuietHours` | `23:00-08:00` | 这个时段不主动开口；格式 `HH:MM-HH:MM`，留空 = 不限 |

除了这四项，还有两道内置的闸：两次主动之间至少隔 1.5 倍冷场时长；连着 3 次主动开口都没人接，这个会话今天就先闭嘴。

## 作息（睡前提醒 · 晚安静默 · 夜里的消息留到早上）

私人用的一套作息：到点提醒她去睡，她说「晚安」之后就不再回消息，休息时段收到的消息不立刻回、攒到早上一次交出去。

| 配置 | 默认 | 说明 |
| --- | --- | --- |
| `sleepMode` | `true` | 作息总开关 |
| `bedtimeAt` | `23:30` | 到点提醒一句「该睡了」，每个会话每天一次；留空 = 不提醒 |
| `sleepFrom` | `00:00` | 休息时段从几点开始。这时候起收到的私聊**不立刻回**，只记下来 |
| `sleepTo` | `08:00` | 休息时段到几点结束。出窗口时把夜里攒的消息一次性交给它，附上「昨晚你发来这几条」 |
| `goodnightWords` | `晚安,睡了,睡觉,去睡,我先睡,困了,睡啦` | 说到这些词就当她说晚安了；之后不再回消息，直到第二天 `sleepTo`。逗号分隔 |

两个细节：**说晚安那条本身照常回**（得有人应一句），静默从下一条开始；**群聊不参与作息**（群里的消息照常处理，只是群聊默认是关的）。

## 夜里的读书问询（默认关，只有本地开）

半夜没人说话，但也不是完全没事可做。这一层会在夜里挑一两次，往会话里投一轮**问询**——问它要不要看会儿书。**它可以拒绝**，拒绝了就什么都不做，不用回话、不用解释。

这一轮里它说的话、调的发送工具**一律不会发出去**：`send()` 里和几个发送工具里都硬拦了一道（`record.quietRound`）。所以就算它半夜突然想说点什么，对方手机也不会响。

| 配置 | 默认 | 说明 |
| --- | --- | --- |
| `readingEnabled` | `false` | 总开关。默认关——发布版的人看不见这一层 |
| `readingHours` | `01:00-05:00` | 只在这段时间里问，格式 `HH:MM-HH:MM` |
| `readingAskCount` | `2` | 一夜最多问几次（拒绝也算问过）；`0` = 不问 |
| `readingAskGapMs` | `10800000` | 两次问询至少隔多久（默认 3 小时） |
| `readingBook` | （空） | 书名；留空 = 这层不启用 |
| `readingChapterDir` | （空） | 章节目录，用来判断**读完没有** |
| `readingBookmark` | （空） | 书签文件，从「下次从哪接」那列接着读 |
| `readingPeer` | （空） | 问询投给哪个 QQ 号；留空 = 私聊白名单第一个 |
| `readingTimeoutMs` | `1200000` | 一轮最多等多久（默认 20 分钟——真读两章再写书签，比闲聊慢得多） |

**读完了就不再问**：书签上「下次从哪接」的章号超过 `readingChapterDir` 里最大的章号，这一夜就不再问——问了也只能得到一句「没得看了」。

书签是五列的 markdown 表（四列的旧书签认不出来，会当「还没开始读」处理）：

    | 书 | 读到哪 | 哪天 | 下次从哪接 | 一句话心得 |
    | --- | --- | --- | --- | --- |
    | 网游之天下第一 | 第2章 | 2026-10-27 | 第0003章 | 柳云死得干脆 |

本地怎么开：在 `~/.dsh/profiles/<profile>/cordis.patch.yml` 里给 `dsh-qq-onebot` 加这组键（`readingBook`、`readingChapterDir`、`readingBookmark` 三个都要填，缺一个就当没开）：

    readingEnabled: true
    readingBook: 网游之天下第一
    readingChapterDir: 'D:\鲸鲸的专用文件\图书馆\小说\网游之天下第一\chapters'
    readingBookmark: 'D:\鲸鲸的专用文件\图书馆\书签\老大.md'
    readingAskCount: 2

## 连发合并

她一口气发三条，别回三遍——那样像接线员。

| 配置 | 默认 | 说明 |
| --- | --- | --- |
| `mergeWindowMs` | `5000` | 收到一条私聊后先等这么久（毫秒），期间再来就并成同一轮；`0` = 关掉，来一条回一条 |

只在私聊里生效（群聊本来就该一条条接）。

## 上下文 · 人设 · 记忆

| 配置 | 默认 | 说明 |
| --- | --- | --- |
| `contextGuard` | `true` | 上下文守卫：压缩后补回聊天规矩；接近窗口上限时换新会话 |
| `contextSwapRatio` | `0.7` | 最近一次请求的 token 超过窗口这个比例就换会话（0 = 不换，交给 DSH 自动压缩） |
| `contextSwapMinTokens` | `200000` | 换会话的绝对下限：请求 token 不到这个数就不换 |
| `contextMemoryKeep` | `30` | 换会话前把最近多少条聊天记进 `记忆区/压缩记忆/` |
| `chatLog` | `true` | 把每天的收发追加进 `<会话目录>/聊天记录/<日期>.md`（原始流水，不是日记） |
| `persona` | 空 | 它是谁：名字、性格、怎么称呼人（见 [让它有性格](persona.md)） |
| `directPrompt` | 空 | 私聊时加在聊天规矩后面的补充指令 |
| `groupPrompt` | 空 | 群聊时的补充指令（比如「在群里说话，一两句就好」） |
| `debug` | `false` | 打开细节日志 |

## `chatLog` 为什么值得开着

QQ 会话是「一问一答」的，模型很少会主动写日记，聊完就忘——第二天问它「昨天说的那个」，它根本不知道。流水由插件来记，不依赖模型自觉，也不多花一轮 token；被跳过的群消息也记，方便回查「它当时到底看到了什么、为什么接不上话」。

要注意这是**原始流水，不是日记**。要日记还得让 Agent 自己（或者定时任务）从流水里提炼，见 [让它自己长期跑](deployment.md)。写失败只记调试日志，绝不影响聊天。
