DEV Community

Cover image for Directed Creation: What a 3B Model Can Actually Do to My Files
Tariq Davis
Tariq Davis

Posted on

Directed Creation: What a 3B Model Can Actually Do to My Files

This started as an abstract way I think about AI. I wanted to manipulate files inside my own local system, on my own PC, with no internet and no API key. And look, agents far more advanced and more useful than what I built here already exist. I know that. But this is free, it's easy to make through vibe coding, it runs on my own machine, and it does exactly the narrow thing I want. That's the whole trade.

So I built a small tool around a 3-billion-parameter model running locally through Ollama and wired it to touch my files directly. This is the honest report: what it does well, exactly where it falls on its face, and the one rule that keeps the whole thing safe to use.

The model is qwen2.5-coder:3b. For scale, the models you read about have hundreds of billions of parameters. This one is small enough to run on a laptop and answer in a second or two. That smallness is the whole point, and the whole constraint. The script is even called vibe.py, because that's what it is: vibe coded, on purpose, into something specific.

The design decision that matters most

Here's the tool's core. It's deliberately not agentic:

def cmd_edit(args):
    with open(args.file, "r") as f:
        original = f.read()

    prompt = f"""Here is the full content of {args.file}:

{original}

Instruction: {args.instruction}

Output ONLY the complete new file content. No explanation, no markdown fences."""

    new_content = strip_fences(ask_model(prompt))

    if not show_diff(original, new_content, args.file):
        return

    if input("\nApply this change? (y/n): ").strip().lower() == "y":
        with open(args.file, "w") as f:
            f.write(new_content)
        print("Applied.")
    else:
        print("Discarded — file untouched.")
Enter fullscreen mode Exit fullscreen mode

Read what it does, and especially what it refuses to do. It reads one file. It asks the model for the entire file back, rewritten. It shows me a diff. And it does not write a single byte until I type y.

No tool-calling. No agent deciding to modify six files on its own. One file in, one diff out, one human approval. I made that choice on purpose, and the rest of this post is why it was the right one.

There's a hidden cost in "give me the entire file back": the model has to reproduce every line it isn't changing, perfectly, or it quietly corrupts the file. For a small model that reproduction is exactly where the risk lives. Hold that thought, we walk straight into it.

This tool isn't alone, it's one rule from a whole system

vibe.py didn't come from nowhere. It's one script in a personal system I've been building across two WSL2 machines, wired together in VS Code with the extensions, the connections between the machines, private GitHub hubs for organization, and a stack of small scripts that each do one precise thing. vibe.py is one of those scripts. This demo is really about it working through the others, to set up a Dev.to target from a selected file inside the system.

I won't lay the whole thing out here, that's its own post, and I'm building a full demo of how the dual-machine setup fits together. But the one idea worth taking is the reason vibe.py is shaped the way it is. I call it directed creation.

Directed creation means this: I let AI do the work, not the thinking or the creating. Yes, you can vibe code. But you have to be very focused on what you're vibe coding, testing, and iterating toward. The model doesn't decide what gets built or whether it's right. It does the labor once I've decided the shape. The moment you let it do the thinking too, you're not directing anymore, you're just hoping.

vibe.py is that idea compressed into one script. The model does the rote part: read the file, propose the whole rewrite. It never reaches the part that matters: deciding the change is correct and writing it to disk. That's mine. The y/n prompt is the exact line where the labor ends and the direction begins.

So when you watch the tests below, you're not watching a tool being clever. You're watching that line get held.

Test one: does it even do the simple thing?

Before trusting a local model with anything real, I throw it a warmup. The tool has a read-only explain command that runs the same model path as edit without touching disk. If it hallucinates on the warmup, I stop and reach for a real model instead.

It passed clean. Read the actual file, summarized the actual commands, invented nothing.

Then the simplest possible edit: add a docstring to a two-line function.

The diff was perfectly scoped. Kept the signature, kept the body, dropped the docstring in between and nothing else. This is squarely inside what it's good at. The model isn't reasoning here, it's decorating, and small models decorate fine.

A clean diff is a claim though, not a fact. The tool prints Applied., that's the tool's word for it. So the habit, every time, is to cat the file and look. The claim and the disk have to agree, and the only way to know is to check. Hold onto that, it becomes the whole point at the end.

Test two: a real fix, slightly harder

A docstring is free. So I gave it something with an actual bug:

def apply_discount(price, percent):
    return price - price * percent
Enter fullscreen mode Exit fullscreen mode

The bug: it's meant to take percent as a whole number, 20 for 20% off, but it treats 20 as 2000%. I asked the tool to fix exactly that.

This one's a real result, because the model had to get the intent, that "20 means twenty percent", and turn it into the right math, while leaving the function shape alone. A small model can do this. This is the useful part, and it's genuinely useful.

Test three: walking into the ceiling on purpose

Now the interesting part. I gave it an instruction I knew was underspecified:

make it safe

That's not a real spec. Safe against what? Negative prices? A percent over 100? A non-number input? I didn't say, on purpose. I wanted to see what it does when I haven't drawn the line for it.

I ran it three times. Same three words each time. Here's what came back.

