DEV Community

Cover image for Why Your Local LLM Feels "Dumb" Compared to Cloud APIs
Shakib S.
Shakib S.

Posted on

Why Your Local LLM Feels "Dumb" Compared to Cloud APIs

The Experiment That Changed How I Think About AI

I had just set up a fully local AI stack. Ollama running Llama 3 8B, clean terminal, no API keys, no monthly bill. I typed a real-world task:

"Research the latest patch notes for Elden Ring and save a summary to my desktop."

It failed. Politely, apologetically — but it failed.

Then I opened Claude. Same task. It searched the web, summarized the results, and handed me a file.

My first instinct was the obvious one: Claude is just smarter.

That instinct was wrong. And understanding why it's wrong is the most useful thing you can learn about AI right now.


The Brain in a Jar Problem

A local LLM in its default state is exactly this: a brain in a jar.

It has knowledge. Enormous knowledge, compressed into billions of parameters. But it has no limbs. It cannot reach the internet. It cannot write to your filesystem. It cannot verify its own outputs. It cannot call an API. It just... predicts the next token, over and over, inside a sealed container.

When Claude "thinks" through a complex task, it isn't doing so purely with a bigger or smarter model. It's using tool calling — a mechanism where the model emits structured instructions, and a surrounding system executes them.

The model says: "I need to search the web."

The system does the search.

The results come back into context.

The model says: "Now write this to a file."

The system writes the file.

The model itself never touched the internet. Never touched your filesystem. It just coordinated — and the orchestration layer did the actual work.

That orchestration layer is what you're paying for when you pay for Claude or GPT-4. The model is increasingly a commodity. The IP is the system built around it.


Proving It: Raw vs. Orchestrated

To make this concrete, I ran an experiment with a simple setup.

The stack:

  • Inference: Ollama (Llama 3 8B)
  • Orchestration: Python middleware with basic function calling
  • Tools: A web search API (Tavily) and a local filesystem writer

The logic flow for the Elden Ring task:

Without orchestration, the model has no path to success. It knows what patch notes are. It knows how to summarize. But it cannot get the data, so the task dies before it starts.

With orchestration, the flow looks like this:

  1. User sends the request
  2. The model emits structured JSON: {"tool": "web_search", "query": "Elden Ring patch notes 2026"}
  3. The Python middleware intercepts this, runs the search, returns the text
  4. The model summarizes and emits: {"tool": "write_file", "filename": "summary.txt", "content": "..."}
  5. The middleware writes the file

Same 8B model. Completely different result.

The finding was stark: an orchestrated 8B model consistently outperforms a raw 70B model on real-world tasks. Not because it's smarter — because it has agency. It has limbs.


The Code That Makes It Real

Here's a minimal Python implementation of this pattern. This is not pseudocode — this runs.

import json
import requests

OLLAMA_URL = "http://localhost:11434/api/chat"
MODEL = "llama3:8b"

TOOLS = {
    "web_search": lambda query: search_web(query),
    "write_file": lambda filename, content: write_to_disk(filename, content),
}

def search_web(query: str) -> str:
    # Replace with your Tavily or SearXNG endpoint
    response = requests.get(
        "https://api.tavily.com/search",
        params={"query": query, "api_key": "YOUR_KEY"}
    )
    results = response.json().get("results", [])
    return "\n".join([r["content"] for r in results[:3]])

def write_to_disk(filename: str, content: str) -> str:
    with open(f"/tmp/{filename}", "w") as f:
        f.write(content)
    return f"File written: /tmp/{filename}"

def run_agent(user_input: str):
    system_prompt = """
    You are an agent with access to tools. When you need to use a tool, 
    respond ONLY with valid JSON in this format:
    {"tool": "tool_name", "args": {"arg1": "value1"}}

    Available tools:
    - web_search: {"tool": "web_search", "args": {"query": "your query"}}
    - write_file: {"tool": "write_file", "args": {"filename": "name.txt", "content": "..."}}

    When the task is complete, respond normally in plain text.
    """

    messages = [
        {"role": "system", "content": system_prompt},
        {"role": "user", "content": user_input}
    ]

    for _ in range(5):  # max 5 tool calls
        response = requests.post(OLLAMA_URL, json={
            "model": MODEL,
            "messages": messages,
            "stream": False
        })
        reply = response.json()["message"]["content"].strip()

        try:
            tool_call = json.loads(reply)
            tool_name = tool_call["tool"]
            args = tool_call.get("args", {})

            if tool_name in TOOLS:
                result = TOOLS[tool_name](**args)
                messages.append({"role": "assistant", "content": reply})
                messages.append({"role": "user", "content": f"Tool result: {result}"})
                continue

        except (json.JSONDecodeError, KeyError):
            # Model responded in plain text — task is done
            print("Final answer:", reply)
            return reply

    print("Max iterations reached.")

run_agent("Research the latest Elden Ring patch notes and save a summary to my desktop.")
Enter fullscreen mode Exit fullscreen mode

This is the bridge. Not complex — but powerful. Once you have this pattern, you can add any tool: database lookups, calendar access, code execution, anything.


The Distillation Bonus

There's a second force making this even more interesting right now.

Larger models are being used to train smaller ones — a process called distillation. The practical result: we are approaching a point where the reasoning capability of a trillion-parameter model gets compressed into 7 billion weights.

Models like DeepSeek-R1, Qwen3, and Mistral's latest releases are examples of this trend. The gap between a "small" local model and a frontier cloud model is shrinking every quarter.

This creates a compounding advantage for the local orchestration approach:

  • The model gets smarter (distillation brings frontier reasoning to edge hardware)
  • The system gives it agency (orchestration adds tools and memory)
  • Your data stays local (the privacy moat stays intact)

You get increasing capability without increasing your exposure. That's a rare combination.


The Practical Takeaway

Stop searching for a smarter model. The model is rarely your bottleneck.

The three things that actually determine usefulness are:

1. Web access — A model without current information is operating blind. Add a search tool. Even a free SearXNG instance changes everything.

2. Memory / context persistence — By default, each conversation starts from zero. A simple vector store (Chroma, Qdrant) or even a plain text log fed back into context gives your model continuity.

3. File system / execution access — The ability to write, read, and run code transforms the model from an advisor into an agent.

These are not advanced features. Each one is a weekend project. And together, they close most of the gap between a local 8B model and a productized cloud API.


Closing: The Race Has Already Shifted

The AI competition used to be about who could train the biggest model. That race is becoming irrelevant for most practical use cases.

The new race is about who builds the best system around the most efficient model.

Cloud providers understood this first — that's why their APIs feel so capable. But the tools to replicate that architecture locally are open, documented, and running on consumer hardware right now.

You don't need a bigger model.

You need better plumbing.


If you want to go deeper on the self-hosting stack itself — Ollama, Open WebUI, and SearXNG working together — I covered the full setup in Part 1 of this series.

Top comments (0)