<?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: Alex Duch</title>
    <description>The latest articles on DEV Community by Alex Duch (@alex_duch).</description>
    <link>https://dev.to/alex_duch</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3970985%2F0ff6614d-7efa-4140-8279-db8bde738575.png</url>
      <title>DEV Community: Alex Duch</title>
      <link>https://dev.to/alex_duch</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alex_duch"/>
    <language>en</language>
    <item>
      <title>Three checks that separate an agent demo from a production agent</title>
      <dc:creator>Alex Duch</dc:creator>
      <pubDate>Sat, 06 Jun 2026 09:05:58 +0000</pubDate>
      <link>https://dev.to/alex_duch/three-checks-that-separate-an-agent-demo-from-a-production-agent-5a8b</link>
      <guid>https://dev.to/alex_duch/three-checks-that-separate-an-agent-demo-from-a-production-agent-5a8b</guid>
      <description>&lt;p&gt;Shipping an agent demo takes an afternoon. Shipping one that survives a quarter in production is a different job — and the gap is almost never the model. It's three boring things that are usually missing entirely.&lt;/p&gt;

&lt;p&gt;I maintain an open, MIT-licensed Agentic Product Standard, and v2.0 was mostly about turning those three things from advice into code you can run. Here they are, with the actual code.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Security is structural, not a filter
The most common mistake is treating safety as a guardrail — an input/output filter near the edge. The problem is that filters have a ceiling. The best content classifiers run around 97% accuracy, which means ~3% of prompt-injection attempts land by design. That's not a bug you tune away; it's the nature of filtering.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Real safety comes from architecture. The check I reach for first is Simon Willison's lethal trifecta: an agent becomes an exfiltration tool the moment it has all three of —&lt;/p&gt;

&lt;p&gt;access to private data,&lt;br&gt;
exposure to untrusted content, and&lt;br&gt;
the ability to communicate externally.&lt;br&gt;
Any one is fine. All three together is a data-exfiltration channel waiting for a payload hidden in a retrieved document. The fix is never "better filter" — it's break a leg: gate egress, quarantine untrusted input, or scope the data.&lt;/p&gt;

&lt;p&gt;Here's the gate, as a CI step. You declare what your agent can touch; the build fails if the trifecta is unmitigated:&lt;/p&gt;

&lt;h1&gt;
  
  
  agent-capabilities.json
&lt;/h1&gt;

&lt;h1&gt;
  
  
  { "name": "support-agent",
&lt;/h1&gt;

&lt;h1&gt;
  
  
  "private_data": true, "untrusted_content": true, "external_comms": true,
&lt;/h1&gt;

&lt;h1&gt;
  
  
  "mitigations": [{"leg": "external_comms", "control": "egress allow-list + approval"}] }
&lt;/h1&gt;

&lt;p&gt;LEGS = ("private_data", "untrusted_content", "external_comms")&lt;/p&gt;

&lt;p&gt;def evaluate(spec):&lt;br&gt;
    present = [l for l in LEGS if spec.get(l) is True]&lt;br&gt;
    if len(present) &amp;lt; 3:&lt;br&gt;
        return 0, f"OK: only {len(present)}/3 legs present"&lt;br&gt;
    broken = {m["leg"] for m in spec.get("mitigations", []) if m.get("control")}&lt;br&gt;
    if broken &amp;amp; set(LEGS):&lt;br&gt;
        return 0, f"OK: trifecta present but broken at {', '.join(broken)}"&lt;br&gt;
    return 1, "FAIL: lethal trifecta, no leg broken"&lt;br&gt;
The second structural control is MCP supply chain. Community MCP servers are untrusted code, and a server can hand you a benign tool description at approval time, then mutate it later (a "rug pull," or tool-definition poisoning). So: pin tool definitions by hash and alert on change. A few lines of bash in CI catches it:&lt;/p&gt;

