All posts
aiAI-Powered

Multi-agent AI systems in 2025: architecture, tools, pitfalls

How to build production-ready multi-agent systems in 2025: Hermes-style orchestration, LangChain, autogen, evaluation. Practical guide with code and real pitfalls.

5 min readUpdated: June 12, 2026

Multi-agent AI systems in 2025

Over the past two years I have built five multi-agent systems for various clients β€” from lead generation platforms to internal code analysis tools. Each looked different on a diagram, but the patterns repeat. This post is about those patterns.

What a multi-agent system actually is

It is not five LLM models calling each other indefinitely. It is problem decomposition into roles:

  • Planner β€” receives the high-level goal, breaks it into steps
  • Executor β€” performs concrete actions (function calling, API, database)
  • Reviewer β€” critiques Executor's output, requests a fix or accepts
  • Researcher (optional) β€” supplies context, documents, code snippets

A well-designed multi-agent system is not a chat. It has state, it has control flow and it has a way to stop when the task is done or when it gets stuck in a loop.

Minimal architecture that works in production

The simplest system maintainable in production is 2 agents + a loop with iteration limit:

import openai
import json

MAX_ITERATIONS = 8

def run_agent_loop(task: str) -> str:
    state = {"task": task, "history": [], "iteration": 0}

    while state["iteration"] < MAX_ITERATIONS:
        state["iteration"] += 1

        # Planner: decides what is next
        plan = call_planner(state)
        state["history"].append({"role": "planner", "content": plan})

        if plan["action"] == "DONE":
            return plan["final_answer"]

        if plan["action"] == "TOOL_CALL":
            # Executor: performs the concrete call
            result = call_executor(plan["tool"], plan["args"])
            state["history"].append({"role": "executor", "content": result})

            # Reviewer: critiques
            critique = call_reviewer(state)
            state["history"].append({"role": "reviewer", "content": critique})

            if critique["approved"]:
                return critique["final_answer"]

    raise RuntimeError(f"Agent loop stuck after {MAX_ITERATIONS} iterations")

This is the skeleton β€” 30 lines handling planning, execution and validation. Add:

  • persistence (state to Redis/SQLite, not in memory)
  • observability (LangSmith or a custom logger with token counts)
  • schema validation on every output (Pydantic / Zod)
  • rate limiting (429 from OpenAI is not an exception, it is normal)

And you have a production-grade system.

Hermes-style orchestration: a pattern that works in my projects

Internally I call it Hermes-style after my main agent framework (Hermes Agent, which is what I work on). The key idea: the orchestrator is a thin layer over deterministic code, not over another LLM.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Orchestrator (Python/TypeScript)           β”‚
β”‚  ───────────────────────────────            β”‚
β”‚  - knows what the user wants                β”‚
β”‚  - calls agents in the right order          β”‚
β”‚  - validates results (Zod)                  β”‚
β”‚  - logs everything                          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚
   β”Œβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”
   β–Ό       β–Ό       β–Ό
 Agent1  Agent2  Agent3
 (LLM)   (LLM)   (LLM)
   β”‚       β”‚       β”‚
   β–Ό       β–Ό       β–Ό
 Tools   Tools   Tools

The orchestrator is not an LLM. It is plain code with if/else, loops, retry logic. Only the inner agents are LLMs. This inverts a typical mistake: people make the orchestrator an LLM, then wonder why it hallucinates at the control level.

Tools β€” when to use what

| Framework | When to use | When NOT to use | |-----------|-------------|-----------------| | Own code (OpenAI function calling) | 1-3 agents, prototype, MVP | Above 5 agents, you need a state graph | | LangGraph | 4+ agents, complex graph, persistence | Simple sequential pipeline β€” overengineering | | autogen (Microsoft) | Conversational agents, research, exploration | Production needing low latency and low cost | | CrewAI | Role-based agents (like a crew), content creation | When you need precise control flow | | LlamaIndex Agents | RAG-heavy workflow (agents read documents) | Mostly function calling, no RAG |

My recommendation: start with your own code. Frameworks give 30% time savings at the start, but 200% more pain when debugging in production, when LangChain version changes and everything breaks.

Five pitfalls I have seen at clients

1. No iteration limit

The agent enters a loop: Planner says "execute X", Executor returns an error, Planner says "try X again", Executor returns the same error. After 200 iterations you have a $47 OpenAI bill and a Cloudflare timeout. Always set MAX_ITERATIONS. I default to 8.

2. No schema validation

The agent returns "looks good" instead of JSON. The parser throws. The code breaks. Every agent output must be validated (Pydantic in Python, Zod in TypeScript). Without it you do not know whether the bug is in the agent, the prompt, the parser, or the data.

3. Too many agents from the start

A client wants "a chatbot that does research, writes reports, manages a calendar and books flights". These are four different products, not one. Start with one agent doing one thing well. Add a second only when the user really needs both capabilities at the same time.

4. No observability

A multi-agent system in production without logs is a black box. When the user says "it did not work", you have no idea why. Minimum:

  • Every LLM call: timestamp, model, prompt, output, token count, latency
  • Every orchestrator decision: why it called agent X not Y
  • Every tool call: input, output, execution time

Without these you do not debug, you guess.

5. Hallucinations cascade

Agent A hallucinates that the user wants X. Agent B takes it for granted. Agent C builds on that. None of the three knows A was wrong. Solution: a Reviewer agent (pattern above) or grounding in data β€” the agent must cite the source, otherwise its answer is rejected.

When multi-agent does NOT make sense

Let us be honest. In 60% of cases where people deploy multi-agent, they would be better off with:

  • A good prompt + one GPT-4o model
  • Function calling for 3-4 tools
  • RAG with good retrieval

Multi-agent makes sense when:

  • The task naturally splits into roles (research β†’ plan β†’ execute β†’ review)
  • A single agent regularly overflows the context (long conversation history)
  • You need different models for different tasks (e.g. GPT-4o for planning, GPT-4o-mini for simple classifications β€” 80% cost savings)
  • You have an auditability requirement (each step has its own agent = own log)

If none of these fit β€” do not build a multi-agent. Stay with one agent and function calling. It is less "impressive" but 10x easier to maintain.

What is next

In upcoming posts I will expand on specific topics:

If you are building a multi-agent system and you are stuck β€” get in touch. I help with architecture, code review and production debugging.

Tags:#ai#multi-agent#langchain#architecture#llm

NajczΔ™Ε›ciej zadawane pytania

What is a multi-agent system in AI?
A multi-agent system is an architecture where several specialized LLM models (agents) collaborate on a complex task, exchanging context, tools and intermediate results. Each agent has its own system prompt, limited context and clearly defined role β€” e.g. Researcher, Planner, Executor, Reviewer.
Do I need LangChain or autogen to build multi-agent systems?
No. The simplest multi-agent systems can be built in 200 lines of code on top of OpenAI function calling and a plain loop. Frameworks (LangGraph, autogen, CrewAI) become useful only past 3-4 agents, when you need state management, persistence and observability.
How many agents is 'multi-agent' and when is it overkill?
Practical limit: 2-3 agents solves 80% of cases (e.g. Planner + Executor + Reviewer). Above 5 agents token costs explode, debugging becomes a nightmare, and hallucinations cascade. Start with 1 agent, add a second when you see a real need, not 'just in case'.
How do you debug a multi-agent system in production?
Three layers: (1) full conversation logging with timestamps and token counts, (2) tracing like LangSmith or Helicone, (3) intermediate assertions β€” agent N must return JSON in a given schema, otherwise the loop breaks. Without these three, a multi-agent system in production is a black box.

Related posts