<?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: Chandan Garg</title>
    <description>The latest articles on DEV Community by Chandan Garg (@chandan_garg_954ece04493e).</description>
    <link>https://dev.to/chandan_garg_954ece04493e</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%2F4080155%2F65795748-44cf-41ad-8246-a6195b5829b4.png</url>
      <title>DEV Community: Chandan Garg</title>
      <link>https://dev.to/chandan_garg_954ece04493e</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chandan_garg_954ece04493e"/>
    <language>en</language>
    <item>
      <title>Signal: A Scraper that heals itself</title>
      <dc:creator>Chandan Garg</dc:creator>
      <pubDate>Sun, 23 Aug 2026 06:11:14 +0000</pubDate>
      <link>https://dev.to/chandan_garg_954ece04493e/signal-a-scraper-that-heals-itself-4nbg</link>
      <guid>https://dev.to/chandan_garg_954ece04493e/signal-a-scraper-that-heals-itself-4nbg</guid>
      <description>&lt;p&gt;SIGNAL: I Built a Web Scraper That Fixes Itself&lt;br&gt;
Because nobody wants to manually debug broken scrapers at 2 AM.&lt;/p&gt;

&lt;p&gt;The Problem Nobody Talks About&lt;br&gt;
Web scraping is easy. Web scraping that stays working is hard.&lt;br&gt;
Every developer who's built a scraper knows the drill: you write selectors, they work for a week, then a site updates its HTML and your scraper returns empty objects. Suddenly you're debugging $('.article-title') vs $('.post-title') at midnight, and your data pipeline is broken.&lt;/p&gt;

&lt;p&gt;I wanted to solve this. Not by writing better selectors, but by building a system that writes its own fixes.&lt;/p&gt;

&lt;p&gt;What I Built&lt;br&gt;
SIGNAL: is a self-healing web scraper with RAG-powered Q&amp;amp;A. It does three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Scrapes documentation sites using Bright Data Scraper Studio&lt;/li&gt;
&lt;li&gt;Indexes the content into a searchable knowledge base (local RAG, no paid APIs)&lt;/li&gt;
&lt;li&gt;Self-heals when scrapers break — automatically detects degradation, triggers AI to fix the collector, and verifies the fix worked
The whole system runs on a single Railway container. No GPU, no OpenAI key, no vector database service.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;How I Built It: Cursor AI as a Pair Programmer&lt;br&gt;
I want to be upfront about how this got built: I used Cursor AI throughout development. Not as a replacement for engineering judgment, but as a pair programmer that kept me moving when I would have otherwise gotten stuck.&lt;/p&gt;

&lt;p&gt;Here's what that looked like in practice:&lt;br&gt;
Debugging in real-time. When the self-healing loop was stuck in an infinite retry cycle, I pasted the logs into Cursor and asked "why is this looping?" It traced the issue to a missing stuck_since timestamp reset — a bug I'd been staring at for an hour.&lt;br&gt;
Architecture decisions. I'd sketch out a module (like the normalizer that handles 8+ different collector schemas) and ask Cursor to review the approach. It caught that my initial field-name mapping would break on Wikipedia's HTML content and suggested the HTML-stripping fallback.&lt;br&gt;
Rapid prototyping. The entire heal pipeline — diagnose → trigger → poll → approve → re-scrape — went from concept to working code in an afternoon. Cursor helped me write the Bright Data API client, the health metrics scoring, and the auto-approval logic without getting bogged down in API documentation.&lt;/p&gt;

&lt;p&gt;The boring stuff. Pydantic models, FastAPI route boilerplate, config loading from .env — Cursor handled all of it so I could focus on the interesting parts: the self-healing logic and the RAG pipeline.&lt;br&gt;
What it didn't replace: I still made the design decisions. I chose Bright Data over Scrapy, ChromaDB over Pinecone, sentence-transformers over OpenAI embeddings. I decided the heal loop should auto-approve rather than require manual review. I wrote the demo script and the architecture explanation. Cursor wrote code; I made it make sense together.&lt;br&gt;
The honest truth: without Cursor, this project would have taken 3-4x longer. Not because the code is complex, but because the integration surface is huge — Bright Data APIs, ChromaDB, Groq, FastAPI, React, Railway deployment. Cursor kept me from getting lost in documentation and boilerplate.&lt;/p&gt;

