<?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: cavitnation</title>
    <description>The latest articles on DEV Community by cavitnation (@cavitnation).</description>
    <link>https://dev.to/cavitnation</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%2F1094809%2F3da06d21-15cd-4488-8a7b-507b4aa18505.png</url>
      <title>DEV Community: cavitnation</title>
      <link>https://dev.to/cavitnation</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/cavitnation"/>
    <language>en</language>
    <item>
      <title>How I stop an LLM from hallucinating in production (RAG + entity-match + MCP)</title>
      <dc:creator>cavitnation</dc:creator>
      <pubDate>Sat, 12 Sep 2026 12:24:40 +0000</pubDate>
      <link>https://dev.to/cavitnation/how-i-stop-an-llm-from-hallucinating-in-production-rag-entity-match-mcp-2ndj</link>
      <guid>https://dev.to/cavitnation/how-i-stop-an-llm-from-hallucinating-in-production-rag-entity-match-mcp-2ndj</guid>
      <description>&lt;p&gt;How do you make an LLM answer questions about real-world entities — companies, people, records — without it confidently making things up?&lt;/p&gt;

&lt;p&gt;I build &lt;a href="https://verivello.org" rel="noopener noreferrer"&gt;Verivello&lt;/a&gt;, a live AI agent that answers questions about any UK company from official registers (Companies House, HM Land Registry, FCA, The Gazette, sanctions lists). In that domain, a &lt;em&gt;wrong&lt;/em&gt; director or ownership figure is worse than no answer at all. So "don't hallucinate" isn't a nice-to-have — it's the whole product.&lt;/p&gt;

&lt;p&gt;Here's the system design I use to keep an LLM grounded in production. No prompt-magic, no fine-tuning. Just architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;LLMs are fluent, and fluency reads as confidence. Ask a model "who is the director of Acme Ltd?" and it will happily produce a plausible name — whether or not it actually knows. For casual use that's fine. For anything a business acts on, it's a liability.&lt;/p&gt;

&lt;p&gt;The usual first instinct is "add RAG." Retrieval-augmented generation helps, but on its own it isn't enough: retrieval can pull the &lt;em&gt;wrong&lt;/em&gt; record, and the model can still paraphrase it into something subtly false. Grounding is a pipeline, not a single step.&lt;/p&gt;

&lt;h2&gt;
  
  
  The system at a glance
&lt;/h2&gt;



&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    U[User question] --&amp;gt; C[Classifier: intent + entity]
    C --&amp;gt; R[Engine router]
    R --&amp;gt; T[Tool / function-calling loop]
    T --&amp;gt; M[MCP tool servers]
    M --&amp;gt; S1[Companies House]
    M --&amp;gt; S2[Land Registry]
    M --&amp;gt; S3[FCA / ICO]
    M --&amp;gt; S4[Gazette / Sanctions]
    S1 &amp;amp; S2 &amp;amp; S3 &amp;amp; S4 --&amp;gt; V[Grounding layer:&amp;lt;br/&amp;gt;verbatim output + entity-match]
    V --&amp;gt; A[Streamed, source-cited answer]
    A --&amp;gt; U&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Two rules in that &lt;strong&gt;grounding layer&lt;/strong&gt; do most of the anti-hallucination work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rule 1 — Verbatim tool output, not model memory
&lt;/h2&gt;

&lt;p&gt;The model is &lt;strong&gt;never&lt;/strong&gt; allowed to recall a fact about an entity. Every fact in an answer must come from a tool call whose raw output is passed through unchanged. The model's job is to &lt;em&gt;explain and cite&lt;/em&gt; retrieved records — not to &lt;em&gt;know&lt;/em&gt; them.&lt;/p&gt;

&lt;p&gt;Concretely:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tools return structured records (JSON), and that JSON is placed in context verbatim.&lt;/li&gt;
&lt;li&gt;The system prompt instructs the model to answer &lt;strong&gt;only&lt;/strong&gt; from provided records and to cite them.&lt;/li&gt;
&lt;li&gt;If a fact isn't in the retrieved records, the model must not supply it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This one rule kills the most common failure mode: the model "filling in" a field from training data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rule 2 — Entity-match verification
&lt;/h2&gt;

&lt;p&gt;This is the rule most people miss. Name-search APIs happily return &lt;em&gt;a&lt;/em&gt; record for a query — often the wrong one. Search "Smith Consulting" and you might get any of dozens. If you feed that straight into context, you've grounded the model in the wrong company. Now it's confidently wrong &lt;em&gt;with a citation&lt;/em&gt;, which is worse.&lt;/p&gt;

&lt;p&gt;So before any record is allowed into the grounded context, it's re-verified against the entity the user actually asked about. Illustrative shape (not production code):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Reject records that don't actually match the entity the user asked about.&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;verifyMatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Strongest signal: exact identifier match.&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;companyNumber&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;companyNumber&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;companyNumber&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;companyNumber&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// Otherwise: normalised name + a corroborating signal.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;normalise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;normalise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;similarity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.92&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;sameIncorporationYear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Only verified records are allowed into the grounded context.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;grounded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;toolResults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;verifyMatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mismatches are dropped, not shown. The model only ever sees records that provably belong to the queried entity.&lt;/p&gt;

&lt;h2&gt;
  
  
  The golden rule — fail closed
&lt;/h2&gt;

&lt;p&gt;If nothing verifies, the agent says so. &lt;strong&gt;No verified record → an honest "I don't know," never a plausible guess.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is a product decision as much as an engineering one. In due diligence, a confident wrong answer is the expensive failure mode — far more costly than an honest gap. So the whole pipeline is built to fail &lt;em&gt;closed&lt;/em&gt;: when in doubt, refuse.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why MCP for the tool layer
&lt;/h2&gt;

