<?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: Keerat Rashid</title>
    <description>The latest articles on DEV Community by Keerat Rashid (@keerat_rashid).</description>
    <link>https://dev.to/keerat_rashid</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4017461%2F2b2ad173-3ad2-414e-b548-3f7f9efe2d46.jpg</url>
      <title>DEV Community: Keerat Rashid</title>
      <link>https://dev.to/keerat_rashid</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/keerat_rashid"/>
    <language>en</language>
    <item>
      <title>The Black Box in Your Workflow: Why Undocumented AI Agent Decisions Are a Growing Risk</title>
      <dc:creator>Keerat Rashid</dc:creator>
      <pubDate>Tue, 21 Jul 2026 12:41:36 +0000</pubDate>
      <link>https://dev.to/keerat_rashid/the-black-box-in-your-workflow-why-undocumented-ai-agent-decisions-are-a-growing-risk-388f</link>
      <guid>https://dev.to/keerat_rashid/the-black-box-in-your-workflow-why-undocumented-ai-agent-decisions-are-a-growing-risk-388f</guid>
      <description>&lt;p&gt;AI agents no longer just answer questions — they book meetings, approve refunds, call APIs, update records, and chain together dozens of small decisions into a single outcome. Most of the time, this works quietly and well. But when something goes wrong, a troubling question surfaces: why did the agent do that? For a large share of deployed systems today, there's no good answer. The reasoning, the data consulted, the tools invoked, and the intermediate steps simply weren't recorded. This is the problem of undocumented agent decisions — and it's becoming one of the central risks of the agentic AI era.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "Undocumented" Actually Means
&lt;/h2&gt;

&lt;p&gt;An undocumented decision isn't necessarily a bad one. It's simply one that can't be reconstructed after the fact. In practice, this shows up in a few common ways:&lt;/p&gt;

&lt;p&gt;Output without reasoning. The system logs what the agent did, but not the chain of thought, tool calls, or data sources that led there.&lt;br&gt;
Ephemeral intermediate state. Multi-step agent chains often discard the "scratch work" between steps — the very material that would explain a decision — once the final output is produced.&lt;br&gt;
Reviewer blind spots. A human approves a final recommendation without ever seeing the reasoning that produced it, which looks like oversight but isn't meaningful oversight.&lt;br&gt;
No durable storage. Logs exist for a few days or weeks, then age out — so when someone asks for the record months later, it's gone.&lt;/p&gt;

&lt;p&gt;The common thread is a gap between acting and accounting for the action.&lt;/p&gt;

&lt;h2&gt;
  
  
  This Is Becoming Urgent
&lt;/h2&gt;

&lt;p&gt;A few forces are converging to make this problem harder to ignore:&lt;/p&gt;

&lt;p&gt;Agents are doing more, autonomously. As agents move from single-turn assistants to systems that independently call APIs, touch databases, and trigger downstream workflows, the number of undocumented micro-decisions multiplies. A single customer request might now involve a dozen internal steps, each a potential decision point.&lt;/p&gt;

&lt;p&gt;Incidents are already happening. Surveys of enterprise AI deployments in 2026 have found that a large majority of organizations have experienced some kind of AI agent security or behavior incident in the past year, while only a small minority had real-time visibility into what their agents were doing — and a third had no audit trail at all. When something breaks, teams can often tell that it went wrong, but not what the agent processed or where the data went.&lt;/p&gt;

&lt;p&gt;Regulation is catching up. The EU AI Act's high-risk system obligations become fully enforceable in mid-2026, requiring timestamped logs, model version tracking, and evidence of meaningful human review for systems used in areas like hiring, lending, healthcare, and law enforcement. Frameworks like the NIST AI Risk Management Framework and ISO 42001 are converging on the same basic demand: comprehensive, tamper-resistant records of how automated decisions were made. Under rules like these, "the model decided" is no longer an acceptable answer on its own — organizations need to show the decision chain.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's Hard to Fix After the Fact
&lt;/h2&gt;

&lt;p&gt;Retrofitting documentation onto an existing agent system is harder than it sounds, for a few reasons:&lt;/p&gt;

&lt;p&gt;State is distributed. In a multi-agent chain, each agent hands off to the next, and reasoning signals live only in memory unless someone deliberately writes them to durable storage at every handoff.&lt;br&gt;
"Final output" review isn't the same as oversight. A human who signs off on a conclusion without seeing the path that produced it hasn't actually exercised meaningful judgment — they've rubber-stamped a black box.&lt;br&gt;
Logging has a cost, and it's easy to underinvest. Capturing full reasoning traces, tool calls, and data provenance adds engineering overhead, and it's tempting to treat it as optional until an incident or audit proves otherwise.&lt;br&gt;
Versioning gets lost. Even when a decision is logged, teams often fail to record which version of a model, prompt, or policy was active at the time — so the record exists but can't be tied to a specific, reproducible configuration.&lt;/p&gt;

&lt;p&gt;Organizations that are getting this right tend to converge on a similar structure, regardless of industry:&lt;/p&gt;

&lt;p&gt;A complete decision chain, not just an output: inputs, the specific policy or rule version applied, the data sources consulted, the reasoning steps, the tools invoked, and the final action.&lt;br&gt;
Immutable, tamper-resistant storage — often append-only logs with cryptographic hashing — so records can't be quietly altered after the fact.&lt;br&gt;
Explicit human-oversight points, marked as either hard gates (action blocked pending approval) or soft gates (action proceeds, human notified), so it's clear where a person actually had the power to intervene.&lt;br&gt;
Durability that matches regulatory timelines. If a regulator or auditor might ask for a record 18 months later, the system needs to still have it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Underlying Principle
&lt;/h2&gt;

&lt;p&gt;Strip away the compliance language, and the core idea is simple: any system that acts on your behalf should be able to explain itself. That's true whether the agent is approving a loan, screening a job applicant, or just rebooking a flight. Autonomy without traceability isn't really delegation — it's just risk with extra steps.&lt;/p&gt;

