DEV Community

Cover image for How I Stopped Babysitting 5 AI APIs and Built a Real Multi-Model Workflow
caicaibig-tige
caicaibig-tige

Posted on

How I Stopped Babysitting 5 AI APIs and Built a Real Multi-Model Workflow

Last month I shipped a content moderation feature for a client app. The requirement sounded simple: catch toxic comments fast, but also handle nuanced cases like sarcasm and code-switching. I started with one model — my default GPT-class endpoint — and within a week I was staring at a spreadsheet of failed edge cases. One model wasn't enough, and wiring up three different provider SDKs by hand turned into a glue-code nightmare.

The problem with single-model thinking

We tend to pick one AI provider and stick with it. But models have different strengths. A small fast model is great for first-pass filtering. A larger reasoning model is better for ambiguous text. A specialized moderation model might catch things the others miss.

The issue isn't knowing this — it's the operational overhead:

  • Different auth schemes
  • Different request/response shapes
  • Rate limits all over the place
  • No unified logging

I counted it up: I spent roughly 40% of my build time just normalizing responses between two providers.

A pragmatic multi-model pattern

The pattern that worked for me: route → parallel call → reconcile.

  1. A cheap classifier decides which models to invoke
  2. Those models run in parallel
  3. A simple scoring function merges results

Here's a stripped-down Python example using asyncio and a unified client approach:

import asyncio
import json

async def call_model(client, model_name, prompt):
    # client abstracts provider differences
    resp = await client.generate(model=model_name, prompt=prompt)
    return {"model": model_name, "result": resp["text"], "score": resp.get("confidence", 0.5)}

async def moderate(text):
    # Route: cheap model first
    router = await call_model(client, "mini-filter", f"toxic? {text}")
    tasks = []
    if router["score"] > 0.3:
        tasks.append(call_model(client, "reason-model", f"analyze: {text}"))
        tasks.append(call_model(client, "mod-specialist", f"flag: {text}"))
    else:
        tasks.append(call_model(client, "mini-filter", f"confirm safe: {text}"))

    results = await asyncio.gather(*tasks)
    # Reconcile: average scores, prefer specialist flags
    flagged = any(r["model"] == "mod-specialist" and r["score"] > 0.6 for r in results)
    avg = sum(r["score"] for r in results) / len(results)
    return {"flagged": flagged, "confidence": avg}
Enter fullscreen mode Exit fullscreen mode

The key is that client hides the provider mess. I found https://xinghuo1300ai.com which aggregates 30+ models under one API key, and that let me swap reason-model and mod-specialist between providers without touching the routing logic.

What actually broke in production

A few honest notes from running this for 3 weeks:

  • Latency adds up. Parallel helps, but two 800ms calls still beat one 400ms call in worst-case time. Set a hard timeout per model.
  • Cost isn't linear. The router model saved me ~30% on token spend because it killed unnecessary big-model calls.
  • Reconciliation is bias-prone. If you always trust the specialist, you inherit its false positives. I log every merge decision now.

Smaller teams should default to aggregation

If you're a solo dev or a small team, don't build your own model abstraction layer from scratch. I tried, and it rotted the moment a provider changed their SDK. Tools like https://xinghuo1300ai.com make model switching trivial — I swapped a flaky endpoint for a different backend mid-sprint with a one-line config change.

Where I landed

The moderation feature now uses three models behind one key, and my incident count dropped. Not because AI is magic, but because I stopped forcing one model to do a job it wasn't built for. If you're building anything with real user input, consider the route-parallel-reconcile split before you reach for another prompt tweak.

Top comments (0)