<?xml version="1.0" encoding="UTF-8"?>
<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://purl.org/rss/1.0/"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel rdf:about="https://hxling.top/index.php/feed/rss/">
<title>Hello World</title>
<link>https://hxling.top/</link>
<description>Your description here.</description>
<items>
<rdf:Seq>
<rdf:li resource="https://hxling.top/index.php/archives/12/"/>
<rdf:li resource="https://hxling.top/index.php/archives/7/"/>
<rdf:li resource="https://hxling.top/index.php/archives/3/"/>
</rdf:Seq>
</items>
</channel>
<item rdf:about="https://hxling.top/index.php/archives/12/">
<title>把大肥鱼接入QQ</title>
<link>https://hxling.top/index.php/archives/12/</link>
<dc:date>2026-11-02T14:36:33+08:00</dc:date>
<description>把「大肥鱼」接进 QQ：一次从零到稳定的部署复盘环境：阿里云 ECS · Ubuntu 26.04 LTS · 2 vCPU / 1.7 GiB / 40 GB时间：2026-11-01 ~ 11-02说明：文中所有 IP、QQ 号、AppID、API Key、密码均已替换为占位符，请按自己的实际情况填写。0. TL;DR一句话：「让一个模型在 QQ 群里像人一样说话」，拆开是三件事——接一张嘴（消息通道）、写一份人设（提示词）、让它有记忆和情绪（插件）。两条路线都跑通了：路线组成特点我的选择路线一官方 QQ Bot + DeepSeek Harness（dsh）官方通道、扫码即用、零封号风险、主动发言受限先跑通，后停用路线三NapCat + AstrBot + DeepSeek个人号协议、人格化程度高、插件生态丰富、有封号风险最终在用如果只想「让鱼在群里正常说话」，路线一省心到离谱，二十分钟能搞定。如果想要「会发表情包、会改签名、会记仇、会自己插话」，只有路线三。本文涉及的全部开源仓库（人设包、表情包素材、协议端、插件）整理在第 15 节，其中包含两个「已失效仓库」的记录，省得你顺着旧链接白找。1. 开始之前：先想清楚要什么动手前先把需求拆成四层，否则很容易在「装插件」里迷路：通道：消息从哪来、回到哪去人格：它说话的味道能力：记忆、表情包、改资料、主动发言稳定性：掉线怎么办、封号怎么办、内存够不够这四层的优先级是倒过来的：稳定性 &gt; 人格 &gt; 能力 &gt; 通道。因为通道挂了你会立刻知道，而「人格不像」「记忆错乱」往往要跑一天才发现，返工成本高得多。2. 环境盘点与第一道坎2.1 机器Ubuntu 26.04.1 LTS
2 vCPU / 1735 MiB / 40 GB
Node.js v22.23.4（apt 自带版本太老，必须另行安装）2.2 第一道坎：网络这台机器在国内，遇到的问题按顺序是：目标状态解法deb.nodesource.com✅ 通直接装registry.npmjs.org / github.com✅ 通直接 cloneregistry-1.docker.io❌ 超时配置镜像加速我这台本机的 web_fetch❌ 全部“解析到非公网 IP”改用 Node 的 https 模块直连顺手记一个排查技巧：curl: (35) schannel: AcquireCredentialsHandle failed 不是网络问题，是 TLS 凭据拿不到；而 node -e &#039;require(&quot;https&quot;).get(...)&#039; 往往能通——因为 Node 走自己的 OpenSSL，不依赖系统证书存储。2.3 Docker 镜像加速// /etc/docker/daemon.json
{
  &quot;registry-mirrors&quot;: [
    &quot;https://docker.m.daocloud.io&quot;,
    &quot;https://docker.1ms.run&quot;,
    &quot;https://dockerproxy.net&quot;
  ],
  &quot;log-driver&quot;: &quot;json-file&quot;,
  &quot;log-opts&quot;: { &quot;max-size&quot;: &quot;10m&quot;, &quot;max-file&quot;: &quot;3&quot; }
}实测 docker.m.daocloud.io 返回 401 是正常的（说明 Registry API 在响应），不代表不可用。3. 路线一：官方 QQ Bot + dsh（20 分钟）3.1 装curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
apt install -y nodejs
npm i -g pnpm @deepseek-ai/dsh      # pnpm 必须单独装，dsh 的插件管理依赖它

dsh --version                        # 0.2.1-rc.53.2 三步接入dsh plugin --profile qqbot add @tencent-connect/dsh-qqbot
dsh --profile qqbot首次启动会打印二维码，同时打印一条可点击的绑定链接：https://q.qq.com/qqbot/openclaw/connect.html?task_id=&lt;uuid&gt;&amp;source=dsh-qqbot&amp;_wv=2服务器上没有图形界面也没关系——把这条链接发到手机浏览器打开即可扫码。绑定成功后凭据自动写入：~/.dsh/profiles/qqbot/cordis.patch.yml之后重启不再需要扫码（这一点我实测过三次）。3.3 一个关键认知dsh 在 systemd 下不能扫码（无 TTY）。正确的顺序永远是：前台手动跑一次 → 扫码绑定 → 凭据落盘 → 再交给 systemd 托管反过来做（先写 unit 再启动）会一直卡在二维码界面。3.4 为什么最后停用了它官方通道的限制很硬：被动回复。用户不说话，它不能说话。对「群里的活人感」这个目标来说，这条限制是致命的——它永远只能当客服，不能当群友。systemctl disable --now dsh-qqbot 之后配置全部保留，随时可以切回来。4. 路线三：NapCat + AstrBot4.1 架构QQ 用户 ──▶ QQ 服务端 ──▶ NapCat（协议端）
                              │  反向 WebSocket
                              ▼
                          AstrBot（大脑）
                              │
                    ┌─────────┴─────────┐
                 DeepSeek          硅基流动
                (聊天/总结)        (记忆向量)方向别搞反：AstrBot 的 aiocqhttp 适配器是反向 WS 服务端（监听 6199），NapCat 作为客户端连上去。所以配置写在 NapCat 侧：&quot;websocketClients&quot;: [{
  &quot;name&quot;: &quot;astrbot&quot;,
  &quot;url&quot;: &quot;ws://astrbot:6199/ws&quot;,
  &quot;token&quot;: &quot;&lt;你的token&gt;&quot;,
  &quot;reconnectInterval&quot;: 3000
}]4.2 docker-composeservices:
  napcat:
    image: mlikiowa/napcat-docker:latest
    container_name: napcat
    restart: unless-stopped
    environment:
      - NAPCAT_UID=1000
      - NAPCAT_GID=1000
      - ACCOUNT=&lt;小号QQ&gt;
      - NAPCAT_QUICK_PASSWORD=&lt;小号密码&gt;
      - TZ=Asia/Shanghai
    mem_limit: 800m
    volumes:
      - ./napcat/config:/app/napcat/config
      - ./napcat/qq:/app/.config/QQ
      - ./napcat/cache:/app/napcat/cache
    networks: [botnet]

  astrbot:
    image: soulter/astrbot:latest
    container_name: astrbot
    restart: unless-stopped
    depends_on: [napcat]
    environment:
      - TZ=Asia/Shanghai
    mem_limit: 700m
    ports:
      - &quot;127.0.0.1:6185:6185&quot;        # 面板只绑本机，走 SSH 隧道
    volumes:
      - ./astrbot/data:/AstrBot/data
    networks: [botnet]

