<?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: Pavan Indupudi</title>
    <description>The latest articles on DEV Community by Pavan Indupudi (@the_infernix).</description>
    <link>https://dev.to/the_infernix</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%2F4196784%2F4c97d8af-c769-4e3a-9909-a421ddc17fe7.png</url>
      <title>DEV Community: Pavan Indupudi</title>
      <link>https://dev.to/the_infernix</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/the_infernix"/>
    <language>en</language>
    <item>
      <title>Inside VOICE-RAG: From Speech to Grounded Answers</title>
      <dc:creator>Pavan Indupudi</dc:creator>
      <pubDate>Tue, 20 Oct 2026 19:50:59 +0000</pubDate>
      <link>https://dev.to/the_infernix/inside-voice-rag-from-speech-to-grounded-answers-1khn</link>
      <guid>https://dev.to/the_infernix/inside-voice-rag-from-speech-to-grounded-answers-1khn</guid>
      <description>&lt;p&gt;&lt;strong&gt;Pavan Ganesh Indupudi (Infernix)&lt;/strong&gt; · Originally published at &lt;a href="https://the-infernix.github.io/blog/posts/voice-rag-speech-to-grounded-answers.html" rel="noopener noreferrer"&gt;the-infernix.github.io&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ask most RAG demos &lt;em&gt;"Who is the Prime Minister of India?"&lt;/em&gt; and they will happily hallucinate an answer from whatever passage happens to look semantically close. VOICE-RAG — built for HH Goa 2026 — answers that question with a refusal and a number:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Refused: Top retrieval score 0.779 below relevance floor 0.840&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That sentence is the whole thesis. The system answers spoken questions in English, Hindi, Gujarati, and Telugu — strictly from a curated 40,000-passage corpus — and &lt;strong&gt;refuses when the corpus does not contain the answer&lt;/strong&gt;. This article covers why that turned out to be the hard part, and how the 56 ms core around it was built.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem: fluent answers with no evidence
&lt;/h2&gt;

&lt;p&gt;Retrieval-augmented generation fails in two directions. Too loose, and the generator papers over missing evidence with fluent nonsense. Too strict, and the system refuses questions it could have answered. Most demos only tune for the first failure — making answers &lt;em&gt;sound&lt;/em&gt; grounded. We optimized for the second failure being &lt;em&gt;visible&lt;/em&gt;: every refusal states its score and its floor, and the UI shows the entire evidence chain either way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Constraints
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Voice is the primary input.&lt;/strong&gt; Hold-to-talk capture, 16 kHz mono WAV, Sarvam &lt;code&gt;saaras:v3&lt;/code&gt; speech-to-text. Voice adds roughly 1 s of STT; everything after it had to be fast enough to disappear.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Four languages, one index.&lt;/strong&gt; English, Hindi, Gujarati, Telugu over the MSMARCO-XI corpus — 10k passages per language, 384-dim multilingual E5 embeddings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Corpus-bounded by design.&lt;/strong&gt; World-knowledge questions with zero supporting passages must be refused, not answered from parametric memory.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No bundler.&lt;/strong&gt; The frontend is a vanilla-JS SPA served directly by FastAPI. Zero node_modules, zero build step.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Architecture: three guards around a 56 ms core
&lt;/h2&gt;

&lt;p&gt;The pipeline is a strict sequence, and every stage reports its latency and its decision into a trace the UI renders as a "why this answer" chain:&lt;/p&gt;