&lt;p&gt;As agents take on more consequential, higher-stakes work, the organizations that treat decision documentation as a first-class part of the system — not an afterthought bolted on before an audit — will be the ones able to actually trust, debug, and defend what their agents are doing. The ones that don't may find themselves in the position so many teams already have: staring at an output with no explanation, and no way to reconstruct how it got there.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>automation</category>
      <category>documentation</category>
    </item>
    <item>
      <title>ai-slop-that-slaps</title>
      <dc:creator>Keerat Rashid</dc:creator>
      <pubDate>Mon, 20 Jul 2026 13:58:05 +0000</pubDate>
      <link>https://dev.to/keerat_rashid/ai-slop-that-slaps-3ld</link>
      <guid>https://dev.to/keerat_rashid/ai-slop-that-slaps-3ld</guid>
      <description>&lt;p&gt;I was on twitter the other day, and apparently there's a category of internet content that shouldn't work but does. &lt;em&gt;It's AI-generated, it's obviously AI-generated, and somehow that's the whole point. A cursed content, brain rot, digital slop, but often a piece of it breaks through the noise and becomes genuinely, inexplicably great.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"AI slop" became internet shorthand for the flood of low-effort, mass-produced AI content clogging feeds: the fake product photos, the uncanny "motivational" images.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The term is usually an insult. But somewhere along the way, people started noticing that some of this stuff was &lt;em&gt;funny&lt;/em&gt;. Not ironically funny in a "look how bad AI is" way, but actually, unpredictably funny, the kind of humor that only exists because a model doesn't understand physics, continuity, or why a shrimp is just another name of a prawn.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;separating slop-that-slaps from regular-slop:&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The best viral AI content doesn't try to be photorealistic or seamless. It's funny because the seams show — extra fingers, morphing faces, physics that give up halfway through a video. The failure mode becomes the joke.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Nobody cares that an AI video of a cat running a small business has janky continuity, because the concept itself is already committing to the bit. &lt;em&gt;The absurdity does the heavy lifting.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;People layering their own captions, stitching reactions, remaking the format with new subjects&lt;/em&gt;. It becomes a template rather than a one-off, which is basically the definition of a meme.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The tool doesn't have a sense of humor. The person prompting it, selecting it, and captioning it does. A good prompt engineer with a sense of absurdity is doing something closer to editing or curation than "generating."&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;the Catch-22&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The reason this genre is fun is precisely the reason it's fragile: it works because it's rough around the edges. As the underlying models get better at photorealism and continuity, a lot of what makes this stuff charming. Ironically, "good" AI might kill "good" AI slop, so might as well enjoy it while the seams still show.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>discuss</category>
      <category>webdev</category>
      <category>humor</category>
    </item>
    <item>
      <title>Vibe Coding: From Viral Tweet to Default Development Practice</title>
      <dc:creator>Keerat Rashid</dc:creator>
      <pubDate>Sun, 19 Jul 2026 17:27:38 +0000</pubDate>
      <link>https://dev.to/keerat_rashid/vibe-coding-from-viral-tweet-to-default-development-practice-2bi9</link>
      <guid>https://dev.to/keerat_rashid/vibe-coding-from-viral-tweet-to-default-development-practice-2bi9</guid>
      <description>&lt;p&gt;In February 2025, AI researcher Andrej Karpathy described a new way of writing software: describe what you want in plain language, let an AI model generate the code, and largely stop reading it line by line. He called it "vibe coding." A little over a year later, it's not a novelty — it's close to the default way software gets built.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the Term Came From
&lt;/h2&gt;

&lt;p&gt;Karpathy's original framing was casual almost to the point of being a joke: a workflow where a developer fully gives in to the vibes, embraces exponentials, and forgets that the code even exists. The phrase caught on fast. Collins Dictionary named it the Word of the Year for 2025, and Google Trends showed searches for the term jump roughly 2,400% since Karpathy first used it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Big This Actually Got
&lt;/h2&gt;

&lt;p&gt;The adoption numbers by early 2026 are hard to overstate. 92% of US developers now use AI coding tools daily, and 82% do so weekly worldwide. GitHub reports that 46% of all new code is now AI-generated, and among Y Combinator's Winter 2025 startup cohort, 21% of companies have codebases that are 91% or more AI-generated. Even at a company the scale of Google, roughly a quarter of code is already AI-assisted. Forecasters expect the share to keep climbing — Gartner projects 60% of all new software code will be AI-generated by the end of 2026.&lt;/p&gt;

&lt;p&gt;The money has followed the usage. The AI coding tools market is growing at roughly a 38% compound annual rate, well ahead of traditional developer tools at around 16%, and Cursor reportedly went from $1 billion to $2 billion in annualized revenue in just three months. Average deal size for AI coding startups jumped more than 70-fold between 2022 and the end of 2025.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "Vibe Coding" Actually Means Today
&lt;/h2&gt;

&lt;p&gt;The term has drifted a fair amount from Karpathy's original, looser definition. Most simply: you describe what you want in natural language, AI generates the code, and you accept it without fully reviewing every line — iterating by prompting rather than by typing code yourself.&lt;/p&gt;

&lt;p&gt;But by 2026, that casual version has largely given way to something more disciplined. Vibe coding now more often means a developer writes natural-language specs and AI generates code under structured human oversight, with persistent project context and layered validation, rather than a developer surrendering all control and accepting output as-is. Developers describe it less as abandoning oversight entirely and more as a shift in role — the developer stays involved in specification, architectural review, and quality control, but expresses intent through precise prompts and conversational refinement rather than writing implementation code line by line.&lt;/p&gt;

&lt;p&gt;One writer's description of how disciplined practitioners actually work captures this middle ground well: most developers who vibe code today aren't blindly accepting every line of output — they treat the AI like a very fast junior developer, giving it direction, letting it execute quickly, and checking the work afterward.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Uncomfortable Gap: Speed Without Trust
&lt;/h2&gt;

&lt;p&gt;The most striking statistic in the current data isn't the adoption rate — it's the trust gap sitting right next to it. While 92% of US developers use AI coding tools daily, only 29% actually trust the code those tools produce. That's not a contradiction so much as a description of where the industry actually is: teams have adopted the speed of vibe coding faster than they've built the process to verify what it produces.&lt;/p&gt;

&lt;p&gt;One analysis put the underlying dynamic bluntly: testing practices haven't kept pace with how fast code generation has been automated — the differentiator in 2026 isn't whether a team uses AI tools, since virtually everyone does, but whether they actually test and verify what the AI produces before it reaches users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters Beyond Individual Developers
&lt;/h2&gt;

&lt;p&gt;Vibe coding hasn't just accelerated how existing developers work — it's lowered the barrier to building software at all. The approach has opened software development to non-technical users, letting entrepreneurs go from an idea to a working application through prompting alone, without prior coding knowledge.&lt;/p&gt;

&lt;p&gt;That accessibility has reshaped the competitive landscape for startups. Gartner has found that more than 70% of new SaaS categories now have 50 or more competitors — a direct consequence of the barrier to shipping software dropping so far. In that kind of crowded environment, the advantage shifts toward teams that can execute reliably, govern AI-assisted development, and keep their engineering process portable across tools — rather than teams that simply generated code the fastest.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Risk Side of the Ledger
&lt;/h2&gt;

