Back to projects

Selected work

Multi-Channel AI Chatbot Platform

A multi-user AI companion platform operated in a QQ community, built around knowledge grounding, controlled group-chat interaction, and progressive safety controls.

From a character chatbot to a community system

I independently deployed and iterated on a multi-user AI companion platform for a character and interest community. QQ was the reference deployment with real operating evidence; Discord completed end-to-end validation, but has no long-running operating metrics.

The challenge was not simply producing an in-character response. The underlying model had not been trained for this particular character or community, and the bot had to remain useful without replying to every message, breaking down under concurrent state writes, or becoming an easy target for disruptive interactions.

Between May and July 2026, the QQ reference deployment accumulated 22 days of actual uptime and recorded 5,689 calls to the core DeepSeek model. It was not a continuous period: the bot was offline for roughly one to two weeks while I could not maintain it. The deployment served a 5,000+ member community. The 300+ figure refers to members who posted repeatedly in the group during that period and interacted with the bot to some degree; it is neither a concurrent-user count nor a reply count.

My role and the platform boundary

I owned the QQ deployment, iterative feature work, operational retrospectives, and feedback analysis. I built the web-data collection and knowledge-preparation workflow, the layered safety and model-routing rules, multi-user enforcement behavior, time- and random-event persona behavior, a QQ sticker-capture replacement plugin, and the operational configuration and tests.

AstrBot, OneBot/NapCat, RAG, and long-term memory are integrated and extended framework or ecosystem capabilities, not systems I claim to have built from scratch. I substantially redesigned the upstream-inspired enhance-mode and life-scheduler plugins; their core funnel and event logic are my work.

Simplified message-processing path: incoming messages pass through safety and routing controls, receive persona, RAG, and memory context, then reach the response model.

This is the simplified path used to explain the case study. The public repository contains the fuller deployment topology, plugin structure, and operational details.

Calibrating persona and social rhythm

I repeatedly revised the character system prompt when responses missed signature speech patterns, became overly accommodating, or let users steer the character too far from its constraints. After each update, I checked targeted questions alongside group-chat feedback and reviews against character reference material and narrative writing. I used both human review and AI-assisted review as supporting signals, not as a substitute for judgment.

This work did not train a model. It made the platform's existing model follow a clearer, testable set of persona constraints.

A group-chat bot that immediately answers every message quickly disrupts normal conversation. I reduced passive reply frequency so that, after a trigger, the bot could wait for a number of messages or a time interval before responding.

At the same time, it was not entirely passive. When no one had triggered it, the bot could speak under a time- and event-table policy, while remaining silent late at night. This separated two goals that otherwise conflict: avoiding constant interruptions while retaining a bounded sense of character presence.

For proactive replies, I integrated and lightly adapted an upstream-inspired capability into the interaction funnel. It batches candidate messages before passing them to a small intent model. The small model decides whether a reply is warranted; only a positive decision calls the larger response model. Batching avoids repeatedly sending nearly identical context while the group conversation is still moving.

Grounding a model in the world users actually discuss

The unmodified model could give weak answers about character details, world-building, community nicknames, and aliases. To give the existing retrieval capability better inputs, I collected public material from sources including Moegirlpedia and the official Uma Musume site, then cleaned, filtered, and organized it into documents.

The framework handled chunking and knowledge-base import. I selected chunking values after several iterations rather than presenting this as a self-built RAG stack. The useful contribution was preparing and tuning a body of knowledge that matched the character and the community's actual questions.

Feedback supported that direction, but it was not a large research study. Among 25 anonymized responses from 44 views, 16 respondents prioritized reliable reactive replies and 15 valued long-term preferences and memories. I used those signals to continue tuning interaction rhythm and world-knowledge support; raw survey data is not public.

Applying community controls before expensive model calls

In a shared community, the bot also received hostile language, attempts to bypass character or safety constraints, and emotionally coercive prompt-injection attempts. I designed a layered path: allow/deny rules and lightweight or local-model classification run before a request reaches a heavier generation model. Unsafe messages can be ignored or blocked, while safe messages continue to response generation.

Repeated violations follow progressive restrictions of 12, 24, then 48 hours, with the record resetting after one week. In the QQ reference deployment, the rules intercepted approximately four clearly hostile attempts and could handle some homophones and indirect phrasing. The bot's own ignore or block path was used in operation. QQ administrator bans were designed as a separate option, but the bot was not granted administrator privileges, so I do not claim that administrator-ban path was exercised in production.

Resolving a real SQLite write-contention failure

Under concurrent writes to memory or state, the bot could remain running yet stop replying. Group members might reasonably assume it was simply offline; I identified the problem from operational database is locked errors.

After several attempts, I added SQLite WAL and targeted PRAGMA configuration to address write contention without introducing another database service. The observed class of database error was resolved in this deployment. This was a scope-appropriate reliability trade-off, not a claim that SQLite is the right persistence layer for every scale or workload.

Evidence, limits, and reflection

The QQ deployment supplies the operating evidence in this case study. Discord demonstrates an end-to-end multi-channel integration only. The repository includes the deployment configuration, SQLite patch, plugin extensions, and tests; chat logs, survey exports, secrets, and identifiable information remain private.

The next constraint was not solely technical quality. Sustaining the project depended on maintenance time and community adoption: not every traditional group-chat member welcomes an AI participant, and willingness to pay did not naturally cover continued operation. That distinction changed how I evaluate future work: a capable feature is not automatically a product that a community will sustain.