<?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: Victor</title>
    <description>The latest articles on DEV Community by Victor (@vicarioy).</description>
    <link>https://dev.to/vicarioy</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%2F4108517%2F088683b5-acb5-41fc-85db-bb000c7ebdd8.jpg</url>
      <title>DEV Community: Victor</title>
      <link>https://dev.to/vicarioy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vicarioy"/>
    <language>en</language>
    <item>
      <title>Nebula Agent: An AI That Surfaces Contradictions in Structured Content</title>
      <dc:creator>Victor</dc:creator>
      <pubDate>Sun, 27 Sep 2026 10:27:07 +0000</pubDate>
      <link>https://dev.to/vicarioy/nebula-agent-an-ai-that-surfaces-contradictions-in-structured-content-3760</link>
      <guid>https://dev.to/vicarioy/nebula-agent-an-ai-that-surfaces-contradictions-in-structured-content-3760</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/sanity-2026-09-16"&gt;Sanity Challenge, Path One: Ship an Agent That Queries Real Content&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;Sanity-powered AI agent&lt;/strong&gt; that answers questions about a fictional board game called &lt;strong&gt;Nebula&lt;/strong&gt; and, crucially, &lt;strong&gt;surfaces contradictions between sources&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The agent doesn't just search for keywords. It writes its own &lt;strong&gt;GROQ queries(Graph Relational Object Queries)&lt;/strong&gt; against a Sanity Content Lake in real time, reads the structured articles it retrieves, and when two sources disagree and presents &lt;strong&gt;both claims side by side with their source URLs&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The core question:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"According to the Nebula rulebook and errata, how many energy tokens do players start with? Are there any contradictions?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;The agent's answer:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The sources provide conflicting information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Nebula Core Rulebook - Setup&lt;/strong&gt; states players start with &lt;strong&gt;5 energy tokens&lt;/strong&gt; (&lt;a href="https://example.com/nebula-rulebook" rel="noopener noreferrer"&gt;source&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nebula Official Errata v2.1&lt;/strong&gt; corrects this to &lt;strong&gt;8 energy tokens&lt;/strong&gt; (&lt;a href="https://example.com/nebula-errata" rel="noopener noreferrer"&gt;source&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;A keyword search would have returned both articles. It would not have known which was authoritative, nor that the errata corrects the rulebook. My agent does this &lt;strong&gt;because the content was structured&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;Since the agent runs in the terminal, here's a live transcript from two consecutive runs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Question 1_Energy tokens:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;text
👤 User: According to the Nebula rulebook and errata, how many energy tokens
   do players start with? Are there any contradictions?

🤖 Agent using tool: query_documents
   Args: {"query":"*[_type match \"*rule*\" || _type match \"*errata*\" || lower(title) match \"*rule*\" || lower(title) match \"*errata*\"]"}
   Result preview: [{"_type":"article","body":"To win Nebula, you must collect 10 stars..."}]

🤖 Agent: According to the Nebula rulebook and errata, there is a contradiction
regarding how many energy tokens players start with:

* Nebula Core Rulebook - Setup: States that each player starts with 5 energy
  tokens. Source: https://example.com/nebula-rulebook

* Nebula Official Errata v2.1: States that the core rulebook is incorrect and
  that the correct starting amount is 8 energy tokens.
  Source: https://example.com/nebula-errata
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Question 2_Win condition:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;👤 User: What is the win condition for Nebula?

🤖 Agent using tool: query_documents
   Args: {"query":"*[_type == \"article\" &amp;amp;&amp;amp; (title match \"*Nebula*\" || body match \"*Nebula*\")]{title, body, source}"}
   Result preview: [{"body":"Many players struggle with energy management..."}]

🤖 Agent: The sources provide conflicting information regarding the win
condition for Nebula:

* According to the Nebula Core Rulebook and the strategy guide, you must
  collect 10 stars to win (Rulebook, Strategy Guide).