&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%2F6ykrhx0n22v40gm950so.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%2F6ykrhx0n22v40gm950so.png" alt="VOICE-RAG interface: a spoken question with evidence drawer, guard decision chain, and per-stage latency breakdown" width="799" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The evidence drawer and latency trace — every guard decision is user-visible, not server-side secret sauce.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input guard → embed → vector search → relevance guard → generate → grounding guard.&lt;/strong&gt; Measured medians (n = 2,000 benchmark queries):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Stage&lt;/th&gt;
&lt;th&gt;P50 latency&lt;/th&gt;
&lt;th&gt;What happens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Input guard&lt;/td&gt;
&lt;td&gt;0.02 ms&lt;/td&gt;
&lt;td&gt;Injection / unsafe content / length checks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Embed query&lt;/td&gt;
&lt;td&gt;23.67 ms&lt;/td&gt;
&lt;td&gt;E5 &lt;code&gt;query:&lt;/code&gt; prefix → 384-dim vector&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vector search&lt;/td&gt;
&lt;td&gt;30.74 ms&lt;/td&gt;
&lt;td&gt;Cosine over 40k passages + language rerank&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Relevance guard&lt;/td&gt;
&lt;td&gt;0.01 ms&lt;/td&gt;
&lt;td&gt;Top score vs calibrated floor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Answer generation&lt;/td&gt;
&lt;td&gt;0.09 ms&lt;/td&gt;
&lt;td&gt;Best-sentence extraction from top passage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grounding guard&lt;/td&gt;
&lt;td&gt;0.17 ms&lt;/td&gt;
&lt;td&gt;Token overlap, embedding fallback if needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total core&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;55.75 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Target: &amp;lt; 200 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Design decision&lt;/strong&gt;&lt;br&gt;
Default to &lt;strong&gt;extractive&lt;/strong&gt; answering — pick the best-supported sentence from the top passage — and make LLM generation strictly opt-in. Extractive answers are 0.09 ms and grounded by construction; generative answers take multiple seconds through OpenRouter and need a citation contract to stay honest. Latency budget first, generative flair when asked for.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The refusal engine: floors calibrated from measurements
&lt;/h2&gt;

&lt;p&gt;The relevance guard is one comparison — top-1 cosine score against a per-language floor — but the floors are not guesses. They were calibrated by measuring score distributions: known-relevant queries clustered around P25–P50 ≈ 0.84–0.86, off-corpus junk around ≈ 0.79–0.85, and the floors were set at the separation point:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Language&lt;/th&gt;
&lt;th&gt;Floor&lt;/th&gt;
&lt;th&gt;Low-confidence band&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;English&lt;/td&gt;
&lt;td&gt;0.840&lt;/td&gt;
&lt;td&gt;+0.03&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hindi&lt;/td&gt;
&lt;td&gt;0.850&lt;/td&gt;
&lt;td&gt;+0.03&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gujarati&lt;/td&gt;
&lt;td&gt;0.840&lt;/td&gt;
&lt;td&gt;+0.03&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Telugu&lt;/td&gt;
&lt;td&gt;0.840&lt;/td&gt;
&lt;td&gt;+0.03&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;em&gt;query's&lt;/em&gt; language selects the floor — not the top chunk's — so cross-language junk cannot sneak through under a borrowed threshold. Language detection is Unicode-script based (Telugu block → &lt;code&gt;te&lt;/code&gt;, Gujarati → &lt;code&gt;gu&lt;/code&gt;, Devanagari → &lt;code&gt;hi&lt;/code&gt;, else &lt;code&gt;en&lt;/code&gt;), and auto-detected language applies a soft +0.03 rerank bonus toward same-language passages over a 3× candidate pool.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Q: Who is the Prime Minister of India?   → REFUSED  (0.779 &amp;lt; 0.840)
Q: Who won the FIFA World Cup 2022?      → REFUSED  (0.796 &amp;lt; 0.840)
Q: How much does an Xbox 360 cost?       → answered ($80–$250, conf 0.933)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The grounding guard: answers must be traceable
&lt;/h2&gt;

&lt;p&gt;Passing the relevance gate is not enough. The grounding guard verifies that the answer is actually supported by retrieved text: token overlap ≥ 0.231 passes instantly; below that, an embedding-similarity check (≥ 0.794) runs as fallback. Ungrounded generations are replaced with the extractive answer or refused outright.&lt;/p&gt;

&lt;p&gt;When generative mode is enabled, the LLM must return strict JSON — &lt;code&gt;{answer, citations[], confidence}&lt;/code&gt; — citing only supplied passages. Malformed output gets exactly one retry. Invented or out-of-range citations are dropped, and if none survive, the pipeline falls back to extractive. &lt;strong&gt;An ungrounded generation can never reach the user.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Experiments: 2,000 queries, and the hybrid detour
&lt;/h2&gt;