Run one produced this, and it looked sophisticated. It reached for Python's Decimal type "for precise floating point arithmetic", exactly the kind of phrase that makes you nod and hit yes:

I approved it. Y. It looked careful, it looked like it knew what it was doing.

It was broken. Watch:

The line decimal.Decimal(str(percent) + '/100') builds the string "20/100" and hands it to Decimal(). But Decimal doesn't do division, it tries to read "20/100" as one number, fails, and throws InvalidOperation. The "safe" version crashes on every single call. And I'd already said yes, because the diff looked competent.

That's the whole lesson of this post in one screenshot. A small model will hand you confident, well-commented code that doesn't work, and if you're skimming, you'll wave it through.

Run three, same instruction, gave me something completely different, and this time actually good:

Type checks, a negative-price guard, a cap at 100%. Genuinely more defensible than my one-liner. And I rejected it. N.

Why reject the good one? Look closely: it quietly renamed the parameter percent to discount_percent. Anything calling apply_discount(price, percent=20) by keyword breaks. It improved the inside of the function and silently changed the interface, a real cost buried in a good-looking diff. Saying no kept my one working line intact:

So: three runs, three answers. One that crashes, one I wrongly approved, one that was solid but I rightly turned down. It was never consistent. The only constant in the whole test was me reading the diff. That's not a flaw in the tool, that's the tool doing exactly what it's built to do. It proposes, endlessly and unpredictably. I decide, every single time.

The y/n prompt isn't a convenience. It's the entire safety model. The model can propose anything. It can change nothing.

Test four: what it thinks it's looking at

One more, a different kind of ceiling. The tool can read a folder's structure and summarize what the project does. No files opened, just the shape. I pointed it at a real project of mine and asked.

The folder is a security awareness-check tool. The model read awareness-check, recipients.txt, a couple of scripts, and confidently decided it was about "emotional intelligence or positive psychology", with "surveys, quizzes", maybe "analyzing facial expressions". Completely wrong, and completely fluent about being wrong.

That's the ceiling in its purest form. It saw a shape and pattern-matched it into a plausible story, with zero signal the story was made up. On structure it's never seen, a small model doesn't say "I'm not sure". It guesses, confidently, and the guess reads exactly like knowledge. That's the failure you have to keep in your head every time it tells you something you can't immediately check.

Where the floor actually is

After using this for real, here's the honest map of a 3B model with direct file access.

It's reliable for:

  • scoped edits to small files: docstrings, renames, a contained bugfix, reformatting.
  • anything where the change is local and the file is small enough to reproduce perfectly.

It is not reliable for:

  • regenerating large files. Every line it has to reproduce unchanged is a line it can quietly drop or alter. The bigger the file, the higher the odds of silent corruption.
  • summarizing anything it can't cross-check. It reads a shape and tells a confident story, and the story can be flat wrong (test four) with no tell that it's guessing.
  • anything multi-step or autonomous. Small models are fine at responding. They are not fine at deciding.
  • being trusted without a look. Ever.

That last one is the rule the whole tool is built around, and it's the thing I'd tell anyone wiring a local model to their files:

A local model's output is a draft, never a verdict. The diff is what it claims. The file on disk is what's true. Always check the second one.

Every stage of every test above ended with a cat, reading the actual file, not trusting the tool's Applied.. That's not paranoia. Small models have shown me a clean diff and then not written it. The check is cheap. The corruption isn't.

So what do I actually use it for

Honestly? It's flawed, but it's not bad. Once you give it specific instructions the prompt attempts are decent, and that's the whole trick, be specific. Where it earns its place is the small stuff: editing or analyzing reports, journals, markdown or text files, or just adding some data to a file by asking vibe to do it instead of typing it in myself. And if I'd rather type it manually, I do. The tool doesn't take the wheel, it just saves me the drive when I don't feel like driving.

That's the honest scope. It's not writing my software. It's one adaptive, genuinely useful piece of the larger system I've built, and it's free. Pretty cool, honestly. And there's something fun about watching these raw little scripts connect and mash together and just work.

Why the small model is the point, not the compromise

I could pipe all of this to a frontier model and get a smarter result. Sometimes I do, for real reasoning, the kind of thing where being wrong is expensive. I reach for the bigger tool then.

But the small local model gives me something the big one can't. It runs offline, it costs nothing, it touches only my machine, and it's fast enough to sit inside my actual workflow instead of interrupting it. The trick was never making it smarter. It's knowing exactly where it stops being good, building the tool so it can't act past that line on its own, and staying the one who decides at every write.

That's directed creation, and it's bigger than local models. The tool isn't valuable because the AI is good. It's valuable because I know precisely where the AI stops being good, and I drew the line there myself instead of hoping the model would stay inside a line nobody drew.

I let it do the work. Not the thinking.


This tool is one piece of a two-machine setup I built on WSL2, one side for building, one for security work, the same local-AI layer wired into both. I'm putting together a full walkthrough of how it fits together. If that's something you'd want to read, follow and it'll land here.


Check out my website: tagzauthor.com
Support TagzAuthor: ko-fi.com/tagzauthor
My author page: Amazon Bookstore

Top comments (0)