Skip to main content

Coordinator Agent

Multi-agent orchestrator with specialized sub-agents.

Difficulty: Expert

What You'll Learn​

  • Multi-agent orchestration
  • Sub-agent invocation control
  • State sharing between agents
  • Nested audit trails

Architecture​

┌─────────────┐
│ Coordinator │
└──────┬──────┘
┌───────────────┼───────────────┐
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐
│ Researcher │ │ Writer │ │ Reviewer │
└────────────┘ └────────────┘ └────────────┘
│
▼
┌────────────┐
│ Wikipedia │
└────────────┘

Sub-Agents​

AgentRoleHTTP Calls
ResearcherGathers factsWikipedia, Ollama (LLM)
WriterCreates contentOllama (LLM)
ReviewerReviews qualityOllama (LLM)

All three sub-agents run as in-process function calls under the same guest identity, so HTTP policy can only be scoped by destination, not by which sub-agent issued the call. The policy below allows Ollama and Wikipedia for the agent as a whole — it's a single shared allowlist, not a per-sub-agent one.

Workflow​

  1. Plan: Extract topic from request
  2. Research: Gather facts via Wikipedia
  3. Write: Create content from research
  4. Review: Quality feedback
  5. Finalize: Compile output

Setup​

agentvisor template create langgraph/coordinator-agent
cd coordinator-agent
docker compose up -d
agentvisor serve .

Each sub-agent's LLM calls go through Ollama at a loopback address, which the proxy's SSRF guard blocks by default — the shipped agentvisor.yaml allowlists it; see Egress Controls for how proxy.ssrf_allowed_cidrs works.

Test It​

THREAD=$(curl -sX POST http://localhost:8090/threads | jq -r '.thread_id')

# Request content (allow 120s for all sub-agents)
curl -sX POST "http://localhost:8090/threads/$THREAD/runs?wait=120s" \
-H "Content-Type: application/json" \
-d '{"input": {"messages": [{"role": "user", "content": "Write an article about quantum computing"}]}}'

Expected Output​

# Content Creation Complete

## Topic: Quantum Computing

---

## Content

[Written content...]

---

## Review

[Reviewer feedback...]

---

*Created by Coordinator Agent*

Key Code​

Sub-Agent Module​

# agents/researcher.py
def research(topic: str) -> str:
wiki_data = search_wikipedia(topic)
llm = ChatOllama(...)
response = llm.invoke([
SystemMessage(content=SYSTEM_PROMPT),
HumanMessage(content=f"Research: {topic}\n\n{wiki_data}")
])
return response.content

Coordinator Graph​

builder = StateGraph(CoordinatorState)

builder.add_node("plan", plan)
builder.add_node("research", do_research)
builder.add_node("write", do_write)
builder.add_node("review", do_review)
builder.add_node("finalize", finalize)

builder.set_entry_point("plan")

builder.add_edge("plan", "research")
builder.add_edge("research", "write")
builder.add_edge("write", "review")
builder.add_edge("review", "finalize")
builder.add_edge("finalize", END)

Policy Highlights​

Sub-Agent Allowlist​

annotations:
- name: "allowed_subagents"
value:
- "researcher"
- "writer"
- "reviewer"

HTTP Allowlist (shared across sub-agents)​

# Applies to every outbound HTTP call the agent makes, regardless of which
# sub-agent triggered it — enforcement is scoped by destination only.
annotations:
- name: "allowed_patterns"
value:
- "^ollama(:\\d+)?(/.*)?$"
- "^localhost:11434(/.*)?$"
- "^127\\.0\\.0\\.1:11434(/.*)?$"
- "^en\\.wikipedia\\.org(/.*)?$"

Performance Notes​

  • Each sub-agent makes LLM calls
  • Research also makes HTTP call
  • Expect 60-120 seconds total
  • Use ?wait=120s timeout

Extending​

To add a new sub-agent:

  1. Create agents/new_agent.py
  2. Export from agents/__init__.py
  3. Add node in coordinator
  4. Update policy allowlist

Next Steps​

  • Pentest Agent: gVisor shell execution, credential substitution, and agentvisor build