Multi-Agent Routing in Telegram Topics

This is the setup I ended up wanting after the usual one-agent-does-everything phase started to feel cramped.

One bot was still enough. What I did not want was one giant general-purpose agent carrying unrelated context, tools, and memory all the time. Telegram forum topics turned out to be a much cleaner boundary than adding more bots or trying to cram every specialty into one workspace.

One of the first specialized agents that felt genuinely useful was a personal trainer agent connected to Hevy, the workout tracking app. That was not accidental. If I am already logging training consistently, it makes sense to let the assistant see the same patterns, plateaus, and routine changes I am already tracking.

This page is the public version of that pattern.

It still leaves out private values and deployment-specific identifiers. The point is the architecture, not my exact bot handle, chat IDs, topic IDs, file paths, or secrets.

Why I Prefer Topics Over More Bots

The main advantage is that the interaction model stays simple.

I still talk to one Telegram bot. What changes is the topic where I send the message.

That gives me a few things at once:

  1. A general agent can stay general.
  2. A specialized agent can have its own workspace, skills, memory, and model.
  3. The routing stays visible and human-readable because it follows the topic structure in Telegram itself.

In practice, that feels much better than remembering which separate bot does what.

The Shape of the Setup

At a high level, the setup looks like this:

Telegram
├── Direct message with the bot        -> main agent
└── Private supergroup with topics
    ├── General topic                  -> main agent
    ├── Fitness topic                  -> personal-trainer agent
    └── Other future topics            -> future specialized agents

The important part is that the bot stays the same, while the topic becomes the routing key.

What You Need Before You Start

At minimum, you need three things in place:

  1. A Telegram bot already connected to OpenClaw.
  2. A private Telegram supergroup with topics enabled.
  3. An OpenClaw setup that already works with one default agent.

If the single-agent setup is still unstable, I would not add topic routing yet. This gets much easier once the boring parts already work. If you still need the broader deployment context, start with the OpenClaw Operations overview and the secure deployment guide.

Create the Telegram Supergroup

The Telegram side is simpler than it looks.

The flow is:

  1. Create a private supergroup.
  2. Enable forum topics for that group.
  3. Add the bot.
  4. Make the bot an admin so it can reliably see and answer messages in the topics you care about.
  5. Create at least one specialized topic, for example Fitness.

You do not need a separate bot per specialization. That is the whole point of the setup.

In practice, I would keep one default topic for general conversation and add topic-specific agents only when a domain has enough recurring context to justify isolation.

Get the Topic IDs Without Guessing

OpenClaw needs the Telegram group ID and the topic ID to route messages correctly.

You do not need to hardcode those from memory. The easiest path is to create the topic, send a message inside it, and inspect what the gateway logs or Telegram update payloads show you.

The important detail is this:

  1. The group gives you the chat boundary.
  2. The topic gives you the routing boundary inside that group.

Once you have those two values, the rest is just config.

What Stays Shared and What Gets Isolated

This is the part that makes the pattern worth using.

Shared across the setup
  • Telegram bot identity
  • Telegram supergroup
  • Same user-facing chat flow
Topic boundary

The topic becomes the routing key. The bot and chat surface stay shared, but each agent keeps its own operating context.

  • General topic main agent
  • Fitness topic personal-trainer agent
Isolated per agent
  • Workspace files
  • Skills
  • Memory
  • Default model
  • Domain-specific instructions

That isolation matters more than the routing itself. Without it, the specialized agent is just a costume on top of the same old context.

Add a Second Agent in OpenClaw

On the OpenClaw side, the first real change is moving from one default agent to an explicit agents.list.

The public version can stay minimal:

{
    "agents": {
        "list": [
            {
                "id": "main",
                "default": true,
                "workspace": "/path/to/workspace",
                "model": { "primary": "openai/gpt-5-nano" }
            },
            {
                "id": "personal-trainer",
                "workspace": "/path/to/workspace/personal-trainer",
                "model": { "primary": "openai/gpt-5-mini" }
            }
        ]
    }
}

That already tells the story.

The main agent stays the default for direct messages and the general topic. The personal-trainer agent gets its own workspace and can carry its own instructions, memory, and skills without dragging that context into everything else.

I would only use a separate agent when the boundary is real. If the second agent has the same tools, same memory, same model, and same instructions, then you do not really have a second agent. You just have more config.

Route One Topic to the Specialized Agent

Once the extra agent exists, the Telegram routing is straightforward.

The public version does not need real IDs. Placeholders are enough:

{
    "channels": {
        "telegram": {
            "groups": {
                "<GROUP_CHAT_ID>": {
                    "allowFrom": ["<YOUR_USER_ID>"],
                    "requireMention": false,
                    "topics": {
                        "<FITNESS_TOPIC_ID>": {
                            "agentId": "personal-trainer"
                        }
                    }
                }
            }
        }
    }
}