&lt;p&gt;The benchmark suite runs 2,000 corpus queries (500 per language, balanced, seed-fixed) against the prebuilt index:&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;All&lt;/th&gt;
&lt;th&gt;English&lt;/th&gt;
&lt;th&gt;Hindi&lt;/th&gt;
&lt;th&gt;Gujarati&lt;/th&gt;
&lt;th&gt;Telugu&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;P50&lt;/td&gt;
&lt;td&gt;55.8 ms&lt;/td&gt;
&lt;td&gt;54.9&lt;/td&gt;
&lt;td&gt;56.0&lt;/td&gt;
&lt;td&gt;56.7&lt;/td&gt;
&lt;td&gt;55.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;P90&lt;/td&gt;
&lt;td&gt;69.4 ms&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Within 200 ms&lt;/td&gt;
&lt;td&gt;99.95%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;99.8%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Guard activity in that run: &lt;strong&gt;1,893 answered · 107 refused&lt;/strong&gt; — weak-match queries correctly declined rather than answered with junk. The single P100 outlier (273 ms, Gujarati) was one OS-scheduling event; the next-worst query across all languages was 178 ms.&lt;/p&gt;

&lt;p&gt;We also built and measured a hybrid BM25 + dense leg (bm25s, reciprocal rank fusion), evaluated against MSMARCO's own gold labels:&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;Dense&lt;/th&gt;
&lt;th&gt;Hybrid (BM25 + RRF)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;HitRate@10&lt;/td&gt;
&lt;td&gt;80.31%&lt;/td&gt;
&lt;td&gt;80.69%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MRR@10&lt;/td&gt;
&lt;td&gt;0.4664&lt;/td&gt;
&lt;td&gt;0.4669&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Search P50&lt;/td&gt;
&lt;td&gt;16.2 ms&lt;/td&gt;
&lt;td&gt;16.9 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Trade-off&lt;/strong&gt;&lt;br&gt;
Hybrid bought +0.4 pp hit rate for ~1 ms and a large maintenance surface. Measured parity means dense-only stays the default; the sparse leg ships flag-ready behind &lt;code&gt;retrieval.hybrid.enabled&lt;/code&gt; and off by default. RRF is used for candidate-pool expansion only — final ordering is always cosine, which keeps retrieval scores calibrated with the refusal floors. A hybrid ordering would have silently invalidated every floor we measured.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Every search also runs against a 200 ms deadline; skipped legs surface as explicit degradation strings (&lt;code&gt;sparse_leg_skipped_deadline&lt;/code&gt;) in the stage trace, never as silent timeouts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Corpus-bounded knowledge.&lt;/strong&gt; "What is the capital of India?" has zero supporting passages and is correctly refused — a feature, but it surprises users expecting a chatbot.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The borderline zone is real.&lt;/strong&gt; A band around 0.84–0.86 overlaps between weak-relevant and strong-junk retrievals. Per-language floors trade a little recall for a lot of precision; some answerable questions get refused.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Voice adds ~1 s.&lt;/strong&gt; Sarvam STT dominates end-to-end time; the RAG core's 56 ms is invisible under it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generative mode is opt-in&lt;/strong&gt; precisely because it breaks the latency budget.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Lesson&lt;/strong&gt;&lt;br&gt;
Calibrate thresholds from &lt;strong&gt;measured score distributions&lt;/strong&gt;, not intuition. The floors took an afternoon of histogram work and saved the product: without them, the system would have been confidently wrong in exactly the situations where being wrong matters most. A refusal with a stated score is also infinitely more debuggable than a silent abstention.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Reproducibility
&lt;/h2&gt;

&lt;p&gt;The prebuilt index (40k × 384-dim vectors) ships with the repo, so first run needs no indexing step. Raw per-query benchmark data is committed at &lt;code&gt;bench/results/results.json&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/The-Infernix/VOICE-RAG.git
&lt;span class="nb"&gt;cd &lt;/span&gt;VOICE-RAG
python &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
uvicorn api.main:app &lt;span class="nt"&gt;--port&lt;/span&gt; 7860   &lt;span class="c"&gt;# SPA at http://localhost:7860&lt;/span&gt;

