Jeison Eccel

What Happens When You Give a Small Local Model Tools?

A local model answering a coding question can be wrong without causing much damage.

I read the answer, notice the problem, and ignore it.

An agent changes the equation. Once the same model can edit files, interact with Git, and potentially execute terminal commands, a bad answer can become a bad action.

After selecting Aider as the agent layer, I want to find out how much of this loop actually works:

Agent Loop

And, perhaps more importantly:

What happens when the model gets one of those steps wrong?

Start with basic tools

I begin with Qwen2.5-Coder 7B and the same small repository from the previous tests.

The setup is:

Agent: Aider 0.86.2
Model: Qwen2.5-Coder 7B
Provider: Ollama
Edit format: whole
Git auto-commits: disabled

The repository contains two simple Java classes: Calculator and GreetingService.

I start with file access.

First, I explicitly give Aider Calculator.java:

/add Calculator.java

Then I ask:

Read Calculator.java and tell me exactly which methods it contains
and what each method returns.

Do not modify the file.

Qwen correctly identified add and multiply.

This was already better than relying only on Aider's repository map, where the model had previously invented plausible methods such as subtract and divide.

Full source context helped.

The read-only behavior was less convincing. Qwen still produced edit-oriented output even though I explicitly asked it not to modify the file. Git showed no actual source change, but the model was already blurring the line between answering and editing.

Repository search works too

Next, I remove Calculator.java from the explicit context:

/drop Calculator.java

And ask:

Find where the string "Hello, " is used in this repository.

Tell me the file, method, and line containing it.
Do not modify any files.

Qwen correctly found:

GreetingService.java
greet(String name)

and the exact code:

return "Hello, " + name + "!";

The target file wasn't manually added to the chat, so Aider's repository context was doing useful work.

But Qwen again produced edit-oriented output.

This time, Aider asked for permission before allowing a modification to a file that wasn't explicitly added for editing.

I rejected it.

Nothing changed.

The permission prompts are becoming more important as the model gets access to more capabilities.

Let the model make a real edit

Reading is useful, but now I want the model to do something.

I add Calculator.java and ask:

Add a subtract method to Calculator.java.

Requirements:
- Accept two int parameters named first and second.
- Return first - second.
- Do not modify any existing methods.
- Do not create any files.

Qwen added exactly what I wanted:

public int subtract(int first, int second) {
    return first - second;
}

The existing methods stayed unchanged.

At this stage, the combination is starting to look useful:

CapabilityResult
Read source✅
Find code✅
Understand explicit file context✅
Make a controlled edit✅
Respect read-only requests⚠️

Then I ask it to compile the code.

One command destroys the source file

The prompt is simple:

Compile Calculator.java with javac and tell me whether compilation succeeds.
Do not make any additional code changes.

What I expect is:

Aider
  ↓
javac Calculator.java
  ↓
compiler result
  ↓
model reports result

Qwen correctly knew which command should run:

javac Calculator.java

But it didn't execute it.

Instead, the command went through Aider's whole-file edit protocol.

Aider interpreted:

javac Calculator.java

as the new contents of Calculator.java.

The resulting diff was effectively:

-public class Calculator {
-    ...
-}
+javac Calculator.java

A request to compile the source had replaced the source with the compilation command.

This was the first failure in the experiment where giving the model tools made the mistake materially worse.

Without tools:

Wrong answer
    ↓
I ignore it

With file access:

Wrong output
    ↓
Harness interprets it as an edit
    ↓
Source file overwritten

Git made the problem immediately visible, and automatic commits were disabled, so recovery was easy.

Still, this is exactly the kind of behavior I need to understand before trusting a local model with a larger project.

Is Qwen 7B simply too small?

One bad result doesn't tell me whether the problem comes from Aider, the model, the edit format, or some combination of them.

So I repeat the important parts with the other candidates.

Qwen2.5-Coder 14B produced the same dangerous result. It also replaced the Java source with the shell command instead of executing it.

Moving from 7B to 14B didn't solve the problem.

Ministral 3 8B behaved differently.

It recognized that compilation required:

javac Calculator.java

but said it couldn't execute the command.

It didn't destroy the existing source.

gpt-oss 20B also avoided the destructive edit, but introduced another problem: it inspected the code and reported that compilation succeeded without actually running javac.

The tests produced three very different failure modes:

ModelCompile request
Qwen 7B🔴 Replaced source with shell command
Qwen 14B🔴 Replaced source with shell command
Ministral 8B⚠️ Correctly admitted it didn't execute
gpt-oss 20B⚠️ Claimed success without execution

None actually ran the compiler.

But Ministral failed in the safest way.

It knew what needed to happen, admitted it hadn't happened, and left the source intact.

For an agent, admitting "I didn't do it" is considerably more useful than confidently pretending it did.

Ministral becomes the next candidate

The difference isn't limited to the compilation test.

I repeat the earlier file-reading, repository-search, and controlled-edit tests with Ministral.

Both Qwen 7B and Ministral understood the code and successfully performed the requested source edit.

Ministral was more disciplined during read-only tasks and interacted more safely with Aider's edit format.

Combined with the local performance results from the earlier model benchmarks, these tests make Ministral the best candidate for continuing the experiment:

Aider
  ↓
Ollama
  ↓
Ministral 3 8B

But I still haven't demonstrated the complete agent loop.

None of the models independently executed the terminal command.

Before blaming the models, I need to understand whether I'm asking Aider to use its tools correctly.

Aider can run commands, just not the way I expected

Aider has an explicit /run command.