&lt;p&gt;The core risk of vibe coding isn't that AI writes bad code — it's that unreviewed code, generated fast and often deployed straight to production, tends to accumulate the kinds of problems that only show up under real usage: hardcoded secrets, unhandled edge cases, and architecture that wasn't planned so much as arrived at. The tension isn't going away as adoption climbs — it scales with it, which is exactly why testing and review discipline is emerging as the real differentiator between teams that vibe code well and teams that just vibe code fast.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>ai</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>The Agentic Harness: The Part That Actually Keeps Agents Reliable</title>
      <dc:creator>Keerat Rashid</dc:creator>
      <pubDate>Sat, 18 Jul 2026 10:18:38 +0000</pubDate>
      <link>https://dev.to/keerat_rashid/the-agentic-harness-the-part-that-actually-keeps-agents-reliable-34fh</link>
      <guid>https://dev.to/keerat_rashid/the-agentic-harness-the-part-that-actually-keeps-agents-reliable-34fh</guid>
      <description>&lt;p&gt;When an AI agent fails in production, the instinct is often to blame the model — it should have "known better," reasoned more carefully, been smarter. In practice, most of the failures that show up once an agent leaves the demo stage aren't model problems at all. They're harness problems. The model decides what to do next; the harness decides what's allowed to happen, what gets remembered, and what happens when something goes wrong. A capable model wrapped in a thin harness will still loop, hallucinate tool results, and drift off task — because nothing outside it is keeping score.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What a Harness Actually Is&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The "harness" is everything surrounding the model that turns a single inference call into a working agent: the code that executes tool calls, tracks conversation state, enforces limits, and decides when a task is finished or has gone wrong. The model proposes an action; the harness is what actually runs it, validates it, and feeds the result back.&lt;/p&gt;

&lt;p&gt;This distinction matters because it reframes where the real engineering work is. Prompting shapes what the model &lt;strong&gt;wants&lt;/strong&gt; to do. The harness determines what it's &lt;em&gt;actually able&lt;/em&gt; to do, and what happens when its first choice doesn't work.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Core Responsibilities of a Harness&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Execution and Validation:&lt;/strong&gt;&lt;br&gt;
The harness is responsible for actually running tool calls and, critically, validating them before it does. That means rejecting calls to tools that don't exist, checking arguments against a schema before they hit a real system, and returning clear, unambiguous errors rather than silent failures. A loose harness that lets malformed calls through, or that returns an empty response on failure instead of an explicit error, is what allows an agent to fabricate a plausible "success" instead of recognizing that something actually broke.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;State and Memory:&lt;/strong&gt;&lt;br&gt;
Agents don't naturally remember what they've already tried. That has to be built. A well-designed harness maintains a persistent record of prior actions and outcomes, so a repeated action is recognizable as repeated, and so the system has something to check "have I done this before" against. Without this, retry loops are close to inevitable — the agent has no way to distinguish a fresh attempt from the fifth identical one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Termination and Exit Logic&lt;/strong&gt;&lt;br&gt;
This is the harness responsibility that's easiest to underbuild. Left to the model's own judgment, "when to stop" is a soft, prose-level decision — and a soft decision is exactly what breaks down under pressure to keep looking productive. The more robust approach is moving exit logic out of the model's judgment and into the workflow definition itself: every state in the process declares explicitly where failure routes to next. A failed verification step routes to "retry implementation," not back to itself. If there's no edge in the workflow that allows infinite self-repetition, that failure mode isn't just discouraged — it's structurally unreachable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A Legitimate Way to Fail:&lt;/strong&gt;&lt;br&gt;
Just as important as controlling retries is giving the agent an honest way out. If "I can't complete this" is treated as a failure to be avoided at all costs, the agent has every incentive to fabricate progress rather than admit it's stuck — which produces exactly the kind of hallucinated-success failure that's hardest to catch. A well-designed harness treats a structured, explicit "I don't have what I need to finish this" as a neutral, first-class outcome, not a penalized one. When honest failure costs nothing, agents stop flailing to appear productive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Budgets and Limits:&lt;/strong&gt;&lt;br&gt;
Turn limits, cost caps, and retry ceilings are the blunt instrument version of harness control — not elegant, but essential as a backstop. Even a well-designed exit-logic system benefits from a hard limit underneath it, because no detection mechanism catches every edge case. The budget is what keeps a missed edge case from becoming an expensive one.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Detecting Failure Patterns the Harness Should Catch&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Two failure modes in particular show why harness design matters more than model quality:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retry loops&lt;/strong&gt; are the easy case — the same action, repeated, with the same failing result. A harness that tracks action history can flag this directly: if an identical call has already failed once, don't let it fire again without some change in approach.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Oscillation&lt;/strong&gt; is harder. No single action repeats — the agent edits a file one way, then reverses it, then reintroduces the original change a few steps later — so naive deduplication misses it entirely. One lightweight approach that works well before investing in full state hashing is tracking an &lt;em&gt;outcome digest&lt;/em&gt; rather than full state: if the effects of the last several actions (files touched, calls made, results returned) start repeating with nothing net-new appearing, that's a strong signal of oscillation even though the specific actions differ each cycle. It's a lossy check — it'll miss cases where effects vary while progress still stalls — so it's worth pairing with a hard cap as backup rather than relying on it alone.&lt;/p&gt;

&lt;p&gt;Both of these are structurally invisible from inside a single turn. Catching them requires the harness to hold a window of history, not just the current state, since the pattern only exists across steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Why This Reframes the "Bigger Model" Instinct&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;It's tempting, when an agent keeps failing, to reach for a more capable model. Sometimes that helps — but often it just delays the same failure by giving the agent more attempts before it exhausts a budget. If the underlying issue is that nothing outside the model is tracking history, validating tool calls, or defining where failure routes, a smarter model will still eventually hit that same structural gap. The reliability gains that actually show up in production tend to come from strengthening the execution loop itself: validation, state tracking, and explicit termination logic — not from swapping in a bigger brain and hoping it self-corrects.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Gist&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A capable model and a well-built harness are doing different jobs. The model reasons about what to try next. The harness is what turns that reasoning into safe, bounded, recoverable action — catching malformed calls, remembering what's already failed, and giving the system a real way to stop instead of spinning. Most of what looks like "the AI got confused" in production is actually "nothing was watching," and that's a problem no amount of prompting alone fixes.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>productivity</category>
      <category>programming</category>
    </item>
    <item>
      <title>The Silent Crash: How Hallucinated Tool Breaks AI Agents</title>
      <dc:creator>Keerat Rashid</dc:creator>
      <pubDate>Fri, 17 Jul 2026 13:27:35 +0000</pubDate>
      <link>https://dev.to/keerat_rashid/the-silent-crash-how-hallucinated-tool-breaks-ai-agents-34cc</link>
      <guid>https://dev.to/keerat_rashid/the-silent-crash-how-hallucinated-tool-breaks-ai-agents-34cc</guid>
      <description>&lt;p&gt;There isn't one version of this failure, there are three distinct patterns, and they cause different kinds of damage.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Calling a tool that doesn't exist&lt;/strong&gt;. The agent invokes a function name that was never registered, sometimes because it was described in an earlier turn, sometimes because the model generalizes from a similar tool it has genuinely used before. If the orchestration layer doesn't validate the tool name before execution, this can crash the pipeline outright — or worse, silently no-op.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Inventing arguments that don't match a schema&lt;/strong&gt;. The tool exists, but the agent passes parameters that were never defined, get the wrong type, or omit something required. This is less catastrophic when strict schema validation rejects the call immediately, but if validation is loose, malformed arguments can execute against a real system with unpredictable effects.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fabricating a result when a call fails silently&lt;/strong&gt;. The tool call goes out, something goes wrong on the way, for instance: a timeout, a dropped connection, a malformed response, and instead of surfacing an error, the agent generates a plausible-sounding result and continues as though the action succeeded.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Third Pattern Is the Real Problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The first two failure types are usually loud. A missing tool or a schema mismatch tends to throw an error somewhere in the stack, which gives you a chance to catch it. The third type is quiet by design, the whole danger is that it doesn't look wrong.&lt;/p&gt;

