Multi-Channel AI Agent Routing: Preventing Double-Replies
Running a single AI agent is straightforward. Running multiple agent instances across different devices without them colliding is an architectural challenge. The most visible manifestation of this problem: double-replies in Slack when both your laptop and your Mac Mini respond to the same message.
This article explores how to build a multi-channel routing system for AI agents, ensuring that only one instance handles each request while maintaining the ability to delegate tasks between devices.
The Double-Reply Problem
Imagine you have two AI agent instances:
- Laptop: Interactive development, code reviews, ad-hoc queries
- Mac Mini: Automated monitoring, scheduled reports, always-on tasks
Both instances connect to Slack using the same bot token. A coworker asks a question in #engineering. Both agents see the message. Both respond. Now you have duplicate replies, wasted API calls, and confused users.
The naive solution is to disable Slack on one instance. But this breaks delegation: what if the laptop needs to route a task to the Mac Mini because it requires filesystem access that only exists on that device? What if the Mac Mini needs to escalate an alert to the laptop for interactive debugging?
The constraint is clear: multiple agent instances, one Slack workspace, no duplicate responses, with bidirectional delegation.
Solution: Single Bot Token with Relay Channels
The architecture has three components:
1. Single bot token holder: Only one instance connects directly to Slack with the bot token and has full channel access. This becomes the “primary” instance. For a laptop + Mac Mini setup, the Mac Mini is primary because it’s always-on and handles scheduled tasks.
2. Relay channel: A private Slack channel (e.g., #relay-to-laptop) acts as a message bus. When the primary instance receives a request that should be handled by another instance, it posts the request to the relay channel. The secondary instance monitors the relay channel and processes messages from it.
3. Channel assignment table: A configuration file maps Slack channels to responsible instances. When the primary receives a message, it checks the table. If the channel belongs to the primary, it responds directly. If it belongs to a secondary instance, it forwards to the relay channel.
This architecture ensures exactly-once processing: every Slack message is handled by exactly one instance. The relay channel provides delegation without requiring multiple bot tokens.
Channel Assignment Table
Here’s the channel mapping for a laptop + Mac Mini setup:
| Channel | Owner | Purpose |
|---|---|---|
| #stark-industries | Mac Mini | Investment intelligence, scheduled reports |
| #code-review | Laptop | Interactive PR reviews, requires local repo access |
| #engineering | Mac Mini | General questions, documentation lookups |
| #alerts | Mac Mini | Automated monitoring, cron job outputs |
| #relay-to-laptop | Laptop | Private relay channel for task delegation |
| Direct messages | Mac Mini | Quick questions, default to always-on instance |
The assignment logic is simple: channels requiring interactive access or local filesystem operations go to the laptop. Channels for scheduled tasks, monitoring, or general queries go to the Mac Mini. Ambiguous cases default to the always-on instance (Mac Mini) for reliability.
Delegation Patterns
Even with channel assignments, some tasks need to be routed between instances. Here are the common delegation patterns:
Pattern 1: Mac Mini → Laptop (Interactive Debugging)
The Mac Mini detects an alert that requires human intervention. It posts to #relay-to-laptop with context:
[ALERT ESCALATION]
Source: DOUGHCON monitor
Issue: VIX spike detected (27.3, +15% vs yesterday)
Data: {...}
Action requested: Manual review and trade decision
The laptop agent processes the relay message, opens a thread in #stark-industries, and pings the user for input. After resolution, the laptop posts a summary back to the relay channel so the Mac Mini’s monitoring loop can continue.
Pattern 2: Laptop → Mac Mini (Scheduled Task Creation)
The user asks the laptop to set up a new cron job. The laptop drafts the job configuration but doesn’t have cron access (it’s a mobile device). It posts to a #relay-to-mini channel:
[CRON REQUEST]
Schedule: 0 9 * * * (daily at 9am)
Command: python /scripts/market_scan.py
Description: Morning geopolitical scan
Approve? Y/N
The Mac Mini reads the relay, creates the cron job, and confirms back via Slack thread.
Pattern 3: Capability-Based Routing
Some tasks are capability-dependent, not channel-dependent. Example: “Generate a chart from this CSV.” Both instances can do this, but the laptop has GPU access for faster rendering. The routing logic checks capabilities, not just channel assignment.
This requires each instance to advertise its capabilities:
- Mac Mini: Cron access, always-on, database access, API integrations
- Laptop: GPU, local repositories, interactive shell, portable (for conference demos)
When the Mac Mini receives a request in a channel it owns but the task requires a capability it lacks, it automatically delegates via relay.
Single-Token Architecture
Why use a relay channel instead of giving both instances their own bot tokens? Three reasons:
1. Slack rate limits: Slack’s API has per-workspace rate limits. Two bots double the request volume, increasing the risk of hitting quotas. A single bot with relay channels keeps all requests under one token’s quota.
2. User confusion: Two bots with different names (e.g., @agent-laptop and @agent-mini) force users to remember which bot to ping for which task. A single bot with intelligent routing is simpler.
3. Channel permissions: Managing channel access for two bots is tedious. If you add a new channel, you must invite both bots. With a single token, the primary bot has access everywhere, and routing is configuration-only.
The tradeoff is relay latency: a delegated task requires two Slack API calls (primary → relay, relay → secondary) instead of one. For most use cases, the added ~200ms is negligible compared to LLM inference time.
Routing Architecture Diagram
Here’s the message flow for the single-token relay architecture:
Alternative Architectures Evaluated
Option 1: Dual bot tokens
Give each instance its own bot token and invite both to Slack. Pros: no relay latency, simpler message flow. Cons: users must remember which bot to ping, doubled API usage, doubled permission management. Rejected because user experience degrades.
Option 2: Dashboard-only for secondary instance
Remove Slack entirely from the laptop. Interact only via local CLI or web dashboard. Pros: no double-reply risk, simplest architecture. Cons: breaks the “single interface” principle — users must context-switch between Slack (for Mac Mini) and terminal (for laptop). Rejected because it fragments the UX.
Option 3: Leader election
Both instances connect with the same bot token, and they use a leader election protocol (e.g., etcd, Redis) to decide who handles each message. Pros: dynamic failover if primary goes offline. Cons: requires shared infrastructure (Redis server), complex failure modes, overkill for two instances. Rejected as over-engineered.
The single-token relay architecture wins because it’s simple, requires no external dependencies, and scales to 3+ instances by adding more relay channels.
Phase 1 vs Phase 2
Phase 1 (current): Laptop is primary, no Mac Mini yet. All channels handled locally. Relay channels exist but unused. This allows testing the routing logic before adding a second instance.
Phase 2 (post-Mac Mini arrival): Mac Mini becomes primary. Laptop connects via relay. Channel assignments activate. The laptop transitions from “handles everything” to “handles only delegated tasks.”
The phased rollout minimizes risk. Phase 1 validates the relay mechanism with zero impact (relay channels are no-ops when secondary instance is offline). Phase 2 flips the switch without code changes — only configuration updates.
Tasks That Route to Local vs Stay on Cloud
Some tasks inherently belong to one instance:
Always route to laptop:
- Code review (requires local git repos and IDE integration)
- Interactive debugging (user is physically present at laptop)
- Exploratory data analysis (benefits from GPU acceleration)
Always stay on Mac Mini:
- Scheduled cron jobs (laptop may be offline or sleeping)
- Monitoring and alerting (needs 24/7 uptime)
- Database queries (Mac Mini has persistent connection to local PostgreSQL instance)
Context-dependent:
- “Summarize this article” — either instance can handle it, defaults to primary for simplicity
- “Generate a visualization” — laptop preferred if user is active (for immediate feedback), Mac Mini if user is offline (for async delivery)
The routing logic checks three factors: channel assignment, task capability requirements, and instance availability. If all three are satisfied by multiple instances, default to primary.
Implementation Notes
Relay message format: Messages posted to relay channels include metadata:
{
"type": "relay",
"source_channel": "code-review",
"original_user": "U12345",
"original_message": "Review PR #42",
"timestamp": "2026-04-28T10:30:00Z",
"thread_ts": "1234567890.123456"
}
This ensures the secondary instance has full context to respond appropriately.
Response routing: When the laptop finishes processing a relayed task, it posts back to the original channel. But it doesn’t have a bot token, so it sends the response to another relay channel (#relay-to-mini) with instructions for the Mac Mini to post on its behalf.
Monitoring: Each instance logs all routing decisions. A daily report summarizes routing stats: how many messages handled directly, how many relayed, average relay latency. This helps identify misconfigured channel assignments.
Failover: If the Mac Mini goes offline, the laptop doesn’t receive relay messages. To handle this, the laptop periodically checks for messages in the channels it owns (e.g., #code-review). If it sees an unhandled message older than 5 minutes, it responds directly by posting via the Mac Mini’s bot token (requires securely sharing the token, e.g., via 1Password).
Conclusion
Running multiple AI agent instances across devices without double-replies requires intentional routing architecture. The single-token relay pattern solves this: one instance holds the bot token and routes messages to other instances via private relay channels.
Key principles:
- Assign each channel to exactly one instance based on task type
- Use relay channels for delegation, not multiple bot tokens
- Include full context in relay messages (original channel, user, thread)
- Implement capability-based routing for ambiguous tasks
- Monitor routing stats to detect misconfigurations
With this architecture, you can run a laptop for interactive work and a Mac Mini for always-on monitoring without collision. Users see a single bot that intelligently routes their requests to the right backend.
The relay pattern scales beyond two instances: add a cloud instance by creating #relay-to-cloud and updating the routing table. As long as only one instance holds the bot token and all others route through relays, you maintain exactly-once message processing.