<?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: tutorial</title>
    <description>The latest articles tagged 'tutorial' on DEV Community.</description>
    <link>https://dev.to/t/tutorial</link>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tag/tutorial"/>
    <language>en</language>
    <item>
      <title>Pay for APIs with USDC — Build an x402 Payment Proxy in 10 Minutes</title>
      <dc:creator>scotia1973-bot</dc:creator>
      <pubDate>Sun, 12 Jul 2026 18:41:05 +0000</pubDate>
      <link>https://dev.to/scotia1973bot/pay-for-apis-with-usdc-build-an-x402-payment-proxy-in-10-minutes-4636</link>
      <guid>https://dev.to/scotia1973bot/pay-for-apis-with-usdc-build-an-x402-payment-proxy-in-10-minutes-4636</guid>
      <description>&lt;h2&gt;
  
  
  What is x402?
&lt;/h2&gt;

&lt;p&gt;x402 is a payment protocol that lets you charge users for API access using USDC on Base. No credit cards, no Stripe, no bank account needed. Just crypto.&lt;/p&gt;

&lt;p&gt;Every request carries a micropayment. If they pay, they get the data. If not, they get a 402 Payment Required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Build This?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Users don't want another subscription.&lt;/strong&gt; They want to pay per use.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You don't want to process payments.&lt;/strong&gt; x402 handles it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Base is cheap.&lt;/strong&gt; Transactions cost fractions of a cent.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setup in 10 Minutes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Install
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;x402
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Connect to the Proxy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;x402&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x402&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;x402&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.gadgethumans.com/v1/proxy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0xYOUR_WALLET_ADDRESS&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Now any API call is auto-charged&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.example.com/data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Behind the scenes: sends USDC payment, gets response&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: One Curl Command to Start Accepting Payments
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://api.gadgethumans.com/v1/proxy/register &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"endpoint": "https://yourapi.com/data", "price_usdc": 0.01}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. Your API is now monetized. Users pay 0.01 USDC per call, you collect 99.5% (we take 0.5%).&lt;/p&gt;

&lt;h2&gt;
  
  
  Test It Live
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://x402.gadgethumans.com/v1/health
&lt;span class="c"&gt;# → {"status": "ok", "network": "base", "price_per_call": "0.01 USDC"}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why This Works
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Devs have crypto wallets.&lt;/strong&gt; Every web3 developer has USDC.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No signup.&lt;/strong&gt; No Stripe account, no KYC, no monthly minimums.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instant settlement.&lt;/strong&gt; Payments arrive in your wallet in seconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Free to start.&lt;/strong&gt; 100 transactions/month free. $4.99/mo for Pro.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Dashboard coming next week — see all your API revenue in one place&lt;/li&gt;
&lt;li&gt;Webhook notifications when you get paid&lt;/li&gt;
&lt;li&gt;Automatic invoice generation&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Built with x402 v1.2.0 on Base (eip155:8453). Commission: 0.5%. Questions? Join our Discord.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>web3</category>
      <category>api</category>
      <category>payment</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>The Developer's Guide to Picking the Right Coding LLM at Scale</title>
      <dc:creator>gentleforge</dc:creator>
      <pubDate>Sun, 12 Jul 2026 18:21:30 +0000</pubDate>
      <link>https://dev.to/gentleforge/the-developers-guide-to-picking-the-right-coding-llm-at-scale-14dl</link>
      <guid>https://dev.to/gentleforge/the-developers-guide-to-picking-the-right-coding-llm-at-scale-14dl</guid>
      <description>&lt;p&gt;The Developer's Guide to Picking the Right Coding LLM at Scale&lt;/p&gt;

&lt;p&gt;Six months ago, I was staring at our monthly AI bill — $14,000 and climbing fast. We were using the "premium" model for everything, including trivial code completions. That night, I built a small internal benchmark to figure out which models actually earn their cost. What I learned reshaped how we think about AI tooling, vendor lock-in, and what "production-ready" really means.&lt;/p&gt;

&lt;p&gt;Here's the raw truth from my testing rig, what we shipped, and how we cut costs by 70% without touching output quality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Stopped Trusting Default Recommendations
&lt;/h2&gt;

&lt;p&gt;Every vendor says their model is the best. Every benchmark site ranks things differently. Most "best of" lists are either sponsored or built on vibes. I needed numbers that matched my actual workflow: generating Python services, debugging JavaScript race conditions, implementing TypeScript algorithms, and reviewing Go for security.&lt;/p&gt;

&lt;p&gt;So I took ten models, threw identical prompts at them, and scored them myself. No vendor PR. No cherry-picked examples. Just the same five tasks, run the same way, scored on the same rubric.&lt;/p&gt;

&lt;p&gt;Here are the ten models I tested, with their output pricing per million tokens — because at scale, that's the metric that decides whether your AI strategy is viable or a margin killer.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Provider&lt;/th&gt;
&lt;th&gt;Output $/M&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek V4 Flash&lt;/td&gt;
&lt;td&gt;DeepSeek&lt;/td&gt;
&lt;td&gt;$0.25&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek Coder&lt;/td&gt;
&lt;td&gt;DeepSeek&lt;/td&gt;
&lt;td&gt;$0.25&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen3-Coder-30B&lt;/td&gt;
&lt;td&gt;Qwen&lt;/td&gt;
&lt;td&gt;$0.35&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek V4 Pro&lt;/td&gt;
&lt;td&gt;DeepSeek&lt;/td&gt;
&lt;td&gt;$0.78&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek-R1&lt;/td&gt;
&lt;td&gt;DeepSeek&lt;/td&gt;
&lt;td&gt;$2.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kimi K2.5&lt;/td&gt;
&lt;td&gt;Moonshot&lt;/td&gt;
&lt;td&gt;$3.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GLM-5&lt;/td&gt;
&lt;td&gt;Zhipu&lt;/td&gt;
&lt;td&gt;$1.92&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen3-32B&lt;/td&gt;
&lt;td&gt;Qwen&lt;/td&gt;
&lt;td&gt;$0.28&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hunyuan-Turbo&lt;/td&gt;
&lt;td&gt;Tencent&lt;/td&gt;
&lt;td&gt;$0.57&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ga-Standard&lt;/td&gt;
&lt;td&gt;GA Routing&lt;/td&gt;
&lt;td&gt;$0.20&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Before you ask: yes, I tested against the originals. I also tested against Global API's unified routing layer, which lets you hit any of these through one endpoint. More on that later — it became the architectural decision that actually saved us.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Benchmark Methodology (No Marketing Fluff)
&lt;/h2&gt;

&lt;p&gt;I built five tasks that mirror what my engineers actually do every week. Not synthetic academic puzzles — real production scenarios.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Function Implementation&lt;/strong&gt; — "Write a Python function to flatten a nested list recursively"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bug Fix&lt;/strong&gt; — "Fix the race condition in this async/await JavaScript code"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Algorithm&lt;/strong&gt; — "Implement Dijkstra's shortest path in TypeScript"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code Review&lt;/strong&gt; — "Review this Go code for security issues and performance"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full Feature&lt;/strong&gt; — "Build a REST API endpoint with Express.js that paginates and filters users"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each output got scored 1–10 based on correctness, code quality, documentation, and edge-case handling. Two senior engineers on my team did the blind review. No model names visible. Just code.&lt;/p&gt;

&lt;p&gt;That last point matters. If you want honest rankings, you can't have bias in the scoring loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Final Rankings — Score vs. ROI
&lt;/h2&gt;

&lt;p&gt;This is the table I wish someone had handed me six months ago. The "Value" column is the only one that matters when you're running at scale.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rank&lt;/th&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Score&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;th&gt;Value (Score/$)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🥇&lt;/td&gt;
&lt;td&gt;Qwen3-Coder-30B&lt;/td&gt;
&lt;td&gt;8.8&lt;/td&gt;
&lt;td&gt;$0.35&lt;/td&gt;
&lt;td&gt;25.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🥈&lt;/td&gt;
&lt;td&gt;DeepSeek V4 Flash&lt;/td&gt;
&lt;td&gt;8.7&lt;/td&gt;
&lt;td&gt;$0.25&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;34.8&lt;/strong&gt; 🏆&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🥉&lt;/td&gt;
&lt;td&gt;DeepSeek Coder&lt;/td&gt;
&lt;td&gt;8.6&lt;/td&gt;
&lt;td&gt;$0.25&lt;/td&gt;
&lt;td&gt;34.4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;DeepSeek V4 Pro&lt;/td&gt;
&lt;td&gt;9.1&lt;/td&gt;
&lt;td&gt;$0.78&lt;/td&gt;
&lt;td&gt;11.7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;DeepSeek-R1&lt;/td&gt;
&lt;td&gt;9.4&lt;/td&gt;
&lt;td&gt;$2.50&lt;/td&gt;
&lt;td&gt;3.8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Kimi K2.5&lt;/td&gt;
&lt;td&gt;9.0&lt;/td&gt;
&lt;td&gt;$3.00&lt;/td&gt;
&lt;td&gt;3.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Qwen3-32B&lt;/td&gt;
&lt;td&gt;8.3&lt;/td&gt;
&lt;td&gt;$0.28&lt;/td&gt;
&lt;td&gt;29.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;GLM-5&lt;/td&gt;
&lt;td&gt;8.0&lt;/td&gt;
&lt;td&gt;$1.92&lt;/td&gt;
&lt;td&gt;4.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;Hunyuan-Turbo&lt;/td&gt;
&lt;td&gt;7.5&lt;/td&gt;
&lt;td&gt;$0.57&lt;/td&gt;
&lt;td&gt;13.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;Ga-Standard&lt;/td&gt;
&lt;td&gt;8.5*&lt;/td&gt;
&lt;td&gt;$0.20&lt;/td&gt;
&lt;td&gt;42.5*&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The asterisk on Ga-Standard is critical — it's a smart router, so the score fluctuates per task. But at $0.20/M, the value column becomes theoretical.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The pattern that jumped out:&lt;/strong&gt; the cheapest models cluster near the top. Premium-tier models like Kimi K2.5 ($3.00/M) score higher on raw quality, but their value score tanks. If you're optimizing for engineering throughput per dollar, premium isn't where you should be spending.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Picked DeepSeek V4 Flash as My Default
&lt;/h2&gt;

&lt;p&gt;Three reasons:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First, it's fast.&lt;/strong&gt; Latency matters when engineers are waiting on completions. DeepSeek V4 Flash consistently returned full functions in under 1.2 seconds. Some premium models took 4+ seconds for the same output. That adds up across a team of 15 engineers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second, it's predictable.&lt;/strong&gt; I don't need a model that occasionally produces genius-level output and occasionally hallucinates. I need a model that's solid 95% of the time. DeepSeek V4 Flash hits that bar.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Third, at $0.25/M output, the economics just work.&lt;/strong&gt; If my team makes 50,000 LLM calls a day, that's still under $400/month. Compare that to Kimi K2.5 at $3.00/M — same call volume would be $4,800/month. For what? A 0.3-point quality bump?&lt;/p&gt;

&lt;p&gt;This is where vendor lock-in awareness comes in. If I built everything around Kimi K2.5, I'd be paying 12x more for marginal gains, and switching would mean rewriting prompts, refactoring integrations, retraining my engineers on new output styles. That's the lock-in tax.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Reasoning Models: When $2.50/M Is Worth It
&lt;/h2&gt;

&lt;p&gt;DeepSeek-R1 scored the highest on raw quality (9.4). For hard algorithmic problems, it produces thinking-traced output that often includes Big-O analysis, alternative approaches, and edge cases the cheaper models miss.&lt;/p&gt;

&lt;p&gt;I tested it specifically on the Dijkstra's algorithm task. It returned a perfect TypeScript implementation with proper type safety, a priority queue, and clean handling of disconnected graphs. DeepSeek V4 Flash got 95% of the way there for 1/10th the cost.&lt;/p&gt;

&lt;p&gt;So here's the architecture decision I made: &lt;strong&gt;route by task complexity.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simple functions, bug fixes, code review → DeepSeek V4 Flash ($0.25/M)&lt;/li&gt;
&lt;li&gt;Hard algorithms, architectural design questions → DeepSeek-R1 ($2.50/M)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's not complicated to implement, and the ROI is obvious.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Task-by-Task Breakdown
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Task 1: Python List Flattening
&lt;/h3&gt;

&lt;p&gt;"Write a Python function to flatten a nested list recursively"&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Score&lt;/th&gt;
&lt;th&gt;What I Noticed&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek V4 Flash&lt;/td&gt;
&lt;td&gt;9.0&lt;/td&gt;
&lt;td&gt;Clean recursive solution with type hints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen3-Coder-30B&lt;/td&gt;
&lt;td&gt;9.0&lt;/td&gt;
&lt;td&gt;Added iterative alternative + edge cases&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek Coder&lt;/td&gt;
&lt;td&gt;8.5&lt;/td&gt;
&lt;td&gt;Correct but verbose&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kimi K2.5&lt;/td&gt;
&lt;td&gt;9.0&lt;/td&gt;
&lt;td&gt;Most readable, added docstring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek-R1&lt;/td&gt;
&lt;td&gt;9.5&lt;/td&gt;
&lt;td&gt;Included complexity analysis&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;DeepSeek-R1 won this one. If I were shipping a library, I'd want that thinking output. If I were debugging my own code at 2 AM, I'd take V4 Flash and move on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Task 2: JavaScript Race Condition Fix
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// The buggy snippet every model had to diagnose&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Always logs null — race condition!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Score&lt;/th&gt;
&lt;th&gt;What I Noticed&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek V4 Flash&lt;/td&gt;
&lt;td&gt;9.0&lt;/td&gt;
&lt;td&gt;Clear explanation + 3 fix options&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen3-Coder-30B&lt;/td&gt;
&lt;td&gt;9.0&lt;/td&gt;
&lt;td&gt;Added error handling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek Coder&lt;/td&gt;
&lt;td&gt;8.5&lt;/td&gt;
&lt;td&gt;Correct fix, minimal explanation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen3-32B&lt;/td&gt;
&lt;td&gt;8.5&lt;/td&gt;
&lt;td&gt;Good fix, slightly verbose&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Tie between DeepSeek V4 Flash and Qwen3-Coder-30B. Both nailed it. V4 Flash gave me three different fix patterns (async/await, Promise chaining, IIFE) which was useful for picking the right fit for our codebase.&lt;/p&gt;

&lt;h3&gt;
  
  
  Task 3: Dijkstra's Algorithm in TypeScript
&lt;/h3&gt;

&lt;p&gt;This is where reasoning models earn their cost. DeepSeek-R1 produced the cleanest output with proper priority queue implementation and full type safety. DeepSeek V4 Flash got close, but lacked the explanatory depth.&lt;/p&gt;

&lt;p&gt;For an algorithm task, you want the model that thinks. For a CRUD endpoint, you want the model that ships.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Production Architecture: The Routing Layer
&lt;/h2&gt;

&lt;p&gt;Here's the real takeaway. Instead of hardcoding a single provider, I built a thin abstraction layer using Global API's unified endpoint. One base URL, every model available, same SDK pattern.&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt;

&lt;span class="c1"&gt;# Single client, every model behind it
&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_GLOBAL_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://global-apis.com/v1&lt;/span&gt;&lt;span class="sh"&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;generate_code&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;complexity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;simple&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;Route by task complexity — the core of our cost strategy.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="n"&gt;model_map&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;simple&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;deepseek-v4-flash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;# $0.25/M
&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;code_review&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;deepseek-v4-flash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# $0.25/M
&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;algorithm&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;deepseek-r1&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.50/M
&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;architecture&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;deepseek-r1&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.50/M
&lt;/span&gt;    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model_map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;complexity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;deepseek-v4-flash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&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;role&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;system&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;content&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;You are a senior engineer. Write production-ready code.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;role&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;user&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;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;

&lt;span class="c1"&gt;# Cheapest path
&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generate_code&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Write a Python debounce decorator&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;simple&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Premium path for hard problems
&lt;/span&gt;&lt;span class="n"&gt;algorithm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generate_code&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Implement a consistent hash ring in Go&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;algorithm&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;p&gt;This single change saved us $9,000/month. No, really. The previous architecture was Kimi K2.5 for everything. The new architecture routes by need.&lt;/p&gt;

&lt;p&gt;Want to add Qwen3-Coder-30B for specialized code generation tasks? One line change:&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="n"&gt;model_map&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;simple&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;deepseek-v4-flash&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;code_generation&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;qwen3-coder-30b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# $0.35/M
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;algorithm&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;deepseek-r1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. No vendor lock-in. No rewriting integration code when pricing shifts. If a new model drops next quarter that beats everything in my benchmark, I add it to the map and ship by Friday.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Vendor Lock-In Question
&lt;/h2&gt;

&lt;p&gt;If you're a CTO reading this, you already know the trap. You pick a model. Your team builds around its output format, its latency profile, its prompt quirks. Six months later, the pricing changes or a better model drops, and you're stuck.&lt;/p&gt;

&lt;p&gt;I learned this the hard way in 2024 when we were locked into a provider that suddenly 3x'd their pricing overnight. Three weeks of migration hell.&lt;/p&gt;

&lt;p&gt;The Global API abstraction layer means my team writes prompts against an OpenAI-compatible interface. The model underneath can be DeepSeek, Qwen, Kimi, or whatever comes next. We never touch the underlying provider directly. That's not just cost optimization — that's future-proofing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ga-Standard: The Smart Router Worth Watching
&lt;/h2&gt;

&lt;p&gt;The last row in my rankings is Ga-Standard at $0.20/M. It's a smart routing model that automatically picks the best underlying model for your prompt. The score varied by task (8.5 average), but the value proposition is insane: 42.5 score-per-dollar.&lt;/p&gt;

&lt;p&gt;If you don't want to build your own routing layer, this is a solid default. The downside is you don't control which model handles which task. For some teams, that's fine. For my team, I wanted the granularity.&lt;/p&gt;

&lt;p&gt;But for a small startup that just wants "good code generation that doesn't break the bank," Ga-Standard through Global API is a reasonable starting point.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Final Recommendations
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;If you're optimizing for ROI at scale:&lt;/strong&gt; DeepSeek V4 Flash. Score 8.7, $0.25/M, value score 34.8. This is what most of your code generation traffic should hit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you need dedicated code model quality:&lt;/strong&gt; Qwen3-Coder-30B at $0.35/M. Score 8.8. Worth the slight premium when generating entire modules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you can afford reasoning overhead:&lt;/strong&gt; DeepSeek-R1 for hard algorithms and architecture questions. $2.50/M is expensive, but the output quality on complex problems justifies it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you want maximum flexibility:&lt;/strong&gt; Build a routing layer on top of Global API's unified endpoint. One integration, every model, no lock-in.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd Do Differently
&lt;/h2&gt;

&lt;p&gt;If I were starting from scratch today, I'd skip the per-provider integration entirely. I'd build everything against Global API's OpenAI-compatible endpoint from day one. The hours I spent writing provider-specific adapters were wasted — I rewrote all of them within a quarter anyway.&lt;/p&gt;

&lt;p&gt;I also wouldn't have run this benchmark myself. The pattern was obvious in hindsight: the cheap models are competitive enough that the value calculation almost always favors them. But running the test gave my engineering team confidence in the switch, which is half the battle in any architecture decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;Code generation AI has matured. The "AI writes buggy code" stigma is dead. What matters now is which model gives you production-ready output at a cost your business can sustain.&lt;/p&gt;

&lt;p&gt;For most use cases, that's DeepSeek V4 Flash at $0.25/M. For specialized code, Qwen3-Coder-30B at $0.35/M. For hard thinking, DeepSeek-R1 at $2.50/M — used sparingly.&lt;/p&gt;

&lt;p&gt;And for the integration layer? I route everything through Global API at &lt;code&gt;https://global-apis.com/v1&lt;/code&gt;. One API key, every model, no vendor lock-in. If you're evaluating coding models for your team, it's worth checking out — the unified endpoint saved us weeks of integration work and keeps us flexible as the landscape shifts.&lt;/p&gt;

&lt;p&gt;The AI model market moves fast. The best decision you can make isn't picking the perfect model today.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>webdev</category>
      <category>api</category>
      <category>deepseek</category>
    </item>
    <item>
      <title>Power BI DAX Essential Functions — Explained with Examples</title>
      <dc:creator>EricMWaimiri</dc:creator>
      <pubDate>Sun, 12 Jul 2026 18:20:08 +0000</pubDate>
      <link>https://dev.to/ericmwaimiri/power-bi-dax-essential-functions-explained-with-examples-kif</link>
      <guid>https://dev.to/ericmwaimiri/power-bi-dax-essential-functions-explained-with-examples-kif</guid>
      <description>&lt;p&gt;If you’ve ever struggled with &lt;code&gt;CALCULATE()&lt;/code&gt; or wondered why &lt;code&gt;SUMX()&lt;/code&gt; behaves differently from &lt;code&gt;SUM()&lt;/code&gt;, this guide is for you.&lt;br&gt;&lt;br&gt;
DAX (Data Analysis Expressions) is the language that powers &lt;strong&gt;Power BI&lt;/strong&gt;, &lt;strong&gt;Analysis Services&lt;/strong&gt;, and &lt;strong&gt;Power Pivot&lt;/strong&gt; — enabling dynamic calculations, filtering, and time intelligence.&lt;/p&gt;

&lt;p&gt;Below is a categorized cheat sheet of &lt;strong&gt;essential DAX functions&lt;/strong&gt;, plus examples showing how to use each in real-world Power BI scenarios.&lt;/p&gt;




&lt;h2&gt;
  
  
  Filtering &amp;amp; Context
&lt;/h2&gt;

&lt;p&gt;These functions control how filters are applied and evaluated in your calculations.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CALCULATE()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CALCULATE(SUM(Sales[Amount]), Region[Name] = "Nairobi")&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Changes filter context to calculate total sales for Nairobi.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FILTER()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;FILTER(Sales, Sales[Amount] &amp;gt; 10000)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns a table filtered by condition.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ALL()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CALCULATE(SUM(Sales[Amount]), ALL(Region))&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ignores filters on Region.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;REMOVEFILTERS()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CALCULATE(SUM(Sales[Amount]), REMOVEFILTERS(Region))&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Removes filters from Region.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;VALUES()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;VALUES(Customer[City])&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns unique list of cities.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SELECTEDVALUE()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SELECTEDVALUE(Product[Category], "All")&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns selected category or “All” if none.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TREATAS()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TREATAS(VALUES(Temp[City]), Customer[City])&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Applies one table’s values as filters on another.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;KEEPFILTERS()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CALCULATE(SUM(Sales[Amount]), KEEPFILTERS(Product[Category] = "Electronics"))&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Keeps existing filters and adds new ones.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ALLSELECTED()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CALCULATE(SUM(Sales[Amount]), ALLSELECTED(Region))&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Respects user selections in visuals.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ALLEXCEPT()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CALCULATE(SUM(Sales[Amount]), ALLEXCEPT(Sales, Sales[Year]))&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Removes all filters except Year.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Aggregation
&lt;/h2&gt;

