<?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: Muhammad Ahmad Nazir Ahmad</title>
    <description>The latest articles on DEV Community by Muhammad Ahmad Nazir Ahmad (@emadaed).</description>
    <link>https://dev.to/emadaed</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%2F4190919%2F68fffdc5-c626-4710-8c63-c9130b1335f3.jpg</url>
      <title>DEV Community: Muhammad Ahmad Nazir Ahmad</title>
      <link>https://dev.to/emadaed</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/emadaed"/>
    <language>en</language>
    <item>
      <title>Finding the Bugs I Built an AI Agent to Prevent: Building Deterministic Guardrails with LangGraph &amp; Python</title>
      <dc:creator>Muhammad Ahmad Nazir Ahmad</dc:creator>
      <pubDate>Sat, 17 Oct 2026 18:28:21 +0000</pubDate>
      <link>https://dev.to/emadaed/finding-the-bugs-i-built-an-ai-agent-to-prevent-building-deterministic-guardrails-with-langgraph--agf</link>
      <guid>https://dev.to/emadaed/finding-the-bugs-i-built-an-ai-agent-to-prevent-building-deterministic-guardrails-with-langgraph--agf</guid>
      <description>&lt;p&gt;``&lt;br&gt;
A real debugging log: fabrication bugs, deployment gaps, and false test passes found and closed across 6 rounds of adversarial testing.&lt;/p&gt;

&lt;p&gt;System prompts are a leash, not a lobotomy. A system prompt can be ignored, misread, or drift under pressure. Deterministic code cannot.&lt;/p&gt;

&lt;p&gt;When I built Ahmad-AI — a local AI agent using LangGraph, Python, and Ollama with access to live SQL databases and Islamic knowledge bases — I established one non-negotiable design rule:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It must never state a specific citation or data claim unless that claim is independently verified. Verified, or it says nothing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Because general-purpose LLMs fabricate citations confidently and often, "confidently wrong" is worse than "honestly uncertain." I spent weeks trying to prove the system would break its own rule anyway.&lt;/p&gt;

&lt;p&gt;It did. Repeatedly. Here is what I learned across six rounds of adversarial testing against a live agent.&lt;/p&gt;

&lt;h3&gt;
  
  
  Round 1: The First Adversarial Pass
&lt;/h3&gt;

&lt;p&gt;I ran a set of test questions against the live agent — empty database queries, Quran &amp;amp; Hadith citation requests, and ambiguous sorting queries.&lt;/p&gt;

&lt;p&gt;Two critical bugs immediately surfaced:&lt;/p&gt;

&lt;h3&gt;
  
  
  The Ghost Row Bug:
&lt;/h3&gt;

&lt;p&gt;When querying a table with no matching rows, the agent correctly output: "The query did not return any results." In the exact same sentence, it invented two fake item IDs with fake stock levels.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Regex Mismatch:
&lt;/h3&gt;

&lt;p&gt;When asked about usury (riba), the agent cited verse 5:51 (a real Quran verse, but entirely about interfaith alliances). My citation guard failed to fire because its regex expected Surah [name] X:Y, while the model wrote (Quran X:Y). A safety mechanism that doesn't match the shape of the output is functionally non-existent.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Fix Philosophy: Code, Not More Instructions
&lt;/h3&gt;

&lt;p&gt;My first instinct was to strengthen the system prompt. I resisted. Instead of writing tighter rules for the LLM, I built a deterministic backstop in Python that overrides the model’s prose when known failure patterns appear in state:&lt;/p&gt;

&lt;p&gt;`&lt;code&gt;&lt;/code&gt;python&lt;br&gt;
python:&lt;br&gt;
def apply_tool_result_guards(state, response):&lt;br&gt;
    """&lt;br&gt;
    Deterministic backstop: if the last DB query came back empty or hard-errored, &lt;br&gt;
    override entirely with a fixed, honest message regardless of what the model wrote.&lt;br&gt;
    """&lt;br&gt;
    if is_no_data(last_tool_message):&lt;br&gt;
        return "No matching data was found for that query. The data may not exist yet."&lt;/p&gt;

&lt;p&gt;ALLOWED_CITATIONS = {"20:114"}  # Pre-verified whitelist&lt;/p&gt;

&lt;p&gt;def enforce_citation_guard(text):&lt;br&gt;
    for match in CITATION_PATTERN.finditer(text):&lt;br&gt;
        if not any(allowed in match.group(0) for allowed in ALLOWED_CITATIONS):&lt;br&gt;
            return SAFE_ISLAMIC_FALLBACK  # Blocked even if the citation is real&lt;br&gt;
    return text&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`&lt;/p&gt;

&lt;h3&gt;
  
  
  Building an Evaluation Harness That Doesn't Lie
&lt;/h3&gt;

&lt;p&gt;Manually retyping test prompts in a terminal doesn't scale. To keep the agent honest, I split automated testing into two distinct layers:&lt;/p&gt;

&lt;p&gt;Unit Tests (22 tests): Direct execution of Python guard functions against synthetic inputs (fast, no live LLM required).&lt;/p&gt;

&lt;p&gt;End-to-End Evaluation Harness: Drives the live LangGraph execution against Ollama and Postgres.&lt;/p&gt;

&lt;p&gt;Treating fuzzy model outputs as binary pass/fail either hides real regressions behind noisy false failures or downgrades hard security checks to "close enough."&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Takeaways for AI Engineers
&lt;/h3&gt;

&lt;p&gt;A whitelist beats a correctness checker: Verifying "is this citation accurate?" is hard. Checking "is this citation on the pre-approved whitelist?" is simple and reliable.&lt;/p&gt;

&lt;p&gt;Print a Build Stamp at Startup: A version stamp printed at agent startup costs nothing and prevents testing against stale code.&lt;/p&gt;

&lt;p&gt;Distinguish Transient from Permanent Errors: Retrying a connection drop makes sense. Retrying a bad SQL column name is just a slower way to fail.&lt;/p&gt;

&lt;p&gt;Don't ask LLMs to do arithmetic or row matching: Stating query answers explicitly in tool outputs prevents models from misreading JSON rows.&lt;/p&gt;

&lt;p&gt;Read the Full Engineering Log&lt;br&gt;
This architecture is live, tested against a 22-unit-test suite and a 5/5 E2E hard check harness.&lt;/p&gt;

&lt;p&gt;For the complete technical breakdown — including deployment gap fixes, bounded execution limits (MAX_TOOL_ROUNDS), and verbatim adversarial outputs:&lt;/p&gt;

&lt;p&gt;📖 Read the full interactive log on my portfolio:&lt;/p&gt;

&lt;p&gt;👉  &lt;a href="https://emadaed.github.io/digisuite/engineering-log.html" rel="noopener noreferrer"&gt;Engineering Log 01: Guardrails &amp;amp; Hallucination Prevention&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Muhammad Ahmad is an AI Agent Engineer and Digital Supply Chain Architect (PMP®, RMP®) with 15+ years of operational leadership experience.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>langgraph</category>
      <category>rag</category>
    </item>
  </channel>
</rss>