&lt;p&gt;Architecture: Three Layers&lt;br&gt;
&lt;strong&gt;Layer 1:&lt;/strong&gt; Scraping&lt;br&gt;
I use Bright Data Scraper Studio for the heavy lifting — anti-bot protection, JavaScript rendering, proxy rotation. For simple public pages, there's a direct HTTP fallback using httpx.&lt;br&gt;
Each scrape produces a normalized JSON with url, title, content, and metadata. The normalizer handles 8+ different collector schemas (news articles, docs, job listings, Wikipedia, GitHub READMEs) in a single function.&lt;br&gt;
&lt;strong&gt;Layer 2:&lt;/strong&gt; RAG Pipeline&lt;br&gt;
Content gets chunked into 800-character pieces with 150-character overlap (sentence-aware splitting). Each chunk is embedded locally using sentence-transformers/all-MiniLM-L6-v2 — a 80MB model that runs on CPU.&lt;br&gt;
Embeddings go into ChromaDB (persistent, local, free). Queries use cosine similarity to find the top 5 relevant chunks, which get passed as context to Groq's LLM for answer generation.&lt;br&gt;
Key numbers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;858 chunks indexed across 12 documentation sites&lt;/li&gt;
&lt;li&gt;0.68 max cosine distance threshold for relevance filtering&lt;/li&gt;
&lt;li&gt;5500 character context window for LLM&lt;/li&gt;
&lt;li&gt;&amp;lt;2 second average query response time
&lt;strong&gt;Layer 3:&lt;/strong&gt; Self-Healing (The Cool Part)
This is where it gets interesting. Every scraper has health metrics:&lt;/li&gt;
&lt;li&gt;Success rate: % of records with both title AND body&lt;/li&gt;
&lt;li&gt;Empty title %: % of records missing titles
&lt;/li&gt;
&lt;li&gt;Empty body %: % of records missing content&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When these metrics drop below thresholds (success &amp;lt; 60% or empty fields &amp;gt; 40%), the system automatically:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Diagnoses — runs a scrape to measure current extraction health&lt;/li&gt;
&lt;li&gt;Triggers Bright Data AI — sends the collector to Bright Code Fixer, which analyzes the site's HTML changes and proposes a collector modification&lt;/li&gt;
&lt;li&gt;Auto-approves — validates the fix preview looks reasonable (has title-like and body-like fields, no junk values)&lt;/li&gt;
&lt;li&gt;Saves to production — publishes the fix to the same collector ID&lt;/li&gt;
&lt;li&gt;Re-scrapes — runs a fresh scrape to measure "after" metrics&lt;/li&gt;
&lt;li&gt;Compares — shows before/after improvement
The whole loop runs in 7-8 minutes. If the site changes again next week, the system catches it and heals again.
Real Example
I triggered a heal on a broken React documentation scraper:
BEFORE HEAL:
Empty Title:   100%
Empty Body:    100%
Success Rate:    0%&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;AFTER HEAL:&lt;br&gt;
  Empty Title:     0%&lt;br&gt;
  Empty Body:      0%&lt;br&gt;
  Success Rate:  100%&lt;/p&gt;

&lt;p&gt;Bright Data's AI analyzed the React docs HTML, found that the old selectors were targeting deprecated class names, and rewrote the extraction logic. I didn't touch a single line of code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F30cts9xza21akpafc0hm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F30cts9xza21akpafc0hm.png" alt=" " width="799" height="249"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Total cost: $0 per query. Everything runs locally except the LLM (Groq free tier) and scraping (Bright Data free tier: 5,000 credits/month).&lt;/p&gt;

&lt;p&gt;The Interesting Parts&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Schema-Aware Normalizer
The normalizer handles 8+ different collector schemas in a single function. News articles have headline + article_contet. Documentation sites have main_content + section_headings + code_examples. Wikipedia has article_content (HTML) + short_description. Each path extracts title and content from different field names, strips HTML, and produces a uniform NormalizedDoc.&lt;/li&gt;
&lt;li&gt;Interleaved Multi-Query Retrieval
The retriever doesn't just embed the question and search. It generates expanded queries (strips site names, adds alternative phrasings), runs them in parallel, and interleaves results so specialized searches aren't buried by generic matches.&lt;/li&gt;
&lt;li&gt;Memory-Safe Ingestion
Railway gives you 1-2GB RAM. The ingestion pipeline processes embeddings in slices of 4 documents with explicit gc.collect() between batches to stay within limits. Each batch chunks, embeds, and upserts to ChromaDB without OOM.&lt;/li&gt;
&lt;li&gt;Smart Query Routing
The pipeline auto-routes queries:&lt;/li&gt;
&lt;li&gt;Weather questions → Open-Meteo API (live, free, no scrape needed)&lt;/li&gt;
&lt;li&gt;Wikipedia questions → Wikipedia REST API + Groq summarization&lt;/li&gt;
&lt;li&gt;Corpus questions → ChromaDB retrieval + Groq generation&lt;/li&gt;
&lt;li&gt;Out-of-scope → Refuses to answer, lists indexed domains&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;Make the heal loop event-driven instead of polling. Right now it polls Bright Data every 2 seconds, which works but isn't elegant.&lt;/li&gt;
&lt;li&gt;Add a feedback loop — if the same collector heals repeatedly, something is structurally wrong with the selectors.&lt;/li&gt;
&lt;li&gt;Support more sites — the 13 collectors are a good start, but the normalizer could handle more schemas.
Try It
API: &lt;a href="https://healscrape-production.up.railway.app/docs" rel="noopener noreferrer"&gt;https://healscrape-production.up.railway.app/docs&lt;/a&gt;
# Query the knowledge base
curl -X POST &lt;a href="https://healscrape-production.up.railway.app/query" rel="noopener noreferrer"&gt;https://healscrape-production.up.railway.app/query&lt;/a&gt; \
-H "Content-Type: application/json" \
-d '{"query": "What are React server components?"}'&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Check health metrics
&lt;/h1&gt;