pytest tests/ &lt;span class="nt"&gt;-q&lt;/span&gt;                   &lt;span class="c"&gt;# 178 passing&lt;/span&gt;
python bench/run_bench.py &lt;span class="nt"&gt;--limit&lt;/span&gt; 2000      &lt;span class="c"&gt;# latency&lt;/span&gt;
python bench/eval_retrieval.py               &lt;span class="c"&gt;# HitRate@10 / MRR@10 vs gold&lt;/span&gt;
python bench/run_bench.py &lt;span class="nt"&gt;--limit&lt;/span&gt; 500 &lt;span class="nt"&gt;--hybrid&lt;/span&gt; &lt;span class="nt"&gt;--soft-lang&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--out&lt;/span&gt; bench/results/ab_hybrid              &lt;span class="c"&gt;# the hybrid A/B above&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ol&gt;
&lt;li&gt;Refusal is a product feature, not an error path. Make it measurable, calibrated, and visible.&lt;/li&gt;
&lt;li&gt;Guard every stage independently — input, relevance, grounding — and surface every decision in the UI trace.&lt;/li&gt;
&lt;li&gt;When a measured improvement is inside the noise, ship the simpler system and keep the complex one behind a flag.&lt;/li&gt;
&lt;li&gt;Keep retrieval ordering aligned with whatever your thresholds were calibrated against.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/The-Infernix/VOICE-RAG" rel="noopener noreferrer"&gt;VOICE-RAG repository&lt;/a&gt; — bench results, 178-test suite, config&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bench/results/summary.json&lt;/code&gt; — the latency numbers quoted above&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bench/eval_retrieval.py&lt;/code&gt; — gold-label evaluation against MSMARCO &lt;code&gt;is_selected&lt;/code&gt; labels&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;guardrails/relevance_guard.py&lt;/code&gt; — the calibrated per-language floors&lt;/li&gt;
&lt;li&gt;Built for HH Goa 2026, Task 2&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Pavan Ganesh Indupudi (Infernix)&lt;/strong&gt; — CS student at Andhra University building privacy-preserving AI. Full article and more at &lt;a href="https://the-infernix.github.io/" rel="noopener noreferrer"&gt;the-infernix.github.io&lt;/a&gt; · &lt;a href="https://github.com/The-Infernix" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; · &lt;a href="https://www.linkedin.com/in/pavan-indupudi-a38759334" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>nlp</category>
      <category>rag</category>
    </item>
    <item>
      <title>Building PRIVYSE: Designing a Privacy-First Browser Agent</title>
      <dc:creator>Pavan Indupudi</dc:creator>
      <pubDate>Tue, 20 Oct 2026 19:24:27 +0000</pubDate>
      <link>https://dev.to/the_infernix/building-privyse-designing-a-privacy-first-browser-agent-14fd</link>
      <guid>https://dev.to/the_infernix/building-privyse-designing-a-privacy-first-browser-agent-14fd</guid>
      <description>&lt;p&gt;&lt;strong&gt;Pavan Ganesh Indupudi (Infernix)&lt;/strong&gt; · Originally published at &lt;a href="https://the-infernix.github.io/blog/posts/privyse-privacy-first-browser-agent.html" rel="noopener noreferrer"&gt;the-infernix.github.io&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The pitch for browser agents is seductive: give a vision-language model access to your tabs, and it can fill forms, file reports, and navigate tools you never wrote an integration for. The implementation detail that pitch skips is brutal — &lt;strong&gt;to see the page, the model sees everything on the page&lt;/strong&gt;. Your email. Your PAN. The card number half-typed into a KYC form. Your face in a video call thumbnail.&lt;/p&gt;

&lt;p&gt;PRIVYSE — built for SIH track 26171, &lt;em&gt;On-device Visual Perception for Lightweight Browser Agents&lt;/em&gt; — starts from a different question: &lt;strong&gt;how do we let the model control the browser without giving it the user's private screen?&lt;/strong&gt; The answer is not a privacy policy bolted onto a normal agent. It is an inverted pipeline where privacy is the architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem: agents that trust the model with the screen
&lt;/h2&gt;

&lt;p&gt;A conventional computer-use agent does one thing every step: capture the viewport, ship the pixels, ask the model what to do. That single habit can expose emails, phone numbers, government IDs, passwords, OTPs, API keys, faces, and sensitive documents — all in one JPEG. Prompting the model to "ignore" the PII does nothing; the data has already left the device.&lt;/p&gt;

&lt;p&gt;Sanitizing &lt;em&gt;after&lt;/em&gt; the model has seen the screen is not sanitization. The only place privacy can be enforced is &lt;strong&gt;before the privacy boundary&lt;/strong&gt; — between the browser and the model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Constraints that shaped the design
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The boundary must be demonstrable.&lt;/strong&gt; Not "we promise not to log it" — a gate a user can watch, failing closed, on every step.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The DOM is not enough.&lt;/strong&gt; Modern pages hide values in &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt;, SVG, images, video, and tiny rendered text. A DOM-only sanitizer has holes you cannot see.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redaction cannot destroy context.&lt;/strong&gt; If every email becomes a black box, the model cannot complete tasks that need to reference it. The rewrite has to preserve referential structure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The agent must stay useful.&lt;/strong&gt; A gate that blocks every image-heavy page forever is a denial-of-service, not a privacy system — degradation has to be explicit, not silent.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Architecture: capture → sanitize → gate → reason → act
&lt;/h2&gt;

