A Practical Guide

Building Autonomous Agent Hierarchies

How to design, deploy, and orchestrate agents and specialized sub-agents on top of self-hosted language models — written so both newcomers and engineers can follow along.

10 stepsfrom model to production
4 patternsof agent architecture
2 audienceslayman & engineer, one guide
Start Here

What is an "agent," really?

Before touching any infrastructure, it helps to have the mental model right. Use the toggle below to switch between a plain-language explanation and the technical one throughout this guide.

In plain terms

Think of an agent as a digital employee: you give it a goal, and instead of just answering once, it plans, takes actions (like searching, writing, or calling other tools), checks its own results, and keeps going until the goal is done.

A sub-agent is a specialist you hire under that employee — one who's only good at one thing, like "research" or "writing code." The main agent acts like a manager: it breaks a big job into smaller ones and hands each piece to the right specialist.

Technically speaking

An agent is an LLM wrapped in a control loop: it receives a task, generates a plan or next action, invokes tools via function calling, observes the result, and repeats until a stop condition is met (goal satisfied, max steps, or human interrupt).

A sub-agent is a narrower instance of that same loop — often a smaller or fine-tuned model, a restricted tool set, and a focused system prompt — invoked by a parent orchestrator agent that handles task decomposition, routing, and result aggregation.

↑ Switch to Technical mode above to see this same explanation in engineering terms.

↑ Switch to Plain-language mode above to see this same explanation without jargon.

Why self-hosted? Everything in this guide assumes you're running your own model (via Ollama, vLLM, TGI, or llama.cpp) rather than calling a hosted API. The agent logic is the same either way — what changes is latency, cost, and how much control you have over context length, batching, and fine-tuning.

Agent Architecture

Architecture is simply how the manager and specialists are organized — who talks to whom, and how work flows between them.

In plain terms

Agent architecture is the org chart. In the simplest setup, one manager (the orchestrator) hands tasks directly to specialists and collects their answers — like a small team reporting to one boss. In bigger systems, managers can supervise other managers, specialists can pass work straight to each other, or everyone can share a common notice board instead of talking one-on-one.

Technically speaking

Agent architecture defines the topology of control and data flow between the orchestrator and its sub-agents. Common topologies include hierarchical (single orchestrator, flat sub-agents), sequential / pipeline (fixed hand-off order), blackboard (shared mutable state instead of direct messages), and peer-to-peer (decentralized negotiation, no fixed leader). The topology you choose determines coupling, latency, and how failures propagate through the system.

↑ Switch to Technical mode above to see this in engineering terms.

↑ Switch to Plain-language mode above to see this without jargon.

The four concrete patterns — hierarchical, pipeline, blackboard, and peer-to-peer — are diagrammed in detail under Architecture Patterns below.

Why Self-Host

Why run your own models instead of using an API?

Self-hosting trades convenience for control. That trade is worth it when privacy, cost at scale, or customization matter more than getting started quickly.

Self-hostedHosted API
Data privacyData never leaves your infrastructureData sent to a third party
Cost at scaleFixed hardware cost, cheaper per-token at volumePay per token, scales linearly
LatencyDepends on your hardware & networkUsually fast, provider-managed
CustomizationFull control — fine-tune, quantize, swap modelsLimited to provider's options
Operational burdenYou manage uptime, scaling, GPUsProvider handles it
Setup timeHigher — infra, drivers, serving stackMinutes
In short: self-hosting is like owning a car instead of calling a taxi every time. More upfront work and maintenance, but cheaper and more private if you're doing a lot of driving.
Core Concepts

The six building blocks

Every agent hierarchy, however complex, is assembled from the same handful of parts.

01

Orchestrator

The "manager" agent. Decides what needs doing and who should do it.

02

Sub-agents

Specialists with a narrow job, a focused prompt, and limited tools.

03

Tools

Functions an agent can call — search the web, run code, query a database.

04

Memory

What the system remembers: the current conversation, and anything stored long-term.

05

Communication protocol

The structured format agents use to pass tasks and results between each other.

06

Guardrails

Checks, timeouts, and human approval points that keep the system safe and on track.

Architecture Patterns

Four ways to organize agents

Choose a pattern based on how much the sub-tasks depend on each other, and how much autonomy each specialist needs.

Orchestrator Agent
↓ ↓ ↓
Research Sub-agent
Coding Sub-agent
Review Sub-agent

