<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rohan Jain</title>
    <description>The latest articles on DEV Community by Rohan Jain (@rohanjain2312).</description>
    <link>https://dev.to/rohanjain2312</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4100837%2Fa26d1e6f-89ca-47bc-aa0e-c245df2318ab.jpg</url>
      <title>DEV Community: Rohan Jain</title>
      <link>https://dev.to/rohanjain2312</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rohanjain2312"/>
    <language>en</language>
    <item>
      <title>Harness Engineering: The Part of the Agent That's Actually Yours</title>
      <dc:creator>Rohan Jain</dc:creator>
      <pubDate>Sat, 29 Aug 2026 23:43:24 +0000</pubDate>
      <link>https://dev.to/rohanjain2312/harness-engineering-the-part-of-the-agent-thats-actually-yours-4b23</link>
      <guid>https://dev.to/rohanjain2312/harness-engineering-the-part-of-the-agent-thats-actually-yours-4b23</guid>
      <description>&lt;p&gt;&lt;em&gt;Two years of building agents, and almost every fix that held was outside the model.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;My agent did something dumb, so I went looking at the model. Benchmarks, leaderboard position, public opinion on Twitter.&lt;/p&gt;

&lt;p&gt;What actually fixed things was boring: a reranker, a retry ladder, a state schema, a gate in CI. Code I wrote around the model, not the model itself.&lt;/p&gt;

&lt;p&gt;Training, fine-tuning, RLHF, DPO: all of that is GPU, time and data intensive, which keeps it with big companies and frontier labs, so I built agents instead. Once I'd exhausted the "just get a better model" instinct, I moved on to swapping checkpoints, bumping the context window, rewriting system prompts. Those helped, but the gains were small and they didn't hold. Then I started fixing the scaffolding, and things stayed fixed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Models are like people
&lt;/h2&gt;

&lt;p&gt;Having worked at a financial institution, I'd seen firsthand the mistakes people make. No matter their tenure or subject knowledge, the mistakes were usually silly ones, in steps they had performed flawlessly a hundred times before. The causes varied: exhaustion, a slip of the finger, skipping a basic step.&lt;/p&gt;

&lt;p&gt;I built controls and guardrails to eliminate these many times over, learnt that no matter how good someone is, they will still make mistakes, and it's my job to make sure there are guardrails to catch them and redirect them onto the right path.&lt;/p&gt;

&lt;p&gt;Some of you have probably already guessed the parallel. Models fail for different reasons than people do, not fatigue, but ambiguity, distribution shift, and no memory of the hundred times they got it right. But the remedy is identical. You want the best operator you can get, and that alone does not guarantee fail-safe operation. You need crash barriers that stop the thing from doing something hazardous.&lt;/p&gt;

&lt;p&gt;I eventually built those same loan servicing controls into an agent, a &lt;a href="https://github.com/Rohanjain2312/loan-servicing-agent" rel="noopener noreferrer"&gt;multi-agent system for syndicated loan documents&lt;/a&gt; that ingests a Credit Agreement or a Notice PDF and executes the lifecycle action end to end. It shows up a few times below, because it's my clearest example of a harness where a mistake means a real drawdown against a real facility.&lt;/p&gt;

&lt;p&gt;And as a bonus: if your harness is strong enough, a cheaper model can do the same work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agent = Model + Harness
&lt;/h2&gt;

&lt;p&gt;The definition that stuck with me is from Vivek Trivedy's write-up at LangChain:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Agent = Model + Harness&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The first part is usually not in our hands. So let's talk about the second.&lt;/p&gt;

&lt;p&gt;The harness is everything that isn't the weights. Retrieval, tool schemas, the control loop, the sandbox, what persists between turns, what gets thrown away when you run out of room. The nice thing about the early days of something new is that you can put almost anything under its umbrella without much scrutiny, which is exactly what I've been doing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hold the model fixed and the numbers still move
&lt;/h2&gt;

&lt;p&gt;Between March and April 2026, Claude Code got noticeably worse and nobody outside the company could say why. Anthropic's &lt;a href="https://www.anthropic.com/engineering/april-23-postmortem" rel="noopener noreferrer"&gt;postmortem&lt;/a&gt; named three changes, none to the weights and none to the API. My favourite is the smallest: a system prompt line capping text between tool calls at 25 words. That text is the model's scratchpad, so capping it shortened the thinking along with it. Their own ablations measured roughly a 3% drop in coding quality from that one line.&lt;/p&gt;