&lt;p&gt;Every agent step runs the same visible pipeline. The most important component in it is not the model — it is the gate.&lt;/p&gt;

&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%2F6zrq6u60jt6juifdz4kg.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%2F6zrq6u60jt6juifdz4kg.png" alt=" " width="800" height="578"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;One agent step end-to-end. The gate (04) is the only path across the privacy boundary.&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Capture&lt;/strong&gt; — &lt;code&gt;captureVisibleTab&lt;/code&gt; grabs the viewport; a DOM walker serializes interactive elements (id, label, role, value, bbox) capped at 120 elements, and hashes pixels for the perception cache.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Perceive&lt;/strong&gt; — MobileViT-Small (quantized ONNX, 6.3 MB), BlazeFace, and region-restricted Tesseract.js OCR run locally, in parallel.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sanitize&lt;/strong&gt; — three tiers of redaction (below), then Set-of-Marks paints numbered &lt;code&gt;[N]&lt;/code&gt; tags so the model targets integers, never coordinates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gate&lt;/strong&gt; — two independent checks, detailed in the next section. Any hit blocks the step.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reason&lt;/strong&gt; — only the redacted JPEG plus numbered DOM reach the local FastAPI server, which routes to a Qwen2.5-VL 3B or 7B and gets exactly one JSON action back.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Act&lt;/strong&gt; — a guarded executor resolves element ids and drives pointer/keyboard events through scheme allow-lists and a DO-NOT-MODIFY veto.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Three tiers of redaction — and why tokens beat black boxes
&lt;/h2&gt;

&lt;p&gt;Not all sensitive data deserves the same protection. PRIVYSE classifies into three tiers:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;th&gt;Information&lt;/th&gt;
&lt;th&gt;Protection&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Tier A&lt;/td&gt;
&lt;td&gt;Passwords, OTPs, CVVs, API keys&lt;/td&gt;
&lt;td&gt;Solid black box — no text, no token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tier B&lt;/td&gt;
&lt;td&gt;Email, phone, PAN, Aadhaar, cards, names&lt;/td&gt;
&lt;td&gt;Stable privacy token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tier C&lt;/td&gt;
&lt;td&gt;Faces, canvas, video, non-DOM graphics&lt;/td&gt;
&lt;td&gt;Blur / region taint&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Tier B is the interesting one. Destroying a value with a black box removes context the model needs; leaving it is a leak. The compromise is &lt;strong&gt;stable tokenization&lt;/strong&gt; — the same raw value always maps to the same token for the run, so the model keeps continuity without ever seeing the secret:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;john@example.com           4111 1111 1111 1111          9876543210@ybl
       ↓                           ↓                          ↓
   [EMAIL_1]                   [CARD_1]                   [UPI_1]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cross-source continuity is deliberate: a value discovered by OCR in an image receives the &lt;em&gt;same&lt;/em&gt; token when it later appears in the DOM. Indian identifiers — UPI IDs, voter ID, driving licence, passport, PAN, Aadhaar, IFSC — have dedicated detection patterns, measured at 100% precision/recall on the GT benchmark pages.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Design decision&lt;/strong&gt;&lt;br&gt;
Tokens must be &lt;strong&gt;stable across the whole run&lt;/strong&gt;, not per-screenshot. Per-frame tokens would make the model re-learn the mapping every step and break any task that references a value found earlier. Global stability is also what lets the gate recognize a token as already-sanitized rather than as a leak.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The zero-leak gate: two checks, one fail-closed rule
&lt;/h2&gt;