&lt;p&gt;From the outside, a fabricated result and a genuine one are often indistinguishable in the transcript. The agent says the file was written, the API call succeeded, the record was updated, and moves on to the next step, reasoning from that claim as if it were fact. Every subsequent action in the chain now inherits a false premise, and because nothing in the flow raised an alarm, there's no natural point where a human or a monitoring system would think to check.&lt;/p&gt;

&lt;p&gt;This is what makes it a genuine "&lt;strong&gt;agentic crash&lt;/strong&gt;" rather than just an error: the system doesn't stop instead it keeps running, confidently, on a foundation that isn't there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This Happens Because&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Silent failure paths&lt;/strong&gt;. If a tool call can fail without returning a clear, distinguishable error signal, the model has to guess what happened, and models tend to guess in the direction of "it probably worked."&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pressure to produce output&lt;/strong&gt;. An agent trained or prompted to always report progress has an incentive, in a sense, to describe something happening at every step, even when the honest answer is "I don't know if that worked."&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Long context, thin verification&lt;/strong&gt;. Over a long multi-step task, there's often no built-in step that goes back and independently confirms a claimed action actually took effect. The agent's own report becomes the only record.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tool sprawl&lt;/strong&gt;. As the number of available tools grows, the chance of the model conflating two similar tools, or inventing a plausible-sounding one, increases.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How to Catch It Before It Compounds&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because this failure mode is defined by looking like success, catching it requires checks that don't rely on the agent's own account of what happened.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Independent verification, not self-report&lt;/strong&gt;. After a consequential action, check the actual state of the system — did the file really get written, does the record really exist — rather than trusting the agent's summary of the call.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Explicit, loud failure signals&lt;/strong&gt;. Make sure every tool integration returns an unambiguous error on failure rather than a null or empty response the model might interpret charitably.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Strict schema and tool-name validation&lt;/strong&gt;. Reject unrecognized tools and malformed arguments at the execution layer, before they ever reach a real system, rather than trusting the model's output as-is.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Result grounding&lt;/strong&gt;. Where possible, have the agent quote or reference specific fields from the actual tool response in its next step, this makes it harder for it to return a call with glossy outputs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Periodic reconciliation&lt;/strong&gt;. On longer tasks, insert checkpoints that compare the agent's claimed state of the world against ground truth, so a fabricated success gets caught within a few steps rather than at the very end.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Agentic crashes are mostly visible in the form of a loop, an error, a timeout. Guard railing against it means building verification into the system itself, rather than trusting that a well-behaved agent will always tell you the truth about what just happened.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>architecture</category>
      <category>automation</category>
    </item>
    <item>
      <title>Stuck in the Loop: Why AI Agents Retry, Oscillate, and Never Finish</title>
      <dc:creator>Keerat Rashid</dc:creator>
      <pubDate>Thu, 16 Jul 2026 12:44:11 +0000</pubDate>
      <link>https://dev.to/keerat_rashid/stuck-in-the-loop-why-ai-agents-retry-oscillate-and-never-finish-2kbk</link>
      <guid>https://dev.to/keerat_rashid/stuck-in-the-loop-why-ai-agents-retry-oscillate-and-never-finish-2kbk</guid>
      <description>&lt;p&gt;An agent moving through a multi-step task needs two things it doesn't automatically have: a reliable way to know when the task is actually finished, and a reliable way to recognize when its current approach isn't working. Without both, the agent has no internal alarm bell. It just keeps acting — and if the same action keeps producing the same unhelpful result, nothing tells it to stop, change course, or ask for help.&lt;/p&gt;

&lt;p&gt;This isn't a minor implementation detail. It's a structural gap in how most agent loops are built: observe, decide, act, observe again. That loop has no natural exit condition unless one is explicitly designed in.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Pattern One: The Retry Loop&lt;/strong&gt;:
&lt;/h2&gt;

&lt;p&gt;The retry loop is the simpler of the two failure modes. The agent takes an action, it fails, and the agent tries the exact same action again — sometimes with trivial variation — expecting a different outcome.&lt;/p&gt;

&lt;p&gt;A few reasons this happens:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Misread failures&lt;/strong&gt;: The agent doesn't correctly interpret why the action failed, so it can't adjust its approach. It just repeats the attempt.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No failure memory&lt;/strong&gt;: Without a persistent record of "I already tried this and it didn't work," the agent has nothing to check against before trying again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Overconfidence in the plan&lt;/strong&gt;: If the agent's internal reasoning treats the original plan as correct, it may conclude the execution was the problem, not the plan — and simply re-execute.&lt;/p&gt;

&lt;p&gt;The result is a kind of insanity loop: identical input, identical output, repeated until a turn limit, budget cap, or timeout finally intervenes from the outside.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Pattern Two: Oscillation&lt;/strong&gt;:
&lt;/h2&gt;

&lt;p&gt;Oscillation is subtler and, in some ways, more dangerous, because it can look like activity rather than failure. The agent doesn't repeat the same action — it alternates between two (or more) states, undoing its own progress each cycle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A classic example&lt;/strong&gt;: An agent editing a file makes a change, then in a later step "fixes" that change back to something close to the original, believing it's correcting an error. The next cycle, it reintroduces the original change again. Neither state is stable; the agent just orbits between them.&lt;/p&gt;