The useful behavior is:

  1. Direct messages still hit main.
  2. The general topic still hits main.
  3. The fitness topic routes to personal-trainer.

That gives you specialization without multiplying bots or changing the chat surface.

Give the Specialized Agent Its Own Workspace

The routing only becomes meaningful once the new agent has a real boundary around it.

The minimum shape I would document is:

workspace/personal-trainer/
├── SOUL.md
├── AGENTS.md
├── USER.md
├── TOOLS.md
├── memory/
└── skills/
        ├── hevy-cli/
        └── personal-trainer/

That is the part that keeps the trainer agent from feeling like the same assistant wearing a different label.

What goes there is more important than the file names themselves:

  1. SOUL.md sets the tone.
  2. AGENTS.md defines how the agent should work.
  3. USER.md gives it the relevant domain-specific context.
  4. TOOLS.md records the environment assumptions that only matter to that agent.
  5. skills/ limits what that agent can actually do.

Why the Personal Trainer Agent Is a Good Example

The personal trainer agent works well as a public example because it is easy to explain and it shows why topic-based routing is useful.

It is not just “an AI that talks about fitness.” It has a clear job:

  1. Look at training data.
  2. Notice patterns over time.
  3. Answer in the context of an ongoing routine instead of as a generic wellness chatbot.

That is where the Hevy integration fits.

Instead of making recommendations out of thin air, the agent can work from actual workout logs. That changes the tone of the interaction immediately. The useful version of a trainer agent is not motivational fluff. It is a data-aware assistant that can tell when volume is drifting, when progression stalls, or when a routine stops making sense.

The Hevy Part

For this kind of agent, Hevy is a better fit than a manual note file because the data already exists in a structured form.

The public version of the setup can keep this practical and simple:

  1. The trainer agent talks to a small Hevy integration layer.
  2. That integration returns structured workout data.
  3. The agent uses that data for analysis before making recommendations.

The important bit is not whether the integration uses a wrapper script, a plugin, or a small skill around a CLI.

The important bit is that the trainer agent should query workout data in a structured form before making recommendations.

For example, the internal contract can be as simple as:

  1. Ask the fitness skill for the latest training data.
  2. Return JSON.
  3. Analyze that JSON before suggesting changes.

That is enough to make the agent useful.

What I would avoid here is turning the article into a credential or deployment tutorial. The blog does not need exact secret names, exact host paths, or exact wrapper scripts unless those details are essential to the concept.

How I Would Wire Hevy Without Overcomplicating It

The clean version is:

  1. Keep the Hevy integration inside the trainer agent’s workspace.
  2. Make the integration return structured output.
  3. Keep write actions behind explicit approval.

That gives you a nice split:

  1. Read workout history automatically.
  2. Analyze trends freely.
  3. Keep routine creation or modification as a deliberate action.

That last part matters. Reading data is one thing. Writing training changes back into a system should stay more controlled.

What The Public Build Steps Actually Look Like

The private implementation plan has a lot more detail than a public doc needs. Stripped down to the useful part, the build is really this:

  1. Start with one default agent.
  2. Add a Telegram supergroup with topics enabled.
  3. Create a second named agent with its own workspace and skills.
  4. Route one topic to that agent and keep the general topic on the default agent.
  5. Give the specialized agent access to the domain data it actually needs.

That is enough to make the pattern reproducible without dumping a private runbook into the docs.

What I Would Keep Out of the Public Version

This part should stay explicit.

I would not publish:

  1. Bot handles.
  2. Group names.
  3. Chat IDs.
  4. Thread IDs.
  5. User IDs.
  6. Host filesystem paths.
  7. Secret names or API key wiring that reveals too much about the live setup.

Those belong in a private runbook, not in public docs.

Validation I Would Still Do

Even in a public write-up, I would still document the checks that matter:

  1. Direct messages still land on the default agent.
  2. The general topic still lands on the default agent.
  3. The fitness topic lands on the trainer agent.
  4. The trainer agent can read workout data.
  5. The general agent cannot see trainer-only skills or memory.

That last check is the one I care about most. If the isolation is fake, the routing is just decoration.

Why I Am Keeping This as One Doc

I think this works better as one doc for now.

The interesting part only really lands when the three ideas stay together:

  1. Telegram topics as routing.
  2. Multi-agent isolation as the real boundary.
  3. A trainer agent with real workout data as the concrete example.

If I split it too early, it risks turning into three smaller posts that are individually correct but less useful.

Good Follow-Up Angle

The strongest follow-up is probably not “how to configure Telegram.”

It is “what changed once the trainer agent had real workout data instead of generic context.”

That is where the architecture stops being a config trick and starts feeling like a real assistant.

References