* According to the Nebula Official Errata v2.1, the win condition was updated
  to collecting 12 stars (Errata).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Vicarioy" rel="noopener noreferrer"&gt;
        Vicarioy
      &lt;/a&gt; / &lt;a href="https://github.com/Vicarioy/sanity-nebula-agent" rel="noopener noreferrer"&gt;
        sanity-nebula-agent
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Nebula Sanity Agent&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;A command-line agent that uses Gemini to answer questions from article content stored in Sanity.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;How It Works&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;The agent checks its Sanity connection, sends a built-in question about Nebula's win condition to Gemini, and answers using Sanity query results. It can query documents with GROQ or list the dataset's document types and fields. Its instructions require source URLs for claims and ask it to show contradictory sources side by side.&lt;/p&gt;

&lt;p&gt;The command prints the question, connection status, tool name, a result preview, and the final answer. Query arguments are not printed. Errors are written to stderr.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Stack&lt;/h2&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Node.js with ES modules&lt;/li&gt;
&lt;li&gt;Gemini model &lt;code&gt;gemini-3.5-flash-lite&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Sanity project &lt;code&gt;uyvc8si1&lt;/code&gt;, dataset &lt;code&gt;production&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;GROQ for Sanity queries&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Setup&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;Run these commands from the &lt;code&gt;agent&lt;/code&gt; directory:&lt;/p&gt;

&lt;div class="highlight highlight-source-powershell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;npm install&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Create an &lt;code&gt;.env&lt;/code&gt; file in the &lt;code&gt;agent&lt;/code&gt; directory with both keys:&lt;/p&gt;

&lt;div class="highlight highlight-source-dotenv notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-v"&gt;GEMINI_API_KEY&lt;/span&gt;&lt;span class="pl-k"&gt;=&lt;/span&gt;&lt;span class="pl-s"&gt;your-gemini-api-key&lt;/span&gt;
&lt;span class="pl-v"&gt;SANITY_API_TOKEN&lt;/span&gt;&lt;span class="pl-k"&gt;=&lt;/span&gt;&lt;span class="pl-s"&gt;your-sanity-api-token&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;The Sanity token must have…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Vicarioy/sanity-nebula-agent" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Stack:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Node.js&lt;br&gt;&lt;br&gt;
Gemini 3.5 Flash Lite (free tier)&lt;br&gt;&lt;br&gt;
&lt;a class="mentioned-user" href="https://dev.to/sanity"&gt;@sanity&lt;/a&gt;/client for GROQ queries&lt;br&gt;&lt;br&gt;
@google/genai for function calling&lt;/p&gt;

&lt;h2&gt;
  
  
  How i used Sanity
&lt;/h2&gt;

&lt;p&gt;** Structured Content Model**&lt;br&gt;
i defined a single document type in sanity studio using typescript:&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;article&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineType&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;article&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Article&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;document&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;defineField&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="nf"&gt;defineField&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;slug&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Slug&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;slug&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="nf"&gt;defineField&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;body&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Body&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="nf"&gt;defineField&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;source&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Source&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;url&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I populated it with three documents that set up a deliberate contradiction:&lt;/p&gt;

&lt;p&gt;The title and the source for each of the documents are:&lt;br&gt;
Nebula Core Rulebook; The Setup - &lt;a href="https://example.com/nebula-rulebook" rel="noopener noreferrer"&gt;https://example.com/nebula-rulebook&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nebula Official Errata - &lt;a href="https://example.com/nebula-errata" rel="noopener noreferrer"&gt;https://example.com/nebula-errata&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How to Win at Nebula - &lt;a href="https://example.com/nebula-strategy" rel="noopener noreferrer"&gt;https://example.com/nebula-strategy&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(The source URLs are placeholder examples. In a real deployment, this field would point to the actual publisher page.)&lt;/p&gt;
&lt;h2&gt;
  
  
  How the agents read and queries Content
&lt;/h2&gt;