&lt;p&gt;Summarize or aggregate data across rows or columns.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SUM()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SUM(Sales[Amount])&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Adds all sales amounts.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AVERAGE()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;AVERAGE(Sales[Amount])&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Calculates mean value.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;COUNT()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;COUNT(Customer[ID])&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Counts non-blank entries.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;COUNTROWS()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;COUNTROWS(Sales)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Counts rows in a table.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DISTINCTCOUNT()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DISTINCTCOUNT(Customer[ID])&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Counts unique customers.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MIN()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;MIN(Sales[Amount])&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Finds smallest sale.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MAX()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;MAX(Sales[Amount])&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Finds largest sale.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Iterator (X) Functions
&lt;/h2&gt;

&lt;p&gt;Perform row-by-row calculations before aggregation.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SUMX()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SUMX(Sales, Sales[Quantity] * Sales[Price])&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Calculates total revenue per row, then sums.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AVERAGEX()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;AVERAGEX(Products, Products[ProfitMargin])&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Averages profit margins across products.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MINX()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;MINX(Orders, Orders[DeliveryDays])&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Finds minimum delivery days.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MAXX()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;MAXX(Orders, Orders[DeliveryDays])&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Finds maximum delivery days.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Logical Functions
&lt;/h2&gt;

&lt;p&gt;Control flow and conditional logic.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IF()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;IF(Sales[Amount] &amp;gt; 10000, "High", "Low")&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns “High” or “Low” based on condition.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SWITCH()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SWITCH(TRUE(), Sales[Amount] &amp;gt; 10000, "High", Sales[Amount] &amp;gt; 5000, "Medium", "Low")&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Multi-condition logic.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;COALESCE()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;COALESCE(Sales[Discount], 0)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Replaces blanks with default value.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Relationship Functions
&lt;/h2&gt;

&lt;p&gt;Work across related tables.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RELATED()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;RELATED(Customer[Name])&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fetches related customer name.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LOOKUPVALUE()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;LOOKUPVALUE(Customer[Email], Customer[ID], Sales[CustomerID])&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Finds email based on ID.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;USERELATIONSHIP()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CALCULATE(SUM(Sales[Amount]), USERELATIONSHIP(Sales[Date], Calendar[Date]))&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Activates inactive relationship.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Ranking Functions
&lt;/h2&gt;

&lt;p&gt;Rank or sort data dynamically.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RANKX()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;RANKX(ALL(Customer), SUM(Sales[Amount]), , DESC)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ranks customers by total sales.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TOPN()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TOPN(5, Sales, Sales[Amount], DESC)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns top 5 sales records.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Mathematical Functions
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DIVIDE()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DIVIDE(SUM(Sales[Profit]), SUM(Sales[Revenue]), 0)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Safe division avoiding errors.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ROUND()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ROUND(Sales[Amount], 2)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Rounds to two decimals.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Date &amp;amp; Time Functions
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TODAY()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TODAY()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns current date.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NOW()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;NOW()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns current date and time.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DATEDIFF()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DATEDIFF(Orders[OrderDate], Orders[ShipDate], DAY)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Calculates days between two dates.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Time Intelligence
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DATEADD()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DATEADD(Calendar[Date], -1, YEAR)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Shifts date context by one year.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SAMEPERIODLASTYEAR()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SAMEPERIODLASTYEAR(Calendar[Date])&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Compares same period last year.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TOTALYTD()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TOTALYTD(SUM(Sales[Amount]), Calendar[Date])&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Year-to-date total.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DATESBETWEEN()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DATESBETWEEN(Calendar[Date], DATE(2026,1,1), DATE(2026,6,30))&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Filters dates between range.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DATESINPERIOD()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DATESINPERIOD(Calendar[Date], MAX(Calendar[Date]), -30, DAY)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Last 30 days.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Table Functions
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ADDCOLUMNS()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ADDCOLUMNS(Sales, "Profit", Sales[Revenue] - Sales[Cost])&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Adds calculated column.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SUMMARIZE()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SUMMARIZE(Sales, Region[Name], "TotalSales", SUM(Sales[Amount]))&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Groups and summarizes data.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SUMMARIZECOLUMNS()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SUMMARIZECOLUMNS(Region[Name], "TotalSales", SUM(Sales[Amount]))&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Similar to SUMMARIZE but optimized.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UNION()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;UNION(TableA, TableB)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Combines two tables.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;INTERSECT()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;INTERSECT(TableA, TableB)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns common rows.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EXCEPT()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;EXCEPT(TableA, TableB)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns rows in A not in B.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Text Functions
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LEFT()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;LEFT(Customer[Name], 3)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;First three letters.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RIGHT()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;RIGHT(Customer[Name], 3)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Last three letters.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SEARCH()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SEARCH("Ltd", Company[Name], 1, -1)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Finds position of substring.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CONCATENATEX()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CONCATENATEX(Customer, Customer[Name], ", ")&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Joins names with commas.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Information Functions
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ISBLANK()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;IF(ISBLANK(Sales[Amount]), 0, Sales[Amount])&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Checks for blank values.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;HASONEVALUE()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;IF(HASONEVALUE(Product[Category]), VALUES(Product[Category]), "Multiple")&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Detects single selection.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;p&gt;These functions form the foundation of every Power BI model.&lt;br&gt;&lt;br&gt;
Mastering them means you can build dynamic dashboards, automate KPIs, and handle complex business logic with ease.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Tip: Start with &lt;code&gt;CALCULATE()&lt;/code&gt;, &lt;code&gt;FILTER()&lt;/code&gt;, and &lt;code&gt;SUMX()&lt;/code&gt; — they’re the most powerful trio in DAX.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  Source
&lt;/h3&gt;

&lt;p&gt;Microsoft DAX Documentation&lt;br&gt;&lt;br&gt;
Essential DAX functions for building powerful Power BI reports and models.&lt;/p&gt;




</description>
      <category>analytics</category>
      <category>datascience</category>
      <category>microsoft</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Tool Use and RAG in Production — A Complete Beginner's Study Guide</title>
      <dc:creator>Subham Kumar</dc:creator>
      <pubDate>Sun, 12 Jul 2026 18:11:27 +0000</pubDate>
      <link>https://dev.to/digital_subham/tool-use-and-rag-in-production-a-complete-beginners-study-guide-379i</link>
      <guid>https://dev.to/digital_subham/tool-use-and-rag-in-production-a-complete-beginners-study-guide-379i</guid>
      <description>&lt;p&gt;This guide assumes you know nothing about LLMs beyond "I can chat with ChatGPT/Claude." Every concept is introduced with a real-world analogy first, then explained technically, then backed by runnable code.&lt;/p&gt;




&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Tool Use Fundamentals&lt;/li&gt;
&lt;li&gt;Tool Schema Design&lt;/li&gt;
&lt;li&gt;Parallel Tool Calls and Failure Handling&lt;/li&gt;
&lt;li&gt;Hybrid Search (Dense + Sparse Retrieval)&lt;/li&gt;
&lt;li&gt;Metadata Filtering&lt;/li&gt;
&lt;li&gt;Reranking: Cross-Encoders vs Bi-Encoders&lt;/li&gt;
&lt;li&gt;Query Rewriting with HyDE&lt;/li&gt;
&lt;li&gt;Semantic Caching&lt;/li&gt;
&lt;li&gt;Full System Design: RAG with 10M Documents, Zero Hallucinations&lt;/li&gt;
&lt;li&gt;Appendix A: Streaming Tool Calls&lt;/li&gt;
&lt;li&gt;Appendix B: Evaluating RAG with RAGAS&lt;/li&gt;
&lt;li&gt;Glossary&lt;/li&gt;
&lt;li&gt;"Why This Matters in Production" Summary&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1. Tool Use Fundamentals
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1.1 The simple example first
&lt;/h3&gt;

&lt;p&gt;Imagine you ask a friend, "Hey, what's the weather in Tokyo right now?" Your friend doesn't &lt;em&gt;know&lt;/em&gt; this off the top of their head — nobody just knows live weather. So your friend pulls out their phone, opens a weather app, types "Tokyo," reads the result, and tells you: "It's 24°C and cloudy."&lt;/p&gt;

&lt;p&gt;Notice what happened: your friend (the "brain" doing the reasoning) never &lt;em&gt;became&lt;/em&gt; a weather sensor. They recognized they needed external, live information, used a &lt;em&gt;tool&lt;/em&gt; (the weather app) to fetch it, and then used that fetched information to answer you in natural language.&lt;/p&gt;

&lt;p&gt;This is exactly what "tool use" (also called "function calling") means for an LLM like Claude or GPT. The model is the friend. It's great at reasoning and language, but it has no live connection to today's weather, your company's database, or a calculator that never makes arithmetic mistakes. So we give it a list of "apps" (tools) it's allowed to ask for, and something else — your application code — actually goes and uses those apps on its behalf.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.2 Technical explanation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Tool use&lt;/strong&gt; extends a model's capabilities to call "external systems" — anything outside the model's own weights and training data. Examples from the notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Given a city, get the current temperature.&lt;/li&gt;
&lt;li&gt;Given a math expression, solve it precisely.&lt;/li&gt;
&lt;li&gt;Run a bash command.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are all things a language model is fundamentally bad at doing internally (it can't refresh live weather data, and it's notoriously unreliable at exact arithmetic), so instead of asking the model to guess, we let it delegate to a real, deterministic function.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it's wired up:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You provide the model with a &lt;strong&gt;list of tools&lt;/strong&gt; (each tool = a function name + a description + a schema of its parameters) alongside your prompt.&lt;/li&gt;
&lt;li&gt;The model reads your message and &lt;em&gt;decides&lt;/em&gt;, based on the user's request, whether an external call is needed, and if so, which tool and with what arguments.&lt;/li&gt;
&lt;li&gt;This decision is called a &lt;strong&gt;tool_use request&lt;/strong&gt; — the model doesn't execute anything, it just emits a structured message saying "I'd like to call &lt;code&gt;get_weather&lt;/code&gt; with &lt;code&gt;{"city": "Tokyo"}&lt;/code&gt;."&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The single most important sentence in this whole section, straight from the notes:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"The model never calls a function itself!"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The model's role is &lt;em&gt;reasoning and argument construction only&lt;/em&gt;. It cannot reach out to the internet, touch your database, or execute code. Everything it does is emit text/JSON. Your application is the one with hands.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The full lifecycle (the loop), step by step:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Tools are defined as a &lt;strong&gt;list of functions + metadata&lt;/strong&gt; (name, description, parameter schema). This is sent to the model as part of the conversation (typically bundled with — or alongside — the user's message).&lt;/li&gt;
&lt;li&gt;The model reads the message and decides: is an external call needed? If yes, which tool, and with what arguments?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application intercepts the response.&lt;/strong&gt; Your code (not the model) receives this tool_use request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application invokes the function.&lt;/strong&gt; Your code actually runs &lt;code&gt;get_weather("Tokyo")&lt;/code&gt; for real — hits a weather API, database, calculator, whatever the tool represents.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application sends the result back to the model&lt;/strong&gt; as a "tool-result message."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model proceeds&lt;/strong&gt; — now that it has the real data, it continues generating its final natural-language answer to the user.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Along the way your application also has to handle practical realities like &lt;strong&gt;rate limits&lt;/strong&gt; on the tools you're calling (e.g., a weather API only allows so many requests per minute) — that's your application's job too, not the model's.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.3 Code: the full request/response loop
&lt;/h3&gt;

&lt;p&gt;Below is a minimal, close-to-runnable example using the Anthropic SDK style referenced in the notes. It defines a &lt;code&gt;get_weather&lt;/code&gt; tool, sends a prompt, receives a &lt;code&gt;tool_use&lt;/code&gt; block, executes a &lt;strong&gt;mock&lt;/strong&gt; function (standing in for a real weather API), and sends the result back so the model can finish its answer.&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;# pip install anthropic --break-system-packages
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;anthropic&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="c1"&gt;# Step 0: create a client (assumes ANTHROPIC_API_KEY is set as an environment variable)
&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;anthropic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Anthropic&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# ---------------------------------------------------------------------------
# Step 1: Define the tool as "a function + metadata"
# This is NOT the real function. It's a JSON description that tells the model
# "this function exists, here's its name, what it does, and what arguments it needs."
# ---------------------------------------------------------------------------
&lt;/span&gt;&lt;span class="n"&gt;tools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&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;name&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;get_weather&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;description&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;Get the current weather for a given city.&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;input_schema&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;type&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;object&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;properties&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;city&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;type&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;string&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;description&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;City name, e.g. Tokyo&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;unit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;type&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;string&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;enum&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;celsius&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;fahrenheit&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;description&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;Temperature unit to return&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;required&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;city&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="c1"&gt;# unit is optional
&lt;/span&gt;        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# ---------------------------------------------------------------------------
# Step 2: This is the REAL function that will actually run.
# In production this would call a weather API (e.g. OpenWeatherMap).
# Here we mock it so the example is runnable without external dependencies.
# ---------------------------------------------------------------------------
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_weather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;celsius&lt;/span&gt;&lt;span class="sh"&gt;"&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;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Pretend weather lookup — replace with a real HTTP call in production.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;fake_database&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;Tokyo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;London&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Delhi&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;33&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;temp_c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fake_database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;temp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;temp_c&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;celsius&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="nf"&gt;else &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temp_c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;city&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;temperature&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# ---------------------------------------------------------------------------
# Step 3: Send the user's question + the tool list to the model.
# The model will read the question and DECIDE if it needs the tool.
# ---------------------------------------------------------------------------
&lt;/span&gt;&lt;span class="n"&gt;messages&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;role&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;user&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;content&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;What is the weather in Tokyo?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude-sonnet-4-6&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# ---------------------------------------------------------------------------
# Step 4: "Application intercepts the response."
# We look through the model's response blocks. If it contains a "tool_use"
# block, that means the model wants us to run a function on its behalf.
# The model itself did NOT run anything — it only asked.
# ---------------------------------------------------------------------------
&lt;/span&gt;&lt;span class="n"&gt;tool_use_block&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&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;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool_use&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;tool_use_block&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tool_use_block&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&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;Model wants to call: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tool_use_block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; with &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tool_use_block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Step 5: "Application invokes the function." We actually run the real code.
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tool_use_block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;get_weather&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_weather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;tool_use_block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&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;error&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;unknown tool&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# Step 6: "Application sends the result back to model" as a tool_result message.
&lt;/span&gt;    &lt;span class="c1"&gt;# We must echo back the ORIGINAL assistant message (containing the tool_use)
&lt;/span&gt;    &lt;span class="c1"&gt;# plus a new "user" message containing the tool_result, so the model has
&lt;/span&gt;    &lt;span class="c1"&gt;# full context of what it asked for and what it got back.
&lt;/span&gt;    &lt;span class="n"&gt;messages&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&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;assistant&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;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;messages&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="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&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;user&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;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&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;tool_result&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;tool_use_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;tool_use_block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Step 7: "Model proceeds..." — call the model again so it can turn the
&lt;/span&gt;    &lt;span class="c1"&gt;# raw tool result into a natural-language final answer.
&lt;/span&gt;    &lt;span class="n"&gt;final_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude-sonnet-4-6&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&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;block&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;final_response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&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;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Final answer:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;block&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="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# The model answered directly without needing a tool.
&lt;/span&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&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;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Final answer:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;block&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the loop shape: &lt;strong&gt;model decides → app intercepts → app invokes → app returns result → model proceeds.&lt;/strong&gt; That five-step handoff is the entire mental model for tool use, and it never changes no matter how complex the tool is.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Tool Schema Design
&lt;/h2&gt;

&lt;h3&gt;
  
  
  2.1 The simple example first
&lt;/h3&gt;

&lt;p&gt;Imagine you're hiring a new employee and you hand them a one-line job description: "Search for products." That's it — no other instructions.&lt;/p&gt;

&lt;p&gt;On day one, a customer asks them "how much does this cost?" and the new employee, trying to be helpful, uses the "search for products" tool because it's the only tool they were told about, even though there's actually a &lt;em&gt;dedicated&lt;/em&gt; pricing tool sitting right next to it that they didn't know they should prefer. They also aren't told whether "search" means searching by name, by category, or by a product code — so they guess, and sometimes guess wrong.&lt;/p&gt;

&lt;p&gt;Now imagine instead you hand them a proper SOP: "Search for products by name, category, or SKU. Use this only when the customer is explicitly trying to find or filter catalog items. Do NOT use this for pricing questions or stock checks — those have their own dedicated tools." Now the employee has almost no room to misinterpret their job.&lt;/p&gt;

&lt;p&gt;This is the difference between a bad tool schema and a good one. The LLM is that new employee, and the schema is the only "training" it gets before being thrown into the job.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.2 Technical explanation
&lt;/h3&gt;

&lt;p&gt;From the notes: &lt;strong&gt;"JSON schema is [the] contract between your intent and the model's behaviour."&lt;/strong&gt; And critically: &lt;strong&gt;"Ambiguous schema is [the] most common root cause of tool failure and is 'invisible' until production."&lt;/strong&gt; It's invisible because in your own testing you tend to ask clean, obvious questions — production users ask messy, ambiguous, unexpected ones, and that's where a vague schema quietly breaks.&lt;/p&gt;

&lt;p&gt;There are &lt;strong&gt;three rules&lt;/strong&gt; for designing a good tool schema:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rule 1 — Specify semantics, not just a description.&lt;/strong&gt;&lt;br&gt;
Don't just say what the function is named or roughly does. Say &lt;em&gt;when to use it&lt;/em&gt;, &lt;em&gt;when not to use it&lt;/em&gt;, and &lt;em&gt;what it should never be confused with&lt;/em&gt;. The notes' worked example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ Bad: &lt;code&gt;"search for products"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;✅ Good: &lt;code&gt;"Search for products by name, category, or SKU. Use only when user is explicitly looking to find or filter catalog items. Do not use for pricing questions or stock checks — those have other dedicated tools."&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Rule 2 — Be deliberate about required vs. optional parameters.&lt;/strong&gt;&lt;br&gt;
If a parameter is optional and you don't clearly explain what happens when it's omitted, the model may &lt;strong&gt;hallucinate a value&lt;/strong&gt; for it just to fill the slot (marked with a red ✗ in the notes). Conversely, if a parameter is marked mandatory but the model doesn't have a value for it, it may pass nothing at all or an invalid placeholder (also marked ✗). The fix from the notes: &lt;strong&gt;provide default values for the model to use&lt;/strong&gt; so there's no ambiguity about what an "empty" optional parameter should look like.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rule 3 — Enums are the cheapest reliability primitive you have.&lt;/strong&gt;&lt;br&gt;
Whenever a parameter has a fixed, known set of valid values (a category, a unit, a status), constrain it with an &lt;strong&gt;enum&lt;/strong&gt; instead of leaving it as a free-form string. This "keeps things readable and reduces hallucination" — the model can only pick from the list you gave it, it can't invent &lt;code&gt;"CategoryXYZ"&lt;/code&gt; out of thin air.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The "too many tools" problem, and its fix — dynamic tool loading.&lt;/strong&gt;&lt;br&gt;
The notes flag: &lt;em&gt;"large number of tools: model gets confused"&lt;/em&gt; — when a model is handed 50+ tools at once, two hard questions creep in: &lt;em&gt;"What category tool?"&lt;/em&gt; and &lt;em&gt;"Which specific tool?"&lt;/em&gt; The model starts guessing between similar-looking tools. The fix is &lt;strong&gt;dynamic tool loading&lt;/strong&gt;: instead of always sending the model your entire tool catalog, your application first figures out (from context, intent classification, or the conversation's domain) which &lt;em&gt;subset&lt;/em&gt; of tools is actually relevant, and only sends that subset to the model for this particular turn. Fewer, more relevant choices means fewer wrong guesses.&lt;/p&gt;
&lt;h3&gt;
  
  
  2.3 Code: bad schema vs. good schema, side by side
&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;# =============================================================================
# BAD SCHEMA
# Vague description, no enum for category, ambiguous optional parameter.
# =============================================================================
&lt;/span&gt;&lt;span class="n"&gt;bad_tool&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;name&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;search_products&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;description&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;Search for products&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;lt;-- too vague: search how? for what purpose?
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input_schema&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;type&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;object&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;properties&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;query&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;type&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;string&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;category&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;type&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;string&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;lt;-- free text: model may invent categories
&lt;/span&gt;            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;include_out_of_stock&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;type&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;boolean&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;lt;-- optional, no default explained
&lt;/span&gt;        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;required&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;query&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;# Problems this causes in production:
# 1. Model may call this tool for pricing questions, since nothing tells it not to.
# 2. Model may pass category="Electronics &amp;amp; Gadgets" when your real categories
#    are ["electronics", "apparel", "home"] -&amp;gt; zero results, silent failure.
# 3. Model may omit include_out_of_stock, or guess True/False randomly, since
#    there's no guidance on what happens if it's left out.
&lt;/span&gt;

&lt;span class="c1"&gt;# =============================================================================
# GOOD SCHEMA
# Specifies semantics (when to use / not use), enum for category,
# explicit default behaviour for optional params.
# =============================================================================
&lt;/span&gt;&lt;span class="n"&gt;good_tool&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;name&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;search_products&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;description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;Search for products by name, category, or SKU. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Use only when the user is explicitly looking to find or filter catalog &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;items. Do NOT use this for pricing questions or stock-level checks — &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;those have their own dedicated tools (get_price, check_stock).&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;input_schema&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;type&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;object&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;properties&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;query&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;type&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;string&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;description&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;Free-text search term, e.g. a product name or SKU.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;category&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;type&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;string&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="c1"&gt;# Enum = the model can ONLY pick from these exact values.
&lt;/span&gt;                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;enum&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;electronics&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;apparel&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;home&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;books&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;toys&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;description&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;Optional. Restrict results to one catalog category.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;include_out_of_stock&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;type&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;boolean&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;description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;Whether to include out-of-stock items. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;If omitted, DEFAULTS to false (only show in-stock items).&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;required&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;query&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# The good schema tells the model: what it's for, what it's NOT for, exactly
# which category strings are valid, and exactly what "not specifying" means.
# That triple clarity is what the notes call the "contract" between your
# intent and the model's behaviour.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  2.4 Code sketch: dynamic tool loading
&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;# A tiny illustration of picking a relevant SUBSET of tools before calling
# the model, instead of always sending your entire tool catalog.
&lt;/span&gt;
&lt;span class="n"&gt;ALL_TOOLS&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;search_products&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{...},&lt;/span&gt;   &lt;span class="c1"&gt;# schema dicts omitted for brevity
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;get_price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;check_stock&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;get_weather&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;run_bash_command&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;search_hr_docs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{...},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# A simple keyword-based intent router. In production this is often a small
# classifier model or an embedding-similarity lookup, not just keywords —
# but the principle (narrow the toolset BEFORE calling the main model) is identical.
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;select_relevant_tools&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&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;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user_message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;price&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;cost&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;how much&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;get_price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stock&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;available&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;in stock&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;check_stock&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;find&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;search&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;looking for&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;search_products&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;weather&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;get_weather&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="c1"&gt;# Fallback: give a small default set rather than everything
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;search_products&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;get_price&lt;/span&gt;&lt;span class="sh"&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;build_tools_for_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&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;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;relevant_names&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;select_relevant_tools&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ALL_TOOLS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;name&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;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;relevant_names&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ALL_TOOLS&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# Usage: only the relevant tools go into the API call, not all 6+.
# tools_for_this_call = build_tools_for_request("How much does the red mug cost?")
# -&amp;gt; only sends the get_price tool, so the model can't confuse it with search_products.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Parallel Tool Calls and Failure Handling
&lt;/h2&gt;
&lt;h3&gt;
  
  
  3.1 The simple example first
&lt;/h3&gt;

&lt;p&gt;Suppose you ask a travel-planning friend: "Can you check the weather in Paris, Tokyo, and New York for me?" A slow friend would check Paris, wait, tell you, then check Tokyo, wait, tell you, then check New York. A smart friend instead opens three browser tabs at once, checks all three simultaneously, and gives you all three answers together. Same total work, far less waiting.&lt;/p&gt;

&lt;p&gt;Now imagine one of those three lookups fails — say the New York weather site is down. Does your friend refuse to tell you about Paris and Tokyo just because New York failed? Or do they hand you what they &lt;em&gt;do&lt;/em&gt; have, and mention New York didn't work? That's a judgment call — and it's the same judgment call your application has to make about tool calls.&lt;/p&gt;
&lt;h3&gt;
  
  
  3.2 Technical explanation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Parallel tool calls:&lt;/strong&gt; the notes describe a model that can emit &lt;strong&gt;multiple tool-call requests in a single turn&lt;/strong&gt; — e.g., "compare weather across N cities" or a "trip plan" that needs both &lt;code&gt;search_flight&lt;/code&gt; and &lt;code&gt;search_hotel&lt;/code&gt; at once. Your &lt;strong&gt;orchestration layer&lt;/strong&gt; (the application code, not the model) is responsible for "fanning out" these calls &lt;strong&gt;concurrently&lt;/strong&gt;, collecting all the results, and sending them back together. The direct payoff: it &lt;strong&gt;improves overall time to completion&lt;/strong&gt; — you pay for the &lt;em&gt;slowest&lt;/em&gt; of the N calls, not the &lt;em&gt;sum&lt;/em&gt; of all N. The notes' guidance: &lt;strong&gt;use this whenever the operations are independent of one another&lt;/strong&gt; (one call's result doesn't depend on another's).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Failure handling — "it depends".&lt;/strong&gt; The notes are refreshingly honest here: &lt;em&gt;"Answer to any question could well be IT DEPENDS."&lt;/em&gt; There isn't one universal right answer; it's a judgment call based on your product's tolerance for incompleteness vs. correctness. Three named strategies:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Prevent&lt;/strong&gt; — stop the bad outcome before it happens. E.g., validate inputs up front, apply timeouts, use circuit breakers so a known-broken downstream tool isn't even attempted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Absorb&lt;/strong&gt; — let the failure happen, but contain the blast radius: send back only the &lt;em&gt;successful&lt;/em&gt; tool-call responses and quietly drop/flag the failed ones, so the model can still produce a partial, useful answer rather than crashing the whole turn.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fail gracefully&lt;/strong&gt; — if absorbing isn't safe (e.g., you can't answer &lt;em&gt;any&lt;/em&gt; meaningful part of the question without the missing piece), abort the entire execution and return a clear, honest error/refusal rather than a partial or misleading answer.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The notes give a concrete example of when parallel fan-out matters: &lt;em&gt;"4 websearch calls, lookup across multiple knowledge bases."&lt;/em&gt; If a question needs facts from 4 different knowledge bases, firing all 4 lookups concurrently instead of one after another is the entire difference between a fast, responsive system and a sluggish one.&lt;/p&gt;
