Framework Guides12 min read

Agno Performance Optimization: From 2 Seconds to 200ms

Agno is fast out of the box. But getting to sub-200ms response times requires specific patterns.

Carolina Fogliato

February 5, 2026

Agno is already fast. The team claims 70x faster than LangChain, and in our benchmarks, that's not marketing—it's real.

But "fast" is relative.

When we first deployed our dispute resolution system, agent responses averaged 2.1 seconds. For a chatbot, that's fine. For real-time mediation where users expect near-instant responses, it's not.

We needed sub-200ms.

Getting there required specific patterns—some obvious, some counterintuitive. This post breaks down exactly what we did, with code examples you can apply to your own Agno deployments.

Why 200ms Matters

First, let's talk about why we cared about 200ms specifically.

The Research Is Clear

  • • Under 100ms feels instant
  • 100-300ms feels responsive
  • 300-1000ms feels noticeable
  • • Over 1000ms feels slow

For our dispute resolution system, users are in emotional situations. They're frustrated. They want answers. Every second of latency compounds that frustration.

But it's not just UX. At scale, latency compounds:

Before: 2.1 Seconds

2.1s × 15,000 cases × 8 calls

= 78 hours of waiting

After: 200ms

0.2s × 15,000 cases × 8 calls

= 6.7 hours of waiting

That's 71 hours of user time saved. Per year. For one system.

The Baseline: 2.1 Seconds

Let's start with what we were working with. Here's where the time went:

ComponentTime
Context serialization50ms
LLM call (GPT-4)1,400ms
Tool execution (sequential)500ms
Response parsing50ms
Overhead100ms
Total2,100ms

GPT-4 was the biggest chunk, but we couldn't just "make GPT-4 faster." We needed to optimize everything we controlled.

Pattern #1: Parallel Tool Execution

The first win was obvious once we looked at it. Our agent called four tools sequentially:

Sequential Tool Calls

  1. 1. Retrieve contract (120ms)
  2. 2. Retrieve precedents (180ms)
  3. 3. Retrieve party history (100ms)
  4. 4. Check jurisdiction rules (100ms)

Total: 500ms

But these tools don't depend on each other. They can run in parallel.

After: Parallel Execution

agent = DisputeAnalysisAgent(
    tool_execution_mode="parallel"  # Enable parallel tools
)
# Total: 180ms (longest tool)

Result: 500ms → 180ms. 64% reduction in tool execution time.

Pattern #2: Model Routing

Here's the counterintuitive one: not every task needs GPT-4.

Our dispute analysis had multiple sub-tasks:

  • • Case classification (simple)
  • • Entity extraction (medium)
  • • Legal reasoning (complex)
  • • Confidence scoring (simple)

GPT-4 is overkill for classification. A fine-tuned small model is faster AND cheaper.

Model Router Implementation

model = ModelRouter({
    "classification": "gpt-3.5-turbo",     # Fast, cheap
    "extraction": "claude-3-haiku",         # Good at structure
    "reasoning": "gpt-4",                   # Complex tasks only
    "scoring": "gpt-3.5-turbo",            # Fast, cheap
})
TaskBefore (GPT-4)After (Routed)
Classification400ms80ms
Extraction350ms120ms
Reasoning500ms500ms
Scoring150ms40ms
Total LLM time1,400ms740ms

Result: 1,400ms → 740ms. 47% reduction in LLM time.

For simple cases (60% of volume), we skip GPT-4 entirely: 1,400ms → 280ms

Pattern #3: Context Optimization

We were passing way too much context. For a complex case, that could be 15,000+ tokens of context.

Problem: More tokens = more latency. GPT-4's time-to-first-token scales with input size.

Build Minimal Context Per Task

def build_optimized_context(case: Case, task: str) -> str:
    # Always include core identifiers
    base = f"Case: {case.id} | Type: {case.dispute_type}"

    if task == "classification":
        return base + f"\nDescription: {case.description[:500]}"

    elif task == "reasoning":
        # Only for complex reasoning do we include more
        relevant_docs = retrieve_relevant_chunks(case, top_k=3)
        return base + f"\nRelevant Sections: {relevant_docs}"

    return base

# Before: 10,000+ tokens
# After: 200-800 tokens depending on task

Result: Average 100ms reduction just from context optimization.

Pattern #4: Response Streaming with Early Exit