&lt;p&gt;The agent uses function calling with Gemini. It has one primary tool and it was coded out with javascript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;name:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"query_documents"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;description:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Run a GROQ query against the Sanity dataset. Returns JSON."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;parameters:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;type:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;properties:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&gt;query:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;type:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;description:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A GROQ query string."&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;required:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the User asks a question, the LLM:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Writes a GROQ query itself: an example- *[_type == "article"]{title, body, source}&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;My code executes it via &lt;a class="mentioned-user" href="https://dev.to/sanity"&gt;@sanity&lt;/a&gt;/client against project uyvc8sil, dataset production&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The JSON result is fed back into the LLM's Context&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The LLM reads the content and answers with citations&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The system prompt tells the model: &lt;br&gt;
"When two sources contradict each other, show both claims side by side with their sources. Cite the source URL for every claim. Never invent information."&lt;/p&gt;

&lt;h2&gt;
  
  
  What the agent does with retrieved content
&lt;/h2&gt;

&lt;p&gt;Because the content is structured, the agent can do the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Compare specific fields across documents; it reads body from the rulebook and body from the errata and notices the numeric values differ.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Attribute each claim to its source URL; the citations aren't guessed, they're data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Distinguish types of documents; the errata is authoritative over the rulebook because its title says so.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A plain-text search would have surfaced both documents but had no way to reason about which was newer, more authoritative, or even that they contradicted each other.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designs decisions that mattered
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Schema-informed prompting; The LLM initially guessed type names like card and document, returning empty results. I injected a schema description into the system prompt so it knows to query *[_type == "article"]. This is a small detail that helps to dramatically improved reliability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sanity as the source of truth(where the articles are confirmed from); No content is hardcoded in the agent. Every fact the agent reports comes from a live GROQ query against the Content Lake. If I update an article in the Studio, the next question gets the updated answer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;useCdn(false); I disabled the CDN for the agent so responses are always fresh from the Content Lake and always 100% Real Time. This matters for an agent where "the latest errata" is the whole point. &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Sanity Project Details
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Project ID: uyvc8sil&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Dataset: production&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Document type: article (fields: title, slug, body, source)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;p&gt;I learned a lot because this is my first time of hearing about sanity. I learned how to made the case for structured content viscerally clear. So this provide me an opportunity that an LLM can reason about provenance when we have fields like title, body, and source defined in a schema.  This led me to understand the difference between these statements "here are two documents" and "these two sources disagree, and here's the URL for each claim".&lt;/p&gt;

&lt;p&gt;The hardest part wasn't the LLM logic, it was teaching the agent the schema. Once I stopped trying to make the model guess type names and instead told it was in the content Lake, everything clicked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thanks
&lt;/h2&gt;

&lt;p&gt;Thanks to Sanity and DEV for the challenge. It pushed me to build something I'd been wanting to try: an agent that treats structured content as the foundation, not an afterthought.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>sanitychallenge</category>
      <category>sanity</category>
      <category>ai</category>
    </item>
    <item>
      <title>Correlation vs. Causation: What They Actually Mean</title>
      <dc:creator>Victor</dc:creator>
      <pubDate>Mon, 14 Sep 2026 18:48:49 +0000</pubDate>
      <link>https://dev.to/vicarioy/correlation-vs-causation-what-they-actuallymean-1ejl</link>
      <guid>https://dev.to/vicarioy/correlation-vs-causation-what-they-actuallymean-1ejl</guid>
      <description>&lt;p&gt;" Correlation isn't causation" is one of the most repeated phrases in data science and one of the least explained. Here's what each term actually means, why the confusion happens, and four specific ways it goes wrong in practice.&lt;/p&gt;

