AI & ML
Your system prompt isn't instructions. It's data.
Nathan C. DEV Community
1 views
My system prompt had an example of a good Slack message in it. It opened with "Morning all, quick one:".
The model started opening real Slack drafts with that exact phrase. Then it started saying "Morning." when I typed "hey", which is a small lie, because it cannot see a clock.
So I added a rule telling it not to reuse examples from its own instructions. Three rebuilds. No change.
Then I deleted the phrase. Fixed on the next build.
That is when it clicked. The model does not read your system prompt as a list of instructions. It reads it as text that is likely to appear near its own output. Every finding below falls out of that one idea.
The four rules I now write prompts by
If a phrase must not appear in the output, it must not appear in the prompt. Banning it does not work. Deleting it does.
Naming a bad example summons it. "Not the bank balance one" is an excellent way to get the bank balance one.
Position beats wording. A rule buried mid-section gets read and traded away. The same words at the top of that section hold.
Concrete beats principled. "Call fsync() before the rename" lands immediately. "Describe only the guarantee the code actually makes" does nothing.
And the one that saved me the most time after it cost me the most time: verify on three seeds before you believe any of it.
Here is the evidence for each.
The setup
Flash Onyx is the model line behind Flash, my local agent shell. There is no fine-tuning involved. Onyx is a base model plus a system prompt that has grown to roughly 680 lines, built into an Ollama tag with a small script:
python3 models/build.py models/flash-onyx-2.5.Modelfile --size 31b-cloudbase -n Natuworkguy
2.5 is the version where I stopped editing that prompt by feel.
The loop is not clever: edit the prompt, rebuild the tag, run a fixed set of prompts at pinned seeds, read the output, decide whether anything actually changed. Seeds are pinned so two runs are comparable. That is the entire method, and it is the difference between "this reads better to me" and "this went from failing on three seeds to passing on three seeds".
1. Your examples are not examples. They are samples.
The Slack line was the small version. Here is the expensive one.
While fixing how Onyx explains things, I gave it a demonstration answer for "what is a deadlock", complete with two functions taking locks in opposite orders. Onyx pasted that answer back word for word, invented function names included.
Then the opener from that demo started showing up as the answer to "what is a race condition".
Which is a different concept. A style demonstration had turned into a correctness bug.
Demonstrations are still the most powerful tool in the box. They just have to be shaped so that a verbatim paste is either harmless or impossible. If I quote a full answer, it is now for a question nobody asks, and anything I actually want copied gets quoted in fragments the model has to assemble.
2. Six rebuilds on a bug that never existed
I spent six revisions trying to stop Onyx answering "explain what a race condition is" with a textbook lecture: definition sentence, numbered trace with two threads, closing line about locks. Nothing I wrote moved it.
Then I ran the same prompt at three other seeds.
All three had been clean prose for most of those six revisions. Seed 7 was an outlier, and I had been rewriting rules that already worked.
Pinned seeds make runs reproducible, which is the point of them. They also make a single unlucky sample look exactly like a deterministic rule failure. Three seeds before touching anything, every time now.
3. Moving a line fixed what four rewrites couldn't
The prompt tells Onyx to target Python 3.9, because that is what a Mac hands you as python3 by default. It kept writing str | Path annotations, which raise a TypeError on 3.9.
I rewrote that rule four times. I stripped every pipe union out of the prompt in case they were priming it. I added a correct example signature to copy.
Every seed, every build: str | Path.
The rule was fourteen lines into the PYTHON section. I moved the same words, unchanged in meaning, into that section's opening lines.
Fixed on all three seeds immediately.
I have since watched this happen twice more. A rule that sets the frame for a whole section has to be at the top of that section, or the model reads it and treats it as a detail it can trade away later. When a rule fails repeatedly, move it before you rewrite it again.
4. The principle did nothing. The two function names fixed it.
Two edits went into the same build. One said the guarantee described in a reply has to be the guarantee the code actually makes. The other said, in effect, write flush() and fsync() before the rename.
Same build, same seeds. The concrete one landed instantly. The principle did nothing at all.
Onyx had been writing an atomic-save function that wrote a temp file, renamed it, and told the user a crash could not truncate their config. Without the sync, that promise is not true. Naming the two calls fixed both the code and the claim about it.
Stop reading generated code. Run it.
Judging generated code by reading it does not scale, and it flatters the model. So the coding eval extracts the fenced block out of each reply, writes it to a file with my own tests appended, and executes it. Pass or fail. No opinion involved.
The regression suite is 23 tasks and Onyx passes all of them on two seeds. Then I built a harder set to find the edge, and it went 6/8 on the first attempt, including things I did not expect from 31B:
full semver precedence, so 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0
weighted interval scheduling across 40,000 jobs inside a time bound
a minimal LCS-based line diff
a thread pool returning results in input order, propagating the first exception, leaking no threads
Where the ceiling actually is
The one it cannot do is an expression parser. Precedence, parentheses, unary minus, reject anything malformed.
It fails on every seed, and it fails differently each time: accepts 1 2, then accepts 1++2, then rejects valid input, then returns a wrong answer. I put those exact failing inputs into the prompt. It still shipped code that accepts them.
That is not a prompting problem. Holding a complete validation invariant across sixty lines of recursive descent is a capability, and no wording buys it.
I made three attempts, then deleted the parser-specific lines rather than keep paying tokens for a rule that does not work. Knowing which failures are yours to fix and which belong to the weights is most of what this loop is for.
My tests were wrong twice
Worth saying out loud, because eval code is code.
I asserted that inserting 4 into [1,2,2,2,3] gives index 4. It gives 5. Onyx's binary search was right and my expectation was wrong.
Then I wrote a sliding window limiter test where three calls all happen at t=1000, and asserted that only one slot frees up ten seconds later. All three age out together, obviously, the moment you look at it. Onyx was right again.
Both times the harness said FAIL and the model was correct. If your eval has never been wrong, you have not looked closely at a failure yet.
Try this on your own prompt
If you take one thing from this, take the cheapest experiment in it.
Open your system prompt and find the rule you have rewritten the most times. The one that never quite sticks. Do not rewrite it a fifth time.
Move it to the top of its section. Change nothing else. Rebuild, and run it at three different seeds.
Then tell me in the comments which it was: wording, or position. I have only proven this on gemma4 weights, three times, in one prompt. That is a finding, not a law, and I want to know whether it holds anywhere else. If it fails for you, that is the more interesting comment.
The whole thing is open: Flash on GitHub, Modelfiles included, so you can read the 680 lines and tell me which of them are load-bearing. Half the value of publishing a prompt is finding out which parts you were wrong about.
Read original: https://dev.to/natuworkguy/your-system-prompt-isnt-instructions-its-data-43m8
← Previous
My adaptive memory stayed empty in production, and it wasn't a bug
Next →
NextAuth / Auth.js Database Schema Explained
Related
AI Tools for Niche Software Development in 2026: Real Stats & Tools
AI & ML
0
DEV Community
AI Search Traffic Is Concentrated and Volatile, Previsible’s 6.77M-Session Study Finds
AI & ML
0
DEV Community
The Email Headers That Actually Stop Out-of-Office Auto-Replies
AI & ML
2
Dev.to (EN Zone)
The Thermal Shock Series #2: Designing a Thermal Shock Scoring Algorithm
AI & ML
2
Dev.to (EN Zone)
Comments0
No comments yet — be the first