A WeChat Group Chat AI Built on GUI Automation: 5 Real Failures and 7 Anti-Robot Design Rules
When a platform refuses to let bots into a channel, GUI automation can impersonate a human at the OS level—but the cost is fragility, a full-time desktop, and a persistent account-ban risk. The hard problem isn't generating replies; it's deciding when not to speak, which inverts the usual priorities for chat-agent design.
Official WeChat channels leave no room for bots in ordinary group chats: personal-account AI is explicitly prohibited from being added to groups, Work WeChat bots are restricted to internal enterprise groups, and Tencent's own built-in AI can't send group messages. A working alternative operates a real WeChat account through GUI automation—locating the input box, writing to the clipboard, pasting, pressing Enter—and reads messages via local vision (Ollama) and speech-to-text (faster-whisper) models. In the group, its messages are indistinguishable from a human's in the moment they're seen.
A five-day run counted 229 group messages against 9 private ones, and the incident log records five real failures that shaped the system: duplicate sends from running two instances, replying to an 86-minute-old message, a 186-second queue delay, a good reply silently evicted by newer messages, and the worst—the model believing it had already said something it never actually sent. Each failure forced a design change, from a stale-message gate (10 minutes) to a queue that evicts the oldest rather than the newest, to separating "what to say" from "what was sent" in the state layer.
The system's guiding principle is that limiting the AI's ability to speak is ten times more important than making it good at speaking. It holds a 30-second minimum delay, uses truncated log-normal delays, sends at most three short lines in a burst, debounces incoming messages (1.5 seconds when @-ed, 6 seconds for normal chat), caps emoji at 8 per day with 15-minute intervals, carries only the last 20 messages as context, and builds per-member profiles with traits and evidence-backed facts. A separate journal counts deliberate silences (51 in the logged period) as its proudest metric. The whole thing runs on a secondary account in a small known group, and the risk of platform enforcement is stated plainly.
The platform's refusal to expose group chat bots is a design choice, not a capability gap, so any workaround carries an inherent ban risk and requires a clear ethical line.
The three-day build time was spent mostly on making the system shut up rather than talk, which inverts the typical feature-priority list for chat agents.
A model that believes it said something it never sent creates a social break that no prompt engineering can fix; the fix lives in the state layer, separating generation from delivery.
The 25:1 difference between group and private message volume shows that context availability, not model skill, determines whether an agent has anything to say.
Bypassing the bot label is the one part of the project its own creator admits to being unsure about; the justification rests on a small trusted group and immediate shutdown on warning.
Logging deliberate silences proved more valuable than logging speech, because the absence of a message is the hardest failure to debug without a trace.
Incident 5, that 'it thought it had said it' — I stepped in the same pit on the other side: I thought it hadn't replied, but it actually had. On the web-end streaming response there's no reliable message ID, so the only way is to take a baseline snapshot of the reply list before sending and count the extra containers. But the outer container gets wholesale replaced before and after rendering, and the reference is already detached from the document, so what's read is still the previous round — in the group it looks like 'why is it repeating what it just said'. Switching to deduplication by text hash, when both rounds reply '好的' (okay) it gets falsely killed, and it keeps waiting until timeout. The judgement finally comes down to one criterion: the reply text is sampled twice in a row without changing, plus the send button returns to clickable. One different view: these two accounts shouldn't be charged to the conversation layer. Letting the model itself remember 'I said' is exactly the cause of incident 5. On my side, delivery success only writes an external marker, and I check it before retrying — keep it inside, and it will cover up lies for you.
A hug to a fellow professional.