It’s tempting to build a separate bot for every channel: one for Telegram, one for Slack, one for your internal dashboard. It works—until your product logic starts drifting between surfaces and every “small change” becomes three rewrites.
Buffy Agent is built around the opposite pattern: one behavior core, many thin interfaces. The bots are replaceable. The behavior model and history are not.
Definition: behavior core vs. adapter
- Behavior core: the single place where you model activities, reminders, and memory—and decide what the agent should do next.
- Adapter: a thin layer that turns platform events (Telegram/Slack/ChatGPT/internal) into a unified message, and renders replies back in that platform’s UX.
If you keep this separation strict, adding a new channel becomes an integration task, not a new product.
The anti-pattern: a bot brain per channel
Multi-bot stacks usually fail in the same ways:
- Feature drift: Telegram supports snooze; Slack doesn’t. The experience diverges.
- Copy-paste logic: “completion rules” and scheduling get duplicated, then updated inconsistently.
- Fragmented state: each channel has “its own truth”, so you can’t reason over the whole user journey.
If you’re building an agent that coordinates habits/tasks/routines across a day, drift isn’t just annoying—it breaks correctness.
The pattern: one core, thin adapters
The core idea is simple:
- Adapters receive events from channels (Telegram/Slack/ChatGPT/internal).
- They normalize events into a unified message format.
- The behavior core owns the logic: parse intent → update activities/history → schedule reminders → generate replies.
Adapters should not contain rules like “what counts as done” or “how to schedule a routine.” That’s how drift happens.
A minimal unified message format
You don’t need a giant schema. You need enough to keep behavior consistent:
- Identity:
user_id(and optionallyworkspace_id) - Channel:
platform(telegram/slack/chatgpt/internal) + conversation/thread ids - Message: user text or structured command
- Timestamp: when it happened
- Context (optional): timezone, locale, user preferences
The adapter’s job is mapping whatever the platform gives you into this envelope.
Reference architecture (mental model)
At a high level:
- Interface layer: Telegram handler, Slack app, ChatGPT surface, internal bot
- Gateway: auth, rate limits, normalization
- Behavior core:
- Activity model (habit/task/routine)
- Reminder engine
- Memory system (short-term + episodic + semantic)
- Reply generation
If you want an OpenClaw-centric reference boundary, start here:
Concrete example: adding a new channel in one week
Say you already support Telegram and you want to add Slack.
What you don’t want to do
- Re-implement “snooze 20m” in Slack.
- Rebuild routine scheduling logic in Slack.
- Invent a second database for Slack-specific state.
What you do instead (thin adapter)
- Receive Slack events (message, thread, user).
- Normalize into your unified envelope.
- Call the behavior core API.
- Render the core’s reply back into Slack’s UX conventions (threads, DMs, channel messages).
The result: behavior stays consistent across channels because the same core decisions drive both.
Practical checklist (to avoid drift)
- Keep adapters “dumb”: I/O + normalization + rendering.
- Centralize behavior rules: completion, snooze/skip semantics, scheduling, escalation.
- Use one canonical event history so the core can learn cross-channel patterns.
- Prefer windows over fixed times; avoid platform-specific scheduling hacks.
- Add “further reading” links in your product docs so users know where to start.
Where to go next
- Next step: if you’re building adapters in OpenClaw-style workflows, start here: Buffy + OpenClaw API Integration