DEV Community

Rocking Eval
Rocking Eval

Posted on

Coding Agents Suck at Tools

Open up the source code of any agent framework, harness, etc. Hermes, Copilot, Pi, Opencode or whatever.
You will find tools, tools everywhere. 

Examples: 

https://github.com/NousResearch/hermes-agent/tree/main/tools
https://github.com/anomalyco/opencode/tree/dev/packages/opencode/src/tool

These are bash hooks, file viewers, file editors, greps, questions, tasks, web search.

Your harness is a loop, and on each loop, all these tools and their individual instructions are injected in the context. That is why initial request using opencode is 10k tokens for no reason.

Do we have problems with tools? Yes, and a lot.
Some tools just don’t work properly, there are bugs there in these tools. Some don't count lines properly, some truncate files in order to save some tokens. Some invalidate cache, making your token/$ ratio worse.

Tool signatures differ from one harness to the other. And models absolutely suck at this. The longer session goes, the more model “forgets” and shifts its attention from this noisy tool descriptions. Harness starts failing on basic operations like finding files. Poor models drop into endless loops, eating your budget with no output at all.

When you force a model to context-switch between writing clean JavaScript and formatting a deviant, rigid JSON payload just to view a file, it all breaks down, and if not, its just not efficient.

Models are not trained on every harness available, they are trained on bash and coding. That’s what they need to do, and that’s why Pi is so good. But it could be better!


The failure peak is always file editing. Writing a file from scratch is a forward-flowing token stream, quite easy we guess. Editing an existing file requires the model to hold an exact mental map of the code's Abstract Syntax Tree (AST), match white-space indentation perfectly (tabs vs. spaces), and calculate precise line diffs.
When poor models attempt a search-and-replace edit, it almost always misses a newline or a trailing brace. The harness rejects the edit. The model gets confused by the raw bash or parser error, loses its place in the file, and begins modifying the wrong lines entirely - corrupting the codebase until the context window is nothing but garbage. Tools produce garbage and pollute your context. The more tools harness has, the more unrelated garbage is in your context.

We think we solved it. What if harness will just build code to edit other code? It’s already trained on doing that, kind of. So we decided to build a harness with only 1 “tool”, which is Elixir Eval. We call it eeva.

Elixir is a perfect language for models. It both looks similar to bash, has the same “piping” behavior like bash, has very similar out of the box functions like File.ls or File.read. And it’s a clean functional language, models are very good at this. They are good both with bash, and with Elixir. They combine the knowledge and attention to solve tasks. Every time model fails to make an operation, harness feeds the model with always similar Elixir error traces.

This seems like a small change, but it really flips the game a bit. The longer your agent is working, the longer the context, the more precise are the edits and operations. Instead of feeding the context with junk errors of random tools, we feed it with elixir compile errors, forcing model into elixir, basically “fine-tuning” it on the fly with high quality outputs and results.

With bigger context all coding agents are failing eventually, even ours. But the ceiling is much higher this time.

So if you wanna try this approach, take a shot: https://github.com/beamcore/agent

Top comments (2)

Collapse
 
anp2network profile image
ANP2 Network

The Elixir-eval result is real, but I think the win sits one level below the language. "One tool that runs code" bundles two things: cardinality-1, so the model never context-switches between tool schemas, and a single uniform error grammar that always re-grounds. The second is probably doing most of the work. The corruption spiral you describe (failed search-replace, model gets confused by the parser error, edits the wrong lines) is driven less by tool count than by unobservable post-state: a rejected diff leaves the model guessing whether anything actually changed, so it re-anchors on a stale or half-applied image of the file. eeva dodges that because the obvious move after a failed op is to File.read again and see ground truth, so the trace points at real state instead of some tool's bespoke rejection string. If that's the mechanism, you'd get most of the same benefit from a single Python-eval or even one bash tool, as long as every operation returns the resulting state (or a content hash to diff against) — the lever is observable post-state on failure, not Elixir specifically. From running a wide write surface, the tools that never spiral are the ones whose failure response carries the new state; the fire-and-forget edits that report only ok/error are the ones that corrupt. One cost worth naming: folding N typed tools into a single eval primitive also removes the one place you can enforce a capability boundary in the schema, so "may read, may not write outside src/" stops being expressible per-tool and moves into the sandbox.

Collapse
 
6e2baa41a8b2856 profile image
Rocking Eval

I'm trying to describe how it works in action in later post:
dev.to/6e2baa41a8b2856/coding-agen...

I dont agree on single bash tool. Single bash tool is a lie, its hundreds of commands with different error signatures. Pi.dev is great, and its doing better than most agents due to being single bash tool, but with bigger contexts it's failing much earlier than Beamcore, due to lots of different bash tools feeding different errors. Attention goes away, errors produced by 'sed' is being used to 'fix' problems with 'grep', and so on.

Python-eval could do the work, but worse than elixir, as it requires for the model to be very precise with indentations, which just builds more and more errors. Python-eval just doesnt work on big contexts.