Skip to main content
Clone agents to give each channel, tenant, or session its own isolated instance — without losing config or hitting RLock pickling errors.
The user deploys one base agent to several channels; each clone keeps the same instructions with isolated session state.
Fix reference: PraisonAI PR #3769clone_for_channel() now isolates memory per clone so one channel’s history never leaks into another.

Quick Start

1

Single clone

2

Multiple clones for multi-channel deployment


How It Works

Agent cloning creates a fresh instance with the same configuration but isolated state.

What gets cloned vs. what’s reset

Per-clone memory isolation (fixed in PraisonAI #3769): clone_for_channel() now guarantees each clone gets its own memory store when memory is configured via MemoryConfig/db()/dict. If you passed a live Memory/FileMemory instance instead, the clone rebuilds a fresh backend from its stored config; if that isn’t possible, the clones share the backend and a WARNING is logged: "clone_for_channel: could not isolate the live memory backend; clones will share it. Pass memory via MemoryConfig/db() for per-channel isolation." For guaranteed per-channel/per-user isolation, prefer MemoryConfig or db("sqlite:./channel.db") over a shared live backend.
Per-clone sandbox isolation (fixed in PraisonAI 826c2e6 + 9d6f6dc): clone_for_channel() now reads sandbox_config correctly (previously it read _sandbox_config, which is never assigned, so every clone silently ran unisolated). Framework-generated execute_python_code / execute_shell_command tools are tagged with _praison_sandbox_tool = True and stripped from the clone before regeneration, so each clone gets its own SandboxManager — no shared isolation state across channels. User-defined tools with those same names lack the tag and are preserved unchanged.
Per-clone as_tool() rebinding (PraisonAI PR #4146): clone_for_channel() now swaps every as_tool()-derived callable back for its source Handoff before regeneration, so each clone rebinds it to itself. Without this, the closure would capture the source agent’s chat_history/tools/memory and every channel would silently delegate through the source’s state. The rebinding is tracked via a _praison_handoff_source attribute on the generated closure; user-defined tools with the same name lack the attribute and are preserved unchanged.

Memory isolation

Per-channel clones need per-channel memory — otherwise one user’s chat history and long-term memory leaks into another. clone_for_channel() handles this automatically. Recommended (fully isolated): pass memory as a config, not a live instance. Each clone re-resolves the config into its own fresh backend.
Live-instance memory works too — the clone rebuilds a fresh backend of the same type per clone (via deepcopy, or type(mem)(mem.cfg) as a fallback). If isolation is genuinely impossible (a custom backend without a cfg attribute), the clone logs a warning and falls back to sharing so clone_for_channel() never hard-fails.
If you see the log line clone_for_channel: could not isolate the live memory backend; clones will share it, switch to a MemoryConfig / db() / dict config to guarantee isolation.

Common Patterns

Per-channel clone in a custom gateway

Per-tenant clone in a multi-tenant API

Use with copy.deepcopy


Best Practices

clone_for_channel() is the supported path for creating channel-safe clones. It’s optimized for multi-channel scenarios and properly handles handoffs. __deepcopy__ is provided for general Python compatibility but isn’t specifically designed for channel isolation.
Clone drops handoffs by design. Each channel should have independent routing logic. If you need cross-channel handoffs, implement them at the gateway level rather than the agent level.
If a tool holds mutable per-channel state, wrap it in a factory function or use instance-based tools. The clone shares tool instances with the original agent, which is usually fine for stateless tools.
A MemoryConfig or db("…") is a description of a backend — each clone builds its own from it. A live Memory(...) instance is a single store passed by reference; even after #3769 we fall back to sharing it if we can’t rebuild. Users who want a guaranteed independent store per Telegram/Discord/tenant clone should pass config, not an instance.

Bot Gateway

Multi-channel gateway using agent cloning

Thread Safety

Agent thread safety and concurrency