&lt;p&gt;This tends to emerge from:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No persistent sense of completed work&lt;/strong&gt;: If the agent re-evaluates the current state from scratch each turn, without a clear memory of why it made a prior change, it may re-judge that earlier change as wrong and reverse it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conflicting objectives&lt;/strong&gt;: If two instructions or two parts of the task pull in opposite directions, satisfying one requires violating the other, and the agent can flip back and forth trying to satisfy both.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Noisy or inconsistent tool feedback&lt;/strong&gt;: If a test, linter, or validation step gives slightly different signals from one run to the next, the agent may interpret this as "my last change made things worse," triggering a reversal that wasn't actually warranted.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Why Oscillation Is Harder to Detect&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A retry loop is easy to spot — the exact same action shows up over and over in the log. Oscillation is harder to catch programmatically because no single action repeats; it's the pattern across actions that's the problem. Naive check like "did the agent just do the same thing twice" won't catch it. You need to track state over a longer window and recognize when the system is cycling rather than progressing.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Designing Against Both&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A few practices address these patterns directly, rather than relying on a blunt turn limit to eventually cut the agent off:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explicit progress tracking&lt;/strong&gt;: Give the agent (or the surrounding harness) a persistent record of what's been tried and what the outcome was, so a repeated action is recognizable as repeated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;State hashing or diffing&lt;/strong&gt;: For tasks like file edits, comparing the current state to recent past states can flag oscillation — if the agent is heading back toward a state it was just in, that's a signal worth surfacing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Distinct failure handling&lt;/strong&gt;: Treat "the action failed" as a trigger for a different strategy, not a retry of the same one. This can be as simple as requiring the agent to state what it will do differently before trying again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bounded retries with escalation&lt;/strong&gt;: Cap how many times a given action can be attempted, and route to a fallback — asking for human input, trying an alternative tool, or giving up gracefully — once the cap is hit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stability checks before finishing&lt;/strong&gt;: Before declaring a task done, verify the result reflects a stable state rather than one snapshot in an ongoing oscillation.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Underlying Lesson&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Both retry loops and oscillation come from the same root gap: an agent acting without a reliable model of its own history. A human doing the same task naturally remembers what they already tried and gets frustrated enough to change strategy. An agent doesn't have that unless it's built in deliberately. Fixing looping behavior isn't about making the model smarter in the moment — it's about giving the surrounding system enough memory and self-awareness to recognize when it's going in circles.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>architecture</category>
      <category>automation</category>
    </item>
    <item>
      <title>Where this agent engineering heading</title>
      <dc:creator>Keerat Rashid</dc:creator>
      <pubDate>Wed, 15 Jul 2026 11:58:34 +0000</pubDate>
      <link>https://dev.to/keerat_rashid/3-am-thought-where-this-agent-engineering-heading-17i7</link>
      <guid>https://dev.to/keerat_rashid/3-am-thought-where-this-agent-engineering-heading-17i7</guid>
      <description>&lt;p&gt;Agent engineering is system design that turns a capable but uneven model into a reliable task finisher under real constraints: messy inputs, long horizons, imperfect tools, and hard budgets.&lt;/p&gt;

&lt;p&gt;Agent Engineering is System Design that primarily uses:&lt;br&gt;
State &amp;amp; context strategy: Context and state the agent remembers, retrieves, forgets. &lt;br&gt;
Contracts: schemas, expected inputs/outputs, timeouts, retries. &lt;br&gt;
Verification: Checks if a step is working. &lt;br&gt;
Budgets &amp;amp; stop conditions: hard caps on turns, tokens, and time. &lt;br&gt;
Permissions &amp;amp; risk tiers: read-only from destructive actions and gating the latter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why does Agent Engineering exists&lt;/strong&gt;&lt;br&gt;
While LLMs are great at solving coding problems but it struggles  to count the letter "r" in the word "strawberry" — The Strawberry Problem is the great example. The LLMs hallucinates — giving out the false information, when it doesn't know the answer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Trajectory of Agent Engineering&lt;/strong&gt;&lt;br&gt;
The trajectory is toward more agents doing more of the coordination work themselves. The practices behind it will continue to advance as AI agents become more capable. In the coming years, we can expect agentic systems to handle increasingly complex tasks. As more agents talk to more agents with less human involvement in the loop, the harness — the permissions, the logging, the failure isolation, the evaluation — is what determines whether the result is a reliable system or an expensive mess.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>automation</category>
      <category>agents</category>
      <category>mcp</category>
    </item>
    <item>
      <title>RAG from Scratch with ChromaDB (No LangChain Required)</title>
      <dc:creator>Keerat Rashid</dc:creator>
      <pubDate>Wed, 08 Jul 2026 14:39:18 +0000</pubDate>
      <link>https://dev.to/keerat_rashid/rag-from-scratch-with-chromadb-no-langchain-required-517a</link>
      <guid>https://dev.to/keerat_rashid/rag-from-scratch-with-chromadb-no-langchain-required-517a</guid>
      <description>&lt;p&gt;Frameworks like LangChain are great for moving fast, but they also hide a lot of what's actually happening under the hood. If you want to understand RAG at a deeper level — or just want a lighter-weight stack without extra abstraction layers — you can build a fully functional Retrieval-Augmented Generation pipeline using just &lt;strong&gt;ChromaDB&lt;/strong&gt; and a handful of standard Python libraries.&lt;/p&gt;

&lt;p&gt;This article walks through building RAG from first principles: chunking documents, generating embeddings, storing and querying vectors in ChromaDB, and passing retrieved context to an LLM — all without a single LangChain import.&lt;/p&gt;

&lt;p&gt;LangChain is useful, but going framework-free has real advantages in certain situations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Transparency&lt;/strong&gt; — You see exactly what's happening at each step: chunking, embedding, retrieval, prompt construction. Nothing is hidden behind an abstraction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fewer dependencies&lt;/strong&gt; — Your project stays lightweight, with fewer version-compatibility headaches.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full control&lt;/strong&gt; — You can customize chunking logic, retrieval scoring, or prompt formatting exactly the way you want, without working around a framework's opinions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easier debugging&lt;/strong&gt; — When something goes wrong, you're debugging your own code, not tracing through several layers of framework abstraction.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What Is ChromaDB?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Chroma is an open-source, embedding-native vector database. It can run fully in-process (embedded, like SQLite) or as a standalone client-server service. It handles storing vectors, associated metadata, and performing similarity search — exactly the retrieval half of a RAG pipeline — without requiring any other orchestration library.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Building the Pipeline Step by Step&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Install Dependencies&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&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;chromadb openai
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll use OpenAI for both embeddings and generation, but you can swap in any embedding model or LLM provider — the pipeline logic stays the same.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Set Up ChromaDB&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Chroma can persist data to disk, so your index survives across script runs.&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;PersistentClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./chroma_db&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="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;get_or_create_collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rag_demo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;metadata&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;hnsw:space&lt;/span&gt;&lt;span class="sh"&gt;"&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&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;A &lt;code&gt;collection&lt;/code&gt; in Chroma is roughly equivalent to a table or index — it's where your document chunks and their embeddings live.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Load and Chunk Your Documents&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Without LangChain's text splitters, you write your own simple chunking function. A straightforward sliding-window approach works well for most text:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;folder_path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;docs&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;filename&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;folder_path&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;filename&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;folder_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&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;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;docs&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;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;filename&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;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&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;docs&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;chunk_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chunk_size&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="n"&gt;overlap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&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;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;chunk_size&lt;/span&gt;
        &lt;span class="n"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;end&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;chunk_size&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;overlap&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;chunks&lt;/span&gt;

&lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_documents&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;all_chunks&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;doc&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;chunk_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="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;all_chunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&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="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&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="s"&gt;_chunk_&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;source&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="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="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This mirrors what LangChain's &lt;code&gt;RecursiveCharacterTextSplitter&lt;/code&gt; does internally — split into fixed-size windows with overlap — just written explicitly so you can tune it however you like.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Generate Embeddings&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Call the embedding model directly, batching requests where possible to keep things fast.&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;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="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_OPENAI_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_embeddings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;texts&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;text-embedding-3-small&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;openai_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;embeddings&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="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;texts&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;texts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;all_chunks&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;embeddings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_embeddings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;texts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Store Chunks in ChromaDB&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&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;ids&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;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;chunk&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;all_chunks&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;embeddings&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;embeddings&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="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;all_chunks&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;metadatas&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;source&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;source&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;chunk&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;all_chunks&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;Chroma stores the raw text, the vector, and any metadata together, so retrieval results come back ready to use — no separate lookup needed.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Retrieve Relevant Chunks for a Query&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&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;retrieve&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;top_k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;query_embedding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_embeddings&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="mi"&gt;0&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_embeddings&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;query_embedding&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="n"&gt;top_k&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="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;span class="mi"&gt;0&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;metadatas&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What does our refund policy cover?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;retrieved_docs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;retrieved_metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;retrieve&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Construct the Prompt and Call the LLM&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is the "augment and generate" step — you build the prompt manually, giving you full control over formatting.&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_prompt&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;context_chunks&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="sh"&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context_chunks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Answer the question based only on the following context.
If the answer isn&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;t in the context, say you don&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;t know.

Context:
&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="si"&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;query&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_answer&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;context_chunks&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;gpt-4o-mini&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="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;build_prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context_chunks&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;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&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="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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;

&lt;span class="n"&gt;answer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generate_answer&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;retrieved_docs&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;answer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;** Putting It All Together**&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;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;top_k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;retrieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;top_k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;top_k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;generate_answer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;docs&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="nf"&gt;rag_query&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 does our refund policy cover?&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;That's the entire pipeline: load, chunk, embed, store, retrieve, prompt, generate — with no orchestration framework in between.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design Considerations for Production RAG&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once you move past a prototype, a few decisions matter more:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Chunking strategy&lt;/strong&gt; — A fixed-size sliding window is simple but not always ideal. Splitting on sentence or paragraph boundaries often produces more coherent chunks than a raw character count.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metadata filtering&lt;/strong&gt; — Chroma's &lt;code&gt;query&lt;/code&gt; method accepts a &lt;code&gt;where&lt;/code&gt; filter, letting you narrow search by metadata (e.g., only search chunks from a specific &lt;code&gt;source&lt;/code&gt; or date range) before similarity search runs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Batch embedding calls&lt;/strong&gt; — Embedding APIs are usually billed and rate-limited per request; batch your &lt;code&gt;texts&lt;/code&gt; list instead of calling the embedding endpoint once per chunk.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persistence and backups&lt;/strong&gt; — &lt;code&gt;PersistentClient&lt;/code&gt; writes to local disk by default; for production, consider running Chroma as a client-server deployment so multiple processes can share the same index safely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Re-ranking&lt;/strong&gt; — After the initial vector search, you can re-score the top results with a cross-encoder model before passing only the best few chunks to the LLM, improving answer quality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evaluation&lt;/strong&gt; — Since there's no framework enforcing structure, it's worth writing your own lightweight evaluation harness (e.g., checking retrieval precision or answer faithfulness) as your document set grows.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You don't need a heavyweight framework to build a working RAG system. ChromaDB alone — paired with a plain embedding call and a plain LLM call — gives you a transparent, fully controllable pipeline: chunk your documents, embed them, store and search them in Chroma, then feed the retrieved context into your model of choice. This approach trades a bit of convenience for a much clearer picture of exactly what your RAG system is doing at every step, which pays off the moment you need to debug, customize, or optimize it.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>langchain</category>
      <category>rag</category>
    </item>
    <item>
      <title>A Dev's Guide to Loop Engineering: The System That Works Itself</title>
      <dc:creator>Keerat Rashid</dc:creator>
      <pubDate>Tue, 07 Jul 2026 11:01:56 +0000</pubDate>
      <link>https://dev.to/keerat_rashid/a-devs-guide-to-loop-engineering-the-system-that-works-itself-81o</link>
      <guid>https://dev.to/keerat_rashid/a-devs-guide-to-loop-engineering-the-system-that-works-itself-81o</guid>
      <description>&lt;p&gt;“You shouldn’t be prompting coding agents anymore. You should be designing loops that prompt your agents.” - Peter Steinberger &lt;/p&gt;

&lt;p&gt;Loop engineering is built around the idea that system work itself in a loop of Act, Check, and Decides and the loop continues until a goal is achieved. The only thing human has to do is to write “loops” for the systems to work. Think of it like writing a prompt to give enough context to train the models  Done right, it's the closest thing in software to a system that works itself. Done wrong, it's an infinite loop with better PR.&lt;/p&gt;

&lt;p&gt;“Loop engineering is replacing yourself as the person who prompts the agent. You design the system that does it instead. A loop here can be thought of a recursive goal where you define a purpose and the AI iterates until complete. I believe this may be the future of how we work with coding agents.” – Addy Osmani &lt;/p&gt;

&lt;p&gt;Loop Engineering is built over harness engineering. Harness Engineering builds the infrastructure the agent operates in. It focuses on permanent, systemic fixes: whenever the agent makes a mistake, the environment is updated so that specific error becomes structurally impossible to repeat.&lt;/p&gt;