Hierarchical (most common). One orchestrator decomposes the task and delegates to specialists, then merges their results. Best default for most projects.

Step 1: Plan
Step 2: Draft
Step 3: Verify

Sequential pipeline. Fixed order of agents, each one's output feeds the next. Predictable and easy to debug, but rigid.

Shared Memory / Blackboard
↑ ↓
Agent A
Agent B
Agent C

Blackboard. Agents read and write to shared state instead of talking directly. Good when contributions don't need a strict order.

Agent A
Agent B
Agent C
Agent D

Peer-to-peer / swarm. No fixed leader — agents negotiate directly. Powerful but harder to control; use only when you need emergent behavior.

Rule of thumb: start hierarchical. It's the easiest to reason about, debug, and constrain with guardrails. Move to pipeline or blackboard patterns only once you've identified a concrete bottleneck the hierarchy can't solve.
Step-by-Step

Building your agent hierarchy

Ten steps from a bare model to a monitored, production system.

01

Choose and host your model

Pick a serving stack that matches your hardware and latency needs.

  • Ollama — simplest to start, great for a single machine or prototyping.
  • vLLM — high-throughput serving with continuous batching, ideal for production and multiple concurrent agents.
  • Text Generation Inference (TGI) — Hugging Face's production server, strong for teams already in that ecosystem.
  • llama.cpp — CPU-friendly, best for edge or resource-constrained deployments.
Sizing tip: the orchestrator usually needs the strongest reasoning model you can afford; sub-agents doing narrow, repetitive work often run fine on a smaller quantized model (7B–14B), which cuts cost and latency significantly.
02

Pick an orchestration framework

You can hand-roll a control loop, but a framework saves time on routing, state, and retries.

  • LangGraph — graph-based state machine, good fit for hierarchical and pipeline patterns with explicit control flow.
  • CrewAI — role-based abstraction (crew, agents, tasks) that maps directly onto the manager/specialist model.
  • AutoGen / AG2 — conversation-driven, strong for peer-to-peer and blackboard-style setups.
  • Custom loop — a plain function-calling loop around your model's API; more work, but zero framework overhead and full transparency.
03

Design the orchestrator

This agent's only job is to decide who does what — it shouldn't do the work itself.

  • Write a system prompt that defines its role as a router and aggregator, not a doer.
  • Give it a directory of available sub-agents with a one-line description of each (this becomes its "org chart").
  • Define a decomposition strategy: does it plan the full task upfront, or delegate one step at a time and react to results?
04

Define your sub-agents

Narrow scope is what makes sub-agents reliable — resist the urge to make them general-purpose.

  • One job per sub-agent (e.g. "search the web," "write unit tests," "summarize a document").
  • Give each one only the tools it needs — this limits blast radius if something goes wrong.
  • Write a tight system prompt with explicit input/output expectations, ideally a structured schema.
05

Give agents tools

Tools are how agents act on the world beyond generating text.

  • Define each tool with a clear name, description, and parameter schema (JSON schema works with most self-hosted function-calling models, e.g. Hermes, Qwen, or Llama 3.x fine-tunes).
  • Sandbox anything that executes code or touches the filesystem.
  • Add rate limits and timeouts per tool so one runaway call can't stall the whole hierarchy.
06

Set up communication and handoff

Agents need a shared, structured "language" to pass work between each other.

  • Use structured messages (JSON) rather than free text for task handoff — fewer parsing errors.
  • Decide on a delegation format: direct call-and-return, or a shared task queue / blackboard.
  • Always include a result schema (status, output, confidence, errors) so the orchestrator can act on failures.
07

Add memory

Distinguish between what an agent needs right now and what the system should remember long-term.

  • Short-term: manage context window carefully — summarize or truncate old turns rather than letting it overflow.
  • Long-term: use a vector store (e.g. Qdrant, Weaviate, Chroma) for retrieval-augmented recall across sessions.
  • Episodic log: keep a plain record of what each agent did and why, for debugging and audits.
08

Add guardrails and monitoring

Autonomy without oversight is where agent systems get expensive or unsafe.

  • Validate every tool call and structured output against its schema before execution.
  • Set a maximum number of steps/loops per task to prevent infinite delegation chains.
  • Add human-in-the-loop checkpoints for high-stakes actions (payments, external emails, deletions).
  • Instrument with tracing (e.g. Langfuse, OpenTelemetry, or LangSmith self-hosted) to see every step, token, and cost.
