Skip to content

Agent as Tool

Wrap a specialised agent as a tool that another agent can call. This requires no orchestrator class — just agent.as_tool() passed to the parent agent's tools list.

Agent delegation

  1. Wrap — Call specialist.as_tool() to create an AgentTool.
  2. Register — Pass the tool to the orchestrator agent's tools parameter.
  3. Delegate — The orchestrator's model decides when to invoke the sub-agent.
  4. Execute — The sub-agent runs its own agent loop (with its own tools and hooks) and returns a result.
  5. Continue — The orchestrator incorporates the result and continues until done.

Usage

python
from elsai import Agent
from elsai.agent import AgentConfig

researcher = Agent(
    system_prompt="You are a research specialist. Find accurate, up-to-date information.",
    config=AgentConfig(
        name="researcher",
        description="Searches the web and returns summarised findings on any topic.",
    ),
)

orchestrator = Agent(
    system_prompt="You are a helpful assistant. Use the researcher tool when you need information.",
    config=AgentConfig(name="orchestrator"),
    tools=[researcher.as_tool()],
)

result = orchestrator("What are the key differences between React and Vue?")
print(result)

Preserve context — keep conversation history across sub-agent calls:

python
memory_agent = Agent(
    config=AgentConfig(
        name="memory",
        description="Stores and retrieves information from our working memory.",
    ),
)

orchestrator = Agent(
    tools=[memory_agent.as_tool(preserve_context=True)],
)

Nested agents:

python
from elsai.agent import AgentConfig

data_agent = Agent(config=AgentConfig(name="data", description="Retrieves raw data from databases"))
analyst = Agent(
    config=AgentConfig(name="analyst", description="Analyses data and produces insights"),
    tools=[data_agent.as_tool()],
)
orchestrator = Agent(
    config=AgentConfig(name="orchestrator"),
    tools=[analyst.as_tool()],
)

result = orchestrator("Analyse our top customers and explain the key trends")

Sub-agents with their own tools:

python
from elsai import Agent, tool
from elsai.agent import AgentConfig

@tool
def evaluate_expression(expression: str) -> str:
    """Evaluate a mathematical expression."""
    return str(eval(expression))

coder = Agent(
    config=AgentConfig(
        name="coder",
        description="Writes and executes Python code to solve problems",
    ),
    tools=[evaluate_expression],
)

manager = Agent(
    config=AgentConfig(name="manager"),
    tools=[coder.as_tool()],
)

result = manager("Calculate the compound interest on $10,000 at 5% for 10 years")

Configuration

ParameterDefaultDescription
nameagent.nameTool name the model sees
descriptionagent.descriptionHow the model decides when to call this tool
preserve_contextFalseTrue = keep history across calls; False = fresh start each time
mode"reject"Concurrency when the same tool is invoked in parallel — see below
python
from elsai.agent import AgentAsToolMode

researcher.as_tool(
    name="research",
    description="Searches and summarises information on any topic.",
    preserve_context=False,
    mode=AgentAsToolMode.SPAWN,  # or "spawn", "queue", "reject"
)

Concurrent invocation (mode)

When a parent agent uses the default ConcurrentToolExecutor, the model may issue two tool calls to the same sub-agent in one turn. Each sub-agent is wrapped with a shared runtime and a lock. The mode parameter controls what happens on the second concurrent call.

Registry default

Passing tools=[researcher] auto-wraps the agent with as_tool() using mode="reject". To use queue or spawn, wrap explicitly: tools=[researcher.as_tool(mode="spawn")].

Modes

ModeRuntimeSecond concurrent call
reject (default)Shared template agentError: Agent '…' is already processing a request
queueShared template agentWaits until the first call finishes
spawnNew isolated worker per callRuns in parallel

Different specialists (tools=[researcher, writer]) run in parallel without any mode configuration.

When to use which

python
from elsai import Agent
from elsai.agent import AgentConfig

researcher = Agent(config=AgentConfig(name="researcher", description="Research specialist"))
writer = Agent(config=AgentConfig(name="writer", description="Writing specialist"))

# Different sub-agents — default mode is fine
manager = Agent(tools=[researcher, writer])

# Same specialist called twice in one turn — parallel
manager = Agent(tools=[researcher.as_tool(mode="spawn")])

# Same specialist — OK to wait on the shared agent
manager = Agent(tools=[researcher.as_tool(mode="queue")])

# Stateful shared specialist (preserve history, serialize access)
researcher.as_tool(preserve_context=True, mode="queue")

Shared modes (reject, queue)

  • Use the template agent referenced by the tool wrapper.
  • With preserve_context=False, messages and state reset to the snapshot taken at wrap time before each call.
  • queue acquires a lock so the second call blocks until the first completes.

Spawn mode

Each invocation creates an isolated Agent worker:

  • Worker agent_id is {template_agent_id}:spawn:{tool_use_id} for distinct traces.
  • Template event_loop_metrics are not updated — workers run in isolation.
  • MCP clients are shared with reference-counted lifecycle; worker.cleanup() runs after each call.
  • On interrupt, the worker is retained until resume completes.

Built-in conversation managers (SlidingWindowConversationManager, SummarizingConversationManager, NullConversationManager) and spawn-ready plugins clone onto each worker. See Plugins — Spawn compatibility and Conversation Management — Spawn cloning.

Memory pipelines

as_tool(mode="spawn") is not compatible with elsai memory integration conversation managers (ElsaiConversationManager, CompositeConversationManager) today. Use mode="queue", or implement clone_for_spawn() on a custom conversation manager and supports_spawn() / clone_for_spawn() on any custom plugins.

Construction validation

ConfigResult
mode="reject" or mode="queue"OK
mode="spawn" + preserve_context=TrueValueError
mode="spawn" + template has session_managerValueError
mode="spawn" + non-spawnable pluginValueError (names the plugin)
mode="spawn" + non-cloneable conversation managerValueError
Invalid mode stringValueError

Import the enum from elsai.agent:

python
from elsai.agent import AgentAsToolMode

Relation to ConcurrentInvocationMode

AgentConfig.concurrent_invocation_mode controls direct agent() / agent.stream_async() calls on a single agent. AgentAsToolMode is a separate layer for the tool wrapper when a parent agent invokes a sub-agent as a tool.

Full agent loop

A sub-agent invoked via as_tool() runs a complete agent loop — its own tools, hooks, plugins, and session manager all apply independently of the orchestrator.

Copyright © 2026 elsai foundry.