&lt;p&gt;This guide is for developers building that second kind of system — retry logic, polling daemons, autonomous agents, self-healing pipelines — anything that's meant to run its own loop rather than be called once and forget.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Anatomy of a Self-Running Loop&lt;/strong&gt;&lt;br&gt;
Every loop worth engineering has the same four stages, and almost every bug in a loop-based system traces back to one of them being sloppy:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Sense =&amp;gt; observe the current state of the world.&lt;/li&gt;
&lt;li&gt; Decide =&amp;gt; compare that state against the goal and choose the next move.&lt;/li&gt;
&lt;li&gt; Act =&amp;gt; execute the move.&lt;/li&gt;
&lt;li&gt; Check exit =&amp;gt; decide whether to stop or loop again.
It looks trivial. It is not. Almost all the real engineering work lives inside sensing, deciding, and checking exit is usually the easy part.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;1. Sense: Don't Trust Stale State&lt;/strong&gt;&lt;br&gt;
The single most common bug in loop-based systems is acting on state that's already out of date by the time you act on it. A polling loop that reads a queue depth, spends time deciding what to do, and then acts on a number that's now wrong is a classic example.&lt;br&gt;
Practical rules:&lt;br&gt;
• Re-sense right before you act, not just at the top of the loop, if there's any meaningful delay between sensing and acting.&lt;br&gt;
• Timestamp your state. If the decision step can't tell how old its input is, it can't tell whether to trust it.&lt;br&gt;
• Distinguish "no signal" from "bad signal." A sensor timeout and a sensor reading of zero are different failure modes and should never be handled the same way.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Decide: This Is Where Loops Go Unstable&lt;/strong&gt;&lt;br&gt;
This is the step that separates a loop that converges from one that oscillates or runs away. Three failure patterns show up constantly:&lt;br&gt;
Overcorrection. If your loop reacts to the full size of an error every cycle, it tends to overshoot, then overshoot back the other way. The fix, borrowed straight from control theory, is to dampen the response, react to a fraction of the error each cycle, not all of it.&lt;br&gt;
Compounding drift. In multi-step loops (agents especially), a small mistake in the first cycle becomes the input to the next, and errors stack on top of each other. Guard against this by periodically re-grounding against a source of truth instead of only trusting the loop's own last output.&lt;br&gt;
No memory of history. A loop that only looks at the current error will happily repeat the same failed move forever. A loop that only looks at the current state, with no memory at all, can't tell "getting better slowly" from "stuck." Give it a short history to check against, and look for actual progress every few cycles, not just whether the last cycle technically succeeded.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Act: Make Actions Safe to Repeat&lt;/strong&gt;&lt;br&gt;
If your loop might retry an action after a partial failure, and if it runs more than once, it eventually will, and that action needs to be safe to run twice. This is the difference between a retry loop that heals itself and one that quietly corrupts state on the second attempt.&lt;br&gt;
The general pattern: before taking an action, the loop should be able to check whether that action has already happened, and skip it if so. Payment systems solve this with idempotency keys; other systems solve it by checking current state before acting rather than assuming a blank slate. If an action truly can't be made repeat-safe, the loop needs to guarantee it only ever fires once, which is a much harder property to build and worth avoiding if a repeat-safe design is available instead.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Check Exit: The Part Everyone Forgets&lt;/strong&gt;&lt;br&gt;
An unbounded loop is a liability by default, not a feature. Every self-running loop needs at least two independent exit conditions, because relying on just one is how you end up debugging a process that's been running for six hours:&lt;br&gt;
• A success condition =&amp;gt; the actual goal has been met.&lt;br&gt;
• A hard ceiling =&amp;gt; a maximum number of iterations, a maximum wall-clock time, or a maximum cost, regardless of whether the goal was met.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both limits should be enforced in the code itself, not just something a human plans to watch on a dashboard. Dashboards don't get paged at 3am; runaway loops do.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backoff: Loop Engineering's Most Reused Pattern&lt;/strong&gt;&lt;br&gt;
If your loop is retrying against something that might be struggling: a flaky API, an overloaded queue, reacting at full speed on every failure is how a brief hiccup turns into an outage. You end up hammering a system that's already struggling. The standard fix is exponential backoff with jitter: wait longer after each successive failure, and add a small random variation to that wait time.&lt;br&gt;
The jitter matters more than people expect. Without it, every client retries at exactly the same moment, which doesn't solve the thundering-herd problem,  it just moves it to a slightly later, equally synchronized timestamp.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agent Loops: The Same Rules, Higher Stakes&lt;/strong&gt;&lt;br&gt;
If you're building an LLM-based agent loop, read a tool result, decide the next action, call a tool, repeat, every rule above still applies, plus a few that matter more once the "decide" step is a model call instead of a formula:&lt;br&gt;
• Bound every dimension you can: maximum tool calls, maximum tokens, maximum wall-clock time, maximum cost. An agent loop with only a "the model decided it's done" exit condition will eventually hit a case where the model never decides that.&lt;br&gt;
• Re-ground periodically. Don't let the agent's own past outputs be its only source of truth about what's actually happening — feed it fresh tool results, not just its own earlier summaries of those results.&lt;br&gt;
• Make tool calls repeat-safe, same as any other retried action. An agent that reissues a "send email" call because it lost track of state should not send the email twice.&lt;br&gt;
• Insert a human checkpoint before high-consequence actions. This is a deliberate damping step, functionally identical to a safety interlock in industrial control loops — it exists specifically to catch the cases the automated exit conditions didn't anticipate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A Pre-Ship Checklist&lt;/strong&gt;&lt;br&gt;
Before you let a loop run unattended, it should be able to answer all of these:&lt;br&gt;
• Can it tell the difference between "no data" and "data says zero"?&lt;br&gt;
• Does it re-check ground truth periodically, or only trust its own last output forever?&lt;br&gt;
• Is every action safe to run twice?&lt;br&gt;
• Does it have a hard ceiling on iterations, time, and cost — independent of its success condition?&lt;br&gt;
• Does its retry logic back off, with jitter, instead of hammering at a fixed rate?&lt;br&gt;
• If it fails to converge, does it fail loudly, or just quietly loop forever?&lt;br&gt;
A loop that passes all six is one you can actually trust to run itself. A loop that's missing even one of them isn't a self-running system, it's a landmine with no way to defuse itself.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>loopengineering</category>
      <category>promptengineering</category>
      <category>mcp</category>
    </item>
    <item>
      <title>Building Retrieval-Augmented Generation (RAG) Systems with LangChain and Pinecone</title>
      <dc:creator>Keerat Rashid</dc:creator>
      <pubDate>Mon, 06 Jul 2026 15:47:41 +0000</pubDate>
      <link>https://dev.to/keerat_rashid/building-retrieval-augmented-generation-rag-systems-with-langchain-and-pinecone-3hfn</link>
      <guid>https://dev.to/keerat_rashid/building-retrieval-augmented-generation-rag-systems-with-langchain-and-pinecone-3hfn</guid>
      <description>&lt;p&gt;While LLMs are great, there are some limitations in using LLMs: LLMs can hallucinate, presenting factually incorrect information when they don't know the answers, and their knowledge gets frozen at the time of training. That's when Retrieval Augmented Generation (RAG) addresses both of these problems. It is the process of optimizing the output of the LLM.&lt;/p&gt;

&lt;p&gt;This article walks through what RAG is, why it matters, and how to build a working RAG pipeline using two of the most popular tools in the space: &lt;strong&gt;LangChain&lt;/strong&gt;, a framework for building LLM-powered applications, and &lt;strong&gt;Pinecone&lt;/strong&gt;, a managed vector database designed for fast similarity search at scale.&lt;/p&gt;