09

Test, evaluate, iterate

Treat agent behavior like any other system under test.

  • Build a small set of "golden" tasks with known-good outcomes and run them on every change.
  • Track failure modes per sub-agent, not just the system as a whole — it tells you which specialist to fix.
  • Red-team with adversarial or ambiguous inputs before shipping.
10

Deploy and scale

Move from a single script to a resilient service.

  • Containerize each component (model server, orchestrator, tool services) independently.
  • Put a queue in front of long-running agent tasks so bursts don't overwhelm your GPUs.
  • Autoscale model replicas based on queue depth, not just CPU/GPU utilization.
Reference Implementation

A minimal orchestrator + sub-agent script

A working example in Python using an OpenAI-compatible client against a self-hosted model server — this API shape is served by Ollama, vLLM, and TGI alike, so the same code runs against any of them.

  1. Connect — point the OpenAI client at your local server instead of api.openai.com.
  2. Define sub-agents — each is a plain function with its own system prompt and settings.
  3. Build a tool directory — describe each sub-agent as a callable "tool" with a JSON schema, so the orchestrator can pick one.
  4. Run the orchestrator loop — let the model choose tools, execute them, feed results back, and repeat until it stops calling tools.
  5. Call it — give the orchestrator a task that spans both specialists and let it delegate.
Where to paste and run this: save the code below into a file named orchestrator.py on your machine. Then, in a terminal opened in that same folder, run pip install openai once, followed by python orchestrator.py each time you want to execute it. Make sure your self-hosted model is already running first — e.g. ollama run llama3.1:8b in a separate terminal tab — before you launch the script.
"""
Minimal orchestrator + sub-agent example against a self-hosted,
OpenAI-compatible endpoint (Ollama, vLLM, or TGI).

Install:
    pip install openai

Run a local model first, e.g.:
    ollama run llama3.1:8b
"""

from openai import OpenAI
import json

# 1. Connect — point the client at your self-hosted server
client = OpenAI(
    base_url="http://localhost:11434/v1",  # Ollama's OpenAI-compatible endpoint
    api_key="not-needed",                   # self-hosted servers usually ignore this
)

MODEL = "llama3.1:8b"


# 2. Define sub-agents — small, focused functions with their own prompts
def research_agent(query: str) -> str:
    """Sub-agent specialised in summarising information for a query."""
    response = client.chat.completions.create(
        model=MODEL,
        messages=[
            {"role": "system", "content": (
                "You are a research specialist. Given a topic, return a "
                "concise, factual summary in 3 bullet points. Do not answer "
                "anything outside the research task."
            )},
            {"role": "user", "content": query},
        ],
        temperature=0.2,
    )
    return response.choices[0].message.content


def coding_agent(spec: str) -> str:
    """Sub-agent specialised in writing small code snippets."""
    response = client.chat.completions.create(
        model=MODEL,
        messages=[
            {"role": "system", "content": (
                "You are a coding specialist. Given a specification, return "
                "only working Python code with no explanation."
            )},
            {"role": "user", "content": spec},
        ],
        temperature=0.1,
    )
    return response.choices[0].message.content