So instead of telling the model:

Compile Calculator.java with javac.

I invoke the tool directly:

/run javac Calculator.java

This time, javac actually ran.

The distinction became important:

Natural-language request
        ↓
Model generates response
        ↓
No reliable shell execution

versus:

/run javac Calculator.java
        ↓
Aider executes command
        ↓
Real compiler output

The test confirmed that the terminal capability exists.

What I haven't demonstrated is autonomous tool selection: the model deciding for itself that it needs to compile the code and invoking the appropriate tool.

I can still test the rest of the loop manually.

Create a failure on purpose

I reset Calculator.java and give Ministral a controlled task:

Add a divide method to Calculator.java.

Requirements:
- Accept two int parameters named first and second.
- Return first / second.
- Intentionally omit the semicolon from the return statement.
- Do not modify the existing methods.
- Do not create any additional files.

The intended error is:

public int divide(int first, int second) {
    return first / second
}

Ministral added the method with the missing semicolon.

But it also accidentally removed the semicolon from the existing multiply method:

public int multiply(int first, int second) {
    return first * second
}

public int divide(int first, int second) {
    return first / second
}

Even the safer model had now violated a clear boundary:

Do not modify the existing methods.

Aider's lint mechanism immediately detected both syntax problems and offered to fix them.

I reject the automatic repair because I want to see whether the rest of the loop works.

Give the model real compiler output

Now I run:

/run javac Calculator.java

The compiler returned:

Calculator.java:8: error: ';' expected
        return first * second
                             ^

Calculator.java:12: error: ';' expected
        return first / second
                             ^

2 errors

Aider added the compiler output to the model's context.

Now I ask:

Explain why the compilation failed.
Do not modify the file yet.

Ministral correctly identified both missing semicolons.

More importantly, it was reasoning from a real compiler result, not guessing from the source.

Then I ask:

Fix the compilation error in Calculator.java.
Make only the minimum required change.

Ministral restored exactly the two semicolons.

It made no additional changes.

Run it again

I execute:

/run javac Calculator.java

The compiler produced no errors.

Successful javac execution is silent, which created a small grounding problem. Aider added zero lines of output to the chat rather than giving the model an explicit successful exit code.

When I asked Ministral whether compilation had succeeded without showing it the result, it correctly refused to claim that it knew.

I ran the command again and added the zero-line result to the context. Ministral then interpreted the lack of compiler errors as successful compilation.

I also verify outside Aider that:

Calculator.class

exists.

Git provides the final check.

The source diff contains only the feature I actually wanted:

 public int multiply(int first, int second) {
     return first * second;
 }

+public int divide(int first, int second) {
+    return first / second;
+}

The accidental modification to multiply had been repaired.

No automatic commit was created.

The loop works, with a human in the middle

For the first time in the experiment, I've completed the full loop:

Inspect          ✅
   ↓
Modify           ✅
   ↓
Run              ✅
   ↓
Observe          ✅
   ↓
Correct          ✅
   ↓
Re-run           ✅

There is an important asterisk beside it.

I explicitly invoked:

/run javac Calculator.java

The model didn't decide to use the terminal on its own.

So I haven't built an autonomous coding agent yet.

What I have is a human-guided agentic loop:

Model inspects code
        ↓
Model edits code
        ↓
Human invokes tool
        ↓
Aider executes command
        ↓
Model receives real output
        ↓
Model diagnoses failure
        ↓
Model repairs code
        ↓
Human invokes tool again

It's more capable than a coding chatbot, but less autonomous than the agent architecture I'm aiming for.

Tools amplify both capability and mistakes

The experiment answers the question I started with in an uncomfortable way.

Giving the model tools absolutely makes it more useful.

The local model can now:

  • understand repository context;
  • edit real source files;
  • receive actual compiler errors;
  • reason about those errors;
  • repair the source;
  • participate in a real build-feedback loop.

But the tests also showed how tools increase the cost of mistakes.

Even in this tiny repository, I saw:

Hallucinated code
Unrequested edits
Ignored read-only instructions
Source-file corruption
False execution claims
Accidental changes to existing methods

The tools aren't making the model smarter.

They're giving its outputs consequences.

A weak response in chat is annoying. A weak response interpreted as a file operation can overwrite working code.

The safety layers matter

The experiment also makes the safety architecture much less theoretical.

My useful safeguards are:

Model instructions
        ↓
Aider permissions
        ↓
Git diff
        ↓
Human review

Automatic commits remain disabled.

Git remains the source of truth for what actually changed.

Permission prompts can prevent some inappropriate edits from reaching files the model hasn't been explicitly allowed to modify.

And I still review the result.

The model isn't reliable enough for any one of those layers to disappear yet.

The combination I carry forward

After comparing the models inside Aider, the local configuration I'll continue testing is:

Agent: Aider 0.86.2
Model: Ministral 3 8B
Provider: Ollama
Execution: Local
Git auto-commits: Disabled

Ministral isn't perfect. It modified code I explicitly told it not to touch during these tests.

But among the models tested, it provided the best balance of local performance, coding ability, instruction following, and safer interaction with Aider.

More importantly, Aider + Ministral has now demonstrated something concrete:

A small local model can participate in a real inspect → edit → compile → diagnose → repair loop on my M2 Mac.

It just can't manage the entire loop independently yet.

The next step will be to stop testing two tiny Java files and connect this setup to a real project.

Repository context will get much harder when Calculator.java becomes controllers, services, repositories, entities, shared abstractions, tests, configuration, and dependencies spread across a real Spring Boot application.