&lt;p&gt;A typical RAG pipeline has three core steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Retrieve&lt;/strong&gt;: When a query is entered, the system searches an external data source (like a vector database) for the most relevant documents.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Augment&lt;/strong&gt;: The system attaches those relevant retrieved documents to the original user prompt.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generate&lt;/strong&gt;: The LLM reads the appended context and formulates a highly accurate, grounded answer.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;RAG is popular because it solves practical problems that pure fine-tuning or prompting can't easily solve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Freshness&lt;/strong&gt; — You can update the knowledge base without retraining the model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Domain specificity&lt;/strong&gt; — You can ground responses in your company's internal documents, product manuals, or proprietary data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Traceability&lt;/strong&gt; — Because answers are based on retrieved documents, you can cite sources and reduce hallucination.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost&lt;/strong&gt; — Retrieval is far cheaper than fine-tuning a model every time your data changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why LangChain and Pinecone?
&lt;/h2&gt;

&lt;p&gt;LangChain drastically speeds up AI development. It is an open-source orchestration framework that provides pre-built components to connect Large Language Models (LLMs) to external data, manage memory, and create multi-step workflows. It abstracts away the complex boilerplate usually required to build production-ready AI applications.&lt;/p&gt;

&lt;p&gt;Pinecone is a purpose-built vector database. Once your documents are converted into embeddings (numerical vectors that capture semantic meaning), Pinecone stores them and lets you perform fast, approximate nearest-neighbor search — finding the vectors most similar to a query vector, even across millions of records, with low latency.&lt;/p&gt;

&lt;p&gt;Together, they form a clean, production-ready stack: LangChain handles orchestration and LLM calls, Pinecone handles storage and retrieval.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building a RAG Pipeline Step by Step
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Install Dependencies
&lt;/h3&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;langchain langchain-openai langchain-pinecone pinecone-client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Set Up Pinecone
&lt;/h3&gt;

&lt;p&gt;First, create a Pinecone account and get an API key. Then initialize an index — the container that holds your vectors.&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;pinecone&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Pinecone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ServerlessSpec&lt;/span&gt;

&lt;span class="n"&gt;pc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Pinecone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_PINECONE_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;index_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;rag-demo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Create the index if it doesn't already exist
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;index_name&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;pc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list_indexes&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;names&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;index_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;dimension&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1536&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# must match your embedding model's output size
&lt;/span&gt;        &lt;span class="n"&gt;metric&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cosine&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;ServerlessSpec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cloud&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;aws&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;us-east-1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;dimension&lt;/code&gt; parameter must match the size of the embeddings you plan to store — for example, OpenAI's &lt;code&gt;text-embedding-3-small&lt;/code&gt; outputs 1536-dimensional vectors.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Load and Split Your Documents
&lt;/h3&gt;

&lt;p&gt;Raw documents are usually too long to embed as single chunks, so LangChain provides text splitters that break them into manageable, semantically coherent pieces.&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;langchain_community.document_loaders&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;DirectoryLoader&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_text_splitters&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RecursiveCharacterTextSplitter&lt;/span&gt;

&lt;span class="n"&gt;loader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DirectoryLoader&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;glob&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;**/*.txt&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="n"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;splitter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RecursiveCharacterTextSplitter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;chunk_size&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="n"&gt;chunk_overlap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;splitter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split_documents&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;chunk_overlap&lt;/code&gt; ensures that context isn't awkwardly cut off between chunks, which helps preserve meaning across chunk boundaries.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Embed and Upload to Pinecone
&lt;/h3&gt;

&lt;p&gt;LangChain's Pinecone integration handles both embedding generation and upsertion into the index in a single call.&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;langchain_openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAIEmbeddings&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_pinecone&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PineconeVectorStore&lt;/span&gt;

&lt;span class="n"&gt;embeddings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAIEmbeddings&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;text-embedding-3-small&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;vector_store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PineconeVectorStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_documents&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;chunks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;embeddings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;index_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;index_name&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each chunk of text is converted into a vector and stored in Pinecone alongside metadata (like the source filename), which makes it possible to trace an answer back to its origin later.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Build the Retriever
&lt;/h3&gt;

&lt;p&gt;Once your data is indexed, you can turn the vector store into a retriever that fetches the top-k most relevant chunks for any query.&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;retriever&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vector_store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;as_retriever&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;search_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;similarity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;search_kwargs&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;k&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&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;h3&gt;
  
  
  6. Wire Up the LLM and Prompt
&lt;/h3&gt;

&lt;p&gt;Now combine retrieval with generation. LangChain's &lt;code&gt;create_retrieval_chain&lt;/code&gt; (or the older &lt;code&gt;RetrievalQA&lt;/code&gt; chain) makes this straightforward.&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;langchain_openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.chains.combine_documents&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;create_stuff_documents_chain&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.chains&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;create_retrieval_chain&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_core.prompts&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatPromptTemplate&lt;/span&gt;

&lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ChatOpenAI&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;gpt-4o-mini&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ChatPromptTemplate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
Answer the question based only on the following context.
If the answer isn&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;t in the context, say you don&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;t know.

Context:
{context}

Question: {input}
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;document_chain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_stuff_documents_chain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;rag_chain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_retrieval_chain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;retriever&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;document_chain&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  7. Query the System
&lt;/h3&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;rag_chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&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&lt;/span&gt;&lt;span class="sh"&gt;"&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 does our refund policy cover?&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;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;answer&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;Behind the scenes, LangChain has: embedded the query, sent it to Pinecone to retrieve the top matching chunks, inserted those chunks into the prompt template, and passed the whole thing to the LLM for a grounded answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design Considerations for Production RAG
&lt;/h2&gt;

&lt;p&gt;A working prototype is easy to build, but a few decisions matter a lot once you move toward production:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Chunking strategy&lt;/strong&gt; — Too small, and chunks lose context; too large, and retrieval becomes imprecise. Experiment with chunk size and overlap for your specific document types.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metadata filtering&lt;/strong&gt; — Pinecone supports filtering by metadata (e.g., document type, date, department), which lets you narrow retrieval before similarity search runs, improving both speed and relevance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hybrid search&lt;/strong&gt; — Combining semantic (vector) search with traditional keyword search often improves retrieval quality, especially for queries containing exact terms, product codes, or names.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Re-ranking&lt;/strong&gt; — Adding a re-ranking step after initial retrieval (using a cross-encoder model) can significantly improve the quality of the final context passed to the LLM.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evaluation&lt;/strong&gt; — Track retrieval precision/recall and answer faithfulness over time. Tools like RAGAS or custom LLM-based evaluators can help catch regressions as your document set evolves.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Index maintenance&lt;/strong&gt; — Set up a pipeline to re-embed and update Pinecone entries when source documents change, so the index doesn't go stale.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;RAG has become one of the most practical patterns for making LLMs useful with real, current, and domain-specific information. LangChain and Pinecone form a natural pairing for this: LangChain handles the orchestration of loading, splitting, embedding, and prompting, while Pinecone provides fast, scalable vector search to ground responses in relevant retrieved context. The pipeline above is a solid starting point — from there, the real engineering work lies in tuning chunking, retrieval, and evaluation to fit your specific data and use case.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>rag</category>
      <category>langchain</category>
    </item>
  </channel>
</rss>
