· 5 min read ·

What Economics Got Right About AI Agent Delegation

Source: simonwillison

Simon Willison noted a new capability in Codex: support for subagents and custom agents built on the OpenAI Agents SDK. The technical mechanics are well-documented at this point. The as_tool() method wraps an Agent into something an orchestrating model can call like a function. Each subagent runs in a fresh context window, isolated from the parent’s accumulated state. Specialization, parallelism, context hygiene: the standard case is clear.

What the standard case glosses over is the older problem this pattern imports from economics.

When you split a coding task between an orchestrating agent and one or more subagents, you create a principal-agent relationship. The orchestrator (principal) wants a result. The subagent (agent) does the work. The principal cannot observe the agent’s full process; it sees only what the agent reports back.

This is the textbook principal-agent problem, studied since at least the 1970s in the context of insurance, employment, and financial delegation. It has known failure modes and known design responses. Both transfer directly to multi-agent coding systems.

Why the Observation Gap Matters

In a single-agent Codex session, everything is visible. The model’s reasoning, tool calls, and decisions live in one context window. When something goes wrong, the failure mode is usually traceable through the transcript.

Subagents break that property. When the orchestrator calls a subagent via as_tool(), the child’s internal reasoning, intermediate tool calls, and decision-making are all hidden from the parent. The parent receives the subagent’s final output and builds on it for subsequent steps.

If the subagent did something unexpected, the orchestrator doesn’t know. It receives what looks like a completed result and proceeds. The error propagates silently until something downstream breaks in a way the orchestrator can detect, which may be several steps later.

from agents import Agent, Runner

implementation_agent = Agent(
    name="implementer",
    instructions="Implement the requested feature in the provided codebase.",
    tools=[read_file, write_file, run_tests]
)

orchestrator = Agent(
    name="orchestrator",
    tools=[
        implementation_agent.as_tool(
            tool_name="implement_feature",
            tool_description="Implement a specified feature. Provide task description and relevant file paths."
        )
    ]
)

The orchestrator calls implement_feature, waits, and receives a text response. Whether the implementation agent ran tests, skipped them, partially completed the work, or encountered errors it quietly swallowed, none of that is in the tool response unless the agent is designed to put it there.

The Three Responses Economics Identified

Principal-agent theory has studied this observation gap extensively, and the responses that make delegation reliable fall into three categories: contracting (define the output standard explicitly), monitoring (observe what you can), and incentive alignment (design the situation so the agent’s interests match the principal’s).

All three have direct analogues in subagent design.

Contracting means defining output structure. Instead of expecting a natural language summary that the orchestrator has to interpret, require a structured completion manifest:

# Weak: free-form claim
"I implemented the login endpoint and wrote tests. The feature is complete."

# Strong: structured manifest
{
  "status": "complete",      # or "partial" or "failed"
  "files_modified": ["src/auth.py", "tests/test_auth.py"],
  "tests_run": true,
  "test_result": "23 passed, 0 failed",
  "external_actions": [],
  "caveats": []
}

The structured form lets the orchestrator detect partial completion and decide how to proceed. “23 passed, 0 failed” is a fact the orchestrator can verify against expectations or use to inform the next step. “The feature is complete” is a claim the orchestrator has to take on trust. The output contract is the mechanism by which the subagent is required to demonstrate rather than assert.

Monitoring means using the tracing infrastructure. The Agents SDK tracing captures the full execution tree, including each subagent’s input, output, and turn-level reasoning:

from agents import Runner, trace

with trace(workflow_name="auth-feature") as t:
    result = Runner.run_sync(orchestrator, "Add login rate limiting")

for span in t.spans:
    print(f"{span.agent_name}: input={span.input[:100]}...")
    for child in span.children:
        print(f"  {child.agent_name}: output={child.output[:100]}...")

The dashboard renders this as a navigable tree. Unlike the flat conversation log of single-agent sessions, debugging a multi-agent run means following branches, each representing a subagent’s lifecycle. The mental model is closer to distributed system tracing with tools like Jaeger or OpenTelemetry than to reading a chat transcript. Span IDs, parent-child relationships, and latency per branch are the relevant dimensions.

Tracing is opt-in. If you don’t instrument, you get flat output logs with no visibility into the delegation structure. For any workflow complex enough to benefit from subagents, the trace context is worth enabling from the start.

Incentive alignment means writing instructions that specify what to prioritize, not just what to do. An implementation agent told only to “implement the feature” will optimize for completing the task with minimum friction. An agent told to “implement the feature, write tests covering error conditions, and explicitly report any edge cases you could not cover” has a more aligned objective. The subagent’s instructions function as the incentive structure; they define what the agent is trying to maximize within its context window.

This is why the instructions you write for a custom agent and the tool description you give the orchestrator for calling that agent are two distinct design artifacts. The instructions define what the agent tries to do. The description defines when the orchestrator calls it and what it expects back. Both need to be explicit.

How Custom Agents in Codex Make This Design Explicit

The Codex custom agent feature is useful precisely because it forces you to make these design decisions explicitly. You can’t add a custom agent without writing its instructions and its tool description. The platform requires the interface definition.

Contrast this with ad-hoc subagent calls using raw API calls: it is easy to spawn a new client.messages.create() call without thinking carefully about what output structure you expect or how you will detect partial completion. The SDK’s Agent abstraction at least surfaces the relevant fields.

Some teams working with multi-agent systems have started treating tool descriptions with the same rigor they bring to OpenAPI specifications: enumerating input types, documenting output structure, and writing explicit triggering conditions. The practice reflects the same underlying insight: you are defining an interface that a caller will use without the ability to ask clarifying questions.

Where the Pattern Pays Off

The economics of multi-agent decomposition are real. More agents means more delegation overhead, more potential for silent failure, and more design work upfront. The error compounding math is not favorable: if each agent step has a 90% success rate, a five-step pipeline succeeds end-to-end roughly 59% of the time. Subagents should be applied only to tasks large enough to genuinely benefit from clean context isolation or parallel execution.

Where the pattern earns its cost is on tasks that exceed a single context window’s useful capacity, or that decompose into genuinely independent subtasks benefiting from different model configurations. A security review agent running a slower reasoning model at temperature zero alongside a documentation agent running faster at higher temperature is a reasonable use of the pattern. Breaking a three-line bug fix into an orchestrator and a subagent is not.

The Agents SDK documentation covers the mechanics well. The design discipline that makes multi-agent coding systems reliable is older than the SDK. Structured output contracts, observable execution trees, and instructions that align behavior to objectives are the same problems that make any delegated work trustworthy, and the solutions are recognizable from domains that have been thinking about this longer than the AI tooling ecosystem has.

Was this interesting?