&lt;h3&gt;
  
  
  3.3 Code: concurrent tool calls with all three failure strategies
&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;random&lt;/span&gt;

&lt;span class="c1"&gt;# ---------------------------------------------------------------------------
# Mock tool: fetch weather for one city. Sometimes fails, to demonstrate
# failure handling (simulating a flaky downstream API).
# ---------------------------------------------------------------------------
&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;fetch_weather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&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;dict&lt;/span&gt;&lt;span class="p"&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;sleep&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;uniform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# simulate network latency
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;New York&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;and&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;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ConnectionError&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;Weather API timed out for &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;city&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;temp_c&lt;/span&gt;&lt;span class="sh"&gt;"&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;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;


&lt;span class="c1"&gt;# =============================================================================
# STRATEGY 1: PREVENT
# Validate/guard BEFORE attempting the call, so we never even try a call
# we already know is unsafe (e.g. an unsupported city, a rate-limited tool).
# =============================================================================
&lt;/span&gt;&lt;span class="n"&gt;SUPPORTED_CITIES&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;Paris&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;Tokyo&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;New York&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;London&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&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;call_with_prevention&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&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;dict&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;city&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;SUPPORTED_CITIES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# We "prevent" the failure by never calling the API for a city we
&lt;/span&gt;        &lt;span class="c1"&gt;# know isn't supported, instead of letting it fail downstream.
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;city&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&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;unsupported_city&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;skipped_call&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch_weather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="c1"&gt;# =============================================================================
# STRATEGY 2: ABSORB
# Run all calls concurrently; if one fails, catch the exception, keep going,
# and return only the successful results (plus a note about what failed).
# =============================================================================
&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;call_with_absorption&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cities&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&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;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;tasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;fetch_weather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;city&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;city&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cities&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="c1"&gt;# return_exceptions=True means a failed task returns its Exception object
&lt;/span&gt;    &lt;span class="c1"&gt;# instead of crashing the whole asyncio.gather() call.
&lt;/span&gt;    &lt;span class="n"&gt;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;tasks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;return_exceptions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;successes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;failures&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&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;city&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&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;cities&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;failures&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;city&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)})&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;successes&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;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;successful_results&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;successes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;failed_cities&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;failures&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="c1"&gt;# =============================================================================
# STRATEGY 3: FAIL GRACEFULLY
# If ANY call fails and a partial answer would be misleading (e.g. this tool
# call was mandatory context for the user's question), abort entirely and
# surface a clear, honest message instead of a silently incomplete answer.
# =============================================================================
&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;call_with_graceful_failure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cities&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&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;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;tasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;fetch_weather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;city&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;city&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cities&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;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;tasks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# no return_exceptions: first failure raises
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;results&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;refused&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reason&lt;/span&gt;&lt;span class="sh"&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;Could not complete weather comparison: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&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;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;cities&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;Paris&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;Tokyo&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;New York&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PREVENT strategy (one unsupported city):&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&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="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;call_with_prevention&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&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;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Paris&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;Atlantis&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]]))&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;ABSORB strategy (best-effort, partial results ok):&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;call_with_absorption&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cities&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;FAIL GRACEFULLY strategy (all-or-nothing):&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;call_with_graceful_failure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cities&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;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&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;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Which strategy to pick is a product decision, not a technical one — that's the "it depends" in the notes. A weather-comparison feature can tolerate "absorb" (2 out of 3 cities is still useful). A financial transaction pipeline probably needs "fail gracefully" (a half-completed transfer is dangerous, not just incomplete).&lt;/p&gt;


&lt;h2&gt;
  
  
  4. Hybrid Search (Dense + Sparse Retrieval)
&lt;/h2&gt;
&lt;h3&gt;
  
  
  4.1 The simple example first
&lt;/h3&gt;

&lt;p&gt;Say you're searching your email for something. If you search "birthday party invite," a smart search that understands &lt;em&gt;meaning&lt;/em&gt; (semantic search) will find an email titled "Come celebrate turning 30!" even though none of your exact words appear in it — because it understands the &lt;em&gt;concept&lt;/em&gt; of a birthday party.&lt;/p&gt;

&lt;p&gt;But now say you search for an exact error code, like &lt;code&gt;1099-MISC&lt;/code&gt;. A meaning-based search might get confused and return generic tax documents about "how do I reset my password" style topics — it doesn't realize you need an &lt;em&gt;exact string match&lt;/em&gt;, not a conceptual one. A classic old-school keyword search (like Ctrl+F, but smarter) would nail this instantly because it's built for exact-term matching.&lt;/p&gt;

&lt;p&gt;Neither approach alone is good at everything. The fix: run &lt;strong&gt;both kinds of search at the same time&lt;/strong&gt;, and combine (merge and rerank) their results — a bit like asking two friends with different strengths for restaurant recommendations, and combining their two rankings into one better, more trustworthy ranking.&lt;/p&gt;
&lt;h3&gt;
  
  
  4.2 Technical explanation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Dense retrieval&lt;/strong&gt; (semantic search): your query and your documents are both converted into vectors ("embeddings") that capture &lt;em&gt;meaning&lt;/em&gt;. Two pieces of text that mean similar things end up as vectors that are close together in space, even if they don't share any exact words. This is great when there's genuine &lt;strong&gt;semantic similarity&lt;/strong&gt; — the notes' example: "How do I reset my password?" naturally matches a document titled "Account Recovery Steps," even with zero overlapping words.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sparse retrieval / BM25&lt;/strong&gt;: this is the notes' name for &lt;strong&gt;traditional keyword-based search&lt;/strong&gt; ("BM25" = "Best Matching 25," a well-known statistical formula scoring how well a document's exact terms match a query's exact terms, weighted by term rarity and frequency). BM25 is the &lt;em&gt;opposite&lt;/em&gt; strength of dense search: it's excellent at exact terms, codes, IDs, acronyms — things like "error code 1099-MISC" — but it has no concept of meaning, so it's poor at "How do I reset my password?" style natural-language questions where the right document doesn't share your exact wording.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hybrid search = dense + sparse, run together.&lt;/strong&gt; The notes' rule of thumb: &lt;em&gt;"20|50 documents certainly having the right answer"&lt;/em&gt; — hybrid retrieval &lt;strong&gt;improves recall&lt;/strong&gt; (the fraction of genuinely relevant documents that get retrieved at all), because you're covering both failure modes (semantic gap AND exact-term gap) simultaneously. The fix line from the notes: &lt;strong&gt;"Run both and merge and rerank!"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reciprocal Rank Fusion (RRF)&lt;/strong&gt; is the merging technique. Analogy: imagine two friends each independently rank the same 10 restaurants from 1 (best) to 10 (worst). To get one combined ranking that reflects both opinions, you don't just average their scores (their internal scoring "scales" may be totally different: one friend rates out of 5 stars, another out of 100 points). Instead, RRF says: &lt;em&gt;only the rank position matters, not the internal score&lt;/em&gt; — a restaurant ranked #1 by both friends should shoot to the top of the combined list, even if their raw scores were on incompatible scales. This "brings reciprocal ranks together" as the notes put it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The formula&lt;/strong&gt;, straight from the notes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RRF(d) = Σ  1 / (k + rank_r(d))
         r∈R
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read this piece by piece:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;d&lt;/code&gt; is a particular document you're scoring.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;R&lt;/code&gt; is the set of ranked result lists you're fusing (in our case: the dense-search ranking and the BM25 ranking).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;rank_r(d)&lt;/code&gt; is the position of document &lt;code&gt;d&lt;/code&gt; in ranking &lt;code&gt;r&lt;/code&gt; (1st place, 2nd place, etc.).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;k&lt;/code&gt; is a small constant, &lt;strong&gt;usually 60&lt;/strong&gt; (per the notes) — it dampens the effect of very high ranks so a #1 spot doesn't dominate too extremely, and it also means documents that don't appear at all in one of the lists aren't catastrophically penalized.&lt;/li&gt;
&lt;li&gt;You take &lt;code&gt;1 / (k + rank)&lt;/code&gt; for &lt;em&gt;each&lt;/em&gt; list the document appears in, and &lt;strong&gt;sum&lt;/strong&gt; those fractions across all lists. A document ranked highly (small rank number) in multiple lists gets a bigger combined score.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The notes' visual intuition: dense list gives fractions like &lt;code&gt;1/1, 1/2, ...&lt;/code&gt; and sparse list gives &lt;code&gt;1/61, 1/62, ...&lt;/code&gt; (i.e., &lt;code&gt;1/(60+1), 1/(60+2), ...&lt;/code&gt;) — "&lt;strong&gt;Fusion is better&lt;/strong&gt;" because a document doesn't need to win outright in either individual list; it just needs to consistently rank &lt;em&gt;reasonably well&lt;/em&gt; across both to accumulate a high combined score.&lt;/p&gt;

&lt;p&gt;One more practical note from the notes: &lt;strong&gt;Weaviate and Elasticsearch (ES) support hybrid search natively&lt;/strong&gt; — meaning they've built RRF-style fusion directly into the database. Otherwise, you have to do it yourself with a &lt;strong&gt;scatter-gather&lt;/strong&gt; pattern (fire off both queries yourself, gather both result sets back in your application, then fuse them).&lt;/p&gt;

&lt;h3&gt;
  
  
  4.3 Code: RRF fusion from scratch (no external libraries)
&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;# =============================================================================
# Reciprocal Rank Fusion (RRF) — implemented from the raw formula.
# RRF(d) = sum over each ranked list r containing d of  1 / (k + rank_r(d))
# =============================================================================
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;reciprocal_rank_fusion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ranked_lists&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&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;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&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;
    ranked_lists: a list of ranked result lists. Each inner list is already
                  sorted best-to-worst, e.g. [&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;doc3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;doc1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;doc9&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;].
    k: the RRF damping constant (60 is the standard default from the notes).

    Returns a list of (doc_id, fused_score) sorted from best to worst.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;float&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ranked_list&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ranked_lists&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;position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;doc_id&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ranked_list&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;rank&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;  &lt;span class="c1"&gt;# ranks start at 1, not 0
&lt;/span&gt;            &lt;span class="c1"&gt;# This is the exact formula: 1 / (k + rank)
&lt;/span&gt;            &lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;doc_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;rank&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Sort documents by their fused score, highest first
&lt;/span&gt;    &lt;span class="n"&gt;fused_ranking&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scores&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;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fused_ranking&lt;/span&gt;