&lt;p&gt;Before anything crosses the boundary, the outbound payload is verified twice:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;DOM verification&lt;/strong&gt; — a pure regex scan (&lt;code&gt;assertNoLeaks&lt;/code&gt;) over the outbound DOM JSON. Always runs. Cannot be skipped.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pixel verification&lt;/strong&gt; — the &lt;em&gt;actual sanitized screenshot bytes&lt;/em&gt; are OCR-scanned again. This is the check that catches PII the DOM never carried.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Stated exception — read this before trusting the boundary&lt;/strong&gt;&lt;br&gt;
The two layers do not fail identically. A DOM-layer failure can never send. A pixel-layer check that runs and finds PII can never send. But if the on-device vision host is down, PII that exists &lt;em&gt;only in pixels&lt;/em&gt; is not OCR-verified on that step. Blocking every image-heavy page indefinitely would make the agent unusable, so the degradation is deliberate — the side panel reports &lt;code&gt;gate: skip&lt;/code&gt; — and all nine failure paths are walked in &lt;code&gt;docs/perception-failure-path-audit.md&lt;/code&gt;. It is a real, narrow gap, not a claim of airtightness.&lt;/p&gt;
&lt;/blockquote&gt;

&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%2Fia98axkvuvvyhh7c7hgl.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%2Fia98axkvuvvyhh7c7hgl.png" alt="The exact sanitized image shipped to the VLM: a KYC form with every sensitive value replaced by a stable token" width="800" height="1500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The exact bytes that cross the privacy boundary — reproduced by the benchmark suite's demo-image runner.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Perception-driven model routing
&lt;/h2&gt;

&lt;p&gt;On-device perception pays a second dividend: the MobileViT tile map tells the server how much reasoning the step needs &lt;em&gt;before&lt;/em&gt; the model is called. Interactive steps (form fill, navigation, typing) route to the 3B; read/report/captcha steps route to the 7B. Measured on the routing benchmark:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Routing config&lt;/th&gt;
&lt;th&gt;Visual-context tasks&lt;/th&gt;
&lt;th&gt;Steps&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;qwen2.5vl:3b only&lt;/td&gt;
&lt;td&gt;2 / 3&lt;/td&gt;
&lt;td&gt;4.7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;qwen2.5vl:7b only&lt;/td&gt;
&lt;td&gt;3 / 3&lt;/td&gt;
&lt;td&gt;1.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Routed 3B / 7B&lt;/td&gt;
&lt;td&gt;2 / 3&lt;/td&gt;
&lt;td&gt;5.0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The routed read/report failure is documented as a property of the harder prose-heavy test page, not of the routing logic — interactive accuracy is 3/3 either way. The small model is never trusted blindly: streamed actions are validated, coerced, and guarded before execution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Experiments: what the benchmarks actually say
&lt;/h2&gt;

&lt;p&gt;All numbers come from the repo's own suite (&lt;code&gt;benchmarks/results/dashboard.json&lt;/code&gt; plus the suite runners), against ground-truth-labeled pages:&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;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PII detection F1 (pixels)&lt;/td&gt;
&lt;td&gt;0.984&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Precision / Recall&lt;/td&gt;
&lt;td&gt;0.968 / 1.000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zero-leak&lt;/td&gt;
&lt;td&gt;PASS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Adversarial privacy suite&lt;/td&gt;
&lt;td&gt;30 / 30 blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Executor guard tests&lt;/td&gt;
&lt;td&gt;27 / 27 passed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Live /act spot-check (forged hostile responses)&lt;/td&gt;
&lt;td&gt;10 / 10 refused&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Raw data off-device&lt;/td&gt;
&lt;td&gt;0 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Routed end-to-end latency&lt;/td&gt;
&lt;td&gt;≈ 12.7–13 s / step&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The adversarial suite is the number I trust most, because it is the least flattering: canvas PII, SVG PII, tiny text, obfuscated values, visual and text prompt injection, exfiltration via &lt;code&gt;data:&lt;/code&gt;/&lt;code&gt;blob:&lt;/code&gt;/&lt;code&gt;javascript:&lt;/code&gt; form actions, forged &lt;code&gt;/act&lt;/code&gt; responses, and protected-field overwrite attempts. Thirty attempts, thirty blocks. The executor's DO-NOT-MODIFY veto is deterministic, which means it defends against a &lt;em&gt;hostile server response&lt;/em&gt;, not just an honest model making a mistake.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Known flake — honest reporting&lt;/strong&gt;&lt;br&gt;
The zero-leak OCR gate is occasionally nondeterministic — roughly 1 failure in 4 full runs, a blur-sample variance on one image page. The critical property: it has never failed &lt;em&gt;open&lt;/em&gt;. When OCR cannot confirm, the step is blocked. A flaky gate that fails closed is a latency problem; a reliable gate that fails open is a privacy incident.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Limitations: the two defects we document instead of bury
&lt;/h2&gt;