&lt;p&gt;The research agrees. In a &lt;a href="https://arxiv.org/abs/2605.23950" rel="noopener noreferrer"&gt;controlled comparison&lt;/a&gt;, changing harness at a fixed model moves SWE-bench pass@1 by 8.5 to 13 points, while changing model at a fixed harness moves it 2.5 to 5. The within-model harness range is bigger than the within-harness model range: a strange sentence to write and a hard one to unsee. That holds for big harness swaps, though. &lt;a href="https://www.lesswrong.com/posts/GiPmLmmbT6DyrwYkH/terminal-bench-leaderboard-rankings-luck-or-skill" rel="noopener noreferrer"&gt;Adjacent leaderboard rows&lt;/a&gt; mostly differ by noise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Five places my own harness was the problem
&lt;/h2&gt;

&lt;p&gt;Birgitta Böckeler's &lt;a href="https://martinfowler.com/articles/harness-engineering.html" rel="noopener noreferrer"&gt;harness engineering write-up&lt;/a&gt; is the framing I use now. &lt;strong&gt;Guides&lt;/strong&gt; run before the agent acts and raise the odds it gets things right first time. &lt;strong&gt;Sensors&lt;/strong&gt; run after and let it self-correct. Each can be computational (deterministic, cheap enough to run on every change) or inferential (an LLM judging). Almost every bug I shipped was a missing guide or a missing sensor.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. What it reads before it writes
&lt;/h3&gt;

&lt;p&gt;In the loan servicing agent, a Notice gets validated against four Credit Agreement clauses: conditions precedent, permitted purpose, notice mechanics, repayment conditions. It kept validating against the wrong one and approving things it shouldn't have. I blamed the model's reasoning. Retrieval was handing it the wrong text.&lt;/p&gt;

&lt;p&gt;A repayment conditions clause and a conditions precedent clause are near identical in embedding space and completely different in effect, and because Credit Agreements are drafted from templates the whole corpus is written in one voice. That is exactly where semantic similarity stops discriminating. The fix was hybrid retrieval: full text search alongside vectors, so the defined terms a clause turns on carry weight instead of being averaged away.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Rohanjain2312/graphbench" rel="noopener noreferrer"&gt;GraphBench&lt;/a&gt; is my controlled version of the claim. Two pipelines, same FAISS lookup, same Neo4j subgraph, same Mistral-7B, only the triple selection differs. Across 500 HotpotQA questions that alone moved exact match from 3.2% to 5.0%. The agent's ceiling is set by what reaches its context; a better model reasons better over the wrong document and still gives you a wrong answer.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Computational guide.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. What it does when it's wrong
&lt;/h3&gt;

&lt;p&gt;My &lt;a href="https://github.com/Rohanjain2312/Self-Healing-Code-Agent" rel="noopener noreferrer"&gt;Self-Healing Code Agent&lt;/a&gt; would hit a failing test, try a fix, fail again, then keep trying the same shape of fix until it ran out of budget. It had no failure ladder. Retry was the entire recovery vocabulary.&lt;/p&gt;

&lt;p&gt;What replaced it: confidence-aware routing, so a low-confidence diagnosis goes to a blind retry rather than a targeted repair, because a confident wrong theory is worse than no theory. Parallel repair strategies via LangGraph's &lt;code&gt;Send()&lt;/code&gt;, picking a winner by tournament. A ReAct debugger that tests hypotheses and checks whether repairs are converging. And a stop: max iterations, then a human.&lt;/p&gt;

&lt;p&gt;On the benchmark in the repo, eight tasks, &lt;code&gt;llama3&lt;/code&gt; via Ollama on local CPU: &lt;strong&gt;first-pass success 3 of 8, or 37%. After the repair loop, 7 of 8, or 87%.&lt;/strong&gt; Same model throughout. The 50 point gap is the loop.&lt;/p&gt;

&lt;p&gt;A retry is not a recovery policy. Diagnose, route on confidence, fan out, escalate, hard stop is a recovery policy.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Computational sensor, plus a control policy.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. How hard it thinks, and where
&lt;/h3&gt;

&lt;p&gt;I assumed the rule was cheap model for easy steps, expensive model for hard ones. The Self-Healing agent inverted that. The cheap local 3B runs the generator, the thing that writes and repairs the code. Claude runs adversarial test generation, root cause analysis and the critic.&lt;/p&gt;

