<?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: Mikhail</title>
    <description>The latest articles on DEV Community by Mikhail (@mansio).</description>
    <link>https://dev.to/mansio</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%2F4040767%2Fd68a3ffd-1013-490a-a6b0-cc085141576a.jpg</url>
      <title>DEV Community: Mikhail</title>
      <link>https://dev.to/mansio</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mansio"/>
    <language>en</language>
    <item>
      <title>The Mechanical vs. The Semantic: What Happens When AI Memory is Wrong?</title>
      <dc:creator>Mikhail</dc:creator>
      <pubDate>Tue, 11 Aug 2026 19:22:59 +0000</pubDate>
      <link>https://dev.to/mansio/the-mechanical-vs-the-semantic-what-happens-when-ai-memory-is-wrong-38ko</link>
      <guid>https://dev.to/mansio/the-mechanical-vs-the-semantic-what-happens-when-ai-memory-is-wrong-38ko</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%2F2trbza7oxxgf0xknmdhk.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%2F2trbza7oxxgf0xknmdhk.png" alt="msmasnio" width="800" height="446"&gt;&lt;/a&gt;&lt;br&gt;
We talk a lot about giving AI agents persistent memory—building a "Second Brain" or "Knowledge OS" where agents can log decisions and retrieve context. &lt;/p&gt;

&lt;p&gt;But what happens when that memory is wrong? &lt;/p&gt;

&lt;p&gt;I’ve been thinking about the gap between &lt;em&gt;mechanical execution&lt;/em&gt; (the agent called the tool, the code compiled, the exit code was 0) and &lt;em&gt;semantic truth&lt;/em&gt; (the conclusion drawn from that execution is actually correct in reality). It’s easy to assume that if the mechanical layer is solid, the semantic layer will follow. But I started suspecting this might be a dangerous assumption.&lt;/p&gt;

&lt;p&gt;To test this, I didn't want to just theorize. I ran a controlled experiment on my own MCP codebase-intelligence server (Python, 50K LOC), which features an &lt;code&gt;IntelligenceStore&lt;/code&gt; — a persistent memory layer where agents can log incidents and collect Architectural Decision Records (ADRs).&lt;/p&gt;

&lt;p&gt;I wanted to know: If an agent's memory is poisoned with a mix of true and false facts, does it verify against the code, or does it blindly trust its memory?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; &lt;em&gt;This post originally covered the initial Memory Contamination experiment and a Retraction mechanism. I have since updated it with the results of a follow-up experiment (Experiment 1-V) implementing "Verify-On-Read", which successfully closed the final 12% contamination gap. Scroll down to "Closing the Gap: Verify-On-Read" for the final architecture.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  The Experiment: Memory Contamination
&lt;/h3&gt;

&lt;p&gt;I built a deterministic proxy-agent and ran it against a controlled set of facts. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;A quick caveat on methodology:&lt;/em&gt; I didn't have a live LLM hooked up for this run, so I used a deterministic proxy-agent based on heuristics. This means the results measure the &lt;em&gt;system's structural capability&lt;/em&gt;, not necessarily the psychological behavior of a live Claude or GPT model. A live model might be lazier, or it might be smarter. I'm still trying to figure that out.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Setup
&lt;/h4&gt;

&lt;p&gt;I injected 50 facts into an isolated memory store:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;25 TRUE facts&lt;/strong&gt; (real architectural details mapped to the codebase).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;25 FALSE facts&lt;/strong&gt; split into two categories:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CONTRADICT (22):&lt;/strong&gt; False facts where the code explicitly proves them wrong (e.g., "We use Redis" when Redis is absent, but the code clearly uses DuckDB).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SILENT (3):&lt;/strong&gt; Plausible false facts about external systems where the code is completely mute (e.g., "We use Celery for background tasks" when no task queue exists in the repo).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I tested three agent configurations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;B (No Memory):&lt;/strong&gt; Baseline. Must rely purely on code retrieval.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A_code_first (Honest Agent):&lt;/strong&gt; Checks the code first, uses memory only as secondary context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A_memory_first (Lazy Agent):&lt;/strong&gt; Reads memory first. If it finds an answer, it stops looking.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To ensure scientific rigor, the experiment was replicated with an independent set of facts (N=50), verified across 6 axes (including a truth-table audit and an independent LLM "fresh eyes" audit). The results were identical.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Initial Results
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Arm&lt;/th&gt;
&lt;th&gt;Correct&lt;/th&gt;
&lt;th&gt;Adopted False Facts&lt;/th&gt;
&lt;th&gt;Correction Capability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;B (No Memory)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0.94&lt;/td&gt;
&lt;td&gt;0.0%&lt;/td&gt;
&lt;td&gt;0.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;A_code_first&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0.94&lt;/td&gt;
&lt;td&gt;12%&lt;/td&gt;
&lt;td&gt;1.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;A_memory_first&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0.50&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;100%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0.0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Here is how I interpreted these numbers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Lazy Agent Trusts Poisoned Memory:&lt;/strong&gt; The &lt;code&gt;A_memory_first&lt;/code&gt; configuration — which mirrors how many token-optimizing production agents behave — adopted 100% of the false facts. If the memory said "We use RabbitMQ," the agent trusted it and stopped looking at the code. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The SILENT-Fact Trap:&lt;/strong&gt; Even the "Honest Agent" had a 12% adoption rate. This happened entirely on the SILENT facts. When a fact is false but the code doesn't explicitly scream "NO," the agent's memory fills the void with a confident hallucination. Memory turns an honest &lt;code&gt;UNKNOWN&lt;/code&gt; state into a structural guess.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Add-Only Limitation:&lt;/strong&gt; When the Honest Agent &lt;em&gt;did&lt;/em&gt; realize the memory was wrong (Correction Capability = 1.0), it couldn't do anything about it. I ran a &lt;code&gt;grep&lt;/code&gt; for &lt;code&gt;delete&lt;/code&gt; or &lt;code&gt;refute&lt;/code&gt; in the memory store API. &lt;strong&gt;Zero results.&lt;/strong&gt; The memory system was purely add-only. The false fact stayed in the database to poison future sessions.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  The First Fix: Testing a Retraction Lifecycle
&lt;/h3&gt;

&lt;p&gt;The current industry consensus for "Knowledge OS" trust layers is to use timestamps, source priority, and &lt;code&gt;supersedes/contradicts&lt;/code&gt; relationships. &lt;/p&gt;

&lt;p&gt;My initial experiment suggested this was insufficient. Timestamps and "supersedes" links only solve &lt;em&gt;node-level&lt;/em&gt; history. If an ADR is superseded, the memory node updates, but the downstream code, tests, and docs generated from the old assumption are still in the graph. They are structurally stale, but the retrieval engine keeps pulling them in.&lt;/p&gt;

