DEV Community

NexusCore
NexusCore

Posted on

Why Your Multi-Agent RAG System Hangs in Production (And the 3-Line Fix)

Every multi-agent framework eventually hits the same problem.

Everything works perfectly in demos.

Then production traffic arrives.

One malformed tool response, one empty vector search, or one invalid JSON payload later...

Your expensive LLM agents begin doing this forever:

Retrieve documents
↓

No results
↓

Call retrieval tool again
↓

Still empty
↓

Try again...
↓

Try again...
↓

Try again...
Enter fullscreen mode Exit fullscreen mode

CPU usage climbs.

OpenAI bill climbs.

Users wait.

Nothing ever finishes.

If you're using CrewAI, LangGraph, AutoGen, OpenAI Agents SDK, or your own orchestration layer, you've probably seen some variation of this.

The good news is that the fix is surprisingly small.


The Real Production Failure

Most agent frameworks assume tools eventually return something useful.

Reality is messier.

Typical retrieval flow:

Question
      ↓
Retriever
      ↓
Vector DB
      ↓
Empty Context
      ↓
LLM:
"I should search again."
      ↓
Retriever
      ↓
Empty Context
      ↓
Repeat forever
Enter fullscreen mode Exit fullscreen mode

The model isn't "broken."

It's following its objective.

Without explicit stopping conditions, the optimal strategy becomes:

"Maybe the next retrieval succeeds."

Forever.


Another Common Failure: Invalid JSON

Modern agent frameworks frequently communicate using structured outputs.

Example:

{
  "action": "search",
  "query": "LangGraph memory"
}
Enter fullscreen mode Exit fullscreen mode

Now imagine the model returns:

{
 action: search,
 query: LangGraph memory
Enter fullscreen mode Exit fullscreen mode

Oops.

JSON parser throws.

Your orchestration catches the exception.

The LLM receives:

"Tool failed."

Its response?

Try the tool again.

Another malformed payload.

Loop.


Why Multi-Agent Systems Make This Worse

Single-agent systems usually fail once.

Multi-agent systems amplify failures.

Planner
    ↓
Retriever
    ↓
Researcher
    ↓
Critic
    ↓
Planner again
Enter fullscreen mode Exit fullscreen mode

One empty retrieval can bounce between agents indefinitely.

I've seen production graphs where six agents continuously handed an impossible task to one another for hundreds of iterations before timing out.


The Three-Line Fix

Instead of blindly retrying forever:

if not context:
    raise EmptyContextError()
Enter fullscreen mode Exit fullscreen mode

Intercept it immediately.

MAX_RETRIES = 2

if retries >= MAX_RETRIES:
    return "NO_CONTEXT_AVAILABLE"
Enter fullscreen mode Exit fullscreen mode

Then teach downstream agents to recognize the sentinel value.

if result == "NO_CONTEXT_AVAILABLE":
    return "Answer cannot be generated with current knowledge."
Enter fullscreen mode Exit fullscreen mode

That's it.

No infinite loop.

No runaway token spend.

No mystery production hangs.


A Cleaner Version

Here's a minimal wrapper around any retrieval function.

from typing import Callable, Any

MAX_RETRIES = 2
EMPTY = "NO_CONTEXT_AVAILABLE"


def safe_retrieve(fn: Callable[..., Any], *args, **kwargs):
    retries = 0

    while retries < MAX_RETRIES:
        try:
            docs = fn(*args, **kwargs)

            if docs:
                return docs

        except Exception:
            pass

        retries += 1

    return EMPTY
Enter fullscreen mode Exit fullscreen mode

Now your agent logic becomes predictable.

docs = safe_retrieve(vector_store.search, query)

if docs == EMPTY:
    return {
        "status": "insufficient_context",
        "answer": (
            "No supporting documents were found. "
            "Avoid retrying the retrieval tool."
        ),
    }

# Continue with normal RAG pipeline...
Enter fullscreen mode Exit fullscreen mode

Notice that we don't silently swallow the failure—we return an explicit state that downstream components can understand and handle.


Handling Invalid JSON Safely

Never assume LLM output is valid.

Instead:

import json

def parse_agent_output(raw: str):
    try:
        return json.loads(raw)

    except json.JSONDecodeError:
        return {
            "status": "invalid_json",
            "retry": False
        }
Enter fullscreen mode Exit fullscreen mode

Then your workflow can branch intentionally instead of recursively retrying.

response = parse_agent_output(llm_output)

if response["status"] == "invalid_json":
    return "Agent produced malformed output."
Enter fullscreen mode Exit fullscreen mode

Add a Circuit Breaker

Every production agent should have one.

MAX_STEPS = 20

for step in range(MAX_STEPS):

    result = agent.run()

    if result.done:
        break

else:
    raise RuntimeError(
        "Agent exceeded maximum execution steps."
    )
Enter fullscreen mode Exit fullscreen mode

Think of this as the equivalent of a database query timeout.

Without it, one bad prompt can burn thousands of unnecessary tokens.


Observability Matters More Than Retries

One of the biggest mistakes in agent orchestration is treating every failure as something to retry.

Instead, log structured events.

logger.info(
    "retrieval_failed",
    extra={
        "query": query,
        "attempt": retries,
        "reason": "empty_context",
    },
)
Enter fullscreen mode Exit fullscreen mode

Now your dashboards can answer questions like:

  • Which tools fail most often?
  • Which queries produce empty context?
  • Which agent loops consume the most tokens?
  • How many requests terminate because of malformed outputs?

Once you can measure failures, you can improve them.


Production Checklist

Before deploying any multi-agent RAG system, verify that you have:

  • ✅ Maximum tool retry limits
  • ✅ Empty-context detection
  • ✅ Invalid JSON handling
  • ✅ Maximum execution steps
  • ✅ Circuit breakers
  • ✅ Structured logging
  • ✅ Token usage monitoring
  • ✅ Graceful fallback responses
  • ✅ Tool timeout limits
  • ✅ Human-readable failure states

These safeguards are lightweight, but they prevent many of the most expensive production failures.


Final Thoughts

Most production agent failures don't come from model quality.

They come from missing guardrails.

The most reliable systems aren't the ones with the largest models—they're the ones that know when to stop.

Three lines of defensive logic can save hours of debugging, thousands of wasted tokens, and a lot of confused users.

If you're building AI agents in production, I put together a free resource covering the most common orchestration pitfalls:

📋 10 AI Agent Failure Modes Checklist (Free)

https://shadowcraft41.gumroad.com/l/bjezoo

And if you're building or operating production RAG systems with multiple agents, retrieval pipelines, and tool orchestration, I also created the:

⚙️ Multi-Agent RAG Operations Kit ($29)

https://shadowcraft41.gumroad.com/l/xmzthq

It includes practical patterns, operational checklists, architecture guidance, and production-ready practices for building more reliable multi-agent RAG systems.

Happy shipping!

Top comments (0)