&lt;span class="c1"&gt;# --- Example usage ---------------------------------------------------------
&lt;/span&gt;
&lt;span class="c1"&gt;# Dense (semantic) search results for "how do I reset my password?"
&lt;/span&gt;&lt;span class="n"&gt;dense_results&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;account_recovery_steps&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;login_faq&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;security_settings&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;billing_faq&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# BM25 (keyword) search results for the SAME query — note it ranks things
# differently because it only sees exact word overlap
&lt;/span&gt;&lt;span class="n"&gt;sparse_results&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;login_faq&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;account_recovery_steps&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;terms_of_service&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;billing_faq&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;fused&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;reciprocal_rank_fusion&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;dense_results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sparse_results&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;60&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;doc_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;fused&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&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="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;doc_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Expected behaviour: "account_recovery_steps" and "login_faq" both rank
# highly in BOTH lists, so their fused scores rise above documents that only
# appeared in one list or ranked poorly in both.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4.4 Code: a BM25 call + a vector similarity call, combined
&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;# This shows the SHAPE of a real hybrid search call: one sparse (BM25) query,
# one dense (vector) query, run independently, then fused with RRF.
# (rank_bm25 and sentence-transformers are common lightweight libraries.)
&lt;/span&gt;
&lt;span class="c1"&gt;# pip install rank_bm25 sentence-transformers --break-system-packages
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;rank_bm25&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BM25Okapi&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sentence_transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SentenceTransformer&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="n"&gt;documents&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;To reset your password, go to Account Recovery and click &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Forgot Password&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&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;Error code 1099-MISC indicates a mismatch in the tax reporting form.&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;Our billing FAQ covers refunds, invoices, and subscription changes.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;doc_ids&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;account_recovery&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;error_1099_misc&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;billing_faq&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;how do I reset my password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# --- Sparse (BM25) leg: pure keyword/token overlap scoring -----------------
&lt;/span&gt;&lt;span class="n"&gt;tokenized_corpus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;split&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;doc&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;bm25&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BM25Okapi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokenized_corpus&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;bm25_scores&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bm25&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_scores&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="n"&gt;sparse_ranking&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;doc_ids&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&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;i&lt;/span&gt; &lt;span class="ow"&gt;in&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;argsort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bm25_scores&lt;/span&gt;&lt;span class="p"&gt;)[::&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;

&lt;span class="c1"&gt;# --- Dense (semantic) leg: embed query + docs, rank by cosine similarity ---
&lt;/span&gt;&lt;span class="n"&gt;embedder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SentenceTransformer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;all-MiniLM-L6-v2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# small, fast embedding model
&lt;/span&gt;&lt;span class="n"&gt;doc_embeddings&lt;/span&gt; &lt;span class="o"&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;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;normalize_embeddings&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;query_embedding&lt;/span&gt; &lt;span class="o"&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;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;normalize_embeddings&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cosine_scores&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;doc_embeddings&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt; &lt;span class="n"&gt;query_embedding&lt;/span&gt;  &lt;span class="c1"&gt;# dot product of normalized vectors = cosine similarity
&lt;/span&gt;&lt;span class="n"&gt;dense_ranking&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;doc_ids&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&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;i&lt;/span&gt; &lt;span class="ow"&gt;in&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;argsort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cosine_scores&lt;/span&gt;&lt;span class="p"&gt;)[::&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sparse (BM25) ranking:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sparse_ranking&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Dense (semantic) ranking:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dense_ranking&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# --- Fuse both rankings with RRF (reusing the function from section 4.3) ---
&lt;/span&gt;&lt;span class="n"&gt;final_ranking&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;reciprocal_rank_fusion&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;dense_ranking&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sparse_ranking&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Final hybrid ranking:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;final_ranking&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  5. Metadata Filtering
&lt;/h2&gt;

&lt;h3&gt;
  
  
  5.1 The simple example first
&lt;/h3&gt;

&lt;p&gt;Imagine you're searching your email for a specific message and you know it arrived last week. You could (a) tell your email client "only show me emails from last week," and &lt;em&gt;then&lt;/em&gt; search the text — that's fast and narrow. Or you could (b) search the text across your &lt;em&gt;entire&lt;/em&gt; mailbox history, get back thousands of results, and manually throw away everything not from last week — that's slow and wasteful, since your search engine did a ton of unnecessary work scanning old emails it was always going to discard.&lt;/p&gt;

&lt;p&gt;Now a second, more serious example: at a company, an HR employee should be able to see every department's HR documents, but a regular software engineer should &lt;em&gt;only&lt;/em&gt; see documents relevant to their own team — they should never even see, let alone search inside, another department's confidential HR files. This isn't just a nice-to-have; it's a hard security requirement, often called &lt;strong&gt;RBAC&lt;/strong&gt; (Role-Based Access Control — a plain-English one-liner: &lt;em&gt;"restricting what a user can see or do based on their assigned role"&lt;/em&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  5.2 Technical explanation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Metadata filtering&lt;/strong&gt; applies &lt;strong&gt;hard constraints&lt;/strong&gt; (like date ranges, document owner, department, tags) either &lt;strong&gt;before&lt;/strong&gt; or &lt;strong&gt;after&lt;/strong&gt; the vector search runs, on top of whatever relevance ranking your retrieval produces. The notes' example query: &lt;em&gt;"What was our Q1 2026 revenue?"&lt;/em&gt; — you'd want to filter to &lt;code&gt;date range = Q1 2026&lt;/code&gt; before even letting semantic search loose on the whole corpus.&lt;/p&gt;

&lt;p&gt;Beyond just retrieval quality, the notes stress this is &lt;em&gt;especially&lt;/em&gt; important for &lt;strong&gt;Enterprise RAG and RBAC&lt;/strong&gt;: an employee simply must not be able to retrieve another department's HR docs, no matter how semantically relevant those docs might look to their query. This is a security boundary, not just a relevance tweak.&lt;/p&gt;

&lt;p&gt;There are two places you can apply the filter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pre-filter&lt;/strong&gt; — you extract the filter conditions (date range, department, tags) and pass them &lt;em&gt;into&lt;/em&gt; the vector search itself, so the search engine only ever looks at the allowed subset of documents. The notes call this &lt;strong&gt;faster&lt;/strong&gt;, but note it requires that "the vector db should support metadata filter" natively (not every vector database can efficiently combine filtering with approximate nearest-neighbor search).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Post-filter&lt;/strong&gt; — you run the vector search across the &lt;em&gt;entire&lt;/em&gt; corpus first, get back your top-K results, and &lt;em&gt;then&lt;/em&gt; throw away any results that don't match your filter. The notes call this a &lt;strong&gt;waste of retrieval budget&lt;/strong&gt;: you spent compute finding results you were always going to discard, and worse, you might discard so many that you end up with fewer than K valid results left over (imagine 8 of your top 10 semantic matches belong to a forbidden department — post-filtering leaves you with only 2 usable results).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5.3 Code: pre-filter (vector DB metadata filter) vs. post-filter (Python)
&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;# =============================================================================
# PRE-FILTERING: the filter is passed INTO the vector database query itself.
# The DB never even considers documents outside the RBAC/date boundary, so
# ranking + filtering happen together, efficiently, in one pass.
# (Qdrant-style filter syntax, as referenced in the notes' prototype.)
# =============================================================================
&lt;/span&gt;
&lt;span class="c1"&gt;# pip install qdrant-client --break-system-packages
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;qdrant_client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;QdrantClient&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;qdrant_client.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Filter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FieldCondition&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MatchValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Range&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;QdrantClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:6333&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# assumes a running Qdrant instance
&lt;/span&gt;
&lt;span class="n"&gt;query_vector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.12&lt;/span&gt;&lt;span class="p"&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="mf"&gt;0.31&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# a real embedding would be much longer, e.g. 1536-dim
&lt;/span&gt;
&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;collection_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hr_documents&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;query_vector&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;query_vector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;query_filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="c1"&gt;# RBAC constraint: only documents this user's department may see
&lt;/span&gt;            &lt;span class="nc"&gt;FieldCondition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;department&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;MatchValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;engineering&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
            &lt;span class="c1"&gt;# Date constraint: only Q1 2026 documents
&lt;/span&gt;            &lt;span class="nc"&gt;FieldCondition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gte&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2026-01-01&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lte&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2026-03-31&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Qdrant applies the filter WHILE searching, so all 10 results returned are
# guaranteed to satisfy both constraints — no wasted compute, no leakage risk.
&lt;/span&gt;

&lt;span class="c1"&gt;# =============================================================================
# POST-FILTERING: search everything first, THEN discard invalid results in
# plain Python. Shown for contrast — this is what the notes call "wasteful."
# =============================================================================
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;post_filter_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;all_search_results&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;user_department&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&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;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    all_search_results: e.g. the top-K matches from an UNFILTERED vector search
                         across the entire corpus, regardless of department.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;filtered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;all_search_results&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;department&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;user_department&lt;/span&gt;
        &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2026-01-01&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2026-03-31&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;filtered&lt;/span&gt;

&lt;span class="c1"&gt;# Danger illustrated: if the unfiltered top-10 search returns mostly documents
# from OTHER departments (because they happened to be semantically similar),
# post-filtering might leave you with just 1-2 usable results, or worse —
# if the filtering logic has a bug, a forbidden document could slip through
# into the LLM's context before being filtered, which is a real security risk
# with RBAC-sensitive data. Pre-filtering avoids this entirely.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  6. Reranking: Cross-Encoders vs. Bi-Encoders
&lt;/h2&gt;

&lt;h3&gt;
  
  
  6.1 The simple example first
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Bi-encoder analogy:&lt;/strong&gt; imagine two strangers each write their own short bio independently — "I like hiking, sci-fi movies, and cooking Italian food" and "I enjoy the outdoors, watching space films, and making pasta." You then compare these two bios &lt;em&gt;after the fact&lt;/em&gt; to guess how compatible these two people might be. It's fast (you can pre-write and store millions of bios ahead of time and just compare later), but it's a bit shallow — the two people never actually talked to each other, so subtle nuances get missed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cross-encoder analogy:&lt;/strong&gt; now imagine instead you sit those same two strangers down together for an actual conversation. They can react to each other in real time, clarify ambiguous statements, and pick up on nuance that never would have shown up in two separately-written bios. You get a &lt;em&gt;much&lt;/em&gt; better read on true compatibility — but it only works one pair at a time, and a live conversation takes real time and effort per pair. You couldn't have every person in a stadium have a real conversation with every other person — it simply wouldn't scale.&lt;/p&gt;

&lt;h3&gt;
  
  
  6.2 Technical explanation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Bi-encoders&lt;/strong&gt; (used for the &lt;em&gt;first&lt;/em&gt; stage of dense retrieval, described back in Section 4): the query is turned into an embedding, and each document is turned into an embedding, &lt;strong&gt;completely independently&lt;/strong&gt; of each other. Relevance is then just &lt;strong&gt;vector proximity&lt;/strong&gt; — how close the two embeddings are (measured via cosine similarity). Because documents can be embedded once, offline, ahead of time, bi-encoders are &lt;strong&gt;fast at query time and scalable&lt;/strong&gt; — this is why they're "good enough for most" use cases, per the notes.&lt;/p&gt;

&lt;p&gt;Their key weakness, from the notes: &lt;strong&gt;"No cross attention, i.e. query and doc never see each other."&lt;/strong&gt; Because the two embeddings were produced in isolation, the model can't attend to specific interactions between query terms and document terms. This makes bi-encoders &lt;strong&gt;weaker on negation, exact terms, etc.&lt;/strong&gt; — e.g., a bi-encoder might not clearly distinguish "the flight is NOT delayed" from "the flight IS delayed," because both sentences produce very similar embeddings overall (they share almost all the same words).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cross-encoders&lt;/strong&gt;, by contrast, take the &lt;strong&gt;&lt;code&gt;&amp;lt;query, document&amp;gt;&lt;/code&gt; pair as a single input&lt;/strong&gt; — literally concatenating the query and the candidate document together — and output &lt;strong&gt;one single relevance score&lt;/strong&gt; for that specific pair. Because "every token in the query attends every token of the document" (full cross-attention, across the whole concatenated input), the model can compare query and document &lt;em&gt;together&lt;/em&gt;, catching subtleties bi-encoders miss. This: &lt;strong&gt;improves precision&lt;/strong&gt;, and it genuinely &lt;strong&gt;sees the query and document together&lt;/strong&gt; rather than as two isolated summaries.&lt;/p&gt;

&lt;p&gt;The notes draw a small attention diagram illustrating the &lt;strong&gt;"lost in the middle" problem&lt;/strong&gt;: attention/relevance tends to concentrate most heavily near the query token and decays as you move deeper into a long document — meaning information buried in the &lt;em&gt;middle&lt;/em&gt; of a long document or a long context window can get comparatively under-weighted relative to information at the start or end. This is a known failure mode of transformer attention over long inputs, and it's part of why reranking with a small, focused candidate set (rather than dumping a giant unranked context at the LLM) genuinely helps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cross-encoders: much higher accuracy&lt;/strong&gt; — they can compare every token in the query to every token in the document, so they &lt;strong&gt;handle negation, exact matches, etc.&lt;/strong&gt; far better than bi-encoders. Example model named in the notes: &lt;code&gt;cross-encoder/ms-marco-MiniLM-L-6-v2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The catch — cross-encoders are slow and don't scale.&lt;/strong&gt; From the notes: they work well on "50 doc[s] v[s] million doc[s] per query" — meaning cross-encoders are only practical on a &lt;em&gt;small&lt;/em&gt; shortlist, never the entire corpus. There's &lt;strong&gt;no indexing&lt;/strong&gt; possible (because the score depends on the specific query, you can't precompute it ahead of time the way you can with bi-encoder embeddings), and each pair costs roughly &lt;strong&gt;~10ms of latency&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The latency math, worked out step by step (from the notes):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Assume you have &lt;strong&gt;1 million documents&lt;/strong&gt; and it costs &lt;strong&gt;20ms per &lt;code&gt;&amp;lt;query, doc&amp;gt;&lt;/code&gt; pair&lt;/strong&gt; to score with a cross-encoder.&lt;/li&gt;
&lt;li&gt;Total time for one query = 1,000,000 docs × 20ms = 20,000,000ms = 20,000 seconds.&lt;/li&gt;
&lt;li&gt;Converting to hours: 20,000 seconds ÷ 3,600 seconds/hour ≈ &lt;strong&gt;5.5 hours per single query&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's obviously unusable in a real product. The fix, stated directly in the notes: &lt;strong&gt;"could be done on candidate docs only."&lt;/strong&gt; This is exactly why the two techniques are used &lt;em&gt;together&lt;/em&gt; in a pipeline, not as alternatives: use the cheap, scalable bi-encoder (plus BM25/hybrid search) to narrow millions of documents down to a small shortlist (say, the top 40), and &lt;em&gt;then&lt;/em&gt; run the expensive, high-accuracy cross-encoder reranker only on that small shortlist. You get bi-encoder speed at scale, and cross-encoder accuracy where it matters most — the final ranking of the few documents that actually make it into the LLM's context.&lt;/p&gt;

&lt;h3&gt;
  
  
  6.3 Code: bi-encoder vs. cross-encoder on the same toy dataset
&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;# pip install sentence-transformers --break-system-packages
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sentence_transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SentenceTransformer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CrossEncoder&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="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Why is my API returning a 500 error under high load?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;candidate_docs&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;Throughput degradation under load can trigger unhandled exceptions, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;which surface to callers as HTTP 500 errors.&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;To reset your password, click &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Forgot Password&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; on the login screen.&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;500 errors are NOT related to client-side input validation issues.&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;Our billing FAQ covers refunds, invoices, and subscription cancellations.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# =============================================================================
# STAGE 1 (fast, scalable): BI-ENCODER — embed query and docs SEPARATELY,
# then compare with cosine similarity. This is what you'd run across
# millions of documents to get an initial shortlist.
# =============================================================================
&lt;/span&gt;&lt;span class="n"&gt;bi_encoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SentenceTransformer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;all-MiniLM-L6-v2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;query_vec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bi_encoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;normalize_embeddings&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;doc_vecs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bi_encoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;candidate_docs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;normalize_embeddings&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# cosine similarity = dot product of normalized vectors
&lt;/span&gt;&lt;span class="n"&gt;bi_encoder_scores&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;doc_vecs&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt; &lt;span class="n"&gt;query_vec&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bi-encoder ranking (fast, first-pass):&lt;/span&gt;&lt;span class="sh"&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;idx&lt;/span&gt; &lt;span class="ow"&gt;in&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;argsort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bi_encoder_scores&lt;/span&gt;&lt;span class="p"&gt;)[::&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="nf"&gt;print&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;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;bi_encoder_scores&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&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;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;candidate_docs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# =============================================================================
# STAGE 2 (slow, accurate): CROSS-ENCODER — feed the query+doc TOGETHER as a
# single pair, get one relevance score per pair. Only run this on the small
# shortlist that stage 1 already narrowed down (here, all 4, since it's tiny).
# =============================================================================
&lt;/span&gt;&lt;span class="n"&gt;cross_encoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CrossEncoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cross-encoder/ms-marco-MiniLM-L-6-v2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;pairs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;doc&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;doc&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;candidate_docs&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# literal &amp;lt;query, doc&amp;gt; pairing
&lt;/span&gt;&lt;span class="n"&gt;cross_encoder_scores&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cross_encoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pairs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Cross-encoder ranking (slow, high-precision rerank):&lt;/span&gt;&lt;span class="sh"&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;idx&lt;/span&gt; &lt;span class="ow"&gt;in&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;argsort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cross_encoder_scores&lt;/span&gt;&lt;span class="p"&gt;)[::&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="nf"&gt;print&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;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cross_encoder_scores&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&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;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;candidate_docs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# You'll typically notice the cross-encoder more confidently separates the
# TRUE match (the throughput/500-error doc) from the negation trap (doc #3,
# which mentions "500 errors" but explicitly says they are NOT related to
# the topic) — a distinction bi-encoders are known to struggle with.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  7. Query Rewriting with HyDE
&lt;/h2&gt;

&lt;h3&gt;
  
  
  7.1 The simple example first
&lt;/h3&gt;

&lt;p&gt;Imagine you ask a librarian, "Why is my code slow?" The librarian's catalog, though, doesn't use casual words like "slow" — the technical manuals on the shelf are indexed under formal terms like "throughput degradation," "latency regression," or "bottleneck analysis." If the librarian searches the catalog using your exact casual phrase, they might get nothing useful back, even though the &lt;em&gt;perfect&lt;/em&gt; manual for your problem exists on the shelf. The words just don't line up.&lt;/p&gt;

&lt;p&gt;A clever librarian's trick: before searching, they first jot down (in their head) roughly &lt;em&gt;what the ideal, technical-sounding manual would probably say&lt;/em&gt; about your problem — then they search the catalog using &lt;em&gt;that&lt;/em&gt; imagined, more formal description instead of your casual phrasing. That imagined "ideal answer" acts as a bridge between your everyday words and the library's formal vocabulary.&lt;/p&gt;

&lt;p&gt;That's exactly what &lt;strong&gt;HyDE&lt;/strong&gt; (Hypothetical Document Embedding) does for a RAG system.&lt;/p&gt;

&lt;h3&gt;
  
  
  7.2 Technical explanation
&lt;/h3&gt;

&lt;p&gt;The notes call the &lt;strong&gt;"Query-Document Gap"&lt;/strong&gt; the &lt;em&gt;most persistent failure mode in RAG&lt;/em&gt;. It shows up because user queries tend to be &lt;strong&gt;short and conversational&lt;/strong&gt;, while the underlying documents/corpus are often &lt;strong&gt;semantically distant&lt;/strong&gt; in their vocabulary — the classic example: "why is my pipeline slow?" (user's words) vs. "Throughput degradation is..." (the document's actual wording). Even though these mean almost the same thing conceptually, their raw embeddings may not be close enough for a naive semantic search to reliably connect them.&lt;/p&gt;

&lt;p&gt;There are two named fixes in the notes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Expand or rephrase the user query.&lt;/strong&gt; Simply ask an LLM: &lt;em&gt;"generate 3 alternate phrasings..."&lt;/em&gt; of the user's question, and search using all of them (or a combination), increasing the odds that at least one phrasing's embedding lands close to the real document.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. HyDE — Hypothetical Document Embedding.&lt;/strong&gt; Instead of embedding the user's raw, short question, you first ask an LLM to &lt;em&gt;write a short document that would answer this question&lt;/em&gt; — the exact prompt style from the notes: &lt;strong&gt;"Write a short document that will answer this question. Be factual. If you don't know the answer, write what the answer would typically look like."&lt;/strong&gt; You then take &lt;em&gt;that generated hypothetical document&lt;/em&gt; — not the original question — and embed &lt;em&gt;it&lt;/em&gt;. Because this hypothetical answer is written in a style and vocabulary much closer to how real documents in your corpus are likely written, its embedding tends to land much closer to the &lt;em&gt;real&lt;/em&gt; relevant document's embedding than the original short, casual question would have.&lt;/p&gt;

&lt;p&gt;Technically, HyDE reframes retrieval as a &lt;strong&gt;similarity search between &lt;code&gt;d&lt;/code&gt; and &lt;code&gt;d ∈ D&lt;/code&gt;&lt;/strong&gt; — that is, comparing a (hypothetical) document to the real documents in your corpus &lt;code&gt;D&lt;/code&gt;, rather than comparing a short question to documents (which is a fundamentally harder, more mismatched comparison).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When does this actually help?&lt;/strong&gt; The notes are precise about this: HyDE &lt;strong&gt;"works when corpus vocab is very different than user query."&lt;/strong&gt; Good candidates: &lt;strong&gt;scientific papers, legal texts, internal jargon-heavy documentation&lt;/strong&gt; — domains where the "real" documents are written in dense, formal, or highly technical language that a typical user's question would never naturally use.&lt;/p&gt;

&lt;h3&gt;
  
  
  7.3 Code: HyDE retrieval function
&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;# pip install anthropic sentence-transformers --break-system-packages
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;anthropic&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sentence_transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SentenceTransformer&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="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;anthropic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Anthropic&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;embedder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SentenceTransformer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;all-MiniLM-L6-v2&lt;/span&gt;&lt;span class="sh"&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;generate_hypothetical_answer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&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;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Ask an LLM to write a short, FACTUAL-SOUNDING document that would answer
    the user&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s question. This is the exact prompt shape from the notes:
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Write a short document that will answer this question. Be factual.
    If you don&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;t know the answer, write what the answer would typically
    look like.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;
    We use this generated text purely to bridge the vocabulary gap — we do
    NOT show this hypothetical text to the end user; it&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s an internal
    retrieval-only artifact.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude-sonnet-4-6&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&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;role&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;user&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;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;Write a short document that will answer this question. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Be factual. If you don&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;t know the answer, write what the &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;answer would typically look like.&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;Question: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_query&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hyde_retrieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;doc_texts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;doc_ids&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;top_k&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Full HyDE retrieval: generate a hypothetical answer, embed THAT instead
    of the raw query, then find the closest real documents to it.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="c1"&gt;# Step 1: generate the hypothetical, jargon-matched answer
&lt;/span&gt;    &lt;span class="n"&gt;hypothetical_doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generate_hypothetical_answer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Step 2: embed the HYPOTHETICAL document, not the raw user query
&lt;/span&gt;    &lt;span class="n"&gt;hyde_embedding&lt;/span&gt; &lt;span class="o"&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;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hypothetical_doc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;normalize_embeddings&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Step 3: embed the real corpus documents (in production this is pre-computed
&lt;/span&gt;    &lt;span class="c1"&gt;# and stored in a vector DB, not re-computed on every query)
&lt;/span&gt;    &lt;span class="n"&gt;doc_embeddings&lt;/span&gt; &lt;span class="o"&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;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc_texts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;normalize_embeddings&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Step 4: rank real documents by similarity to the hypothetical embedding
&lt;/span&gt;    &lt;span class="n"&gt;similarities&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;doc_embeddings&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt; &lt;span class="n"&gt;hyde_embedding&lt;/span&gt;
    &lt;span class="n"&gt;top_indices&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="nf"&gt;argsort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;similarities&lt;/span&gt;&lt;span class="p"&gt;)[::&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][:&lt;/span&gt;&lt;span class="n"&gt;top_k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;doc_ids&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;doc_texts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&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;similarities&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&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;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;top_indices&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="c1"&gt;# --- Example -----------------------------------------------------------------
&lt;/span&gt;&lt;span class="n"&gt;corpus_texts&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;Throughput degradation under sustained load is typically caused by &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;connection pool exhaustion or unindexed database queries.&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;Our refund policy allows cancellations within 30 days of purchase.&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;Latency regressions in distributed systems often trace back to N+1 &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;query patterns or missing caching layers.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;corpus_ids&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;perf_doc_1&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;refund_policy&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;perf_doc_2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;hyde_retrieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;why is my code slow?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;corpus_texts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;corpus_ids&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;doc_id&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="n"&gt;score&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&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="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&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;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;doc_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Without HyDE, embedding the raw casual query "why is my code slow?" might
# rank the perf docs only weakly, since they never use the word "slow."
# With HyDE, the generated hypothetical answer naturally uses words like
# "throughput," "latency," and "degradation" -- much closer to the real docs.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  8. Semantic Caching
&lt;/h2&gt;

&lt;h3&gt;
  
  
  8.1 The simple example first
&lt;/h3&gt;

&lt;p&gt;Imagine ten different customers each phrase the exact same underlying question in slightly different words: "How do I integrate libX?", "I want help integrating libX," "How can I get libX working with my app?" A naive cache (the kind that only matches &lt;em&gt;exact&lt;/em&gt; strings) would treat these as three totally unrelated questions and pay to compute a fresh, expensive answer for every single one — even though the &lt;em&gt;right&lt;/em&gt; answer is identical for all three.&lt;/p&gt;

&lt;p&gt;A smarter cache recognizes that all three questions &lt;em&gt;mean&lt;/em&gt; the same thing, and serves the same cached, already-computed answer to all of them — saving time and money on the second and third askers, without them ever noticing anything different.&lt;/p&gt;

&lt;h3&gt;
  
  
  8.2 Technical explanation
&lt;/h3&gt;

&lt;p&gt;The core motivating fact from the notes: &lt;strong&gt;"Every call to LLM cost[s] latency and tokens"&lt;/strong&gt; — i.e., &lt;strong&gt;time and money&lt;/strong&gt;. A &lt;strong&gt;semantic cache&lt;/strong&gt; sits in front of the LLM: it &lt;strong&gt;intercepts a query before it reaches the model&lt;/strong&gt; and, if a &lt;em&gt;semantically similar&lt;/em&gt; (not necessarily an exact-text-match) prior query exists in the cache, it returns the cached response instead of paying for a new LLM call.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When this works best&lt;/strong&gt;, per the notes: &lt;strong&gt;static data with no personalization&lt;/strong&gt; — situations where the "right" answer genuinely doesn't change per-user or over short timeframes. Two named examples:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Asking an AI assistant about your API/integration documentation ("How do I integrate libX?" / "I want help to integrate libX" — both map to the same content already published on libX's website).&lt;/li&gt;
&lt;li&gt;A customer-support chatbot with a &lt;strong&gt;bounded query space&lt;/strong&gt; — the notes cite that in such settings, roughly &lt;strong&gt;60%&lt;/strong&gt; of incoming queries can be near-duplicates of previously-asked ones, making caching extremely high-leverage.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Implementation, step by step (from the notes):&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Take the &lt;strong&gt;incoming query&lt;/strong&gt;, and either normalize it (lowercase, strip punctuation, etc.) or store it as-is.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Index it in a vector database along with its (eventual) response&lt;/strong&gt; — so both the question's embedding and its answer live together.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;During lookup, find similar queries&lt;/strong&gt; — embed the new incoming query, and search the cache's vector index for anything above a chosen similarity threshold.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The core tension: cost vs. correctness&lt;/strong&gt;, governed entirely by &lt;em&gt;what similarity threshold you pick and how&lt;/em&gt; you set it. The notes are blunt about the two failure directions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Threshold too low&lt;/strong&gt; → &lt;strong&gt;wrong answers.&lt;/strong&gt; You'll match queries that only &lt;em&gt;look&lt;/em&gt; superficially similar but actually mean something different (e.g., "How do I cancel my subscription?" vs. "How do I change my subscription plan?" — related, but the correct action is different), and you'll confidently serve the &lt;em&gt;wrong&lt;/em&gt; cached answer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Threshold too high&lt;/strong&gt; → &lt;strong&gt;hit rate collapses.&lt;/strong&gt; You become so strict that almost nothing ever matches, and you rarely benefit from caching at all, defeating the entire purpose.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The reliable way to pick a threshold&lt;/strong&gt;, per the notes: don't guess — &lt;strong&gt;empirically find your "sweet spot" on your own query distribution.&lt;/strong&gt; As a reasonable starting point, the notes suggest &lt;strong&gt;0.85 cosine similarity&lt;/strong&gt;, but this must be &lt;em&gt;calibrated&lt;/em&gt;, not treated as gospel, because every product's query patterns differ.&lt;/p&gt;

&lt;h3&gt;
  
  
  8.3 Code: a minimal semantic cache class
&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;# pip install sentence-transformers --break-system-packages
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sentence_transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SentenceTransformer&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SemanticCache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    A minimal in-memory semantic cache: get() looks for a semantically
    similar prior query above `similarity_threshold`; set() stores a new
    query + response pair for future lookups.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&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;similarity_threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.85&lt;/span&gt;&lt;span class="p"&gt;):&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;embedder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SentenceTransformer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;all-MiniLM-L6-v2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&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;similarity_threshold&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;similarity_threshold&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;cached_queries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cached_embeddings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&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;=&lt;/span&gt; &lt;span class="p"&gt;[]&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;cached_responses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&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;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&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;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Return a cached response if a semantically similar query exists, else None.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&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;cached_queries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

        &lt;span class="n"&gt;query_embedding&lt;/span&gt; &lt;span class="o"&gt;=&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;embedder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;normalize_embeddings&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;similarities&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="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&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;cached_embeddings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt; &lt;span class="n"&gt;query_embedding&lt;/span&gt;
        &lt;span class="n"&gt;best_idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&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;argmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;similarities&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;best_score&lt;/span&gt; &lt;span class="o"&gt;=&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;similarities&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;best_idx&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;best_score&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&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;similarity_threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&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;[cache HIT] similarity=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;best_score&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&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; matched: &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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cached_queries&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;best_idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&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;cached_responses&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;best_idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="nf"&gt;print&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;[cache MISS] best similarity was only &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;best_score&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&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;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Store a new query + response pair in the cache.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="o"&gt;=&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;embedder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;normalize_embeddings&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&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;cached_queries&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;query&lt;/span&gt;&lt;span class="p"&gt;)&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;cached_embeddings&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;embedding&lt;/span&gt;&lt;span class="p"&gt;)&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;cached_responses&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;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="c1"&gt;# --- Demonstration: threshold too low vs. too high --------------------------
&lt;/span&gt;
&lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SemanticCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;similarity_threshold&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.85&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;How do I integrate libX?&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;To integrate libX, install it via pip and call libx.init()...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Reasonable threshold (0.85) ---&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;I want help integrating libX&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# should HIT: same intent, different phrasing
&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s the weather in Tokyo?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# should MISS: unrelated question
&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Threshold set TOO LOW (0.50): risks wrong answers ---&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;loose_cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SemanticCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;similarity_threshold&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;loose_cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;How do I cancel my subscription?&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;Go to Settings &amp;gt; Billing &amp;gt; Cancel Plan.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# This is a RELATED but MEANINGFULLY DIFFERENT question -- with a low
# threshold it may incorrectly match and serve the wrong cached answer.
&lt;/span&gt;&lt;span class="n"&gt;loose_cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;How do I upgrade my subscription plan?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Threshold set TOO HIGH (0.99): hit rate collapses ---&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;strict_cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SemanticCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;similarity_threshold&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.99&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;strict_cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;How do I integrate libX?&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;To integrate libX, install it via pip...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Even a near-identical paraphrase may now fail to match because embeddings
# are almost never PERFECTLY identical between two different sentences.
&lt;/span&gt;&lt;span class="n"&gt;strict_cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;I want help to integrate libX&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;p&gt;The prototype in the notes ("Semantic Caching and Similarity Calibration") describes exactly this experiment at scale: &lt;strong&gt;500 queries → embeddings → index&lt;/strong&gt;, then sweeping thresholds from &lt;strong&gt;0.75 to 0.95&lt;/strong&gt;, populating the cache first, then computing &lt;strong&gt;hit rate&lt;/strong&gt; (fraction of queries found in cache) and &lt;strong&gt;accuracy/precision&lt;/strong&gt;. It uses three types of test queries: &lt;strong&gt;seed&lt;/strong&gt; (the original query, e.g. "Explain the ending of movie Arrival"), &lt;strong&gt;paraphrase&lt;/strong&gt; (same intent, different words, e.g. "Please clarify the conclusion of the movie Arrival" — a &lt;em&gt;correct&lt;/em&gt; match), and &lt;strong&gt;near-miss&lt;/strong&gt; (looks similar but has a genuinely different intent, e.g. "Who directed the movie Arrival" — a &lt;em&gt;trap&lt;/em&gt;, since matching this to the seed's answer would be a &lt;strong&gt;false positive / incorrect hit&lt;/strong&gt;). The accuracy formula from the notes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;accuracy = correct hits (paraphrase matching the seed) / total hits
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Below is a small script that runs this exact style of calibration experiment:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;evaluate_threshold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SemanticCache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;seed_paraphrase_pairs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;near_miss_queries&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    seed_paraphrase_pairs: list of (seed_query, paraphrase_query, response) tuples
                           -- paraphrase SHOULD correctly hit the seed&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s cached entry.
    near_miss_queries: list of queries that LOOK similar but have different intent
                       -- these SHOULD ideally miss (or if they hit, that&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s a false positive).
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&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;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;seed_paraphrase_pairs&lt;/span&gt;&lt;span class="p"&gt;}:&lt;/span&gt;
        &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;total_hits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;correct_hits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;paraphrase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;seed_paraphrase_pairs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paraphrase&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;result&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;total_hits&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="n"&gt;correct_hits&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;  &lt;span class="c1"&gt;# paraphrase hitting its own seed = correct
