Skip to content

Agent handoff/transfer between agents in an agent tree #95

@weeco

Description

@weeco

Problem Statement

FinishReasonTransfer exists in agent/event.go as a declared constant but is a placeholder with no implementation. There is no mechanism for one agent to hand off a conversation to another agent while preserving the session context.

The current workaround is agent-as-tool (agenttool), but this creates a fresh, isolated session for the child agent. The child cannot see the parent's conversation history, and the parent only gets back a text summary. This is delegation, not handoff — the conversation doesn't transfer.

Real agent systems need true handoffs where:

  • Agent A decides Agent B is better suited for the current conversation
  • The conversation (session) transfers to Agent B with full history
  • Agent B continues the same session, not a new one
  • The user experiences a seamless transition

Proposed Solution

Agent tree structure

Agents form a tree via a SubAgents relationship. An agent can transfer to:

  • Sub-agents (children): always allowed
  • Parent: transfer back up (configurable, opt-in)
  • Peers (siblings): transfer to a sibling agent (configurable, opt-in)
agent, _ := llmagent.New("coordinator", systemPrompt, model,
    llmagent.WithSubAgents(billingAgent, techAgent, accountAgent),
    llmagent.WithTransferPolicy(agent.TransferPolicy{
        AllowTransferToParent: true,
        AllowTransferToPeers:  false,
    }),
)

Transfer tool

A built-in transfer_to_agent tool that the LLM can call to initiate a handoff:

// LLM sees this tool in its available tools:
// transfer_to_agent(agent_name: string, reason: string)
//
// When called:
// 1. Validates the target agent exists in the tree
// 2. Returns FinishReasonTransfer
// 3. The runner/orchestrator finds the target agent
// 4. Runs the target agent with the SAME session (full conversation history)

The transfer tool is auto-injected when an agent has sub-agents. The tool description includes the names and descriptions of available transfer targets so the LLM knows where it can route.

Same-session handoff

The critical difference from agent-as-tool: the target agent receives the same session reference, not a copy. It sees the full conversation history and continues appending to it. The user's experience is seamless — they don't know (or care) that a different agent took over.

Agent discovery

FindAgent(name) traverses the agent tree to locate transfer targets:

type Agent interface {
    // ...existing methods...
    SubAgents() []Agent
    FindAgent(name string) Agent
}

Use Case Example

Customer support routing:

coordinator := llmagent.New("support-coordinator",
    "You are a customer support coordinator. Route conversations to the appropriate specialist.",
    model,
    llmagent.WithSubAgents(
        billingAgent,   // Handles billing questions, refunds, invoices
        techAgent,      // Handles technical issues, debugging, configuration
        accountAgent,   // Handles account management, permissions, settings
    ),
)

// User: "I'm being charged twice for my subscription"
// Coordinator LLM decides this is billing → calls transfer_to_agent("billing")
// Billing agent takes over the SAME session, sees full conversation history
// Billing agent: "I can see your account. Let me look into the duplicate charges..."

Multi-level handoff:

// techAgent itself has sub-agents:
techAgent := llmagent.New("tech-support", ...,
    llmagent.WithSubAgents(networkAgent, databaseAgent, authAgent),
)

// coordinator → techAgent → databaseAgent (nested handoff)

Why This Matters

  • Natural conversation flow: Users expect to explain their problem once. With agent-as-tool delegation, context is lost and users must repeat themselves. Handoff preserves the full conversation.
  • Specialization at scale: As agent systems grow, no single agent can handle all tasks. Handoff enables routing to specialists without the overhead of re-summarizing context.
  • Existing infrastructure: FinishReasonTransfer already exists, the event system supports it, and the A2A adapter has no special handling needed. The main work is the transfer tool and agent tree traversal.
  • Composability with orchestration: Handoffs compose naturally with workflow patterns — a router agent is essentially a classifier that uses handoffs instead of delegation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    agentAgent frameworkenhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions