Skip to content

Agent Interface

Integrating external tools (APIs), multi-agent collaboration, and execution control

From API Call to Agent

An agent is not a separate product category — it is what you get after three additions to a plain API call. Each step widens what the model is allowed to do.

    flowchart LR
    A["1. API Call<br/>text in, text out"] --> B["2. Structured Output<br/>parseable JSON"]
    B --> C["3. Function Calling<br/>model requests an action"]
    C --> D["4. Agent<br/>plans and loops"]

    style A fill:#EFF6FF,stroke:#2563EB,color:#1E40AF
    style B fill:#2563EB,stroke:#1D4ED8,color:#fff
    style C fill:#EA580C,stroke:#C2410C,color:#fff
    style D fill:#7C3AED,stroke:#6D28D9,color:#fff
  

Structured Outputs

Prose is fine for a chat window and useless to the calling code. Pin the response to a schema so downstream code can consume it without parsing free text:

# Declare the shape you expect, not the wording
schema = {
    "type": "object",
    "properties": {
        "sentiment": {"type": "string", "enum": ["positive", "neutral", "negative"]},
        "priority": {"type": "integer", "minimum": 1, "maximum": 5},
        "summary":  {"type": "string", "maxLength": 200},
    },
    "required": ["sentiment", "priority", "summary"],
}
  • Constrain enums rather than asking for a word — an enum removes the whole class of “Positive!” vs "positive" mismatches
  • Validate before you trust — even schema-constrained output should be validated at the boundary, the same as any external input
  • Keep schemas flat — deeply nested objects raise the error rate with no gain in usefulness

Function Calling

Function calling inverts the flow: instead of the model answering, it tells you which action to run and with what arguments. Your code executes it and returns the result, and the model continues from there.

User: "How many days of leave do I have left?"
  ↓
Model: → calls get_leave_balance(employee_id="E1042")
  ↓
Your code: executes the function, returns { "remaining": 7.5 }
  ↓
Model: "You have 7.5 days of leave remaining."

The model never runs anything itself — it only requests. That boundary is where authorization, rate limiting, and audit logging belong.

Types of Agent Architectures

    flowchart TD
    A["Agent Type"] --> B["Single Agent<br/>Single Agent"]
    A --> C["Multi-Agent<br/>Multi-Agent"]
    A --> D["Hierarchical Agent<br/>Hierarchical"]

    B --> E["Simple Tool Use<br/>Tool Calling"]
    C --> F["Parallel Execution<br/>Parallel"]
    C --> G["Sequential Execution<br/>Sequential"]
    D --> H["Orchestrator<br/>+ Sub-agents"]

    style A fill:#7C3AED,stroke:#6D28D9,color:#fff
    style B fill:#2563EB,stroke:#1D4ED8,color:#fff
    style C fill:#EA580C,stroke:#C2410C,color:#fff
    style D fill:#16A34A,stroke:#15803D,color:#fff
  

Harness Patterns

The architectures above describe how many agents there are. The harness is the control flow wrapped around them — the loop, the branching, the point where work is split or checked. Nine patterns cover most production systems, and they compose rather than compete.

Single-agent loops — one model, one thread of control:

PatternThe loop it runsReach for it when
ReActReason → act → observe, repeated until the goal is met — see the ReAct prompt patternThe default. Start here for anything involving tools
ReflexionThe agent critiques its own output, records what went wrong, and carries that note into the next attemptFailures are recoverable and a second try with hindsight would succeed

Decomposition — the work is split before it is executed:

PatternHow work is dividedReach for it when
Prompt ChainingA fixed sequence of steps, each step’s output feeding the nextThe steps are known in advance and always run in the same order
RoutingA classifier sends each request to the specialist model or path that fits itRequest types differ enough that one prompt serves none of them well
ParallelizationSectioning runs independent subtasks at once; voting runs the same task several times and takes the consensusLatency is the constraint (sectioning), or a single sample is not reliable enough (voting)

Coordination — control flow spans multiple agents:

PatternWho decides what happens nextReach for it when
Orchestrator–WorkersA lead agent splits the task at runtime, dispatches specialist workers, and merges results — see the pattern belowThe subtasks are not knowable until the request is read
Evaluator–OptimizerOne agent drafts, a second scores it against explicit criteria, and the loop repeats until it passesQuality is judgeable against a rubric and the first draft usually is not good enough
Graph OrchestrationA declared graph of nodes and edges, with conditionals and loops made explicit — the model of LangGraphThe flow is complex enough that you need to see and debug the path it took
SwarmNobody — peer agents coordinate through shared state, with no central controllerExploratory work such as brainstorming. Hardest to bound: without a controller, runaway loops and cost have no natural stop

Choosing and Combining Them

    flowchart LR
    A["Single ReAct agent"] -->|"Requests differ in kind"| B["+ Routing"]
    A -->|"Too slow, or one sample is unreliable"| C["+ Parallelization"]
    A -->|"Output quality is inconsistent"| D["+ Evaluator–Optimizer"]
    B --> E["Orchestrator–Workers<br/>or Graph Orchestration"]
    C --> E
    D --> E

    style A fill:#2563EB,stroke:#1D4ED8,color:#fff
    style B fill:#EFF6FF,stroke:#2563EB,color:#1E40AF
    style C fill:#EFF6FF,stroke:#2563EB,color:#1E40AF
    style D fill:#EFF6FF,stroke:#2563EB,color:#1E40AF
    style E fill:#7C3AED,stroke:#6D28D9,color:#fff
  

Two rules carry most of the value here. Start with the simplest harness that could work — usually a single ReAct agent — and add a pattern only when a specific bottleneck forces it; a multi-agent system built before the bottleneck exists is a debugging problem you chose voluntarily. And expect to combine them: a production system routes incoming requests, parallelizes the retrieval behind them, and runs an evaluator over the draft, which is three patterns in one request path.

Tool Calling Design Principles

Good Tool Design

{
  "name": "search_knowledge_base",
  "description": "Searches the company's internal knowledge base for relevant documents. Use it to find policies, procedures, technical documentation, and similar material.",
  "parameters": {
    "query": "The natural-language question to search for",
    "top_k": "Number of documents to return (default: 5)",
    "category": "Search category (optional): hr, technical, policy"
  }
}

Core Principles of Tool Specification

  • Clear naming: use a verb_noun format (search_document, send_email)
  • Detailed description: clearly state when the AI should use this tool
  • Minimal parameters: only required parameters should be mandatory; make the rest optional

Multi-Agent Patterns

Orchestrator–Sub-agent Pattern

Orchestrator Agent
├── Research Agent (web search, document search)
├── Analysis Agent (data processing, computation)
├── Coding Agent (writing and executing code)
└── Summary Agent (writing the final report)

Comparing Major Frameworks

FrameworkCharacteristicsBest suited for
LangGraphState-graph-based, complex flow controlComplex workflows
AutoGenConversational multi-agentResearch, collaborative tasks
CrewAIRole-based agent teamsBusiness process automation
Claude Code SDKOfficial Anthropic offering, coding-focusedDevelopment automation
OpenAI Agents SDKLightweight primitives — agents, handoffs, guardrails, sessionsStaying close to the OpenAI stack

Error Handling Strategy

# Exceptions that must always be handled during agent execution
try:
    result = agent.run(task)
except ToolExecutionError as e:
    # Tool execution failed → retry with alternative tools
    result = agent.run(task, fallback_tools=True)
except MaxIterationsError:
    # Prevent infinite loops
    result = "Maximum iteration count exceeded. Please break the task into smaller pieces."
except ContextLengthError:
    # Context exceeded → summarize and retry
    result = agent.run_with_compression(task)

Failure Recovery

The exception blocks above handle a failure once. What decides whether an agent survives production is what happens on the second, third, and hundredth failure:

PatternWhat it doesReach for it when
Retry with backoffRe-issues the call after an increasing delay, with jitter to avoid synchronized retriesThe failure is transient — a timeout, a 429, a dropped stream
Fallback chainRoutes to an alternative tool, a smaller model, or a cached answerDegraded output beats no output
Circuit breakerStops calling a dependency after N consecutive failures, then probes it periodicallyA dependency is down and retries are making it worse
CheckpointingResumes a multi-step run from the last completed step instead of the start — see State ManagementSteps are expensive or externally visible
Dead-letter queueParks the request for human review rather than dropping itSilent loss is unacceptable

Retries only make sense when the operation is idempotent. Anything that writes — sending a message, charging a card, filing a ticket — needs an idempotency key generated before the first attempt, or a retry turns one failure into two side effects.