&lt;/span&gt;
    &lt;span class="n"&gt;false_positives&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;near_miss&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;near_miss_queries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;near_miss&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;result&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;total_hits&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="n"&gt;false_positives&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;  &lt;span class="c1"&gt;# near-miss hitting ANY cached entry = wrong
&lt;/span&gt;
    &lt;span class="n"&gt;accuracy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;correct_hits&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;total_hits&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;total_hits&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hit_rate_relevant&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;correct_hits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;false_positives&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;false_positives&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;accuracy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;accuracy&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  9. Full System Design: RAG with 10M Documents, Zero Hallucinations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  9.1 The simple example first
&lt;/h3&gt;

&lt;p&gt;Imagine building a "help desk" for a huge company with 10 million internal documents. A visitor walks up and asks a question in plain English. Before answering, your help desk clerk must: (1) understand what's really being asked, (2) go search &lt;em&gt;both&lt;/em&gt; the exact-keyword index and the meaning-based index of every document, (3) combine and re-sort those results by true relevance, (4) double-check that the results are actually confident and trustworthy (not just "vaguely related"), (5) draft an answer strictly grounded in what was found, and — critically — (6) go back and check every single sentence of that draft answer against the source documents &lt;em&gt;before&lt;/em&gt; saying it out loud, refusing to say anything it can't verify. That's the whole system in one sentence: &lt;strong&gt;retrieve carefully, answer carefully, and never say something you can't prove.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  9.2 Technical explanation: requirements and why they matter
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What the system does:&lt;/strong&gt; Answers natural language queries. &lt;strong&gt;The one hard guarantee that shapes the entire design:&lt;/strong&gt; &lt;em&gt;"the system must not assert any claim that cannot be traced to [a] retrieved passage."&lt;/em&gt; This single sentence is why "correctness" (not just relevance, not just speed) drives nearly every architectural decision below. Why this guarantee matters: an internal knowledge system that confidently states wrong facts is often &lt;em&gt;worse&lt;/em&gt; than one that admits "I don't know," because wrong confident answers erode trust and can cause real harm (wrong policy info, wrong financial numbers, etc.).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What the system explicitly is NOT:&lt;/strong&gt; a document management system, an ingestion pipeline product, etc. — the notes flag this so the design stays scoped: we are &lt;em&gt;not&lt;/em&gt; building a general-purpose CMS, just the "ask questions about documents that already exist somewhere" layer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scale numbers (why they matter):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;10 million documents&lt;/strong&gt;, roughly &lt;strong&gt;2KB each&lt;/strong&gt; → this sets the &lt;em&gt;raw&lt;/em&gt; data size baseline (worked out below).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;200 QPS&lt;/strong&gt; (queries per second) → this is &lt;em&gt;very read-heavy&lt;/em&gt; traffic, which shapes almost every capacity and replication decision (lots of replicas for reads, less concern about write throughput).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;~1,000 documents updated per day&lt;/strong&gt; → writes are comparatively rare, which is why the system can tolerate &lt;strong&gt;eventual consistency&lt;/strong&gt; rather than needing instant read-your-writes guarantees.&lt;/li&gt;
&lt;li&gt;We do &lt;strong&gt;not&lt;/strong&gt; need to serve data from a just-updated document immediately — an SLO (service level objective, a plain-English promise about how good the system must be) of roughly &lt;strong&gt;~15 minutes&lt;/strong&gt; eventual consistency is acceptable. Why this matters: it means the write path doesn't need to be synchronously blocking or ultra-low-latency; it can batch, chunk, and index asynchronously.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  9.3 The nine solutioning questions (from the notes)
&lt;/h3&gt;

&lt;p&gt;The notes lay out the exact ordered checklist a systems-design interview or real design doc would walk through:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How do we store the docs, and where?&lt;/li&gt;
&lt;li&gt;What does the query API look like?&lt;/li&gt;
&lt;li&gt;What does the response look like?&lt;/li&gt;
&lt;li&gt;Which databases, and why?&lt;/li&gt;
&lt;li&gt;Capacity estimation: query and storage.&lt;/li&gt;
&lt;li&gt;Read path and query pseudocode.&lt;/li&gt;
&lt;li&gt;What should be returned?&lt;/li&gt;
&lt;li&gt;What are the failure modes in the read path?&lt;/li&gt;
&lt;li&gt;What does the write path look like (S3 → databases)?&lt;/li&gt;
&lt;li&gt;What are we missing? (a deliberate closing gut-check)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's walk through each, grounding every architectural choice in a "why" before the "what."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1 &amp;amp; 2 — Storage location and Query API.&lt;/strong&gt; Documents live in an &lt;strong&gt;S3 bucket&lt;/strong&gt; (e.g. &lt;code&gt;s3://myrag/doc1.txt&lt;/code&gt;), organized per the notes with some inspiration from Git's object storage model (content-addressed, organized by structure like &lt;code&gt;date&lt;/code&gt;/&lt;code&gt;git&lt;/code&gt;-style hashing prefixes, e.g. folders &lt;code&gt;ab/&lt;/code&gt;, &lt;code&gt;ac/&lt;/code&gt;, &lt;code&gt;ad/&lt;/code&gt; — a common trick to avoid dumping literally 10 million flat files into one folder, which many filesystems and object stores handle poorly). The query API is a simple HTTP endpoint: &lt;code&gt;GET /query?&lt;/code&gt; — the notes sketch this as &lt;code&gt;@app.get('/query?')&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3 — Response shape.&lt;/strong&gt; From the notes, a single &lt;code&gt;respond()&lt;/code&gt; call returns a structured object containing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;question&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;
&lt;span class="na"&gt;filters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;date_from&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;date_to&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;source_domains&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;tag&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;topics&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;etc.&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
&lt;span class="na"&gt;answer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;
&lt;span class="na"&gt;refused&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;true/false&amp;gt;&lt;/span&gt;
&lt;span class="na"&gt;refusal_reason&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;string, if refused&amp;gt;&lt;/span&gt;
&lt;span class="na"&gt;passages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;doc_id&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;passage_id&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;text&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;score&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;highlight_idx&lt;/span&gt;&lt;span class="pi"&gt;},&lt;/span&gt; &lt;span class="nv"&gt;...&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;confidence&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;number&amp;gt;&lt;/span&gt;
&lt;span class="na"&gt;query_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;string&amp;gt;   -- used for tracing/debugging a specific request later&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why this shape: it's not enough to return just an answer string. You need the &lt;strong&gt;passages&lt;/strong&gt; (so a user or auditor can verify the claim was actually grounded), a &lt;strong&gt;refused&lt;/strong&gt; boolean plus &lt;strong&gt;reason&lt;/strong&gt; (so "I don't know" is a first-class, explicit outcome, not a vague hedge buried in the answer text), a &lt;strong&gt;confidence&lt;/strong&gt; score (so downstream systems or UI can decide whether to trust/display the answer), and a &lt;strong&gt;query_id&lt;/strong&gt; for &lt;strong&gt;tracing&lt;/strong&gt; — being able to look up exactly what happened for a specific request later, which is essential for debugging a "why did it hallucinate here" incident.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4 — Which databases, and why:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;S3&lt;/strong&gt; — the durable source of truth for raw documents.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Postgres (PG Master + Replica)&lt;/strong&gt; — stores document and passage &lt;strong&gt;metadata&lt;/strong&gt; (id, text, title, URL, date, tags, is-deleted flag for docs; id, doc_id, chunk_index, text, char_start, char_end, token_count for passages). Why Postgres: this is classic structured, relational metadata — dates, tags, foreign keys between docs and their passages — exactly what a relational database is built for, plus JSONB support for flexible tag/topic fields and strong replication for read-heavy traffic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Qdrant (vector database)&lt;/strong&gt; — stores the dense embeddings for approximate nearest-neighbor (ANN) search. Why a dedicated vector DB: embeddings need specialized indexing structures (like HNSW — Hierarchical Navigable Small World, a plain-English one-liner: &lt;em&gt;"a graph-based index structure that lets you find approximately the closest vectors in high-dimensional space without comparing against every single vector"&lt;/em&gt;) that general-purpose databases don't do efficiently at this scale.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ElasticSearch (ES)&lt;/strong&gt; — holds the &lt;strong&gt;BM25 inverted index&lt;/strong&gt; for the sparse/keyword leg of hybrid search (Section 4), using an &lt;strong&gt;English analyzer with stemming and stop-word removal&lt;/strong&gt; (so "running" and "run" are treated as the same term, and words like "the"/"is" don't dilute matching).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redis (cache)&lt;/strong&gt; — caches passages and query results (this is the semantic-caching layer from Section 8), reducing repeat load on the expensive LLM-generation step.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;5 — Capacity estimation, worked step by step (numbers straight from the notes' math):&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Query load:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;50,000 DAU (daily active users) × 8 queries/day/user × 4.5 (peak multiplier)
──────────────────────────────────────────────────────────────────────────  = 200 QPS
                          86,400 seconds/day
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So: 50,000 × 8 = 400,000 total queries/day. 400,000 ÷ 86,400 seconds ≈ 4.6 QPS &lt;em&gt;average&lt;/em&gt;. Multiply by a &lt;strong&gt;peak multiplier of 4.5&lt;/strong&gt; (because traffic isn't spread evenly across 24 hours — there are busy hours) to get roughly &lt;strong&gt;200 QPS at peak&lt;/strong&gt;. The team then designs for a bit of headroom: &lt;strong&gt;250 QPS&lt;/strong&gt; target capacity.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Storage — raw documents:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10,000,000 docs × 2KB each = 20GB raw data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is small enough to be almost trivial to store redundantly — it's the &lt;em&gt;derived&lt;/em&gt; data (embeddings, indexes) that actually dominates storage, as shown next.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Storage — vector embeddings:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10 × 10^6 documents × 8 passages/doc (avg) × 1536 dimensions × 4 bytes/float32
= 10×10^6 × 8 × 1536 × 4 bytes
≈ 491 GB (just the raw embedding vectors)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then add &lt;strong&gt;HNSW graph overhead&lt;/strong&gt; (the extra data structure the ANN index needs to do fast approximate search) — roughly &lt;strong&gt;1.5x&lt;/strong&gt; — bringing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Total vector index storage ≈ 491GB × 1.5 ≈ 700GB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is why the notes conclude: &lt;strong&gt;"Hence, we need a distributed vector database"&lt;/strong&gt; — 700GB (spread across replicas for redundancy and read throughput) is well beyond what a single machine comfortably handles for a low-latency, always-on service. The notes also mention &lt;strong&gt;Product Quantization&lt;/strong&gt; can reduce this by roughly &lt;strong&gt;8x&lt;/strong&gt;, but &lt;em&gt;"at [the] cost of recall"&lt;/em&gt; — i.e., you trade some retrieval accuracy for a much smaller memory footprint, a classic space/accuracy tradeoff.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;ElasticSearch (BM25 index) sizing:&lt;/em&gt; roughly &lt;code&gt;20GB × 1.5 ≈ 30GB&lt;/code&gt; for the inverted index, run as a &lt;strong&gt;3-node cluster with high availability (HA)&lt;/strong&gt; plus 3 replicas, ≈ &lt;strong&gt;90GB total&lt;/strong&gt;, each node with &lt;strong&gt;30GB RAM&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Postgres sizing:&lt;/em&gt; the 20GB of metadata plus JSONB overhead and replication comes out to roughly &lt;strong&gt;60-80GB&lt;/strong&gt;, served with &lt;strong&gt;3 read replicas&lt;/strong&gt; (since this is read-heavy traffic) with a target replication lag of &lt;strong&gt;under 100ms&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Redis:&lt;/em&gt; a &lt;strong&gt;3-node cluster with 3 replicas&lt;/strong&gt;, caching passages and query results — this is explicitly conditioned on the earlier semantic-caching insight: it works best &lt;strong&gt;"if no personalization &amp;amp; data is static."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6, 7, 8 — Read path, what's returned, and failure handling&lt;/strong&gt; are covered together via the code walkthrough in Section 9.4 below (the notes' own &lt;code&gt;respond()&lt;/code&gt; pseudocode).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;9 — Write path&lt;/strong&gt; is covered in Section 9.5 below.&lt;/p&gt;

&lt;h3&gt;
  
  
  9.4 Code: the read path (&lt;code&gt;respond()&lt;/code&gt; function), cleaned up and fully commented
&lt;/h3&gt;

&lt;p&gt;The notes' own hand-drawn pseudocode for the read path is the heart of the "zero hallucinations" guarantee. Here it is translated into clean, fully-commented Python:&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;


&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RespondResult&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;passages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;
    &lt;span class="n"&gt;refused&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;
    &lt;span class="n"&gt;refusal_reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;confidence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;RespondResult&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    The full &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;zero hallucinations&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; read path:
    embed -&amp;gt; hybrid search (dense + sparse) -&amp;gt; RRF fusion -&amp;gt; cross-encoder
    rerank -&amp;gt; confidence threshold -&amp;gt; LLM generate -&amp;gt; claim extraction -&amp;gt;
    NLI fact-check -&amp;gt; final answer OR refusal.

    Every stage is a chance to refuse rather than guess -- that&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s the whole
    point of the &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;must not assert unsupported claims&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; guarantee.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="c1"&gt;# -------------------------------------------------------------------
&lt;/span&gt;    &lt;span class="c1"&gt;# Step 1: Embed the question (turn text into a dense vector).
&lt;/span&gt;    &lt;span class="c1"&gt;# -------------------------------------------------------------------
&lt;/span&gt;    &lt;span class="n"&gt;query_vector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;embed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# -------------------------------------------------------------------
&lt;/span&gt;    &lt;span class="c1"&gt;# Step 2: Hybrid search -- run dense (vector) and sparse (BM25) search
&lt;/span&gt;    &lt;span class="c1"&gt;# CONCURRENTLY (this is the "parallel tool calls" idea from Section 3
&lt;/span&gt;    &lt;span class="c1"&gt;# applied to retrieval itself), each returning their own top-40 shortlist.
&lt;/span&gt;    &lt;span class="c1"&gt;# -------------------------------------------------------------------
&lt;/span&gt;    &lt;span class="n"&gt;dense_results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;qdrant_ann_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query_vector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;top_k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sparse_results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;elasticsearch_bm25&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;top_k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# -------------------------------------------------------------------
&lt;/span&gt;    &lt;span class="c1"&gt;# Step 3: Fuse both ranked lists with Reciprocal Rank Fusion (Section 4).
&lt;/span&gt;    &lt;span class="c1"&gt;# -------------------------------------------------------------------
&lt;/span&gt;    &lt;span class="n"&gt;candidates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;rrf_fusion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dense_results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sparse_results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# -------------------------------------------------------------------
&lt;/span&gt;    &lt;span class="c1"&gt;# Step 4: Rerank the fused candidates with a cross-encoder (Section 6)
&lt;/span&gt;    &lt;span class="c1"&gt;# for much higher-precision final ordering. Cross-encoders are too slow
&lt;/span&gt;    &lt;span class="c1"&gt;# to run on millions of docs, but perfectly fine on ~40-80 candidates.
&lt;/span&gt;    &lt;span class="c1"&gt;# -------------------------------------------------------------------
&lt;/span&gt;    &lt;span class="n"&gt;ranked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;cross_encoder_rerank&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;top_k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# -------------------------------------------------------------------
&lt;/span&gt;    &lt;span class="c1"&gt;# Step 5: CONFIDENCE GATE #1 -- if even the BEST passage scores too low,
&lt;/span&gt;    &lt;span class="c1"&gt;# we don't have real evidence to answer with. Refuse rather than guess.
&lt;/span&gt;    &lt;span class="c1"&gt;# -------------------------------------------------------------------
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;passage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;passage&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ranked&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;refused_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;low_retrieval_confidence&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# -------------------------------------------------------------------
&lt;/span&gt;    &lt;span class="c1"&gt;# Step 6: Take only the top 5 passages -- this keeps the LLM's context
&lt;/span&gt;    &lt;span class="c1"&gt;# small, focused, and less prone to the "lost in the middle" problem
&lt;/span&gt;    &lt;span class="c1"&gt;# (Section 6) that hurts long, noisy contexts.
&lt;/span&gt;    &lt;span class="c1"&gt;# -------------------------------------------------------------------
&lt;/span&gt;    &lt;span class="n"&gt;top_passages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ranked&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;build_prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;top_passages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# -------------------------------------------------------------------
&lt;/span&gt;    &lt;span class="c1"&gt;# Step 7: Ask the LLM to generate an answer, GROUNDED ONLY in top_passages.
&lt;/span&gt;    &lt;span class="c1"&gt;# temperature=0 for maximum determinism/factuality (no creative sampling).
&lt;/span&gt;    &lt;span class="c1"&gt;# -------------------------------------------------------------------
&lt;/span&gt;    &lt;span class="n"&gt;raw_answer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;llm_generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# -------------------------------------------------------------------
&lt;/span&gt;    &lt;span class="c1"&gt;# Step 8: CONFIDENCE GATE #2 -- the prompt instructs the LLM to say
&lt;/span&gt;    &lt;span class="c1"&gt;# "INSUFFICIENT_CONTEXT" if the retrieved passages don't actually answer
&lt;/span&gt;    &lt;span class="c1"&gt;# the question. If it says so, we honor that and refuse cleanly.
&lt;/span&gt;    &lt;span class="c1"&gt;# -------------------------------------------------------------------
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;raw_answer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INSUFFICIENT_CONTEXT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;refused_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;no_relevant_passages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# -------------------------------------------------------------------
&lt;/span&gt;    &lt;span class="c1"&gt;# Step 9: CLAIM EXTRACTION + FACT-CHECKING -- this is the actual
&lt;/span&gt;    &lt;span class="c1"&gt;# "zero hallucinations" enforcement mechanism. Break the raw answer
&lt;/span&gt;    &lt;span class="c1"&gt;# into small, individually-checkable "atomic claims" (e.g. one claim
&lt;/span&gt;    &lt;span class="c1"&gt;# per sentence or per fact), then verify EACH claim is actually
&lt;/span&gt;    &lt;span class="c1"&gt;# entailed (logically supported) by the retrieved passages using an
&lt;/span&gt;    &lt;span class="c1"&gt;# NLI (Natural Language Inference) model -- a plain-English one-liner:
&lt;/span&gt;    &lt;span class="c1"&gt;# "a model that decides whether a piece of text logically follows from,
&lt;/span&gt;    &lt;span class="c1"&gt;# contradicts, or is unrelated to another piece of text."
&lt;/span&gt;    &lt;span class="c1"&gt;# If ANY claim can't be verified, we refuse rather than let an
&lt;/span&gt;    &lt;span class="c1"&gt;# unsupported claim slip through to the user.
&lt;/span&gt;    &lt;span class="c1"&gt;# -------------------------------------------------------------------
&lt;/span&gt;    &lt;span class="n"&gt;claims&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;extract_atomic_claims&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw_answer&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;claim&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;claims&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;nli_entailed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;claim&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;top_passages&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;refused_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unverified_claim&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# -------------------------------------------------------------------
&lt;/span&gt;    &lt;span class="c1"&gt;# All gates passed: the answer is grounded, confident, and every claim
&lt;/span&gt;    &lt;span class="c1"&gt;# has been individually verified against the retrieved evidence.
&lt;/span&gt;    &lt;span class="c1"&gt;# -------------------------------------------------------------------
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;RespondResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;answer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;raw_answer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;passages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;top_passages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;refused&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&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;refused_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;RespondResult&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;A first-class, explicit refusal -- NOT a vague hedge buried in text.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;RespondResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;answer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;passages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="n"&gt;refused&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;refusal_reason&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="c1"&gt;# NOTE: embed(), qdrant_ann_search(), elasticsearch_bm25(), rrf_fusion(),
# cross_encoder_rerank(), build_prompt(), llm_generate(), extract_atomic_claims(),
# and nli_entailed() are each real functions you'd implement using the
# techniques from Sections 4, 6, and standard NLI models (e.g. a model
# fine-tuned on MNLI/SNLI for entailment classification) -- they are stubbed
# here to keep the read-path LOGIC the star of this example.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  9.5 Code: a simple hierarchical chunking function (sentence-boundary + token overlap)
&lt;/h3&gt;

&lt;p&gt;The write path (S3 → chunk ingestor → hierarchical chunking → embeddings → storage) needs a chunker. The notes specify &lt;strong&gt;sentence-level boundaries (using spaCy)&lt;/strong&gt; plus &lt;strong&gt;32-token overlap&lt;/strong&gt; between consecutive chunks (overlap ensures a sentence that would otherwise get awkwardly split across two chunks still has enough surrounding context in at least one of them).&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;# pip install spacy --break-system-packages
# python -m spacy download en_core_web_sm
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;spacy&lt;/span&gt;

&lt;span class="n"&gt;nlp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;spacy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;en_core_web_sm&lt;/span&gt;&lt;span class="sh"&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;chunk_document&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="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_tokens_per_chunk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;overlap_tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;32&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;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Hierarchical, sentence-boundary-respecting chunking with token overlap.

    Why sentence boundaries: splitting mid-sentence produces chunks that are
    semantically broken and embed poorly. Why overlap: a fact stated right at
    a chunk boundary shouldn&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;t lose its surrounding context in EVERY chunk
    that references it.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;nlp&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="n"&gt;sentences&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sent&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="nf"&gt;strip&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;sent&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sents&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;current_chunk_sentences&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&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;current_token_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;char_cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;rough_token_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&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;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# A simple approximation (real systems use a proper tokenizer, e.g.