&lt;p&gt;If you've spent any time around data, you've heard "correlation doesn't imply causation" so many times it's become background noise. But if you stopped someone and asked them to define correlation and causation separately, in plain words, a lot of people would struggle. So let's start there, before touching any of the traps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What Is Correlation?&lt;/strong&gt;&lt;br&gt;
Correlation is simply a measure of how two things tend to move together. If one goes up and the other usually goes up too, that's a positive correlation. If one goes up while the other tends to go down, that's a negative correlation. If they seem to have nothing to do with each other, that's little to no correlation.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A simple, real example:&lt;/em&gt; height and shoe size are correlated. Taller people tend to have bigger feet. That's a positive correlation, and it's intuitive, but notice we haven't said anything yet about why.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Correlation is purely descriptive:&lt;/em&gt; it tells you two variables tend to change together, and roughly how strongly, using a number called the correlation coefficient (often written as r), which ranges from -1 (perfectly opposite) to +1 (perfectly together), with 0 meaning no relationship at all. That's the entire job of correlation: describe a pattern. It has no opinion on what's causing that pattern, or whether anything is causing it at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What Is Causation?&lt;/strong&gt;&lt;br&gt;
Causation is a stronger, different claim: it means one variable directly produces a change in another. There's a mechanism connecting them, and if you intervened and changed the first variable, and only that variable, the second one would change as a result.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Take a simple example:&lt;/em&gt; turning up your oven's temperature causes your food to cook faster. If you change the temperature, cooking time changes because of that action. There's a direct, physical mechanism (more heat transfers energy faster), and you can test it by changing nothing else and watching the result change predictably.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A useful mental test for causation:&lt;/em&gt; "If I could reach in and change only this one thing, would the other thing change as a result?" If yes, that's a causal relationship. If you can't actually intervene, or if changing one doesn't reliably move the other, you're probably just looking at correlation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why We Keep Mixing Them Up&lt;/strong&gt;&lt;br&gt;
Human brains are pattern-matching machines; we're wired to notice when two things happen together and jump to "one must be causing the other," because that instinct was useful for survival long before it was useful for data analysis. The problem is that correlation is easy to measure (a single formula gives you a number) while causation is hard to establish (it usually requires careful experiments or reasoning about mechanisms). So we default to the easy signal and treat it like the hard one.&lt;/p&gt;

&lt;p&gt;This is exactly where the four classic traps come in; they're the specific ways a real, measurable correlation can exist without any real causation behind it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Confounding Variables (The Third-Variable Problem)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This happens when a hidden third factor is actually driving both variables you're looking at, making them look connected to each other when they're really both just reacting independently to something else. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;The classic case:&lt;/em&gt; ice cream sales and shark attacks both rise in summer. It's not that ice cream causes attacks, or attacks cause ice cream sales warmer weather (the confounder) independently drives more people to buy ice cream and more people into the ocean at the same time. The confounder never shows up if you only look at the two headline variables.&lt;/p&gt;

&lt;p&gt;In real analysis work, common hidden confounders include location, income, age, and season  they quietly explain relationships that look meaningful on the surface.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reverse Causality&lt;/strong&gt;&lt;br&gt;
Sometimes the relationship is genuinely causal, just pointing the opposite direction from what you assumed. A model might show "customers who contact support more often churn more," tempting you to conclude support interactions drive churn. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Just as plausible:&lt;/em&gt; customers who are already unhappy and about to leave contact support more because they're frustrated. The outcome is quietly driving the presumed cause.&lt;/p&gt;

