# What If Your AI Agent Doesn't Need Better Prompts — Just Better Tools?

> Source: <https://dev.to/aninmukhe/what-if-your-ai-agent-doesnt-need-better-prompts-just-better-tools-5ba7>
> Published: 2026-08-31 11:10:56+00:00

I rewrote the system prompt fourteen times.

Version 3 was "be thorough." Version 7 was a 900-word manifesto about edge cases. Version 12 threatened the model if it hallucinated a file path. Version 14 still booked a meeting on the wrong calendar and cheerfully summarized the disaster.

The agent wasn't dumb. It was *unarmed*.

If you've been stuck in prompt-tweaking hell with an AI agent that keeps almost-working, this is the post I wish I'd read first. The fix usually isn't another paragraph of instructions. It's giving the thing a real tool.

A prompt is a pep talk before a job interview. A tool is the résumé, the laptop, and the door badge.

You can coach someone forever on "be professional and double-check details." If they can't open the calendar API, they will invent a meeting time that sounds right. LLMs are the same — world-class at *sounding* competent, terrible at *touching* the real world unless you wire them up.

That's the agentic AI shift in one sentence: stop asking the model to *pretend* it did something. Give it a function it can actually call.

My agent had one job: when a GitHub issue is labeled `needs-repro`

, clone the repo, run the failing test, and paste the output back.

I tried better prompts first — "always clone before you speculate," "never invent stack traces," "if you're unsure, say so." It invented a beautifully formatted stack trace from a file that didn't exist. From the model's point of view, a plausible stack trace *is* the assignment when the only tool it has is "generate text."

Then I registered three boring tools: `clone_repo(url)`

, `run_tests(path)`

, `comment_on_issue(number, body)`

. No poetry. Next run: clone → run → paste real output. The intelligence didn't jump. The *surface area of reality* did.

``` python
import json, subprocess
from dataclasses import dataclass
from typing import Callable

@dataclass
class Tool:
    name: str
    description: str
    schema: dict
    handler: Callable[[dict], str]

def run_tests(args: dict) -> str:
    path = args["path"]
    # Never let the model shell out raw — pin the command.
    result = subprocess.run(
        ["pytest", path, "-q"],
        capture_output=True, text=True, timeout=120
    )
    return (result.stdout or result.stderr)[-4000:]

TOOLS = {
    "run_tests": Tool(
        name="run_tests",
        description="Run pytest on a path; return the tail of output.",
        schema={
            "type": "object",
            "properties": {"path": {"type": "string"}},
            "required": ["path"],
        },
        handler=run_tests,
    ),
}

def tools_for_llm() -> list[dict]:
    """Send names/descriptions/schemas — not handlers."""
    return [{
        "type": "function",
        "function": {
            "name": t.name,
            "description": t.description,
            "parameters": t.schema,
        },
    } for t in TOOLS.values()]

def dispatch(tool_name: str, arguments_json: str) -> str:
    tool = TOOLS[tool_name]
    return tool.handler(json.loads(arguments_json))

# Loop: messages + tools_for_llm()
# tool_call -> dispatch() -> append result -> repeat
# stop on normal message or max-steps guard
```

Notice what the prompt no longer has to say: "please don't invent pytest output." The model can't invent what `run_tests`

already returned. Reality is a function return value, not a writing style.

**1. If the model must touch the world, it needs a tool.**

Email, APIs, files, tests, databases — tools, not adjectives in a system prompt.

**2. Prefer boring, narrow tools over one god-tool.**

`run_tests(path)`

beats `do_whatever(shell_command)`

. Narrow tools are easier to log and sandbox. God-tools are how you wake up to a deleted directory and a polite apology.

**3. Put constraints in code, not in prose.**

Timeouts, allow-lists, max bytes, path sandboxes belong in the handler. The model is a creative writer under deadline. Your handler is the adult in the room.

**4. Log tool calls like production traffic.**

Name, args, duration, success/fail. When an agent goes weird, you won't debug prompt vibes — you'll debug the trace: *it called clone_repo with a typo URL three times.*

Imagine a pizza shop where the only employee is a poet. Hand them a long script about greeting customers and using the oven. Without an oven dial, a ticket printer, and a delivery map, you don't have a pizza shop — you have spoken-word night with cheese anxiety.

Agents are the same. Prompting is coaching the poet. Tool-use is installing the oven. Multi-agent setups multiply the moral: researchers need search, coders need file/test tools, reviewers need diffs. More agents with no tools is just a group chat with confidence issues.

Prompts are great at role ("prefer failing closed"), output shape ("short summary after tools"), and tie-breaking ("prefer the read-only tool"). They're bad at physics. "Don't hallucinate test output" is physics. Solve physics with a tool.

Pick one task your current bot fakes:

`fetch_invoice`

, `list_open_prs`

, `tail_logs`

).If success jumps, you didn't find a better spell. You handed the wizard a screwdriver.

Agentic AI feels magical in demos because demos hide the plumbing. In production, the magic is mostly **tool surface area + boring guardrails + a short honest prompt**.

Stop asking your agent to role-play competence. Give it hands. Keep the prompt short enough that you can still read it without scrolling past your own despair.

**Your turn:** What's one tool you wish your agent had yesterday — the specific function, not a vibe? Drop the name and two args in the comments. I'm collecting a hall-of-fame of "why didn't I add this sooner" tools.