&lt;p&gt;Our own audit of the human-in-the-loop path found two high-severity defects, now tracked publicly in the README:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;D2 (open, queued first)&lt;/strong&gt; — &lt;code&gt;history[].result&lt;/code&gt; and the user's &lt;code&gt;task&lt;/code&gt; string are attached to the outbound body untokenized, and the DOM gate only walks &lt;code&gt;body.dom[].text/value&lt;/code&gt;. PII typed into a human-in-the-loop answer could leave in plaintext with the gate blind to it. Until D2 is closed, the honest statement of the boundary is: &lt;em&gt;DOM-sourced PII is provably blocked; PII introduced through the agent's own conversation with the user is not yet covered.&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;D1 (open)&lt;/strong&gt; — the risky-action confirm detects Cancel via &lt;code&gt;startsWith("cancel")&lt;/code&gt;, so typing "no" executes the action. It needs an explicit allow-list.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Listing these is not humility theater. A privacy claim a reader cannot check is marketing; a privacy claim with a public defect table is engineering.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Lesson&lt;/strong&gt;&lt;br&gt;
Build the audit harness &lt;em&gt;before&lt;/em&gt; the feature that needs auditing. D2 was found by walking the human-in-the-loop paths with a test matrix, not by staring at the sanitizer. The same discipline that produced 30/30 on the adversarial suite produced the defect table — it is one muscle.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Reproducibility
&lt;/h2&gt;

&lt;p&gt;Everything above is reproducible from the public repo. The short version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;venv&lt;/span&gt;&lt;span class="n"&gt;\Scripts\activate&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;pip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;requirements.txt&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;uvicorn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;app:app&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--reload&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="c"&gt;# http://127.0.0.1:8000/health&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# optional perception routing&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;PERCEPTION_ROUTING&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;VLM_MODEL_SMALL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"qwen2.5vl:3b"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;VLM_MODEL_BIG&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"qwen2.5vl:7b"&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="nx"&gt;/benchmarks&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;npm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;run.mjs&lt;/span&gt;&lt;span class="w"&gt;                      &lt;/span&gt;&lt;span class="c"&gt;# sanitizer P/R + zero-leak + latency&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;aggregate.mjs&lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="c"&gt;# merge into results/dashboard.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The latency budget model, the perception failure-path audit, and the HITL test matrix live in &lt;code&gt;docs/&lt;/code&gt;. If a number in this article disagrees with the repo, the repo wins.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;Privacy in an agent system is an &lt;strong&gt;ordering constraint&lt;/strong&gt;, not a feature. Sanitize before the model is allowed to look.&lt;/li&gt;
&lt;li&gt;A gate users can &lt;em&gt;watch&lt;/em&gt; — flash PASS/BLOCKED every step — changes what "trust" means. Trust becomes verification.&lt;/li&gt;
&lt;li&gt;On-device perception is not just a privacy cost center; the same inference that protects the screen also routes the model and caches the page.&lt;/li&gt;
&lt;li&gt;Publish your defects. The defect table is the most credible paragraph on the repo.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/The-Infernix/PRIVYSE" rel="noopener noreferrer"&gt;PRIVYSE repository&lt;/a&gt; — benchmarks, docs, defect table&lt;/li&gt;
&lt;li&gt;SIH 26171 track brief — On-device Visual Perception for Lightweight Browser Agents&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;docs/perception-failure-path-audit.md&lt;/code&gt; — all nine gate failure paths&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;docs/hitl-test-matrix.md&lt;/code&gt; — the audit that found D1 and D2&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;docs/latency-budget-judge-gpu.md&lt;/code&gt; — the p50 &amp;lt; 5 s/step target model&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Pavan Ganesh Indupudi (Infernix)&lt;/strong&gt; — CS student at Andhra University building privacy-preserving AI. Full article and more at &lt;a href="https://the-infernix.github.io/" rel="noopener noreferrer"&gt;the-infernix.github.io&lt;/a&gt; · &lt;a href="https://github.com/The-Infernix" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; · &lt;a href="https://www.linkedin.com/in/pavan-indupudi-a38759334" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/p&gt;

</description>
      <category>privacy</category>
      <category>ai</category>
      <category>webdev</category>
      <category>machinelearning</category>
    </item>
  </channel>
</rss>
