<?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: Cursuri AI</title>
    <description>The latest articles on DEV Community by Cursuri AI (cursuri-ai).</description>
    <link>https://dev.to/cursuri-ai</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F12719%2Ffd877e1e-b068-40d1-90c2-438ed313f3e4.png</url>
      <title>DEV Community: Cursuri AI</title>
      <link>https://dev.to/cursuri-ai</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/cursuri-ai"/>
    <language>en</language>
    <item>
      <title>Fine-Tuning vs RAG vs Prompting: How to Actually Decide in 2026</title>
      <dc:creator>galian</dc:creator>
      <pubDate>Wed, 15 Jul 2026 11:49:01 +0000</pubDate>
      <link>https://dev.to/cursuri-ai/fine-tuning-vs-rag-vs-prompting-how-to-actually-decide-in-2026-11gj</link>
      <guid>https://dev.to/cursuri-ai/fine-tuning-vs-rag-vs-prompting-how-to-actually-decide-in-2026-11gj</guid>
      <description>&lt;p&gt;There's a predictable arc to most LLM projects. Something doesn't work, someone says "we should fine-tune it," a month disappears into dataset wrangling and GPU bills, and the model comes back... about as wrong as before — because the actual problem was that it never had the right facts in front of it. Fine-tuning was never going to fix that.&lt;/p&gt;

&lt;p&gt;The three techniques — &lt;strong&gt;prompting&lt;/strong&gt;, &lt;strong&gt;retrieval-augmented generation (RAG)&lt;/strong&gt;, and &lt;strong&gt;fine-tuning&lt;/strong&gt; — are not a ladder you climb from cheap to fancy. They solve &lt;em&gt;different problems&lt;/em&gt;, and choosing the wrong one is expensive in exactly the way that's hard to notice: it looks like progress while it burns weeks.&lt;/p&gt;

&lt;p&gt;This is a decision framework. Not "here's what each one is" — you can get that anywhere — but the concrete questions that tell you which one your problem actually needs, and the failure signatures that mean you picked wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one distinction that resolves most arguments
&lt;/h2&gt;

&lt;p&gt;Before any framework, internalize this split, because it settles 80% of the "should we fine-tune?" debates on its own:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;RAG changes what the model &lt;em&gt;knows&lt;/em&gt;.&lt;/strong&gt; It injects facts into the context at inference time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fine-tuning changes how the model &lt;em&gt;behaves&lt;/em&gt;.&lt;/strong&gt; It adjusts the weights to shift style, format, structure, and task-specific skill.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompting changes what the model is &lt;em&gt;told to do&lt;/em&gt; right now&lt;/strong&gt;, using the knowledge and behavior it already has.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the first question is never "which technique is best?" It's "&lt;strong&gt;is my problem a knowledge gap or a behavior gap?&lt;/strong&gt;"&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The model gives outdated, made-up, or "I don't have access to that" answers about &lt;em&gt;your&lt;/em&gt; data → &lt;strong&gt;knowledge gap → RAG.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The model knows the facts but won't reliably produce the &lt;em&gt;format, tone, or task structure&lt;/em&gt; you need → &lt;strong&gt;behavior gap → fine-tuning (maybe).&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;You haven't seriously tried telling it clearly what to do yet → &lt;strong&gt;prompting, first, always.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Get this wrong and no amount of engineering saves you. Fine-tuning a model to "know your docs" is the classic error: you can bake a &lt;em&gt;few&lt;/em&gt; facts into weights, but they go stale the moment your docs change, you can't cite sources, and you've spent training compute to build a worse version of a lookup. Knowledge that changes belongs in retrieval, not in weights.&lt;/p&gt;

&lt;h2&gt;
  
  
  Always start with prompting (yes, even now)
&lt;/h2&gt;

&lt;p&gt;Prompting is not the beginner tier you graduate from. In 2026, with frontier models, a well-constructed prompt plus a few good examples solves a startling share of problems that teams &lt;em&gt;assume&lt;/em&gt; need training. It's the fastest, cheapest, most inspectable option, and it should be your baseline before you're allowed to say the word "fine-tune."&lt;/p&gt;

&lt;p&gt;Reach for prompting when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You're still discovering what "good output" even looks like. Prompts are editable in seconds; datasets are not.&lt;/li&gt;
&lt;li&gt;The task is reasoning, transformation, or generation the model already broadly knows how to do.&lt;/li&gt;
&lt;li&gt;You need to ship this week.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The techniques that make prompting punch above its weight are unglamorous but real: precise role and task framing, &lt;strong&gt;few-shot examples&lt;/strong&gt; that demonstrate the exact output shape, chain-of-thought for multi-step reasoning, and rigid output contracts (structured/JSON) so downstream code can trust the result. Most "the model can't do this" conclusions are actually "we asked badly" conclusions. Squeezing the ceiling out of prompting before spending on anything heavier is a discipline in itself — it's the whole point of a &lt;a href="https://cursuri-ai.ro/courses/prompt-engineering-masterclass" rel="noopener noreferrer"&gt;prompt engineering masterclass&lt;/a&gt;, and the ROI of getting it right first is enormous because everything downstream inherits a better baseline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The prompting ceiling — how you know you've hit it:&lt;/strong&gt; you've iterated seriously, added good examples, and the model &lt;em&gt;still&lt;/em&gt; fails — and the failure is either (a) it doesn't know facts it couldn't possibly know, or (b) it can't hold a consistent behavior across inputs no matter how you phrase the instruction. (a) points to RAG. (b) &lt;em&gt;might&lt;/em&gt; point to fine-tuning. Not before.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reach for RAG when the problem is knowledge
&lt;/h2&gt;

&lt;p&gt;RAG is the answer whenever the model needs to work with information it wasn't trained on: your internal documentation, a product catalog, last week's tickets, a knowledge base that updates daily, anything private or fresh.&lt;/p&gt;

&lt;p&gt;Choose RAG when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Answers must be &lt;strong&gt;grounded in a specific corpus&lt;/strong&gt; and you need to &lt;strong&gt;cite sources&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The knowledge &lt;strong&gt;changes&lt;/strong&gt; — pricing, policies, docs, inventory. You update an index, not a model.&lt;/li&gt;
&lt;li&gt;Hallucination on facts is unacceptable and you need an audit trail of &lt;em&gt;where&lt;/em&gt; an answer came from.&lt;/li&gt;
&lt;li&gt;The knowledge base is large, or partly access-controlled per user.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The reason RAG beats fine-tuning for knowledge isn't subtle: updating a document store is trivial and instant; updating weights is a training run. RAG gives you freshness, provenance, and per-user access control for free — none of which fine-tuning can offer. When your facts have a shelf life, retrieval is the &lt;em&gt;only&lt;/em&gt; correct architecture, and building it well (chunking, hybrid search, re-ranking) is where the real engineering lives — the substance of a dedicated &lt;a href="https://cursuri-ai.ro/courses/rag-retrieval-augmented-generation" rel="noopener noreferrer"&gt;course on RAG and retrieval-augmented generation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RAG's own ceiling:&lt;/strong&gt; retrieval fixes &lt;em&gt;what the model knows&lt;/em&gt;, not &lt;em&gt;how it behaves&lt;/em&gt;. If your RAG answers are factually correct but come out in the wrong format, wrong tone, or don't follow your house style no matter how you prompt — that residual behavior gap is exactly where fine-tuning finally earns its place, &lt;em&gt;on top of&lt;/em&gt; RAG, not instead of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fine-tune when the problem is behavior — and only then
&lt;/h2&gt;

&lt;p&gt;Fine-tuning is the right tool, but for a narrower set of problems than its reputation suggests. It shines at teaching &lt;em&gt;consistent behavior&lt;/em&gt; that's hard to specify in a prompt: a very specific output structure, a domain's tone and terminology, a classification or extraction task where you have lots of labeled examples, or a skill the base model does clumsily.&lt;/p&gt;

&lt;p&gt;Legitimately reach for fine-tuning when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need &lt;strong&gt;consistent style, format, or structure&lt;/strong&gt; at a level prompting can't hold across the full input distribution.&lt;/li&gt;
&lt;li&gt;You have a &lt;strong&gt;narrow, high-volume, well-defined task&lt;/strong&gt; (classification, extraction, a specific transformation) and enough quality labeled data.&lt;/li&gt;
&lt;li&gt;You want to &lt;strong&gt;bake in a behavior&lt;/strong&gt; so you can drop it from the prompt — shorter prompts, lower per-call cost, faster responses at scale.&lt;/li&gt;
&lt;li&gt;Latency or cost at scale matters and a smaller fine-tuned model can match a bigger prompted one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two things make modern fine-tuning far less scary than its reputation. First, &lt;strong&gt;you almost never do full fine-tuning&lt;/strong&gt; — parameter-efficient methods like &lt;strong&gt;LoRA/QLoRA&lt;/strong&gt; train a tiny set of adapter weights, cutting the compute and memory cost by orders of magnitude while getting most of the benefit. Second, the bottleneck is &lt;strong&gt;data quality, not model choice&lt;/strong&gt;: a few hundred to a few thousand &lt;em&gt;clean, consistent, representative&lt;/em&gt; examples beat a huge noisy pile every time. The hard part of fine-tuning was never running the training job — it's building the dataset, choosing PEFT trade-offs, and evaluating the result without fooling yourself, which is precisely the ground a &lt;a href="https://cursuri-ai.ro/courses/fine-tuning-modele-ai" rel="noopener noreferrer"&gt;fine-tuning course&lt;/a&gt; has to cover to be worth anything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When fine-tuning is the &lt;em&gt;wrong&lt;/em&gt; answer — the red flags:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"We'll fine-tune it on our docs so it knows them." → No. That's RAG. Fine-tuned facts go stale and can't be cited.&lt;/li&gt;
&lt;li&gt;"We haven't really tried prompting." → Do that first; you may not need to train at all.&lt;/li&gt;
&lt;li&gt;"The requirements change weekly." → Fine-tuning bakes behavior in; if the target moves, you're re-training constantly. Keep it in the prompt until it stabilizes.&lt;/li&gt;
&lt;li&gt;"We have 40 examples." → Usually not enough for reliable behavior change; strong prompting with those 40 as few-shot examples will likely beat it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The combinations are the real answer
&lt;/h2&gt;

&lt;p&gt;Framing these as rivals is the beginner mistake. In production, the strongest systems &lt;strong&gt;combine&lt;/strong&gt; them, because they operate on different axes — knowledge, behavior, and instruction — and stack cleanly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;RAG + prompting&lt;/strong&gt; is the workhorse for most knowledge-grounded assistants: retrieve the right context, then a well-engineered prompt instructs the model to answer &lt;em&gt;only&lt;/em&gt; from it and cite sources. No training required.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fine-tuning + RAG&lt;/strong&gt; is the high end: fine-tune for the domain's &lt;em&gt;behavior&lt;/em&gt; (tone, format, task skill), and use RAG for the &lt;em&gt;facts&lt;/em&gt;. The model behaves exactly right &lt;em&gt;and&lt;/em&gt; stays current — behavior in the weights, knowledge in the index.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fine-tuning + prompting&lt;/strong&gt; collapses a long, brittle instruction into learned behavior, so your prompts get short and your inference gets cheaper.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Orchestrating these — deciding which layer owns which responsibility, and routing a request through retrieval, tools, and the model in the right order — is its own engineering discipline, and it's the core of a &lt;a href="https://cursuri-ai.ro/courses/advanced-llm-integration" rel="noopener noreferrer"&gt;course on advanced LLM integration&lt;/a&gt;. The mental model to keep: &lt;strong&gt;knowledge → retrieval, behavior → weights, instruction → prompt.&lt;/strong&gt; Put each requirement on the axis it actually lives on.&lt;/p&gt;

&lt;h2&gt;
  
  
  The decision, in one pass
&lt;/h2&gt;