&lt;/span&gt;        &lt;span class="c1"&gt;# tiktoken, matching whatever embedding model will consume the chunk).
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&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;sentence&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;sentences&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;sentence_tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;rough_token_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sentence&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# If adding this sentence would overflow the chunk, close the
&lt;/span&gt;        &lt;span class="c1"&gt;# current chunk out and start a new one, carrying over the last
&lt;/span&gt;        &lt;span class="c1"&gt;# `overlap_tokens` worth of sentences for continuity.
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;current_token_count&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;sentence_tokens&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;max_tokens_per_chunk&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;current_chunk_sentences&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;chunk_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_chunk_sentences&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;chunks&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;chunk_text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;token_count&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;current_token_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;char_start&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;char_cursor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;char_end&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;char_cursor&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk_text&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="n"&gt;char_cursor&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk_text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="c1"&gt;# Build the overlap: keep trailing sentences until we've got
&lt;/span&gt;            &lt;span class="c1"&gt;# roughly `overlap_tokens` worth, to seed the next chunk.
&lt;/span&gt;            &lt;span class="n"&gt;overlap_sentences&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;overlap_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;sent&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;reversed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_chunk_sentences&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;overlap_sentences&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;overlap_count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nf"&gt;rough_token_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sent&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;overlap_count&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;overlap_tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="k"&gt;break&lt;/span&gt;

            &lt;span class="n"&gt;current_chunk_sentences&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;overlap_sentences&lt;/span&gt;
            &lt;span class="n"&gt;current_token_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;overlap_count&lt;/span&gt;

        &lt;span class="n"&gt;current_chunk_sentences&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;sentence&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;current_token_count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;sentence_tokens&lt;/span&gt;

    &lt;span class="c1"&gt;# Flush the final chunk
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;current_chunk_sentences&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;chunk_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_chunk_sentences&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;chunks&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;chunk_text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;token_count&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;current_token_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;char_start&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;char_cursor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;char_end&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;char_cursor&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk_text&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;chunks&lt;/span&gt;