&lt;p&gt;Reverse causality is sneaky because the correlation itself carries no signal about which direction is correct you need outside knowledge, timing data, or a controlled study to untangle it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Coincidence (Spurious Correlations)&lt;/strong&gt;&lt;br&gt;
Given enough variables and enough time, some completely unrelated trends will line up purely by chance. This is the trap behind the famous "spurious correlations" examples; like per-capita cheese consumption tracking almost perfectly with the number of people who died tangled in their bedsheets. There is no mechanism connecting them at all. With enough random series being compared, some pair will always match closely just by luck which is why testing dozens of variables against each other without a hypothesis ("data dredging") is such a well-known danger in statistics.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Selection Bias&lt;/strong&gt;&lt;br&gt;
This trap comes from how the data was collected, not from the variables themselves. If your sample isn't representative of the population you're trying to understand, you can manufacture a statistical link that isn't real or hide one that is.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A classic case:&lt;/em&gt; surveying only customers who already responded to a marketing email makes that email look more effective than it is, because people who never open emails are systematically missing from the data. The relationship you're seeing was baked in by how the sample was built, not by anything the two variables are doing to each other.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So How Do You Actually Prove Causation?&lt;/strong&gt;&lt;br&gt;
If correlation alone can't do it, what can? A few real tools researchers and data teams use:&lt;br&gt;
● &lt;em&gt;Randomized controlled trials (RCTs):&lt;/em&gt; randomly split a group in two, change one variable for one group only, and compare outcomes. Randomization cancels out confounders on average, so any difference in outcome can be attributed to the variable you changed.&lt;br&gt;
● &lt;em&gt;Natural experiments:&lt;/em&gt; when you can't randomize, look for situations where something close to random chance assigned the variable anyway (like a policy that took effect in one region but not a neighboring one).&lt;br&gt;
● &lt;em&gt;Statistical controls: _techniques like propensity score matching or regression with control variables that try to account for known confounders mathematically, when an experiment isn't possible.&lt;br&gt;
● _A believable mechanism:&lt;/em&gt; even strong statistical evidence is more convincing when there's a plausible, explainable reason why one thing would cause the other.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A Quick Checklist Before You Claim Causation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;● Could a third factor explain both variables?&lt;br&gt;
● Could the arrow of cause and effect be pointing the other way?&lt;br&gt;
● How many other variables did I compare before I found this one?&lt;br&gt;
● Does my sample actually represent the population I'm making claims about?&lt;br&gt;
● Do I have experimental evidence, or just an observed pattern?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why This Matters&lt;/strong&gt;&lt;br&gt;
None of this means correlation is useless a strong correlation is a great lead. It tells you where to look. The mistake isn't noticing the pattern; it's stopping there and calling it an explanation. Treat correlation as the start of an investigation, not the end of one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"""Demonstrating why correlation does not prove causation in marketing.

Scenario:
- A company spends more on ads during the holiday season.
- Customers also buy more during the holiday season.
- Holiday demand causes both variables to increase.
- Marketing spend has no causal effect in this simulation.
"""

import numpy as np

rng = np.random.default_rng(42)
months = np.arange(24)
holiday_season = (months % 12 &amp;gt;= 9).astype(float)

# Holiday demand is a hidden third variable (a confounder).
marketing_spend = 10 + 20 * holiday_season + rng.normal(0, 2, size=24)
sales = 100 + 80 * holiday_season + rng.normal(0, 5, size=24)

correlation = np.corrcoef(marketing_spend, sales)[0, 1]

print(f"Correlation between marketing spend and sales: {correlation:.2f}")
print("A high correlation alone does not prove that marketing caused the sales.")
print("In this simulation, the true causal effect of marketing spend is zero.")

# Once we compare months within the same season, the misleading relationship
# largely disappears because the hidden seasonal factor is held constant.
non_holiday = holiday_season == 0
holiday = holiday_season == 1

non_holiday_correlation = np.corrcoef(marketing_spend[non_holiday], sales[non_holiday])[
    0, 1
]
holiday_correlation = np.corrcoef(marketing_spend[holiday], sales[holiday])[0, 1]

print(f"Correlation in non-holiday months: {non_holiday_correlation:.2f}")
print(f"Correlation in holiday months: {holiday_correlation:.2f}")
print("To test causation, use a randomized marketing experiment or A/B test.")

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>datascience</category>
      <category>statistics</category>
      <category>machinelearning</category>
      <category>aiengineering</category>
    </item>
    <item>
      <title>HealthBridge: Engineering an Offline, CPU-Only LLM to Bridge Nigeria’s 1:4,000 Doctor-to-Patient Gap</title>
      <dc:creator>Victor</dc:creator>
      <pubDate>Sun, 06 Sep 2026 16:12:47 +0000</pubDate>
      <link>https://dev.to/vicarioy/healthbridge-engineering-an-offline-cpu-only-llm-to-bridge-nigerias-14000-doctor-to-patient-gap-34b6</link>
      <guid>https://dev.to/vicarioy/healthbridge-engineering-an-offline-cpu-only-llm-to-bridge-nigerias-14000-doctor-to-patient-gap-34b6</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/challenges/weekend-2026-09-03"&gt;Weekend Challenge: Generosity Edition&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;I built HealthBridge; an offline, CPU-only AI assistant designed to tackle Nigeria's severe doctor shortage and connectivity barriers.&lt;/p&gt;