&lt;p&gt;curl &lt;a href="https://healscrape-production.up.railway.app/knowledge" rel="noopener noreferrer"&gt;https://healscrape-production.up.railway.app/knowledge&lt;/a&gt;&lt;br&gt;
Frontend: The React dashboard shows the full pipeline — radar visualization of indexed sources, query console with source filtering, and the heal dashboard with before/after metrics.&lt;br&gt;
Conclusion&lt;br&gt;
The hardest part of web scraping isn't writing the first scraper — it's keeping it working. SIGNAL solves this by closing the loop: scrape → detect degradation → auto-fix → verify. No manual intervention, no midnight debugging sessions.&lt;br&gt;
And honestly? I couldn't have built it this fast without Cursor AI. It didn't write the architecture or make the design decisions, but it kept me out of the weeds so I could focus on what matters: making the system work end-to-end.&lt;br&gt;
If you're building data pipelines that depend on web scraping, self-healing isn't a nice-to-have. It's the only way to stay sane.&lt;/p&gt;

&lt;p&gt;Built for the Bright Data Hackathon 2026. Developed with Cursor AI. Source code available on &lt;a href="https://github.com/Chandan11232/HealScrape" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Beyond the Prompt: How AI Agent Harnesses Actually Manage Memory</title>
      <dc:creator>Chandan Garg</dc:creator>
      <pubDate>Sun, 16 Aug 2026 13:46:43 +0000</pubDate>
      <link>https://dev.to/chandan_garg_954ece04493e/beyond-the-prompt-how-ai-agent-harnesses-actually-manage-memory-416l</link>
      <guid>https://dev.to/chandan_garg_954ece04493e/beyond-the-prompt-how-ai-agent-harnesses-actually-manage-memory-416l</guid>
      <description>&lt;p&gt;Most tutorials make building an AI agent look deceptively simple: take a user prompt, append the last five chat messages, hand it to an LLM, and parse a JSON tool call.&lt;/p&gt;

&lt;p&gt;In production, this naive pattern fails immediately. Context windows fill with polite conversational filler, token costs skyrocket, latency degrades, and the model forgets critical preferences established three turns earlier.&lt;/p&gt;

&lt;p&gt;To build an agent that operates reliably over weeks or months, you need two systems working together:&lt;br&gt;
A Cognitive Harness: An execution environment that structures memory into distinct cognitive tiers (procedural, episodic, and semantic) and enforces safety guardrails around tool execution.&lt;br&gt;
A Closed-Loop LLMOps Pipeline: An evaluation and observability flywheel that diagnoses failures and continuously tunes prompts and retrieval parameters.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Anatomy of an Agent Harness
The Harness is the runtime engine wrapping the model. It handles context assembly, coordinates memory retrieval, executes tools, and validates outputs before returning a response to the user.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The Working Memory Layer&lt;br&gt;
Working memory is the dynamic prompt assembled just before inference. It combines:&lt;br&gt;
The Static System Prompt: Core identity, high-level behavioral constraints, and format contracts.&lt;br&gt;
The Current Turn Context: The user’s latest query alongside immediate conversational context.&lt;br&gt;
Dynamic Injections: Retrieved knowledge pulled on demand from external memory layers.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Three Cognitive Memory Tiers
Treating all memory as a single append-only text log creates noisy retrievals and bloated context windows. Production architectures adopt a tripartite model borrowed from cognitive neuroscience:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A. Procedural Memory: Knowing How&lt;br&gt;
Procedural memory represents an agent's operational muscle memory. It encodes the rules, workflows, and execution policies that dictate how tasks should be performed without requiring the model to deduce them from scratch.&lt;br&gt;
Implementation: Modular markdown files (e.g., skills.md), standardized tool schemas (OpenAPI specs), and few-shot exemplars demonstrating correct parameter construction.&lt;br&gt;
Role in Runtime: Injected deterministically into the system prompt based on active task classification.&lt;/p&gt;

&lt;p&gt;B. Episodic Memory: Knowing What Happened&lt;br&gt;
Episodic memory captures timestamped records of past experiences, interactions, and raw conversation turns.&lt;br&gt;
Implementation: Append-only message databases (Postgres, DynamoDB) keyed by session_id and timestamp.&lt;br&gt;
Role in Runtime: Queried via time-based lookups or semantic similarity when answering questions about past sessions (e.g., "What did we discuss last Tuesday?").&lt;/p&gt;

&lt;p&gt;C. Semantic Memory: Knowing What Is True&lt;br&gt;
Semantic memory stores distilled, timeless facts, concepts, and user preferences detached from chronological session transcripts.&lt;br&gt;
Implementation: Vector databases (pgvector, Pinecone, Qdrant) or Knowledge Graphs (Neo4j).&lt;br&gt;
Role in Runtime: Retrieved via RAG to ground the model in persistent domain truths (e.g., User prefers Python over TypeScript, Budget ceiling is $5,000).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Memory Consolidation Subsystem
Storing raw chat turns indefinitely causes vector retrieval to degrade because search queries end up matching casual banter instead of key facts. To maintain an accurate semantic store, production harnesses use active background consolidation.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Episodic Capture: Every turn is written directly to the episodic message store.&lt;br&gt;
Periodic Extraction: Every N interactions, an asynchronous background worker (the Summarizer Agent) processes recent turns.&lt;br&gt;
Fact Distillation: The Summarizer identifies atomic facts, strips out conversational filler, and resolves conflicting updates (e.g., updating a previously recorded preference).&lt;br&gt;
Semantic Upsert: Distilled facts are embedded and stored in the semantic database, ready for low-latency retrieval.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Agentic Tool Loop &amp;amp; Guardrails
Once working memory is assembled, the LLM enters an execution loop:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Tool Invocation: The model emits a structured tool call (e.g., reading a calendar, querying a database, or invoking a payment gateway).&lt;br&gt;
Harness Execution: The harness runs the tool in a sandbox, captures output data or error traces, appends the result to the working context, and re-invokes the model.&lt;br&gt;
Guardrail Interception: Once the model generates a final response, it passes through output guardrails before reaching the user:&lt;br&gt;
Schema Validation: Verifies structural compliance of JSON/typed outputs.&lt;br&gt;
Safety &amp;amp; Privacy: Filters PII leaks, system prompt leakage, and policy violations.&lt;br&gt;
Hallucination Checks: Cross-references citations and tool returns against the final claims.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Closing the Loop: The LLMOps Flywheel
Deploying an agent without telemetry turns it into an untraceable black box. The right-hand side of a mature architecture creates an automated feedback loop.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Tracing (1 Trace per Run)&lt;br&gt;
Every interaction generates an end-to-end trace logging the full call tree: retrieved memory chunks, raw prompts, intermediate tool calls, execution latencies, and token consumption.&lt;/p&gt;

&lt;p&gt;The Two-Pronged Evaluation Pipeline&lt;br&gt;
LLM-as-a-Judge: A separate, highly capable model inspects the full trace against standardized evaluation rubrics:&lt;br&gt;
Faithfulness: Did the model hallucinate beyond retrieved context?&lt;br&gt;
Tool Correctness: Were the correct tools selected with valid parameters?&lt;br&gt;
Task Completion: Did the response directly resolve the user’s intent?&lt;br&gt;
System Observability: Tracks infrastructure metrics including p95 latency, token burn rates, error distributions, and rate-limit headroom.&lt;br&gt;
The Diagnostic Gate&lt;br&gt;
The evaluation output routes through a gate:&lt;br&gt;
Eval Failed: Triggers diagnostic alerts. Engineers analyze whether the failure stemmed from missing RAG context, ambiguous tool definitions, or prompt drift, then patch the pipeline.&lt;br&gt;
Eval Passed: Confirmed high-quality traces feed back into the system to optimize system prompts, refine few-shot exemplars, and tune retrieval parameters (such as similarity thresholds and top-k selection).&lt;/p&gt;

</description>
      <category>llm</category>
      <category>agents</category>
      <category>systemdesign</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