&lt;h1&gt;
  
  
  canonical hash per tool, sorted so reordering doesn't trip it but content changes do
&lt;/h1&gt;

&lt;p&gt;jq -cS '(.tools)|sort_by(.name)[]|{name,description,inputSchema}' tools.json \&lt;br&gt;
  | while read -r t; do printf '%s  %s\n' "$(printf '%s' "$t"|sha256sum|cut -d' ' -f1)" \&lt;br&gt;
      "$(printf '%s' "$t"|jq -r .name)"; done &amp;gt; current.lock&lt;br&gt;
diff -u tools.lock current.lock || { echo "::error::MCP tool def changed — possible rug pull"; exit 1; }&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Cost is a circuit breaker, not a dashboard
Agents burn tokens at a rate chat never did — Anthropic has put multi-agent systems at roughly 15× a chat interaction. Most published multi-agent architectures leave the cost ceiling wide open: there's no breaker on a runaway loop.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;v2.0 makes cost a hard control:&lt;/p&gt;

&lt;p&gt;a per-run token/cost ceiling enforced in code (a breaker, checked before each model call), not a bill you read later;&lt;br&gt;
prompt/KV caching on stable prefixes (system prompt, tool schemas);&lt;br&gt;
model routing — small model for classification, flagship for reasoning;&lt;br&gt;
and the economics rule people learn the expensive way: only pay the 15× for multi-agent when the task value justifies it. If one agent clears the bar, the orchestra is waste.&lt;br&gt;
The point is that "cost" stops being a surprise the moment it's a number your code enforces and your traces record per task.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Delete your own scaffolding
This is the one I think about most. Every rule you write to patch a model's weakness becomes dead weight the day that weakness is gone — and worse, it competes for the model's attention and degrades the rules that still matter.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So the standard adds a maintenance doctrine (adapted, with credit, from Daniel Miessler's PAI): audit every instruction on a cadence with one question —&lt;/p&gt;

&lt;p&gt;Would a smarter model make this rule unnecessary?&lt;/p&gt;

&lt;p&gt;If yes, it's scaffolding, not architecture — cut it. Tag rules anti-fragile (eval sets, verification harnesses, tool contracts, real failure gotchas → keep) vs fragile (chain-of-thought orchestrators, output parsers, retry cascades → cut or re-test on the next model upgrade). A standard that only grows is one that rots.&lt;/p&gt;

&lt;p&gt;Making it checkable&lt;br&gt;
Principles are easy to agree with and impossible to audit. So v2.0 ships a self-assessment scorecard — a binary Yes/No maturity check (M0 Prototype → M1 Shippable → M2 Production → M3 Autonomous-ready), mapped to an autonomy ladder. Your level is the highest band where every gate passes; the first "No" is your next task. "Is it production-ready?" becomes a checklist instead of a vibe.&lt;/p&gt;

&lt;p&gt;Alongside it: the red-team kit above plus a CI workflow template that blocks merges when the eval pass-rate slips, and a 2026 refresh (MCP + OAuth 2.1, A2A at the Linux Foundation, OpenTelemetry GenAI tracing, trajectory + pass^k eval metrics).&lt;/p&gt;

&lt;p&gt;The one rule under all of it&lt;br&gt;
The model is the variable. The harness is the constant. Invest proportionally.&lt;/p&gt;

&lt;p&gt;…with the v2.0 twist: the harness isn't something you accumulate forever. You curate it — growing the parts that compound, deleting the parts that only propped up a weaker model.&lt;/p&gt;

&lt;p&gt;It's MIT and vendor-neutral (deliberately not a framework), with an optional Claude Code skill set. I'd rather be told what's wrong with it than collect stars — the deferred list is where I'm least sure.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/Moai-Team-LLC/agentic-product-standard" rel="noopener noreferrer"&gt;https://github.com/Moai-Team-LLC/agentic-product-standard&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>llm</category>
      <category>security</category>
    </item>
  </channel>
</rss>
