# QQ Agent puts a stateless LLM bot in a QQ group on your own Windows desktop

> QQ Agent is an Electron desktop app that connects a chat protocol implementation to any OpenAI compatible API, runs on an event driven and stateless session model so per reply token cost does not grow with the conversation, and keeps its whole configuration in a console. It is MIT licensed, Windows only, and it bundles a portable QQ client you are told to check against Tencent's terms before redistributing.

**K0nd1us/QQ-agent** — 立志做使用起来最简单能实现的功能最全面的LLM机器人。

- Repository: https://github.com/K0nd1us/QQ-agent
- Stars: 826 · Forks: 73
- Language: JavaScript
- License: MIT
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/k0nd1us-qq-agent

## Event driven and stateless, so the bill per reply stays small

The architecture section names the cost control mechanism directly. Group chat messages are written continuously into a local JSON archive, each with a timestamp and a read state. Every time there is a new message, or an opportunity the bot takes on its own, a fresh independent session is opened, and the prompt is a static system prompt holding the persona, the rules and the strategy, plus an archive summary, plus the new message in hand. The session is discarded when it ends, so the model side carries zero history and the cost of one processing run is a constant small amount that does not swell with the size of the chat. While a run is in progress, incoming messages only go to the archive, and once the run finishes the app finds unread messages and opens another session, repeating until the backlog is clear. Long term facts travel separately, through a memory tool that persists across runs. The consequence for an operator is concrete: the token cost of the bot in a busy group is a function of how often it speaks and how much context each reply carries, not of how long the group has been running.

## Protocol side, brain, shell and console are separate pieces

The composition section breaks the system into four parts, and the separation is what makes the app replaceable in each dimension. The protocol side connects to QQ through a OneBot v11 forward WebSocket, with SnowLuma as the default and any compatible implementation swappable in. The brain is any OpenAI compatible API, so the official service, a relay service or a local gateway all work and the model is selected in settings. The desktop shell is Electron, with a resident tray icon, start on boot, closing the window not quitting the app, and automatic yielding when a port is already taken. The console is plain HTML, CSS and JavaScript with no framework, with light and dark themes that can follow the system, and a full design token system. The repository layout mirrors that, with an electron/ directory for the shell, a server/ directory, a src/ directory, a ui/ directory holding the interface, script/ for setup and packaging, doc/ for the developer documentation, test/ for the test suites, and assets/ and build/ for packaging. The package metadata names the entry points, with main at electron/main.js and separate scripts for the desktop app and a headless server mode.

## Response levels, keywords, and the switch that decides whether it talks at all

The chat settings are where the product decisions are, and one slider does most of the work. The response level is a continuous 0 to 100 scale, and level 1 responds only when mentioned, level 2 adds keyword triggers, level 3 responds randomly with a probability determined linearly by the slider position, and level 4 responds to everything. A message that does not match is simply marked as read, which costs no tokens at all. The trigger also decides how much context is included, and a message where the bot was mentioned always carries the most context. There is a separate 0 to 3 slider for sticker eagerness, ranging from discouraged to sticker enthusiast, and the README is explicit that this is guidance in the prompt rather than an enforced rule. The persona system ships a built in character card that works out of the box with several templates, and participation style can be set to quiet, normal or active. Per group switches, a direct message switch and a blocklist sit alongside, and the blocklist behaviour is stated precisely: a blocked person is not archived, does not trigger the bot and does not enter the prompt at all. For a group, that combination is the difference between a bot that joins in and one that gets ignored.

## Memory, search and the safety boundary around the model

Two capabilities extend the bot beyond a prompt and a reply. Memory is bidirectional and per member, so the bot records a long term impression of each person, including what style they write in, what they find funny and what their sore spots are, and the memory page lets you view and edit those records by hand, trigger a cleanup that merges duplicates and drops stale entries, or refresh one person's record. Search is built in with six services, one of which needs no key at all for Bing web parsing, and you can add any compatible service, several if you like. On the safety side, the README lists a set of boundaries that matter for anything with group access. Tools are bound to the session by nature, so the model cannot send a message to a different group. Sending is restricted by a whitelist and rate limited. Web fetching carries full SSRF protection, with DNS validation, IP pinning and manual redirect following, and image addresses are forbidden from pointing at internal networks. The console shows per group and per friend message stores with unread markers, a memory record per member that you can edit or delete, and a usage view with three dimension statistics and drill down detail, including a per call count breakdown over the 28 built in tools grouped by category.

## Plugins and skills, and the one switch that turns them off

Extensions come in two shapes, chosen by when they should run, and dropping a directory in place makes them live. A deterministic type, the plugin, goes in plugins/<id>/ with a plugin.json manifest and an index.js exposing providers and hooks, and it is the core code that decides to run it: when the condition is met it runs, and it never passes through the LLM. An LLM type, the skill, goes in skills/<id>/ with a skill.json manifest and an index.js that calls registerTool and supplies a prompt fragment, and here the model decides, reading the tool description and judging whether to call it. The rule of thumb in the README is one line, if the rule can be hardcoded it is a plugin, if it needs to understand plain language it is a skill. The failure mode of choosing wrong is worth knowing, because the README says it is silent: the feature simply does not work, with no error and no crash. The extension mechanism has rules that protect the core. There is a single switch, config.skills[id].enabled, and disabling an extension makes its tools, capabilities, prompt fragments and request rewrites all stop at once, so you cannot have the interface show it as off while it keeps working. Dependencies are expressed by capability rather than by name, so the core asks who provides llm.request-params and never imports a specific extension, and an extension's prompt fragments are forced below the core rules in priority. The console keeps two tabs for skills and plugins and tells you why something did not take effect, whether not enabled, a missing dependency, a load failure or an unsupported model.