&lt;p&gt;That looks backwards until you notice what each role does. Generation is recoverable, because everything downstream exists to catch its mistakes. Diagnosis is not: a wrong diagnosis sends the next iteration in the wrong direction and nothing catches it. Spend where a mistake is unrecoverable, not where it's most likely. The loan servicing agent applies the same rule across three tiers: Sonnet orchestrates and owns routing, GPT-4o-mini extracts against fixed templates, Haiku validates and executes.&lt;/p&gt;

&lt;p&gt;Set this wrong and you get a system that feels dumber with no error anywhere in your logs. Anthropic ran that wrong default across an entire product for a month.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A guide, and a load-bearing one.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. What it runs without asking
&lt;/h3&gt;

&lt;p&gt;The Self-Healing agent executes generated code, which is where I was most naive. I started with permission checks inside the agent loop. That is not a security boundary: a check inside the harness only sees the call the agent chose to make, and anything reached another way never touches it. The boundary that holds is the one the operating system puts on the process. Mine is a Python subprocess with &lt;code&gt;rlimit&lt;/code&gt; caps, which bounds CPU and memory but is not deny-by-default on filesystem and network. Naming that gap is more useful than pretending I closed it.&lt;/p&gt;

&lt;p&gt;Gates sit on top of the boundary, not instead of it. The gate answers &lt;em&gt;should this happen.&lt;/em&gt; The sandbox answers &lt;em&gt;what is even possible.&lt;/em&gt; In the loan servicing agent every gate has a threshold rather than a vibe:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Any field below &lt;strong&gt;0.75 confidence&lt;/strong&gt; stops and shows the reviewer the inference next to its source text.&lt;/li&gt;
&lt;li&gt;Interest is recomputed in Python on ACT/360 and checked against the notice with a &lt;strong&gt;$30 tolerance band&lt;/strong&gt;. The model doesn't get to do the arithmetic.&lt;/li&gt;
&lt;li&gt;Risk escalation to High stops for a human. Every drawdown stops, unconditionally.&lt;/li&gt;
&lt;li&gt;The transaction log is append-only. No deletes anywhere.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last one is a constraint rather than a sensor: the control that keeps working when everything else is broken.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Constraints and gates.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Whether you would even notice
&lt;/h3&gt;

&lt;p&gt;Read what Anthropic says about detection. The caching bug got through human code review, automated review, unit tests, end-to-end tests and internal dogfooding, and their own evals didn't reproduce it at first. Users reported degradation in early March; the last fix landed April 20. These are people with more eval infrastructure than any of us. The bug was invisible because it lived in the seam between context management, the API and extended thinking, and nobody had a sensor pointed at that seam.&lt;/p&gt;

&lt;p&gt;Three things I do because of this. &lt;strong&gt;Harness config goes in version control, separately from code&lt;/strong&gt;, so &lt;code&gt;git log prompts/&lt;/code&gt; is the first place I look when behavior moves. &lt;strong&gt;The agent never grades itself alone&lt;/strong&gt;: spec-blind tests before the code exists, adversarial tests after, and self-reported and reference-validated kept as separate columns. &lt;strong&gt;Something survives the run&lt;/strong&gt;: an append-only row per notice recording which gates fired and what the human decided, so drift has a record to diff against instead of a vague sense that things used to be better. Anthropic's users had that vague sense for six weeks. The piece I'm still building is the merge gate.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Inferential sensor. Expensive, probabilistic, worth it.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The benchmark trap
&lt;/h2&gt;

&lt;p&gt;I'm currently building &lt;a href="https://github.com/Rohanjain2312/toolsmith" rel="noopener noreferrer"&gt;ToolSmith&lt;/a&gt;, where I fine-tune a small 4B model to get better at calling tools. It will probably beat a bigger general-purpose model on my own test set. That won't mean much. A model tuned for one sandbox winning inside that sandbox is the expected result, not proof it's good in general.&lt;/p&gt;

&lt;p&gt;Even a proper score on a public tool-calling benchmark wouldn't tell me what I need to know. Those benchmarks check whether the model writes a correctly formatted tool call. They don't check what happens when your harness loads forty tool definitions at startup and fills the context before the user has typed anything. They don't check what the loop does when the call fails.&lt;/p&gt;

&lt;p&gt;A benchmark measures the model. Your agent is the model plus everything around it. The number is still worth having. It just won't tell you the five things above.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd tell myself a year ago
&lt;/h2&gt;