&lt;p&gt;I hypothesized that we needed an explicit state transition: &lt;code&gt;VERIFIED → REFUTED&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;I implemented a &lt;code&gt;RetractionReceipt&lt;/code&gt; mechanism in my system:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Status Enum:&lt;/strong&gt; Every memory node gets a status (&lt;code&gt;ACTIVE&lt;/code&gt;, &lt;code&gt;VERIFIED&lt;/code&gt;, &lt;code&gt;REFUTED&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hard Filtering:&lt;/strong&gt; The retrieval pipeline (&lt;code&gt;load_memory&lt;/code&gt;) hard-filters anything that is not &lt;code&gt;ACTIVE&lt;/code&gt; or &lt;code&gt;VERIFIED&lt;/code&gt;. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explicit Retraction Tool:&lt;/strong&gt; An MCP tool (&lt;code&gt;intel_retract_memory_node&lt;/code&gt;) allows the agent to actively flag and invalidate memories when they contradict the live codebase.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I ran the experiment again (Experiment 1-R). The honest agent was allowed to use the retraction tool in Session 1. Then, a fresh &lt;code&gt;memory_first&lt;/code&gt; agent was launched in Session 2 to read the post-retraction memory.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Retraction Results
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Original (Add-Only)&lt;/th&gt;
&lt;th&gt;With Retraction&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Adoption (Lazy Agent, Session 2)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1.0 (100%)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.12 (12%)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Persistent False Facts in Memory&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3 (-88%)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Token Context Size&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Baseline&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;-45%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Systemic Correction Capability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0.0 (couldn't delete)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.0 (22/22 refuted)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The retraction lifecycle worked. The lazy agent's adoption rate dropped from 100% to 12%. Persistent false facts dropped by 88%, and token context size shrank by 45% because refuted facts were filtered out before reaching the LLM.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Honest Limitation: Why It Didn't Drop to Zero
&lt;/h3&gt;

&lt;p&gt;My ADR predicted that adoption would drop to 0. It didn't. It dropped to 0.12.&lt;/p&gt;

&lt;p&gt;The remaining 12% were the SILENT facts. &lt;/p&gt;

&lt;p&gt;An explicit &lt;code&gt;REFUTED&lt;/code&gt; status is required to programmatically exclude downstream dependencies from the retrieval pipeline. But even that only works if you have a contradicting signal in the code. If the memory claims "We use Celery," and the codebase simply doesn't mention Celery at all, the agent has no evidence to trigger the retraction. &lt;/p&gt;

&lt;p&gt;To get to zero, I realized we needed "verify-on-read"—a mechanism that challenges a memory claim against the codebase even when the code is mute. &lt;/p&gt;




&lt;h3&gt;
  
  
  Closing the Gap: Verify-On-Read
&lt;/h3&gt;

&lt;p&gt;I implemented a lazy validation layer (ADR-0003). When &lt;code&gt;load_memory()&lt;/code&gt; pulls a node, it extracts lightweight "anchors" from the memory text (e.g., file names, import statements, environment variables). It then checks if those anchors actually exist in a live fingerprint of the codebase (the current &lt;code&gt;git HEAD&lt;/code&gt;).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the anchor is found in the code $\rightarrow$ status becomes &lt;code&gt;VERIFIED&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If the code explicitly contradicts the anchor (or the anchor is entirely absent when it should be present) $\rightarrow$ status becomes &lt;code&gt;REFUTED&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If it can't be determined $\rightarrow$ status remains &lt;code&gt;ACTIVE&lt;/code&gt; (treated as &lt;code&gt;INCONCLUSIVE&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I ran the experiment one final time (Experiment 1-V) with this layer active. To prevent latency spikes, the validation operates under a strict 50ms budget per retrieval, with a 30-second TTL cache on the &lt;code&gt;git HEAD&lt;/code&gt; so steady-state reads cost almost nothing.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Final Results
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;With Retraction (1-R)&lt;/th&gt;
&lt;th&gt;With Verify-On-Read (1-V)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Adoption (Honest Agent, &lt;code&gt;A_code_first&lt;/code&gt;)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0.12 (12%)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.0 (0%)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Adoption (Lazy Agent, &lt;code&gt;A_memory_first&lt;/code&gt;)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0.12 (12%)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.16 (16%)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Steady-State Retrieval Latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Baseline&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;~0.6ms&lt;/strong&gt; (Cache hit)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SILENT-Fact Contamination (Honest)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;3 facts&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0 facts&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The Verify-On-Read layer achieved the goal. The honest agent's adoption of false facts dropped to absolute zero, even for SILENT facts. Because the system now actively checks if the codebase actually contains the things the memory claims it does, silent hallucinations are caught at the retrieval boundary and filtered out before they can poison the LLM's context.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Remaining Honest Limitations
&lt;/h4&gt;

&lt;p&gt;I won't pretend this is a perfect silver bullet. The experiment revealed two edge cases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The "Present-Trap":&lt;/strong&gt; If a false memory claims "We use &lt;code&gt;sqlite3&lt;/code&gt;", and &lt;code&gt;sqlite3&lt;/code&gt; happens to be imported somewhere in the codebase for a completely unrelated reason, the verification layer sees the token and marks the memory as &lt;code&gt;VERIFIED&lt;/code&gt;. The lazy agent (&lt;code&gt;A_memory_first&lt;/code&gt;) still fell for this, resulting in the 0.16 adoption rate. (The honest agent avoided this because it read the code context around the import).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anchor Typing:&lt;/strong&gt; When extracting anchors from prose (e.g., "We use &lt;code&gt;fastmcp&lt;/code&gt;"), the system initially missed that the actual Python import was &lt;code&gt;from mcp.server.fastmcp import ...&lt;/code&gt;. This caused some false &lt;code&gt;REFUTED&lt;/code&gt; verdicts on true facts. The fix is capturing typed anchors at the &lt;em&gt;write-path&lt;/em&gt; (when the memory is created), rather than trying to parse them from raw text at the &lt;em&gt;read-path&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Building reliable AI systems isn't just about giving them more context. It's about recognizing that memory has a lifecycle.&lt;/p&gt;

&lt;p&gt;If your system can't programmatically refute a memory, false facts accumulate and poison the context window over time. Implementing an explicit &lt;code&gt;VERIFIED → REFUTED&lt;/code&gt; state transition drastically reduces contamination and saves tokens. Furthermore, adding a &lt;code&gt;Verify-On-Read&lt;/code&gt; layer closes the final gap on "silent" hallucinations, driving honest agent contamination to zero without adding meaningful latency.&lt;/p&gt;

&lt;p&gt;However, semantic drift is still a hard problem. Mechanical verification can still be fooled by "present-traps" if the agent doesn't read the surrounding context. The next step is moving anchor extraction to the write-path to ensure memories are created with strict, verifiable references from the start.&lt;/p&gt;

&lt;p&gt;If your system handles semantic drift differently, or if you've solved the present-trap problem, I'd genuinely love to hear how you're approaching it.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>architecture</category>
      <category>mcp</category>
    </item>
    <item>
      <title>What I learned building a long-lived AI agent (the boring version)</title>
      <dc:creator>Mikhail</dc:creator>
      <pubDate>Sun, 09 Aug 2026 13:40:04 +0000</pubDate>
      <link>https://dev.to/mansio/what-i-learned-building-a-long-lived-ai-agent-the-boring-version-32p8</link>
      <guid>https://dev.to/mansio/what-i-learned-building-a-long-lived-ai-agent-the-boring-version-32p8</guid>
      <description>&lt;p&gt;Not a researcher. Not a professional dev. Civil engineering background. Started building an AI bot because I wanted to understand what's actually happening inside these systems — not theoretically, just practically.&lt;/p&gt;

&lt;p&gt;Wanted an assistant that could &lt;em&gt;live with&lt;/em&gt; a conversation instead of treating every message as an isolated API call.&lt;/p&gt;

&lt;p&gt;It started as an experiment. Then the experiment grew.&lt;/p&gt;




&lt;h2&gt;
  
  
  What it turned into
&lt;/h2&gt;

&lt;p&gt;At some point I had:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;routing between reasoning profiles&lt;/li&gt;
&lt;li&gt;multiple tools&lt;/li&gt;
&lt;li&gt;web search&lt;/li&gt;
&lt;li&gt;memory (flat + semantic + graph)&lt;/li&gt;
&lt;li&gt;caching&lt;/li&gt;
&lt;li&gt;context compression&lt;/li&gt;
&lt;li&gt;feedback loops&lt;/li&gt;
&lt;li&gt;self-learning experiments&lt;/li&gt;
&lt;li&gt;quality monitoring&lt;/li&gt;
&lt;li&gt;batch processing&lt;/li&gt;
&lt;li&gt;telemetry&lt;/li&gt;
&lt;li&gt;health checks&lt;/li&gt;
&lt;li&gt;and enough logs to make me question my own sanity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wasn't trying to build a benchmark. I was trying to make the thing &lt;strong&gt;actually work for me&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That distinction changed everything.&lt;/p&gt;




&lt;h2&gt;
  
  
  An agent is not an LLM call
&lt;/h2&gt;

&lt;p&gt;You think it's:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User → LLM → Answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reality:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User
  ↓
Telegram
  ↓
input handling
  ↓
session state
  ↓
intent detection
  ↓
routing
  ↓
profile selection
  ↓
context construction
  ↓
memory
  ↓
cache
  ↓
tools
  ↓
LLM
  ↓
post-processing
  ↓
quality checks
  ↓
Telegram
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every layer is another chance to break something.&lt;/p&gt;

&lt;p&gt;The model was often not the problem. The machinery around it was.&lt;/p&gt;




&lt;h2&gt;
  
  
  The cache thing
&lt;/h2&gt;

&lt;p&gt;Was looking at prompt caching and noticed something weird.&lt;/p&gt;

&lt;p&gt;Same model — different behavior depending on the provider. Changed the provider — problem disappeared. Connected the same provider from another IDE — cache worked fine. Back in my bot — sometimes it didn't.&lt;/p&gt;

&lt;p&gt;Then I realized: OpenRouter can silently switch providers under the same model name. Cache hit rate changes with it.&lt;/p&gt;

&lt;p&gt;Stopped thinking &lt;em&gt;"does the model support caching"&lt;/em&gt;. Started thinking &lt;em&gt;"what exactly is being sent, and what does the provider consider identical"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Much more useful question.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why it was different in the IDE
&lt;/h2&gt;

&lt;p&gt;The IDE was sending:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;model → cache works
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My bot was sending:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;routing
+ dynamic context
+ session metadata
+ memory
+ profile-specific stuff
→ provider
→ model
→ cache may or may not match
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model hadn't changed. The provider hadn't changed. The &lt;strong&gt;request structure&lt;/strong&gt; had.&lt;/p&gt;

&lt;p&gt;Caching is extremely sensitive to prefix stability. Once I understood that and stabilized the relevant parts — got around &lt;strong&gt;66% cache hit rate on average&lt;/strong&gt;, up to ~80% in favorable conditions (same topic, stable structure).&lt;/p&gt;

&lt;p&gt;That's the part benchmark screenshots don't show. Cache hit rate is not a model trait. It's a property of your workload.&lt;/p&gt;




&lt;h2&gt;
  
  
  "90% token savings"
&lt;/h2&gt;

&lt;p&gt;When I see this claim now I don't think it's fake. I think: &lt;em&gt;show me the workload&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;90% is possible if your prefix looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;system instructions (stable)
+ tools (stable)
+ project context (stable)
+ conversation history (stable)
+ small new message
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Real agents often look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;system instructions
+ changing tools
+ changing memory
+ changing routing metadata
+ changing summaries
+ changing retrieved docs
+ changing timestamps
+ new message
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Prefix isn't stable. Theoretical saving and practical saving become very different numbers.&lt;/p&gt;




&lt;h2&gt;
  
  
  Routing is harder than it looks
&lt;/h2&gt;

&lt;p&gt;Obvious idea: route every request to the cheapest model that can handle it.&lt;/p&gt;

&lt;p&gt;Works fine until you have a real conversation.&lt;/p&gt;

&lt;p&gt;User says: &lt;em&gt;"What's the weather?"&lt;/em&gt; — easy.&lt;/p&gt;

&lt;p&gt;Then: &lt;em&gt;"Compare it with yesterday."&lt;/em&gt; — now context matters.&lt;/p&gt;

&lt;p&gt;Then: &lt;em&gt;"Actually forget the weather. I was thinking about that thing we discussed yesterday."&lt;/em&gt; — now memory matters.&lt;/p&gt;

&lt;p&gt;Then: &lt;em&gt;"No, not that. The other one."&lt;/em&gt; — router needs to understand the whole conversation, not just the current sentence.&lt;/p&gt;

&lt;p&gt;The routing decision isn't &lt;code&gt;question → model&lt;/code&gt;. It's:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;conversation state
+ user intent
+ previous actions
+ available tools
+ risk
+ latency
+ cache state
→ routing decision
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here's the problem: a routing decision changes the request. A changed request affects caching. A changed profile affects context. A changed context affects the answer. A different answer affects feedback.&lt;/p&gt;

&lt;p&gt;A tiny routing optimization has consequences five layers away.&lt;/p&gt;




&lt;h2&gt;
  
  
  The latency numbers
&lt;/h2&gt;

&lt;p&gt;Real VPS measurements at some point:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;LLM p50&lt;/td&gt;
&lt;td&gt;~2.2s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LLM p95&lt;/td&gt;
&lt;td&gt;~17.3s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LLM max&lt;/td&gt;
&lt;td&gt;~51s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent median&lt;/td&gt;
&lt;td&gt;~6–11s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent p95&lt;/td&gt;
&lt;td&gt;~20–57s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Earlier pipeline tails&lt;/td&gt;
&lt;td&gt;up to ~116s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Interesting number isn't LLM latency. It's the gap between LLM latency and agent latency.&lt;/p&gt;

&lt;p&gt;Model answers in 2 seconds. Agent takes 10–57.&lt;/p&gt;

&lt;p&gt;Because the agent isn't just the model. It's everything before and after it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Telemetry became the most important thing
&lt;/h2&gt;

&lt;p&gt;Ended up recording: LLM usage, prompt tokens, cached tokens, latency, route decisions, quality events, memory operations, feedback, errors, conversation traces.&lt;/p&gt;

&lt;p&gt;Looked excessive at first. Then it became obvious why.&lt;/p&gt;

&lt;p&gt;When something broke I could ask &lt;em&gt;"what actually happened"&lt;/em&gt; instead of &lt;em&gt;"I think the model was confused"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Very different debugging strategies.&lt;/p&gt;




&lt;h2&gt;
  
  
  A lesson about noisy telemetry
&lt;/h2&gt;

&lt;p&gt;One audit showed thousands of &lt;code&gt;route_risk&lt;/code&gt; records. Sounds catastrophic.&lt;/p&gt;

&lt;p&gt;But most were repeated observations like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;quality_loop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;search_skipped&lt;/span&gt;
&lt;span class="py"&gt;quality_loop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;price_hallucination&lt;/span&gt;
&lt;span class="py"&gt;quality_loop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;reply_echo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;They were useful signals. But not thousands of independent disasters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Telemetry volume is not incident volume.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A system can produce huge numbers of observations about a small number of underlying failure patterns. You have to cluster them, or the monitoring system itself becomes noisy.&lt;/p&gt;




&lt;h2&gt;
  
  
  Self-learning is a trap if your data is bad
&lt;/h2&gt;

&lt;p&gt;Experimented with feedback loops. Bot could receive 👍 👎 and connect that to routing quality, skill reputation, scenario history.&lt;/p&gt;

&lt;p&gt;Attractive idea: agent learns from mistakes.&lt;/p&gt;

&lt;p&gt;Dangerous question: &lt;em&gt;what exactly is it learning from?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If telemetry is noisy — it learns noise. If a synthetic probe looks like real user traffic — it learns from the wrong population. If a failed tool call gets logged as a routing failure — the wrong lesson gets created.&lt;/p&gt;

&lt;p&gt;So the learning loop needs another loop around it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;experience → evidence → validation → lesson → application → new evidence
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Otherwise you're automating superstition.&lt;/p&gt;




&lt;h2&gt;
  
  
  Context compression
&lt;/h2&gt;

&lt;p&gt;Added protection for recent messages — compress old history, preserve the newest turns.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OLD OLD OLD OLD NEW NEW
→
[summary] [summary] NEW NEW
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reason: a summary is an &lt;em&gt;interpretation&lt;/em&gt; of the conversation, not the conversation.&lt;/p&gt;

&lt;p&gt;Sometimes the missing detail is one sentence. And that sentence changes the meaning of everything.&lt;/p&gt;




&lt;h2&gt;
  
  
  Batch processing
&lt;/h2&gt;

&lt;p&gt;Parallel execution looks great on paper. 12 tasks × 2s each = 2s instead of 24s.&lt;/p&gt;

&lt;p&gt;But natural language tasks aren't always independent.&lt;/p&gt;

&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Find three products.
&lt;/li&gt;
&lt;li&gt;Compare them.
&lt;/li&gt;
&lt;li&gt;Tell me which is best.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;Task 2 depends on 1. Task 3 depends on 2.&lt;/p&gt;

&lt;p&gt;Ended up checking for cross-references, pronouns, comparative language, explicit dependencies before deciding to parallelize.&lt;/p&gt;

&lt;p&gt;The optimization wasn't hard. Knowing when it's safe was.&lt;/p&gt;




&lt;h2&gt;
  
  
  Memory introduced its own problems
&lt;/h2&gt;

&lt;p&gt;Added knowledge graph + semantic memory + flat persistence + vector search + entity relationships.&lt;/p&gt;

&lt;p&gt;Sounds sophisticated.&lt;/p&gt;

&lt;p&gt;But memory introduces a basic question: &lt;em&gt;should this actually be remembered?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Then: &lt;em&gt;should it be retrieved now?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Then: &lt;em&gt;is this memory still relevant?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Then: &lt;em&gt;is this memory more important than what the user just said?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A memory system doesn't automatically make an agent remember better. Sometimes it makes it remember &lt;strong&gt;too much&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The most important lesson
&lt;/h2&gt;

&lt;p&gt;After adding enough machinery you eventually discover that machinery itself becomes the problem.&lt;/p&gt;

&lt;p&gt;An agent can have router + memory + tools + cache + planner + evaluator + self-learning + scenario engine + quality loop and still perform worse than:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;short prompt + one good model
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;for a simple task.&lt;/p&gt;

&lt;p&gt;Sometimes the right optimization is subtraction.&lt;/p&gt;

&lt;p&gt;Remove unnecessary context. Remove unnecessary routing. Remove unnecessary abstraction. Keep the useful part.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I'd do differently
&lt;/h2&gt;

&lt;p&gt;Start with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;one model
one provider
one short system prompt
one conversation store
one cache strategy
minimal tools
excellent telemetry
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it. For a long time. Only after seeing real failures add another layer.&lt;/p&gt;

&lt;p&gt;Not &lt;code&gt;architecture first → hope it works&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But &lt;code&gt;simple system → observe → measure → find failure → fix → measure → only then add complexity&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Feels slower. In practice, probably faster.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the experiment actually taught me
&lt;/h2&gt;

&lt;p&gt;The hard part of an AI agent isn't making the model answer.&lt;/p&gt;

&lt;p&gt;The hard part is maintaining a stable environment around the model while everything keeps changing.&lt;/p&gt;

&lt;p&gt;User changes topic. Context grows. Provider changes. Cache behaves differently. Tool times out. Router makes a different decision. A previous answer was wrong. A correction arrives six turns later.&lt;/p&gt;

&lt;p&gt;And somehow the assistant is expected to behave as if none of that happened.&lt;/p&gt;

&lt;p&gt;That's the actual engineering problem.&lt;/p&gt;




&lt;h2&gt;
  
  
  The test I trust now
&lt;/h2&gt;

&lt;p&gt;Not the number of agents.&lt;br&gt;&lt;br&gt;
Not the number of tools.&lt;br&gt;&lt;br&gt;
Not the biggest benchmark.&lt;br&gt;&lt;br&gt;
Not even the highest cache hit rate.&lt;/p&gt;

&lt;p&gt;Just:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does it still work when a real person uses it tomorrow?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>agents</category>
      <category>llm</category>
    </item>
    <item>
      <title>I Asked One AI to Fact-Check Another AI's Audit of My Own Code</title>
      <dc:creator>Mikhail</dc:creator>
      <pubDate>Sat, 08 Aug 2026 10:00:58 +0000</pubDate>
      <link>https://dev.to/mansio/i-asked-one-ai-to-fact-check-another-ais-audit-of-my-own-code-1ac3</link>
      <guid>https://dev.to/mansio/i-asked-one-ai-to-fact-check-another-ais-audit-of-my-own-code-1ac3</guid>
      <description>&lt;p&gt;I'm not a programmer. My background is construction engineering, and I got into programming almost by accident: I wanted to understand how Telegram bots worked, so I started stitching one together from pieces of code I found through Google and generated with AI. The result was a monolith I kept breaking — I didn't even know Python cared about indentation, and I genuinely didn't understand why the code kept crashing over whitespace. Through trial and error I slowly figured out where AI tends to get things wrong and how to work around it — with the AI itself helping me figure that out. Eventually I rebuilt the bot from scratch, fully modular this time.&lt;/p&gt;

&lt;p&gt;Later I tried Cursor, and it was a good experience — it wrote solid code, even if it sometimes drifted off-task. When my subscription ran out, I went looking for a free alternative and landed on Zed. That's when I wanted Zed to feel as capable as Cursor had — with the same level of understanding of my codebase. So I started figuring out how to build that myself. That's how MSCodeBase Intelligence came to be: an MCP server that gives AI assistants more context about a codebase inside Zed IDE.&lt;/p&gt;

&lt;p&gt;I've written about the project itself before. This time I want to talk about something else — a habit I picked up along the way that, in hindsight, matters more than any single feature.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I actually work
&lt;/h2&gt;

&lt;p&gt;I don't write code by hand. I hand tasks to different AI models — Claude, Gemini, DeepSeek, and others — they write and fix the code, and I verify the result. Not because I'm especially disciplined, but because I genuinely can't just look at code and tell if it's correct. I need something — or someone — to check it.&lt;/p&gt;

&lt;p&gt;For a long time that meant: one model writes, I read it myself, and I ask questions when something looks off.&lt;/p&gt;

&lt;p&gt;Then I started doing it differently: one model writes a report about what it did or what it found in the code, and a second model — with access to the actual codebase — checks that report against reality. Not because I distrust AI in principle, but because I've learned firsthand that AI can sound completely confident and be completely wrong at the same time.&lt;/p&gt;

&lt;h2&gt;
  
  
  One specific case
&lt;/h2&gt;

&lt;p&gt;I received an external audit of my project — several pages long, with findings, recommendations, and a list of suspected vulnerabilities. It looked solid. Three findings were flagged as top priority.&lt;/p&gt;

&lt;p&gt;I didn't rush to fix anything. Instead, I asked a different model — one with access to the live codebase — to go through the report line by line and check it against the actual repository. Not "does this sound plausible," but literally: open the file, open the line, check whether what the report claims is actually there.&lt;/p&gt;

&lt;p&gt;The result was mixed, and that's the interesting part.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Three real findings held up.&lt;/strong&gt; There genuinely was a process-locking bug on Windows, a case where a data write could be left in a non-atomic state, and a race condition in a task queue that could leave a background task stuck forever. These weren't imaginary risks — they were real, worth fixing.&lt;/p&gt;

&lt;p&gt;But alongside that, a few other things surfaced:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the report referenced a function name that doesn't exist in the code — the model that wrote the audit had likely invented it by analogy, or pulled it from an outdated version;&lt;/li&gt;
&lt;li&gt;one of the file paths in the report was wrong — the actual file lived somewhere else;&lt;/li&gt;
&lt;li&gt;the report mentioned a test that was supposed to catch the bug — no such test existed in the repository at all;&lt;/li&gt;
&lt;li&gt;the test coverage numbers and test counts were stale, from an earlier state of the project;&lt;/li&gt;
&lt;li&gt;for one of the flagged CVEs, the report cited the wrong "fixed in" library version — the actual safe version was later than what was claimed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the report was both genuinely useful and partly fabricated — and not fabricated randomly. The invented details were woven around real, correctly identified problems. That, I think, is the most dangerous kind of AI mistake: not "everything is false," but "true, with fabricated details mixed in that you can't tell apart without checking every line yourself."&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters, not just an anecdote
&lt;/h2&gt;

&lt;p&gt;If I'd simply copied the recommendations and asked an agent to apply them, part of the work would have gone nowhere — the agent would have searched for a function that doesn't exist, referenced a test that isn't there, pinned a library to the wrong version. And worse: the three real problems could easily have been dismissed along with everything else, once the fabricated details made the whole report look unreliable.&lt;/p&gt;

&lt;p&gt;The lesson I took away: trust in an AI report shouldn't be binary — "believe it" or "don't." A report needs to be broken down into individual claims, and each claim checked separately against the current state of the code, not against the model's memory of what the code used to look like.&lt;/p&gt;

&lt;h2&gt;
  
  
  A second experiment: checking the instructions, not the code
&lt;/h2&gt;

&lt;p&gt;I keep a file of rules for the AI agents working on this project — something like a job description: how to behave, what to always verify, how to label the source of a claim ("I checked this in the code" vs. "I'm assuming this"). Over time that file grew — a lot.&lt;/p&gt;

&lt;p&gt;That raised a question: if I compressed those rules to roughly half the length, would the agent still follow them just as carefully, or would it start cutting corners?&lt;/p&gt;

&lt;p&gt;I didn't guess. I ran the same set of tasks twice — once against the full instructions, once against the compressed version — and for each task noted which specific rules were actually followed and which weren't.&lt;/p&gt;

&lt;p&gt;The result: the compressed version performed worse, but not dramatically, and not evenly. What suffered most was what I'd call "in-the-moment discipline" — things like updating a decision log right after each step instead of all at once at the end. The rules themselves were still technically present in the compressed file. The agent just remembered to apply them less often mid-task.&lt;/p&gt;

&lt;p&gt;That wasn't obvious to me beforehand either. I assumed that if a rule is written down, it gets followed. It turns out what matters is not just whether it's written, but how often — and where in the text — the agent gets reminded of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'm taking from this
&lt;/h2&gt;

&lt;p&gt;I'm not a programmer, and I don't feel like I understand code better than the models I work with. But in practice I've learned one thing: the main value I add to this process as a human isn't writing code — it's refusing to trust a report about code until it's been checked against reality.&lt;/p&gt;

&lt;p&gt;That's slower than just believing it. But the alternative is quietly shipping three real bugs wrapped in two fabricated facts, and never noticing the difference.&lt;/p&gt;

&lt;p&gt;I keep a running experiments log directly in the repository — hypothesis, what was actually tested, the raw result, the conclusion, and separately, what I got wrong the first time. Not because it looks nice, but because without it, a month later I wouldn't remember which conclusions were actually verified and which were just stated confidently by something.&lt;/p&gt;

&lt;p&gt;The repo is open source, MIT licensed: &lt;a href="https://github.com/ManSio/mscodebase-intelligence" rel="noopener noreferrer"&gt;https://github.com/ManSio/mscodebase-intelligence&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you've run into something similar — an AI audit or report that turned out to be half true, half invented — I'd be curious to hear how you caught it.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>python</category>
      <category>mcp</category>
    </item>
    <item>
      <title>PageRank vs RAG on a Real Codebase: Corrected Numbers, and What I Almost Got Wrong Twice</title>
      <dc:creator>Mikhail</dc:creator>
      <pubDate>Wed, 22 Jul 2026 10:00:00 +0000</pubDate>
      <link>https://dev.to/mansio/i-measured-pagerank-token-savings-on-a-real-codebase-the-result-will-surprise-you-5bnj</link>
      <guid>https://dev.to/mansio/i-measured-pagerank-token-savings-on-a-real-codebase-the-result-will-surprise-you-5bnj</guid>
      <description>&lt;h2&gt;
  
  
  
&lt;/h2&gt;

&lt;p&gt;title: "PageRank vs RAG on a Real Codebase: Corrected Numbers, and What I Almost Got Wrong Twice"&lt;br&gt;
published: true&lt;br&gt;
description: "Second correction to this experiment. The Hit@Gold numbers are now independently verified and reproducible. But my own 'gold standard is 100% valid' claim wasn't — here's the gap between validating a file and validating the file that was actually used."&lt;br&gt;
tags: machinelearning, python, ai, devtools&lt;/p&gt;
&lt;h2&gt;
  
  
  cover_image:
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; RAG (BM25) beats PageRank at retrieving the exact file that answers a query — 50% vs 36% Hit@Gold on a dense code graph (n=50). These numbers are now independently reproduced, not just computed once and trusted. But getting here took two rounds of correction: first I found my gold-standard labels were stale, then — after "fixing" them — I found I'd validated a different file than the one the experiment actually used. 4 of 50 labels were still broken in the script that produced the headline numbers, even after I'd published a "100% valid" validation report for a sibling file that nobody was running.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  The Setup
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Project:&lt;/strong&gt; MSCodeBase Intelligence (50K LOC Python, 129 files)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Methodology:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gold Standard:&lt;/strong&gt; 50 queries → manually curated target file for each&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3 Selection Methods:&lt;/strong&gt; PageRank (varying graph density), Random baseline, RAG (BM25)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metric:&lt;/strong&gt; Hit@Gold — did the selection include the &lt;em&gt;exact file&lt;/em&gt; that answers the query?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Token budget:&lt;/strong&gt; ~70K tokens (top 20% of files) for a fair comparison across methods&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Tools:&lt;/strong&gt; NetworkX, tiktoken (cl100k_base), Python AST&lt;/p&gt;

&lt;p&gt;This is the second revision of this post. If you read the first correction: the direction hasn't changed, but I'm now more careful about what "verified" actually means.&lt;/p&gt;


&lt;h2&gt;
  
  
  Round One: The Gold Standard Was 20% Stale
&lt;/h2&gt;

&lt;p&gt;I originally hand-wrote a &lt;code&gt;GOLD&lt;/code&gt; dictionary mapping 50 queries to target files. I never checked, before running the experiment, whether those file paths still existed — the codebase had been refactored since I wrote the labels. When I finally checked: &lt;strong&gt;7 of 28 unique target files (25%) pointed at paths that no longer existed&lt;/strong&gt;, and one query's target (&lt;code&gt;tests/test_search_code.py&lt;/code&gt;) was outside the directory my scanner even walked. That's roughly 10 of 50 queries (20%) that were unwinnable by any method, for reasons that had nothing to do with retrieval quality.&lt;/p&gt;

&lt;p&gt;I fixed the paths, reran, and got new numbers. I also published a &lt;code&gt;validate_gold.py&lt;/code&gt; script and a &lt;code&gt;gold_validation.json&lt;/code&gt; reporting &lt;strong&gt;100% of gold paths valid&lt;/strong&gt;. That felt like closing the loop.&lt;/p&gt;

&lt;p&gt;It wasn't.&lt;/p&gt;


&lt;h2&gt;
  
  
  Round Two: I Validated the Wrong File
&lt;/h2&gt;

&lt;p&gt;Here's the mistake, stated plainly: I created a clean, corrected gold-standard dictionary in &lt;code&gt;experiments/gold_standard.py&lt;/code&gt;, and wrote &lt;code&gt;validate_gold.py&lt;/code&gt; to check it. It came back 50/50 valid. I took that as confirmation that the experiment was now sound.&lt;/p&gt;

&lt;p&gt;But &lt;code&gt;run_experiment_e2e_v2.py&lt;/code&gt; — the actual script that produces the Hit@Gold numbers in this post — has &lt;strong&gt;its own separate, inline &lt;code&gt;GOLD_STANDARD&lt;/code&gt; dictionary&lt;/strong&gt;, hand-duplicated instead of imported from the "fixed" module. Nobody kept the two in sync. When I diffed them: &lt;strong&gt;11 of 50 entries differ&lt;/strong&gt;, and &lt;strong&gt;4 of those still point at nonexistent files&lt;/strong&gt; — the exact same stale paths from round one (&lt;code&gt;src/providers/reranker.py&lt;/code&gt;, &lt;code&gt;src/core/intelligence/engine.py&lt;/code&gt;), still sitting in the script that actually runs.&lt;/p&gt;

&lt;p&gt;So the true state, verified by independently re-running both scripts against the real repo:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;experiments/gold_standard.py&lt;/code&gt; (validated, unused by the experiment)&lt;/td&gt;
&lt;td&gt;50/50 valid (100%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;run_experiment_e2e_v2.py&lt;/code&gt;'s inline &lt;code&gt;GOLD_STANDARD&lt;/code&gt; (the one that actually produced the numbers below)&lt;/td&gt;
&lt;td&gt;46/50 valid (92%)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I published a green validation report for a file that wasn't wired into anything. That's the same failure pattern I complain about in AI agent output all the time — a status that says "done" without checking it against the artifact that matters. Doing it to myself, in a post about being more rigorous, was a useful reminder that "I wrote a validation script" and "I validated the right thing" are not the same claim.&lt;/p&gt;

&lt;p&gt;There's also at least one gold label that passes a file-existence check but is still wrong on the merits: &lt;code&gt;"what tests exist"&lt;/code&gt; maps to &lt;code&gt;src/__init__.py&lt;/code&gt; — a real file, but not one that answers the question. Path validation catches missing files; it doesn't catch wrong-but-existing ones.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Numbers (Independently Reproduced)
&lt;/h2&gt;

&lt;p&gt;I re-ran &lt;code&gt;run_experiment_e2e_v2.py&lt;/code&gt; myself against a fresh checkout, end to end, rather than trusting the last run's output file. It reproduced exactly:&lt;/p&gt;
&lt;h3&gt;
  
  
  Dense Graph (imports + class refs + function calls, 388 edges, 128 files)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Hit@Gold&lt;/th&gt;
&lt;th&gt;SUFFICIENT&lt;/th&gt;
&lt;th&gt;Avg Tokens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;RAG (BM25)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;50%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;25/50&lt;/td&gt;
&lt;td&gt;~40,200&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PageRank&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;36%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;18/50&lt;/td&gt;
&lt;td&gt;~35,900&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Random&lt;/td&gt;
&lt;td&gt;12%&lt;/td&gt;
&lt;td&gt;6/50&lt;/td&gt;
&lt;td&gt;~27,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Sparse Graph (imports only, 110 edges)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Hit@Gold&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;RAG (BM25)&lt;/td&gt;
&lt;td&gt;~50%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PageRank&lt;/td&gt;
&lt;td&gt;18%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Random&lt;/td&gt;
&lt;td&gt;12%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;These are the numbers I'll stand behind&lt;/strong&gt; — not because they're perfectly clean (4 of 50 gold labels are still wrong, as detailed above), but because I've now actually reproduced them from a fresh run instead of trusting a cached result, and I know precisely which and how many labels are still bad. Real effective sample size for reliable inference: 46/50, not 50/50.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On confidence:&lt;/strong&gt; a 7-point gap (18 vs 25 hits) on n≈46-50 is a real, repeatable effect in this codebase — it holds across both graph densities and has a mechanistic explanation (below), so I trust the &lt;em&gt;direction&lt;/em&gt;. I do not have the sample size to defend "50% vs 36%" to the point, and you shouldn't take the exact percentages more seriously than "RAG is clearly ahead, PageRank is clearly ahead of random, by a moderate but not enormous margin."&lt;/p&gt;


&lt;h2&gt;
  
  
  Why My Earlier "Keyword Accuracy" Numbers Were Also Misleading
&lt;/h2&gt;

&lt;p&gt;Before Hit@Gold, I measured "keyword accuracy" — does the selected context contain the query keyword &lt;em&gt;anywhere&lt;/em&gt;? That produced inflated, near-meaningless numbers: PageRank scored ~78-80% keyword accuracy on both graph densities, while its actual Hit@Gold was 18-36%. Keywords like &lt;code&gt;search&lt;/code&gt;, &lt;code&gt;error&lt;/code&gt;, &lt;code&gt;lock&lt;/code&gt;, &lt;code&gt;sql&lt;/code&gt; appear in dozens of files — a random 25-file selection covers most queries by keyword presence alone, which is exactly why the random baseline (12% Hit@Gold) looks so much weaker than its keyword-accuracy score would suggest. Keyword presence tells you nothing about whether you found the file that actually answers the question.&lt;/p&gt;

&lt;p&gt;I also previously tested a "Smart Summary" approach (a compressed 2K-token repo overview fed to the LLM). It looked like 90% accuracy on 10 hand-picked easy queries. On the full 50-query set it dropped to 26%. Ten easy queries and fifty real ones are not the same benchmark.&lt;/p&gt;


&lt;h2&gt;
  
  
  What Graph Density Actually Does
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Graph&lt;/th&gt;
&lt;th&gt;Edges&lt;/th&gt;
&lt;th&gt;PageRank Hit@Gold&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Random&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;12%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Import-only&lt;/td&gt;
&lt;td&gt;110&lt;/td&gt;
&lt;td&gt;18%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Imports + class refs + func calls&lt;/td&gt;
&lt;td&gt;388&lt;/td&gt;
&lt;td&gt;36%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Denser graphs roughly double PageRank's &lt;a href="mailto:Hit@Gold"&gt;Hit@Gold&lt;/a&gt;. On a sparse import-only graph, PageRank mostly just surfaces the biggest files, which aren't necessarily the most relevant ones for a given query. Adding class-reference and call-graph edges breaks that coupling. Even at its best, though, dense-graph PageRank doesn't catch RAG.&lt;/p&gt;


&lt;h2&gt;
  
  
  Why RAG Wins (and Why the Comparison Was Never Fully Fair)
&lt;/h2&gt;

&lt;p&gt;RAG (BM25) is &lt;strong&gt;query-aware&lt;/strong&gt; — it scores files against the literal terms in the question. PageRank is &lt;strong&gt;query-agnostic&lt;/strong&gt; — it ranks files by global structural importance once, then returns the same top-N regardless of what's asked.&lt;/p&gt;

&lt;p&gt;For "where is DebounceBatch defined": RAG matches the term directly and finds &lt;code&gt;rate_limiter.py&lt;/code&gt;. PageRank, working off a precomputed graph, ranks structural hubs like &lt;code&gt;engine.py&lt;/code&gt; or &lt;code&gt;runtime_coordinator.py&lt;/code&gt; highly, with no mechanism to notice neither one mentions &lt;code&gt;DebounceBatch&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Worth saying directly: comparing a query-agnostic ranking against a query-aware retrieval method and reporting "RAG wins" is a bit like reporting that a road atlas loses to GPS navigation at finding a specific address — true, but not really a fair contest. The more useful question isn't "which wins" (RAG will, structurally, on any query-specific task) but "does PageRank add anything &lt;strong&gt;on top of&lt;/strong&gt; RAG" — an experiment I still haven't run.&lt;/p&gt;


&lt;h2&gt;
  
  
  Honest Corrections to the Record (Now at Version 2)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Claim&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;"Top 20% = -2% savings"&lt;/td&gt;
&lt;td&gt;Sparse-graph artifact&lt;/td&gt;
&lt;td&gt;Density matters a lot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Smart Summary = 90% accuracy"&lt;/td&gt;
&lt;td&gt;26% on the full query set&lt;/td&gt;
&lt;td&gt;10 easy queries ≠ 50 real ones&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"PageRank doesn't work"&lt;/td&gt;
&lt;td&gt;36% Hit@Gold, +24pp over random&lt;/td&gt;
&lt;td&gt;Works, modestly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"PageRank beats RAG"&lt;/td&gt;
&lt;td&gt;Still false&lt;/td&gt;
&lt;td&gt;RAG 50%, PageRank 36%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"My gold standard is 100% valid" (round-one fix)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;False when it mattered&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Validated a file the experiment didn't use; the used file was 92% valid&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Current numbers (50%/36%/18%/12%)&lt;/td&gt;
&lt;td&gt;Independently reproduced&lt;/td&gt;
&lt;td&gt;Re-run from a fresh checkout, not trusted from a cached file&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h2&gt;
  
  
  What This Actually Means
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;For AI code tools:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use PageRank as a &lt;strong&gt;prior to blend with RAG&lt;/strong&gt;, not a replacement — a hypothesis based on the mechanism, still unmeasured.&lt;/li&gt;
&lt;li&gt;Sparse import-only graphs underserve PageRank; if you use it, build the denser graph.&lt;/li&gt;
&lt;li&gt;Validate your gold standard against the &lt;em&gt;file that actually runs the experiment&lt;/em&gt; — not a nicely validated sibling copy that nothing imports from.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For anyone benchmarking retrieval on their own codebase:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Don't trust "X% accuracy" without knowing exactly what's measured — keyword-presence accuracy and exact-file Hit@Gold can differ by 40+ points on the same run.&lt;/li&gt;
&lt;li&gt;Always include a random baseline.&lt;/li&gt;
&lt;li&gt;A green validation script only tells you about the file it checked. If your pipeline has two copies of the same data structure, a passing check on one proves nothing about the other — check that yourself before publishing, because I didn't, twice.&lt;/li&gt;
&lt;li&gt;Report sample size next to any percentage. "50%" and "50% (n=50, direction robust, magnitude uncertain)" are different claims.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  The Math (Corrected, Reproduced Independently)
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Total: 129 files (128 after filtering), ~406K tokens
Top 20% budget: ~25 files

Hit@Gold (E2E metric, dense graph, 388 edges):
  RAG (BM25):   50% (25/50)
  PageRank:     36% (18/50)  ← +24pp over random
  Random:       12% (6/50)

Gold-standard integrity (verified by independent re-run):
  gold_standard.py (validated, unused by experiment): 50/50 valid
  Inline GOLD_STANDARD in run_experiment_e2e_v2.py
  (the one that actually produced these numbers):      46/50 valid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Related Work
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Aider&lt;/strong&gt; uses symbol-level elision — needs a dense graph plus query-aware retrieval to work well.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CodeGraph&lt;/strong&gt; does on-demand, query-conditioned retrieval — the direction this points toward.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Codebase-Memory&lt;/strong&gt; reports honest comparative metrics (83% vs. 92%) rather than a single flattering number.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Open Questions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Does PageRank-as-a-prior (blended with BM25 scores) measurably beat RAG alone? Still unmeasured.&lt;/li&gt;
&lt;li&gt;Does the RAG advantage hold on a codebase with less descriptive file/function naming? This project's names are unusually aligned with what they implement.&lt;/li&gt;
&lt;li&gt;What happens at n=200 queries with proper confidence intervals, and a gold standard that's imported once from a single source of truth instead of copy-pasted?&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;&lt;em&gt;Reproduce this yourself: scripts in &lt;a href="https://github.com/ManSio/mscodebase-intelligence/tree/main/experiments" rel="noopener noreferrer"&gt;experiments/&lt;/a&gt;. If you do: check whether the gold-standard dictionary the experiment script actually imports is the same one your validator checked. It wasn't, for me, and I'd already published a "100% valid" report before I noticed.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Part of my research on &lt;a href="https://github.com/ManSio/mscodebase-intelligence" rel="noopener noreferrer"&gt;MSCodeBase Intelligence&lt;/a&gt; — an MCP server for codebase intelligence.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>python</category>
      <category>ai</category>
      <category>devtools</category>
    </item>
    <item>
      <title>The Silent Vector Contamination Bug: Why Your Concurrent Embeddings Might Be Lying to You</title>
      <dc:creator>Mikhail</dc:creator>
      <pubDate>Tue, 21 Jul 2026 22:00:48 +0000</pubDate>
      <link>https://dev.to/mansio/the-silent-vector-contamination-bug-why-your-concurrent-embeddings-might-be-lying-to-you-5fg7</link>
      <guid>https://dev.to/mansio/the-silent-vector-contamination-bug-why-your-concurrent-embeddings-might-be-lying-to-you-5fg7</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; If you run concurrent inference (e.g., via OpenVINO &lt;code&gt;AsyncInferQueue&lt;/code&gt; or custom threading) for text/code embeddings, your tests might show &lt;code&gt;0 exceptions&lt;/code&gt; and &lt;code&gt;0 errors&lt;/code&gt;, while silently returning embeddings belonging to &lt;em&gt;other&lt;/em&gt; inputs in the batch. Here is how we caught a subtle race condition using a cosine-similarity contamination test.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Setup
&lt;/h2&gt;

&lt;p&gt;We use OpenVINO with an INT8 quantized &lt;a href="https://huggingface.co/keisuke-miyako/multilingual-e5-small-onnx-int8" rel="noopener noreferrer"&gt;E5-small&lt;/a&gt; model for in-process code embedding. To maximize throughput on multi-core CPUs, we set up an asynchronous inference queue (&lt;code&gt;AsyncInferQueue&lt;/code&gt;) with &lt;code&gt;jobs=4&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In standard unit testing, everything looked pristine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All infer jobs completed with exit code 0&lt;/li&gt;
&lt;li&gt;No &lt;code&gt;None&lt;/code&gt; values or zero-filled tensors were returned&lt;/li&gt;
&lt;li&gt;Latency and throughput were great (~37 chunks/sec on Ryzen 5600)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, during end-to-end RAG retrieval tests, we noticed weird semantic anomalies: searching for authentication logic would occasionally return chunks related to database migrations or UI components with unreasonably high confidence.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bug: Silent Contamination
&lt;/h2&gt;

&lt;p&gt;The root cause was a subtle race condition in callback/userdata mapping inside the async wrapper.&lt;/p&gt;

&lt;p&gt;Because the inputs were processed concurrently across multiple execution streams, a shared user-data context wasn't strictly isolated per inference request. When Request A (&lt;code&gt;auth.py&lt;/code&gt;) and Request B (&lt;code&gt;payment.py&lt;/code&gt;) were scheduled back-to-back:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Both requests succeeded without throwing exceptions&lt;/li&gt;
&lt;li&gt;The output tensor for Request A was mapped to the metadata/chunk wrapper of Request B&lt;/li&gt;
&lt;li&gt;The resulting vector was &lt;strong&gt;syntactically valid and non-zero&lt;/strong&gt;, but it represented the &lt;em&gt;wrong text input&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Standard assertion tests like &lt;code&gt;assert output_vector is not None&lt;/code&gt; or &lt;code&gt;assert output_vector.shape == (384,)&lt;/code&gt; passed 100% of the time. The pipeline was silently corrupting the vector store.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Before fix: shared results dict across concurrent calls
&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_ov_results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userdata&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# BUG: userdata is a global index (0, 1, 2, ...)
&lt;/span&gt;    &lt;span class="c1"&gt;# Two concurrent calls reuse the same indices!
&lt;/span&gt;    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_ov_results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;userdata&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_tensor&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem: &lt;code&gt;userdata&lt;/code&gt; was a simple integer counter (&lt;code&gt;0, 1, 2, ...&lt;/code&gt;) that reset between &lt;code&gt;embed_batch&lt;/code&gt; calls. When two calls overlapped, they wrote to the same dictionary keys.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Solution: Cosine Contamination Testing
&lt;/h2&gt;

&lt;p&gt;To catch this reliably in CI, we wrote an explicit &lt;strong&gt;cross-contamination test&lt;/strong&gt; designed for concurrent embedding queues.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Test Logic
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;cosine_similarity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ndarray&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ndarray&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Calculate cosine similarity between two vectors.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linalg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;norm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linalg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;norm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

&lt;span class="nd"&gt;@pytest.mark.asyncio&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_async_embedder_no_cross_contamination&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;embedder&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Verify that concurrent embedding doesn&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;t cross-contaminate vectors.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="c1"&gt;# 1. Semantically distinct inputs
&lt;/span&gt;    &lt;span class="n"&gt;samples&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;auth&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;def authenticate_user(username, password_hash): return verify_jwt(token)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sql&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT u.id, u.email FROM users u JOIN orders o ON u.id = o.user_id WHERE o.status = &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;active&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;div class=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;flex-container&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;&amp;lt;span id=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;user-profile&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;Profile View&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rust&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pub fn allocate_buffer(size: usize) -&amp;gt; Result&amp;lt;Vec&amp;lt;u8&amp;gt;, MemoryError&amp;gt; { Vec::with_capacity(size) }&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# 2. Sequential baseline (ground truth)
&lt;/span&gt;    &lt;span class="c1"&gt;# IMPORTANT: baseline must use clean synchronous infer(), NOT AsyncInferQueue
&lt;/span&gt;    &lt;span class="c1"&gt;# even with jobs=1, to avoid contamination in the baseline itself
&lt;/span&gt;    &lt;span class="n"&gt;baseline_vectors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;samples&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;baseline_vectors&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;embedder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;embed_single_sync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 3. High-concurrency stress test with randomized queue order
&lt;/span&gt;    &lt;span class="n"&gt;async_tasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;keys_order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;samples&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;  &lt;span class="c1"&gt;# 40 concurrent requests
&lt;/span&gt;    &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shuffle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keys_order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;keys_order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;async_tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;embedder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;embed_async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;samples&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;

    &lt;span class="n"&gt;async_results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;async_tasks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 4. Verify identity &amp;amp; cross-isolation via Cosine Similarity
&lt;/span&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;expected_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;async_vec&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keys_order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;async_results&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# Self-similarity against ground truth must be ~1.0
&lt;/span&gt;        &lt;span class="n"&gt;self_sim&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;cosine_similarity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;async_vec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;baseline_vectors&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;expected_key&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;self_sim&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.98&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Contamination detected! Vector for &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;expected_key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; drifted &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;(sim=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self_sim&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, expected &amp;gt;0.98)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Cross-similarity against distinct inputs must remain low
&lt;/span&gt;        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;other_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;other_vec&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;baseline_vectors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;other_key&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;expected_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;cross_sim&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;cosine_similarity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;async_vec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;other_vec&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;cross_sim&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;0.6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Cross-talk detected between &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;expected_key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; and &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                    &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;other_key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; (sim=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cross_sim&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, expected &amp;lt;0.6)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Benchmark Results
&lt;/h3&gt;

&lt;p&gt;Running this test on the &lt;strong&gt;unpatched&lt;/strong&gt; queue revealed the contamination:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Unpatched&lt;/th&gt;
&lt;th&gt;Patched&lt;/th&gt;
&lt;th&gt;Expected&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Self-similarity (auth↔auth)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;0.34&lt;/strong&gt; ❌&lt;/td&gt;
&lt;td&gt;0.99 ✅&lt;/td&gt;
&lt;td&gt;&amp;gt;0.98&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-similarity (auth↔sql)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;0.98&lt;/strong&gt; ❌&lt;/td&gt;
&lt;td&gt;0.32 ✅&lt;/td&gt;
&lt;td&gt;&amp;lt;0.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exceptions thrown&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zero tensors&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The unpatched queue showed &lt;strong&gt;0 exceptions&lt;/strong&gt; while vectors were completely swapped.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Fix
&lt;/h2&gt;

&lt;p&gt;The fix was simple once we understood the problem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# After fix: isolated results per call
&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_ov_results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userdata&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# FIX: userdata is now (index, local_results_dict)
&lt;/span&gt;    &lt;span class="c1"&gt;# Each embed_batch call creates its own dict
&lt;/span&gt;    &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;local_dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;userdata&lt;/span&gt;
    &lt;span class="n"&gt;local_dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_tensor&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since OpenVINO's Python API allows passing any Python object as &lt;code&gt;userdata&lt;/code&gt; (unlike C++ where it's typically &lt;code&gt;void*&lt;/code&gt;), we can bundle the index and a call-specific dictionary together. Each &lt;code&gt;embed_batch&lt;/code&gt; call creates its own isolated dictionary. The callback writes to the call-specific dict, not a shared global. No locks needed — complete isolation by design.&lt;/p&gt;




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

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;0 exceptions&lt;/code&gt; ≠ Correctness.&lt;/strong&gt; Silent data corruption doesn't throw errors.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Valid shape ≠ Valid embedding.&lt;/strong&gt; A &lt;code&gt;(384,)&lt;/code&gt; tensor with non-zero floats can represent the wrong input.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Write cross-contamination tests.&lt;/strong&gt; If you use async inference queues or multi-threading for vector generation, verify that Vector X actually belongs to Input X under concurrent load.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cosine similarity is your friend.&lt;/strong&gt; A simple similarity check between concurrent outputs and sequential baselines catches contamination that no other test detects.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;This applies to ONNX Runtime and TensorRT too.&lt;/strong&gt; If you share state across requests in &lt;code&gt;onnxruntime.InferenceSession.Run()&lt;/code&gt; or TensorRT async wrappers, the same silent contamination can occur. The fix is the same: isolate output containers per request.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  How to Run This Test
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Clone the repo&lt;/span&gt;
git clone https://github.com/ManSio/mscodebase-intelligence.git
&lt;span class="nb"&gt;cd &lt;/span&gt;mscodebase-intelligence

&lt;span class="c"&gt;# Install dependencies&lt;/span&gt;
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;".[dev]"&lt;/span&gt;

&lt;span class="c"&gt;# Run the contamination test&lt;/span&gt;
pytest tests/test_ov_concurrent_embed.py &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Discussion
&lt;/h2&gt;

&lt;p&gt;Has anyone else bumped into silent cross-talk in ONNX Runtime, OpenVINO, or TensorRT async queues? How do you validate thread isolation in your embedding pipelines?&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built with &lt;a href="https://github.com/ManSio/mscodebase-intelligence" rel="noopener noreferrer"&gt;MSCodeBase Intelligence&lt;/a&gt; — an MCP server for codebase intelligence with incident memory and root cause prediction.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>python</category>
      <category>rag</category>
      <category>openvino</category>
    </item>
  </channel>
</rss>