## The bundled QQ client and the licensing question it raises

This is the part to read before you install anything. The repository does not distribute QQ itself. SnowLuma, the protocol client, is an independent third party project with its own EULA and is not shipped with the repository, so you obtain it from its official channel and extract it into the project root keeping the directory named snowluma. The QQ client is distributed as a portable package, because QQ 9.9.x uses a Tencent built installer called HummerSetup that does not accept the silent switches used by NSIS or Inno, and because an existing newer QQ on the machine refuses to be downgraded. The setup step downloads the portable package, verifies its SHA256, extracts it and is idempotent, and the package is around 1.5 GB unpacked, writes no registry entries, installs no service, launches with --user-data-dir so it does not conflict with an installed QQ, carries no user data at all, and has its run logs and uninstaller stripped while keeping the licence files. You can also build the archive yourself with npm run pack:qq, which prints the SHA256 and two configuration lines, and the README reports a measured reduction from 1.5 GB to 653 MB, 42 percent, packed in 24 seconds. The warning is explicit: QQ is Tencent's proprietary software, the portable package is a copy made without modification, you must check Tencent's licence terms yourself before redistributing, and the repository does not ship the QQ binary. A separate table explains the difference between the target client version, the portable package and a fallback installer whose filename is hardcoded so that a download failure does not surface as a confusing 404.

## Getting it running, and the licence of the code itself

Installation is three commands, and the third one does the heavy lifting of downloading the protocol client and the portable QQ build.

```bash
git clone https://github.com/K0nd1us/QQ-agent.git
cd qq-agent
npm install
```

The setup step is a separate npm script that fetches SnowLuma and the pinned portable QQ, and the package metadata records what each script does.

```json
    "start": "electron .",
    "server": "node src/server.js",
    "setup": "node scripts/setup.mjs",
    "setup:check": "node scripts/setup.mjs --check",
```

After that you start the app, open the SnowLuma tab, start QQ and scan the QR code to log in, and the settings page opens with a health check card at the top telling you what is missing: the endpoint address and key, the model, and the whitelist. The whitelist is filled by pulling your group and friend lists from the QQ account and ticking the ones you want, and the bot only works for conversations on that list. There is also a headless mode that serves the console in a browser at http://127.0.0.1:3210, and a test script that runs a long chain of suites, from the self test through skill architecture, tool and skill audits, usage end to end and prompt checks. The package metadata requires Node 20 or newer, sets the type to ES modules, and builds a Windows installer with the NSIS target. The code is MIT licensed, which covers the application, and the separate question of the QQ client is the one the README asks you to resolve yourself.

## Conclusion

QQ Agent suits a developer who wants a group chat bot on QQ without paying for a hosted agent service, and who can live with a Windows desktop app, a separately installed protocol client and a portable QQ build. It is the wrong tool if you need a server deployment, since the package metadata targets Windows and the console is a desktop application, and the wrong tool if your members would be uncomfortable talking to a model that answers as a person, because the persona and participation features are built for exactly that. Before you deploy it, read the licensing note on the bundled QQ client, since the README says redistribution is your responsibility to check against Tencent's terms, and decide which responses the response level slider should allow before you point it at a real group.

## FAQ

### How does QQ Agent keep its token cost per reply constant?

The architecture is event driven and stateless at the model level. Each new message opens a fresh session built from a static system prompt, an archive summary and the new message, and the session is discarded when it ends, so the cost of one run does not grow with the length of the chat. Long term information is persisted separately through a memory tool.

### Which model providers can QQ Agent use?

The brain is any OpenAI compatible API, so the official service, a relay service or a local gateway all work, and the model is selected in the settings. There is a model catalogue filled from a Base URL and a key, a one click test that pings the endpoint and shows latency, and a built in price table for 140 models that can be turned off and replaced with your own rates.

### Can I redistribute the portable QQ client that comes with QQ Agent?

That is your call to make. The README states that QQ is Tencent's proprietary software with copyright and licensing belonging to Tencent, that the portable package is a copy made only to make the no install approach possible and contains no modification, and that you should check Tencent's licence terms yourself before redistributing. The repository does not ship the QQ binary itself.

## Sources

- [Issues](https://github.com/K0nd1us/QQ-agent/issues)
- [K0nd1us/QQ-agent on GitHub](https://github.com/K0nd1us/QQ-agent)
- [License: MIT](https://github.com/K0nd1us/QQ-agent/blob/main/LICENSE)
- [README](https://github.com/K0nd1us/QQ-agent/blob/main/README.md)
- [Releases](https://github.com/K0nd1us/QQ-agent/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/k0nd1us-qq-agent