networks:
  botnet:
    driver: bridge三个细节值得单独说：ACCOUNT：不设它，NapCat 只会走扫码；设了才会尝试快速登录NAPCAT_QUICK_PASSWORD：token 失效时的兜底，能省掉无数次半夜扫码mem_limit：1.7 GiB 的机器不给容器设上限，一个内存泄漏就能把整机拖死4.3 AstrBot 后台密码首次启动会生成随机强密码，默认的 astrbot 已经失效。密码只在启动日志里打印一次：docker logs astrbot 2&gt;&amp;1 | grep &quot;Initial password&quot;忘了也没关系，重启一次就会重新打印。4.4 模型配置AstrBot v4 的 provider 结构是「连接」与「模型」分离的：{
  &quot;provider_sources&quot;: [{
    &quot;id&quot;: &quot;deepseek-flash_source&quot;,
    &quot;type&quot;: &quot;openai_chat_completion&quot;,
    &quot;key&quot;: [&quot;sk-****&quot;],
    &quot;api_base&quot;: &quot;https://api.deepseek.com/v1&quot;
  }],
  &quot;provider&quot;: [{
    &quot;id&quot;: &quot;deepseek-flash&quot;,
    &quot;model&quot;: &quot;deepseek-flash&quot;,
    &quot;provider_source_id&quot;: &quot;deepseek-flash_source&quot;,
    &quot;custom_extra_body&quot;: { &quot;thinking&quot;: { &quot;type&quot;: &quot;disabled&quot; } }
  }]
}thinking: disabled 很重要——DeepSeek 的思考过程会拖慢首字延迟，而且对「短句聊天」毫无意义。5. 人格：从 Markdown 到数据库5.1 人设来源用了社区配置包 zexuanw958-svg/dafeiyu-config-pack 的 persona/大肥鱼.md，里面有一个占位符 {OWNER_QQ}，必须替换成真实 QQ 号——它不只是装饰，人设里靠它识别「谁才是主人」，防止有人在群里冒充。顺便说一句这个包的设计：它的 settings/pack.json 是一份「清单式」配置，把 6 个配套插件、两套表情包、语音/记忆/主动聊天的推荐参数都写在一处，再用 tools/import_dafeiyu.py 读清单改 AstrBot。这个思路值得借鉴——配置即文档，改哪项就改哪行，然后重跑一次导入。我没有整包导入，而是按需取用（详见第 11 节）：语音链路需要额外的豆包/Breeze key，对 1.7 G 的机器也不友好；插件则挑了对「像人」帮助最大的几个。5.2 写进数据库AstrBot 的人格存在 SQLite 里，personas 表。第一次 INSERT 会连撞三个坑：sqlite3.IntegrityError: NOT NULL constraint failed: personas.created_at
sqlite3.IntegrityError: NOT NULL constraint failed: personas.sort_order正确写法：db.execute(&quot;&quot;&quot;INSERT INTO personas
  (created_at, updated_at, persona_id, system_prompt,
   begin_dialogs, tools, skills, custom_error_message, folder_id, sort_order)
  VALUES (CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, &#039;大肥鱼&#039;, ?, &#039;[]&#039;, &#039;[]&#039;, &#039;[]&#039;, NULL, NULL, 0)&quot;&quot;&quot;, (prompt,))然后把 agent_runner.config.persona.persona_id 指向它。5.3 配套的基础设置{
  &quot;platform_settings.segmented_reply.enable&quot;: true,
  &quot;platform_settings.segmented_reply.regex&quot;: &quot;.*?(?:\\r?\\n+|[。？！~…]+)|.+$&quot;,
  &quot;platform_settings.empty_mention_waiting&quot;: false,
  &quot;agent_runner.config.compression.max_turns&quot;: 30,
  &quot;provider_settings.datetime_system_prompt&quot;: true
}分段回复是「一条条发」的关键——不切分的话，模型会一次吐出 200 字，立刻暴露是机器人。6. 表情包：把 100 张 GIF 变成插件资源包6.1 素材diwer/ai-biaoqingbao 提供 100 张 512×512 的中文 GIF，带 catalog.json（编号、含义、分类）。分类正好是 7 组：首批常用、日常回应、抽象反应、打工摸鱼、情绪状态、亲密陪伴、祝福庆祝。6.2 插件资源包的目录协议meme_manager 认这个结构：packs/&lt;pack_id&gt;/
├── manifest.json
├── memes_data.json
└── memes/&lt;分类名&gt;/*.gifmanifest.json：{
  &quot;schema_version&quot;: 1,
  &quot;id&quot;: &quot;dafeiyu-biaoqingbao&quot;,
  &quot;name&quot;: &quot;大肥鱼表情包·100个中文GIF&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;categories&quot;: { &quot;抽象反应&quot;: { &quot;description&quot;: &quot;大肥鱼表情：抽象反应&quot; } }
}再把它登记进 registry.json 和 selection_rules.json（设成默认包）。6.3 打包时踩的坑用 PowerShell 的 Compress-Archive 打包，到 Linux 解压后所有目录变成了文件名的一部分：memes\抽象反应\001-不服气.gif     ← 一个带反斜杠的完整文件名因为 Windows 用 \ 作分隔符，而 zip 规范要求 /。解决方法是用 Python 的 zipfile 显式写 POSIX 路径：rel = os.path.relpath(full, root).replace(&quot;\\&quot;, &quot;/&quot;)
z.write(full, rel)6.4 为什么值得用插件而不是自己写我一开始自己写了个 80 行的插件，直接往回复链追加图片，能用。但换成 meme_manager 之后才明白差别在哪：它把分类目录注入到模型提示词里，模型才知道「我有哪些图可以挑」，然后用 &amp;&amp;抽象反应&amp;&amp; 这种标记下单。自己写的插件只是「随机附一张图」，跟情绪无关；插件版是「有合适的才发」。这是「像人」和「像随机图片机器人」的区别。7. 记忆：embedding 选型的成本账7.1 三种方案方案成本可行性硅基流动 BAAI/bge-m3免费✅ 选它百炼 text-embedding-v3约 0.0005 元/千 token ≈ 1–2 元/月✅ 也可本机 ollama免费❌ 模型常驻 1.2 GB，1.7 G 机器跑不动结论：embedding 从来不是钱的问题，是内存问题。7.2 必踩的坑：维度装完 livingmemory 启动直接失败：ValueError: 无效的嵌入向量维度: 0。
请检查该知识库使用的 Embedding Provider 是否正确配置了 embedding_dimensions。openai_embedding 适配器不会自动探测维度，必须在 provider 配置里显式写：{ &quot;embedding_model&quot;: &quot;BAAI/bge-m3&quot;, &quot;embedding_dimensions&quot;: 1024 }7.3 记忆的机制&quot;reflection_engine&quot;: { &quot;summary_trigger_rounds&quot;: 10 },
&quot;recall_engine&quot;: { &quot;top_k&quot;: 5, &quot;recent_memory_count&quot;: 2 }每 10 轮总结一次 → 存成带权重的记忆 → 回复时按语义召回 5 条 + 最近 2 条。后台还有每日 00:05 的衰减/清理/备份调度。8. 成本控制：两层概率8.1 第一层：限流策略AstrBot 默认 rate_limit.strategy = &quot;stall&quot;，实现是：await asyncio.sleep(stall_duration)   # 睡到下一个窗口密集群聊时，超限的消息不是被丢弃，而是排队休眠——这是「延迟越堆越长」的元凶。改成 &quot;discard&quot;：超限直接丢，绝不排队。8.2 第二层：回复概率最初我自己写了个 reply_gate 插件（event.stop_event() 可真正阻止模型调用，实测有效），按 DeepSeek 计费时段分档：工作日 09:00–12:00、14:00–18:00 → 40%其余（含周末、节假日）→ 60%@ / 引用 / 命令 → 100%后来装了 group_chat_plus，把概率交给它，reply_gate 停用。8.3 一个语义陷阱group_chat_plus 的 initial_probability 不是「回复概率」，而是「进入 AI 读空气判断的概率」。所以配 0.6 ≠ 回 60%，实际回复率明显更低——这是它比自建方案更「像人」的代价。8.4 动态时段与平滑过渡{
  &quot;enable_dynamic_reply_probability&quot;: true,
  &quot;reply_time_periods&quot;: &quot;[{\&quot;name\&quot;:\&quot;高峰(上午)\&quot;,\&quot;start\&quot;:\&quot;09:00\&quot;,\&quot;end\&quot;:\&quot;12:00\&quot;,\&quot;factor\&quot;:0.67}]&quot;,
  &quot;reply_time_transition_minutes&quot;: 30,
  &quot;reply_time_use_smooth_curve&quot;: true
}实测日志（14:00 峰值边界）：13:53:27  原始=0.60  系数=0.69  结果=0.4121
13:54:37  原始=0.80  系数=0.68  结果=0.5444
13:58:35  原始=0.60  系数=0.67  结果=0.4022
14:00:00  原始=0.60  系数=0.67  结果=0.4020系数从 0.69 平滑滑到 0.67——说明 30 分钟过渡是在峰值前的 30 分钟就开始爬坡的，不是整点突变。好处是观感自然，代价是「中午的尾巴」被提前压掉了。想要硬边界就把过渡改成 10 分钟。9. 稳定性：掉线、假在线与看门狗这是整个项目最花时间的部分，也是最值得写的部分。9.1 三次掉线，三种原因时间现象原因19:54KickedOffLine 登录已失效 + 弹二维码token 失效22:38同上token 再次失效11:09日志一片安静，什么错都没有会话静默失效（假在线）第三种最坑：容器 Up、WS 连接 ESTABLISHED、AstrBot 显示「适配器已连接」，但 QQ 侧早就不推消息了。从外部完全看不出来，只有「群里人不理它」这个现象。9.2 看门狗# /usr/local/bin/napcat-watchdog.sh（核心逻辑）
RECENT=$(docker logs napcat --since 5m 2&gt;&amp;1)

if ! echo &quot;$RECENT&quot; | grep -qE &quot;KickedOffLine|账号状态变更为离线|二维码已保存&quot;; then
  exit 0
fi

# 10 分钟冷却，避免循环重启
[ $((now - last)) -lt 600 ] &amp;&amp; exit 0

docker restart napcat配 systemd timer 每 2 分钟跑一次。重启后入口脚本会带着 ACCOUNT + NAPCAT_QUICK_PASSWORD 自动登录，35 秒恢复。9.3 看门狗的盲区它只能检测有日志的掉线。第三种「静默失效」一个关键字都不触发，所以看门狗全程没动作。要覆盖它，需要主动健康检查：给 NapCat 开一个本地 HTTP API，让看门狗主动问 get_status。我试过从容器外连它的 WS（3001），三次都是 1006 异常断开——这条路没走通，留作后续。9.4 一个非技术的根因前两次掉线间隔 2 小时 44 分，规律得太可疑。最后发现是手机端同时登录着这个小号——个人号协议下，手机端上线/刷新会把协议端踢掉。让用户在手机上退出这个小号之后：12 小时内 KickedOffLine 次数 0。教训：掉线先问「这个号还在别处登陆吗」，比看一百行日志都快。9.5 内存1.7 GiB 总内存 / 加 2 GiB swap
napcat    ~240–290 MiB
astrbot   ~260–340 MiB（随插件增加）swap 是必须的：不加的话，一次长上下文 + 多发消息就可能 OOM。10. 踩坑总表按「症状 → 原因 → 解法」整理，方便按图索骥。#症状原因解法1所有 URL 抓取失败，报“非公网 IP”本地网络策略把域名解析到内网用 Node https 直连绕过2curl: (35) schannel: SEC_E_NO_CREDENTIALS取不到系统 TLS 凭据换 Node/OpenSSL 栈3docker pull 一直超时Docker Hub 不可达配 registry-mirrors4重启 NapCat 后要重新扫码只有扫码 token，没有密码兜底ACCOUNT + NAPCAT_QUICK_PASSWORD5被踢后容器一直卡在二维码worker 内部重启不走入口脚本，-q 丢失看门狗 docker restart 容器6日志无异常但群里不回会话静默失效（假在线）需要主动健康检查（未彻底解决）7每 2 小时掉一次线手机端同时登录互踢手机端退出该账号8zip 解压后目录变成文件名Windows \ vs zip 规范 /Python zipfile 写 POSIX 路径9Unexpected UTF-8 BOMJSON 带 BOM（AstrBot 写的配置就是）读写都用 utf-8-sig10NOT NULL constraint failed: personas.created_at表有非空列没给值INSERT 补齐 created_at/updated_at/sort_order11无效的嵌入向量维度: 0适配器不自动探测维度显式配 embedding_dimensions: 102412启动时刷 faiss.swigfaiss_avx512 报错faiss 探测可选加速模块无害，忽略13登录后台一直失败默认密码被替换成随机强密码`docker logsgrep "Initial password"`14市场接口解析出 0 个插件返回的是 dict 不是 list按 dict 遍历15git clone 报 could not read Username其实是 404（仓库没了）换源或用 codeload 直下 zip16插件仓库 404市场条目过期（作者删库/改名）用替代品（如下）17裸 @ 不回，且无任何日志empty_mention_waiting: false置 true，或在插件里自己处理18偶发 IndexError: list index out of range空 wake_prefix 让 messages[0] 越界已知副作用，事件本身无内容，可忽略19两套概率插件互相打架概率相乘（0.4 × 0.6 = 0.24）只保留一个概率决策方201.7 G 内存跑浏览器自动化Chromium 常驻 300–600 MB换 4 G 以上机器再考虑关于 #16 的替代品（2026-11 实测）：原仓库状态替代Zhalslar/astrbot_plugin_qqprofile404Akusative/astrbot_plugin_qqprofile_exZhalslar/astrbot_plugin_qzone404sujingyi1/astrbot_plugin_qzone11. 最终形态11.1 插件清单插件作用自主行为meme_manager表情包（100 张 / 7 分类）回复 35% 概率配图group_chat_plus群聊读空气、打字模拟、错字非 @ 消息按概率接话livingmemory长期记忆（bge-m3 向量）每 10 轮总结dafeiyu_clean过滤模型漏出的内部标签无dafeiyu_signature情绪触发改签名2 次/天qqprofile_ex改签名/状态/昵称签名 240min、状态 60min 冷却；昵称已关qzone发/看说说全手动（cron 全空）reply_gate自建概率门（已停用）—11.2 行为分层纯定时（没人说话也会跑）：记忆衰减/备份、索引重建 —— 都在后台，用户无感。对话中自主（有人说话时自己加戏）：接话、配图、改签名、改状态、总结记忆。完全被动（必须指令）：私聊/群聊回复、发说说、看说说、改昵称。12. 运维手册# 服务
cd /opt/napcat-bot &amp;&amp; docker compose ps
docker compose restart astrbot      # 改配置后
docker compose restart napcat       # 会走密码自动登录

# 日志
docker logs astrbot --since 10m 2&gt;&amp;1 | grep -Ei &quot;error|ERRO&quot;
docker logs napcat --since 10m 2&gt;&amp;1 | grep -E &quot;接收|发送&quot;

# 概率判定
docker logs astrbot --since 1h 2&gt;&amp;1 | grep &quot;动态时间调整&quot;

# 掉线排查
docker logs napcat --since 6h 2&gt;&amp;1 | grep -c KickedOffLine
journalctl -u napcat-watchdog --since -6h | tail

# 面板（都只绑本机）
ssh -L 6185:127.0.0.1:6185 root@&lt;服务器IP&gt;   # AstrBot WebUI13. 没做的事，以及原因项目状态原因语音输出（TTS）不做明确不需要；四条语音链路都要额外 key 或显卡唱歌插件不做语音衍生功能定时发说说不做主动行为最容易被风控，手动触发足够主动找人聊天不做同上；且会打扰人抖音群聊不做无官方群机器人 API，只能浏览器自动化；1.7 G 跑 Chromium 会拖垮现有服务微信个人号暂不做只能靠逆向/托管协议，封号风险比 QQ 高一档后两个表情包（官方市场）未装用 ai-biaoqingbao 的 100 张替代14. 方法论：这次学到的东西先打通最小链路，再叠功能。通道 → 模型 → 人格 → 插件，每一步都验证过再往上加。跳过任何一环，后面出问题都定位不到。把「以为」变成「日志」。「它好像不回我」和「reply_gate 判定 p=0.40 roll=0.65 → skip」是两件事。前者需要猜，后者只需要看。区分「主动」和「被动」是设计的基础。同一个机器人，哪些行为会自己冒出来、哪些必须被叫，必须能一句话说清。说不清就说明设计还没定型。国内服务器部署，网络问题占一半工作量。Docker Hub、GitHub、npm，三条链路的状态各不相同，先测再动手。复杂度要能被一句话解释。最后稳定下来的形态其实很简单：一个协议端、一个大脑、两个模型、几个插件。中间所有的折腾，都是在排除「不必要」。风控是产品约束，不是技术 bug。个人号协议天然有封号风险。接受它，然后通过「用小号、降频率、别主动」把风险压到可接受——而不是幻想把它降到零。15. 项目与素材来源这次用到的全部仓库，按用途归类。注意：社区插件变动极快，链接随时可能 404，请以实际可达为准。15.1 人设与配置包项目链接说明大肥鱼配置包zexuanw958-svg/dafeiyu-config-pack人设 Markdown + 6 个配套插件清单 + 语音/记忆参数 + 一键导入脚本这个包是整个项目里信息密度最高的一个，值得单独读一遍——它不只是给了一份人设，而是把「一个能跑的大肥鱼需要哪些部件」列成了清单。15.2 表情包素材项目链接说明大肥鱼表情包diwer/ai-biaoqingbao100 张中文 GIF（240/512 两档），带 catalog.json 分类目录仓库里还有 NOTICE.txt 写明了二创来源与使用边界（不提供商用授权），如果要商用请先看那份说明。15.3 平台与协议端项目链接说明NapCatNapNeko/NapCatQQQQ NT 协议端，输出 OneBot v11NapCat Dockermlikiowa/napcat-docker本文使用的部署形态AstrBotAstrBotDevs/AstrBot机器人框架（大脑）DeepSeek Harnessdeepseek-ai/deepseek-harness路线一的官方 Agent 运行时15.4 插件插件链接用途meme_manageranka-afk/astrbot_plugin_meme_manager表情包管理 + 语境发送group_chat_plusHim666233/astrbot_plugin_group_chat_plus群聊读空气、打字拟态livingmemorylxfight/astrbot_plugin_livingmemory长期记忆（向量检索）qqprofile_exAkusative/astrbot_plugin_qqprofile_ex改签名/状态/昵称qzonesujingyi1/astrbot_plugin_qzoneQQ 空间说说dsh-qqbottencent-connect/dsh-qqbot路线一的官方 QQ Bot 通道15.5 已失效的仓库（记录在此，省得后来人再踩）仓库状态替代Zhalslar/astrbot_plugin_qqprofile404Akusative/astrbot_plugin_qqprofile_exZhalslar/astrbot_plugin_qzone404sujingyi1/astrbot_plugin_qzoneqqprofile_ex 是基于原版改的，多了「AI 自主改签名/状态/昵称 + 定时状态切换」，工具名换成 change_my_* 后反倒更不容易撞名。装完记得检查：它默认三个 auto_* 全开，其中改昵称建议关掉——群里突然换了名字，成员会一脸问号。15.6 一点使用提醒插件的协议要看清。group_chat_plus 是 AGPL-3.0，二次分发有传染性要求。第三方插件等于把宿主机权限交给别人的代码。它们能读配置文件、能发网络请求。装之前至少扫一眼 main.py 有没有可疑的 subprocess。表情包素材的版权边界比插件更麻烦。二创角色、AI 改绘、原图来源，这三层权利关系在素材仓库的 NOTICE.txt 里写得很清楚，商用前务必确认。附录：几个可以直接复用的片段A. 看门狗 systemd 单元# /etc/systemd/system/napcat-watchdog.service
[Unit]
Description=NapCat offline watchdog
After=docker.service
Requires=docker.service

[Service]
Type=oneshot
ExecStart=/usr/local/bin/napcat-watchdog.sh# /etc/systemd/system/napcat-watchdog.timer
[Unit]
Description=Run NapCat watchdog every 2 minutes

[Timer]
OnBootSec=3min
OnUnitActiveSec=2min
Unit=napcat-watchdog.service

[Install]
WantedBy=timers.targetB. 概率门插件的核心（AstrBot）@filter.event_message_type(filter.EventMessageType.GROUP_MESSAGE)
async def gate(self, event: AstrMessageEvent) -&gt; None:
    text = (event.message_str or &quot;&quot;).strip()
    if text.startswith(&quot;/&quot;):
        return
    # @ / 引用 必回
    for seg in event.get_messages():
        if isinstance(seg, Comp.At) and str(seg.qq) == str(event.get_self_id()):
            return
    prob = 0.4 if is_peak(datetime.now()) else 0.6
    if random.random() &gt;= prob:
        event.stop_event()      # 关键：真正阻止模型调用event.stop_event() 有效的原因：管道调度器在阶段之间检查 is_stopped()，命中就 break，所以模型根本不会被调用——省的是真金白银。C. 两份必须收好权限的文件chmod 600 /opt/napcat-bot/docker-compose.yml            # 含小号密码
chmod 600 /path/to/astrbot/data/cmd_config.json        # 含所有 API Key
chmod 600 /path/to/astrbot/data/plugin_data/*/config.json（全文完。如果这篇对你有用，欢迎在评论区交流；有踩到别的坑也欢迎补充。）</description>
</item>
<item rdf:about="https://hxling.top/index.php/archives/7/">
<title>CC Switch 安装教程：从下载到一键切换 API 全流程图解</title>
<link>https://hxling.top/index.php/archives/7/</link>
<dc:date>2026-09-22T20:49:00+08:00</dc:date>
<description>简介： CC Switch 是一款免费开源的跨平台 AI 编程工具配置管理器，支持 Windows / macOS / Linux。它统一管理 Claude、Codex、Gemini 等工具的 API 供应商，通过可视化界面一键切换，免去手动编辑 JSON/TOML 配置的繁琐操作。CC Switch 是一款免费、开源的桌面应用，作用是统一管理 AI 编程工具的 API 供应商，Windows、macOS、Linux 全平台支持。用 Claude Code、Codex、Gemini CLI、OpenCode 这类 AI 编程工具时，经常要在不同供应商（官方、中转、自建等）之间切换，手动改配置文件很麻烦。CC Switch 把这些工具的供应商配置集中到一个图形界面里，切换时点一下就行，不用再复制粘贴配置内容。一、获取 CC SwitchCC Switch 安装包：CC Switch安装包（源自 GitHub，Windows + macOS + Linux）CC Switch 官方提供了各平台的安装包：安装包名称对应平台（适用场景）格式说明文件大小CC-Switch-v3.19.2-Windows.msiWindows x64（主流 Windows 电脑，推荐）MSI 安装包，双击安装12.5 MBCC-Switch-v3.19.2-Windows-Portable.zipWindows x64（免安装便携使用）压缩包，解压即用12.5 MBCC-Switch-v3.19.2-Windows-arm64.msiWindows Arm 64（骁龙等 ARM 芯片电脑）MSI 安装包，双击安装11.8 MBCC-Switch-v3.19.2-Windows-arm64-Portable.zipWindows Arm 64（免安装便携使用）压缩包，解压即用11.9 MBCC-Switch-v3.19.2-macOS.dmgmacOS（Apple Silicon 和 Intel 芯片均可，推荐）DMG 安装包，双击拖入应用程序25.9 MBCC-Switch-v3.19.2-Linux-x86_64.AppImageLinux x64（主流 Linux 发行版，推荐）AppImage 单文件，赋予执行权限即可运行87.6 MBCC-Switch-v3.19.2-Linux-x86_64.debLinux x64（Debian/Ubuntu 系发行版）Debian 包，用 dpkg/apt 安装12.5 MBCC-Switch-v3.19.2-Linux-x86_64.rpmLinux x64（RedHat/CentOS/Fedora 系发行版）RPM 包，用 rpm/yum 安装12.5 MBCC-Switch-v3.19.2-Linux-arm64.AppImageLinux Arm 64（ARM 架构设备）AppImage 单文件，赋予执行权限即可运行85.3 MBCC-Switch-v3.19.2-Linux-arm64.debLinux Arm 64（Debian/Ubuntu 系发行版）Debian 包，用 dpkg/apt 安装12 MBCC-Switch-v3.19.2-Linux-arm64.rpmLinux Arm 64（RedHat/CentOS/Fedora 系发行版）RPM 包，用 rpm/yum 安装12 MB二、安装 CC Switch1. Windows 平台安装有了 .msi 文件后双击启动，点击 Next：选择安装路径。默认装在 C 盘，想省系统盘空间可以改到 D 盘：点击 Install，开始安装：等待安装完成，完成后桌面会出现 CC Switch 快捷方式，双击就能打开它：如果不想写注册表，下载 Portable 便携版，解压后直接运行 exe 文件：注意： Windows 版本已禁用「一键安装」功能，如果需要安装 Claude Code 等工具，请手动安装后再通过 CC Switch 来管理。2. macOS 平台安装有了 .dmg 文件后双击打开，把 CC Switch 图标拖入「应用程序」文件夹，应用就能打开了。3. Linux 平台安装Ubuntu/Debian/Mint 用户获得 .deb 文件，执行：sudo apt install ./CC-Switch-*.debFedora/RHEL 用户获得 .rpm 文件，执行：sudo dnf install ./CC-Switch-*.rpm其他发行版可以直接获得 .AppImage 文件，加执行权限后运行即可：chmod +x CC-Switch-*.AppImage
./CC-Switch-*.AppImage三、CC Switch 基础使用CC Switch 的核心功能是管理和切换供应商，下面依次给大家讲清楚。1. 认识主界面打开 CC Switch 后，顶部是导航栏，可以选择当前管理的 AI 编程工具（Claude、Claude Desktop、Codex、Gemini、OpenCode、OpenClaw、Hermes），切换后下方列表就显示对应应用的供应商配置：导航栏最右侧是添加按钮（+）。界面主体是供应商卡片列表，每个供应商以一张卡片展示：2. 添加供应商点击主界面右上角的 + 按钮，在弹出窗口的「预设供应商」框里选择你的供应商（常用预设包括智谱 GLM、MiniMax、DeepSeek、Kimi 等），预设会自动填好端点地址，你只需要填写 API Key，点击「添加」即可。如果你的供应商不在预设里，选择「自定义」手动配置接口地址和模型信息。3. 切换供应商添加完成后供应商出现在列表中，切换有两种方式：一是主界面切换，把鼠标悬停在目标供应商卡片上，点击卡片上的「启用」按钮（▶）：二是托盘快速切换，右键系统托盘里的 CC Switch 图标，在「Claude」「Codex」等子菜单里直接点供应商名称，当前启用的会带勾选标记，不用打开主界面就能切。4. 健康检查添加完供应商后，可以点击旁边的「健康检查」按钮，发送一个测试请求来验证 API Key 和网络连通性。这个功能很实用，能帮你快速排除是 API Key 的问题还是网络的问题。配置正常的话，CC Switch 会给出「运行正常」的提示：5. 管理供应商卡片把鼠标悬停在卡片上，会显示一排操作按钮：启用（使用中）、编辑、复制、检测连通、配置用量查询、删除：编辑可以改供应商配置；复制能快速创建副本；检测连通可以测试模型连通性和响应速度；删除可以移除不需要的供应商（当前正在用的供应商要先切换到别的才能删除）。按住卡片左侧的拖拽手柄（≡）可以上下拖动调整供应商顺序。四、新手注意事项CC Switch 是管理工具，本身不包含任何 AI 服务。添加供应商时需要填入自己的 API 密钥或配置信息，密钥由各服务商提供。首次使用先添加一个供应商测试连通性，确认能正常调用再批量添加其他供应商。切换供应商后，已打开的 AI 编程工具（如 Claude Code）需要重启或新开会话才能生效。这是正常现象，不是 CC Switch 的问题——配置是启动时读取的，切换后记得重启对应工具。不同 AI 编程工具的配置格式不一样，CC Switch 为每个工具提供了对应的供应商添加入口。添加时先选对工具类型（Claude Code、Codex、Gemini CLI 等），再填对应配置，填错位置会导致切换无效。</description>
</item>
<item rdf:about="https://hxling.top/index.php/archives/3/">
<title>益诗误删文件复盘</title>
<link>https://hxling.top/index.php/archives/3/</link>
<dc:date>2026-09-19T21:46:36+08:00</dc:date>
<description>九月十九日下午，我在服务器上整理文件，敲了一条删除命令，删错了一个目录。益诗上线四个月，所有用户上传的图片都放在那里。包括 571 个人的头像，124 张诗歌封面，50 个诗集封面，79 张 AI 海报。这篇是复盘。我把它完整写下来，因为里面有几步值得记住，也有两个我自己犯的错误值得你绕开。一、先清点：什么还在，什么没了先要做的事是把边界划清楚。数据库完好无损。用户 1770 个，诗歌 15332 首（未删除部分），评论 2368 条，通知 34896 条，还有点赞收藏、关注关系、诗集、漂流瓶、成长记录——全部安好。这些都在 MySQL 里，误删的目录跟它无关。后端程序也活着。yishi.jar 在另一个目录，进程已经连续跑了 48 天。systemd 单元文件在 /etc/systemd/system/，不在 wwwroot 下面，所以数据库密码、Redis 密码、JWT 密钥、通义千问的 API Key、MinIO 凭据、QQ 互联的 AppID 和 AppKey，一个都没丢。这一点后来省了我很多事。丢的是这些：位置内容/www/wwwroot/poem-client/用户端 H5 静态文件/www/wwwroot/poem-admin/管理端静态文件/www/wwwroot/yishi-server/logs/日志目录/www/wwwroot/yishi-server/minio/minioMinIO 可执行文件本身/www/wwwroot/yishi-server/minio/data/对象存储数据，全部图片Nginx 配置在宝塔的 vhost 目录里，不在 wwwroot 下，也活着。二、让站点先活过来前端最容易补，本地构建产物都在。H5 有 82 个资源文件，管理端 85 个，打包压缩后 scp 上去解包，改属主权限就好。后端 jar 完全不用重传。我比对了两边的 MD5：线上  f160342af2d74778653aab791676de8f
本地  f160342af2d74778653aab791676de8f一致，说明线上跑的就是我本地这份构建，省掉一次重新打包和上传。APK 也在本地，yishi-0.11.0.apk，20,340,809 字节，放进 /apk/ 目录。Nginx 没有单独的 /apk/ location，走的是 SPA 兜底的静态服务，能正常下载。真正麻烦的是 MinIO。 它的可执行文件被删了，而且 —— dl.min.io 这个官方下载域已经停用，所有路径都返回 410 Gone：https://dl.min.io/server/minio/release/linux-amd64/minio            → 410
https://dl.min.io/server/minio/release/linux-amd64/archive/...      → 410
https://dl.min.io/server/minio/release/linux-amd64/minio.asc        → 410GitHub Releases 也是 404。最后在官方中文源拿到了：curl -sL --retry 3 -o minio \
  https://dl.minio.org.cn/server/minio/release/linux-amd64/minio版本 RELEASE.2025-07-23T15-54-02Z，110 MB，仍然支持 --console-address，systemd 单元一个字不用改。装回去之后还有一层坑。数据目录当时只剩一个 .minio.sys，MinIO 启动时报的是：Unable to load healing tracker: unformatted drive found, re-initializing..
Error: Read failed. Insufficient number of drives online它认不出这个盘，于是 mc mb 建桶直接报 Resource requested is unwritable。重启一次，让它把空盘重新格式化，然后建桶、设公共读：mc mb --ignore-existing local/yishi
mc anonymous set download local/yishi到这一步站点是活的，但 824 张图片真的没了。磁盘上、备份里、回收站里都没有。三、意外的出路那天下午我想通一件事：这些图片在我的服务器上没了，但它们在你手机里。/img/ 这个路径的响应头是这样的：expires: Sun, 19 Sep 2027 09:39:02 GMT
cache-control: max-age=31536000
cache-control: public, immutable一年有效期，immutable 意味着浏览器可以直接复用，不必回服务器校验。MinIO 的文件名是 UUID，永不变更，当初这么配就是为了让图片秒开。这个配置让图片在别人手机上留了一份完整的字节副本，而且源站返回 404 也不会触发重新拉取。同源页面上的 JavaScript 可以把它读出来，再传回服务器。因为写回的是完全相同的 object key，数据库里那 824 条 URL 一个字都不用改。我写了一个临时页面干这件事，跑完就撤了。技术路线值得记下来。第一次尝试，失败了最初我用两种方式去读缓存：fetch(url, {cache: &#039;force-cache&#039;})，以及一个游离的 new Image() 对象。结果 nginx 访问日志里是一整片 404 —— 每一次请求都打到了服务器。浏览器渲染图片时用的是另一条缓存通道。这一版只捞回 93 张，而且命中大多来自别的原因。换成真实渲染第二版换了思路：把图片真实地 &lt;img&gt; 渲染进 DOM，再从已经渲染出来的元素上取像素。// 图片既然能显示，就一定能从它身上取到像素
ctx.drawImage(img, 0, 0, cw, ch);
const blob = await new Promise(res =&gt; canvas.toBlob(res, type, 0.94));因为要经过 canvas 重新编码，还得配一条降级链：toBlob 编码不出来就退回 toDataURL，标准尺寸失败就把图缩到 60%、35% 再试。这一版数字从 93 涨到 272。然后页面崩了头像组有 571 张。第二版一次性把整组缩略图都挂进 DOM，移动端浏览器的内存直接爆掉了。第三版改成固定槽位：只保留 N 个 &lt;img&gt; 元素循环复用（默认 3 个，可调 2/3/4），每张判完状态立刻 removeAttribute(&#039;src&#039;) 释放，再换下一张。// DOM 节点数和内存占用恒定，跟图片总数无关
function release(slot) {
  try { slot.img.removeAttribute(&#039;src&#039;); } catch (e) { slot.img.src = &quot;&quot;; }
}跑 50 张和跑 571 张，占用完全一样。命中的图用 88×88 的小画布裁切收进网格，上限 400 张，单张约 31KB。顺带还发现一件事：/img/ 的响应带 vary: Origin，而站内「生成海报」功能是用 crossOrigin=&quot;anonymous&quot; 读图的 —— 那是另一条缓存条目。所以补了一条 crossorigin 备用路径。四、三分之一的墙最终停在 272 / 824，约 33%。为什么不是更多？我拿一个指标去验证：每张图片最后一次被服务器成功取到的时间。已经找回来的头像：98 张里 97 张的最后一次成功加载在九月仍然缺失的诗歌封面：44 张停在六月，25 张停在五月，只有 18 张落在九月规律很清楚了。immutable 和 expires 管的是新鲜度：浏览器愿意直接复用，不必回服务器校验。至于留存，移动浏览器和 iOS Safari 依然会按最近最少使用（LRU）清理长期不碰的条目。头像出现在每一个页面上，被反复读取，缓存条目一直是热的。诗歌封面只在特定卡片上出现，而 15333 首诗里只有 127 首带封面 —— 出现频率太低，条目早就凉了。我做了一个决定性的验证：把用户当天实际打开过的每一首诗的封面逐一对照。结果是 封面仍缺失的数量为 0。也就是说，他们能在手机上看到的那些诗歌图片，正是已经接回来的那 47 张。剩下那些，确实不在任何设备上留下过副本。这条路走到头了。五、然后我犯了第二个错误我没有立刻收手，转而去试磁盘取证 —— 图片被删掉才两个多小时，服务器还有 16G 空闲空间，我猜数据块可能还躺在原地。前半段是有收获的。我摸清了 MinIO 在磁盘上的真实布局：每个对象是一个以对象名为名的目录，里面有：xl.meta，约 350 字节，含 etag 和 content-typeDDir-&lt;uuid&gt;/part.1，实际数据，约 656KB 一片这意味着目录名本身就是文件名，理论上可以整目录找回，比缓存那条路完整得多。然后我跑了这条命令：dd if=/dev/vda3 bs=8M | LC_ALL=C grep -aob -F -f patterns.txtpatterns.txt 有 555 个模式 —— 552 个缺失文件名加上几个图片魔数。目标是遍历 40GB 磁盘找残留。这是那次事故里最糟的一个决定。它把生产盘的 I/O 占满，内存也被吃掉，最终拖垮了整台服务器，我只能在阿里云控制台强制重启。正确的做法是在离线快照上做，或者用 debugfs 定点读取 inode。任何长耗时的扫描都应该加 nice -n 19 和 ionice -c3，并且预先设好超时。判断数据能否恢复，优先用轻量证据：看一个 inode 的 extent 记录、做小范围采样，都足够下结论。代价是我为了多救几张图片，让整个站点又断了一次。六、重启之后服务器重启后所有服务都自动起来了：yishi、minio、nginx、mysql、redis、cron 全部 active。桶内 272 个对象完好，数据库行数与重启前一致。我做了收尾：删除临时恢复页面、接收程序、分组清单、nginx 反代配置、8099 端口还原 nginx 配置并删除部署时产生的备份文件移除 mc 的 local 别名清理 /tmp 和本地脚本目录复查确认站点根目录只剩 index.html / assets / static / apk站内公告也改写了一版，去掉了所有引导用户参与的步骤 —— 找回通道关了，不该再让任何人点进去。七、值得记住的几件事在生产目录里删东西之前，先确认它有没有备份。 这次的全部损失，源头就是这一条。我有数据库的每日备份，但对象存储目录不在任何备份计划里。immutable 管的是新鲜度。 这个响应头让浏览器愿意直接复用、不回源校验。条目能留多久，由最近最少使用策略决定 —— 低频访问的资源，缓存待不了太久。顺手配的缓存策略，在灾难时会变成救命的东西。 那 272 张图片能回来，靠的就是当初为了让页面快一点而加的 immutable。当时做这个决定，只想到性能这一层。读浏览器缓存要用渲染。 fetch(cache: &#039;force-cache&#039;) 和游离的 new Image() 都读不到渲染通道那份缓存，实测每一次请求都会打到服务器。长耗时的扫描要放在离线快照上做。 第二个错误比第一个更不可原谅，因为它是我在已经出事之后，又主动制造的一次故障。八、接下来已经加上了对象存储目录的异地备份，跟数据库放在同一套计划任务里。找不回的 552 张图片，位置会留空。头像随时可以重新上传，新图会被正常保存。那 272 张能回来，是一台台设备上那些看不见的副本替我保住的。写这篇文章的时候我在想，如果当初没有顺手开那个缓存头，现在的数字会是 0。写于 2026-09-19，服务器重启之后。</description>
</item>
</rdf:RDF>