For many tasks, we don't need the full response. We can start processing as soon as we have enough.

Stream and Exit Early

async def get_classification(case: Case) -> str:
    async for chunk in agent.stream(prompt):
        # Check if we have enough to classify
        if classification := try_parse_classification(chunk):
            return classification  # Exit early

    # Fallback: parse complete response
    return parse_classification(chunk)

For classification tasks, we typically exit after 30-40% of the response is generated.

Result: 80ms → 35ms for classification. 56% reduction.

Pattern #5: Intelligent Caching

Many inputs are repeated or similar. Caching prevents redundant computation.

Layer 1: Exact Match Cache

Use LRU cache with prompt hashing. Hit rate: ~15%

Layer 2: Semantic Cache

For similar (not identical) prompts, use embedding-based caching with 0.95 similarity threshold. Hit rate: ~25%

Layer 3: Tool Result Caching

Tool results often don't change frequently. Cache contracts for 1 hour, rules for 5 minutes. Hit rate: ~60%

Result: Effective 30-40% reduction in average latency from caching.

Pattern #6: Connection Pooling & Keep-Alive

This one's boring but impactful. New connection = 100-200ms overhead.

Connection Pooling

http_client = httpx.Client(
    http2=True,
    limits=httpx.Limits(
        max_connections=100,
        max_keepalive_connections=20,
        keepalive_expiry=30
    )
)

agno.configure(
    http_client=http_client,
    connection_pool_size=100
)

Result: 100-200ms reduction on first request, consistent low latency on subsequent requests.

Pattern #7: Async All The Way

If you're doing anything synchronous in your agent pipeline, you're leaving performance on the table.

Async Pipeline

async def process_case(case: Case) -> Result:
    # Independent steps run in parallel
    classification, entities = await asyncio.gather(
        classify(case),
        extract_entities(case)
    )

    # Dependent step runs after
    analysis = await analyze(case, classification, entities)

    return Result(classification, entities, analysis)

Before: 80ms + 120ms + 500ms = 700ms

After: max(80ms, 120ms) + 500ms = 620ms

The Combined Result

OptimizationReductionNew Total
Baseline2,100ms
Parallel tools-320ms1,780ms
Model routing-660ms1,120ms
Context optimization-100ms1,020ms
Streaming + early exit-45ms975ms
Caching (30% hit rate)-290ms685ms
Connection pooling-150ms535ms
Async pipeline-80ms455ms

Wait—that's 455ms, not 200ms. Right. For the average case.

Simple Cases (60% of volume)

175ms

Classification 35ms + Extraction 60ms + Analysis 80ms

Complex Cases (40% of volume)

650ms

Includes GPT-4 for complex reasoning

Weighted Average: 365ms

With Caching: 255ms

We got to sub-200ms for simple cases, sub-300ms weighted average. 88% reduction from baseline.

The Optimization Priority

If you're optimizing Agno performance, here's the priority order:

1. Model routing — Biggest impact. Don't use GPT-4 for simple tasks.

2. Parallel tool execution — Easy win. Enable it.

3. Caching — Compound returns. Start with tool results.

4. Context optimization — Reduce tokens, reduce latency.

5. Connection pooling — One-time setup, permanent benefit.

6. Async pipeline — If you have independent steps.

7. Streaming + early exit — For classification/extraction tasks.

You don't need all of these. Implement 1-3 and you'll see dramatic improvement.

When NOT to Optimize

A word of caution: premature optimization is still a thing.

Don't optimize if:

  • • Your latency is already acceptable for your use case
  • • You're still iterating on agent behavior
  • • You don't have production traffic yet
  • • The complexity cost outweighs the latency benefit

Get it working first. Get it correct. Then make it fast.

Final Thought

Agno is fast. But "fast" is a starting point, not a destination.

The patterns here—model routing, parallel execution, caching, context optimization—aren't Agno-specific. They're general principles for building performant AI systems.

2 seconds to 200ms isn't magic. It's methodology.

Building performance-critical AI systems?

Let's talk

We've done this optimization dance across multiple production deployments.

Newsletter

Performance patterns and production insights—every Tuesday.

Join 2,500+ engineers and leaders getting practical AI implementation insights.

Subscribe to AI That Ships

Your Privacy Matters

We use cookies to enhance your experience, analyze traffic, and serve targeted ads.

By clicking "Accept All", you consent to all cookies. Cookie Policy