dwgx@blog:~$dwgx
> cd ../posts

YuKiKo: A Self-Hosted AI QQ Bot

// created

YuKiKo is a self-hosted AI QQ bot that can chat, take song requests, and parse video links. It’s built on NoneBot2 + OneBot V11, with NapCat sitting on the QQ side and connecting in over a reverse WebSocket.

It never touches the QQ protocol itself, that’s left to NapCat to chew on, and YuKiKo only speaks OneBot V11. NapCat is configured to report over reverse WS to ws://<host>:<port>/onebot/v11/ws, and once the access tokens on both sides match, the link is up. The protocol layer and the logic layer are fully decoupled, so if NapCat swaps implementations or goes down one day, not a single line changes on this end.

The model isn’t locked to any single vendor: OpenAI, Claude, DeepSeek, Gemini, Tongyi, and Moonshot all plug in, and whichever is cheap and online gets the call. The routing is where the effort went. When a message comes in, it first decides whether to respond at all and which model should answer, gated by a confidence threshold, so a random line in a group chat doesn’t fire off an expensive inference every time.

The agent system is the core: dozens of built-in tools, with the model doing multi-step reasoning and calling tools in sequence on its own, rather than one question, one answer. Music playback had the most pitfalls: multi-platform search, VIP unlocking, guarding against mixing up artists, and falling back to Bilibili audio when nothing turns up. Video parsing supports Bilibili / Douyin / Kuaishou / AcFun, and once a clip is downloaded it can analyze the content along the way.

Config is split into three layers. .env holds the sensitive stuff like ports and keys, config/config.yml governs bot behavior and model parameters, and each plugin gets its own yml. Keys are stored encrypted, permissions come in three tiers, and there’s a self-check that stops the bot from treating its own messages as user input and answering itself in a loop, a pitfall that took a long time to sort out.

The admin panel is written in React: hot-reload config online, watch logs in real time, import/export the database, and chat with the bot directly in the browser to test it. Plugins are hot-swappable, drop one into plugins/, set up its own yml, and it works, with visual toggles in the panel; NewAPI, Wayback, and ConnectCLI come built in. Deployment has a bootstrap script, and one command does it:

bash <(curl -fsSL https://raw.githubusercontent.com/dwgx/YuKiKo/main/bootstrap.sh)

System dependencies, the Python virtualenv, the WebUI build, and systemd registration all happen in one pass, and when it’s done you get a yukiko CLI with ops commands like status/logs/doctor/backup/update all in there, which I hammer on every day myself.