&lt;p&gt;Each data source is exposed as a typed &lt;a href="https://modelcontextprotocol.io" rel="noopener noreferrer"&gt;Model Context Protocol&lt;/a&gt; (MCP) tool server. That gives a few real benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Isolation &amp;amp; testability&lt;/strong&gt; — each source is an independent server with a typed schema, unit-testable on its own.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reuse&lt;/strong&gt; — the same tool servers work across clients (Claude Code, Claude Desktop, the product itself).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A clean boundary&lt;/strong&gt; — the model requests &lt;em&gt;what&lt;/em&gt; it needs; the tool layer owns &lt;em&gt;how&lt;/em&gt; it's fetched and verified.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I open-sourced a small, keyless example of this pattern — an MCP server exposing UK public-data tools, with unit tests + CI — here: &lt;strong&gt;&lt;a href="https://github.com/cavitnation/mcp-uk-tools" rel="noopener noreferrer"&gt;github.com/cavitnation/mcp-uk-tools&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;p&gt;If you're wiring an LLM up to real data, grounding is a pipeline:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Verbatim tool output&lt;/strong&gt; — the model explains and cites; it never recalls.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Entity-match verification&lt;/strong&gt; — prove each record belongs to the queried entity before using it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fail closed&lt;/strong&gt; — no verified record, no answer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP tool servers&lt;/strong&gt; — a clean, testable, reusable boundary between model and data.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's the difference between a demo and something a business can actually trust to act.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I wrote up the full architecture (with the diagram, no product code) here: &lt;a href="https://github.com/cavitnation/verivello-architecture" rel="noopener noreferrer"&gt;github.com/cavitnation/verivello-architecture&lt;/a&gt;. I'm an AI-native full-stack engineer — happy to talk shop in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>webdev</category>
      <category>llm</category>
    </item>
    <item>
      <title>Hi DEV — I build backend-heavy web apps</title>
      <dc:creator>cavitnation</dc:creator>
      <pubDate>Tue, 08 Sep 2026 10:38:02 +0000</pubDate>
      <link>https://dev.to/cavitnation/hi-dev-i-build-backend-heavy-web-apps-16ji</link>
      <guid>https://dev.to/cavitnation/hi-dev-i-build-backend-heavy-web-apps-16ji</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr5l4tqzkntjsi0srb89t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr5l4tqzkntjsi0srb89t.png" alt="Jeemmo — backend-heavy web app development" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm Azeem — I build backend-heavy web apps solo as &lt;a href="https://jeemmo.com/" rel="noopener noreferrer"&gt;Jeemmo&lt;/a&gt;, since 2011, for UK / EU / US clients.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I do:&lt;/strong&gt; custom web apps, real-time features (WebSockets), Stripe / payment integrations, rescuing old PHP, and making slow WooCommerce stores fast.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stack:&lt;/strong&gt; PHP / Laravel, Node.js, Python, MySQL, Redis.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A few things I've built:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://archstudent.com/" rel="noopener noreferrer"&gt;ArchStudent&lt;/a&gt; — content platform for architecture students&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://quranroshni.com/" rel="noopener noreferrer"&gt;Quran Roshni&lt;/a&gt; — fast, ad-free Quran reader (~30 languages)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://worldhotsprings.com/" rel="noopener noreferrer"&gt;World Hot Springs&lt;/a&gt; — data-pipeline world directory&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://goodping.com/" rel="noopener noreferrer"&gt;GoodPing&lt;/a&gt; — free network tools + uptime monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can see more of what I build at &lt;a href="https://jeemmo.com/" rel="noopener noreferrer"&gt;jeemmo.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>php</category>
      <category>wordpress</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Are coders still getting hired now that AI can write code?</title>
      <dc:creator>cavitnation</dc:creator>
      <pubDate>Fri, 31 Oct 2025 21:49:12 +0000</pubDate>
      <link>https://dev.to/cavitnation/are-coders-still-getting-hired-now-that-ai-can-write-code-3oa6</link>
      <guid>https://dev.to/cavitnation/are-coders-still-getting-hired-now-that-ai-can-write-code-3oa6</guid>
      <description></description>
    </item>
    <item>
      <title>Wordpress theme development - version update function</title>
      <dc:creator>cavitnation</dc:creator>
      <pubDate>Sat, 03 Jun 2023 12:00:31 +0000</pubDate>
      <link>https://dev.to/cavitnation/wordpress-theme-development-version-update-function-2mpi</link>
      <guid>https://dev.to/cavitnation/wordpress-theme-development-version-update-function-2mpi</guid>
      <description>&lt;p&gt;Hello,&lt;/p&gt;

&lt;p&gt;how to add wordpress theme update function in source files? do i need to add a code in function.php ?&lt;/p&gt;

&lt;p&gt;is there any standard code to add? what other files i need to add update functionality code?&lt;/p&gt;

&lt;p&gt;i want to do this.&lt;br&gt;
1- I will take wordpress standard theme files from underscores.me and make it as version 1.0&lt;br&gt;
2- Make some fancy work on it, i will make Landing Pages/sub pages.&lt;br&gt;
3- Make theme documentation for users to read and install.&lt;br&gt;
4- upload on envato market.&lt;/p&gt;

&lt;p&gt;So after 2-3 months if i upgrade my theme as in version 1.1&lt;br&gt;
so how i noptify this to my envato users? where i need to add code in my theme for this function?&lt;/p&gt;

&lt;p&gt;I know i can take api token generate from envato api website and update this in envato plugin in my wordpress dashboard.&lt;/p&gt;

&lt;p&gt;is there anyone doing the same job or have done it?&lt;/p&gt;

&lt;p&gt;Please guide&lt;br&gt;
Best Regards&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