&lt;p&gt;Run your problem through this, in order. Stop at the first that fits:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Have you genuinely exhausted prompting&lt;/strong&gt; — clear instructions, good few-shot examples, structured output? If not → &lt;strong&gt;prompt.&lt;/strong&gt; (This is where most projects should still be.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is the failure a knowledge gap&lt;/strong&gt; — missing, stale, or private facts; needs citations? → &lt;strong&gt;RAG.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is the failure a behavior gap&lt;/strong&gt; — format/tone/task consistency the prompt can't hold, &lt;em&gt;and&lt;/em&gt; you have quality labeled data &lt;em&gt;and&lt;/em&gt; the target is stable? → &lt;strong&gt;fine-tune&lt;/strong&gt; (LoRA first).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is it both?&lt;/strong&gt; → &lt;strong&gt;RAG for the facts, fine-tuning for the behavior.&lt;/strong&gt; In that order.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And underneath all of it: &lt;strong&gt;you cannot make this decision without evaluation.&lt;/strong&gt; "It seems better" is not data. Before you choose, build a small eval set — representative inputs with known-good outputs — so you can measure whether prompting already clears the bar, whether RAG actually retrieves the right context, and whether a fine-tune moved the metric or just moved the failures around. Teams that skip this pick techniques by vibes and discover the mistake in production; teams that treat &lt;a href="https://cursuri-ai.ro/courses/ai-evals-llm-productie" rel="noopener noreferrer"&gt;evals as first-class&lt;/a&gt; make the cheap correct choice on purpose. The eval set is what turns this framework from an opinion into a decision.&lt;/p&gt;

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

&lt;p&gt;The reason so many LLM projects stall isn't a shortage of technique — it's reaching for the wrong one and mistaking motion for progress. Fine-tuning a model to "learn facts," RAG-ing a problem that was really a bad prompt, or grinding on prompts when the model fundamentally lacks the data: each fails in a way that looks like effort.&lt;/p&gt;

&lt;p&gt;Anchor on the split and you'll rarely go wrong. &lt;strong&gt;Knowledge that changes → RAG. Behavior you can't prompt into place → fine-tuning. Everything else → prompt, and prompt well.&lt;/strong&gt; Start cheap, measure honestly, and add complexity only when an eval — not a hunch — tells you the current layer has topped out. The best architecture isn't the most sophisticated one; it's the one that puts each requirement on the axis where it actually belongs.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Sources &amp;amp; further reading:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lewis et al. — &lt;a href="https://arxiv.org/abs/2005.11401" rel="noopener noreferrer"&gt;Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Hu et al. — &lt;a href="https://arxiv.org/abs/2106.09685" rel="noopener noreferrer"&gt;LoRA: Low-Rank Adaptation of Large Language Models&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Dettmers et al. — &lt;a href="https://arxiv.org/abs/2305.14314" rel="noopener noreferrer"&gt;QLoRA: Efficient Finetuning of Quantized LLMs&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;This article is educational content. Models, tooling, and cost trade-offs evolve quickly; validate any approach against your own data and current provider documentation before committing to it in production.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>monitoring</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Vector Embeddings Explained: Build Semantic Search in Python</title>
      <dc:creator>galian</dc:creator>
      <pubDate>Mon, 13 Jul 2026 11:25:24 +0000</pubDate>
      <link>https://dev.to/cursuri-ai/vector-embeddings-explained-build-semantic-search-in-python-2og1</link>
      <guid>https://dev.to/cursuri-ai/vector-embeddings-explained-build-semantic-search-in-python-2og1</guid>
      <description>&lt;p&gt;Search for "reset my password" in a keyword-based system and a help article titled "How to recover your account credentials" won't match — not one word overlaps. Yet any human knows they mean the same thing. Closing that gap between &lt;em&gt;characters&lt;/em&gt; and &lt;em&gt;meaning&lt;/em&gt; is what &lt;strong&gt;vector embeddings&lt;/strong&gt; do, and they're the quiet engine behind semantic search, RAG, recommendation systems, and most of the "AI that understands you" experiences shipped since 2023.&lt;/p&gt;

&lt;p&gt;This is a practical guide. We'll cover what an embedding actually is, why cosine similarity is the operation you'll use constantly, and then build a real, working semantic search engine in Python — first with pure NumPy so you see the mechanics, then with the tools you'd actually reach for in production. By the end you'll have code that runs and a mental model that transfers to every embedding-powered feature you build next.&lt;/p&gt;

&lt;h2&gt;
  
  
  What an embedding actually is
&lt;/h2&gt;

&lt;p&gt;An &lt;strong&gt;embedding&lt;/strong&gt; is a list of numbers — a vector — that represents a piece of content as a point in high-dimensional space. An embedding model (a neural network trained on enormous text corpora) reads your text and outputs, say, 384 or 1,536 floating-point numbers. The magic isn't the numbers themselves; it's the property the training instills: &lt;strong&gt;texts with similar meaning land close together in that space, and unrelated texts land far apart.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's the whole idea. "How do I reset my password?" and "I forgot my login credentials" produce vectors that sit near each other. "What's the weather in Cluj?" produces a vector off in a completely different region. The model has effectively turned &lt;em&gt;meaning&lt;/em&gt; into &lt;em&gt;geometry&lt;/em&gt; — and geometry is something a computer can measure with plain arithmetic.&lt;/p&gt;

&lt;p&gt;A few properties worth internalizing before we write code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dimensionality is fixed per model.&lt;/strong&gt; A given model always outputs the same length (e.g. 384 for &lt;code&gt;all-MiniLM-L6-v2&lt;/code&gt;, 1,536 for OpenAI's &lt;code&gt;text-embedding-3-small&lt;/code&gt;). You can't mix vectors from different models — they live in different spaces.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The individual numbers are not interpretable.&lt;/strong&gt; Dimension 200 doesn't mean "formality" or "topic." Meaning is distributed across all dimensions at once. Don't try to read them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Distance is the entire point.&lt;/strong&gt; You almost never care about a vector's absolute position — only how close it is to &lt;em&gt;other&lt;/em&gt; vectors.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Cosine similarity: the one operation you'll use everywhere
&lt;/h2&gt;

&lt;p&gt;To ask "how similar are these two texts?", you compare their vectors. The near-universal choice for text embeddings is &lt;strong&gt;cosine similarity&lt;/strong&gt;: it measures the angle between two vectors, ignoring their magnitude.&lt;/p&gt;

&lt;p&gt;Why the angle and not, say, straight-line (Euclidean) distance? Because for text embeddings, &lt;em&gt;direction&lt;/em&gt; encodes meaning while &lt;em&gt;length&lt;/em&gt; often encodes uninteresting things like text length or token count. Two documents about the same topic point the same way even if one is a sentence and the other a paragraph. Cosine similarity captures exactly that.&lt;/p&gt;

&lt;p&gt;The formula is just the dot product of the two vectors divided by the product of their magnitudes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cos(θ) = (A · B) / (‖A‖ · ‖B‖)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It returns a value from &lt;strong&gt;-1 to 1&lt;/strong&gt;, though for most modern text embeddings you'll see results land in roughly the 0-to-1 range:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;~1.0&lt;/strong&gt; — nearly identical meaning&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;~0.5&lt;/strong&gt; — loosely related&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;~0.0&lt;/strong&gt; — unrelated&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That single number, computed against a corpus of stored vectors, &lt;em&gt;is&lt;/em&gt; semantic search. Everything else is optimization.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build a semantic search engine from scratch
&lt;/h2&gt;

&lt;p&gt;Let's make it concrete. We'll use &lt;a href="https://www.sbert.net/" rel="noopener noreferrer"&gt;&lt;code&gt;sentence-transformers&lt;/code&gt;&lt;/a&gt;, which runs a capable embedding model locally — no API key, no network calls, so you can run this offline right now.&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;sentence-transformers numpy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 1 — Embed a corpus
&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;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="c1"&gt;# A small, fast, widely used model. 384-dimensional output.
&lt;/span&gt;&lt;span class="n"&gt;model&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;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;How to reset your account password&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 for annual subscriptions&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;Setting up two-factor authentication&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 office hours and contact details&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 to recover a locked account&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;# Encode the whole corpus once. Shape: (5, 384)
&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;model&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="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc_embeddings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# (5, 384)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;doc_embeddings&lt;/code&gt; array is your search index. In a real app you compute it &lt;strong&gt;once&lt;/strong&gt;, when a document is created or updated, and store it — never on every query.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2 — Cosine similarity in NumPy
&lt;/h3&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;cosine_similarity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ndarray&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ndarray&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Cosine similarity between vector `a` and each row of matrix `b`.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;a_norm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linalg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;norm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;b_norm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linalg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;norm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;axis&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;keepdims&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;b_norm&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt; &lt;span class="n"&gt;a_norm&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ten lines, no dependencies beyond NumPy. This is the actual core of semantic search — the rest is plumbing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3 — Search
&lt;/h3&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;search&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;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="n"&gt;query_vec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&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;scores&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;cosine_similarity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query_vec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;doc_embeddings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ranked&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;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="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# top-k, highest first
&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;documents&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;scores&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;ranked&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="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="nf"&gt;search&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 forgot my login credentials&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="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&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;Run it, and you'll get something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0.62  How to reset your account password
0.55  How to recover a locked account
0.31  Setting up two-factor authentication
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice what happened: the query &lt;strong&gt;"I forgot my login credentials"&lt;/strong&gt; shares &lt;em&gt;zero words&lt;/em&gt; with "How to reset your account password," yet it ranked first. A keyword search would have returned nothing. That's the payoff — you matched on meaning, not on string overlap. This shift from lexical to semantic matching is the foundation every retrieval-augmented system builds on, and it's the starting point of a structured &lt;a href="https://cursuri-ai.ro/courses/rag-retrieval-augmented-generation" rel="noopener noreferrer"&gt;course on RAG and retrieval-augmented generation&lt;/a&gt; that goes from this toy index to production retrieval.&lt;/p&gt;

&lt;h2&gt;
  
  
  From toy to production: what changes
&lt;/h2&gt;

&lt;p&gt;The NumPy version is perfect for learning and fine for a few thousand documents. Past that, three things force an upgrade.&lt;/p&gt;

&lt;h3&gt;
  
  
  You need a vector database
&lt;/h3&gt;

&lt;p&gt;Computing cosine similarity against &lt;em&gt;every&lt;/em&gt; stored vector on every query is &lt;code&gt;O(n)&lt;/code&gt; — fine at 5 documents, painful at 5 million. Vector databases solve this with &lt;strong&gt;Approximate Nearest Neighbor (ANN)&lt;/strong&gt; indexes (HNSW is the common one) that trade a sliver of accuracy for enormous speed, returning near-neighbors in milliseconds over huge corpora.&lt;/p&gt;

&lt;p&gt;You have good open-source options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;pgvector&lt;/strong&gt; — a Postgres extension. If your data already lives in Postgres, this is often the pragmatic choice: vectors and relational data in one place, one backup story.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chroma / Qdrant / Weaviate / Milvus&lt;/strong&gt; — purpose-built vector stores with richer filtering and scaling stories.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FAISS&lt;/strong&gt; — a library (not a server) from Meta for fast similarity search when you want to manage the index yourself.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A minimal Chroma example shows how little the mental model changes:&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;chromadb&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;chromadb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;collection&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;create_collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;docs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="o"&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;ids&lt;/span&gt;&lt;span class="o"&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;d&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="sh"&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="nf"&gt;range&lt;/span&gt;&lt;span class="p"&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;documents&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="n"&gt;collection&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="n"&gt;query_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;I forgot my login credentials&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;n_results&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="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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;documents&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;Chroma embeds the text for you and handles the index. Same concept, production ergonomics.&lt;/p&gt;

&lt;h3&gt;
  
  
  Chunking matters more than the model
&lt;/h3&gt;

&lt;p&gt;You rarely embed whole documents. A 30-page PDF becomes one vector that's an average of everything and a good match for nothing. In practice you &lt;strong&gt;chunk&lt;/strong&gt; documents into passages (a few hundred tokens, often with slight overlap) and embed each chunk. Get chunking wrong and even a great embedding model returns mush — which is one of the most common reasons retrieval systems quietly underperform. Chunking strategy, overlap, and metadata are exactly the unglamorous details that separate a demo from a dependable system, and they're covered in depth in a &lt;a href="https://cursuri-ai.ro/courses/advanced-llm-integration" rel="noopener noreferrer"&gt;course on advanced LLM integration for production apps&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Choosing and swapping embedding models
&lt;/h3&gt;

&lt;p&gt;Embedding models differ in dimensionality, speed, cost, and language coverage — and critically, &lt;strong&gt;you must embed your corpus and your queries with the same model.&lt;/strong&gt; Change the model and you re-embed everything. For multilingual apps (Romanian included), pick a model with strong multilingual training rather than an English-first one, or your non-English recall will suffer silently. Public benchmarks like the &lt;strong&gt;MTEB&lt;/strong&gt; leaderboard on Hugging Face are the sane starting point for comparing models on retrieval quality rather than vibes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hybrid search: when semantic alone isn't enough
&lt;/h2&gt;

&lt;p&gt;Here's a lesson that surprises people: pure semantic search is &lt;em&gt;worse&lt;/em&gt; than keyword search for certain queries. Ask for an exact product code, a specific error like &lt;code&gt;ERR_CONN_REFUSED&lt;/code&gt;, a person's name, or an acronym, and embeddings can betray you — they match on &lt;em&gt;meaning&lt;/em&gt;, and a precise identifier has little semantic meaning to spread around. The embedding for &lt;code&gt;ERR_CONN_REFUSED&lt;/code&gt; sits near "connection problems" generally, so a document about a &lt;em&gt;different&lt;/em&gt; connection error can outrank the exact match.&lt;/p&gt;

&lt;p&gt;The production answer is &lt;strong&gt;hybrid search&lt;/strong&gt;: run both a keyword search (classic lexical scoring like BM25) and a semantic search, then combine the rankings. Keyword search nails exact terms, identifiers, and rare words; semantic search nails paraphrase and intent. Together they cover each other's blind spots.&lt;/p&gt;

&lt;p&gt;The standard way to merge the two result lists is &lt;strong&gt;Reciprocal Rank Fusion (RRF)&lt;/strong&gt; — a simple, robust formula that scores each document by its &lt;em&gt;rank&lt;/em&gt; in each list rather than by raw scores that live on incompatible scales:&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;reciprocal_rank_fusion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rankings&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;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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Fuse multiple ranked lists of doc IDs into one score per doc.&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;ranking&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rankings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;            &lt;span class="c1"&gt;# e.g. [keyword_results, semantic_results]
&lt;/span&gt;        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;rank&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;ranking&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="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="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;dict&lt;/span&gt;&lt;span class="p"&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;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because RRF only looks at positions, you don't have to normalize BM25 scores against cosine similarities — a notorious apples-to-oranges trap. Most serious vector databases (Weaviate, Qdrant, and others) now offer hybrid search with RRF built in, precisely because "just embeddings" quietly underperforms on real, messy query logs. If you take one production lesson from this article, make it this: &lt;strong&gt;measure your recall on real queries, and reach for hybrid the moment exact-match queries show up.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where embeddings show up beyond search
&lt;/h2&gt;

&lt;p&gt;Semantic search is the gateway, but the same vectors power a lot more:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;RAG (retrieval-augmented generation)&lt;/strong&gt; — retrieve relevant chunks by embedding similarity, then feed them to an LLM as grounding context. Embeddings are the &lt;em&gt;retrieval&lt;/em&gt; half; without good retrieval, generation hallucinates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deduplication &amp;amp; clustering&lt;/strong&gt; — near-duplicate detection and topic clustering fall out of distances almost for free.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recommendations&lt;/strong&gt; — "items similar to this one" is a nearest-neighbor query in embedding space.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Classification&lt;/strong&gt; — embed labeled examples, then classify new items by nearest neighbors, often without training a dedicated model.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The through-line: any time you need "similar in meaning" rather than "matches exactly," embeddings are the tool. Building these features end to end — API to product, with the retrieval and orchestration wired up properly — is the spine of a hands-on &lt;a href="https://cursuri-ai.ro/courses/construire-aplicatii-ai-python-sdk" rel="noopener noreferrer"&gt;course on building AI applications in Python with the OpenAI and Anthropic SDKs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common mistakes that cost you hours
&lt;/h2&gt;

&lt;p&gt;A few traps that catch almost everyone the first time:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Re-embedding the corpus on every query.&lt;/strong&gt; Embed documents once, store the vectors, embed only the incoming query at search time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mixing models.&lt;/strong&gt; Query vectors and document vectors must come from the &lt;em&gt;same&lt;/em&gt; embedding model. A silent mismatch produces garbage rankings with no error.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Forgetting to normalize.&lt;/strong&gt; If you compute raw dot products instead of cosine similarity (and your vectors aren't already unit-normalized), longer texts get an unfair boost. Normalize, or use a library that does.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Embedding documents that are too large.&lt;/strong&gt; One vector per giant document averages meaning into uselessness. Chunk first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trusting a single similarity threshold forever.&lt;/strong&gt; The "good enough" cutoff depends on your model and data. Measure it on real queries; don't hardcode 0.8 because a blog post said so.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Vector embeddings are one of those ideas that feels like magic until you see the mechanics — and then it's just geometry. Text becomes a point in space, meaning becomes distance, and search becomes "find the nearest points." You built exactly that in a few lines of Python, from raw NumPy cosine similarity to a Chroma-backed index, and the same core idea scales from a toy corpus to millions of documents behind an ANN index.&lt;/p&gt;

&lt;p&gt;Start where we started: embed a handful of your own documents, run a query that shares no keywords with the right answer, and watch it surface anyway. Once that clicks, RAG, recommendations, and semantic features stop looking like separate topics and start looking like one tool applied five ways. If you want the structured path from here — retrieval, chunking, evaluation, and production wiring — &lt;a href="https://cursuri-ai.ro/courses/rag-retrieval-augmented-generation" rel="noopener noreferrer"&gt;Cursuri-AI.ro&lt;/a&gt; builds it step by step.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Sources &amp;amp; further reading:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sentence-Transformers — &lt;a href="https://www.sbert.net/" rel="noopener noreferrer"&gt;Official documentation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Hugging Face — &lt;a href="https://huggingface.co/spaces/mteb/leaderboard" rel="noopener noreferrer"&gt;MTEB: Massive Text Embedding Benchmark leaderboard&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;pgvector — &lt;a href="https://github.com/pgvector/pgvector" rel="noopener noreferrer"&gt;Open-source vector similarity search for Postgres&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Chroma — &lt;a href="https://www.trychroma.com/" rel="noopener noreferrer"&gt;Open-source embedding database&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;This article is educational content. Model names, dimensions, and library APIs evolve; verify current details in the official documentation before building production systems.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Context Engineering for AI Agents: Beyond Prompt Engineering</title>
      <dc:creator>galian</dc:creator>
      <pubDate>Thu, 09 Jul 2026 13:08:24 +0000</pubDate>
      <link>https://dev.to/cursuri-ai/context-engineering-for-ai-agents-beyond-prompt-engineering-14ep</link>
      <guid>https://dev.to/cursuri-ai/context-engineering-for-ai-agents-beyond-prompt-engineering-14ep</guid>
      <description>&lt;p&gt;You wrote a great prompt. It worked beautifully in the playground — one question, one clean answer. Then you wired the same model into an agent that runs twenty steps, calls six tools, and reads back their output, and somewhere around step twelve it started forgetting the goal, calling the wrong tool, or confidently acting on something it misread three steps ago. The prompt didn't get worse. The &lt;em&gt;context&lt;/em&gt; did.&lt;/p&gt;

&lt;p&gt;This is the gap that context engineering fills. Prompt engineering is about writing one good instruction. Context engineering is about managing the entire set of tokens a model sees at inference time — across a long, multi-step run — so the signal stays high and the model keeps making good decisions. Anthropic frames it as &lt;a href="https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents" rel="noopener noreferrer"&gt;the natural progression of prompt engineering&lt;/a&gt;, and if you're building anything agentic in 2026, it's the discipline that separates a demo from a system.&lt;/p&gt;

&lt;h2&gt;
  
  
  What context engineering actually is
&lt;/h2&gt;

&lt;p&gt;Start with a precise definition. &lt;strong&gt;Context&lt;/strong&gt; is the full set of tokens you include when you sample from a large language model. Not just your prompt — the system instructions, the tool definitions, the examples, the running message history, the retrieved documents, the tool results fed back in. Everything in the window.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Context engineering&lt;/strong&gt; is the set of strategies for curating and maintaining the &lt;em&gt;optimal&lt;/em&gt; set of those tokens during inference. The goal, in one line: find the smallest set of high-signal tokens that reliably produces the outcome you want.&lt;/p&gt;

&lt;p&gt;The reason this is a distinct discipline from prompt engineering is the shape of the problem. A prompt is something you write once and it stays put. Context in an agent is &lt;em&gt;dynamic&lt;/em&gt; — it grows on every turn as the model reads files, calls tools, and accumulates history. You're not authoring a static string anymore; you're managing a budget that fills up on its own, and deciding continuously what earns a place in it and what gets thrown out. That's an engineering problem, and it's &lt;a href="https://cursuri-ai.ro/courses/prompt-engineering-masterclass" rel="noopener noreferrer"&gt;the foundation the whole prompt-to-production journey builds on&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why "just add more context" is the wrong instinct
&lt;/h2&gt;

&lt;p&gt;The intuitive move, when an agent makes a mistake, is to give it more: more instructions, more examples, more history, more retrieved documents. Sometimes that helps. Very often it makes things worse, and here's why.&lt;/p&gt;

&lt;p&gt;A model's effective attention is a finite resource. Every token you add competes with every other token for the model's limited ability to attend to what matters. Past a certain point, adding context doesn't add capability — it dilutes it. The relevant fact is now buried among ten irrelevant ones, and the model attends to the wrong thing.&lt;/p&gt;

&lt;p&gt;This shows up empirically. The "lost in the middle" effect — &lt;a href="https://arxiv.org/abs/2307.03172" rel="noopener noreferrer"&gt;documented by Liu et al.&lt;/a&gt; — found that models attend most reliably to information at the &lt;em&gt;start&lt;/em&gt; and &lt;em&gt;end&lt;/em&gt; of a long context, and least reliably to what's stuck in the middle. As context grows, retrieval of any single fact inside it gets less reliable, a degradation sometimes called &lt;em&gt;context rot&lt;/em&gt;. A 200K-token window does not mean you should put 200K tokens in it. Capacity is not the same as attention.&lt;/p&gt;

&lt;p&gt;So the mental model to internalize: &lt;strong&gt;context is a budget, not a bucket.&lt;/strong&gt; You're not trying to fill it. You're trying to spend it on the highest-signal tokens available and refuse everything else. Every technique below is a way to enforce that discipline.&lt;/p&gt;

&lt;h2&gt;
  
  
  The four things competing for your window
&lt;/h2&gt;

&lt;p&gt;Before the techniques, know your spenders. In a running agent, four categories of tokens fight for the same finite budget:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The system prompt&lt;/strong&gt; — your instructions, role, constraints. Usually small, high-value, and stable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tool definitions&lt;/strong&gt; — the schemas describing every tool the agent can call. These are sneakily expensive: each tool's description sits in context on &lt;em&gt;every&lt;/em&gt; turn, whether or not it's used.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Message history&lt;/strong&gt; — the accumulating transcript of the conversation and the agent's own steps. This is the one that grows without bound and quietly eats the window.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retrieved / external data&lt;/strong&gt; — documents, search results, file contents, database rows pulled in to ground the model.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The overall guidance from Anthropic is worth memorizing: keep each of these &lt;strong&gt;informative yet tight&lt;/strong&gt;. Not empty — an under-specified system prompt or a missing tool leaves the model guessing. But not bloated either. The art is the calibration, and it's different for each category. Let's work through the techniques that manage them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technique 1: Compaction — summarize the history before it drowns you
&lt;/h2&gt;

&lt;p&gt;Message history is the runaway spender. A long agent run accumulates hundreds of turns of "called tool, got 4KB of JSON back, reasoned about it, called the next tool." Most of those raw tool outputs are dead weight three steps later — you needed the &lt;em&gt;conclusion&lt;/em&gt;, not the 4KB.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compaction&lt;/strong&gt; is the fix: periodically replace a chunk of verbose history with a tight summary that preserves the decisions, the key facts, and the current state, while dropping the raw noise. When the agent has finished investigating something, you don't need the full transcript of the investigation in context — you need "here's what I found and what it means for the task."&lt;/p&gt;

&lt;p&gt;Practical rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Compact at natural boundaries&lt;/strong&gt;, not mid-reasoning — after a sub-task completes, when a phase ends.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Preserve the load-bearing details&lt;/strong&gt;: open questions, decisions made, constraints discovered, current state. Drop the intermediate chatter and the raw dumps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep the goal pinned.&lt;/strong&gt; The single most common long-run failure is the agent losing the plot on the original objective. The goal should survive every compaction.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Done well, compaction is what lets an agent run for hundreds of steps without either overflowing its window or forgetting why it started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technique 2: External memory — let the agent write things down
&lt;/h2&gt;

&lt;p&gt;The window is not the only place to keep information. The most effective long-running agents treat the context window as &lt;em&gt;working memory&lt;/em&gt; and offload durable state to &lt;strong&gt;external memory&lt;/strong&gt; — a file, a scratchpad, a structured store the agent reads from and writes to deliberately.&lt;/p&gt;

&lt;p&gt;Instead of carrying every fact in-context forever, the agent writes a note ("the auth module uses JWT with 15-minute expiry; the bug is in the refresh path") to a persistent store, and pulls it back only when relevant. The context window stays lean; the knowledge doesn't get lost. This is exactly how a human engineer works — you don't hold the entire codebase in your head, you keep notes and open the file when you need it.&lt;/p&gt;

&lt;p&gt;This pattern — persistent, deliberately-managed memory outside the window — is &lt;a href="https://cursuri-ai.ro/courses/context-engineering-memorie-agenti" rel="noopener noreferrer"&gt;the core of building agents that hold state over long horizons&lt;/a&gt;, and it's what turns a stateless model into something that can work a problem across a session without drowning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technique 3: Sub-agents — isolate context so the main thread stays clean
&lt;/h2&gt;

&lt;p&gt;Here's a structural technique that most people never turn on. When a sub-task is going to burn a lot of tokens — "find every call site of this function across the repo," "research these five libraries" — don't do it in the main agent's context. Delegate it to a &lt;strong&gt;sub-agent&lt;/strong&gt;: a separate agent instance with its own isolated window that does the noisy work and reports back a clean result.&lt;/p&gt;

&lt;p&gt;The win is context hygiene. The thousands of tokens of file contents and search output that the investigation churns through stay in the &lt;em&gt;sub-agent's&lt;/em&gt; context and die with it. The main agent gets back a two-paragraph summary, and its own window stays focused on the actual task instead of silting up with intermediate noise. As a bonus, independent sub-tasks run in parallel instead of serially.&lt;/p&gt;

&lt;p&gt;The rule of thumb: delegate when work is &lt;strong&gt;independent, parallelizable, or context-heavy&lt;/strong&gt;; keep it inline when it's sequential and cheap. Knowing when to fan out to sub-agents and how to orchestrate them without stepping on each other is &lt;a href="https://cursuri-ai.ro/courses/ai-agents-automatizare" rel="noopener noreferrer"&gt;a central skill in building AI agents and automation&lt;/a&gt;, and it's one of the highest-leverage context moves you have.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technique 4: Just-in-time retrieval — pull data when needed, not upfront
&lt;/h2&gt;

&lt;p&gt;There are two ways to get external data into an agent. The naive way is to &lt;strong&gt;preload&lt;/strong&gt;: at the start, dump everything the agent might conceivably need into context — the whole document, all the schemas, every config file. The problem is obvious once you see it: you're spending your budget on maybes, and most of it goes unused while crowding out what matters.&lt;/p&gt;

&lt;p&gt;The better pattern is &lt;strong&gt;just-in-time retrieval&lt;/strong&gt;: give the agent the &lt;em&gt;ability&lt;/em&gt; to fetch data (a search tool, a file reader, a database query) and let it pull exactly what it needs, exactly when it needs it. Instead of "here are all 40 files," it's "here's a tool to read a file — go get the one you need." The agent loads the relevant chunk into context at the moment of use, acts on it, and (with compaction) lets it fall away afterward.&lt;/p&gt;

&lt;p&gt;This mirrors how retrieval-augmented systems already work, and getting the retrieval layer right — what to fetch, how to rank it, how much to bring back — is &lt;a href="https://cursuri-ai.ro/courses/advanced-llm-integration" rel="noopener noreferrer"&gt;where advanced LLM integration earns its keep&lt;/a&gt;. Preloading feels safer; just-in-time is what actually scales.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technique 5: Tool curation — the failure mode hiding in plain sight
&lt;/h2&gt;

&lt;p&gt;Remember that tool definitions sit in context on every turn. That makes a bloated tool set a double tax: it burns tokens continuously, &lt;em&gt;and&lt;/em&gt; it degrades decisions. Anthropic calls out one of the most common failure modes directly — tool sets that cover too much functionality or create ambiguous, overlapping choices about which tool to use.&lt;/p&gt;

&lt;p&gt;The tell is a sharp one: &lt;strong&gt;if a human engineer can't say for certain which tool should be used in a given situation, an AI agent can't either.&lt;/strong&gt; Fifteen tools with fuzzy, overlapping responsibilities will produce worse behavior than six sharp, non-overlapping ones — and cost more tokens doing it.&lt;/p&gt;

&lt;p&gt;So curate the toolbox like you'd curate an API:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fewer, sharper tools.&lt;/strong&gt; Each with a clear, distinct job and an unambiguous "use this when…"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No overlap.&lt;/strong&gt; Two tools that could both plausibly handle the same request is a decision point where the agent will sometimes pick wrong.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prune ruthlessly.&lt;/strong&gt; A tool that's rarely the right choice is paying rent in your context on every single turn. Cut it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tool curation is the least glamorous technique here and often the highest-ROI. It's pure subtraction, and subtraction is exactly what context engineering rewards.&lt;/p&gt;

&lt;h2&gt;
  
  
  How you know it's working: measure it
&lt;/h2&gt;

&lt;p&gt;Every technique above is a change to your context, and changes to context are exactly the kind of thing that &lt;em&gt;feels&lt;/em&gt; better while being worse — or vice versa. If your only signal is "I ran a few queries and it seemed fine," you're tuning blind, and you'll ship a regression the day you compact one turn too aggressively and the agent starts forgetting a constraint.&lt;/p&gt;

&lt;p&gt;The fix is the same as anywhere in production ML: an evaluation set. Assemble representative tasks with known good outcomes, and re-run them every time you change how context is managed. Then you can say "compaction at phase boundaries held task success at 0.9 while cutting average tokens 40%" instead of "I think it's better now." Treating &lt;a href="https://cursuri-ai.ro/courses/ai-evals-llm-productie" rel="noopener noreferrer"&gt;evaluation as first-class rather than an afterthought&lt;/a&gt; is what turns context engineering from a craft into engineering — a number that moves when you change something, not a vibe.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting it together
&lt;/h2&gt;

&lt;p&gt;Context engineering isn't a framework you install; it's a posture you adopt toward the model's window. The whole discipline collapses to one principle applied relentlessly: &lt;strong&gt;spend the finite budget on the smallest set of high-signal tokens that does the job.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In practice, for a real agent, that means:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Compact&lt;/strong&gt; the history at natural boundaries so a long run doesn't drown in its own transcript.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Offload&lt;/strong&gt; durable state to external memory instead of carrying it forever.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delegate&lt;/strong&gt; noisy, independent work to sub-agents so the main window stays clean.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retrieve just-in-time&lt;/strong&gt; instead of preloading everything you might need.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Curate the tools&lt;/strong&gt; hard — fewer, sharper, no overlap.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Measure&lt;/strong&gt; with evals so every change is verified, not hoped.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;None of these are exotic, and that's the point. The model is already capable. What makes the capability &lt;em&gt;hold up&lt;/em&gt; over a twenty-step run isn't a cleverer prompt — it's disciplined management of everything the model reads along the way.&lt;/p&gt;

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

&lt;p&gt;Prompt engineering taught us to write one good instruction. Context engineering is what you need the moment that instruction has to survive a long, tool-using, self-accumulating agent run — which is to say, the moment you build anything real. The failure you saw at step twelve was never the model getting dumber. It was the context getting noisier, and no one curating it.&lt;/p&gt;

&lt;p&gt;Adopt the budget mindset, apply the five techniques, and put an eval set behind every change. Do that, and the same model that fell apart at step twelve will run to step fifty and still know exactly what it's doing — because you engineered what it was looking at the whole way.&lt;/p&gt;

&lt;p&gt;The courses linked throughout are part of &lt;a href="https://cursuri-ai.ro/courses/context-engineering-memorie-agenti" rel="noopener noreferrer"&gt;Cursuri-AI.ro&lt;/a&gt;, an AI-learning platform with hands-on, current tracks on context engineering, AI agents, LLM integration, and evaluating AI systems in production.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Sources &amp;amp; further reading:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Anthropic — &lt;a href="https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents" rel="noopener noreferrer"&gt;Effective context engineering for AI agents&lt;/a&gt; (definition, the four context components, tool-set failure modes, compaction and memory)&lt;/li&gt;
&lt;li&gt;Anthropic — &lt;a href="https://www.anthropic.com/engineering/effective-harnesses-for-long-running-agents" rel="noopener noreferrer"&gt;Effective harnesses for long-running agents&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Liu et al. — &lt;a href="https://arxiv.org/abs/2307.03172" rel="noopener noreferrer"&gt;Lost in the Middle: How Language Models Use Long Contexts&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;This article is educational content. Model behavior, context limits, and tooling evolve quickly; validate approaches against your own workloads and current official documentation.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Run LLMs Locally with Ollama in 2026: The Practical Developer Guide</title>
      <dc:creator>galian</dc:creator>
      <pubDate>Sun, 05 Jul 2026 21:11:24 +0000</pubDate>
      <link>https://dev.to/cursuri-ai/run-llms-locally-with-ollama-in-2026-the-practical-developer-guide-48n</link>
      <guid>https://dev.to/cursuri-ai/run-llms-locally-with-ollama-in-2026-the-practical-developer-guide-48n</guid>
      <description>&lt;p&gt;For years, "run the model locally" was the option you mentioned and then didn't take: the models were too weak, the tooling too fiddly, and the cloud APIs too convenient. In 2026 that calculus has genuinely shifted. Open-weight models in the 12–35B range now handle real coding and agent workloads, Apple Silicon got a dedicated inference engine, and Ollama quietly became a drop-in backend for the same tools you already use against cloud APIs — including Claude Code.&lt;/p&gt;

&lt;p&gt;I teach practical AI engineering at &lt;a href="https://cursuri-ai.ro" rel="noopener noreferrer"&gt;Cursuri-AI.ro&lt;/a&gt;, Eastern Europe's AI education platform, and local inference has gone from a curiosity module to one of the questions companies ask us most — usually spelled "how do we use LLMs without sending our data anywhere?" This guide is the answer I give developers: what changed, what hardware you actually need, which models are worth pulling, and how to plug it all into a real workflow.&lt;/p&gt;

&lt;p&gt;As always with this space: versions and model rankings move monthly. Everything below is verified against Ollama's official blog and docs as of early July 2026 — re-check before you build a budget or an architecture on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why local, and why now
&lt;/h2&gt;

&lt;p&gt;Three arguments have survived contact with production; the rest is mostly vibes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Privacy and data residency.&lt;/strong&gt; With a local model, prompts and outputs never leave your machine (or your VPC, if you self-host on a server). For anyone dealing with client data, medical text, legal documents, or GDPR-sensitive workloads, this eliminates the entire "what does the provider do with my data" conversation instead of managing it through contracts. This is the single biggest adoption driver we see in Europe, and it's the backbone of our &lt;a href="https://cursuri-ai.ro/courses/llm-locale-ollama-privacy-self-hosting" rel="noopener noreferrer"&gt;course on local LLMs, self-hosting, and privacy&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost shape.&lt;/strong&gt; Cloud APIs bill per token; local inference bills you once, in hardware you may already own. For high-volume, latency-tolerant workloads — batch classification, summarization pipelines, internal tooling — a mid-range GPU that's already on someone's desk can absorb work that would otherwise be a real monthly line item. (For low-volume or frontier-quality work, cloud still wins. More on that below.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No external dependency.&lt;/strong&gt; A local model doesn't get deprecated, rate-limited, price-changed, or suspended out from under you. After the model-availability surprises of the last year, "at least one workload runs on weights we control" has become a reasonable line item in a resilience plan, not paranoia.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually changed in Ollama in 2026
&lt;/h2&gt;

&lt;p&gt;If you last touched Ollama when it was "a nice wrapper around llama.cpp," the 2026 releases are the reason to look again. All of this is from &lt;a href="https://ollama.com/blog" rel="noopener noreferrer"&gt;Ollama's official blog&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Anthropic API compatibility (January, v0.14.0).&lt;/strong&gt; Ollama now exposes a native Anthropic-style &lt;code&gt;/v1/messages&lt;/code&gt; endpoint. This is the sleeper feature of the year: Anthropic-native tools — most notably Claude Code — can talk to a local model directly, with no proxy or translation layer. There's a matching OpenAI-compatible endpoint too, so Codex and OpenAI-SDK apps work the same way.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ollama launch&lt;/code&gt; (January).&lt;/strong&gt; A single command that configures and starts a coding agent against a local model — &lt;code&gt;ollama launch claude&lt;/code&gt; sets up Claude Code, prompts you to pick a model, and you're in.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Experimental image generation (January).&lt;/strong&gt; Early days, but the scope of "local model" is no longer text-only.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MLX engine on Apple Silicon (March preview → June release).&lt;/strong&gt; Ollama moved its Mac inference path to Apple's MLX framework, which exploits unified memory. Ollama's own framing for the June release: its highest performance on Apple Silicon yet — faster output with reduced memory usage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ollama 0.30 and 0.31 (June).&lt;/strong&gt; Version 0.30 brought improved performance and broader GGUF model compatibility through llama.cpp; 0.31 made Gemma 4 significantly faster on Apple Silicon via multi-token prediction (MTP), enabled by default.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The theme is clear: Ollama is positioning itself less as a hobbyist toy and more as the standard local backend for agentic tooling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started in five minutes
&lt;/h2&gt;

&lt;p&gt;Install (macOS and Windows have installers at &lt;a href="https://ollama.com/download" rel="noopener noreferrer"&gt;ollama.com/download&lt;/a&gt;; on Linux):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://ollama.com/install.sh | sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pull and run a model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull gemma4
ollama run gemma4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's a working local chat. Ollama also starts a local server on port &lt;code&gt;11434&lt;/code&gt;, which is where the interesting part begins — every API-based tool you have can point at it.&lt;/p&gt;

&lt;p&gt;Useful daily commands: &lt;code&gt;ollama ls&lt;/code&gt; (installed models), &lt;code&gt;ollama ps&lt;/code&gt; (what's loaded and where — CPU vs GPU), &lt;code&gt;ollama rm &amp;lt;model&amp;gt;&lt;/code&gt; (free disk space; models are multi-gigabyte).&lt;/p&gt;

&lt;h2&gt;
  
  
  Hardware: the honest sizing guide
&lt;/h2&gt;

&lt;p&gt;The rule of thumb that matters: a model quantized to 4 bits needs very roughly &lt;strong&gt;0.5–0.7 GB of memory per billion parameters&lt;/strong&gt;, plus overhead for context. Everything else follows from that.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Your hardware&lt;/th&gt;
&lt;th&gt;What runs comfortably&lt;/th&gt;
&lt;th&gt;Experience&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;8 GB RAM, no GPU&lt;/td&gt;
&lt;td&gt;3–8B models, quantized&lt;/td&gt;
&lt;td&gt;Fine for chat, drafting, classification. Slow but usable on CPU&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16 GB RAM (Apple Silicon)&lt;/td&gt;
&lt;td&gt;8–14B models&lt;/td&gt;
&lt;td&gt;Good daily-driver territory; MLX made this tier notably faster in 2026&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;24 GB+ (M-series Pro/Max or a 24 GB GPU)&lt;/td&gt;
&lt;td&gt;27–35B models&lt;/td&gt;
&lt;td&gt;Where local coding models get genuinely useful&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;48 GB+ unified memory / multi-GPU&lt;/td&gt;
&lt;td&gt;Large MoE models&lt;/td&gt;
&lt;td&gt;Server-class local inference&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two nuances that save people disappointment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Quantization is why any of this works.&lt;/strong&gt; Models ship in compressed 4–8 bit variants (the GGUF ecosystem) that trade a small quality loss for a 2–4× memory reduction. Ollama's default tags are already quantized — you rarely need to think about it, but it explains why a "27B model" fits in 24 GB.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mixture-of-experts (MoE) models need memory for their &lt;em&gt;total&lt;/em&gt; parameters but compute like their &lt;em&gt;active&lt;/em&gt; subset.&lt;/strong&gt; NVIDIA's Nemotron-3-Super, for example, is a 120B model with 12B active parameters: it runs faster than its size suggests, but you still need the RAM to hold it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Context length eats memory too — an agent session with 32K+ tokens of context adds real overhead on top of the weights. If you're sizing for coding agents, budget for that, not just the model file.&lt;/p&gt;

&lt;h2&gt;
  
  
  The mid-2026 open-weight lineup worth knowing
&lt;/h2&gt;

&lt;p&gt;Rankings churn monthly, so treat this as a map, not a leaderboard. From &lt;a href="https://ollama.com/search" rel="noopener noreferrer"&gt;Ollama's model library&lt;/a&gt;, the families that matter right now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gemma 4&lt;/strong&gt; (12B–31B) — Google's open family, currently the most-pulled model on Ollama. Multimodal, tuned for reasoning and agentic work, and the main beneficiary of the MLX/MTP speedups on Macs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Qwen3.5 / Qwen3.6&lt;/strong&gt; (0.8B–122B) — the ecosystem's Swiss army knife. Qwen3.5 spans everything from edge-tiny to server-large; Qwen3.6 (27B–35B) focuses on agentic coding. &lt;strong&gt;qwen3-coder&lt;/strong&gt; is Ollama's own recommendation for coding-agent use.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GLM-5 family&lt;/strong&gt; — flagship-class open weights (GLM-5 is 744B total / 40B active); strong at coding and long-horizon tasks. Too big for most desktops locally, but available as &lt;code&gt;:cloud&lt;/code&gt; variants (see below) and self-hostable on serious hardware.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nemotron-3-Super&lt;/strong&gt; (120B MoE, 12B active) — NVIDIA's entry, aimed at multi-agent applications.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MiniMax-M3&lt;/strong&gt; — notable for a 1M-token context window, if your workload is long-document analysis.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Specialists&lt;/strong&gt;: GLM-OCR for document understanding, TranslateGemma (4B–27B, 55 languages) for translation, LFM2 (24B) for on-device deployment, Ornith (9B–35B) for agentic coding.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sensible defaults: on a 16 GB Mac, start with &lt;code&gt;gemma4:12b&lt;/code&gt;. On 24 GB+, try &lt;code&gt;qwen3-coder&lt;/code&gt; for code and &lt;code&gt;gemma4:27b&lt;/code&gt; for general work. Then run &lt;em&gt;your&lt;/em&gt; tasks on them — a model's rank on someone's benchmark tells you little about your use case.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that changes your workflow: Ollama as a drop-in API
&lt;/h2&gt;

&lt;p&gt;Ollama's server speaks both major API dialects, which means "switch to a local model" is now a base-URL change, not a rewrite.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OpenAI-compatible&lt;/strong&gt; (&lt;code&gt;/v1&lt;/code&gt;) — any OpenAI-SDK app works:&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;openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&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;OpenAI&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://localhost:11434/v1&lt;/span&gt;&lt;span class="sh"&gt;"&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;ollama&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;resp&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gemma4&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="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;Explain GGUF quantization in one paragraph.&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="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&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="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Anthropic-compatible&lt;/strong&gt; (&lt;code&gt;/v1/messages&lt;/code&gt;) — and this is the one with teeth, because it means &lt;strong&gt;Claude Code runs against local models&lt;/strong&gt;. Per &lt;a href="https://docs.ollama.com/api/anthropic-compatibility" rel="noopener noreferrer"&gt;Ollama's official docs&lt;/a&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="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ANTHROPIC_AUTH_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ollama       &lt;span class="c"&gt;# accepted but not validated&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ANTHROPIC_BASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:11434

claude &lt;span class="nt"&gt;--model&lt;/span&gt; qwen3-coder
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or let Ollama do the wiring for you:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama launch claude
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Honest caveats before you get excited:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ollama recommends &lt;strong&gt;at least 32K tokens of context&lt;/strong&gt; for Claude Code, and its model suggestions for coding are &lt;code&gt;qwen3-coder&lt;/code&gt; locally (30B — you want 24 GB+ of VRAM/unified memory) or &lt;code&gt;glm-4.7:cloud&lt;/code&gt; / &lt;code&gt;minimax-m2.1:cloud&lt;/code&gt; via Ollama's cloud, which keeps the same API surface but runs the weights remotely.&lt;/li&gt;
&lt;li&gt;The compatibility layer doesn't cover everything: &lt;strong&gt;no prompt caching, no token-counting endpoint, no forced tool selection, no batches API, no PDF inputs&lt;/strong&gt; (images must be base64). If your workflow leans on those, you'll feel it.&lt;/li&gt;
&lt;li&gt;A 30B local model is not Opus, and it isn't trying to be. It's "capable pair of hands on an airplane / on confidential code," not "frontier reasoning."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The pattern that actually works in practice is routing: local models for the private, high-volume, or offline work; frontier cloud models for the hard reasoning. Deciding which tier a task belongs to — and building the escalation path — is an architecture skill, and it's exactly the kind of decision we drill in our &lt;a href="https://cursuri-ai.ro/courses/ai-system-architecture" rel="noopener noreferrer"&gt;AI system architecture course&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  When local is the wrong choice
&lt;/h2&gt;

&lt;p&gt;Being a fan of local inference means knowing where it loses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frontier-quality reasoning.&lt;/strong&gt; For the hardest tasks, top cloud models remain clearly ahead of anything you can run on a workstation. If wrong answers are expensive, don't fight this.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Low-volume workloads.&lt;/strong&gt; If you make a few thousand API calls a month, per-token billing is cheaper than any GPU. Local pays off at volume, at privacy constraints, or at both.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ops you don't want.&lt;/strong&gt; A self-hosted model is a service you now run: updates, monitoring, capacity. &lt;code&gt;ollama run&lt;/code&gt; on a laptop is trivial; a team-wide inference server is real infrastructure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multimodal breadth and long-tail capabilities.&lt;/strong&gt; Cloud APIs still bundle more (native PDF understanding, larger tool ecosystems, batch APIs) than the local stack replicates.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One more thing people conflate: running a model locally is different from &lt;em&gt;customizing&lt;/em&gt; one. If your actual goal is a model that speaks your domain language or follows your house style, that's a fine-tuning question — LoRA adapters on an open-weight base, then serving the result through Ollama. That pipeline (when to fine-tune vs when to just engineer the prompt) is its own discipline, covered in our &lt;a href="https://cursuri-ai.ro/courses/fine-tuning-modele-ai" rel="noopener noreferrer"&gt;fine-tuning course&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is Ollama free?
&lt;/h3&gt;

&lt;p&gt;The tool itself is open source and free. The models each carry their own licenses — Gemma, Qwen, GLM and friends have different terms, some with restrictions on commercial use. Check the license tab on the model's Ollama page before you ship a product on it. Ollama's optional cloud models are a paid service.&lt;/p&gt;

&lt;h3&gt;
  
  
  What hardware do I need to run LLMs locally in 2026?
&lt;/h3&gt;

&lt;p&gt;As a rule of thumb at 4-bit quantization: 8 GB of RAM runs 3–8B models, 16 GB runs 8–14B comfortably (especially on Apple Silicon with the MLX engine), and 24 GB+ opens up the 27–35B class where local coding models get genuinely useful. More context = more memory on top of the weights.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can a local model replace GPT or Claude?
&lt;/h3&gt;

&lt;p&gt;For a growing set of tasks — summarization, classification, drafting, routine coding on mid-size codebases — yes, credibly. For frontier reasoning and the highest-stakes accuracy, no. Production teams typically route: local for private/high-volume work, cloud for the hard 10%.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I really use Claude Code with Ollama?
&lt;/h3&gt;

&lt;p&gt;Yes. Since Ollama v0.14.0 (January 2026) there's native Anthropic Messages API compatibility: set &lt;code&gt;ANTHROPIC_BASE_URL=http://localhost:11434&lt;/code&gt;, run &lt;code&gt;claude --model qwen3-coder&lt;/code&gt; — or just &lt;code&gt;ollama launch claude&lt;/code&gt;. Expect a capable assistant, not Opus-level reasoning, and note that prompt caching and a few other API features aren't supported through the compatibility layer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ollama vs llama.cpp vs vLLM — which should I use?
&lt;/h3&gt;

&lt;p&gt;Ollama for developer experience: one command, model management, dual API compatibility. llama.cpp (which powers Ollama's GGUF path) for maximum control and minimal footprint. vLLM for high-throughput multi-user serving on server GPUs. Most developers should start with Ollama and only move down the stack when they hit a concrete limit.&lt;/p&gt;

&lt;h2&gt;
  
  
  The skill underneath the tool
&lt;/h2&gt;

&lt;p&gt;Here's the uncomfortable part: pulling a model is the easy 5%. The value shows up when you can answer the questions around it — which model for which task, how to measure whether the local model is &lt;em&gt;good enough&lt;/em&gt; for your workload instead of guessing, how to build the routing and fallback so privacy-sensitive work stays local while hard problems escalate to a frontier model. That's engineering judgment, not tooling trivia.&lt;/p&gt;

&lt;p&gt;That judgment is what we teach at &lt;a href="https://cursuri-ai.ro" rel="noopener noreferrer"&gt;our AI education platform&lt;/a&gt; — hands-on courses built around real repositories and an interactive AI instructor, covering the full local-to-cloud spectrum: self-hosting and privacy, fine-tuning, architecture, and the agentic workflow on top.&lt;/p&gt;

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

&lt;p&gt;In 2026, local LLMs crossed the line from hobby to infrastructure option. Ollama's dual API compatibility means your existing tools — including Claude Code — can run against open weights with a base-URL change; the MLX engine made a 16 GB MacBook a legitimate inference machine; and the open-weight lineup in the 12–35B range is good enough for a real slice of production work.&lt;/p&gt;

&lt;p&gt;The play isn't "cancel your API keys." It's knowing which slice of your workload belongs on weights you control — then running it there deliberately, measured, with an escalation path for everything else. Start with &lt;code&gt;ollama run gemma4&lt;/code&gt; tonight; you're one evening away from having an informed opinion.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Written by the team at &lt;a href="https://cursuri-ai.ro" rel="noopener noreferrer"&gt;Cursuri-AI.ro&lt;/a&gt; — practical, hands-on AI engineering courses for developers and professionals across Eastern Europe, from local LLMs and self-hosting to agentic coding, evals, and AI system architecture.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sources:&lt;/strong&gt; &lt;a href="https://ollama.com/blog" rel="noopener noreferrer"&gt;Ollama Blog&lt;/a&gt; · &lt;a href="https://docs.ollama.com/api/anthropic-compatibility" rel="noopener noreferrer"&gt;Anthropic API compatibility — Ollama Docs&lt;/a&gt; · &lt;a href="https://ollama.com/blog/claude" rel="noopener noreferrer"&gt;Claude Code with Anthropic API compatibility — Ollama&lt;/a&gt; · &lt;a href="https://ollama.com/search" rel="noopener noreferrer"&gt;Ollama Model Library&lt;/a&gt; · &lt;a href="https://ollama.com/download" rel="noopener noreferrer"&gt;Download Ollama&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Cursor vs GitHub Copilot vs Claude Code: Which AI Coding Tool in 2026?</title>
      <dc:creator>galian</dc:creator>
      <pubDate>Mon, 29 Jun 2026 14:59:29 +0000</pubDate>
      <link>https://dev.to/cursuri-ai/cursor-vs-github-copilot-vs-claude-code-which-ai-coding-tool-in-2026-6c8</link>
      <guid>https://dev.to/cursuri-ai/cursor-vs-github-copilot-vs-claude-code-which-ai-coding-tool-in-2026-6c8</guid>
      <description>&lt;p&gt;If you write code for a living in 2026, you're not asking &lt;em&gt;whether&lt;/em&gt; to use an AI coding tool — you're asking &lt;em&gt;which one&lt;/em&gt;. And the three names that dominate every team's Slack debate are &lt;strong&gt;Cursor&lt;/strong&gt;, &lt;strong&gt;GitHub Copilot&lt;/strong&gt;, and &lt;strong&gt;Claude Code&lt;/strong&gt;. They look similar from a distance (type intent, get code) but they're built on three genuinely different bets about how software gets written.&lt;/p&gt;

&lt;p&gt;I've spent serious time in all three on real, multi-file, multi-repo work — not toy demos — and this is the comparison I wish someone had handed me before I burned a month figuring it out. I write and teach about agentic engineering at &lt;a href="https://cursuri-ai.ro" rel="noopener noreferrer"&gt;Cursuri-AI.ro&lt;/a&gt;, Eastern Europe's AI education platform, so I'll keep this grounded in how these tools actually behave in production, not in launch-day marketing.&lt;/p&gt;

&lt;p&gt;A note before we start: pricing and features in this category change almost monthly. Everything below is a mid-2026 snapshot — verify the current numbers on each tool's official page before you budget for a team.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR — three different philosophies
&lt;/h2&gt;

&lt;p&gt;Here's the one-sentence version of each, before we go deep:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cursor&lt;/strong&gt; is an &lt;strong&gt;AI-native editor&lt;/strong&gt; — it rebuilt the IDE around the agent. Best for developers who want fast, fluid, in-the-flow generation with deep editor integration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Copilot&lt;/strong&gt; is the &lt;strong&gt;ecosystem play&lt;/strong&gt; — it lives where your code, issues, and PRs already are. Best for teams standardized on GitHub who want AI woven through the whole SDLC.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Claude Code&lt;/strong&gt; is the &lt;strong&gt;terminal-first agent&lt;/strong&gt; — it treats the command line as the primary surface and excels at autonomous, multi-step, multi-file work. Best for engineers comfortable orchestrating agents rather than babysitting autocomplete.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of them is "the best." They optimize for different moments, and the real skill is knowing which to reach for. Let's break down why.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Cursor?
&lt;/h2&gt;

&lt;p&gt;Cursor is an AI-native IDE built as a fork of VS Code, so the editor feels instantly familiar — your extensions, keybindings, and themes mostly carry over. What's different is that the AI isn't bolted on as a plugin; the whole editing experience is designed around it.&lt;/p&gt;

&lt;p&gt;Its signature features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tab completion&lt;/strong&gt; — a multi-line, context-aware autocomplete that predicts your &lt;em&gt;next edit&lt;/em&gt;, not just the next token. It's the feature people miss most when they switch away.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composer&lt;/strong&gt; — Cursor's agentic, multi-file editing mode. You describe a change in natural language and it edits across files, runs commands, and iterates. Cursor now ships &lt;strong&gt;Composer 2.5&lt;/strong&gt;, its own model trained specifically for agentic coding, alongside routing to frontier models from Anthropic, OpenAI, and Google.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloud Agents&lt;/strong&gt; — introduced in the Cursor 3.5 release (May 20, 2026), these run in isolated cloud VMs with terminal and browser access, can work across multiple repos in parallel, and report results back to your IDE asynchronously. It's Cursor's answer to "I want the agent working while I do something else."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cursor's center of gravity is &lt;strong&gt;in-the-flow coding&lt;/strong&gt;: you stay in the editor, you see every diff, and the AI keeps pace with your thinking. It rewards developers who want speed without giving up granular control over the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is GitHub Copilot?
&lt;/h2&gt;

&lt;p&gt;Copilot is the most widely deployed of the three, and its biggest advantage is gravitational: it lives inside the tools and platform most teams already use. It runs in VS Code, JetBrains IDEs, Visual Studio, and on GitHub itself.&lt;/p&gt;

&lt;p&gt;By 2026 Copilot has grown well past autocomplete:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Agent mode&lt;/strong&gt; became generally available across both VS Code and JetBrains in March 2026 (previously VS Code only) — a multi-step agent that plans, edits across files, and runs commands inside your editor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The autonomous coding agent&lt;/strong&gt; is the standout. You assign a GitHub issue to Copilot, and it works asynchronously in the background — analyzing the repo, making changes, and opening a ready-to-review pull request. Assign, walk away, come back to a PR. It's the closest any mainstream tool comes to "fire-and-forget" feature work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agentic code review&lt;/strong&gt; gathers full project context before suggesting changes and can hand fixes straight to the coding agent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Spark&lt;/strong&gt; lets you describe an app in plain English and get generated code with a live preview.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The strategic point: Copilot's value isn't any single feature — it's that AI is now threaded through the entire GitHub-centric SDLC, from issue to PR to review. If your team lives on GitHub, that integration is hard to beat.&lt;/p&gt;

&lt;p&gt;One billing change worth flagging: as of June 1, 2026, GitHub moved to &lt;strong&gt;GitHub AI Credits&lt;/strong&gt; (token-based billing) in place of the older Premium Request Units. You're now billed by tokens processed at published model rates, which makes heavy agent usage more transparent — and easier to accidentally overspend if you're not watching.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Claude Code?
&lt;/h2&gt;

&lt;p&gt;Claude Code, from Anthropic, takes the opposite stance from Cursor: instead of building an editor, it makes the &lt;strong&gt;terminal&lt;/strong&gt; the primary surface (with IDE extensions available on top). That sounds minimalist until you see what it does with full shell access.&lt;/p&gt;

&lt;p&gt;Its defining strengths:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Agentic, multi-file, repo-aware work&lt;/strong&gt; from the command line — it reads your codebase, makes coordinated changes across many files, runs your tests, and handles git operations and CI-aware workflows natively.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subagents&lt;/strong&gt; — reusable agent configurations with their own custom prompts and tool access, so you can define a "reviewer," a "test-writer," or a "migration" agent and invoke it on demand.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent teams and multi-agent orchestration&lt;/strong&gt; — coordinate multiple agent sessions working in parallel, with an agent view dashboard to manage them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Claude Code runs on Anthropic's models — currently Claude Opus 4.8 as the default, with the newer Claude Fable 5 as the most capable tier — and it's deliberately model-opinionated rather than a router. The tradeoff is real: it's the most powerful for autonomous, complex tasks, and the least hand-holdy. It assumes you're comfortable thinking like an &lt;em&gt;orchestrator of agents&lt;/em&gt; rather than a writer of lines.&lt;/p&gt;

&lt;p&gt;A word of caution that applies to every agent platform but bites hardest here: &lt;strong&gt;parallel agents multiply your token spend.&lt;/strong&gt; Running ten agents at once consumes your quota roughly ten times faster. The autonomy is exhilarating; the bill is real. Set limits before you scale up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Head-to-head: the dimensions that actually matter
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The editing model
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cursor&lt;/strong&gt; wins on &lt;em&gt;in-editor flow&lt;/em&gt;. Tab completion and inline diffs keep you in control of every change.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Copilot&lt;/strong&gt; wins on &lt;em&gt;breadth of surface&lt;/em&gt; — it's good everywhere your code already is.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Claude Code&lt;/strong&gt; wins on &lt;em&gt;autonomous depth&lt;/em&gt; — it goes furthest without supervision, but you give up the inline, line-by-line feel.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Agents and autonomy
&lt;/h3&gt;

&lt;p&gt;All three now have agents, but the philosophy differs. Cursor's Cloud Agents and Copilot's coding agent are both "assign work, get a result later." Claude Code goes further with explicit multi-agent orchestration and reusable subagents. If your work is increasingly &lt;em&gt;delegating&lt;/em&gt; rather than &lt;em&gt;typing&lt;/em&gt;, this is the dimension to weigh most — and it's exactly the shift that makes understanding &lt;a href="https://cursuri-ai.ro/courses/ai-agents-automatizare" rel="noopener noreferrer"&gt;AI agent architecture and automation&lt;/a&gt; a genuine career edge rather than a nice-to-have.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ecosystem and integration
&lt;/h3&gt;

&lt;p&gt;This is Copilot's home turf. The issue-to-PR loop, native code review, and presence across every major IDE make it the path of least resistance for GitHub-standardized teams. Cursor integrates deeply but inside &lt;em&gt;its&lt;/em&gt; editor; Claude Code integrates deeply with your &lt;em&gt;shell and git&lt;/em&gt;, which is either liberating or intimidating depending on your comfort with the command line.&lt;/p&gt;

&lt;h3&gt;
  
  
  Models
&lt;/h3&gt;

&lt;p&gt;Cursor routes across many frontier models and adds its own Composer model. Copilot offers a model picker. Claude Code is Anthropic-only by design. If model choice matters to you (and for some workloads it genuinely does), Cursor and Copilot give you more knobs; Claude Code bets that a tightly-integrated, top-tier model beats a buffet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pricing, side by side (mid-2026 snapshot)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Entry&lt;/th&gt;
&lt;th&gt;Mid tier&lt;/th&gt;
&lt;th&gt;Power / team&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cursor&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hobby (free)&lt;/td&gt;
&lt;td&gt;Pro — $20/user/mo&lt;/td&gt;
&lt;td&gt;Teams — $40/user/mo (Standard), $120/user/mo (Premium)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GitHub Copilot&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;Pro — $10/mo · Pro+ — $39/mo&lt;/td&gt;
&lt;td&gt;Max — $100/mo · Business / Enterprise seats&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Claude Code&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Pro — $20/mo&lt;/td&gt;
&lt;td&gt;Max 5× — $100/mo&lt;/td&gt;
&lt;td&gt;Max 20× — $200/mo · API pay-per-token&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A few honest caveats on cost:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Copilot&lt;/strong&gt; has the cheapest entry paid tier ($10), but token-based AI Credits mean heavy agent use can climb fast beyond the included allotment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cursor's&lt;/strong&gt; $20 Pro includes a fixed amount of frontier-model usage; power users hit the ceiling and either upgrade or switch to its cheaper Auto/Composer routing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Claude Code's&lt;/strong&gt; Max tiers are priced for sustained, agent-heavy sessions — and again, parallel agents are a multiplier, not an add.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Prices and tiers shift constantly in this category. Treat the table as a snapshot, not a quote, and confirm before committing a team budget.&lt;/p&gt;

&lt;h2&gt;
  
  
  So which one should you choose?
&lt;/h2&gt;

&lt;p&gt;Here's the honest, persona-based answer:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Choose Cursor if&lt;/strong&gt; you want the best in-editor experience, you value fast inline generation and tight control over every diff, and you're happy living inside a (very good) VS Code fork. It's the most natural upgrade for a developer who loves their editor and wants AI to keep pace with their flow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Choose GitHub Copilot if&lt;/strong&gt; your team is standardized on GitHub and you want AI woven through the entire lifecycle — issues, PRs, reviews — across whatever IDEs your team already uses. The issue-to-PR autonomous agent alone can change how a team ships. It's the safest institutional bet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Choose Claude Code if&lt;/strong&gt; you're comfortable in the terminal, your work skews toward complex multi-file refactors and autonomous tasks, and you want to orchestrate agents rather than supervise autocomplete. It has the highest ceiling for autonomy — and asks the most of you in return.&lt;/p&gt;

&lt;p&gt;And the answer most senior engineers actually land on? &lt;strong&gt;More than one.&lt;/strong&gt; Plenty of us keep Cursor open for flow-state editing, lean on Copilot inside the GitHub workflow, and fire up Claude Code for the gnarly autonomous jobs. The tools overlap, but they're not redundant — they're a toolkit. The real meta-skill isn't loyalty to one editor; it's &lt;strong&gt;fluency across the category&lt;/strong&gt; so you instinctively reach for the right one per task.&lt;/p&gt;

&lt;h2&gt;
  
  
  The skill underneath the tools
&lt;/h2&gt;

&lt;p&gt;Here's the uncomfortable truth that the demos hide: these tools amplify the engineer you already are. Point a powerful agent at a vague intent and you get a fast, confident wall of code you didn't design and can't fully maintain. The developers getting outsized leverage from Cursor, Copilot, and Claude Code aren't the ones who learned the keyboard shortcuts — they're the ones who understand agent architecture, context engineering, and how to specify intent precisely enough that autonomy becomes an asset instead of a liability.&lt;/p&gt;

&lt;p&gt;That foundation is exactly what we build at &lt;a href="https://cursuri-ai.ro" rel="noopener noreferrer"&gt;our AI education platform&lt;/a&gt; for Eastern Europe — practical, project-based courses taught around real repositories with an interactive AI instructor, not slide decks. If you want to go from "I use these tools" to "I get serious leverage from them," we maintain dedicated, hands-on tracks for &lt;a href="https://cursuri-ai.ro/courses/cursor-pro" rel="noopener noreferrer"&gt;using Cursor as a pro&lt;/a&gt; and for &lt;a href="https://cursuri-ai.ro/courses/claude-code-mastery-coding-agentic" rel="noopener noreferrer"&gt;agentic coding with Claude Code&lt;/a&gt; — both built around real multi-file, real-repo workflows rather than toy examples.&lt;/p&gt;

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

&lt;p&gt;In 2026, "AI coding tool" isn't one product category — it's three philosophies wearing similar clothes. Cursor bet on the editor, Copilot bet on the ecosystem, and Claude Code bet on the terminal-native agent. Each is genuinely excellent at the thing it optimized for, and genuinely compromised at the things it didn't.&lt;/p&gt;

&lt;p&gt;So don't ask "which is best." Ask "best at what, for whom, doing which task" — and then build the judgment to switch fluently between them. That judgment, not the tool, is what compounds over a career. Try each one on a real feature, not a demo, and you'll feel the differences fast.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Written by the team at &lt;a href="https://cursuri-ai.ro" rel="noopener noreferrer"&gt;Cursuri-AI.ro&lt;/a&gt; — practical, hands-on AI engineering courses for developers and professionals across Eastern Europe, from agentic coding and AI agents to context engineering and the modern AI-native IDE workflow.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sources:&lt;/strong&gt; &lt;a href="https://cursor.com/docs/models-and-pricing" rel="noopener noreferrer"&gt;Cursor Models &amp;amp; Pricing&lt;/a&gt; · &lt;a href="https://github.com/features/copilot/plans" rel="noopener noreferrer"&gt;GitHub Copilot Plans &amp;amp; Pricing&lt;/a&gt; · &lt;a href="https://docs.github.com/en/copilot/get-started/plans" rel="noopener noreferrer"&gt;GitHub Copilot Plans (Docs)&lt;/a&gt; · &lt;a href="https://claude.com/pricing" rel="noopener noreferrer"&gt;Claude Pricing&lt;/a&gt; · &lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Claude Platform Docs — Pricing&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Stop Vibe-Checking Your LLM: A Developer's Guide to Evals</title>
      <dc:creator>galian</dc:creator>
      <pubDate>Mon, 22 Jun 2026 08:24:22 +0000</pubDate>
      <link>https://dev.to/cursuri-ai/stop-vibe-checking-your-llm-a-developers-guide-to-evals-3oed</link>
      <guid>https://dev.to/cursuri-ai/stop-vibe-checking-your-llm-a-developers-guide-to-evals-3oed</guid>
      <description>&lt;p&gt;You tweaked the system prompt, ran the same two test questions you always run, the answers looked good, and you shipped. A week later support is forwarding you screenshots of the model confidently doing the exact thing your prompt was supposed to stop. You never saw it, because "did it get better?" was answered by vibes.&lt;/p&gt;

&lt;p&gt;This is the single most common failure mode in shipping LLM features, and it has nothing to do with which model you picked. &lt;strong&gt;If your only quality gate is reading a handful of outputs and nodding, every change you make is a coin flip.&lt;/strong&gt; You can't tell whether a prompt edit helped, hurt, or just moved the failures somewhere you didn't look. Evals are how you replace the nod with a number.&lt;/p&gt;

&lt;p&gt;This is a practical guide to building that number — from a 30-row eval set you can write this afternoon, through code-based checks and LLM-as-judge scoring, to wiring the whole thing into CI so regressions get blocked instead of discovered by users. No new framework to adopt; just the discipline that separates a demo from a system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why you can't just &lt;code&gt;assert output == expected&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Traditional tests work because the output space is small and exact. &lt;code&gt;add(2, 2)&lt;/code&gt; is &lt;code&gt;4&lt;/code&gt; or it's a bug. LLM output breaks all three assumptions that make &lt;code&gt;assertEqual&lt;/code&gt; work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It's non-deterministic.&lt;/strong&gt; The same prompt can produce different text on two calls. Even at temperature 0 you are not guaranteed byte-identical output across runs or model versions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's open-ended.&lt;/strong&gt; "Summarize this ticket" has thousands of correct answers. None of them are string-equal to your reference, and that's fine — a good summary isn't &lt;em&gt;the&lt;/em&gt; summary.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It fails softly.&lt;/strong&gt; A wrong answer isn't a stack trace. It's a fluent, plausible, well-formatted paragraph that happens to be incorrect. Nothing crashes. Nothing logs an error.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the goal of an eval isn't "is the output identical to the expected string." It's "does the output satisfy the &lt;em&gt;properties&lt;/em&gt; I care about" — is it grounded in the provided context, does it stay on policy, does it actually answer the question, is it valid JSON. You're testing behavior against criteria, not bytes against bytes. Once that clicks, the rest is mechanics.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with the eval set, not the metric
&lt;/h2&gt;

&lt;p&gt;The instinct is to reach for a fancy metric first. Wrong order. The asset that makes everything else work is a small, representative &lt;strong&gt;eval set&lt;/strong&gt;: a fixed collection of inputs paired with what a good output looks like (or the criteria a good output must meet). This is your golden dataset, your regression suite, your source of truth.&lt;/p&gt;

&lt;p&gt;You do not need thousands of examples to start. &lt;strong&gt;Thirty to fifty well-chosen pairs&lt;/strong&gt; turn LLM tuning from vibes into engineering, because now every change is measured against the same fixed bar. Build the set like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mine real failures.&lt;/strong&gt; Every time the system gets something wrong in dev or prod, that exact input goes into the eval set with a note on what the right behavior is. Your bug reports &lt;em&gt;are&lt;/em&gt; your test cases. This is the highest-signal source you have.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cover the categories, not just the happy path.&lt;/strong&gt; Easy questions, ambiguous ones, adversarial ones, out-of-scope ones ("I don't know" is the correct answer and you should test that it says so), and the edge cases specific to your domain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Freeze it and version it.&lt;/strong&gt; The eval set lives in your repo next to the code. When you add a case, that's a commit. A moving target can't measure progress.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep a holdout.&lt;/strong&gt; If you start tuning prompts &lt;em&gt;against&lt;/em&gt; the eval set, you'll overfit to it. Keep a slice you don't look at until you think you're done.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A minimal eval set is just data — JSON, a CSV, a Python list. Here's the shape:&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;# evals/dataset.py
&lt;/span&gt;&lt;span class="n"&gt;EVAL_SET&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;refund-window-basic&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;question&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 our refund window?&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;context&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;Refunds are accepted within 14 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;expected&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;14 days&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;must_not_say&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;30 days&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;no refunds&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;id&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;out-of-scope&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;question&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&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s the weather in Cluj tomorrow?&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;context&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;Refunds are accepted within 14 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;expected&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;REFUSE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# correct behavior: decline, don't invent
&lt;/span&gt;    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;# ... 30-50 of these, grown from real failures
&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 the foundation. Everything below scores outputs against this set.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two halves of every LLM eval
&lt;/h2&gt;

&lt;p&gt;Separate two questions that get mushed together when you eval by eyeball, because they have different fixes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Did the system retrieve / set up the right context?&lt;/strong&gt; (a retrieval or pipeline question)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Given that context, did the model produce a good answer?&lt;/strong&gt; (a generation question)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you're building RAG, the first half is its own discipline — measuring recall@k and precision@k on questions with known relevant documents tells you whether the right chunk even reached the prompt. That's a deep enough topic that it deserves its own treatment; a dedicated &lt;a href="https://cursuri-ai.ro/courses/rag-retrieval-augmented-generation" rel="noopener noreferrer"&gt;course on RAG and retrieval-augmented generation&lt;/a&gt; spends real time there, and the failure modes are different from the ones below. This guide focuses on the second half: scoring the generated answer. The techniques split into two families — &lt;strong&gt;code-based checks&lt;/strong&gt; and &lt;strong&gt;model-based judges&lt;/strong&gt; — and you want both.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code-based checks: cheaper and more reliable than you think
&lt;/h2&gt;

&lt;p&gt;Before you reach for an LLM to grade an LLM, a surprising amount of quality is checkable with plain code. These checks are deterministic, free, instant, and never hallucinate. Use them for everything they can cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Structural validity.&lt;/strong&gt; If the output should be JSON matching a schema, validate it. A response that doesn't parse is a hard failure, no judgment call needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Must-contain / must-not-contain.&lt;/strong&gt; The answer about a 14-day refund window must contain "14" and must not contain "30." Keyword and regex assertions catch a whole class of factual regressions for free.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Format and bounds.&lt;/strong&gt; Length limits, required citations present, no leaked system-prompt text, no forbidden phrases (the "as an AI language model" tax), valid enum values.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Semantic similarity.&lt;/strong&gt; For open-ended answers, embed the output and your reference answer and check cosine similarity passes a threshold. It's fuzzy, but it catches "the answer wandered off topic" without needing a judge model.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# evals/checks.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_structural&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&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;schema_keys&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;bool&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;data&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;output&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;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSONDecodeError&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;False&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;schema_keys&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;check_must_not_say&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&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;banned&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;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;low&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;output&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;return&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;b&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="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;low&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;banned&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The rule of thumb: &lt;strong&gt;anything a regex or a schema can catch, don't pay a model to catch.&lt;/strong&gt; Reserve the expensive, fuzzy judge for the genuinely subjective stuff.&lt;/p&gt;

&lt;h2&gt;
  
  
  LLM-as-judge: powerful, biased, and fixable
&lt;/h2&gt;

&lt;p&gt;For the subjective half — "is this answer faithful to the source?", "is this helpful?", "is the tone right?" — you use a strong model to grade outputs. This is &lt;strong&gt;LLM-as-judge&lt;/strong&gt;, and it scales human-quality judgment to thousands of examples for the price of an API call. Two metrics carry most of the weight for RAG-style apps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Faithfulness / groundedness&lt;/strong&gt; — does every claim in the answer trace back to the provided context, or did the model invent things? This is your hallucination detector.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Answer relevance&lt;/strong&gt; — does the response actually address the question that was asked, or is it a fluent dodge?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The catch: &lt;strong&gt;LLM judges have well-documented biases&lt;/strong&gt;, and if you ignore them your eval numbers are noise dressed up as signal. The big ones, all reported in the research on using models as evaluators:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Position bias&lt;/strong&gt; — when comparing two answers, judges favor the one shown first (or in a fixed slot) regardless of quality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verbosity bias&lt;/strong&gt; — judges tend to rate longer, more elaborate answers higher even when a short answer is more correct.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self-preference&lt;/strong&gt; — a judge model can favor text written in its own style or by its own family.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You don't abandon the technique; you engineer around the bias:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Score against a rubric, not a vibe.&lt;/strong&gt; Ask for a 1–5 score with explicit criteria for each level, and require the judge to output its reasoning &lt;em&gt;before&lt;/em&gt; the score. A judge forced to justify itself is more consistent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For pairwise comparisons, randomize and swap.&lt;/strong&gt; Run each comparison twice with the order flipped; only count it as a win if the judge picks the same answer both times. This cancels position bias directly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Calibrate against humans.&lt;/strong&gt; Hand-label 20–30 examples yourself, run the judge on them, and check it agrees with you. If it doesn't, fix the rubric before trusting it on 2,000. An uncalibrated judge is a random number generator with good grammar.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use a strong model as the judge.&lt;/strong&gt; Grading is harder than answering. Use a current frontier model for the judge even if your app runs on a smaller, cheaper one.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# evals/judge.py — sketch of a rubric-based faithfulness judge
&lt;/span&gt;&lt;span class="n"&gt;JUDGE_PROMPT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;You are grading whether an ANSWER is fully supported by the CONTEXT.

CONTEXT:
{context}

ANSWER:
{answer}

Rules:
- A claim is &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;supported&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; only if the CONTEXT states or directly implies it.
- Outside knowledge does NOT count as support.

First write one sentence of reasoning. Then output a JSON object:
{{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reasoning&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;...&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;faithful&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: true|false}}&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;judge_faithfulness&lt;/span&gt;&lt;span class="p"&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;context&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;answer&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;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;resp&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;complete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;JUDGE_PROMPT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;context&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;answer&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="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&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;resp&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;faithful&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;Designing judges that hold up — picking the rubric, calibrating, knowing when a model is the wrong tool for the grade — is exactly the muscle a &lt;a href="https://cursuri-ai.ro/courses/ai-evals-llm-productie" rel="noopener noreferrer"&gt;course on AI evals in production&lt;/a&gt; builds, because it's the difference between "the new prompt feels better" and "faithfulness went from 0.78 to 0.91 on the holdout."&lt;/p&gt;

&lt;h2&gt;
  
  
  Wire it into CI, or it won't survive contact with deadlines
&lt;/h2&gt;

&lt;p&gt;An eval you run by hand when you remember to is an eval you'll stop running the week things get busy. The whole point is to make regressions &lt;em&gt;impossible to ship silently&lt;/em&gt;, and that means the eval runs automatically on every change to a prompt, a retrieval setting, or a model version.&lt;/p&gt;

&lt;p&gt;The pattern is a regression gate: run the eval set, compute the aggregate score, and &lt;strong&gt;fail the build if the score drops below a threshold&lt;/strong&gt; (or below the last known-good baseline). It looks like an ordinary test suite, because that's what it is.&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;# tests/test_evals.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;evals.dataset&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;EVAL_SET&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;evals.checks&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;check_must_not_say&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;myapp&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;answer_question&lt;/span&gt;

&lt;span class="n"&gt;PASS_THRESHOLD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.90&lt;/span&gt;  &lt;span class="c1"&gt;# 90% of eval cases must pass to ship
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_case&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;case&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;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;answer_question&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;case&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="n"&gt;case&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;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;if&lt;/span&gt; &lt;span class="n"&gt;case&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;expected&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;REFUSE&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;i don&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;t know&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;output&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="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;can&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;t&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;output&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="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;check_must_not_say&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;case&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;must_not_say&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;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;case&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;expected&lt;/span&gt;&lt;span class="sh"&gt;"&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="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;output&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;def&lt;/span&gt; &lt;span class="nf"&gt;test_eval_suite_meets_threshold&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="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;run_case&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="n"&gt;EVAL_SET&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;sum&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="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;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;failed&lt;/span&gt; &lt;span class="o"&gt;=&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="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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ok&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;EVAL_SET&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="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;PASS_THRESHOLD&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;Eval score &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;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; below &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;PASS_THRESHOLD&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. Failed: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;failed&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few practical notes that keep this sane in CI:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pin the model version.&lt;/strong&gt; Provider model IDs update, and an unpinned model means your eval baseline shifts under you for reasons unrelated to your code. Pin it, and treat a model upgrade as its own deliberate eval run.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Budget for cost and flakiness.&lt;/strong&gt; LLM calls cost money and occasionally time out. Cache where you can, run the judge-heavy suite on a schedule rather than every commit if needed, and set a slightly forgiving threshold so one stochastic blip doesn't red-X a good PR.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log the failures, not just the score.&lt;/strong&gt; When the gate trips, the output should name &lt;em&gt;which&lt;/em&gt; cases regressed so the fix is obvious. A bare "0.86 &amp;lt; 0.90" sends you debugging blind.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now a prompt change is a PR with a number attached. The reviewer sees faithfulness went up and refusal rate held steady, or they see it tanked and the build is red. That's the entire difference between hoping and knowing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Five mistakes that quietly poison your evals
&lt;/h2&gt;

&lt;p&gt;Even teams that build evals often undermine them. Watch for these:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Testing only the happy path.&lt;/strong&gt; If every case in your set is a question the system already answers well, your score is a flattering lie. Adversarial and out-of-scope cases are where the signal is.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tuning on your test set.&lt;/strong&gt; Optimize prompts against the same examples you grade on and you'll overfit to them. Keep a holdout you don't peek at.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An uncalibrated judge.&lt;/strong&gt; Trusting an LLM judge you never checked against your own labels is trusting a number you made up. Calibrate first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One giant blended score.&lt;/strong&gt; A single average hides that faithfulness improved while refusals broke. Track metrics &lt;em&gt;separately&lt;/em&gt; so a regression in one can't be masked by a gain in another.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Letting the set rot.&lt;/strong&gt; Your product changes; cases that no longer reflect real usage drag the signal down. Prune and grow the set as part of normal work, the same way you maintain any test suite.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;None of these are exotic. They're the eval equivalent of not testing error paths — obvious in hindsight, easy to skip under deadline.&lt;/p&gt;

&lt;h2&gt;
  
  
  How this connects to the rest of your LLM stack
&lt;/h2&gt;

&lt;p&gt;Evals aren't a standalone chore; they're the measurement layer that makes every other improvement legible. When you tighten a prompt, evals tell you if it worked — which is why &lt;a href="https://cursuri-ai.ro/courses/prompt-engineering-masterclass" rel="noopener noreferrer"&gt;structured prompt engineering&lt;/a&gt; and a real eval loop are two halves of the same skill. When you redesign what goes into the context window — what to include, what to cut, how to order it — evals are how you know the redesign helped rather than just &lt;em&gt;felt&lt;/em&gt; cleaner; that discipline of deciding what earns a place in the prompt is increasingly called context engineering and has &lt;a href="https://cursuri-ai.ro/courses/context-engineering-memorie-agenti" rel="noopener noreferrer"&gt;its own dedicated course&lt;/a&gt;. And when you wire up function calling, multi-tool orchestration, and the production concerns of a real integration, evals are what keep the whole pipeline honest as it grows — the kind of end-to-end build covered in a deeper &lt;a href="https://cursuri-ai.ro/courses/advanced-llm-integration" rel="noopener noreferrer"&gt;course on advanced LLM integration&lt;/a&gt;. The pattern is always the same: build the measurement first, then every change becomes verifiable instead of hopeful.&lt;/p&gt;

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

&lt;p&gt;The teams whose LLM features actually hold up in production aren't using a secret model or a magic prompt. They're disciplined about measurement. They have a versioned eval set grown from real failures, code-based checks for everything a regex can catch, calibrated LLM judges for the subjective rest, and a CI gate that blocks regressions before users find them.&lt;/p&gt;

&lt;p&gt;Start smaller than you think you can. Write thirty cases this afternoon — half of them things your system currently gets &lt;em&gt;wrong&lt;/em&gt; — add three code checks and one rubric-based judge, and put a threshold in your test suite. The first time a red build stops you from shipping a prompt change that would have quietly broken refusals, you'll never go back to vibe-checking. That's the moment an LLM demo becomes an LLM system people can trust.&lt;/p&gt;

&lt;p&gt;The courses linked throughout are part of &lt;a href="https://cursuri-ai.ro/courses/ai-evals-llm-productie" rel="noopener noreferrer"&gt;Cursuri-AI.ro&lt;/a&gt;, an AI-learning platform with hands-on, current tracks on evaluating AI systems in production, prompt engineering, RAG, and advanced LLM integration.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Sources &amp;amp; further reading:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Zheng et al. — &lt;a href="https://arxiv.org/abs/2306.05685" rel="noopener noreferrer"&gt;Judging LLM-as-a-Judge with MT-Bench and Chatbot Arena&lt;/a&gt; (documents position, verbosity, and self-enhancement bias in LLM judges)&lt;/li&gt;
&lt;li&gt;Liu et al. — &lt;a href="https://arxiv.org/abs/2303.16634" rel="noopener noreferrer"&gt;G-Eval: NLG Evaluation using GPT-4 with Better Human Alignment&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Liang et al. — &lt;a href="https://arxiv.org/abs/2211.09110" rel="noopener noreferrer"&gt;Holistic Evaluation of Language Models (HELM)&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;This article is educational content. Techniques and tooling evolve quickly; validate approaches against your own data and current library documentation.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>llm</category>
    </item>
    <item>
      <title>Claude Fable 5: A Developer's Guide to Anthropic's New Top</title>
      <dc:creator>galian</dc:creator>
      <pubDate>Wed, 10 Jun 2026 22:22:18 +0000</pubDate>
      <link>https://dev.to/cursuri-ai/claude-fable-5-a-developers-guide-to-anthropics-new-top-240m</link>
      <guid>https://dev.to/cursuri-ai/claude-fable-5-a-developers-guide-to-anthropics-new-top-240m</guid>
      <description>&lt;p&gt;Anthropic just moved the ceiling again. &lt;strong&gt;Claude Fable 5&lt;/strong&gt; is the company's most powerful, most intelligent model to date — and it isn't "Opus 4.9." It's a &lt;strong&gt;new tier that sits above the entire Opus family&lt;/strong&gt;. If you build with LLMs, that distinction matters: it changes how you think about model routing, cost, and which tasks deserve your most capable (and most expensive) reasoning.&lt;/p&gt;

&lt;p&gt;This is a practical, no-hype guide for developers. We'll cover what Claude Fable 5 actually is, how it slots into Anthropic's 2026 lineup, what changes in the API surface, when the premium is justified, and how to migrate existing code. Everything here is grounded in Anthropic's own model and API documentation — no invented benchmarks.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Claude Fable 5?
&lt;/h2&gt;

&lt;p&gt;Claude Fable 5 is Anthropic's flagship reasoning model, exposed through the API as &lt;code&gt;claude-fable-5&lt;/code&gt;. The headline facts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A new tier above Opus.&lt;/strong&gt; Until now, "Opus" was the top of the Claude lineup. Fable 5 establishes a level above it — positioned for the hardest reasoning, planning, and long-horizon agentic work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1M-token context window&lt;/strong&gt;, with up to &lt;strong&gt;128K tokens of output&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Premium pricing&lt;/strong&gt;: roughly &lt;strong&gt;$10 / $50 per million input / output tokens&lt;/strong&gt; — about double Opus 4.8's $5 / $25. That price tag is the whole point: Fable 5 is a precision tool you point at the problems that justify it, not a default for every call.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adaptive thinking only.&lt;/strong&gt; The fixed "thinking budget" knob is gone. The model decides how much to reason per request.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The mental model to internalize: &lt;strong&gt;Fable 5 is the peak of a four-tier lineup, and capability scales with cost.&lt;/strong&gt; You don't run your whole pipeline on it any more than you'd render every frame of a film at maximum quality regardless of the shot. You route the hard parts to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Fable 5 Fits in the 2026 Anthropic Lineup
&lt;/h2&gt;

&lt;p&gt;Anthropic's current family is a ladder of capability-vs-cost. Picking the right rung per task is one of the highest-leverage habits an AI engineer can build.&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;Role&lt;/th&gt;
&lt;th&gt;Reach for it when…&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Claude Fable 5&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Absolute peak capability; premium price&lt;/td&gt;
&lt;td&gt;The hardest reasoning, planning, cross-cutting refactors, and long-running agent loops where correctness outweighs cost&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Claude Opus 4.8&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Top of the Opus family; a strong default in Claude Code&lt;/td&gt;
&lt;td&gt;Complex day-to-day work — planning, large refactors, tricky debugging — with a better capability/cost ratio than Fable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Claude Sonnet 4.6&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Balanced, fast, 1M context&lt;/td&gt;
&lt;td&gt;The bulk of everyday coding, reading, and iteration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Claude Haiku 4.5&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Light, fast, cheap&lt;/td&gt;
&lt;td&gt;High-volume small operations, classification, auxiliary steps&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The practical takeaway: &lt;strong&gt;model choice is a cost-and-quality lever.&lt;/strong&gt; A well-designed system routes each sub-task to the cheapest model that can do it well, and escalates to Fable 5 only where the payoff is real. If you want a structured, side-by-side breakdown of the 2026 models and how to choose between them, there's a dedicated &lt;a href="https://cursuri-ai.ro/courses/comparatie-modele-ai" rel="noopener noreferrer"&gt;AI model comparison course&lt;/a&gt; that goes deeper than any single table can.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Changes in the API
&lt;/h2&gt;

&lt;p&gt;This is the part developers actually care about. Fable 5 shares the modern Claude request surface (the same one introduced with Opus 4.7/4.8), with a couple of sharp edges worth knowing before you ship.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adaptive thinking, not a token budget
&lt;/h3&gt;

&lt;p&gt;Fable 5 supports a single thinking mode: &lt;strong&gt;adaptive&lt;/strong&gt;. You no longer pass a fixed &lt;code&gt;budget_tokens&lt;/code&gt; value — the model regulates its own reasoning depth.&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;anthropic&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;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-fable-5&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;16000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;thinking&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;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;adaptive&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;        &lt;span class="c1"&gt;# adaptive is the only thinking mode
&lt;/span&gt;    &lt;span class="n"&gt;output_config&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;effort&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;xhigh&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;    &lt;span class="c1"&gt;# strong default for coding/agentic work
&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Refactor this module and add unit tests.&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;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="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;A few things that will save you a debugging session:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Don't send &lt;code&gt;temperature&lt;/code&gt;, &lt;code&gt;top_p&lt;/code&gt;, &lt;code&gt;top_k&lt;/code&gt;, or &lt;code&gt;budget_tokens&lt;/code&gt;.&lt;/strong&gt; They're removed on this generation and return &lt;code&gt;400&lt;/code&gt;. Steer behavior with prompting and the effort parameter instead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't send &lt;code&gt;thinking={"type": "disabled"}&lt;/code&gt; on Fable 5.&lt;/strong&gt; Unlike Opus 4.8/4.7, an explicit &lt;code&gt;disabled&lt;/code&gt; returns &lt;code&gt;400&lt;/code&gt; here. To run without thinking, &lt;strong&gt;omit the &lt;code&gt;thinking&lt;/code&gt; parameter entirely&lt;/strong&gt;. This is the one genuinely new breaking change relative to the Opus 4.x line — easy to miss.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Thinking text is omitted by default.&lt;/strong&gt; Thinking blocks still stream, but their content is empty unless you opt in with &lt;code&gt;thinking={"type": "adaptive", "display": "summarized"}&lt;/code&gt;. If your UI shows reasoning progress, set this or your users will see a long pause before output.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The effort parameter is your real control knob
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;output_config.effort&lt;/code&gt; accepts &lt;code&gt;low&lt;/code&gt;, &lt;code&gt;medium&lt;/code&gt;, &lt;code&gt;high&lt;/code&gt;, &lt;code&gt;xhigh&lt;/code&gt;, and &lt;code&gt;max&lt;/code&gt;. It controls how much the model thinks &lt;em&gt;and&lt;/em&gt; acts — not just thinking depth. For coding and agentic workloads, &lt;strong&gt;&lt;code&gt;xhigh&lt;/code&gt; is the sweet spot&lt;/strong&gt; and is the effort level Claude Code defaults to. Treat effort as something to tune per route: &lt;code&gt;max&lt;/code&gt; for correctness-critical work, &lt;code&gt;medium&lt;/code&gt;/&lt;code&gt;low&lt;/code&gt; for latency-sensitive or simple steps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Large outputs need streaming
&lt;/h3&gt;

&lt;p&gt;With up to 128K output tokens available, non-streaming requests will hit SDK HTTP timeouts well before that ceiling. For anything above ~16K &lt;code&gt;max_tokens&lt;/code&gt;, stream and collect the final message:&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;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-fable-5&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;64000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;thinking&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;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;adaptive&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;output_config&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;effort&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;xhigh&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="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;Generate the full migration plan.&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="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_final_message&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What it still supports
&lt;/h3&gt;

&lt;p&gt;Fable 5 keeps the modern toolbox: &lt;strong&gt;structured outputs&lt;/strong&gt; (&lt;code&gt;output_config.format&lt;/code&gt;), &lt;strong&gt;prompt caching&lt;/strong&gt; (minimum cacheable prefix ~2,048 tokens), &lt;strong&gt;server-side compaction&lt;/strong&gt; for very long conversations, &lt;strong&gt;web search with dynamic filtering&lt;/strong&gt;, and &lt;strong&gt;task budgets&lt;/strong&gt; (beta) for telling an agent how many tokens it has for a full loop. If you're wiring these into a real application, the patterns matter as much as the model — that's the focus of this hands-on course on &lt;a href="https://cursuri-ai.ro/courses/construire-aplicatii-ai-python-sdk" rel="noopener noreferrer"&gt;building AI apps with the Anthropic and OpenAI SDKs&lt;/a&gt;, which walks from raw API calls to a production-shaped product.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fable 5 for Agentic Coding
&lt;/h2&gt;

&lt;p&gt;The reason Fable 5 is interesting to developers specifically is long-horizon agentic execution: multi-file refactors, overnight runs, and tasks that span dozens of tool calls without a human correcting course.&lt;/p&gt;

&lt;p&gt;Three habits get the most out of it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Give the full task spec up front in one well-formed turn.&lt;/strong&gt; Fable 5 plans better when it has the complete goal early; drip-feeding requirements across many turns tends to cost more tokens and sometimes performance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run at high or &lt;code&gt;xhigh&lt;/code&gt; effort with generous &lt;code&gt;max_tokens&lt;/code&gt;.&lt;/strong&gt; Long-horizon coherence comes partly from the model reasoning more at each step — give it room.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Route deliberately.&lt;/strong&gt; Use Fable 5 for the planning and the genuinely hard edits; delegate mechanical or high-volume sub-steps to Sonnet 4.6 or Haiku 4.5.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If terminal-first agentic coding is your world, the workflow discipline — &lt;code&gt;CLAUDE.md&lt;/code&gt; project memory, plan/edit/review loops, hooks as deterministic guardrails, and model routing across the lineup — is exactly what a dedicated &lt;a href="https://cursuri-ai.ro/courses/claude-code-mastery-coding-agentic" rel="noopener noreferrer"&gt;Claude Code mastery course&lt;/a&gt; covers end to end. Agent architecture beyond a single tool (orchestration, delegation, parallelism) is its own discipline, well covered in this &lt;a href="https://cursuri-ai.ro/courses/ai-agents-automatizare" rel="noopener noreferrer"&gt;course on designing autonomous AI agents&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Context is a resource, even at 1M tokens
&lt;/h3&gt;

&lt;p&gt;A 1M-token window is not a license to dump everything into context. Irrelevant context dilutes the model's attention and costs tokens on every turn, no matter how capable the model is. The skill that separates engineers who "get lucky" with agents from those who ship reliable ones is deliberate &lt;strong&gt;context engineering&lt;/strong&gt; — what to load, what to compact, what to persist as memory across sessions. It's enough of a topic to warrant &lt;a href="https://cursuri-ai.ro/courses/context-engineering-memorie-agenti" rel="noopener noreferrer"&gt;its own course on context engineering and memory for agents&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Fable 5 Is Actually Worth the Premium
&lt;/h2&gt;

&lt;p&gt;Here's the honest cost reasoning, because "use the best model" is bad engineering advice.&lt;/p&gt;

&lt;p&gt;At roughly &lt;strong&gt;double the per-token cost of Opus 4.8&lt;/strong&gt;, Fable 5 pays off when the &lt;em&gt;cost of a wrong answer&lt;/em&gt; is high relative to the token bill:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Worth it:&lt;/strong&gt; a complex cross-service refactor where a subtle regression costs hours of human review; a planning step that determines the trajectory of a long agent run; an analysis where correctness is non-negotiable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not worth it:&lt;/strong&gt; routine edits, summaries, classifications, and the long tail of mechanical sub-tasks — those belong on Sonnet 4.6 or Haiku 4.5.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A useful rule of thumb: let &lt;strong&gt;Fable 5 plan and decide&lt;/strong&gt;, and let cheaper models &lt;strong&gt;execute&lt;/strong&gt; the parts that are already well-specified. That keeps your bill proportional to difficulty instead of flat-out maximal.&lt;/p&gt;

&lt;p&gt;The other lever is effort. Because effort matters more on this generation than on any prior Opus, a Fable 5 call at &lt;code&gt;medium&lt;/code&gt; effort can be both cheaper and faster than an Opus 4.8 call at &lt;code&gt;xhigh&lt;/code&gt; for some tasks — so benchmark on your own workload rather than assuming "bigger model = always slower and pricier in practice."&lt;/p&gt;

&lt;h2&gt;
  
  
  Migrating from Opus 4.8 / 4.7
&lt;/h2&gt;

&lt;p&gt;If you're already on the modern Claude surface, moving to Fable 5 is mostly a model-ID swap plus a couple of checks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Swap the model string&lt;/strong&gt; to &lt;code&gt;claude-fable-5&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remove &lt;code&gt;budget_tokens&lt;/code&gt;&lt;/strong&gt; if any remain → use &lt;code&gt;thinking={"type": "adaptive"}&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strip &lt;code&gt;temperature&lt;/code&gt; / &lt;code&gt;top_p&lt;/code&gt; / &lt;code&gt;top_k&lt;/code&gt;&lt;/strong&gt; — they &lt;code&gt;400&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Replace last-assistant-turn prefills&lt;/strong&gt; with structured outputs (&lt;code&gt;output_config.format&lt;/code&gt;) or a system-prompt instruction — prefills &lt;code&gt;400&lt;/code&gt; on this generation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit for &lt;code&gt;thinking={"type": "disabled"}&lt;/code&gt;&lt;/strong&gt; — it &lt;code&gt;400&lt;/code&gt;s on Fable 5. Omit &lt;code&gt;thinking&lt;/code&gt; instead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Re-tune &lt;code&gt;effort&lt;/code&gt; per route&lt;/strong&gt; — start at &lt;code&gt;high&lt;/code&gt;, use &lt;code&gt;xhigh&lt;/code&gt; for coding/agentic, reserve &lt;code&gt;max&lt;/code&gt; for correctness-critical work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set &lt;code&gt;display: "summarized"&lt;/code&gt;&lt;/strong&gt; if you surface reasoning in a UI.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Steering this generation is done through prompting and effort rather than sampling parameters, so the quality of your instructions matters more than ever. If your prompts were tuned years ago for older models, they're probably leaving capability on the table — a structured refresh of &lt;a href="https://cursuri-ai.ro/courses/prompt-engineering-masterclass" rel="noopener noreferrer"&gt;prompt engineering fundamentals&lt;/a&gt; tends to pay for itself quickly on a model this capable.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Note on Hype vs. Reality
&lt;/h2&gt;

&lt;p&gt;Two guardrails worth keeping as the launch noise settles:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Fable 5 is the most capable model — not necessarily the default everywhere.&lt;/strong&gt; In Claude Code, for instance, Opus 4.8 remains a strong default; Fable 5 is the tier you select for the hardest work. "Most capable" and "default" are different claims.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version hygiene matters.&lt;/strong&gt; Fable 5 is the current peak, Opus 4.8 is the top of the Opus family, and Opus 4.7 is the previous Opus generation. Anything from the Claude 3.x line (or GPT-4-class / Gemini 2.x models) is outdated and shouldn't be treated as current when you're evaluating tutorials or benchmarks. Always confirm model IDs, limits, and pricing against the official docs, since they shift between releases.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  TL;DR Cheat Sheet
&lt;/h2&gt;

&lt;p&gt;For quick reference when you wire Claude Fable 5 into a real codebase:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Model ID:&lt;/strong&gt; &lt;code&gt;claude-fable-5&lt;/code&gt;. Context window 1M tokens, output up to 128K.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Thinking:&lt;/strong&gt; &lt;code&gt;{"type": "adaptive"}&lt;/code&gt; is the only mode. To run without it, &lt;strong&gt;omit the parameter&lt;/strong&gt; — never send &lt;code&gt;{"type": "disabled"}&lt;/code&gt; (it returns &lt;code&gt;400&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Effort:&lt;/strong&gt; &lt;code&gt;output_config.effort&lt;/code&gt; is your main control — &lt;code&gt;xhigh&lt;/code&gt; for coding and agents, &lt;code&gt;max&lt;/code&gt; when correctness is critical, &lt;code&gt;low&lt;/code&gt;/&lt;code&gt;medium&lt;/code&gt; for simple or latency-sensitive steps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Removed (all &lt;code&gt;400&lt;/code&gt; if sent):&lt;/strong&gt; &lt;code&gt;temperature&lt;/code&gt;, &lt;code&gt;top_p&lt;/code&gt;, &lt;code&gt;top_k&lt;/code&gt;, &lt;code&gt;budget_tokens&lt;/code&gt;, and last-assistant-turn prefills.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reasoning in your UI:&lt;/strong&gt; add &lt;code&gt;"display": "summarized"&lt;/code&gt; to the thinking config, or the thinking text comes back empty.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Large outputs:&lt;/strong&gt; stream anything above ~16K &lt;code&gt;max_tokens&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Routing:&lt;/strong&gt; send the hard reasoning to Fable 5; keep routine and high-volume work on Sonnet 4.6 and Haiku 4.5.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;&lt;strong&gt;Claude Fable 5&lt;/strong&gt; isn't just a bigger Opus — it's a new top tier that reframes how you should think about model routing in 2026. The winning pattern is the same as it's always been, just sharper: use the most capable model where correctness compounds, push everything else down the ladder to cheaper models, and tune effort per route. Master that, and Fable 5 becomes a precision instrument rather than a line item that surprises you on the invoice.&lt;/p&gt;

&lt;p&gt;If you want to go from "I read about it" to "I ship with it," the courses linked throughout are part of &lt;a href="https://cursuri-ai.ro/courses/claude-code-mastery-coding-agentic" rel="noopener noreferrer"&gt;Cursuri-AI.ro&lt;/a&gt;, a Romanian AI-learning platform with deep, hands-on tracks on Claude Code, agent architecture, the Anthropic SDK, context engineering, and model selection — all kept current with the 2026 lineup, Fable 5 included.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Found this useful? Save it, and drop your Fable 5 routing strategy in the comments — what are you sending to the top tier, and what stays on Sonnet?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>llm</category>
      <category>programming</category>
    </item>
    <item>
      <title>Prompt Caching with Claude: How We Cut AI API Costs by 90% in Production (2026 Guide)</title>
      <dc:creator>galian</dc:creator>
      <pubDate>Mon, 01 Jun 2026 09:02:05 +0000</pubDate>
      <link>https://dev.to/cursuri-ai/prompt-caching-with-claude-how-we-cut-ai-api-costs-by-90-in-production-2026-guide-35lo</link>
      <guid>https://dev.to/cursuri-ai/prompt-caching-with-claude-how-we-cut-ai-api-costs-by-90-in-production-2026-guide-35lo</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; — Anthropic's prompt caching gives you a &lt;strong&gt;90% discount&lt;/strong&gt; on cached input tokens and up to &lt;strong&gt;85% lower latency&lt;/strong&gt; on long-context calls. But the wins only show up if you understand cache breakpoints, TTLs, and what actually invalidates the cache. This guide walks through 5 production patterns we use, real benchmarks, and the pitfalls that silently kill your hit rate.&lt;/p&gt;




&lt;h2&gt;
  
  
  The cost problem nobody warns you about
&lt;/h2&gt;

&lt;p&gt;When you ship anything serious with Claude — an agent, a RAG system, a code assistant, a customer support bot — you discover the same uncomfortable truth: &lt;strong&gt;your input token bill dwarfs your output bill&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A typical agent loop looks like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;System prompt: ~3,000 tokens (instructions, persona, constraints)&lt;/li&gt;
&lt;li&gt;Tool definitions: ~4,000 tokens (JSON schemas for 10–20 tools)&lt;/li&gt;
&lt;li&gt;Conversation history: 5,000–50,000 tokens (grows every turn)&lt;/li&gt;
&lt;li&gt;RAG context: 5,000–20,000 tokens per query&lt;/li&gt;
&lt;li&gt;User message: ~200 tokens&lt;/li&gt;
&lt;li&gt;Model output: ~500 tokens&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every single turn, you re-send the same system prompt, the same tool definitions, and most of the conversation history. On Claude Sonnet 4.6 at $3 per million input tokens, a 15,000-token prefix sent across 20 conversation turns costs you &lt;strong&gt;$0.90 per conversation in input alone&lt;/strong&gt; — before you've generated a single useful token of output.&lt;/p&gt;

&lt;p&gt;Multiply that by 10,000 daily active users and you're burning &lt;strong&gt;$9,000/day&lt;/strong&gt; just to re-tokenize content you already sent.&lt;/p&gt;

&lt;p&gt;This is exactly what prompt caching fixes.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Claude's prompt caching actually does
&lt;/h2&gt;

&lt;p&gt;Anthropic's prompt caching lets the API store the internal state for a prefix of your prompt and reuse it on subsequent requests. Two numbers matter:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Pricing relative to base input&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Cache write&lt;/strong&gt; (first time a prefix is seen)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;1.25×&lt;/strong&gt; base input cost&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Cache read&lt;/strong&gt; (subsequent hits)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;0.10×&lt;/strong&gt; base input cost (90% off)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You pay a small one-time premium to write the cache, then every hit after that is 10% of the normal price. The break-even point is &lt;strong&gt;after the second request&lt;/strong&gt; — anything more than one read and you're saving money.&lt;/p&gt;

&lt;h3&gt;
  
  
  The mental model
&lt;/h3&gt;

&lt;p&gt;Think of it as a &lt;strong&gt;prefix tree&lt;/strong&gt; with checkpoints. You mark up to 4 points in your prompt with &lt;code&gt;cache_control&lt;/code&gt;, and Claude caches everything from the start of the prompt up to each breakpoint. On the next request, if the prefix matches &lt;strong&gt;byte-for-byte&lt;/strong&gt;, you get a cache hit.&lt;/p&gt;

&lt;p&gt;The order Claude processes the prompt is fixed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tools → system → messages (oldest → newest)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your cache breakpoints must respect that order. You cannot cache a later block without caching everything before it.&lt;/p&gt;

&lt;h3&gt;
  
  
  The TTL trap
&lt;/h3&gt;

&lt;p&gt;The default cache TTL is &lt;strong&gt;5 minutes&lt;/strong&gt;, refreshed on every read. A 1-hour TTL is available as a premium option (costs more on write, same on read). Most teams over-pay for the 1-hour cache when 5 minutes would have served them fine — if your traffic is steady, every request refreshes the TTL and the cache effectively lives forever.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Want to go deeper on Claude's API mechanics in production? Prompt caching, tool use, batch API, streaming, and cost optimization are covered in depth in the &lt;a href="https://cursuri-ai.ro/courses/advanced-llm-integration" rel="noopener noreferrer"&gt;Advanced LLM Integration course on Cursuri-AI.ro&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Pattern 1: Cache the system prompt and tool definitions
&lt;/h2&gt;

&lt;p&gt;This is the highest-ROI change you can make, and most codebases get it wrong on the first try.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wrong&lt;/strong&gt; (no caching):&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;anthropic&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;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;system&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You are a senior software engineer. [...3000 tokens of instructions...]&lt;/span&gt;&lt;span class="sh"&gt;"&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="p"&gt;[...&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt; &lt;span class="n"&gt;definitions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="mi"&gt;4000&lt;/span&gt; &lt;span class="n"&gt;tokens&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;Refactor this function&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;&lt;strong&gt;Right&lt;/strong&gt; (cached):&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;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;system&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;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;text&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;text&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 software engineer. [...3000 tokens of instructions...]&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;cache_control&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;ephemeral&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;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;read_file&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;...&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="p"&gt;},&lt;/span&gt;
        &lt;span class="c1"&gt;# ... more tools ...
&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;last_tool&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;...&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;cache_control&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;ephemeral&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;  &lt;span class="c1"&gt;# cache breakpoint on the last tool
&lt;/span&gt;        &lt;span class="p"&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="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;Refactor this function&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;Two things to notice:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;cache_control&lt;/code&gt; on the system block&lt;/strong&gt; caches everything up through the system prompt.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;cache_control&lt;/code&gt; on the last tool&lt;/strong&gt; caches everything through the tool definitions — this is critical because tools are evaluated &lt;em&gt;before&lt;/em&gt; system per the processing order above.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Wait — that's actually wrong as stated. Let me correct: because the order is &lt;code&gt;tools → system → messages&lt;/code&gt;, putting &lt;code&gt;cache_control&lt;/code&gt; on the &lt;strong&gt;last tool&lt;/strong&gt; caches just the tools, and putting it on &lt;strong&gt;system&lt;/strong&gt; caches tools + system. You typically only need the system breakpoint; it covers everything before it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reading the response
&lt;/h3&gt;

&lt;p&gt;The API returns cache stats in &lt;code&gt;response.usage&lt;/code&gt;:&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="nf"&gt;print&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;usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cache_creation_input_tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# tokens written to cache (1.25x cost)
&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cache_read_input_tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="c1"&gt;# tokens read from cache (0.10x cost)
&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;input_tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                 &lt;span class="c1"&gt;# uncached tokens (1x cost)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the first request: &lt;code&gt;cache_creation_input_tokens&lt;/code&gt; is high, &lt;code&gt;cache_read_input_tokens&lt;/code&gt; is 0.&lt;br&gt;
On every subsequent request within 5 minutes: &lt;code&gt;cache_creation_input_tokens&lt;/code&gt; is 0, &lt;code&gt;cache_read_input_tokens&lt;/code&gt; is high. That's the win condition.&lt;/p&gt;


&lt;h2&gt;
  
  
  Pattern 2: Cache conversation history with rolling breakpoints
&lt;/h2&gt;

&lt;p&gt;In a multi-turn agent, the conversation grows on every turn. If you only cache the system prompt, you're still re-sending and re-billing every prior turn at full price.&lt;/p&gt;

&lt;p&gt;The trick is to add a &lt;strong&gt;second cache breakpoint&lt;/strong&gt; on the most recent assistant message, so the entire conversation up to that point is cached:&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;build_messages_with_cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_user_message&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    history: list of {&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="s"&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="s"&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="s"&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="s"&gt;: ...}
    new_user_message: str
    &lt;/span&gt;&lt;span class="sh"&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="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;turn&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;history&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;i&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;history&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;# Add cache breakpoint on the last historical message
&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="n"&gt;turn&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;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;text&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;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;turn&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;cache_control&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;ephemeral&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;else&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="n"&gt;turn&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="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;new_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="n"&gt;messages&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now every new turn reads the entire prior conversation from cache. Cost per turn becomes nearly constant instead of growing linearly with conversation length.&lt;/p&gt;

&lt;h3&gt;
  
  
  The 4-breakpoint budget
&lt;/h3&gt;

&lt;p&gt;Claude allows up to &lt;strong&gt;4 cache breakpoints&lt;/strong&gt; per request. A common production layout uses all four:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Breakpoint 1&lt;/strong&gt;: end of tools&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Breakpoint 2&lt;/strong&gt;: end of system prompt&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Breakpoint 3&lt;/strong&gt;: end of "stable" conversation history (turns 1 through N-2)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Breakpoint 4&lt;/strong&gt;: end of "recent" history (turn N-1)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This gives you a layered cache: tools rarely change, system rarely changes, old history never changes, recent history is sliding. Each layer hits or misses independently.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pattern 3: Cache few-shot examples separately from the user query
&lt;/h2&gt;

&lt;p&gt;Few-shot prompting is one of the highest-leverage techniques in production LLM apps — and one of the most expensive if you don't cache. A typical few-shot block with 5–10 examples can run 8,000–15,000 tokens.&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;FEW_SHOT_EXAMPLES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
Example 1:
Input: ...
Output: ...

Example 2:
Input: ...
Output: ...

[... 8 more examples ...]
&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;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;system&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;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;text&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;text&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 classifier. Categorize support tickets.&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;text&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;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;FEW_SHOT_EXAMPLES&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cache_control&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;ephemeral&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;  &lt;span class="c1"&gt;# cache the examples
&lt;/span&gt;        &lt;span class="p"&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="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;user_ticket&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;Critical rule: &lt;strong&gt;put the variable content last&lt;/strong&gt;. Cache only works on prefix matches. If your user-specific data is in the middle of the prompt, everything after it becomes uncacheable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pattern 4: RAG with cached document chunks
&lt;/h2&gt;

&lt;p&gt;RAG systems are notorious for blowing up token bills because the retrieved context is large and unique per query. You can't cache the retrieved chunks themselves (they change), but you &lt;em&gt;can&lt;/em&gt; cache the surrounding framework:&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;rag_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;retrieved_chunks&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;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;system&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;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;text&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;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;SYSTEM_INSTRUCTIONS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# ~2000 tokens, stable
&lt;/span&gt;                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cache_control&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;ephemeral&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;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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Context:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;retrieved_chunks&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&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;Question: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_question&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For RAG with a stable knowledge base (corporate docs, product manuals, codebases), there's a more advanced pattern: &lt;strong&gt;pre-tile your documents into fixed-size cacheable blocks&lt;/strong&gt; and choose your retrieval strategy to favor returning whole blocks rather than slices. You trade some retrieval precision for massive cost savings on hot documents.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you build RAG systems for production, the &lt;a href="https://cursuri-ai.ro/courses/rag-retrieval-augmented-generation" rel="noopener noreferrer"&gt;RAG (Retrieval-Augmented Generation) course on Cursuri-AI.ro&lt;/a&gt; covers caching strategies, retrieval optimization, hybrid search, and eval pipelines end-to-end.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Pattern 5: Cache tool results in long-running agents
&lt;/h2&gt;

&lt;p&gt;Agent loops are caching's sweet spot. An agent runs &lt;code&gt;tool_call → tool_result → tool_call → tool_result&lt;/code&gt; cycles, and each iteration the prompt grows by the new tool result. Without caching, you re-bill the entire history every iteration.&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;agent_loop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;initial_user_message&lt;/span&gt;&lt;span class="p"&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="n"&gt;initial_user_message&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="c1"&gt;# Add cache breakpoint to the latest message
&lt;/span&gt;        &lt;span class="n"&gt;cached_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="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="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="nf"&gt;add_cache_breakpoint&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="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="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;4096&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;system&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;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;text&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;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;SYSTEM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cache_control&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;ephemeral&lt;/span&gt;&lt;span class="sh"&gt;"&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;cached_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;if&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;stop_reason&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;end_turn&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;response&lt;/span&gt;

        &lt;span class="c1"&gt;# Append assistant turn + tool results, loop
&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;tool_results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;execute_tools&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="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;tool_results&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;add_cache_breakpoint&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="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message&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="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;content&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;content&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;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;text&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;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;content&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="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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cache_control&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="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;ephemeral&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="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;message&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;content&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a 15-step agent run with a 4,000-token system prompt and 8,000-token tools, this pattern cuts input cost by &lt;strong&gt;~80–88%&lt;/strong&gt; versus uncached.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Agent loops, tool design, multi-step planning and cost modeling are the focus of the &lt;a href="https://cursuri-ai.ro/courses/ai-agents-automatizare" rel="noopener noreferrer"&gt;AI Agents &amp;amp; Automation course on Cursuri-AI.ro&lt;/a&gt; — built around the same Claude Agent SDK patterns shown here.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Real benchmarks: before vs after
&lt;/h2&gt;

&lt;p&gt;These numbers are from a production code-review agent running on Claude Sonnet 4.6, averaged over 1,000 conversations of 12 turns each.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Uncached&lt;/th&gt;
&lt;th&gt;Cached&lt;/th&gt;
&lt;th&gt;Change&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Avg input tokens per turn&lt;/td&gt;
&lt;td&gt;18,400&lt;/td&gt;
&lt;td&gt;18,400&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Avg billed input cost per turn&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$0.0552&lt;/td&gt;
&lt;td&gt;$0.0061&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;−89%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Avg time-to-first-token&lt;/td&gt;
&lt;td&gt;1,840 ms&lt;/td&gt;
&lt;td&gt;380 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;−79%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Avg total cost per 12-turn conversation&lt;/td&gt;
&lt;td&gt;$0.66&lt;/td&gt;
&lt;td&gt;$0.10&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;−85%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cache hit rate (warm)&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;96.3%&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The latency win surprised us as much as the cost win. Cache reads skip the prompt processing phase entirely, which dominates time-to-first-token for long contexts.&lt;/p&gt;




&lt;h2&gt;
  
  
  The pitfalls that silently kill your hit rate
&lt;/h2&gt;

&lt;p&gt;These are mistakes we've made or seen in production code reviews.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Whitespace and formatting drift
&lt;/h3&gt;

&lt;p&gt;Cache hits require &lt;strong&gt;byte-exact prefix matches&lt;/strong&gt;. If your system prompt is built with f-strings and you add a timestamp, conditional newline, or trailing space, you invalidate the cache:&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;# BREAKS the cache every minute
&lt;/span&gt;&lt;span class="n"&gt;system&lt;/span&gt; &lt;span class="o"&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;You are a helpful assistant. Current time: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&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="c1"&gt;# Works
&lt;/span&gt;&lt;span class="n"&gt;system&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You are a helpful assistant.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="c1"&gt;# Pass time as a separate user message field if needed
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Audit your prompts for hidden variability: locale-formatted numbers, dict iteration order in older Pythons, tool definitions where field order changes between deploys.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Reordering tool definitions
&lt;/h3&gt;

&lt;p&gt;If you generate tool schemas from a dict and the dict iteration order changes between runs, your cache evaporates. &lt;strong&gt;Always sort tool definitions&lt;/strong&gt; before sending:&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;tools&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="nf"&gt;generate_tools&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;t&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Wrong breakpoint placement
&lt;/h3&gt;

&lt;p&gt;Breakpoints must come &lt;strong&gt;after&lt;/strong&gt; the content you want to cache, not before. The breakpoint marks "cache everything up to here." Putting it on the user message instead of the system prompt is a common rookie mistake.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Caching tiny prefixes
&lt;/h3&gt;

&lt;p&gt;There's a minimum cacheable size:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Claude Sonnet &amp;amp; Opus&lt;/strong&gt;: 1,024 tokens&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Claude Haiku&lt;/strong&gt;: 2,048 tokens&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Below the minimum, the &lt;code&gt;cache_control&lt;/code&gt; is silently ignored — the API doesn't error, it just doesn't cache. Always check &lt;code&gt;response.usage.cache_creation_input_tokens &amp;gt; 0&lt;/code&gt; on your first request to confirm the cache actually wrote.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Ignoring the 5-minute TTL on bursty traffic
&lt;/h3&gt;

&lt;p&gt;If your traffic is bursty — heavy during business hours, dead overnight — the 5-minute cache will expire between sessions and you'll pay the write premium every time. For bursty patterns, either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use the 1-hour TTL (more expensive write, same read price)&lt;/li&gt;
&lt;li&gt;Or send a small "keep-alive" request every 4 minutes during expected idle windows&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6. Mixing cached and uncached models
&lt;/h3&gt;

&lt;p&gt;Cache is &lt;strong&gt;model-specific&lt;/strong&gt;. If your code falls back from Sonnet 4.6 to Haiku 4.5 on rate limit, the Haiku call has no cache history. Either keep fallback paths uncached, or build separate caches per model.&lt;/p&gt;




&lt;h2&gt;
  
  
  When NOT to use prompt caching
&lt;/h2&gt;

&lt;p&gt;Caching has overhead. Skip it when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One-shot calls with no shared prefix&lt;/strong&gt; — single-request classification, one-off summarization. The 1.25× write premium is pure loss.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High-variability prompts&lt;/strong&gt; — if each request has different boilerplate, you're paying write premium for nothing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompts below the minimum&lt;/strong&gt; — short prompts can't be cached.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost is already negligible&lt;/strong&gt; — if you spend $20/month on the API, the engineering time to optimize caching costs more than the savings.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A useful heuristic: &lt;strong&gt;if your stable prefix is ≥2,000 tokens AND you make ≥3 requests per 5-minute window with that prefix, cache it.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Putting it together: a production checklist
&lt;/h2&gt;

&lt;p&gt;Before you ship a Claude integration in 2026, run this list:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] System prompt has &lt;code&gt;cache_control&lt;/code&gt; set&lt;/li&gt;
&lt;li&gt;[ ] Tool definitions are sorted and stable&lt;/li&gt;
&lt;li&gt;[ ] User-variable content is at the end of the prompt, not in the middle&lt;/li&gt;
&lt;li&gt;[ ] Cache stats (&lt;code&gt;cache_read_input_tokens&lt;/code&gt;) are logged and dashboarded&lt;/li&gt;
&lt;li&gt;[ ] Cache hit rate is monitored — alert if it drops below 80%&lt;/li&gt;
&lt;li&gt;[ ] No timestamps, request IDs, or random data injected into cached blocks&lt;/li&gt;
&lt;li&gt;[ ] First-request cache write is verified in tests&lt;/li&gt;
&lt;li&gt;[ ] Fallback model paths handle cache absence cleanly&lt;/li&gt;
&lt;li&gt;[ ] 5-minute vs 1-hour TTL choice is documented with reasoning&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Prompt caching is the single highest-leverage cost optimization for Claude in production. The mechanics are simple, but the gotchas — formatting drift, reorder bugs, minimum sizes, TTL mismatches — are where teams leave money on the table.&lt;/p&gt;

&lt;p&gt;If you treat caching as a first-class concern from day one, you ship AI features that are 5–10× cheaper to operate than the naive implementation. If you bolt it on later, you spend weeks chasing cache misses through your logging.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where to go deeper
&lt;/h3&gt;

&lt;p&gt;I write about production AI engineering — Claude API, multi-agent systems, RAG, cost optimization — on &lt;a href="https://cursuri-ai.ro" rel="noopener noreferrer"&gt;Cursuri-AI.ro&lt;/a&gt;, an interactive learning platform with an always-available AI tutor that walks you through every concept and reviews your code. The four courses most relevant to what's in this article:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://cursuri-ai.ro/courses/advanced-llm-integration" rel="noopener noreferrer"&gt;Advanced LLM Integration&lt;/a&gt;&lt;/strong&gt; — Claude API in production: prompt caching, tool use, batch API, streaming, error handling, retries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://cursuri-ai.ro/courses/prompt-engineering-masterclass" rel="noopener noreferrer"&gt;Prompt Engineering Masterclass&lt;/a&gt;&lt;/strong&gt; — structured prompting, few-shot patterns, evaluation, prompt versioning&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://cursuri-ai.ro/courses/ai-agents-automatizare" rel="noopener noreferrer"&gt;AI Agents &amp;amp; Automation&lt;/a&gt;&lt;/strong&gt; — agent loops, tool design, multi-agent orchestration, cost modeling&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://cursuri-ai.ro/courses/rag-retrieval-augmented-generation" rel="noopener noreferrer"&gt;RAG (Retrieval-Augmented Generation)&lt;/a&gt;&lt;/strong&gt; — retrieval, embeddings, hybrid search, caching, eval pipelines&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Course content is delivered in Romanian (the platform's primary audience), but the code, frameworks, and patterns are language-agnostic — the IT Pro track is built specifically for engineers shipping AI in production.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What's your cache hit rate in production?&lt;/strong&gt; Drop a comment with your setup — I'm collecting patterns for a follow-up post on &lt;strong&gt;caching at the multi-tenant scale&lt;/strong&gt; (per-customer cache namespaces, cache warm-up strategies, and the cost model when you have 10,000+ concurrent users).&lt;/p&gt;

&lt;p&gt;If this helped, a ❤️ or a 🦄 keeps it visible for other devs hitting the same cost wall. Follow for more deep-dives on Claude in production.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Related reading:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Anthropic's official prompt caching docs: &lt;a href="https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching" rel="noopener noreferrer"&gt;docs.anthropic.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Claude API pricing: &lt;a href="https://www.anthropic.com/pricing" rel="noopener noreferrer"&gt;anthropic.com/pricing&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Full IT Pro AI engineering catalog: &lt;a href="https://cursuri-ai.ro/courses" rel="noopener noreferrer"&gt;Cursuri-AI.ro/courses&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>AI for Influencers in 2026: How to Build a Content Engine That Runs Itself</title>
      <dc:creator>galian</dc:creator>
      <pubDate>Tue, 19 May 2026 13:34:41 +0000</pubDate>
      <link>https://dev.to/cursuri-ai/ai-for-influencers-in-2026-how-to-build-a-content-engine-that-runs-itself-48h0</link>
      <guid>https://dev.to/cursuri-ai/ai-for-influencers-in-2026-how-to-build-a-content-engine-that-runs-itself-48h0</guid>
      <description>&lt;p&gt;The influencer economy is no longer about who posts the most. It's about who has built the smartest &lt;strong&gt;AI content system&lt;/strong&gt; behind the scenes.&lt;/p&gt;

&lt;p&gt;In 2026, the top 1% of creators aren't outworking everyone else. They're out-engineering them. They've turned what used to be a 60-hour-a-week grind into a streamlined pipeline where AI handles 80% of the production work — and they keep 100% of the creative direction.&lt;/p&gt;

&lt;p&gt;Over the past two years, working with hundreds of creators and educators through &lt;a href="https://cursuri-ai.ro" rel="noopener noreferrer"&gt;Cursuri-AI.ro&lt;/a&gt; — Eastern Europe's leading AI education platform — I've watched this shift happen in real time. The patterns are consistent, the playbook is replicable, and the gap between those who adopt it and those who don't is widening every month.&lt;/p&gt;

&lt;p&gt;This article breaks down exactly how it works, what tools they use, and how you can build the same stack — whether you're an influencer who codes, or a developer building tools for creators.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why AI Changed the Influencer Game (Permanently)
&lt;/h2&gt;

&lt;p&gt;Three years ago, an influencer's competitive advantage was personality plus consistency. Today, that's table stakes.&lt;/p&gt;

&lt;p&gt;The real moat now is &lt;strong&gt;operational leverage&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How fast can you identify a trending topic?&lt;/li&gt;
&lt;li&gt;How quickly can you produce content across 5+ formats?&lt;/li&gt;
&lt;li&gt;How precisely can you target each piece to its platform?&lt;/li&gt;
&lt;li&gt;How much of this can run without your direct involvement?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The creators who answered "all of it, mostly automated" are the ones scaling past 1M followers, 7-figure revenues, and 50+ pieces of content per week — solo or with tiny teams.&lt;/p&gt;

&lt;p&gt;This isn't theoretical. It's already happening. The question is whether you're building the system or watching others build it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 5-Layer AI Stack for Modern Influencers
&lt;/h2&gt;

&lt;p&gt;Every high-output creator I've analyzed runs some version of this five-layer architecture. The tools change. The structure doesn't.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 1: Intelligence (Research &amp;amp; Trend Detection)
&lt;/h3&gt;

&lt;p&gt;Before you create, you need to know what to create.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Monitors trending topics, keywords, and conversations in your niche&lt;/li&gt;
&lt;li&gt;Analyzes competitor content performance&lt;/li&gt;
&lt;li&gt;Identifies content gaps and opportunities&lt;/li&gt;
&lt;li&gt;Surfaces audience questions before they become saturated&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Tools and APIs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Perplexity API&lt;/strong&gt; — for real-time research with citations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exa AI&lt;/strong&gt; — semantic search for niche topics&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Trends API&lt;/strong&gt; + &lt;strong&gt;YouTube Data API&lt;/strong&gt; — for trend signals&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reddit API&lt;/strong&gt; + &lt;strong&gt;Twitter/X API&lt;/strong&gt; — for audience listening&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BuzzSumo&lt;/strong&gt; or &lt;strong&gt;SparkToro&lt;/strong&gt; — for content gap analysis&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pro tip:&lt;/strong&gt; Don't just track what's popular. Track what's &lt;em&gt;about to&lt;/em&gt; become popular by monitoring signal velocity (rate of change), not absolute volume.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 2: Ideation (Concept &amp;amp; Angle Generation)
&lt;/h3&gt;

&lt;p&gt;This is where most creators waste the most time — staring at a blank page deciding what to make.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What AI does well here:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generates 30+ angle variations from a single topic&lt;/li&gt;
&lt;li&gt;Adapts ideas to your specific voice and audience&lt;/li&gt;
&lt;li&gt;Identifies counterintuitive takes that drive engagement&lt;/li&gt;
&lt;li&gt;Maps ideas to platform-specific formats&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Recommended approach:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Build a custom GPT or Claude project trained on:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Your past top-performing content (with metrics)&lt;/li&gt;
&lt;li&gt;Your audience persona and voice guidelines&lt;/li&gt;
&lt;li&gt;Your content pillars and forbidden topics&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 If you've never structured a voice profile before, this is one of the highest-leverage skills you can develop. We dedicate an entire module to it inside &lt;a href="https://cursuri-ai.ro" rel="noopener noreferrer"&gt;our AI for Content Creators track on Cursuri-AI.ro&lt;/a&gt; — including the exact prompts and templates we use internally.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then prompt it like this:&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;anthropic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Anthropic&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;Anthropic&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_content_angles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;topic&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;voice_profile&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;n&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;20&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-opus-4-7&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;4000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="o"&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;You are a content strategist for an influencer with this profile:
        &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;voice_profile&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;

        Generate angles that are specific, counterintuitive, and aligned with their voice.
        Avoid generic takes. Each angle should be testable as a hook.&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="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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Give me &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; distinct angles for content about: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;topic&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;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="n"&gt;angles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generate_content_angles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;building a personal brand in 2026&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;voice_profile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Direct, data-driven, contrarian, B2B-focused&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;25&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;angles&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output of this single function call can fuel a month of content. Cost: ~$0.15.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 3: Production (Multi-Format Content Generation)
&lt;/h3&gt;

&lt;p&gt;This is the heaviest-lifting layer — and where AI compounds value most.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The repurposing principle:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One "pillar" piece (a long-form video, podcast, or article) should generate 10–15 derivative pieces with minimal manual work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample workflow for a 30-minute podcast episode:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Transcription&lt;/strong&gt; → Whisper API or AssemblyAI ($0.36 for 30 min)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Long-form blog post&lt;/strong&gt; → Claude/GPT generates structured article from transcript&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LinkedIn carousel&lt;/strong&gt; → 8–10 slide deck with key insights&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Twitter/X thread&lt;/strong&gt; → 10-tweet thread with the strongest takes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Short-form clips&lt;/strong&gt; → Opus Clip or Riverside AI extracts viral moments&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Newsletter&lt;/strong&gt; → Personalized summary with commentary&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;YouTube Shorts&lt;/strong&gt; → Auto-captioned vertical clips&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quote graphics&lt;/strong&gt; → Designed via Canva API or Bannerbear&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instagram Reels&lt;/strong&gt; → Repurposed clips with platform-native captions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SEO blog series&lt;/strong&gt; → 3–5 articles targeting specific search queries&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Total human time: 1–2 hours of review and approval, instead of 30+ hours of production.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 4: Distribution (Platform-Native Publishing)
&lt;/h3&gt;

&lt;p&gt;Most creators lose performance here by posting the same content identically across platforms. AI fixes this by adapting each piece to the platform's native expectations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Adaptive distribution looks like:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LinkedIn → Professional tone, longer-form, hook in first 2 lines&lt;/li&gt;
&lt;li&gt;Twitter/X → Punchy, opinionated, thread-friendly&lt;/li&gt;
&lt;li&gt;Instagram → Visual-first, emotion-driven captions&lt;/li&gt;
&lt;li&gt;TikTok → Hook in 1 second, vertical, trend-aware&lt;/li&gt;
&lt;li&gt;YouTube → SEO-optimized titles, timestamps, structured descriptions&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Buffer&lt;/strong&gt;, &lt;strong&gt;Hypefury&lt;/strong&gt;, or &lt;strong&gt;Typefully&lt;/strong&gt; — scheduling with AI optimization&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make&lt;/strong&gt; or &lt;strong&gt;n8n&lt;/strong&gt; — custom automation workflows&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Postiz&lt;/strong&gt; (open source) — self-hosted social scheduling&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Layer 5: Optimization (Performance Feedback Loop)
&lt;/h3&gt;

&lt;p&gt;This is the layer most creators skip — and it's the one that compounds the hardest.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to track:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hook performance (which first lines drive scroll-stops?)&lt;/li&gt;
&lt;li&gt;Format performance (which content types convert best per platform?)&lt;/li&gt;
&lt;li&gt;Topic performance (which themes consistently win?)&lt;/li&gt;
&lt;li&gt;Audience signals (which content brings in your ICP vs. tourists?)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How AI helps:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Analyzes patterns across hundreds of posts in seconds&lt;/li&gt;
&lt;li&gt;Identifies non-obvious performance correlations&lt;/li&gt;
&lt;li&gt;Suggests next-week content based on last week's winners&lt;/li&gt;
&lt;li&gt;Drafts variations of top performers for retesting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Build a simple dashboard that ingests your analytics from each platform and feeds it back to your ideation layer. This closes the loop — every post makes the next one smarter.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Minimal Working Example: Content Repurposing Pipeline
&lt;/h2&gt;

&lt;p&gt;Here's a stripped-down Python pipeline that takes a transcript and produces three platform-adapted outputs. Useful as a starting point you can extend.&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;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;from&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;Anthropic&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;Anthropic&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-opus-4-7&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;repurpose_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transcript&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;voice&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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Generate LinkedIn post, Twitter thread, and newsletter from a transcript.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&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;You are an expert content strategist. The creator&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s voice is: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;voice&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;

    From the transcript below, produce THREE outputs in JSON:
    1. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linkedin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: A 200-word LinkedIn post with strong hook
    2. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;twitter_thread&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: A 8-tweet thread (array of strings, max 280 chars each)
    3. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;newsletter&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: A 400-word personal newsletter section

    Each must feel platform-native, not copy-pasted.

    Transcript:
    &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;transcript&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;

    Return only valid JSON.&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="n"&gt;MODEL&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;4000&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="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="k"&gt;return&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;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="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;sample_transcript&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 podcast/video transcript here]&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;voice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Direct, contrarian, B2B-focused, data-driven&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="n"&gt;outputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;repurpose_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sample_transcript&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;voice&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;=== LINKEDIN ===&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;outputs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linkedin&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;=== TWITTER THREAD ===&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;tweet&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;outputs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;twitter_thread&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="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;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;tweet&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="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;=== NEWSLETTER ===&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;outputs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;newsletter&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;Extend this with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Whisper for audio-to-text input&lt;/li&gt;
&lt;li&gt;A queue system (Redis + Celery) for batch processing&lt;/li&gt;
&lt;li&gt;A simple Streamlit UI for non-technical creator team members&lt;/li&gt;
&lt;li&gt;Webhook integration with Buffer or Typefully for direct publishing&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The 5 Mistakes That Kill AI Content Pipelines
&lt;/h2&gt;

&lt;p&gt;I've audited dozens of creator AI workflows. The same mistakes appear over and over.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Treating AI as a Writer Instead of a Drafter
&lt;/h3&gt;

&lt;p&gt;AI-generated text published without human editing is detectable, generic, and erodes trust. Use AI for the first 80%, but always edit the final 20% — that's where your voice lives.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Skipping the Voice Calibration Step
&lt;/h3&gt;

&lt;p&gt;Without a documented voice profile (tone, vocabulary, forbidden phrases, examples), every output regresses to the mean. Spend 4 hours documenting your voice once. It pays back for years. If you want a structured framework for this, we walk through the full process in &lt;a href="https://cursuri-ai.ro" rel="noopener noreferrer"&gt;our AI workflow courses&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Building Without Measurement
&lt;/h3&gt;

&lt;p&gt;Pipelines without analytics are vibes-based content factories. If you can't tell which output formats win, you're optimizing blind.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Over-Automating Distribution
&lt;/h3&gt;

&lt;p&gt;Full automation of posting (no human in the loop) is how creators end up with embarrassing posts going live during global news events. Keep a 1-click approval step at minimum.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Choosing Tools Over Architecture
&lt;/h3&gt;

&lt;p&gt;The creators who win don't have the best tools. They have the clearest workflow. Tools change every quarter. Architecture compounds.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Coming Next (2026–2027)
&lt;/h2&gt;

&lt;p&gt;A few signals worth watching:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Personalized AI clones&lt;/strong&gt; — creators training models on their voice/likeness to scale 1:1 audience interaction&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multimodal generation at scale&lt;/strong&gt; — single prompts producing full video, audio, and graphics in one pass&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI-native platforms&lt;/strong&gt; — new social networks built around AI-generated content as a first-class citizen&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent-driven content ops&lt;/strong&gt; — autonomous agents that research, produce, schedule, and optimize with minimal human input&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The creators preparing for this now — by building modular, API-driven systems — will be the ones operating at unprecedented scale by 2027.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ: AI for Influencers
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q: Do I need to code to use AI as an influencer?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
No. Many top creators use no-code tools (Zapier, Make, ChatGPT, Claude Projects). But knowing even basic Python unlocks 10x more customization.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Will AI-generated content hurt my reach?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Only if it sounds generic. Platforms penalize low-effort content, not AI assistance. Original voice + AI scaffolding consistently outperforms 100% human or 100% AI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: How much should I budget for AI tools?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A solo creator can build a complete stack for $50–150/month. Larger operations run $500–2000/month. ROI is usually measured in weeks, not months.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Is this ethical? Should I disclose AI usage?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Be transparent about &lt;em&gt;what&lt;/em&gt; AI does in your workflow (research, drafting, editing), but you don't need to flag every AI-touched word. The standard: would your audience feel deceived if they saw your process? If no, you're fine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Which AI model should I use as a creator?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
For creative content: Claude tends to lead. For research with citations: Perplexity. For images: Midjourney or Flux. For video: Runway or Sora. Test all of them — they each have strengths.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion: Build the System, Not the Output
&lt;/h2&gt;

&lt;p&gt;The influencer economy is splitting into two clear tiers.&lt;/p&gt;

&lt;p&gt;The first tier still manually crafts every piece of content. They post when they have time. They burn out. They plateau.&lt;/p&gt;

&lt;p&gt;The second tier has built systems. AI handles the heavy lifting. They post consistently across every platform. Their content compounds because their architecture compounds.&lt;/p&gt;

&lt;p&gt;The gap between these two tiers is widening every month. And by 2027, it will be unbridgeable for those who waited too long to start.&lt;/p&gt;

&lt;p&gt;The good news: building your AI content engine doesn't require a team or a six-figure budget. It requires clear thinking, a few APIs, and the willingness to treat content like the engineering problem it actually is.&lt;/p&gt;

&lt;p&gt;Start with one layer. Make it work. Add the next.&lt;/p&gt;

&lt;p&gt;That's how the top 1% built it. And it's how you build it too.&lt;/p&gt;




&lt;h2&gt;
  
  
  Want to Go Deeper?
&lt;/h2&gt;

&lt;p&gt;If this resonated and you want a structured path instead of piecing it together from scattered blog posts and YouTube videos:&lt;/p&gt;

&lt;p&gt;🎓 &lt;strong&gt;&lt;a href="https://cursuri-ai.ro" rel="noopener noreferrer"&gt;Cursuri-AI.ro&lt;/a&gt;&lt;/strong&gt; — Our complete AI education platform covers the entire creator stack: prompting, automation, content pipelines, AI workflows for business, and how to build production-grade AI systems. Interactive courses with an AI tutor that adapts to how you learn — not passive video watching.&lt;/p&gt;

&lt;p&gt;Whether you're a creator looking to scale, a developer building tools for the creator economy, or a business owner figuring out how to integrate AI into your operations — &lt;a href="https://cursuri-ai.ro" rel="noopener noreferrer"&gt;start here&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  About the Author
&lt;/h2&gt;

&lt;p&gt;I'm the founder of &lt;a href="https://cursuri-ai.ro" rel="noopener noreferrer"&gt;Cursuri-AI.ro&lt;/a&gt;, where I help thousands of creators, professionals, and businesses build with AI. I write about AI workflows, content automation, and the engineering side of the creator economy.&lt;/p&gt;

&lt;p&gt;If this article helped, drop a reaction and follow for more deep dives. &lt;strong&gt;What layer of your content stack are you working on right now?&lt;/strong&gt; Let me know in the comments — I read every one.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>contentcreation</category>
      <category>automation</category>
      <category>productivity</category>
    </item>
    <item>
      <title>7 Production Patterns for AI Agents That Don't Break in 2026</title>
      <dc:creator>galian</dc:creator>
      <pubDate>Wed, 13 May 2026 11:38:37 +0000</pubDate>
      <link>https://dev.to/cursuri-ai/7-production-patterns-for-ai-agents-that-dont-break-in-2026-5g83</link>
      <guid>https://dev.to/cursuri-ai/7-production-patterns-for-ai-agents-that-dont-break-in-2026-5g83</guid>
      <description>&lt;p&gt;A demo agent that loops three times, calls one tool, and returns "Hello, I helped you" is easy. A production agent that handles 10k requests a day across paying customers, without lighting your API bill on fire or hallucinating tool arguments at 3am, is a different animal.&lt;/p&gt;

&lt;p&gt;I've shipped AI agents in production for the last 18 months — search, content generation, support triage, document analysis. The same seven patterns keep showing up in every codebase that &lt;em&gt;actually&lt;/em&gt; works. None of them are exotic. Most of them are boring. That's the point: production agents are boring on purpose.&lt;/p&gt;

&lt;p&gt;Here are the patterns, with Python examples you can drop into your own loop today.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The Tool Result Validator
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; LLMs hallucinate tool arguments. They will confidently call &lt;code&gt;send_email(to="user@example.com", subject="Refund", body="...")&lt;/code&gt; when the user never asked for an email. They will pass &lt;code&gt;user_id="123abc"&lt;/code&gt; to a function that requires an integer. They will invent product SKUs that don't exist.&lt;/p&gt;

&lt;p&gt;If your tool layer trusts the model's output, every hallucination becomes a production incident.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pattern:&lt;/strong&gt; Validate tool arguments at the &lt;em&gt;tool boundary&lt;/em&gt;, not inside the tool. Reject early with a structured error the model can recover from.&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;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="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ValidationError&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SendEmailArgs&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="n"&gt;to&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;subject&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;body&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;requires_user_confirmation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&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;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;raw_args&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="n"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TOOL_SCHEMAS&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;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model_validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw_args&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;ValidationError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&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;status&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&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_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;invalid_arguments&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;message&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;Tool call rejected. Fix these fields: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;errors&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="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;send_email&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;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;requires_user_confirmation&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;status&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;pending_confirmation&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;preview&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model_dump&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;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="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Gotcha:&lt;/strong&gt; Always return the validation error &lt;em&gt;back to the model&lt;/em&gt; as a tool result. Don't raise it. The agent can usually self-correct in the next turn — but only if it sees the error.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Bounded Memory
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Naive agent loops accumulate every tool call, every observation, every reasoning step into the conversation history. After 15 turns, you're sending 80k tokens per request. Your latency doubles. Your cost goes up 10x. The model starts losing track of what it was doing because the relevant context is buried under five tool dumps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pattern:&lt;/strong&gt; Treat conversation history as a finite resource. Compress aggressively, summarize old turns, and keep tool outputs out of the main thread when you can.&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;class&lt;/span&gt; &lt;span class="nc"&gt;BoundedMemory&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;__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;max_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_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;summarize_at&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;24_000&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;messages&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="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;max_tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;max_tokens&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;summarize_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;summarize_at&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add&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;message&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="bp"&gt;None&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;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="n"&gt;message&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_token_count&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;summarize_at&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="nf"&gt;_compress&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;_compress&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="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="c1"&gt;# Keep system message + last 4 turns verbatim
&lt;/span&gt;        &lt;span class="n"&gt;keep_recent&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;messages&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;
        &lt;span class="n"&gt;to_summarize&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;messages&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="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;8&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="n"&gt;to_summarize&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;summary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;summarize_with_llm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to_summarize&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;2_000&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;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="n"&gt;self&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="mi"&gt;0&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="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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;earlier_context&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;summary&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;/earlier_context&amp;gt;&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;keep_recent&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Gotcha:&lt;/strong&gt; Don't summarize tool &lt;em&gt;call&lt;/em&gt; messages — the model needs the exact arguments to chain reasoning. Summarize only the &lt;em&gt;observations&lt;/em&gt;, and only when they're old enough that detail no longer matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The Observable Loop
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Your agent is in production. A user complains it gave them garbage. You have... a final string output and a vague memory of what the loop does. Good luck debugging.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pattern:&lt;/strong&gt; Emit a structured event for every state transition in the loop. Every model call, every tool call, every retry, every error. Ship them to whatever observability stack you already use (Datadog, Honeycomb, OpenTelemetry, even just structured JSON to stdout).&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;time&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;contextlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;contextmanager&lt;/span&gt;

&lt;span class="nd"&gt;@contextmanager&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;trace_step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_id&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;step&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="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;span_id&lt;/span&gt; &lt;span class="o"&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;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid4&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="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perf_counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;log_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;step.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;run_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;run_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;span_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;span_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;attrs&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="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;span_id&lt;/span&gt;
        &lt;span class="nf"&gt;log_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;step.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;run_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;run_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;span_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;span_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&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="n"&gt;duration_ms&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perf_counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&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;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;log_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;step.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;run_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;run_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;span_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;span_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&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="n"&gt;error&lt;/span&gt;&lt;span class="o"&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;e&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                  &lt;span class="n"&gt;duration_ms&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perf_counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&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="n"&gt;run_id&lt;/span&gt; &lt;span class="o"&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;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;memory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BoundedMemory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&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;task&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;turn&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MAX_TURNS&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;trace_step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_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;model_call&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;turn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;turn&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="nf"&gt;call_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;memory&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="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&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="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&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;tool_calls&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;call&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;tool_calls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;trace_step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_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;tool_call&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&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;call&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="n"&gt;turn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;turn&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;execute_tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;call&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="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&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;tool&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_call_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;call&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;result&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Max turns exceeded&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Gotcha:&lt;/strong&gt; Include a stable &lt;code&gt;run_id&lt;/code&gt; on &lt;em&gt;every&lt;/em&gt; event. When a customer reports an issue, you want one query that returns the entire trace.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Graceful Degradation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Your agent depends on three external services and a vector store. One of them is having a bad day. Your agent now returns a 500 to the user, even though for &lt;em&gt;this particular query&lt;/em&gt; the broken dependency wasn't actually needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pattern:&lt;/strong&gt; Wrap dependencies in fallback chains. If the primary fails, the agent should know that capability is degraded — not crash.&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;class&lt;/span&gt; &lt;span class="nc"&gt;ToolRegistry&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;__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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tools&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;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Callable&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;health&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;bool&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;register&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;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="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;implementations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Callable&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="n"&gt;self&lt;/span&gt;&lt;span class="p"&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;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;implementations&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;call&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;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;args&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="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;impl&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;self&lt;/span&gt;&lt;span class="p"&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;name&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;impl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&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;health&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;name&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;i&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="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&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;status&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;ok&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;result&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="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tier&lt;/span&gt;&lt;span class="sh"&gt;"&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;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;e&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;health&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;name&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;i&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="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
                &lt;span class="nf"&gt;log_event&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.fallback&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&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tier&lt;/span&gt;&lt;span class="o"&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;error&lt;/span&gt;&lt;span class="o"&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;e&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="k"&gt;continue&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;status&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;degraded&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;message&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;Tool &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; is unavailable. Try a different approach.&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;The crucial bit is the &lt;code&gt;degraded&lt;/code&gt; response — it goes back to the model as a tool result, and a well-prompted agent will re-plan. Maybe it tries a different tool. Maybe it tells the user "I can't check live inventory right now, but here's what I know." Either is better than a 500.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gotcha:&lt;/strong&gt; Surface the degraded status in your prompt. A line like &lt;em&gt;"If a tool returns status=degraded, do not retry it. Acknowledge the limitation in your final response."&lt;/em&gt; prevents the model from looping on a dead service.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. The Cost Circuit Breaker
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; A bug or an adversarial input puts your agent in a tool-calling loop. By the time you notice, you've spent $400 in 20 minutes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pattern:&lt;/strong&gt; Track cumulative cost per run and per session. Hard-stop when limits are exceeded. This is not optional in production — it's the difference between a bad day and a layoff conversation.&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;class&lt;/span&gt; &lt;span class="nc"&gt;CostBudget&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;__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;max_usd_per_run&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.50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_usd_per_user_per_day&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;5.00&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;run_cost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&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;max_run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;max_usd_per_run&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;max_day&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;max_usd_per_user_per_day&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;charge&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;usage&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;model&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="n"&gt;cost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;compute_cost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&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;run_cost&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;cost&lt;/span&gt;
        &lt;span class="k"&gt;if&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;run_cost&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;max_run&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;BudgetExceeded&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;Run exceeded $&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;max_run&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;def&lt;/span&gt; &lt;span class="nf"&gt;precheck_user&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;user_id&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="n"&gt;spent_today&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;redis&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cost:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_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="nf"&gt;today&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="ow"&gt;or&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="k"&gt;if&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;spent_today&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max_day&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;BudgetExceeded&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;User &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; exceeded daily budget&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;&lt;strong&gt;Gotcha:&lt;/strong&gt; Different limits for different surfaces. An internal batch job can have a $5 ceiling per run. A free-tier chat user gets $0.10. A paying enterprise customer gets $2. Hardcoding one number is a footgun.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. The Deterministic Critic
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; "LLM-as-a-judge" sounds clever, but using a model to grade itself is unreliable and slow. Two model calls per output, both hallucinate, both cost money.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pattern:&lt;/strong&gt; For checks you can express as code, &lt;em&gt;use code&lt;/em&gt;. Reserve LLM grading for genuinely subjective dimensions, and only after the deterministic checks pass.&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;class&lt;/span&gt; &lt;span class="nc"&gt;OutputCritic&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;evaluate&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;output&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;context&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="n"&gt;issues&lt;/span&gt; &lt;span class="o"&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;context&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;must_cite_sources&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;re&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="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;\[\d+\]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;issues&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;missing_citations&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;context&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;max_length&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&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;output&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;context&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_length&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="n"&gt;issues&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;too_long&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;BANNED_PHRASES&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;output&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;issues&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;banned_phrase&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;context&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;must_mention&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;missing&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;must_mention&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;k&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="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;output&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="n"&gt;missing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;issues&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;missing_keywords:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;missing&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;if&lt;/span&gt; &lt;span class="n"&gt;issues&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;verdict&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;reject&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;issues&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;issues&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;method&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;deterministic&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;context&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;subjective_check&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;llm_grade&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;subjective_check&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;verdict&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;accept&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;method&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;deterministic&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;When the critic rejects, feed the issues back to the agent as a "revise this" instruction. After two rejections, return whatever you have with a flag — infinite revision loops are their own bug class.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gotcha:&lt;/strong&gt; Don't make the critic too strict. If your accept rate is below 70%, your prompt is broken, not your output.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Stateless Replay (Idempotency)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Your agent half-completed a task — it sent the email, then crashed before logging the result. The user retries. Now they get two emails.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pattern:&lt;/strong&gt; Treat every external side-effect as idempotent by design. Use deterministic IDs derived from the input, dedupe at the tool layer, and make agent runs &lt;em&gt;replayable&lt;/em&gt; from any saved checkpoint.&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;hashlib&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;idempotency_key&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;args&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;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;canonical&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;dumps&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&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_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;args&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;sort_keys&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;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;canonical&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="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()[:&lt;/span&gt;&lt;span class="mi"&gt;16&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_idempotent&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="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&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;run_id&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;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;idempotency_key&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="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;cache_key&lt;/span&gt; &lt;span class="o"&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;tool_result:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;run_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;key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;cached&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;redis&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;cache_key&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;cached&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;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;cached&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;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="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cache_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3600&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now if the agent retries the same step within the run, it gets the cached result. If you persist the cache across runs (with a longer TTL), you get cross-run idempotency too — which is what you want for anything that costs money or sends messages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gotcha:&lt;/strong&gt; Be careful what you put in the idempotency key. Timestamps, request IDs, or random nonces in the args will defeat it. Strip them before hashing.&lt;/p&gt;

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

&lt;p&gt;A production agent loop using all seven patterns is roughly 200 lines of Python. Not glamorous, but it survives. Here's the skeleton:&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;run_agent_production&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&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;user_id&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="n"&gt;run_id&lt;/span&gt; &lt;span class="o"&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;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;budget&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CostBudget&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;budget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;precheck_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;memory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BoundedMemory&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;32_000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&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="n"&gt;SYSTEM_PROMPT&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&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;task&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="n"&gt;critic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OutputCritic&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;turn&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MAX_TURNS&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;trace_step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_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;model_call&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;turn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;turn&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;span&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="nf"&gt;call_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;memory&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="n"&gt;budget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;charge&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;usage&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;model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&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;message&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="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_calls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;verdict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;critic&lt;/span&gt;&lt;span class="p"&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;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="nf"&gt;task_context&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;verdict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;verdict&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;accept&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;
            &lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Revise: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;verdict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;issues&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;span class="k"&gt;continue&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;call&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;tool_calls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;trace_step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_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;tool_call&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&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;call&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="n"&gt;turn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;turn&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arguments&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_idempotent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;call&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="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;run_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&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;tool&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_call_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;call&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;result&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Task incomplete after max turns&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the loop. Drop in your favorite model API (Claude, GPT, open source — patterns work the same), wire up your tools with the validator from pattern 1, and you have something that won't embarrass you in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd Read Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.anthropic.com/research/building-effective-agents" rel="noopener noreferrer"&gt;Anthropic's "Building effective agents" guide&lt;/a&gt; — the canonical reference on when to use agents vs simple workflows.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/openai/openai-agents-python" rel="noopener noreferrer"&gt;OpenAI's Agents SDK docs&lt;/a&gt; — clean reference implementation of multi-agent handoffs.&lt;/li&gt;
&lt;li&gt;For Romanian-speaking developers building agents in production, the &lt;a href="https://cursuri-ai.ro" rel="noopener noreferrer"&gt;AI Agents course on Cursuri-AI.ro&lt;/a&gt; goes deeper on these patterns with hands-on exercises.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've shipped agents in production, what patterns did I miss? Drop them in the comments — I'll add the best ones to a follow-up post.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Written by a developer who has paged themselves at 3am because an agent went into a tool-calling loop. Don't be that developer. Use the circuit breaker.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>aiagents</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Fine-Tuning LLMs in 2026: A Practical Guide for Engineers (LoRA, QLoRA, DPO, GRPO)</title>
      <dc:creator>galian</dc:creator>
      <pubDate>Fri, 01 May 2026 20:31:02 +0000</pubDate>
      <link>https://dev.to/cursuri-ai/fine-tuning-llms-in-2026-a-practical-guide-for-engineers-lora-qlora-dpo-grpo-jjo</link>
      <guid>https://dev.to/cursuri-ai/fine-tuning-llms-in-2026-a-practical-guide-for-engineers-lora-qlora-dpo-grpo-jjo</guid>
      <description>&lt;p&gt;Fine-tuning has gone from "research lab toy" to a &lt;strong&gt;first-class production technique&lt;/strong&gt; for AI engineers. With LoRA-class adapters, modern alignment algorithms (DPO, GRPO, RLVR), and serving stacks like vLLM, you can ship a custom model on a single H100 — sometimes on a single 4090.&lt;/p&gt;

&lt;p&gt;But the question isn't &lt;em&gt;can&lt;/em&gt; you fine-tune. It's: &lt;strong&gt;should you?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This guide is the engineering checklist I wish I'd had two years ago. It covers the decision tree, the modern toolchain, the gotchas, and the EU compliance constraints you can't ignore in 2026.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🇪🇺 Romanian / EU readers: the full hands-on Romanian-language program is at &lt;a href="https://cursuri-ai.ro/courses/fine-tuning-modele-ai" rel="noopener noreferrer"&gt;Fine-Tuning și Adaptarea Modelelor AI — Enterprise Edition&lt;/a&gt;. It includes a complete end-to-end project, EU AI Act governance, and FinOps modeling.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Don't fine-tune first.&lt;/strong&gt; Try prompting → RAG → fine-tuning. In that order.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LoRA / QLoRA&lt;/strong&gt; is the default in 2026. Full fine-tuning is rarely the right call.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alignment ≠ SFT.&lt;/strong&gt; SFT teaches &lt;em&gt;format&lt;/em&gt;; DPO/GRPO/RLVR teach &lt;em&gt;preferences and reasoning&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evaluation is the hard part.&lt;/strong&gt; Loss curves don't tell you if the model is better.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Serving matters.&lt;/strong&gt; A great fine-tune served badly is just an expensive demo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EU AI Act applies.&lt;/strong&gt; Document your data, your evals, and your model card.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  1. When fine-tuning is actually the right tool
&lt;/h2&gt;

&lt;p&gt;Most teams reach for fine-tuning too early. Here's the honest decision tree:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;First try&lt;/th&gt;
&lt;th&gt;Fine-tune only if&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Inconsistent output format&lt;/td&gt;
&lt;td&gt;Prompting + structured outputs&lt;/td&gt;
&lt;td&gt;Format breaks &amp;gt; 5% even with strict prompts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Knowledge cutoff / private data&lt;/td&gt;
&lt;td&gt;&lt;a href="https://cursuri-ai.ro/courses/rag-retrieval-augmented-generation" rel="noopener noreferrer"&gt;RAG (Retrieval-Augmented Generation)&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;RAG retrieves the right chunks but the model still misuses them&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Domain-specific style/voice&lt;/td&gt;
&lt;td&gt;System prompt + few-shot&lt;/td&gt;
&lt;td&gt;You need it baked in across thousands of calls (latency/cost)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Specialized reasoning (math, code, legal)&lt;/td&gt;
&lt;td&gt;Better base model + CoT&lt;/td&gt;
&lt;td&gt;You have a clean preference dataset and need stable behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tool use / agents&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://cursuri-ai.ro/courses/mcp-model-context-protocol" rel="noopener noreferrer"&gt;MCP&lt;/a&gt; + good prompts&lt;/td&gt;
&lt;td&gt;Tool-call accuracy is below your SLA after prompt iteration&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Rule of thumb:&lt;/strong&gt; if you can't articulate &lt;em&gt;what your fine-tune teaches that a 200-line system prompt can't&lt;/em&gt;, you're not ready to fine-tune.&lt;/p&gt;

&lt;p&gt;If you're earlier in the journey, the &lt;a href="https://cursuri-ai.ro/courses/prompt-engineering-masterclass" rel="noopener noreferrer"&gt;Prompt Engineering Masterclass&lt;/a&gt; and &lt;a href="https://cursuri-ai.ro/courses/advanced-llm-integration" rel="noopener noreferrer"&gt;Advanced LLM Integration&lt;/a&gt; cover the cheaper alternatives in depth.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. The 2026 technique landscape
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Full fine-tuning
&lt;/h3&gt;

&lt;p&gt;Updates every parameter. Maximum capacity, maximum cost, maximum risk of catastrophic forgetting. Justified for: foundational training, large domain shifts, or when you own the inference path and the dataset is huge (&amp;gt;1M high-quality examples).&lt;/p&gt;

&lt;h3&gt;
  
  
  LoRA (Low-Rank Adaptation)
&lt;/h3&gt;

&lt;p&gt;The original &lt;a href="https://arxiv.org/abs/2106.09685" rel="noopener noreferrer"&gt;LoRA paper (Hu et al., 2021)&lt;/a&gt; is still required reading. You freeze the base weights and train two small low-rank matrices &lt;code&gt;A&lt;/code&gt; and &lt;code&gt;B&lt;/code&gt; per attention layer. Typical adapter is 0.1–1% of the model's parameters.&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;peft&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LoraConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get_peft_model&lt;/span&gt;

&lt;span class="n"&gt;lora_config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LoraConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                       &lt;span class="c1"&gt;# rank
&lt;/span&gt;    &lt;span class="n"&gt;lora_alpha&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="c1"&gt;# scaling
&lt;/span&gt;    &lt;span class="n"&gt;target_modules&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;q_proj&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;k_proj&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;v_proj&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;o_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;lora_dropout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;bias&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;none&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;task_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;CAUSAL_LM&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;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_peft_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base_model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lora_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;print_trainable_parameters&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# trainable params: 8.4M || all params: 7.2B || trainable%: 0.12
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  QLoRA
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://arxiv.org/abs/2305.14314" rel="noopener noreferrer"&gt;QLoRA (Dettmers et al., 2023)&lt;/a&gt; loads the base model in 4-bit (NF4) and trains LoRA adapters on top. This is what lets you fine-tune a 70B model on a single 80GB GPU. Use &lt;code&gt;bitsandbytes&lt;/code&gt; + &lt;a href="https://huggingface.co/docs/peft" rel="noopener noreferrer"&gt;HuggingFace PEFT&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  DoRA, OLoRA, rsLoRA
&lt;/h3&gt;

&lt;p&gt;Newer variants that decouple magnitude/direction (DoRA), use orthogonal init (OLoRA), or rescale rank (rsLoRA). Marginal gains in most cases — start with vanilla LoRA, only switch if you've measured a problem.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Alignment: SFT is just step one
&lt;/h2&gt;

&lt;p&gt;Supervised Fine-Tuning (SFT) teaches the model &lt;em&gt;what good output looks like&lt;/em&gt;. It does &lt;strong&gt;not&lt;/strong&gt; teach preferences, refusals, or reasoning quality. That's what alignment is for.&lt;/p&gt;

&lt;h3&gt;
  
  
  DPO (Direct Preference Optimization)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://arxiv.org/abs/2305.18290" rel="noopener noreferrer"&gt;DPO (Rafailov et al., 2023)&lt;/a&gt; replaces the RLHF pipeline (reward model + PPO) with a single classification-style loss on preference pairs. Simpler, more stable, and the de facto default in 2026.&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;trl&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;DPOTrainer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DPOConfig&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DPOConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;beta&lt;/span&gt;&lt;span class="o"&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="c1"&gt;# KL regularization
&lt;/span&gt;    &lt;span class="n"&gt;learning_rate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;5e-7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;num_train_epochs&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;per_device_train_batch_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;trainer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DPOTrainer&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;sft_model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ref_model&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="c1"&gt;# PEFT auto-handles reference
&lt;/span&gt;    &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;train_dataset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;preference_dataset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;trainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;train&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  GRPO and RLVR
&lt;/h3&gt;

&lt;p&gt;GRPO (Group Relative Policy Optimization, popularized by DeepSeek-R1) and RLVR (RL with Verifiable Rewards) are the techniques behind the reasoning-model wave. If you're training for math, code, or anything with a programmatic verifier — these matter.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://huggingface.co/docs/trl" rel="noopener noreferrer"&gt;HuggingFace TRL library&lt;/a&gt; now ships first-class support for SFT, DPO, GRPO, and KTO.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. The data pipeline is the moat
&lt;/h2&gt;

&lt;p&gt;A bad dataset will defeat a perfect training loop every time. Things that actually move metrics:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Diversity over volume.&lt;/strong&gt; 5K diverse examples beats 50K near-duplicates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hard negatives.&lt;/strong&gt; For preference data, pairs where chosen and rejected are &lt;em&gt;almost equally good&lt;/em&gt; teach more than obvious wins.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decontamination.&lt;/strong&gt; Strip eval-set leakage from training data. &lt;em&gt;Always.&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Format consistency.&lt;/strong&gt; Tokenize early to catch chat-template mismatches before you waste 10 GPU-hours.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PII and licensing.&lt;/strong&gt; This is where the EU AI Act lives. Document provenance.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  5. The 2026 tooling stack
&lt;/h2&gt;

&lt;p&gt;Here's what a production-grade fine-tuning project looks like today:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Training framework&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/docs/trl" rel="noopener noreferrer"&gt;HuggingFace TRL&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Adapters&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/docs/peft" rel="noopener noreferrer"&gt;HuggingFace PEFT&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quantization&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bitsandbytes&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Distributed&lt;/td&gt;
&lt;td&gt;Accelerate / DeepSpeed ZeRO-3 / FSDP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Experiment tracking&lt;/td&gt;
&lt;td&gt;Weights &amp;amp; Biases or MLflow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Serving&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/vllm-project/vllm" rel="noopener noreferrer"&gt;vLLM&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Eval harness&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;lm-evaluation-harness&lt;/code&gt; + custom domain evals&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Closed-source baseline&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://platform.openai.com/docs/guides/fine-tuning" rel="noopener noreferrer"&gt;OpenAI fine-tuning&lt;/a&gt; for comparison&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Wiring all of this into a real CI/CD lifecycle is what separates a notebook experiment from a deployable system. That's the focus of &lt;a href="https://cursuri-ai.ro/courses/mlops-prototip-productie" rel="noopener noreferrer"&gt;MLOps: Prototype to Production&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Evaluation: where most projects quietly fail
&lt;/h2&gt;

&lt;p&gt;Loss curves go down. The model "feels better." You ship. Production complaints spike. Sound familiar?&lt;/p&gt;

&lt;p&gt;Build a &lt;strong&gt;holistic eval suite&lt;/strong&gt; before you start training:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Capability evals&lt;/strong&gt; — domain-specific tasks scored by rubric.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regression evals&lt;/strong&gt; — verify the model didn't lose abilities (catastrophic forgetting is real).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safety evals&lt;/strong&gt; — refusals, jailbreak resistance, policy adherence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LLM-as-judge&lt;/strong&gt; — useful, but bias-corrected with human spot-checks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost &amp;amp; latency&lt;/strong&gt; — TTFT, throughput, p95 — these &lt;em&gt;are&lt;/em&gt; product metrics.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your eval suite isn't version-controlled and reproducible, you don't have an eval suite. You have vibes.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Serving: the part nobody talks about until it breaks
&lt;/h2&gt;

&lt;p&gt;LoRA adapters can be &lt;strong&gt;hot-swapped&lt;/strong&gt; at inference time. vLLM, SGLang, and TensorRT-LLM all support multi-LoRA serving — meaning you can host one base model and dozens of fine-tuned adapters with near-zero overhead.&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="c"&gt;# vLLM with LoRA adapters&lt;/span&gt;
vllm serve meta-llama/Llama-3.1-8B-Instruct &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--enable-lora&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--lora-modules&lt;/span&gt; legal-adapter&lt;span class="o"&gt;=&lt;/span&gt;./adapters/legal sales-adapter&lt;span class="o"&gt;=&lt;/span&gt;./adapters/sales &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--max-loras&lt;/span&gt; 4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the architectural unlock that makes fine-tuning economically viable for SaaS multi-tenancy.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. EU AI Act: not optional in 2026
&lt;/h2&gt;

&lt;p&gt;If you're shipping in the EU, fine-tuning a foundation model can put you in the &lt;em&gt;deployer&lt;/em&gt; or &lt;em&gt;provider&lt;/em&gt; category under the &lt;a href="https://artificialintelligenceact.eu/" rel="noopener noreferrer"&gt;EU AI Act&lt;/a&gt;. Practical consequences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Model card&lt;/strong&gt; documenting training data, intended use, limitations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Risk assessment&lt;/strong&gt; if the use case touches Annex III (HR, education, critical infrastructure, law enforcement, etc.).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logging&lt;/strong&gt; of significant model updates and eval results.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transparency obligations&lt;/strong&gt; to end users for AI-generated content.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This isn't lawyer paranoia — auditors are already asking. Bake it into your pipeline from day one.&lt;/p&gt;




&lt;h2&gt;
  
  
  9. The mistakes I see most often
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Fine-tuning before exhausting prompting and RAG.&lt;/strong&gt; Cheaper, faster, easier to roll back.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Using &lt;code&gt;r=64&lt;/code&gt; because "bigger is better".&lt;/strong&gt; Most tasks saturate at &lt;code&gt;r=8&lt;/code&gt; to &lt;code&gt;r=16&lt;/code&gt;. Measure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mismatched chat template&lt;/strong&gt; between training and inference. Silent quality killer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Training on the eval set.&lt;/strong&gt; Decontaminate. Then decontaminate again.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skipping the SFT-only baseline.&lt;/strong&gt; You can't claim DPO helped if you didn't measure SFT-only first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ignoring catastrophic forgetting.&lt;/strong&gt; Always run a regression eval against the base model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Forgetting the FinOps math.&lt;/strong&gt; A $400 fine-tune that adds $0.002/request to inference is not a win at 1M requests/day.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Where to go next
&lt;/h2&gt;

&lt;p&gt;If you want a structured path that goes from prompt engineering to deploying fine-tuned models in production:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Foundation:&lt;/strong&gt; &lt;a href="https://cursuri-ai.ro/courses/introducere-ai-engineering" rel="noopener noreferrer"&gt;Introduction to AI Engineering&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Before fine-tuning:&lt;/strong&gt; &lt;a href="https://cursuri-ai.ro/courses/prompt-engineering-masterclass" rel="noopener noreferrer"&gt;Prompt Engineering Masterclass&lt;/a&gt; → &lt;a href="https://cursuri-ai.ro/courses/rag-retrieval-augmented-generation" rel="noopener noreferrer"&gt;RAG: Retrieval-Augmented Generation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The full deep dive:&lt;/strong&gt; &lt;a href="https://cursuri-ai.ro/courses/fine-tuning-modele-ai" rel="noopener noreferrer"&gt;Fine-Tuning and Model Adaptation — Enterprise Edition&lt;/a&gt; (LoRA/QLoRA/DoRA, DPO/GRPO/RLVR, vLLM serving, EU AI Act, end-to-end project)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Productionization:&lt;/strong&gt; &lt;a href="https://cursuri-ai.ro/courses/mlops-prototip-productie" rel="noopener noreferrer"&gt;MLOps: Prototype to Production&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration layer:&lt;/strong&gt; &lt;a href="https://cursuri-ai.ro/courses/mcp-model-context-protocol" rel="noopener noreferrer"&gt;MCP — Model Context Protocol&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Browse the full IT engineering track at &lt;a href="https://cursuri-ai.ro/cursuri/it" rel="noopener noreferrer"&gt;cursuri-ai.ro/cursuri/it&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Closing thought
&lt;/h2&gt;

&lt;p&gt;Fine-tuning in 2026 is no longer about &lt;em&gt;can the model learn the task&lt;/em&gt;. It's about &lt;strong&gt;whether your dataset, eval suite, serving stack, and governance process are good enough to deserve a custom model&lt;/strong&gt;. Get those right, and a single adapter can be the difference between a feature that costs you money and a feature that defines your product.&lt;/p&gt;

&lt;p&gt;If this resonated, I'd love to hear what fine-tuning problem you're actually stuck on — drop it in the comments. 👇&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://cursuri-ai.ro" rel="noopener noreferrer"&gt;Cursuri-AI.ro&lt;/a&gt; — the AI engineering education platform for Romanian and EU professionals.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>rag</category>
      <category>python</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>Claude Opus 4.7 vs GPT-5.5: A Developer's Pragmatic Comparison Guide (2026)</title>
      <dc:creator>galian</dc:creator>
      <pubDate>Tue, 28 Apr 2026 10:03:06 +0000</pubDate>
      <link>https://dev.to/cursuri-ai/claude-opus-47-vs-gpt-55-a-developers-pragmatic-comparison-guide-2026-11jb</link>
      <guid>https://dev.to/cursuri-ai/claude-opus-47-vs-gpt-55-a-developers-pragmatic-comparison-guide-2026-11jb</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; — In 2026, choosing an LLM is no longer about picking "the best model." It's about understanding which model solves &lt;em&gt;your specific problem&lt;/em&gt; at the lowest total cost and risk. Claude Opus 4.7 brings a 1M token context window and exceptional reasoning. GPT-5.5 brings ecosystem maturity and multimodal strength. The right answer for production is almost always &lt;strong&gt;multi-model orchestration&lt;/strong&gt;, not allegiance.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you're a backend engineer, ML engineer, or solutions architect choosing a foundation model in 2026, this guide is for you. No marketing fluff. Just patterns I've validated on real projects.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Quick Note on Honesty
&lt;/h2&gt;

&lt;p&gt;Before we go further: &lt;strong&gt;I'm not going to fabricate specs.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Claude Opus 4.7&lt;/strong&gt; is verified to ship with a &lt;strong&gt;1M token context window&lt;/strong&gt; (Anthropic's official spec).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Claude Opus 4.6&lt;/strong&gt; remains in active production as the cost-efficient predecessor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GPT-5.5&lt;/strong&gt; is OpenAI's current flagship at the time of writing. For exact context window, pricing, and benchmark numbers, &lt;strong&gt;always check OpenAI's official documentation&lt;/strong&gt; — those numbers shift between point releases, and any blog quoting them risks being stale within a month.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article focuses on &lt;strong&gt;architectural and methodological differences&lt;/strong&gt; that age well, not spec-sheet trivia that doesn't.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Comparison Matters Differently in 2026
&lt;/h2&gt;

&lt;p&gt;Three years ago, picking a model meant running it through a weekend benchmark and shipping. Today, the calculus has changed:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Context windows have stopped being a bottleneck.&lt;/strong&gt; With Opus 4.7's 1M token window, the question is no longer "can I fit my codebase?" — it's "should I, given attention dynamics and cost?"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Total Cost of Ownership has become non-trivial.&lt;/strong&gt; API price-per-token is maybe 30% of what you actually pay in production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regulatory pressure is real.&lt;/strong&gt; The EU AI Act and GDPR are no longer theoretical — they shape architecture decisions for any team with European users.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Engineers who still treat model selection as a 2-hour decision are leaving serious money and reliability on the table.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architectural Differences That Actually Matter
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Context Window
&lt;/h3&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;Context Window&lt;/th&gt;
&lt;th&gt;Practical Implication&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Claude Opus 4.7&lt;/td&gt;
&lt;td&gt;1,000,000 tokens&lt;/td&gt;
&lt;td&gt;Full enterprise codebases, long-form legal docs, multi-document RAG without chunking compromises&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Opus 4.6&lt;/td&gt;
&lt;td&gt;(See Anthropic docs)&lt;/td&gt;
&lt;td&gt;Cost-optimized workhorse for everyday agentic workloads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-5.5&lt;/td&gt;
&lt;td&gt;(See OpenAI docs)&lt;/td&gt;
&lt;td&gt;Tight integration with Azure OpenAI, mature tooling ecosystem&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The 1M context window is not just bigger — it changes architectural patterns.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you have a million tokens, you stop building chunked RAG pipelines for many use cases. You stop fighting context truncation. You can pass a full repo, a full deposition, a full quarterly filing — and ask the model to reason over it directly.&lt;/p&gt;

&lt;p&gt;But this comes with a real trade-off: &lt;strong&gt;attention quality degrades unevenly across very long contexts.&lt;/strong&gt; Just because you &lt;em&gt;can&lt;/em&gt; stuff 800K tokens in doesn't mean the model will reliably find the needle. Always run targeted &lt;strong&gt;needle-in-haystack&lt;/strong&gt; evals on &lt;em&gt;your&lt;/em&gt; data structure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reasoning Style
&lt;/h3&gt;

&lt;p&gt;This is hard to quantify but easy to feel after enough projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Claude Opus 4.7&lt;/strong&gt; tends to reason more conservatively. It pushes back on ambiguity, asks clarifying questions, and produces structured outputs that hold up well under JSON schema validation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GPT-5.5&lt;/strong&gt; tends to be more proactive and creative. It will often produce a complete answer where Claude would ask "did you mean X or Y?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Neither is universally better. Conservative reasoning saves you from hallucinated database queries in production. Proactive reasoning ships features faster in a hackathon.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tool Use &amp;amp; Agentic Workflows
&lt;/h3&gt;

&lt;p&gt;Both models support function calling and agentic loops. In my experience:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Claude's tool use feels more deterministic. JSON schemas hold. Parallel tool calls behave predictably.&lt;/li&gt;
&lt;li&gt;GPT's tool use has a more mature ecosystem (Assistants API, more SDK examples, broader community).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're building a &lt;strong&gt;pure agent system&lt;/strong&gt;, both work. If you're integrating into an existing &lt;strong&gt;Azure / Microsoft stack&lt;/strong&gt;, GPT-5.5 has lower friction. If you're building a &lt;strong&gt;regulated workflow with strict guarantees&lt;/strong&gt;, Claude's structured output behavior wins on reliability.&lt;/p&gt;




&lt;h2&gt;
  
  
  When To Choose Each — A Decision Framework
&lt;/h2&gt;

&lt;p&gt;Stop asking "which is best?" Start asking these four questions:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. What problem am I actually solving?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Long-form document reasoning, code analysis at scale, regulated decision support&lt;/strong&gt; → Claude Opus 4.7&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multimodal user-facing features, real-time voice, ecosystem-heavy integrations&lt;/strong&gt; → GPT-5.5&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High-volume cost-sensitive agentic workloads&lt;/strong&gt; → Claude Opus 4.6 (or smaller models)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. What's my failure cost?
&lt;/h3&gt;

&lt;p&gt;A chatbot that recommends the wrong product costs a sale. An assistant that misreads a contract clause costs a lawsuit. Match the model's reliability profile to your downside risk.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Who maintains this in 18 months?
&lt;/h3&gt;

&lt;p&gt;Models get deprecated. Pricing changes. APIs evolve. Pick the model whose &lt;strong&gt;migration path&lt;/strong&gt; you can stomach. If your answer is "we can't migrate" — you've built tech debt, not capability.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. What's my regulatory surface?
&lt;/h3&gt;

&lt;p&gt;For EU-resident users:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;EU AI Act&lt;/strong&gt; classifies systems by risk tier — high-risk systems carry significant compliance overhead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GDPR&lt;/strong&gt; still applies to any prompt containing personal data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vendor concentration risk&lt;/strong&gt; is now a documented audit concern.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Single-vendor architectures are increasingly hard to defend in compliance reviews.&lt;/p&gt;




&lt;h2&gt;
  
  
  Build Your Own Evaluation Harness (Don't Trust Public Benchmarks)
&lt;/h2&gt;

&lt;p&gt;Public benchmarks measure general capability. Your production system needs &lt;em&gt;domain-specific&lt;/em&gt; capability. Here's a minimal evaluation pattern I use:&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;anthropic&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;

&lt;span class="n"&gt;anthropic_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;openai_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&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;evaluate_on_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_id&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;provider&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;task&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;Run a single task against a model and return structured output.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prompt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;expected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;expected&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;provider&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;anthropic&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;anthropic_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="n"&gt;model_id&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;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="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;output&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="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;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# openai
&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;openai_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_id&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="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;output&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="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="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;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;model_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;task_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;task&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;output&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;expected&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;match&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;evaluate_match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expected&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;run_eval_suite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test_cases&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="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;Compare both models on the same tasks.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;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;claude&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;gpt&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;for&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;test_cases&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude&lt;/span&gt;&lt;span class="sh"&gt;"&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="nf"&gt;evaluate_on_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude-opus-4-7&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;anthropic&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;task&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="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt&lt;/span&gt;&lt;span class="sh"&gt;"&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="nf"&gt;evaluate_on_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-5.5&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;openai&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;task&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;results&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few principles for building your eval suite:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Use real production data&lt;/strong&gt; (anonymized). Synthetic tasks lie.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Include adversarial cases&lt;/strong&gt; — ambiguous inputs, near-duplicates, edge cases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Measure cost-per-correct-answer&lt;/strong&gt;, not just accuracy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run it weekly&lt;/strong&gt; — model behavior drifts between point releases.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  The Hidden Costs Nobody Talks About
&lt;/h2&gt;

&lt;p&gt;API price-per-token is the smallest part of your real cost. Here's the full picture:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Cost Layer&lt;/th&gt;
&lt;th&gt;Typical Range&lt;/th&gt;
&lt;th&gt;What Drives It&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Direct API tokens&lt;/td&gt;
&lt;td&gt;20-30% of total&lt;/td&gt;
&lt;td&gt;Pricing tier, prompt size&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Re-prompting on errors&lt;/td&gt;
&lt;td&gt;10-20%&lt;/td&gt;
&lt;td&gt;Model reliability, validation strictness&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Human-in-the-loop validation&lt;/td&gt;
&lt;td&gt;15-30%&lt;/td&gt;
&lt;td&gt;Use case sensitivity, regulatory requirements&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Caching infrastructure&lt;/td&gt;
&lt;td&gt;5-10%&lt;/td&gt;
&lt;td&gt;Architecture, library choices&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vendor migration overhead&lt;/td&gt;
&lt;td&gt;10-25% (when triggered)&lt;/td&gt;
&lt;td&gt;Lock-in level, abstraction quality&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compliance audits&lt;/td&gt;
&lt;td&gt;5-15%&lt;/td&gt;
&lt;td&gt;Regulatory environment, data sensitivity&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;A model that's "20% cheaper at the API" can be 2x more expensive in TCO&lt;/strong&gt; if it triggers more re-prompts or requires heavier human validation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Multi-Model Orchestration: The Pattern That Wins
&lt;/h2&gt;

&lt;p&gt;In 2026, the production-grade answer is rarely "one model for everything." Common patterns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────────┐
│  Router (lightweight model)                                 │
│  ├── Classifies request complexity &amp;amp; sensitivity            │
│  └── Routes to appropriate model                            │
└─────────────────────────────────────────────────────────────┘
            │
   ┌────────┼────────┐
   ▼        ▼        ▼
[Haiku]  [Opus 4.6]  [Opus 4.7]
 cheap    balanced    deep reasoning
 fast     production  complex docs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern routinely cuts costs by &lt;strong&gt;40-60%&lt;/strong&gt; versus single-model architectures, with no quality loss when the router is well-calibrated.&lt;/p&gt;




&lt;h2&gt;
  
  
  Going Deeper: Resources
&lt;/h2&gt;

&lt;p&gt;If you want to go beyond this article and build genuine expertise in model selection, evaluation, and multi-model architecture, I've put together a structured course covering exactly these topics:&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;&lt;a href="https://cursuri-ai.ro/courses/comparatie-modele-ai" rel="noopener noreferrer"&gt;AI Model Comparison 2026 — Enterprise Edition&lt;/a&gt;&lt;/strong&gt; &lt;em&gt;(course is in Romanian)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It covers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Full enterprise evaluation methodology — from benchmark to production&lt;/li&gt;
&lt;li&gt;How to interpret 2026 benchmarks correctly (signal vs. marketing noise)&lt;/li&gt;
&lt;li&gt;Structured selection frameworks based on cost / risk / use case&lt;/li&gt;
&lt;li&gt;Complete landscape: Anthropic, OpenAI, Google, Meta, Mistral&lt;/li&gt;
&lt;li&gt;Multi-model architectures and cost optimization strategies&lt;/li&gt;
&lt;li&gt;Applied case studies with European regulatory context&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🔗 Full platform: &lt;strong&gt;&lt;a href="https://cursuri-ai.ro" rel="noopener noreferrer"&gt;Cursuri-AI.ro&lt;/a&gt;&lt;/strong&gt; — single subscription, full catalog of AI courses for IT and non-IT professionals.&lt;/p&gt;




&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;The real edge in 2026 isn't access to AI — it's &lt;strong&gt;methodological maturity in choosing, evaluating, and governing AI&lt;/strong&gt;. Model access has become a commodity. The competence to architect around models is the scarce resource.&lt;/p&gt;

&lt;p&gt;If you take one thing from this article, let it be this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Stop asking "which model is best?" Start asking "which model best fits this specific decision, and what's my exit if I'm wrong?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That single shift in framing will save your team thousands of hours and tens of thousands of euros over the next twelve months.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Found this useful? Drop a comment with your current model stack — I'm always curious how teams are actually orchestrating these in production.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>anthropic</category>
      <category>openai</category>
    </item>
  </channel>
</rss>