&lt;p&gt;Specifically, here is exactly what I delivered:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;A fully offline LLM-powered health education tool&lt;br&gt;
I packaged a quantized large language model (Qwen2.5-1.5B-Instruct in GGUF Q4_K_M) so it runs entirely on a standard laptop's CPU. No internet, no GPU, and no recurring cloud costs making it viable for rural and peri-urban Nigerian communities where connectivity is spotty or unaffordable.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A triage and patient education interface&lt;br&gt;
I built an interactive assistant (working through the Streamlit interface challenges you saw in the report) that does four specific jobs for community health workers, patients, and caregivers:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;. Explains common symptoms in plain language.&lt;/p&gt;

&lt;p&gt;. Describes basic treatments (e.g., oral rehydration therapy for cholera/diarrhea).&lt;/p&gt;

&lt;p&gt;. Guides users on when to urgently seek hospital care (red-flag triage).&lt;/p&gt;

&lt;p&gt;. Actively steers people away from seeking advice from unqualified friends, neighbours, or unregistered local pharmacies.&lt;/p&gt;

&lt;p&gt;Crucially, it is strictly scoped as a complement to professionals; it never claims to replace clinical diagnosis.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;A lightweight, resource-optimized runtime&lt;br&gt;
I chose the llama.cpp runtime and carefully picked the Q4_K_M quantization to hit the sweet spot: coherent, medically-contextual responses while fitting comfortably inside an 8GB RAM budget (peaks at just 1.7 GB during my tests). The model runs at 16.76 tokens/second on integrated graphics; no discrete GPU required.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A zero-dependency deployment pipeline&lt;br&gt;
I set up a download_model.sh script to fetch the model via Hugging Face, but after that initial download, the entire inference pipeline runs completely offline. I ensured the model file itself isn't committed to git, so evaluators can pull it fresh and run it immediately on the ADTC standard laptop (10th–12th gen i5, 8GB DDR4, Ubuntu 22.04).&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In short: I built a portable, cost-free, offline health-information kiosk-in-a-laptop that gives populations accurate, actionable guidance—bridging the gap between community pharmacies and overstretched hospitals, without ever pretending to be a doctor. &lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://youtu.be/PI3vzJGBDSA" rel="noopener noreferrer"&gt;https://youtu.be/PI3vzJGBDSA&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/Vicarioy/adtc-2026-submission-template.git" rel="noopener noreferrer"&gt;https://github.com/Vicarioy/adtc-2026-submission-template.git&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;Here is the technical walkthrough of how I built HealthBridge; covering the architecture, the tough trade-offs, and the exact steps I took to wrestle it onto resource-constrained hardware.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Model Selection: The "Goldilocks" Search
The first decision was choosing the brain. I couldn't just pick the biggest model.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What I tested: I ran local benchmarks with Phi-3-mini-4k (3.8B—too much RAM, risked exceeding the 8GB target), Qwen2.5-0.5B (fit easily but gave vague, sometimes incorrect health advice), and Llama-3.2-1B (decent, but weaker at following multi-turn health Q&amp;amp;A prompts).&lt;/p&gt;

&lt;p&gt;The decision: I chose Qwen2.5-1.5B-Instruct. It sits perfectly in the middle strong enough to reason through symptom-checking and triage logic, yet small enough to leave headroom for the OS and interface. Its multilingual tokenizer also helps handle local language nuances when users describe symptoms in pidgin or their native tongue.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Quantization Strategy (The Accuracy-vs-Memory Trade-off)
A raw 1.5B FP16 model wouldn't fit in 8GB comfortably, so I needed compression.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I rejected Q4_0 (too much precision loss—critical for medical context) and Q5/Q8 (too heavy).&lt;/p&gt;