&lt;span class="c1"&gt;# --- Example ---------------------------------------------------------------
&lt;/span&gt;&lt;span class="n"&gt;sample_doc&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;Throughput degradation under sustained load is a common issue. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;It is typically caused by connection pool exhaustion. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Another frequent cause is unindexed database queries. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Monitoring dashboards should track p50, p95, and p99 latency. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Alerts should fire when p99 crosses a defined SLO threshold.&lt;/span&gt;&lt;span class="sh"&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;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;chunk_document&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sample_doc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_tokens_per_chunk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;overlap_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
    &lt;span class="nf"&gt;print&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;Chunk &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&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;p&gt;Each resulting chunk (a "passage" in the notes' schema) would then get an embedding computed (using one of the models the notes list — &lt;code&gt;microsoft/hamier&lt;/code&gt;, &lt;code&gt;Qwen3 (8B)&lt;/code&gt;, &lt;code&gt;BGE-M3&lt;/code&gt;, or an OpenAI/OSS embedding API), stored in Qdrant (the vector), Postgres (the metadata: doc_id, chunk_index, char_start, char_end, token_count), and ElasticSearch (the BM25 inverted index).&lt;/p&gt;

&lt;h3&gt;
  
  
  9.6 The write path, explained in words
&lt;/h3&gt;

&lt;p&gt;Flow: &lt;strong&gt;S3 (raw doc storage) → Chunk Ingestor → Hierarchical Chunking → Embeddings → Storage (Postgres, Qdrant, ElasticSearch, Redis).&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Step by step: a document lands in S3. A &lt;strong&gt;chunk ingestor&lt;/strong&gt; process picks it up (typically via an event notification or polling), performs three sub-steps the notes list explicitly (&lt;strong&gt;list files, download, read&lt;/strong&gt;), then runs &lt;strong&gt;hierarchical chunking&lt;/strong&gt; (Section 9.5's sentence-boundary + overlap logic) to split it into passages. Each passage is embedded, and the results are fanned out to storage: &lt;strong&gt;Postgres&lt;/strong&gt; gets the passage/doc metadata, &lt;strong&gt;Qdrant&lt;/strong&gt; gets the embedding vectors, and &lt;strong&gt;ElasticSearch&lt;/strong&gt; gets the text for its BM25 inverted index. This asynchronous pipeline is exactly why the system can tolerate the &lt;strong&gt;~15-minute eventual consistency SLO&lt;/strong&gt; mentioned earlier — the write path doesn't need to be instantaneous, it needs to be reliable and complete within a bounded, acceptable delay.&lt;/p&gt;

&lt;p&gt;The notes also flag a &lt;strong&gt;timebox&lt;/strong&gt; mechanism on the read side with two named fallback behaviors — &lt;strong&gt;cascade&lt;/strong&gt; and &lt;strong&gt;fallback&lt;/strong&gt; — meaning if some part of the read path takes too long (e.g., the cross-encoder rerank step or the LLM generation call, described as "the long pole" of end-to-end latency), the system has a time budget after which it cascades to a simpler/cheaper strategy or falls back to a safe default (like a refusal) rather than hanging indefinitely. This connects directly back to the &lt;strong&gt;query latency numbers&lt;/strong&gt; given in the notes: &lt;strong&gt;p50 = 1.5 sec, p99 = 5 sec&lt;/strong&gt;, with the &lt;strong&gt;LLM inference call&lt;/strong&gt; explicitly called out as the &lt;strong&gt;long pole&lt;/strong&gt; (the slowest, bottleneck stage) — largely because the very first token the user sees (their &lt;strong&gt;Time To First Token&lt;/strong&gt;, covered next in the Appendix) is gated on that call finishing its reasoning.&lt;/p&gt;




&lt;h2&gt;
  
  
  10. Appendix A: Streaming Tool Calls
&lt;/h2&gt;

&lt;h3&gt;
  
  
  10.1 The simple example first
&lt;/h3&gt;

&lt;p&gt;Think about the difference between getting a text message that appears &lt;strong&gt;all at once&lt;/strong&gt; after a long pause, versus watching someone type it live, word by word, right in front of you. Even though the &lt;em&gt;total&lt;/em&gt; message might take the same amount of time to fully arrive, watching it appear progressively &lt;em&gt;feels&lt;/em&gt; far less frustrating — you get immediate feedback that something is happening, instead of staring at a blank screen wondering if anything is working at all.&lt;/p&gt;

&lt;p&gt;That's the entire motivation for streaming: even if the total response time is identical, showing partial output as it's generated dramatically improves perceived responsiveness.&lt;/p&gt;

&lt;h3&gt;
  
  
  10.2 Technical explanation
&lt;/h3&gt;

&lt;p&gt;The most important metric here, per the notes: &lt;strong&gt;TTFT — Time To First Token&lt;/strong&gt; (a plain-English one-liner: &lt;em&gt;"how long the user waits before they see ANY output at all, even if the full response isn't done yet"&lt;/em&gt;). If a tool call takes real time to execute (a slow database query, a slow external API), and the user has to wait through the &lt;em&gt;entire&lt;/em&gt; tool call before seeing anything, that's flagged directly in the notes as &lt;strong&gt;bad UX&lt;/strong&gt; — "like everything else," slow, silent waiting erodes trust in the product.&lt;/p&gt;

&lt;p&gt;The tricky part specific to &lt;em&gt;streaming tool calls&lt;/em&gt;: &lt;strong&gt;"what if tool input arrives as partial JSON fragments?"&lt;/strong&gt; When a model streams its response token by token, and part of that response is a &lt;code&gt;tool_use&lt;/code&gt; block (e.g., building up &lt;code&gt;{"city": "Tok&lt;/code&gt; ... &lt;code&gt;yo"}&lt;/code&gt; character by character), you cannot execute the tool with a half-formed, syntactically-invalid JSON blob. The notes' answer: &lt;strong&gt;"wait until the entire input is available"&lt;/strong&gt; — i.e., &lt;strong&gt;keep buffering&lt;/strong&gt; (accumulating) the partial JSON text as it streams in, and only attempt to parse/execute the tool call once the model signals that this particular content block is fully complete.&lt;/p&gt;

&lt;p&gt;The notes include a cautionary, very concrete illustration of &lt;em&gt;why&lt;/em&gt; this matters: imagine the model is streaming a dangerous tool call like &lt;strong&gt;"delete that record..."&lt;/strong&gt; If your system tried to act on a half-baked, incomplete tool input (say, a delete call where the &lt;em&gt;id&lt;/em&gt; argument hasn't fully arrived yet), it could either error out unpredictably or — worse — &lt;strong&gt;"no input could be fired"&lt;/strong&gt; i.e., act on a malformed/empty argument. &lt;strong&gt;Half-baked input / tool calls with no input could be fired&lt;/strong&gt; is exactly the failure mode the buffering strategy exists to prevent.&lt;/p&gt;

&lt;h3&gt;
  
  
  10.3 Code: the notes' Anthropic SDK streaming example, cleaned up and explained line by line
&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;anthropic&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;anthropic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Anthropic&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# The tool definition is identical in shape to Section 1 -- streaming
# doesn't change WHAT a tool looks like, only HOW the response arrives.
&lt;/span&gt;&lt;span class="n"&gt;tools&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;name&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;get_weather&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;description&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;Get current weather for a location&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;input_schema&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;type&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;object&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;properties&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;location&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;type&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;string&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;unit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;type&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;string&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;enum&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;celsius&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;fahrenheit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;required&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;location&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}]&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;execute_tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tool_input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&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;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Stand-in for a real tool implementation (see Section 1 for a fuller mock).&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tool_name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;get_weather&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;location&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;tool_input&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;location&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;temp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;condition&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;sunny&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&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;unknown tool&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude-sonnet-4-6&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;messages&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;role&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;user&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;content&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;What is the weather in Tokyo?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

    &lt;span class="c1"&gt;# These two variables ACCUMULATE state across streamed events -- this IS
&lt;/span&gt;    &lt;span class="c1"&gt;# the "buffering" the notes insist on. We never act on partial state.
&lt;/span&gt;    &lt;span class="n"&gt;current_tool_input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;
    &lt;span class="n"&gt;current_tool_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# -------------------------------------------------------------
&lt;/span&gt;        &lt;span class="c1"&gt;# "content_block_start": a NEW block (either plain text or a
&lt;/span&gt;        &lt;span class="c1"&gt;# tool_use block) has begun streaming. If it's a tool_use block,
&lt;/span&gt;        &lt;span class="c1"&gt;# note its NAME immediately, but its INPUT starts empty --
&lt;/span&gt;        &lt;span class="c1"&gt;# arguments arrive incrementally via later delta events.
&lt;/span&gt;        &lt;span class="c1"&gt;# -------------------------------------------------------------
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content_block_start&lt;/span&gt;&lt;span class="sh"&gt;"&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;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content_block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool_use&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;current_tool_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content_block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
                &lt;span class="n"&gt;current_tool_input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;  &lt;span class="c1"&gt;# reset the buffer for this new block
&lt;/span&gt;
        &lt;span class="c1"&gt;# -------------------------------------------------------------
&lt;/span&gt;        &lt;span class="c1"&gt;# "content_block_delta": an incremental CHUNK of the current
&lt;/span&gt;        &lt;span class="c1"&gt;# block has arrived. For tool_use blocks, this is a fragment of
&lt;/span&gt;        &lt;span class="c1"&gt;# partial JSON (event.delta.type == "input_json_delta") -- we
&lt;/span&gt;        &lt;span class="c1"&gt;# must ACCUMULATE it, not parse it yet, since it's incomplete.
&lt;/span&gt;        &lt;span class="c1"&gt;# For plain text blocks, we can print immediately since partial
&lt;/span&gt;        &lt;span class="c1"&gt;# text is still readable (this is what gives the "word by word"
&lt;/span&gt;        &lt;span class="c1"&gt;# streaming feel for normal chat responses).
&lt;/span&gt;        &lt;span class="c1"&gt;# -------------------------------------------------------------
&lt;/span&gt;        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content_block_delta&lt;/span&gt;&lt;span class="sh"&gt;"&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;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input_json_delta&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;current_tool_input&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;partial_json&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;lt;- Accumulate
&lt;/span&gt;            &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text_delta&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delta&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="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# -------------------------------------------------------------
&lt;/span&gt;        &lt;span class="c1"&gt;# "content_block_stop": the CURRENT block (text or tool_use) has
&lt;/span&gt;        &lt;span class="c1"&gt;# finished streaming completely. If it was a tool_use block, the
&lt;/span&gt;        &lt;span class="c1"&gt;# buffered JSON string is now guaranteed complete and valid --
&lt;/span&gt;        &lt;span class="c1"&gt;# THIS is the safe moment to parse it and actually execute the
&lt;/span&gt;        &lt;span class="c1"&gt;# tool, never before.
&lt;/span&gt;        &lt;span class="c1"&gt;# -------------------------------------------------------------
&lt;/span&gt;        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content_block_stop&lt;/span&gt;&lt;span class="sh"&gt;"&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;current_tool_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;parsed_input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_tool_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;lt;- input complete
&lt;/span&gt;                &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;execute_tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_tool_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parsed_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;lt;- execute
&lt;/span&gt;                &lt;span class="nf"&gt;print&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="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;[Executed &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;current_tool_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; -&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;current_tool_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;  &lt;span class="c1"&gt;# reset for the next potential tool block
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The three event types to remember: &lt;strong&gt;&lt;code&gt;content_block_start&lt;/code&gt;&lt;/strong&gt; (a new block began — note its type/name), &lt;strong&gt;&lt;code&gt;content_block_delta&lt;/code&gt;&lt;/strong&gt; (a fragment arrived — accumulate if it's tool JSON, print immediately if it's plain text), and &lt;strong&gt;&lt;code&gt;content_block_stop&lt;/code&gt;&lt;/strong&gt; (the block is fully complete — &lt;em&gt;only now&lt;/em&gt; is it safe to parse and execute a tool call).&lt;/p&gt;




&lt;h2&gt;
  
  
  11. Appendix B: Evaluating RAG with RAGAS
&lt;/h2&gt;

&lt;h3&gt;
  
  
  11.1 The simple example first
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Context Precision, simple example:&lt;/strong&gt; think of a search-results page with 5 blue links. Some of them are genuinely useful and directly relevant to what you searched for (imagine green dots next to them); others are irrelevant filler or noise (red dots). A &lt;em&gt;good&lt;/em&gt; search engine doesn't just include some relevant results somewhere on the page — it puts them &lt;strong&gt;near the top&lt;/strong&gt;. A search engine that buries the one truly relevant result at position #5, behind four irrelevant ones, is worse than one that puts it at position #1, even if both technically "found" it somewhere on the page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Faithfulness, simple example:&lt;/strong&gt; imagine grading a student's essay by checking, sentence by sentence, whether each individual claim they made is actually backed up by the assigned textbook. If the student writes five sentences and four of them are directly supported by the textbook but the fifth one is something they simply made up (that isn't in the textbook at all), you wouldn't give them full credit — you'd score the essay based on the &lt;em&gt;fraction&lt;/em&gt; of sentences that are actually grounded in the source material. That's exactly what "faithfulness" measures for an LLM's generated answer.&lt;/p&gt;

&lt;h3&gt;
  
  
  11.2 Technical explanation
&lt;/h3&gt;

&lt;p&gt;The motivation, per the notes: &lt;strong&gt;naive RAG evaluation is just qualitative — "does it seem right?" — and manual feedback loops are slow.&lt;/strong&gt; That doesn't scale, and it's subjective. &lt;strong&gt;RAGAS&lt;/strong&gt; (a plain-English one-liner: &lt;em&gt;"a framework and library for evaluating RAG pipelines using automated, LLM-assisted metrics instead of manual human judgment"&lt;/em&gt;) fixes two specific pain points named in the notes: &lt;strong&gt;no need for human-annotated ground truth&lt;/strong&gt;, and it lets you &lt;strong&gt;verify that changes/optimizations to your pipeline don't quietly downgrade quality&lt;/strong&gt; (a regression-testing mindset applied to RAG quality, not just code correctness).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Context Precision&lt;/strong&gt; — measures the &lt;strong&gt;retriever's&lt;/strong&gt; (or reranker's) &lt;strong&gt;ability to rank relevant chunks higher&lt;/strong&gt;, and it directly &lt;strong&gt;penalizes systems that bury relevant content below noise&lt;/strong&gt; — this is precisely the &lt;strong&gt;"lost in the middle" problem&lt;/strong&gt; flagged back in Section 6. The method: use an LLM to label each retrieved chunk as &lt;strong&gt;RELEVANT&lt;/strong&gt; or &lt;strong&gt;NOT relevant&lt;/strong&gt;, and then, for every position &lt;code&gt;k&lt;/code&gt; in the ranked list, compute &lt;strong&gt;precision@k&lt;/strong&gt; — the fraction of the &lt;em&gt;top k&lt;/em&gt; results that are relevant.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The notes' worked example&lt;/strong&gt;, using a 5-result list marked (green = relevant, red = not relevant): &lt;code&gt;🟢 🔴 🟢 🔴 🔴&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;p@1 = 1&lt;/strong&gt; — the top 1 result: 1 out of 1 is relevant → 1/1 = 1.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;p@2 = 0&lt;/strong&gt; — wait, this looks odd at first: the notes explicitly write &lt;code&gt;p@2 = 0&lt;/code&gt;. Reading the notes' own math carefully: &lt;strong&gt;precision@k is only counted (included in the final average) at positions where the k-th result itself is relevant&lt;/strong&gt; — i.e., you only "credit" a position if the &lt;em&gt;newly added&lt;/em&gt; result at that rank is itself a hit, otherwise that position contributes 0 to the running average (this is the standard "average precision" style computation, where precision at non-relevant ranks doesn't get counted toward the final score).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;p@3 = 2/3&lt;/strong&gt; — of the top 3 results, 2 are relevant (the 1st and 3rd) → 2/3 ≈ 0.67.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;p@4 = 0&lt;/strong&gt;, &lt;strong&gt;p@5 = 0&lt;/strong&gt; — same logic: the 4th and 5th results are themselves not relevant, so they don't contribute.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Final Context Precision formula (from the notes' own arithmetic):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Context Precision = (p@1 + p@3) / (# relevant chunks)
                   = (1 + 0.67) / 2
                   = 1.67 / 2
                   = 0.835
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In words: you sum up the precision@k values &lt;em&gt;at each rank where a relevant chunk appears&lt;/em&gt;, then divide by the &lt;strong&gt;total number of relevant chunks&lt;/strong&gt; in the list (here, 2 relevant chunks out of 5 total). This rewards systems that put their relevant chunks &lt;em&gt;early&lt;/em&gt; — a relevant chunk found at rank 1 contributes a full 1.0, while one found deeper in the list contributes a smaller fraction, exactly reflecting the "don't bury the good stuff" intuition from the simple example.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Faithfulness Score&lt;/strong&gt; — measures &lt;strong&gt;factual consistency of the generated answer&lt;/strong&gt;, and is explicitly called a &lt;strong&gt;"measure of hallucination"&lt;/strong&gt; in the notes. Method: &lt;strong&gt;break the answer into atomic claims&lt;/strong&gt; (using an LLM — the same "claim extraction" step from the read-path pseudocode in Section 9.4), then for each claim, score it &lt;strong&gt;1 if the retrieved context supports the claim&lt;/strong&gt;, or &lt;strong&gt;0 if the context does not support the claim&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Faithfulness formula:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;faithfulness = (# claims supported) / (# claims)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the exact same underlying idea as the &lt;code&gt;nli_entailed()&lt;/code&gt; check baked into the "zero hallucinations" &lt;code&gt;respond()&lt;/code&gt; function from Section 9 — faithfulness is essentially the &lt;em&gt;evaluation-time metric version&lt;/em&gt; of the same &lt;em&gt;runtime&lt;/em&gt; guardrail.&lt;/p&gt;

&lt;h3&gt;
  
  
  11.3 Code: Context Precision and Faithfulness, computed from scratch
&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;# =============================================================================
# CONTEXT PRECISION -- from a list of relevant/irrelevant flags
# (the "green dot / red dot" example from the notes).
# =============================================================================
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;context_precision&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;relevance_flags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;bool&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;
    relevance_flags: e.g. [True, False, True, False, False]
                      -- True = relevant chunk, False = not relevant,
                      in the ORDER they were retrieved/ranked (best first).

    Returns the RAGAS-style Context Precision score.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;total_relevant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;relevance_flags&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;total_relevant&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;  &lt;span class="c1"&gt;# no relevant chunks at all -- precision is undefined/zero
&lt;/span&gt;
    &lt;span class="n"&gt;running_relevant_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;precision_sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;is_relevant&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;relevance_flags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&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;is_relevant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;running_relevant_count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="c1"&gt;# precision@k = (# relevant chunks in top k) / k
&lt;/span&gt;            &lt;span class="n"&gt;precision_at_k&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;running_relevant_count&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;
            &lt;span class="n"&gt;precision_sum&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;precision_at_k&lt;/span&gt;
        &lt;span class="c1"&gt;# if NOT relevant, this rank contributes 0 and is skipped, matching
&lt;/span&gt;        &lt;span class="c1"&gt;# the notes' worked example where p@2, p@4, p@5 = 0
&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;precision_sum&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;total_relevant&lt;/span&gt;


&lt;span class="c1"&gt;# --- Example matching the notes exactly: 🟢 🔴 🟢 🔴 🔴 ----------------------
&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;context_precision&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&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;Context Precision: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# -&amp;gt; should print 0.835
&lt;/span&gt;

&lt;span class="c1"&gt;# =============================================================================
# FAITHFULNESS -- given a list of claims and whether each is supported.
# =============================================================================
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;faithfulness_score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;claim_support_labels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;bool&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;
    claim_support_labels: e.g. [True, True, True, False]
                           -- True = context supports this atomic claim,
                           False = context does NOT support it (hallucinated).
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;claim_support_labels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;  &lt;span class="c1"&gt;# no claims made -- vacuously faithful (nothing to check)
&lt;/span&gt;
    &lt;span class="n"&gt;supported&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;claim_support_labels&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;claim_support_labels&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;supported&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;


&lt;span class="c1"&gt;# --- Example: an answer with 4 atomic claims, 1 of which is unsupported ----
&lt;/span&gt;&lt;span class="n"&gt;claims_supported&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;faith_score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;faithfulness_score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;claims_supported&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&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;Faithfulness: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;faith_score&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# -&amp;gt; 0.75
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  11.4 Code: using the real RAGAS library
&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;# pip install ragas datasets --break-system-packages
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;ragas&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;evaluate&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;ragas.metrics&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;context_precision&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;faithfulness&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datasets&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Dataset&lt;/span&gt;

&lt;span class="c1"&gt;# RAGAS expects a dataset shaped like this: one row per question, with the
# retrieved contexts and the generated answer already filled in.
&lt;/span&gt;&lt;span class="n"&gt;eval_dataset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_dict&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;question&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;Why is my API returning a 500 error under high load?&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;answer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;500 errors under high load are typically caused by connection &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
               &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pool exhaustion or unhandled exceptions from resource contention.&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;contexts&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;Throughput degradation under sustained load can trigger unhandled &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;exceptions, which surface to callers as HTTP 500 errors.&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;Connection pool exhaustion is a common cause of request failures &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;under high concurrency.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]],&lt;/span&gt;
    &lt;span class="c1"&gt;# ground_truth is optional for some metrics but required for others (e.g. recall)
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ground_truth&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;500 errors under load are usually caused by connection &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pool exhaustion or unhandled exceptions.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eval_dataset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;context_precision&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;faithfulness&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# This runs the SAME conceptual computation as the from-scratch functions
# above, but uses an LLM internally to (a) judge chunk relevance for
# context_precision, and (b) extract + verify atomic claims for faithfulness.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  12. Glossary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tool use / function calling&lt;/strong&gt; — letting an LLM request that your application run a specific, predefined function on its behalf, using arguments the model constructs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tool schema&lt;/strong&gt; — the JSON description (name, description, parameters) that tells the model what a tool does and how to call it; the model never sees or runs the actual code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enum&lt;/strong&gt; — a parameter constrained to a fixed list of valid values, so a model can't invent arbitrary strings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RBAC (Role-Based Access Control)&lt;/strong&gt; — restricting what a user can see or do based on their assigned role (e.g., department, seniority).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dense retrieval&lt;/strong&gt; — semantic search using embeddings; finds documents with similar &lt;em&gt;meaning&lt;/em&gt;, even without shared words.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sparse retrieval / BM25&lt;/strong&gt; — traditional keyword-based search; scores documents by exact term overlap, weighted by term rarity/frequency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hybrid search&lt;/strong&gt; — running dense and sparse retrieval together and merging the results, to cover both of their blind spots.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RRF (Reciprocal Rank Fusion)&lt;/strong&gt; — a method for merging multiple ranked lists into one, based only on each item's &lt;em&gt;rank position&lt;/em&gt; in each list, not its raw score.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metadata filtering&lt;/strong&gt; — applying hard constraints (date, department, tags) before or after a search, on top of relevance ranking.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pre-filtering&lt;/strong&gt; — applying metadata constraints &lt;em&gt;inside&lt;/em&gt; the search query itself, so the database never considers disallowed documents.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Post-filtering&lt;/strong&gt; — running an unfiltered search first, then discarding invalid results afterward in application code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bi-encoder&lt;/strong&gt; — embeds the query and each document &lt;em&gt;independently&lt;/em&gt;, then compares them via vector similarity; fast and scalable but can't compare them in context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-encoder&lt;/strong&gt; — feeds the query and a single document together as one input, producing one precise relevance score; accurate but slow and non-scalable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Lost in the middle" problem&lt;/strong&gt; — the tendency for information in the middle of a long context to receive less effective attention than information at the start or end.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HyDE (Hypothetical Document Embedding)&lt;/strong&gt; — generating a fake, ideal-sounding answer to a query first, then using &lt;em&gt;that&lt;/em&gt; text's embedding for retrieval, to bridge a vocabulary gap between casual queries and formal documents.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Semantic caching&lt;/strong&gt; — caching LLM responses keyed by &lt;em&gt;meaning&lt;/em&gt; (embedding similarity) rather than exact text match, so paraphrased questions can still hit the cache.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NLI (Natural Language Inference)&lt;/strong&gt; — a model task/technique for deciding whether one piece of text logically follows from (entails), contradicts, or is unrelated to another.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HNSW (Hierarchical Navigable Small World)&lt;/strong&gt; — a graph-based index structure used by vector databases to find approximately-nearest vectors quickly without comparing against every single one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TTFT (Time To First Token)&lt;/strong&gt; — how long a user waits before seeing any output at all, even if the full response is still being generated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RAGAS&lt;/strong&gt; — a framework/library for evaluating RAG pipelines with automated, LLM-assisted metrics instead of manual human review.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context Precision&lt;/strong&gt; — a RAGAS metric measuring whether relevant retrieved chunks are ranked near the top rather than buried lower down.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Faithfulness&lt;/strong&gt; — a RAGAS metric measuring what fraction of an LLM's generated claims are actually supported by the retrieved context (a direct measure of hallucination).&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  13. "Why This Matters in Production" — One-Liner Per Section
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Tool Use Fundamentals&lt;/strong&gt; — without a disciplined intercept/execute/return loop, you either let a model hallucinate data it can't actually access, or you build a system that silently breaks the moment a tool call needs real-world side effects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tool Schema Design&lt;/strong&gt; — a vague schema is invisible in your own testing and only breaks in production, when real users ask messy questions your clean test prompts never covered.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parallel Tool Calls and Failure Handling&lt;/strong&gt; — sequential tool calls make your product feel painfully slow at scale, and having no explicit failure strategy means one flaky dependency can silently corrupt or crash every response.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hybrid Search&lt;/strong&gt; — relying on dense-only or sparse-only retrieval guarantees you'll systematically miss either exact-term queries or natural-language queries, and users will experience this as "the search is broken" without knowing why.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metadata Filtering&lt;/strong&gt; — get pre- vs post-filtering wrong and you either waste retrieval budget or, worse, leak data across security boundaries like RBAC in enterprise settings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reranking (Cross-Encoders vs Bi-Encoders)&lt;/strong&gt; — skipping reranking means your final answer is only as good as noisy first-pass retrieval; running cross-encoders on your whole corpus means your product times out.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Query Rewriting with HyDE&lt;/strong&gt; — without bridging the query-document vocabulary gap, your retrieval will keep failing on exactly the domains (legal, scientific, internal jargon) where correctness matters most.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Semantic Caching&lt;/strong&gt; — every uncached LLM call costs real latency and real money; miscalibrating the threshold either serves wrong answers or barely saves anything at all.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full RAG System Design&lt;/strong&gt; — at 10M-document scale, "correctness" has to be engineered in at every stage (retrieval, reranking, generation, fact-checking), because a single loose stage can let an unsupported claim reach the user.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streaming Tool Calls&lt;/strong&gt; — users abandon or distrust products that feel frozen; but streaming without proper buffering risks executing dangerous operations (like deletes) on incomplete input.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evaluating RAG with RAGAS&lt;/strong&gt; — without automated, repeatable metrics, you can't tell whether a "quality improvement" you shipped actually helped or quietly made hallucinations worse.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>beginners</category>
      <category>llm</category>
      <category>rag</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Your Email List Is Decaying Right Now</title>
      <dc:creator>MailValid.io</dc:creator>
      <pubDate>Sun, 12 Jul 2026 18:11:13 +0000</pubDate>
      <link>https://dev.to/mailvalid/your-email-list-is-decaying-right-now-2cph</link>
      <guid>https://dev.to/mailvalid/your-email-list-is-decaying-right-now-2cph</guid>
      <description>&lt;p&gt;Verifying an email address at signup only proves it was valid at that moment. It says nothing about whether it's still valid six months later and for most contact databases, a meaningful share of it won't be. If your app verifies once at signup and never again, you're shipping a slow leak, not a fix.&lt;/p&gt;

&lt;p&gt;This post covers why lists decay even without a single unsubscribe, what a re-verification job should actually check, and a basic cron-based implementation for keeping a contacts table clean over time.&lt;/p&gt;

&lt;h2&gt;Why valid-at-signup doesn't mean valid-forever&lt;/h2&gt;

&lt;p&gt;Email addresses have a shelf life, and &lt;a href="https://mailvalid.io/" rel="noopener noreferrer"&gt;email verification&lt;/a&gt; once at collection time only confirms the address was live on that specific day. Roughly 22.5% of email contacts become invalid over the course of a year — around 2.1% per month: driven by job changes, account deactivation, provider migrations, and abandoned inboxes. B2B contact lists decay faster than consumer lists, commonly in the 25–30% annual range, largely because employees change jobs and their work addresses get deactivated almost immediately.&lt;/p&gt;

&lt;p&gt;If your database has contacts older than a few months and you've never re-verified, you're very likely sending to a nontrivial number of dead addresses without knowing it.&lt;/p&gt;

&lt;h2&gt;What causes Email list decay&lt;/h2&gt;

&lt;p&gt;Email list decay is driven by five recurring patterns, and only one of them is under your control.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Job changes — the single biggest driver for B2B lists; an estimated 70% of B2B job-related email addresses change within 12 months of collection.&lt;/li&gt;
&lt;li&gt;Company changes — mergers, acquisitions, rebrands, or shutdowns can invalidate an entire domain's worth of addresses at once.&lt;/li&gt;
&lt;li&gt;Provider migrations — a contact moves from one email provider to another and abandons the old address.&lt;/li&gt;
&lt;li&gt;Account deactivation — mailbox providers periodically purge inactive accounts per their own retention policies.&lt;/li&gt;
&lt;li&gt;Signup typos — the one decay source that real-time syntax/MX verification at signup actually prevents.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first four happen entirely outside your app, on a timeline you don't control, which is exactly why a one-time verification at signup can't be the whole strategy.&lt;/p&gt;

&lt;h2&gt;Email Spam traps: the risk re-verification catches that bounces don't&lt;/h2&gt;

&lt;p&gt;A recycled spam trap is a real address that mailbox providers have reclaimed after roughly 12 months of inactivity, then repurposed specifically to catch senders with stale lists. This is the mechanism that makes re-verification more than a bounce-rate optimization — it's a reputation risk mitigation.&lt;/p&gt;

&lt;p&gt;There are three recognized spam trap types:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Typo traps - created from signup typos (&lt;a href="mailto:user@yahooo.com"&gt;user@yahooo.com&lt;/a&gt;), catching senders who never validated syntax carefully.&lt;/li&gt;
&lt;li&gt;Recycled traps - formerly legitimate addresses that went dormant for a year or more and were reclaimed by the provider as a monitoring mechanism.&lt;/li&gt;
&lt;li&gt;Pristine traps - addresses that were never real signups at all, created solely to catch senders using purchased or scraped lists.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Recycled traps are the one your own signup-time verification can't catch, because the address was real and valid when the person signed up. It only becomes a trap after months of dormancy — which is precisely the gap a scheduled re-verification job is designed to close.&lt;/p&gt;

&lt;h2&gt;Designing the re-verification job&lt;/h2&gt;

&lt;p&gt;A re-verification job needs three things a one-off signup check doesn't:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A cadence - how often each contact gets re-checked (see the how-often section)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Prioritization - verifying your entire table every night at scale is wasteful; segment by last-engagement date or last-verified date and check the stalest/riskiest contacts first&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A status write-back, not a delete — store the verification result (valid, invalid, catch-all, disposable, unknown) and last-checked timestamp rather than silently dropping rows, so your sending logic can make the suppress/send decision explicitly&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;A scheduled job example (Node.js + cron)&lt;/h3&gt;

&lt;p&gt;javascriptconst cron = require('node-cron');&lt;br&gt;
const db = require('./db');&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Runs nightly at 2am — pulls contacts not verified in 90+ days,&lt;/span&gt;
&lt;span class="c1"&gt;// oldest-first, capped at a batch size to respect rate limits.&lt;/span&gt;
&lt;span class="nx"&gt;cron&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;schedule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0 2 * * *&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;staleContacts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
    SELECT id, email FROM contacts
    WHERE last_verified_at &amp;lt; NOW() - INTERVAL '90 days'
       OR last_verified_at IS NULL
    ORDER BY last_verified_at ASC NULLS FIRST
    LIMIT 5000
  `&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;contact&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;staleContacts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;verifyEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contact&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// your verification call&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
      UPDATE contacts
      SET verification_status = $1, last_verified_at = NOW()
      WHERE id = $2
    `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;contact&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;invalid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`UPDATE contacts SET suppressed = true WHERE id = $1`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;contact&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The batch size and interval here are illustrative, tune them to your sending volume and your verification provider's rate limits. The important structural choice is the last_verified_at column: it turns re-verification into an ongoing, queryable process instead of a one-time event.&lt;/p&gt;

&lt;h2&gt;How often should you re-verify?&lt;/h2&gt;

&lt;p&gt;There's no universal answer, but the decay data gives a reasonable default: at roughly 2% monthly decay, a 90-day re-verification cycle catches most staleness before it compounds into a meaningful bounce or spam-trap risk, without re-checking addresses that haven't had time to go stale. High-value B2B lists with faster job-change turnover may justify a 30–60 day cycle; low-churn consumer lists can often stretch to 6 months.&lt;/p&gt;

&lt;p&gt;If you're re-verifying manually or building this in-house, an &lt;a href="https://mailvalid.io/docs#bulk-verification" rel="noopener noreferrer"&gt;email verification API&lt;/a&gt; that returns a distinct catch-all status (rather than lumping it in with valid) matters more here than at signup time, a domain's catch-all configuration can change between checks, and treating it as a hard valid inflates your list-quality metrics without actually reducing risk. MailValid supports exactly this: bulk re-verification with status codes designed for exactly this kind of scheduled job, plus a real-time API for the signup-time check.&lt;/p&gt;

&lt;h3&gt;Frequently Asked Questions&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Does re-verification replace signup-time verification?&lt;/strong&gt;&lt;br&gt;
No, they catch different things. Signup-time verification (syntax + MX + SMTP) prevents typo'd and fake addresses from entering your database at all. Re-verification catches addresses that were genuinely valid at signup but have since gone stale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Will re-verification hurt my sender reputation the way sending mail does?&lt;/strong&gt;&lt;br&gt;
No. SMTP-level verification checks mailbox existence via a RCPT TO command without completing delivery (no DATA command is sent), so it doesn't count as a send and doesn't itself generate spam complaints, provided it's run through a provider with dedicated verification IP infrastructure rather than your own sending domain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Should I delete contacts that fail re-verification?&lt;/strong&gt;&lt;br&gt;
Suppressing them from sends is safer than deleting outright, a suppressed contact might resubscribe, correct a domain-level issue, or represent a data point you still want for reporting. Deletion is a business decision, not a deliverability requirement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if a contact I haven't emailed in a year suddenly becomes a spam trap?&lt;/strong&gt;&lt;br&gt;
This is exactly the recycled-trap scenario. If your last engagement with an address predates your last verification by many months, treat it as higher risk and prioritize it in your re-verification queue rather than assuming dormancy is harmless.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>database</category>
      <category>api</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Web Scraping with Playwright: Handle Any Website</title>
      <dc:creator>qing</dc:creator>
      <pubDate>Sun, 12 Jul 2026 18:08:28 +0000</pubDate>
      <link>https://dev.to/qingluan/web-scraping-with-playwright-handle-any-website-28ea</link>
      <guid>https://dev.to/qingluan/web-scraping-with-playwright-handle-any-website-28ea</guid>
      <description>&lt;h1&gt;
  
  
  Web Scraping with Playwright: Handle Any Website
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Web Scraping with Playwright: Handle Any Website
&lt;/h2&gt;

&lt;p&gt;Imagine having the ability to extract data from any website, no matter how complex or dynamic the content. This is where web scraping comes in – a powerful technique that allows us to automate the collection of data from websites. However, traditional web scraping methods often struggle with modern websites that employ anti-scraping measures, making it difficult to extract the data we need.&lt;/p&gt;

&lt;p&gt;This is where Playwright comes in – a revolutionary tool that has changed the game when it comes to web scraping. Playwright is a Node.js library developed by Microsoft, which provides a high-level API for automating web browsers. In this article, we'll explore how to use Playwright for web scraping, and demonstrate its capabilities with a practical example in Python.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up Playwright
&lt;/h2&gt;

&lt;p&gt;Before we dive into the code, let's first set up Playwright. If you're using Python, you can install Playwright using pip:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;playwright
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll also need to install the &lt;code&gt;wpt&lt;/code&gt; package, which is used to drive the browser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;wpt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once installed, you can verify that Playwright is working by running the following code:&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;playwright.sync_api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sync_playwright&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;sync_playwright&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_page&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.example.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code launches a new Chromium browser instance, navigates to the specified URL, and then closes the browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling Dynamic Content with Playwright
&lt;/h2&gt;

&lt;p&gt;One of the biggest challenges when it comes to web scraping is handling dynamic content. This can include elements that are loaded after the initial page load, or content that is generated on the fly using JavaScript. Playwright makes it easy to handle these types of elements by providing a powerful API for interacting with web pages.&lt;/p&gt;

&lt;p&gt;Let's take a look at an example code snippet that demonstrates how to handle dynamic content with Playwright:&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;playwright.sync_api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sync_playwright&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;sync_playwright&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_page&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.example.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Wait for the page to load
&lt;/span&gt;    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait_for_load_state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;networkidle0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Get all the elements with the class "dynamic-element"
&lt;/span&gt;    &lt;span class="n"&gt;elements&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query_selector_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.dynamic-element&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Iterate over the elements and print their text content
&lt;/span&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text_content&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

    &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this code snippet, we use the &lt;code&gt;wait_for_load_state&lt;/code&gt; method to wait for the page to load, and then use the &lt;code&gt;query_selector_all&lt;/code&gt; method to get all elements with the class "dynamic-element". We then iterate over the elements and print their text content.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling Anti-Scraping Measures
&lt;/h2&gt;

&lt;p&gt;Modern websites often employ anti-scraping measures to prevent web scraping. These can include CAPTCHAs, rate limiting, and other techniques designed to make it difficult for bots to access the website. Playwright provides a number of features that make it easy to handle these types of anti-scraping measures.&lt;/p&gt;

&lt;p&gt;One of the most powerful features of Playwright is its ability to solve CAPTCHAs using the Google CAPTCHA API. To use this feature, you'll need to install the &lt;code&gt;google-captcha&lt;/code&gt; package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;google-captcha
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can then use the following code snippet to solve a CAPTCHA:&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;googlecaptcha&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;GoogleCaptcha&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;playwright.sync_api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sync_playwright&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;sync_playwright&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_page&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.example.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Get the CAPTCHA image
&lt;/span&gt;    &lt;span class="n"&gt;captcha_image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query_selector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.captcha-image&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;get_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;src&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Solve the CAPTCHA
&lt;/span&gt;    &lt;span class="n"&gt;captcha_solver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GoogleCaptcha&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;captcha_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;captcha_solver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;solve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;captcha_image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Enter the CAPTCHA token on the page
&lt;/span&gt;    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;captcha-field&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;captcha_token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Handling Rate Limiting
&lt;/h2&gt;

&lt;p&gt;Rate limiting is another common anti-scraping measure that can make it difficult to access a website. Playwright provides a number of features that make it easy to handle rate limiting.&lt;/p&gt;

&lt;p&gt;One of the most powerful features of Playwright is its ability to simulate a human user. By default, Playwright simulates a human user by introducing delays between requests. This makes it difficult for the website to detect that you're a bot.&lt;/p&gt;

&lt;p&gt;However, if you need to make a large number of requests to a website that employs rate limiting, you may need to take additional steps to simulate a human user. One way to do this is to use a rotating proxy, which can help to distribute your requests across multiple IP addresses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, we've explored how to use Playwright for web scraping. We've demonstrated its capabilities with a practical example in Python, and shown how to handle dynamic content and anti-scraping measures.&lt;/p&gt;

&lt;p&gt;Whether you're a seasoned web scraper or just starting out, Playwright is a powerful tool that can help you extract data from any website. With its powerful API and features like CAPTCHA solving and rotating proxies, Playwright makes it easy to handle even the most complex websites.&lt;/p&gt;

&lt;p&gt;So why not give Playwright a try today? With its ease of use and powerful features, it's the perfect tool for any web scraper.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;💡 Related: **Content Creator Ultimate Bundle (Save 33%)&lt;/em&gt;* — $29.99*&lt;/p&gt;

</description>
      <category>python</category>
      <category>playwright</category>
      <category>webscraping</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>إعداد بيئة تطوير آمنة باستخدام نظام لينكس واداة الدッカー لتشغيل تطبيقات الويب بشكل متوازن وآمن</title>
      <dc:creator>Issam Hilmi</dc:creator>
      <pubDate>Sun, 12 Jul 2026 18:05:20 +0000</pubDate>
      <link>https://dev.to/issam_hilmi_d7d61819b5fbf/dd-byy-ttwyr-amn-bstkhdm-nzm-lynks-wd-ldtuka-ltshgyl-ttbyqt-lwyb-bshkl-mtwzn-wamn-2dhk</link>
      <guid>https://dev.to/issam_hilmi_d7d61819b5fbf/dd-byy-ttwyr-amn-bstkhdm-nzm-lynks-wd-ldtuka-ltshgyl-ttbyqt-lwyb-bshkl-mtwzn-wamn-2dhk</guid>
      <description>&lt;p&gt;إعداد بيئة تطوير آمنة باستخدام نظام لينكس واداة الدッカー&lt;br&gt;
   إعداد بيئة تطوير آمنة باستخدام نظام لينكس واداة الدッカー لتشغيل تطبيقات الويب بشكل متوازن وآمن هو موضوع هام في عالم التطوير البرمجي، حيث أن الأمان يعد العامل الأكثر أهمية في بناء التطبيقات.&lt;br&gt;
   لماذا نظام لينكس؟&lt;br&gt;
   نظام لينكس هو نظام تشغيل مفتوح المصدر ويُعتبر أكثر الأنظمة أمانًا، كما أنه يدعم العديد من الأدوات التي تساعد في بناء بيئة تطوي&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://autocarauto4.blogspot.com/2026/07/blog-post_327.html" rel="noopener noreferrer"&gt;my blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This article was originally published on &lt;a href="https://autocarauto4.blogspot.com/2026/07/blog-post_327.html" rel="noopener noreferrer"&gt;my blog&lt;/a&gt; and is republished here with permission.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>programming</category>
      <category>tutorial</category>
      <category>tech</category>
    </item>
    <item>
      <title>Nginx + Python: The Perfect Production Setup</title>
      <dc:creator>qing</dc:creator>
      <pubDate>Sun, 12 Jul 2026 18:05:17 +0000</pubDate>
      <link>https://dev.to/qingluan/nginx-python-the-perfect-production-setup-12f4</link>
      <guid>https://dev.to/qingluan/nginx-python-the-perfect-production-setup-12f4</guid>
      <description>&lt;h1&gt;
  
  
  Nginx + Python: The Perfect Production Setup
&lt;/h1&gt;

&lt;h3&gt;
  
  
  Why You Need Nginx and Python in Your Production Setup
&lt;/h3&gt;

&lt;p&gt;As a developer, there are few things more satisfying than deploying a beautiful Python application to production. But let's be real – getting it to production is only half the battle. The real challenge begins when you need to ensure your application is secure, scalable, and performs well under load.&lt;/p&gt;

&lt;p&gt;That's where Nginx comes in – the lightweight, powerful web server that's been a staple of production environments for years. And when paired with Python, the results are nothing short of magic.&lt;/p&gt;

&lt;p&gt;In this post, we'll explore the perfect production setup for your Python application: Nginx + Python. We'll cover the benefits of using Nginx, how to set it up with Python, and provide a working example to get you started.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Nginx?
&lt;/h3&gt;

&lt;p&gt;So, why do you need Nginx in your production setup? Here are just a few reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;High-performance&lt;/strong&gt;: Nginx is known for its lightning-fast performance, even under heavy load. This makes it the perfect choice for applications that require a high level of concurrency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lightweight&lt;/strong&gt;: Unlike other web servers like Apache, Nginx is incredibly lightweight, making it easy to deploy and manage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt;: Nginx includes built-in security features like SSL/TLS support, HTTP/2 support, and WAF (Web Application Firewall) capabilities.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexibility&lt;/strong&gt;: Nginx can be used as a reverse proxy, load balancer, and caching layer, making it a versatile addition to your production setup.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Setting Up Nginx with Python
&lt;/h3&gt;

&lt;p&gt;So, how do you set up Nginx with Python? Here's a step-by-step guide:&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 1: Install Nginx and Python
&lt;/h4&gt;

&lt;p&gt;First, you'll need to install Nginx and Python on your server. You can do this using your distribution's package manager or by compiling from source.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;nginx python3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Step 2: Create a New Virtual Host
&lt;/h4&gt;

&lt;p&gt;Next, create a new virtual host for your Python application. You can do this by creating a new file in the &lt;code&gt;/etc/nginx/sites-available&lt;/code&gt; directory, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/nginx/sites-available/python_app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Step 3: Configure Nginx
&lt;/h4&gt;

&lt;p&gt;In the new file, add the following configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;yourdomain.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://localhost:5000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Real-IP&lt;/span&gt; &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;yourdomain.com&lt;/code&gt; with your actual domain name, and &lt;code&gt;5000&lt;/code&gt; with the port number you're using for your Python application.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 4: Activate the New Virtual Host
&lt;/h4&gt;

&lt;p&gt;Next, create a symbolic link to the new virtual host file in the &lt;code&gt;/etc/nginx/sites-enabled&lt;/code&gt; directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo ln&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; /etc/nginx/sites-available/python_app /etc/nginx/sites-enabled/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Step 5: Reload Nginx
&lt;/h4&gt;

&lt;p&gt;Finally, reload Nginx to apply the changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;service nginx reload
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Working Example: A Simple Flask Application
&lt;/h3&gt;

&lt;p&gt;Now that we've set up Nginx with Python, let's create a simple Flask application to test it out. Here's an example:&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jsonify&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/data&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&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;GET&lt;/span&gt;&lt;span class="sh"&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;get_data&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;data&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;Hello, World!&lt;/span&gt;&lt;span class="sh"&gt;'&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;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save this code to a file called &lt;code&gt;app.py&lt;/code&gt; and run it using &lt;code&gt;python3 app.py&lt;/code&gt;. Then, open your web browser and navigate to &lt;code&gt;http://yourdomain.com/data&lt;/code&gt;. You should see the message "Hello, World!".&lt;/p&gt;

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

&lt;p&gt;In this post, we explored the perfect production setup for your Python application: Nginx + Python. We covered the benefits of using Nginx, how to set it up with Python, and provided a working example to get you started.&lt;/p&gt;

&lt;p&gt;Whether you're building a high-traffic web application or a simple REST API, Nginx + Python is the perfect combination for any production environment. So why wait? Get started today and experience the power of Nginx + Python for yourself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Call to Action
&lt;/h3&gt;

&lt;p&gt;Ready to take your Python application to the next level? Try out Nginx + Python today and see the difference for yourself. Share your experiences and tips in the comments below, and don't forget to follow us for more technical blog posts and tutorials!&lt;/p&gt;

</description>
      <category>nginx</category>
      <category>python</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Build a Real-Time Dashboard with Python and WebSockets</title>
      <dc:creator>qing</dc:creator>
      <pubDate>Sun, 12 Jul 2026 18:04:24 +0000</pubDate>
      <link>https://dev.to/qingluan/build-a-real-time-dashboard-with-python-and-websockets-g0j</link>
      <guid>https://dev.to/qingluan/build-a-real-time-dashboard-with-python-and-websockets-g0j</guid>
      <description>&lt;h1&gt;
  
  
  Build a Real-Time Dashboard with Python and WebSockets
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Building a Real-Time Dashboard with Python and WebSockets
&lt;/h2&gt;

&lt;p&gt;Imagine having a dashboard that updates in real-time, reflecting the current state of your system or application. No more refreshing the page or waiting for delayed updates - your dashboard is always up-to-date, providing you with instant insights into what's happening. This is the power of real-time data, and it's easier to achieve than you think.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are WebSockets?
&lt;/h2&gt;

&lt;p&gt;To understand how we'll build a real-time dashboard, we need to talk about WebSockets. WebSockets are a bi-directional communication protocol that allows your web application to send and receive data in real-time. Unlike traditional HTTP requests, WebSockets establish a persistent connection between the client and server, enabling efficient, low-latency communication.&lt;/p&gt;

&lt;p&gt;Here's how it works: when a client (usually a web browser) establishes a WebSocket connection, it sends a request to the server to initiate the connection. The server then accepts the connection and sends a response back to the client, indicating that the connection is established. From this point forward, both the client and server can send data to each other without having to make a new request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up a WebSocket Server with Python
&lt;/h2&gt;

&lt;p&gt;To build our real-time dashboard, we'll use the &lt;code&gt;websockets&lt;/code&gt; library in Python. This library provides a simple and efficient way to establish WebSocket connections and handle real-time data.&lt;/p&gt;

&lt;p&gt;First, we'll install the &lt;code&gt;websockets&lt;/code&gt; library using pip:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;websockets
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, let's create a basic WebSocket server using Python:&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="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;websockets&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;handle_connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# Wait for incoming messages from the client
&lt;/span&gt;            &lt;span class="n"&gt;message&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;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="nf"&gt;print&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;Received message: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="c1"&gt;# Send a response back to the client
&lt;/span&gt;            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&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;Server received your message: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;websockets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConnectionClosed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Connection closed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;break&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;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;websockets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;serve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handle_connection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;localhost&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8765&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&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;Server listening on port 8765&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait_closed&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;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&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;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code sets up a WebSocket server that listens for incoming connections on port 8765. When a client connects, the server prints out any incoming messages and sends a response back to the client.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a Real-Time Dashboard with WebSocket Clients
&lt;/h2&gt;

&lt;p&gt;Now that we have a WebSocket server up and running, let's create a simple dashboard client using JavaScript and the HTML5 WebSocket API.&lt;/p&gt;

&lt;p&gt;We'll use the &lt;code&gt;socket.io-client&lt;/code&gt; library to establish a WebSocket connection to our server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Real-Time Dashboard&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/socket.io-client@2/dist/socket.io.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;// Establish a WebSocket connection to our server&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;socket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;io&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ws://localhost:8765&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Update the dashboard with incoming data&lt;/span&gt;
    &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dashboard&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;p&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/p&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Real-Time Dashboard&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"dashboard"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;// Send a message to the server&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sendButton&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;messageInput&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code establishes a WebSocket connection to our server and updates a dashboard element with any incoming messages. We also have a send button that sends a message to the server when clicked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting It All Together
&lt;/h2&gt;

&lt;p&gt;To see our real-time dashboard in action, we'll run both the server and client code in different terminal windows. First, let's start the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python server.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, open a web browser and navigate to &lt;code&gt;http://localhost:8765&lt;/code&gt;. This will establish a WebSocket connection to our server, and we should see the dashboard update in real-time.&lt;/p&gt;

&lt;p&gt;To test the real-time functionality, let's send a message to the server from the client code. Enter a message in the input field and click the send button. The dashboard should update immediately with the message we just sent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building a real-time dashboard with Python and WebSockets is easier than you think. By using the &lt;code&gt;websockets&lt;/code&gt; library in Python and establishing a WebSocket connection to our server, we can create a dashboard that updates in real-time. With this knowledge, you can build your own real-time dashboards, reflecting the current state of your system or application.&lt;/p&gt;

&lt;p&gt;So, what are you waiting for? Get started today and create your own real-time dashboards with Python and WebSockets!&lt;/p&gt;

</description>
      <category>python</category>
      <category>websockets</category>
      <category>dashboard</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Python Virtual Environments: A Complete 2025 Guide</title>
      <dc:creator>qing</dc:creator>
      <pubDate>Sun, 12 Jul 2026 18:03:40 +0000</pubDate>
      <link>https://dev.to/qingluan/python-virtual-environments-a-complete-2025-guide-4edd</link>
      <guid>https://dev.to/qingluan/python-virtual-environments-a-complete-2025-guide-4edd</guid>
      <description>&lt;h1&gt;
  
  
  Python Virtual Environments: A Complete 2025 Guide
&lt;/h1&gt;

&lt;h2&gt;
  
  
  The Problem with Global Packages
&lt;/h2&gt;

&lt;p&gt;When you're working on a project in Python, you'll often find yourself installing packages using pip, the Python package manager. But have you ever stopped to think about what happens when you install a package globally? The Python interpreter looks for packages in its &lt;code&gt;sys.path&lt;/code&gt; list, which includes the current working directory and any directories specified by the &lt;code&gt;PYTHONPATH&lt;/code&gt; environment variable. But what if you're working on multiple projects at the same time, each with its own dependencies? Installing packages globally can lead to conflicts between projects, and it's a nightmare to manage.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Virtual Environments
&lt;/h2&gt;

&lt;p&gt;That's where virtual environments come in. A virtual environment is a self-contained Python environment that allows you to install packages without affecting the global Python environment. You can think of it like a sandbox where you can experiment with different versions of Python and packages without breaking anything.&lt;/p&gt;

&lt;h3&gt;
  
  
  History of Virtual Environments
&lt;/h3&gt;

&lt;p&gt;Virtual environments have been around since the early days of Python. The first virtual environment tool was &lt;code&gt;virtualenv&lt;/code&gt;, which was released in 2006. &lt;code&gt;virtualenv&lt;/code&gt; was a great tool, but it had some limitations, such as not being able to handle packages with dependencies on other packages. To address this, &lt;code&gt;virtualenvwrapper&lt;/code&gt; was released in 2008, which added some useful features, such as being able to create and manage multiple virtual environments.&lt;/p&gt;

&lt;p&gt;In 2014, &lt;code&gt;virtualenv&lt;/code&gt; was replaced by &lt;code&gt;virtualenv-20&lt;/code&gt;, which is now known as &lt;code&gt;venv&lt;/code&gt;. &lt;code&gt;venv&lt;/code&gt; is the built-in virtual environment tool in Python, and it's the recommended way to create and manage virtual environments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a Virtual Environment
&lt;/h3&gt;

&lt;p&gt;Creating a virtual environment is easy. Here's an example:&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="n"&gt;python&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;venv&lt;/span&gt; &lt;span class="n"&gt;myenv&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a new virtual environment called &lt;code&gt;myenv&lt;/code&gt; in the current working directory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing Packages
&lt;/h3&gt;

&lt;p&gt;Once you've created a virtual environment, you can install packages using pip. Here's an example:&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="n"&gt;source&lt;/span&gt; &lt;span class="n"&gt;myenv&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;activate&lt;/span&gt;
&lt;span class="n"&gt;pip&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that we're using the &lt;code&gt;activate&lt;/code&gt; script to activate the virtual environment. This will modify the &lt;code&gt;sys.path&lt;/code&gt; list and the &lt;code&gt;PYTHONPATH&lt;/code&gt; environment variable to point to the virtual environment's directory.&lt;/p&gt;

&lt;p&gt;You can also install packages using the &lt;code&gt;pip&lt;/code&gt; command without activating the virtual environment. However, this will install the packages globally.&lt;/p&gt;

&lt;h3&gt;
  
  
  Managing Dependencies
&lt;/h3&gt;

&lt;p&gt;One of the biggest advantages of virtual environments is that they make it easy to manage dependencies between projects. Let's say you have two projects, &lt;code&gt;project1&lt;/code&gt; and &lt;code&gt;project2&lt;/code&gt;, and you want to use the same version of the &lt;code&gt;requests&lt;/code&gt; package in both projects. You can create a virtual environment for each project and install the &lt;code&gt;requests&lt;/code&gt; package in each one.&lt;/p&gt;

&lt;p&gt;Here's an example:&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;# Create a virtual environment for project1
&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;venv&lt;/span&gt; &lt;span class="n"&gt;project1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;

&lt;span class="c1"&gt;# Activate the virtual environment
&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="n"&gt;project1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;activate&lt;/span&gt;

&lt;span class="c1"&gt;# Install the requests package
&lt;/span&gt;&lt;span class="n"&gt;pip&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="c1"&gt;# Create a virtual environment for project2
&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;venv&lt;/span&gt; &lt;span class="n"&gt;project2&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;

&lt;span class="c1"&gt;# Activate the virtual environment
&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="n"&gt;project2&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;activate&lt;/span&gt;

&lt;span class="c1"&gt;# Install the requests package
&lt;/span&gt;&lt;span class="n"&gt;pip&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that we're installing the &lt;code&gt;requests&lt;/code&gt; package in each virtual environment separately. This ensures that each project has its own version of the package, and we don't have to worry about conflicts between projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Best Practices
&lt;/h3&gt;

&lt;p&gt;Here are some best practices to keep in mind when working with virtual environments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Always create a new virtual environment for each project&lt;/li&gt;
&lt;li&gt;Use the &lt;code&gt;activate&lt;/code&gt; script to activate the virtual environment&lt;/li&gt;
&lt;li&gt;Install packages using pip&lt;/li&gt;
&lt;li&gt;Use the &lt;code&gt;pip freeze&lt;/code&gt; command to list all installed packages in the virtual environment&lt;/li&gt;
&lt;li&gt;Use the &lt;code&gt;pip install --upgrade&lt;/code&gt; command to upgrade all installed packages in the virtual environment&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Virtual environments are a powerful tool in Python that can help you manage dependencies and avoid conflicts between projects. By following the best practices outlined in this guide, you can create and manage virtual environments like a pro. Remember to always create a new virtual environment for each project, use the &lt;code&gt;activate&lt;/code&gt; script to activate the virtual environment, and install packages using pip. Happy coding!&lt;/p&gt;

</description>
      <category>python</category>
      <category>tutorial</category>
      <category>beginners</category>
      <category>tools</category>
    </item>
    <item>
      <title>How to Build a REST API with FastAPI in 30 Minutes</title>
      <dc:creator>qing</dc:creator>
      <pubDate>Sun, 12 Jul 2026 18:02:50 +0000</pubDate>
      <link>https://dev.to/qingluan/how-to-build-a-rest-api-with-fastapi-in-30-minutes-3851</link>
      <guid>https://dev.to/qingluan/how-to-build-a-rest-api-with-fastapi-in-30-minutes-3851</guid>
      <description>&lt;h1&gt;
  
  
  How to Build a REST API with FastAPI in 30 Minutes
&lt;/h1&gt;

&lt;h2&gt;
  
  
  You Can Build a REST API in 30 Minutes with FastAPI
&lt;/h2&gt;

&lt;p&gt;If you're a developer looking to build a REST API quickly and efficiently, you're in luck. FastAPI, a modern Python framework, allows you to create high-performance APIs with minimal code. In this article, we'll show you how to build a simple REST API using FastAPI in under 30 minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing FastAPI
&lt;/h2&gt;

&lt;p&gt;Before we dive into the code, let's make sure we have FastAPI installed. If you don't have it installed, you can do so using pip:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;fastapi uvicorn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setting Up Your API
&lt;/h2&gt;

&lt;p&gt;With FastAPI installed, let's create a new project folder and create a file called &lt;code&gt;main.py&lt;/code&gt;. This will be the file where we'll write our API code.&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;# main.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&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;read_root&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello&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;World&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;p&gt;In this code, we're creating a new FastAPI instance and defining a single route using the &lt;code&gt;@app.get&lt;/code&gt; decorator. The &lt;code&gt;read_root&lt;/code&gt; function returns a JSON response when the root URL of our API is accessed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining Routes
&lt;/h2&gt;

&lt;p&gt;Let's add a few more routes to our API. We'll create routes for getting a list of items, creating a new item, and updating an existing item.&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;# main.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;

&lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&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;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&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;Item 1&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;description&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;This is item 1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&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;Item 2&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;description&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;This is item 2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/items/&lt;/span&gt;&lt;span class="sh"&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;read_items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;

&lt;span class="nd"&gt;@app.post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/items/&lt;/span&gt;&lt;span class="sh"&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;create_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;items&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;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/items/{item_id}&lt;/span&gt;&lt;span class="sh"&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;read_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&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;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&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;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;item_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&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;Item not found&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;p&gt;In this updated code, we're defining a new &lt;code&gt;Item&lt;/code&gt; model using Pydantic, which allows us to validate and serialize our data. We're also adding a list of items to our API, which we can use to create new items and retrieve existing items.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running Your API
&lt;/h2&gt;

&lt;p&gt;With our API code written, let's run it using Uvicorn, a fast HTTP server for Python.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uvicorn main:app &lt;span class="nt"&gt;--reload&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open a web browser and navigate to &lt;code&gt;http://127.0.0.1:8000/docs&lt;/code&gt; to see our API documentation. You can use the API documentation to test our API and see the responses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing Your API
&lt;/h2&gt;

&lt;p&gt;Let's test our API using a tool like &lt;code&gt;curl&lt;/code&gt;. We can use &lt;code&gt;curl&lt;/code&gt; to send a GET request to our API to retrieve a list of items.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://127.0.0.1:8000/items/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We should see a JSON response with a list of items.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building a REST API with FastAPI is quick and easy. With its intuitive API and robust features, FastAPI makes it easy to create high-performance APIs in minimal code. Whether you're a seasoned developer or just starting out, FastAPI is a great choice for building APIs quickly and efficiently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;Now that you've learned how to build a REST API with FastAPI, here are a few next steps to take:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Explore more features&lt;/strong&gt;: FastAPI has a lot of features to explore, including support for asynchronous code, automatic API documentation, and more.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build a real-world API&lt;/strong&gt;: Take the skills you've learned and apply them to building a real-world API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Join the FastAPI community&lt;/strong&gt;: The FastAPI community is active and helpful, and is a great place to ask questions and share knowledge.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Get building with FastAPI today!&lt;/p&gt;

</description>
      <category>python</category>
      <category>fastapi</category>
      <category>api</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Beyond Black Boxes: A Developer's Guide to Open-Weight LLM API Integration</title>
      <dc:creator>NovaStack</dc:creator>
      <pubDate>Sun, 12 Jul 2026 18:01:13 +0000</pubDate>
      <link>https://dev.to/sbt112321321/beyond-black-boxes-a-developers-guide-to-open-weight-llm-api-integration-1o1f</link>
      <guid>https://dev.to/sbt112321321/beyond-black-boxes-a-developers-guide-to-open-weight-llm-api-integration-1o1f</guid>
      <description>&lt;p&gt;Beyond Black Boxes: A Developer's Guide to Open-Weight LLM API Integration&lt;/p&gt;

&lt;p&gt;Introduction&lt;/p&gt;

&lt;p&gt;The AI landscape is undergoing a massive shift. For a long time, working with Large Language Models (LLMs) meant relying on massive, centralized APIs that acted as black boxes. You sent a prompt, you got a response, but you had zero visibility into the underlying architecture or weights. &lt;/p&gt;

&lt;p&gt;Enter the era of open-weight LLMs. Models like Llama, Mistral, and Falcon have democratized access to state-of-the-art AI. But running these models locally or managing your own GPU infrastructure can be a headache. This is where unified APIs come in, giving developers the flexibility of open-weight models with the seamless integrations they expect from hosted providers. &lt;/p&gt;

&lt;p&gt;In this guide, we’ll explore why open-weight LLM API integration matters and walk through exactly how to plug them into your applications using a simple, unified endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Open-Weight LLMs Matter
&lt;/h2&gt;

&lt;p&gt;Open-weight models change the game for developers. Here’s why they are becoming the go-to choice for modern applications:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Transparency &amp;amp; Auditability:&lt;/strong&gt; Because the model weights are public, researchers and developers can audit the model for biases, safety vulnerabilities, and performance bottlenecks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost Efficiency:&lt;/strong&gt; Running inferencing on open-weight models via an API often bypasses the premium licensing fees associated with proprietary models, significantly reducing your cloud spend.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vendor Lock-in:&lt;/strong&gt; Proprietary models tie you to a specific provider's pricing and uptime. Open-weight models provide the ultimate escape hatch; if one provider goes down or changes their pricing, you can switch to another host or self-host effortlessly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fine-Tuning Flexibility:&lt;/strong&gt; Open weights mean you can take a base model and fine-tune it on your proprietary data, creating a domain-specific powerhouse without asking a tech giant for permission.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting Started with a Unified API
&lt;/h2&gt;

&lt;p&gt;While the benefits of open-weight models are clear, the infrastructure to host them at scale is complex. Migrating between different open-weight model providers often requires rewriting your integration code for each new API provider's unique specification. &lt;/p&gt;

&lt;p&gt;To solve this, developers are turning to unified API gateways. These gateways standardize the request and response formats, allowing you to query various open-weight models using a single, familiar codebase. &lt;/p&gt;

&lt;p&gt;To get started, simply sign up for an API key, and you can immediately start routing requests to the latest open-weight models without changing your application's core architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Example: Building a Chat Endpoint
&lt;/h2&gt;

&lt;p&gt;Let’s look at how simple it is to integrate an open-weight LLM into a Node.js application using a unified API. &lt;/p&gt;

&lt;p&gt;Instead of managing multiple integrations, we use a single base URL: &lt;code&gt;http://www.novapai.ai&lt;/code&gt;. This endpoint handles the routing to the appropriate open-weight model in the backend.&lt;/p&gt;

&lt;p&gt;First, store your API key in your environment variables (e.g., &lt;code&gt;.env&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;&lt;span class="nv"&gt;NOVASTACK_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your_secret_api_key_here
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, create a simple chat completion function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// chat.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;dotenv&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;NOVASTACK_API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NOVASTACK_API_KEY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BASE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://www.novapai.ai/v1/chat/completions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getChatCompletion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;BASE_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;NOVASTACK_API_KEY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;open-weight-model-v1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Specify the open-weight model&lt;/span&gt;
        &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;system&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;You are a developer-focused assistant helping debug code.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="na"&gt;max_tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`API request failed with status &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error fetching chat completion:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Usage&lt;/span&gt;
&lt;span class="nf"&gt;getChatCompletion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;How do I handle a fetch error in JavaScript?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LLM Response:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Python Integration
&lt;/h3&gt;

&lt;p&gt;Prefer Python? The process is just as straightforward using the &lt;code&gt;requests&lt;/code&gt; library:&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;# chat.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;NOVASTACK_API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NOVASTACK_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;BASE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://www.novapai.ai/v1/chat/completions&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_chat_completion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;headers&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;Content-Type&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;application/json&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;Authorization&lt;/span&gt;&lt;span class="sh"&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;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;NOVASTACK_API_KEY&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;payload&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;model&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;open-weight-model-v1&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;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&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;system&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;content&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;You are a developer-focused assistant helping debug code.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;role&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;user&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;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&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;max_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;temperature&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BASE_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;choices&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&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;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# Usage
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_chat_completion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;How do I handle a request exception in Python?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&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;LLM Response: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="si"&gt;}&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;h2&gt;
  
  
  Best Practices for Production
&lt;/h2&gt;

&lt;p&gt;Integrating open-weight models is only the first step. To build resilient, production-ready applications, keep these best practices in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Robust Error Handling:&lt;/strong&gt; APIs go down, rate limits get hit, and tokens expire. Always wrap your API calls in try/catch blocks and implement retry logic with exponential backoff.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Token Management:&lt;/strong&gt; Open-weight models have varying context windows. Always sanitize and trim your prompts to fit within the model's context length to avoid truncation and unexpected billing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streaming Responses:&lt;/strong&gt; If your application is chat-based, implement Server-Sent Events (SSE) or WebSockets using the &lt;code&gt;stream: true&lt;/code&gt; parameter in your fetch request. This allows tokens to be displayed to the user in real-time, drastically improving the perceived performance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evaluate Multiple Providers:&lt;/strong&gt; Because the weights are open, different API providers may deploy different fine-tunings or quantization levels of the same model. Test your prompts across a few unified endpoints to find the best balance of latency and accuracy for your specific use case.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The move toward open-weight LLMs empowers developers to break free from the constraints of walled-garden AI. By leveraging a unified API pattern—interacting with endpoints like &lt;code&gt;http://www.novapai.ai&lt;/code&gt;—you can enjoy the flexibility, transparency, and cost-efficiency of open-source models without devoting your life to DevOps and GPU management.&lt;/p&gt;

&lt;p&gt;Stop wrestling with incompatible API specs and infrastructure headaches. Embrace the open-weight ecosystem, standardize your integrations, and build the next generation of AI-driven applications with the tools you already know and love.&lt;/p&gt;

&lt;h1&gt;
  
  
  ai #api #opensource #tutorial
&lt;/h1&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>opensource</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