&lt;p&gt;When output degrades and you didn't change the model, the harness moved. Read the changelog before you rewrite the prompt.&lt;/p&gt;

&lt;p&gt;Write the failure ladder before the happy path. Retry is not on it.&lt;/p&gt;

&lt;p&gt;Spend your expensive model where a mistake is unrecoverable, not where it's most likely.&lt;/p&gt;

&lt;p&gt;Put your harness config under eval, or you'll find out it moved when a user tells you.&lt;/p&gt;

&lt;p&gt;None of this is free: the harness moves cost from a token bill to an engineering one, which is a trade you might want and might not.&lt;/p&gt;

&lt;p&gt;And the budget part: a 3B model went from 37% to 87% on my benchmark without a single weight changing. The models will keep getting better on their own schedule. The environment around them is the part that's yours.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you're building agents and you've got a harness failure story, I'd like to hear it. Which of these five cost you the most?&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources and further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Vivek Trivedy, &lt;a href="https://blog.langchain.com/the-anatomy-of-an-agent-harness/" rel="noopener noreferrer"&gt;The Anatomy of an Agent Harness&lt;/a&gt;, LangChain&lt;/li&gt;
&lt;li&gt;Birgitta Böckeler, &lt;a href="https://martinfowler.com/articles/harness-engineering.html" rel="noopener noreferrer"&gt;Harness engineering for coding agent users&lt;/a&gt;, martinfowler.com&lt;/li&gt;
&lt;li&gt;Anthropic, &lt;a href="https://www.anthropic.com/engineering/april-23-postmortem" rel="noopener noreferrer"&gt;An update on recent Claude Code quality reports&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Ryan Lopopolo, &lt;a href="https://openai.com/index/harness-engineering/" rel="noopener noreferrer"&gt;Harness engineering: leveraging Codex in an agent-first world&lt;/a&gt;, OpenAI&lt;/li&gt;
&lt;li&gt;Zhang et al., &lt;a href="https://arxiv.org/abs/2605.23950" rel="noopener noreferrer"&gt;Stop Comparing LLM Agents Without Disclosing the Harness&lt;/a&gt;, arXiv&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.lesswrong.com/posts/GiPmLmmbT6DyrwYkH/terminal-bench-leaderboard-rankings-luck-or-skill" rel="noopener noreferrer"&gt;Terminal-Bench leaderboard rankings: luck or skill?&lt;/a&gt;, LessWrong&lt;/li&gt;
&lt;li&gt;Lilian Weng, &lt;a href="https://lilianweng.github.io/posts/2026-07-04-harness/" rel="noopener noreferrer"&gt;Harness Engineering for Self-Improvement&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  My projects referenced
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/Rohanjain2312/loan-servicing-agent" rel="noopener noreferrer"&gt;Loan Servicing Agent&lt;/a&gt;&lt;/strong&gt;: multi-agent system for syndicated loan documents. LangGraph, Neon Postgres with pgvector, confidence-gated human review, ACT/360 validation, append-only audit log.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/Rohanjain2312/Self-Healing-Code-Agent" rel="noopener noreferrer"&gt;Self-Healing Code Agent&lt;/a&gt;&lt;/strong&gt;: generates Python, adversarially tests it, diagnoses failures via a ReAct debugger and repairs iteratively. 37% to 87% across 8 benchmark tasks. &lt;a href="https://huggingface.co/spaces/rohanjain2312/Self-Healing-Code-Agent" rel="noopener noreferrer"&gt;Live demo on HuggingFace Spaces&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/Rohanjain2312/graphbench" rel="noopener noreferrer"&gt;GraphBench&lt;/a&gt;&lt;/strong&gt;: open-source library benchmarking GraphRAG against GNN-RAG on multi-hop QA, with the scaffold held fixed. &lt;code&gt;pip install graphbench-kg&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/Rohanjain2312/toolsmith" rel="noopener noreferrer"&gt;ToolSmith&lt;/a&gt;&lt;/strong&gt;: post-training Qwen3-4B for tool-calling via LoRA SFT and step-level GRPO with verifiable sandbox rewards. Ships an MCP server and a Gradio Space.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://medium.com/@rohanjain2312/harness-engineering-the-part-of-the-agent-thats-actually-yours-ede9e85e4498" rel="noopener noreferrer"&gt;Medium&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>llm</category>
      <category>python</category>
    </item>
  </channel>
</rss>