&lt;p&gt;I settled on GGUF Q4_K_M. This uses K-means grouping on the most important weight matrices. It applies 4-bit quantization but allocates bits more intelligently across layers. This preserves the model's "reasoning" capability far better than flat quantization, giving me medically coherent responses while keeping peak RAM at just 1.7GB, well under the 8GB ceiling.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Runtime &amp;amp; Inference Engine (llama.cpp)
The competition rules strictly mandated llama.cpp, but honestly, it was the perfect choice anyway.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I compiled llama.cpp with standard x86-64 CPU optimizations (leveraging AVX2 instructions on Intel chips).&lt;/p&gt;

&lt;p&gt;I wrote a Python wrapper that uses llama-cpp-python to interface with the .gguf file. The key tweak here was setting n_ctx=32768 (to utilize Qwen's full context window for long patient histories) but carefully tuning n_batch to 512 to maximize throughput without blowing up the CPU cache.&lt;/p&gt;

&lt;p&gt;This setup gave me 16.76 tokens/second on an integrated UHD 620; fast enough for real-time conversation.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Interface Nightmare (Where I Almost Broke)
You noted in the report that building the interface was a massive problem, and you're right. Streamlit kept timing out or failing silently.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The issue: Loading a 1.5B GGUF model inline during Streamlit's startup sequence blocks the main thread. Streamlit's watchdog timer would kill the process if the model loaded slower than ~10 seconds.&lt;/p&gt;

&lt;p&gt;How I fixed it: I decoupled the model lifecycle. Instead of loading the model globally, I wrapped it inside a st.cache_resource decorator with lazy initialization meaning the model only loads on the first user prompt, not when the page boots. I also spawned the llama.cpp inference in a separate background thread with a queue, so the UI remains responsive while the model warms up. This single architectural shift made the app stable and prevented timeouts.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deployment &amp;amp; Zero-Connectivity Pipeline
Since internet is the problem, I had to make internet the non-requirement.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wrote a download_model.sh script to fetch the .gguf from Hugging Face only once during setup.&lt;/p&gt;

&lt;p&gt;I strictly excluded the ~900MB model file from git using .gitignore. The evaluator runs the script, grabs the model, and from that point forward, every inference call hits the local filesystem. No API keys, no network retries, no cloud latency.&lt;/p&gt;

&lt;p&gt;The entire app runs on localhost; I pinned the environment dependencies (llama-cpp-python, streamlit) to specific versions to guarantee reproducible builds on the ADTC Ubuntu 22.04 target.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Domain-Specific Prompt Engineering
Because I'm legally and ethically scoped to education/triage, not diagnosis, I hardcoded a system prompt that:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Explicitly states: "I am an educational assistant, not a doctor. Do not take my advice as a clinical diagnosis."&lt;/p&gt;

&lt;p&gt;Guides the model to always include a "Red Flag" section in responses (e.g., "Go to a hospital immediately if you see blood in stool or have a high fever over 39°C").&lt;/p&gt;

&lt;p&gt;Instructs the model to actively deflect dangerous queries if a user asks for a prescription, it responds with safety advice and urges them to visit an MDCN-registered practitioner.&lt;/p&gt;

&lt;p&gt;Prize Technology Highlight: This project squarely falls into Edge AI / On-Device AI categories. By combining gguf quantization with llama.cpp, I built a production-ready, CPU-only LLM that democratizes health information in bandwidth-scarce regions; proving that impactful AI doesn't require the cloud. &lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
    </item>
    <item>
      <title>5 ML Math Concepts I Stopped Memorizing (And Started Actually Using)</title>
      <dc:creator>Victor</dc:creator>
      <pubDate>Sun, 06 Sep 2026 15:16:26 +0000</pubDate>
      <link>https://dev.to/vicarioy/5-ml-math-concepts-i-stopped-memorizing-and-started-actually-using-md5</link>
      <guid>https://dev.to/vicarioy/5-ml-math-concepts-i-stopped-memorizing-and-started-actually-using-md5</guid>
      <description>&lt;p&gt;I've been deep in linear algebra and calculus for the better part of a year. But up until this week, I realized i was still treating core ML math like the chain rule and eigenvalue; as a syllabus checklist. Week 3 of my structured deep dive is when i finally forced myself to stop, derive them by hand and connect them to actual code using numpy. Here are some of the concepts that finally clicked:&lt;br&gt;
&lt;strong&gt;1. The Chain Rule Is the Engine Behind Backpropagation&lt;/strong&gt;&lt;br&gt;
Every neural network learns by adjusting its weights, and it knows which direction to adjust them in because of the chain rule. A network is really just a stack of functions: input goes through a layer, then an activation function, then another layer, and so on. To know how much a single weight buried deep in that stack contributed to the final error, you need to differentiate through every function it passed through. That's exactly what the chain rule lets you do. I traced this end to end: starting from gradient descent (which says "move the weights a little in the&lt;br&gt;
direction that reduces error"), through the activation function's own derivative, back to the weight itself. Once I could follow that chain by hand instead of trusting a library to do it, backpropagation stopped feeling like magic and started feeling like bookkeeping.&lt;br&gt;
&lt;strong&gt;2. The Gaussian (Normal) Distribution Is Everywhere in ML&lt;/strong&gt;&lt;br&gt;
The bell curve isn't just a statistics-class cliché; it shows up constantly in machine learning: in how we initialize weights, in the noise assumptions behind linear regression, in probabilistic models, and in how we reason about errors clustering around a mean. Understanding its two parameters; mean and variance, is really understanding "where is the data centered" and "how spread out isit," which turns out to be one of the most reused ideas in the entire field.&lt;br&gt;
&lt;strong&gt;3. Eigenvalues Are the Secret Behind Face Recognition&lt;/strong&gt;&lt;br&gt;
This was the concept that surprised me most. Eigenvalues and eigenvectors which sound purely academic are the foundation of techniques like PCA (Principal Component Analysis) and the classic "Eigenfaces" approach to face recognition. The idea: a face image has thousands of pixels, but most of the meaningful variation between different faces can be captured by a much smaller set of directions, the eigenvectors with the largest eigenvalues. Compress along those directions, and you keep what matters while discarding noise. Linear algebra, doing real work.&lt;br&gt;
&lt;strong&gt;4. The Laplace Rule of Succession: Smoothing for Probability&lt;/strong&gt;&lt;br&gt;
What's the probability of an event you've never seen happen? Naively, zero but that's usually wrong and dangerous in ML, especially in text and classification models where an unseen word or category shouldn't automatically get a probability of zero. The Laplace rule of succession solves this by adding a small smoothing count to every outcome, so nothing is ever assigned a flat-out impossible probability. It's a simple idea with a name that sounds far more intimidating than it is.&lt;br&gt;
&lt;strong&gt;5. Sampling and Confidence Intervals: Choosing Data You Can&lt;br&gt;
Trust&lt;/strong&gt;&lt;br&gt;
You can't train on an entire population, so you sample from it; but that raises an obvious question: how do you know your sample actually represents the population? This is where confidence intervals come in. They give you a principled range around an estimate, along with a stated level of certainty, so that when you pick a dataset you know how much to trust conclusions drawn from it. This felt directly relevant to my own work, since a lot of real-world data (like the regional health data in my thesis project) is sampled, imperfect, and needs exactly this kind of scrutiny.&lt;br&gt;
&lt;strong&gt;Why This Matters&lt;/strong&gt;&lt;br&gt;
None of these five ideas are new inventions, they're decades old. But sitting with the math instead of skipping to the library call changed how I read papers. When something behaves strangely, I now have a better instinct for whether the culprit is a distributional assumption, a smoothing issue, or a bad sample, instead of just tuning hyperparameters and hoping.&lt;br&gt;
Next up: going deeper into differential calculus and connecting it more directly to optimizers beyond&lt;br&gt;
plain gradient descent.&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>mlmath</category>
      <category>linearalgebra</category>
      <category>statistics</category>
    </item>
  </channel>
</rss>