# 3. Build a tool directory — how the orchestrator "sees" its sub-agents
SUBAGENT_TOOLS = [
    {
        "type": "function",
        "function": {
            "name": "research_agent",
            "description": "Use for gathering or summarising factual information.",
            "parameters": {
                "type": "object",
                "properties": {"query": {"type": "string"}},
                "required": ["query"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "coding_agent",
            "description": "Use for writing or fixing small code snippets.",
            "parameters": {
                "type": "object",
                "properties": {"spec": {"type": "string"}},
                "required": ["spec"],
            },
        },
    },
]

DISPATCH = {
    "research_agent": research_agent,
    "coding_agent": coding_agent,
}


# 4. Orchestrator loop — delegate, collect results, repeat until done
def orchestrator(task: str) -> str:
    messages = [
        {"role": "system", "content": (
            "You are the orchestrator. You do not answer tasks yourself — "
            "you decide which specialist tool to call for each part of the "
            "task, then combine their outputs into a final answer."
        )},
        {"role": "user", "content": task},
    ]

    response = client.chat.completions.create(
        model=MODEL,
        messages=messages,
        tools=SUBAGENT_TOOLS,
        tool_choice="auto",
    )
    msg = response.choices[0].message

    while msg.tool_calls:
        messages.append(msg)
        for call in msg.tool_calls:
            fn = DISPATCH[call.function.name]
            args = json.loads(call.function.arguments)
            result = fn(**args)
            messages.append({
                "role": "tool",
                "tool_call_id": call.id,
                "content": result,
            })

        response = client.chat.completions.create(
            model=MODEL,
            messages=messages,
            tools=SUBAGENT_TOOLS,
            tool_choice="auto",
        )
        msg = response.choices[0].message

    return msg.content


# 5. Call it
if __name__ == "__main__":
    result = orchestrator(
        "Summarise what a vector database is, then write a Python "
        "function that computes cosine similarity between two vectors."
    )
    print(result)
Swapping servers: for vLLM, change base_url to http://localhost:8000/v1; for TGI, use its OpenAI-compatible route. The tool-calling loop itself doesn't change — only the endpoint and model name do.

What running this actually produces

In plain terms

You send the orchestrator one task. Behind the scenes it quietly asks the research specialist for a summary and the coding specialist for a snippet, then hands you back one merged answer — you never see the two specialists as separate replies, only the final combined result.

Technically speaking

The single orchestrator() call triggers two tool-call round trips (one per sub-agent), appends each tool message to the conversation, then makes a final completion request with no tool_calls in the response — at which point the while loop exits and msg.content holds the merged text.

Running python orchestrator.py against a local Llama 3.1 model produces output similar to this:

$ python orchestrator.py

A vector database stores data as high-dimensional vectors and supports
similarity search rather than exact matching.
- It indexes embeddings so you can retrieve the items "nearest" to a query.
- Common uses include semantic search, recommendation systems, and RAG.
- Popular options include Qdrant, Weaviate, and Chroma.

def cosine_similarity(a, b):
    dot = sum(x * y for x, y in zip(a, b))
    norm_a = sum(x ** 2 for x in a) ** 0.5
    norm_b = sum(y ** 2 for y in b) ** 0.5
    return dot / (norm_a * norm_b)
Note: exact wording will vary by model — what stays constant is the shape of the outcome: one delegated call per specialist, and a single, merged answer as the final output. Not every self-hosted model follows function-calling schemas reliably; if tool calls come back malformed, validate and retry before trusting the arguments (see Step 08, Guardrails).
Reference

A representative self-hosted stack

There's no single correct stack — this is a sensible starting point to adapt.

LayerOptionsNotes
Model servingvLLM, Ollama, TGI, llama.cppvLLM for throughput; Ollama for simplicity
OrchestrationLangGraph, CrewAI, AutoGenMatch to your architecture pattern
Vector storeQdrant, Weaviate, ChromaFor long-term / RAG memory
ObservabilityLangfuse, OpenTelemetryTrace every agent step and cost
Task queueRedis, RabbitMQ, CeleryDecouples request intake from GPU load
ContainersDocker, KubernetesIndependent scaling per component
Case Study

Applying this to Hermes Agent

Hermes Agent, the self-improving open-source agent from Nous Research, ships most of the hierarchy in this guide as a built-in feature rather than something you build from scratch. Here's how the two map onto each other.

In plain terms

Hermes Agent is a ready-made "employee" you install rather than code yourself. Instead of writing the orchestrator loop from our reference script, you just talk to Hermes and it decides on its own when to hand a piece of work to a specialist — and it keeps a growing notebook of tricks it's learned, so it gets better at repeat tasks over time.

Technically speaking

Hermes Agent (MIT licensed, Nous Research, released Feb 2026) implements the orchestrator / sub-agent loop natively via a delegate_task tool, with configurable depth, concurrency, tool restrictions, and per-child model overrides — plus a persistent skills-and-memory layer this guide's generic pattern doesn't cover at all.

↑ Switch to Technical mode above to see this in engineering terms.

↑ Switch to Plain-language mode above to see this without jargon.

Concept map

This guideHermes Agent equivalent
Orchestrator agentthe main session calling delegate_task
Sub-agentchild agent, role="leaf" (default)
Hierarchical patternflat delegation — max_spawn_depth: 1 (default)
Nested orchestrationrole="orchestrator" + max_spawn_depth: 2+
Narrow sub-agent tool accessblocked by default: delegate_task, clarify, memory, execute_code
Cheaper model for sub-agentsdelegation.model override in config.yaml
Step-limit guardrailmax_iterations (default 50), child_timeout_seconds
Monitoring / tracing/agents overlay — live tree, cost per branch
Self-hosted model connectionmodel.base_url custom endpoint (Ollama, vLLM, etc.)
Not in our guide: Hermes's biggest differentiator is a closed learning loop — agents write and refine their own reusable "skills" from experience and carry memory across sessions, so capability compounds over time instead of every call starting from the same static prompt.

Point it at a self-hosted model

Same idea as the reference script earlier — Hermes talks to any OpenAI-compatible endpoint. Set it in ~/.hermes/config.yaml:

# ~/.hermes/config.yaml
model:
  default: qwen2.5-coder:32b
  provider: custom
  base_url: http://localhost:11434/v1   # Ollama; use :8000/v1 for vLLM
  api_key: ""                           # empty for local endpoints

# Optional: route sub-agents to a cheaper/faster model
delegation:
  model: "qwen2.5-coder:7b"
  max_iterations: 50        # per-child step limit
  max_concurrent_children: 3
  max_spawn_depth: 1        # raise to 2+ for nested orchestration
Quickstart: install with curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash, run hermes model and choose "Custom endpoint (self-hosted / VLLM / etc.)", then just talk to it — Hermes delegates to sub-agents automatically when a task is complex enough; you don't need to ask it to.
Common Pitfalls

What tends to go wrong

×

Sub-agents that are too general

A sub-agent with a broad mandate behaves unpredictably. Narrower is more reliable, even if it means more sub-agents.

×

No step limit

Without a hard cap on delegation depth or loop count, a confused orchestrator can spin indefinitely — burning GPU time and money.

×

Free-text handoffs

Passing results as prose between agents invites misparsing. Structured, schema-validated messages catch errors early.

×

Skipping evaluation

Agent behavior drifts as prompts, models, and tools change. Without golden tasks, regressions go unnoticed until a user hits them.

×

Over-provisioning every sub-agent with a large model

Running every specialist on your biggest model wastes GPU budget. Match model size to task difficulty.

×

No human checkpoint for irreversible actions

Sending emails, spending money, or deleting data should pause for approval until you trust the system's track record.

Glossary

Plain-language definitions

Agent
A program built on an LLM that can plan, take actions, and check its own results toward a goal, rather than just answering once.
Sub-agent
A specialized agent handling one narrow task, invoked by a parent orchestrator.
Orchestrator
The "manager" agent that breaks work into pieces and assigns them to sub-agents.
Self-hosted model
An LLM you run on your own hardware or cloud servers, instead of calling a third-party API.
Tool / function calling
A mechanism letting an LLM invoke external code — search, calculators, databases — in a structured way.
Context window
The amount of text a model can "see" at once when generating a response.
Quantization
Shrinking a model's numeric precision to make it smaller and faster, with a small accuracy trade-off.
RAG (Retrieval-Augmented Generation)
Giving a model relevant documents pulled from a database at the moment it answers, instead of relying only on what it was trained on.
Guardrails
Rules, checks, and limits that keep an agent's actions safe, valid, and within scope.
Human-in-the-loop
A checkpoint where a person must approve an agent's action before it proceeds.
?
FAQ

Frequently asked questions

Do I need multiple GPUs to get started?

No. A single consumer GPU (or even CPU with llama.cpp) is enough to prototype a hierarchy with one orchestrator and a couple of sub-agents using a quantized 7B–8B model. Scale hardware once you've validated the design.

Can I mix self-hosted and hosted-API models in one hierarchy?

Yes — many teams route routine, high-volume sub-agent tasks to a self-hosted model and reserve a hosted frontier model for the orchestrator or the hardest sub-tasks. The communication protocol between agents doesn't care where each model runs.

How many sub-agents is too many?

There's no fixed number, but complexity grows fast. Start with 2–4 sub-agents covering distinct responsibilities; add more only when you can point to a specific task the current set handles poorly.

Do sub-agents need to run the same model as the orchestrator?

No, and often they shouldn't. A smaller, cheaper, possibly fine-tuned model is usually sufficient for a narrow sub-agent task, while the orchestrator benefits from a stronger general-reasoning model.

What's the biggest risk in production?

Unbounded loops and irreversible actions taken without review. Cap step counts, validate every structured output, and gate anything irreversible behind human approval until the system has a proven track record.