012: Agent/Delegate
- Requires:
- Complemented by:
The Delegation pattern addresses the critical challenge of scaling and composing agent capabilities. It provides a powerful mechanism for executing Tools in sandboxed contexts, preventing context bleeding and enabling true reusability. By delegating a Call to an external delegate—either another Request definition or an Activity in a sub-request—the system can build complex agentic behaviors from self-contained, independently developed components.
The Problem: Monolithic Tools and Context Bleeding
As agent capabilities grow, defining all Tools within a single, monolithic context becomes untenable.
- Large Schemas: LLMs have practical limits on the complexity of schemas they can handle in a single request. Combining numerous complex Tools can exceed these limits, preventing the LLM from correctly processing the available options.
- Context Bleeding: When all Tools operate in the same context, the LLM can be influenced by irrelevant information, leading to incorrect Tool selection or parameter filling.
- Lack of Reusability: A Tool defined for one agent is not easily portable to another without bringing its entire context with it.
Delegation solves these problems by introducing Delegated Isolation, a way to delegate a Call to an external, isolated execution environment.
Invoking a Delegate
Delegated execution is signaled by the _delegate
property within a Tool's schema. This property instructs the system to treat the Call not as an inline operation, but as a request to an external delegate.
The _delegate
property is a string
and can be used in two ways:
-
A saved, reusable Request is the most common form of an Idea. The Delegation pattern is the primary mechanism for composing these Ideas into more complex systems. See 101: Concept/Idea for details.
Reference a saved Request: The string can be a path or URL to a JSON file that defines a self-contained Request—a JSON object containing
context
andschema
properties. This allows a Tool to delegate its execution to a completely separate set of instructions. -
Create an anonymous delegate: A string literal
'anonymous'
signals an anonymous delegate. This is used to create a fresh, isolated execution environment for a Tool Call (whether latent or explicit) without needing an intermediate JSON file. It automatically scopes a new, empty context for the Call.
Execution in an Isolated Environment
A delegate provides a "clean room" for execution. Instead of running inside the parent agent's bustling context, the Call is processed in a new, self-contained session. The context for this sub-request is carefully constructed, not inherited.
This is where the Scoped context becomes critical. The _scopes
property on the Tool schema acts as a bridge, explicitly declaring which pieces of the parent context should be "imported" into the delegate's dedicated workspace. This gives the parent agent precise control over what the delegate can see, preventing context bleeding and enabling true encapsulation.
Handling Large Schemas
Delegation also provides a solution for managing Tools with very large or complex output schemas. Instead of including a massive _output
schema in the main request—potentially crowding out other tools—a Tool can be defined with only its input
parameters and a _delegate
pointer.
The LLM can plan the Call with just the input, and the complex output will be generated within the delegate's isolated sub-request. This allows an agent to reason about a sequence of complex operations without needing to "see" the entire, detailed schema for every step in a single context window. The LLM trusts that the delegate will produce the correct output, which it will receive and use in subsequent steps.
Delegate Resolution Strategies
A Tool becomes a Delegate simply by including the _delegate
property in its schema. This signals that the Call should be delegated. The key question is when this delegation is resolved. The system supports two strategies, allowing a trade-off between strict safety and dynamic flexibility.
1. Execution-Time Resolution (Default)
The default and most flexible approach is to resolve the delegate at execution time, after an agent has already generated a Call.
This method enables a powerful paradigm that is not possible with traditional code: the LLM acts as an intelligent glue layer. An agent can generate a Call with parameters that don't perfectly match the delegate's expected input
schema. At execution time, the system assembles the delegate's context and the caller's provided input, and the LLM in the sub-request is tasked with bridging the gap.
This is a significant advantage, as it allows delegates to be updated and evolve independently. Even if a delegate changes its input structure, calling agents won't immediately break. The LLM will attempt to adapt the old Call format to the new input
schema, providing a level of resilience and loose coupling that is unique to this architecture.
The process is as follows:
- An agent generates a Call to the modular Tool.
- The executor sees the
_delegate
property and initiates the delegation process. - Context Assembly: The executor fetches the delegate definition file (if not anonymous) and assembles the base context. It then uses
_scopes
to append the caller's context. - Input Mapping: The
params
from the Call are packaged into an Input Message and added to the context. This is where the LLM's "glue" capability comes into play, as it will use this input to fulfill the delegate's logic, even if the schemas don't align perfectly. - Execution: A new, isolated Request is made with the combined context. The result is returned as the output of the original Call.
2. Upfront Resolution (Optional)
For scenarios requiring stricter guarantees, a delegate can be resolved upfront, before the initial Request is sent to the agent.
In this mode, the system pre-fetches the delegate Request definition and merges its input
schema with the Tool's parameter schema. This allows the agent's LLM to see the delegate's exact requirements from the start, ensuring that the generated Call is perfectly formed and type-safe. Crucially, this upfront merge can also include the delegate's _output
schema, providing a strict contract for the expected result.
This approach provides the safety of traditional API contracts, where both the inputs and outputs are known and validated. It sacrifices the flexibility of execution-time resolution and is best used for critical, well-defined integrations where loose coupling is not a desired feature.
Example: Flexible Input Mapping at Execution Time
This example demonstrates the "LLM as glue" concept, where a delegate can be successfully executed even if the calling agent's Call doesn't perfectly match the delegate's input schema. This is the default, execution-time resolution behavior.
Caller's Perspective
An orchestrator agent needs to send a message. It knows about a sendMessage
Tool that delegates to an external agent, identified by a URL. Based on its own context, it generates a Call with userId
and text
parameters, without knowing the delegate's internal requirements.
// CALL GENERATED BY THE ORCHESTRATOR
{
"_tool": "sendMessage",
"_delegate": "http://example.com/agents/speaker_EN",
"userId": "u_123",
"text": "Hello, world!"
}
Delegate's Perspective & Final Context
The speaker_EN
delegate is a separate Request definition. At execution time, the system packages the caller's parameters into the input
property of an Input Message. Critically, it also includes the delegate's own schema
, which does not match the provided input
. The delegate's LLM is now responsible for bridging this semantic gap, intelligently mapping userId
to recipientId
and text
to messageBody
. This transformation isn't programmatic; it's a semantic mapping that occurs within the LLM's latent space.
// FINAL CONTEXT FOR THE SUB-REQUEST
[
{
"type": "system",
"message": "You are an expert in messaging in English."
},
{
"type": "input",
// This is the raw input provided by the calling agent.
"input": {
"userId": "u_123",
"text": "Hello, world!"
},
// This is the expected input format for the delegate
"schema": {
"type": "object",
"properties": {
"recipientId": { "type": "string" },
"messageBody": { "type": "string" }
}
}
}
]
Example: Music makers
Delegates enable powerful composition by allowing Request definitions to act as standalone services that can be orchestrated by other agents. This creates a clear, dynamic hierarchy: high-level agents can focus on orchestration while delegating specialized tasks to low-level, reusable delegates.
Consider a workflow with two specialist delegates: a Composer
and a Sound-Designer
.
-
The
Sound-Designer
is a low-level expert. It's a self-contained Request definition focused on the physics of sound, knowing how to operate synthesizers to produce specific audio data. -
The
Composer
is a mid-level specialist. Its primary job is to create a song. It uses its own inline tools to generate a melody and musical structure. To realize this vision, it then makes Calls to theSound-Designer
delegate to synthesize the actual sounds.
This two-layer hierarchy is a common pattern. However, the true power of delegates lies in their dynamic, task-driven composition.
This arrangement allows for flexible orchestration. A high-level Producer
can delegate to a Composer
, who in turn uses a Sound-Designer
. However, the Producer
can also bypass the Composer
and interact with the Sound-Designer
directly for specific tasks.
Now, let's introduce a high-level Producer
agent. The Producer
's goal is to create a finished record. Based on the specific task, the Producer
can orchestrate its delegates in different ways:
-
Hierarchical Orchestration: For creating a song, the
Producer
might make a single Call to theComposer
delegate. TheProducer
provides high-level direction ("I need a sad ballad"), and theComposer
executes its entire internal workflow, including its own nested Calls to theSound-Designer
. TheProducer
doesn't need to know about theSound-Designer
's existence in this case. -
Parallel Orchestration: If the
Producer
also needs specific sound effects for the record (like foley or an ambient soundscape), it can make Calls directly to theSound-Designer
delegate for those tasks, in parallel with its Call to theComposer
.
This demonstrates the key principle: the composition is not fixed within the tools themselves. The Producer
can choose to treat the Composer
as a black box or to interact with its constituent parts (Sound-Designer
) directly, all depending on the needs of the moment. This flexibility allows the same set of expert delegates to be combined in various arrangements, creating a deeply composable and emergent system.
From Delegation to Scopes
Delegated execution provides an isolated environment, but for it to be useful, a delegate needs a way to receive information from its parent. The _scopes
property provides this mechanism, acting as a controlled bridge between contexts. The specifics of how this bridge works are defined by the Scoped context pattern.