<?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: Meet Dalsania</title>
    <description>The latest articles on DEV Community by Meet Dalsania (@meet_dalsania_4cfe420287e).</description>
    <link>https://dev.to/meet_dalsania_4cfe420287e</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%2F4087650%2F769a7930-ad76-4d21-af0c-d012f96ef909.jpg</url>
      <title>DEV Community: Meet Dalsania</title>
      <link>https://dev.to/meet_dalsania_4cfe420287e</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/meet_dalsania_4cfe420287e"/>
    <language>en</language>
    <item>
      <title>How I Built OmniScrape: A Modular RAG Engine with FastAPI and Qdrant</title>
      <dc:creator>Meet Dalsania</dc:creator>
      <pubDate>Fri, 21 Aug 2026 09:06:22 +0000</pubDate>
      <link>https://dev.to/meet_dalsania_4cfe420287e/how-i-built-omniscrape-a-modular-rag-engine-with-fastapi-and-qdrant-32d7</link>
      <guid>https://dev.to/meet_dalsania_4cfe420287e/how-i-built-omniscrape-a-modular-rag-engine-with-fastapi-and-qdrant-32d7</guid>
      <description>&lt;p&gt;Large websites contain an enormous amount of information.&lt;/p&gt;

&lt;p&gt;The problem isn't always finding information.&lt;/p&gt;

&lt;p&gt;The problem is asking the right question against that information efficiently.&lt;/p&gt;

&lt;p&gt;If you crawl a website and simply store the raw HTML, you haven't really built a useful knowledge system.&lt;/p&gt;

&lt;p&gt;You still need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clean the content&lt;/li&gt;
&lt;li&gt;Remove unnecessary information&lt;/li&gt;
&lt;li&gt;Break the content into meaningful chunks&lt;/li&gt;
&lt;li&gt;Generate embeddings&lt;/li&gt;
&lt;li&gt;Store those embeddings&lt;/li&gt;
&lt;li&gt;Retrieve relevant information&lt;/li&gt;
&lt;li&gt;Generate an answer from the retrieved context&lt;/li&gt;
&lt;li&gt;Show users where that information came from&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That was the motivation behind &lt;strong&gt;OmniScrape&lt;/strong&gt;, a modular RAG engine I built using &lt;strong&gt;FastAPI and Qdrant&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Imagine a website containing hundreds or thousands of pages.&lt;/p&gt;

&lt;p&gt;A user might ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What does this documentation say about authentication?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Traditional keyword search can work well for simple queries.&lt;/p&gt;

&lt;p&gt;But what happens when the user doesn't use the exact terminology from the website?&lt;/p&gt;

&lt;p&gt;For example, the documentation might say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Requests must include a valid bearer token."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;while the user asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How do I authenticate my API requests?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The words are different, but the meaning is related.&lt;/p&gt;

&lt;p&gt;This is where &lt;strong&gt;Retrieval-Augmented Generation (RAG)&lt;/strong&gt; becomes useful.&lt;/p&gt;

&lt;p&gt;Instead of asking an LLM to answer entirely from its general knowledge, the system first retrieves relevant information from the target knowledge base and then gives that information to the model.&lt;/p&gt;

&lt;p&gt;The basic idea is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Question
   ↓
Retrieve relevant content
   ↓
Give retrieved content to LLM
   ↓
Generate answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OmniScrape was designed around this architecture.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Goal
&lt;/h2&gt;

&lt;p&gt;The goal was to build a system that could take website content and turn it into a searchable knowledge base.&lt;/p&gt;

&lt;p&gt;The pipeline needed to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Crawl websites&lt;/li&gt;
&lt;li&gt;Extract useful content&lt;/li&gt;
&lt;li&gt;Clean the content&lt;/li&gt;
&lt;li&gt;Split it into chunks&lt;/li&gt;
&lt;li&gt;Generate embeddings&lt;/li&gt;
&lt;li&gt;Store embeddings and metadata&lt;/li&gt;
&lt;li&gt;Retrieve relevant information&lt;/li&gt;
&lt;li&gt;Generate an answer&lt;/li&gt;
&lt;li&gt;Return citations to the original sources&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The overall architecture looked roughly like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 ┌──────────────┐
                 │    Website   │
                 └──────┬───────┘
                        ↓
                 ┌──────────────┐
                 │   Crawler    │
                 └──────┬───────┘
                        ↓
                 ┌──────────────┐
                 │Content Clean │
                 └──────┬───────┘
                        ↓
                 ┌──────────────┐
                 │   Chunking   │
                 └──────┬───────┘
                        ↓
                 ┌──────────────┐
                 │  Embeddings  │
                 └──────┬───────┘
                        ↓
                 ┌──────────────┐
                 │    Qdrant    │
                 └──────┬───────┘
                        ↓
                    Retrieval
                        ↓
                 ┌──────────────┐
                 │ Answer Model │
                 └──────┬───────┘
                        ↓
                    Answer + Sources
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is that this isn't one large AI operation.&lt;/p&gt;

&lt;p&gt;Each stage has a specific responsibility.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Qdrant?
&lt;/h2&gt;

&lt;p&gt;A vector database is one of the central components of a RAG system.&lt;/p&gt;

&lt;p&gt;I chose &lt;strong&gt;Qdrant&lt;/strong&gt; because it provides a dedicated vector search layer and allows embeddings to be stored together with useful metadata.&lt;/p&gt;

&lt;p&gt;That metadata becomes particularly important when building source-aware retrieval.&lt;/p&gt;

&lt;p&gt;A chunk shouldn't just be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Some paragraph from a website...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It should also carry information about where that paragraph came from.&lt;/p&gt;

&lt;p&gt;Conceptually, a stored record could look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Authentication can be configured..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://example.com/docs/authentication"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Authentication"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"chunk_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"auth-004"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now retrieval can return both the content and its provenance.&lt;/p&gt;

&lt;p&gt;That becomes important later when generating citations.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why FastAPI?
&lt;/h2&gt;

&lt;p&gt;FastAPI was a natural fit for the backend because I wanted a clean API layer between the application and the RAG pipeline.&lt;/p&gt;

&lt;p&gt;Instead of coupling the frontend directly to crawling, embedding, retrieval, and generation logic, the backend exposes clear endpoints.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /crawl
POST /ingest
POST /query
GET  /sources
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The frontend doesn't need to know how the RAG engine works internally.&lt;/p&gt;

&lt;p&gt;It only needs to communicate with the API.&lt;/p&gt;

&lt;p&gt;This separation also makes the backend easier to test and extend.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Ingestion Pipeline
&lt;/h1&gt;

&lt;p&gt;Before a user can ask questions, the website needs to be converted into useful information.&lt;/p&gt;

&lt;p&gt;This is where most of the RAG pipeline actually happens.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Crawl
&lt;/h2&gt;

&lt;p&gt;The first step is retrieving pages from the target website.&lt;/p&gt;

&lt;p&gt;But raw HTML isn't suitable for embedding.&lt;/p&gt;

&lt;p&gt;A webpage can contain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Navigation menus&lt;/li&gt;
&lt;li&gt;Scripts&lt;/li&gt;
&lt;li&gt;Footers&lt;/li&gt;
&lt;li&gt;Advertisements&lt;/li&gt;
&lt;li&gt;Sidebars&lt;/li&gt;
&lt;li&gt;Repeated content&lt;/li&gt;
&lt;li&gt;Other elements unrelated to the actual information&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So crawling is only the beginning.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Clean
&lt;/h2&gt;

&lt;p&gt;The extracted content needs to be converted into meaningful text.&lt;/p&gt;

&lt;p&gt;The objective is to preserve useful information while removing unnecessary page noise.&lt;/p&gt;

&lt;p&gt;For example, instead of storing a page like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Home
Products
Pricing
Documentation
Login

Authentication

Authentication allows applications...

Privacy
Terms
Copyright
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the system should focus primarily on the useful content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Authentication

Authentication allows applications...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This matters because irrelevant content can eventually affect retrieval quality.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Chunk
&lt;/h2&gt;

&lt;p&gt;Long documents cannot simply be treated as one giant piece of information.&lt;/p&gt;

&lt;p&gt;The content needs to be divided into smaller sections.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Documentation
├── Introduction
├── Authentication
├── Configuration
├── API Reference
└── Troubleshooting
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Chunking looks simple, but it has a major effect on retrieval.&lt;/p&gt;

&lt;p&gt;If chunks are too large, retrieval can return a lot of irrelevant information.&lt;/p&gt;

&lt;p&gt;If chunks are too small, important context can be separated across multiple chunks.&lt;/p&gt;

&lt;p&gt;So chunking becomes a tradeoff rather than a fixed rule.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Embed
&lt;/h2&gt;

&lt;p&gt;Each chunk is converted into a vector representation.&lt;/p&gt;

&lt;p&gt;The embedding captures semantic information about the content.&lt;/p&gt;

&lt;p&gt;This allows the system to search based on meaning rather than requiring an exact keyword match.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User query:
"How do I authenticate API requests?"

        ↓

Semantic representation

        ↓

Vector search

        ↓

Relevant documentation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The user's wording doesn't need to exactly match the wording in the original documentation.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Store
&lt;/h2&gt;

&lt;p&gt;The vectors and associated metadata are stored in Qdrant.&lt;/p&gt;

&lt;p&gt;At this point, the website has effectively been transformed into a searchable semantic knowledge base.&lt;/p&gt;

&lt;p&gt;Instead of searching thousands of raw pages every time a user asks a question, the system can search the vector representation of the processed content.&lt;/p&gt;




&lt;h1&gt;
  
  
  Retrieval
&lt;/h1&gt;

&lt;p&gt;When a user asks a question, the query goes through a similar embedding process.&lt;/p&gt;

&lt;p&gt;The system searches Qdrant for chunks that are semantically close to the query.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User:
"How do I authenticate API requests?"

        ↓

Query embedding

        ↓

Qdrant similarity search

        ↓

Relevant documentation chunks

        ↓

LLM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The retrieved chunks become the context for answer generation.&lt;/p&gt;

&lt;p&gt;This is the core of RAG.&lt;/p&gt;

&lt;p&gt;But it also introduces one of the most important problems in RAG systems:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens if the system retrieves the wrong information?&lt;/strong&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Why Retrieval Quality Matters
&lt;/h1&gt;

&lt;p&gt;A RAG system can generate a fluent answer and still be wrong.&lt;/p&gt;

&lt;p&gt;The problem may not be the LLM.&lt;/p&gt;

&lt;p&gt;The retrieval layer may simply have returned poor context.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Question
   ↓
Retriever
   ↓
┌──────────────────────────┐
│ Unrelated chunk          │
│ Navigation content       │
│ Old documentation        │
│ Relevant chunk           │
└──────────────────────────┘
   ↓
LLM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model now has to generate an answer from a noisy context.&lt;/p&gt;

&lt;p&gt;This is one of the most important lessons I learned while building OmniScrape:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Better generation cannot compensate for consistently bad retrieval.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is why ingestion, chunking, metadata, and retrieval quality deserve as much attention as the final LLM.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why Citations Matter
&lt;/h1&gt;

&lt;p&gt;One of the features I considered particularly important was &lt;strong&gt;source awareness&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A generated answer without a source can be difficult to trust.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"API authentication requires a bearer token."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That might sound reasonable.&lt;/p&gt;

&lt;p&gt;But where did it come from?&lt;/p&gt;

&lt;p&gt;A better system can point the user back to the relevant source.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI says something.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the experience becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI says something.

Source:
Authentication Documentation

The user can verify the information.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is particularly useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Technical documentation&lt;/li&gt;
&lt;li&gt;Internal knowledge bases&lt;/li&gt;
&lt;li&gt;Research&lt;/li&gt;
&lt;li&gt;Support systems&lt;/li&gt;
&lt;li&gt;Company documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal isn't just to generate an answer.&lt;/p&gt;

&lt;p&gt;The goal is to make the answer &lt;strong&gt;traceable&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  Engineering Challenges
&lt;/h1&gt;

&lt;p&gt;Building the pipeline also exposed several engineering challenges.&lt;/p&gt;

&lt;h2&gt;
  
  
  Chunk Size
&lt;/h2&gt;

&lt;p&gt;There is no universal perfect chunk size.&lt;/p&gt;

&lt;p&gt;Documentation, blog posts, technical specifications, and long-form articles can have very different structures.&lt;/p&gt;

&lt;p&gt;The chunking strategy therefore needs to match the type of information being processed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Metadata
&lt;/h2&gt;

&lt;p&gt;Metadata initially looks like an implementation detail.&lt;/p&gt;

&lt;p&gt;It isn't.&lt;/p&gt;

&lt;p&gt;Without useful metadata, it becomes much harder to understand where retrieved content came from.&lt;/p&gt;

&lt;p&gt;Useful metadata can include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Source URL&lt;/li&gt;
&lt;li&gt;Page title&lt;/li&gt;
&lt;li&gt;Section&lt;/li&gt;
&lt;li&gt;Chunk ID&lt;/li&gt;
&lt;li&gt;Other document-level information&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This information becomes valuable when debugging retrieval and generating citations.&lt;/p&gt;




&lt;h2&gt;
  
  
  Retrieval Quality
&lt;/h2&gt;

&lt;p&gt;Retrieval is one of the most important parts of the entire system.&lt;/p&gt;

&lt;p&gt;If the wrong chunks are returned, the final answer can be wrong even if the LLM is working exactly as expected.&lt;/p&gt;

&lt;p&gt;This means that evaluating a RAG system shouldn't stop at:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Does the final answer sound good?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A better question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Did the system retrieve the right information to answer the question?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Response Formatting
&lt;/h2&gt;

&lt;p&gt;Even when the correct chunks are retrieved, the final response needs to be presented clearly.&lt;/p&gt;

&lt;p&gt;The system needs to balance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Answer quality&lt;/li&gt;
&lt;li&gt;Source visibility&lt;/li&gt;
&lt;li&gt;Context length&lt;/li&gt;
&lt;li&gt;Response speed&lt;/li&gt;
&lt;li&gt;Readability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Too little context can produce weak answers.&lt;/p&gt;

&lt;p&gt;Too much context can introduce noise.&lt;/p&gt;

&lt;p&gt;Too many citations can make the response difficult to read.&lt;/p&gt;

&lt;p&gt;The final response therefore needs its own design considerations.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why Modular Design?
&lt;/h1&gt;

&lt;p&gt;I intentionally separated the system into components.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;crawler/
├── crawler.py

ingestion/
├── cleaner.py
├── chunker.py

embeddings/
├── embedding_service.py

retrieval/
├── retriever.py

generation/
├── generator.py

api/
├── routes.py

frontend/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact implementation can change, but the principle stays the same:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Each component should have a clear responsibility.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This makes the system easier to evolve.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;Changing the embedding model shouldn't require rewriting the crawler.&lt;/p&gt;

&lt;p&gt;Changing the frontend shouldn't require changing vector storage.&lt;/p&gt;

&lt;p&gt;Improving retrieval shouldn't require rebuilding the entire API.&lt;/p&gt;

&lt;p&gt;This is one of the biggest advantages of modular AI engineering.&lt;/p&gt;




&lt;h1&gt;
  
  
  What OmniScrape Can Become
&lt;/h1&gt;

&lt;p&gt;The architecture isn't limited to one specific website.&lt;/p&gt;

&lt;p&gt;A system like OmniScrape can provide the foundation for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Internal knowledge search&lt;/li&gt;
&lt;li&gt;Documentation assistants&lt;/li&gt;
&lt;li&gt;Customer support tools&lt;/li&gt;
&lt;li&gt;Research assistants&lt;/li&gt;
&lt;li&gt;Technical knowledge bases&lt;/li&gt;
&lt;li&gt;Website question-answering systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The core idea remains the same:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Unstructured Information
        ↓
Structured Knowledge
        ↓
Semantic Retrieval
        ↓
Source-Aware Answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That makes the architecture reusable across different applications.&lt;/p&gt;




&lt;h1&gt;
  
  
  What I Learned From Building It
&lt;/h1&gt;

&lt;p&gt;The biggest lesson from OmniScrape was that RAG isn't simply:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Vector Database + LLM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is considerably more engineering involved.&lt;/p&gt;

&lt;p&gt;Crawling quality affects ingestion.&lt;/p&gt;

&lt;p&gt;Ingestion affects chunking.&lt;/p&gt;

&lt;p&gt;Chunking affects embeddings.&lt;/p&gt;

&lt;p&gt;Embeddings affect retrieval.&lt;/p&gt;

&lt;p&gt;Retrieval affects the context given to the model.&lt;/p&gt;

&lt;p&gt;And the context affects the final answer.&lt;/p&gt;

&lt;p&gt;In other words:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A RAG system is a chain. If one part of the chain is weak, the final answer suffers.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is what made this project particularly valuable to me.&lt;/p&gt;

&lt;p&gt;I wasn't just building a chatbot.&lt;/p&gt;

&lt;p&gt;I was learning how to build an information pipeline around an AI model.&lt;/p&gt;




&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;Building OmniScrape changed the way I think about RAG applications.&lt;/p&gt;

&lt;p&gt;The LLM is only one component.&lt;/p&gt;

&lt;p&gt;The real system includes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Crawling
   ↓
Content Processing
   ↓
Chunking
   ↓
Embeddings
   ↓
Vector Storage
   ↓
Retrieval
   ↓
Generation
   ↓
Sources
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every stage contributes to the final result.&lt;/p&gt;

&lt;p&gt;And when something goes wrong, the solution isn't always to change the model.&lt;/p&gt;

&lt;p&gt;Sometimes the problem is the data.&lt;/p&gt;

&lt;p&gt;Sometimes it's the chunking.&lt;/p&gt;

&lt;p&gt;Sometimes it's retrieval.&lt;/p&gt;

&lt;p&gt;Sometimes it's the way the final context is constructed.&lt;/p&gt;

&lt;p&gt;That's the part of RAG engineering I found most interesting:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Building a system where the model doesn't have to know everything—it just needs to receive the right information at the right time.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Tags
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;#ai&lt;/code&gt; &lt;code&gt;#rag&lt;/code&gt; &lt;code&gt;#python&lt;/code&gt; &lt;code&gt;#fastapi&lt;/code&gt; &lt;code&gt;#qdrant&lt;/code&gt; &lt;code&gt;#webdev&lt;/code&gt;&lt;/p&gt;

</description>
      <category>fastapi</category>
      <category>qdrant</category>
      <category>rag</category>
      <category>brightdatachallenge</category>
    </item>
    <item>
      <title>Lessons I Learned While Building Real-World AI and Automation Projects</title>
      <dc:creator>Meet Dalsania</dc:creator>
      <pubDate>Fri, 21 Aug 2026 09:04:25 +0000</pubDate>
      <link>https://dev.to/meet_dalsania_4cfe420287e/lessons-i-learned-while-building-real-world-ai-and-automation-projects-5dmd</link>
      <guid>https://dev.to/meet_dalsania_4cfe420287e/lessons-i-learned-while-building-real-world-ai-and-automation-projects-5dmd</guid>
      <description>&lt;p&gt;When I started building AI projects, I was mainly focused on one question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Can I make it work?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After working on projects involving AI APIs, web scraping, RAG pipelines, backend integrations, and automation, I realized that this is only the first question.&lt;/p&gt;

&lt;p&gt;A demo can work once.&lt;/p&gt;

&lt;p&gt;A real system needs to keep working when the input changes, an API fails, a website changes its structure, or an AI model returns something unexpected.&lt;/p&gt;

&lt;p&gt;That shift in thinking has probably been one of the most valuable things I've learned while building real-world AI and automation projects.&lt;/p&gt;

&lt;p&gt;In this post, I want to share some of the lessons that changed how I approach projects.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Building a Demo Is Not the Same as Building a System
&lt;/h2&gt;

&lt;p&gt;A simple AI demo often looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Input
  ↓
AI Model
  ↓
Output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And when the output looks correct, it feels like the project is finished.&lt;/p&gt;

&lt;p&gt;But real applications are rarely that simple.&lt;/p&gt;

&lt;p&gt;A more realistic AI pipeline looks something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User Input
    ↓
Validation
    ↓
Processing
    ↓
External API
    ↓
Response Validation
    ↓
Additional Processing
    ↓
Final Output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are multiple places where something can go wrong.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The input may be incomplete.&lt;/li&gt;
&lt;li&gt;An API may return an error.&lt;/li&gt;
&lt;li&gt;OCR may extract incorrect text.&lt;/li&gt;
&lt;li&gt;A website may not contain the expected information.&lt;/li&gt;
&lt;li&gt;Search results may be irrelevant.&lt;/li&gt;
&lt;li&gt;An LLM may return an unexpected format.&lt;/li&gt;
&lt;li&gt;A downstream component may fail because the previous component returned bad data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This made me realize that &lt;strong&gt;the happy path is only one part of the application&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. External APIs Can Fail
&lt;/h2&gt;

&lt;p&gt;Many AI applications depend on several external services.&lt;/p&gt;

&lt;p&gt;In one of my projects, the pipeline involved multiple components for processing product information:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Product Image
     ↓
Google Vision
     ↓
Gemini
     ↓
Google Search
     ↓
Crawl4AI
     ↓
GPT-4
     ↓
Structured Output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each component has a specific responsibility.&lt;/p&gt;

&lt;p&gt;But every external dependency also creates another potential failure point.&lt;/p&gt;

&lt;p&gt;An API can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Return an error&lt;/li&gt;
&lt;li&gt;Return incomplete information&lt;/li&gt;
&lt;li&gt;Take longer than expected&lt;/li&gt;
&lt;li&gt;Change its response&lt;/li&gt;
&lt;li&gt;Hit a rate limit&lt;/li&gt;
&lt;li&gt;Return something different from what the application expects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This taught me not to treat APIs as if they were guaranteed to work.&lt;/p&gt;

&lt;p&gt;Instead, every external service should be considered a component that can fail.&lt;/p&gt;

&lt;p&gt;That means thinking about:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request
  ↓
Did it succeed?
  ↓
Is the response valid?
  ↓
Can the next component use it?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This sounds simple, but it becomes extremely important when multiple APIs are connected together.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. AI Output Needs Validation
&lt;/h2&gt;

&lt;p&gt;One of the biggest differences between traditional software and AI-powered software is that AI output isn't always perfectly predictable.&lt;/p&gt;

&lt;p&gt;Suppose an application expects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"brand"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Nike"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Air Max"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Shoes"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is tempting to simply send the model's response to the next stage.&lt;/p&gt;

&lt;p&gt;But what happens if the response looks like this?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"brand"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Nike"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"product"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Air Max"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or even:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The product appears to be a Nike Air Max shoe.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a human, both responses may be understandable.&lt;/p&gt;

&lt;p&gt;For another program, they may cause problems.&lt;/p&gt;

&lt;p&gt;This is why I started thinking about AI responses as &lt;strong&gt;data that needs validation&lt;/strong&gt;, not as guaranteed application output.&lt;/p&gt;

&lt;p&gt;A useful pipeline is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LLM Response
     ↓
Parse
     ↓
Validate
     ↓
Normalize
     ↓
Use in Application
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This becomes especially important when AI output is being stored in a database or passed to another API.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Modular Architecture Makes Debugging Easier
&lt;/h2&gt;

&lt;p&gt;One of the most important lessons I've learned is to avoid putting an entire workflow into one large script.&lt;/p&gt;

&lt;p&gt;For example, a RAG system can contain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Crawler
   ↓
Content Cleaning
   ↓
Chunking
   ↓
Embeddings
   ↓
Vector Database
   ↓
Retrieval
   ↓
LLM
   ↓
Final Response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If all of this is implemented inside one large function, debugging quickly becomes painful.&lt;/p&gt;

&lt;p&gt;Instead, separating the components makes it easier to understand where a problem is happening.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;crawler/
    crawler.py

processing/
    cleaner.py
    chunker.py

retrieval/
    embeddings.py
    retriever.py

generation/
    generator.py

api/
    routes.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, if retrieval quality is poor, I can investigate the retrieval layer.&lt;/p&gt;

&lt;p&gt;If the content being retrieved is wrong, I can investigate ingestion.&lt;/p&gt;

&lt;p&gt;If the retrieved information is correct but the final response is poor, I can investigate the generation layer.&lt;/p&gt;

&lt;p&gt;That separation saves time.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Better Models Don't Fix Bad Data
&lt;/h2&gt;

&lt;p&gt;This was one of the most important lessons I learned while working with RAG and AI pipelines.&lt;/p&gt;

&lt;p&gt;It is easy to think:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"If I use a better LLM, the answer will become better."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sometimes it will.&lt;/p&gt;

&lt;p&gt;But sometimes the real problem is the information being provided to the model.&lt;/p&gt;

&lt;p&gt;Consider a RAG system.&lt;/p&gt;

&lt;p&gt;If the retriever returns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Chunk 1 → Navigation menu
Chunk 2 → Cookie policy
Chunk 3 → Unrelated documentation
Chunk 4 → Relevant documentation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the LLM now has mostly irrelevant context.&lt;/p&gt;

&lt;p&gt;Even a very capable model has limited ability to produce a reliable answer from poor context.&lt;/p&gt;

&lt;p&gt;This changed the way I think about AI systems.&lt;/p&gt;

&lt;p&gt;Instead of only asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Which model should I use?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I also ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What information am I giving the model?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That question often leads to a better solution.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Web Scraping Is Messier Than It Looks
&lt;/h2&gt;

&lt;p&gt;Working with web data taught me another important lesson.&lt;/p&gt;

&lt;p&gt;A webpage is not a clean document.&lt;/p&gt;

&lt;p&gt;A typical webpage can contain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Navigation
Header
Sidebar
Main Content
Advertisements
Related Articles
Footer
Scripts
Repeated Content
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're building a RAG system, embedding all of that information can introduce unnecessary noise.&lt;/p&gt;

&lt;p&gt;That's why crawling is only the first step.&lt;/p&gt;

&lt;p&gt;The actual pipeline needs to be something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Website
   ↓
Crawl
   ↓
Extract
   ↓
Clean
   ↓
Remove Noise
   ↓
Chunk
   ↓
Embed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This made me realize that &lt;strong&gt;data preparation is often more important than it initially appears&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;AI systems are still affected by the classic principle:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Garbage in, garbage out.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A powerful model doesn't eliminate bad input.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Small Validation Checks Can Prevent Big Problems
&lt;/h2&gt;

&lt;p&gt;Not every reliability improvement needs to be complicated.&lt;/p&gt;

&lt;p&gt;Simple checks can prevent entire parts of a pipeline from failing.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;brand&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Missing brand&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;skip_document&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These checks may look small.&lt;/p&gt;

&lt;p&gt;But imagine a pipeline where one stage returns empty content and that result is passed through five more stages.&lt;/p&gt;

&lt;p&gt;The actual error may happen much later.&lt;/p&gt;

&lt;p&gt;Now debugging becomes much harder.&lt;/p&gt;

&lt;p&gt;Validating data &lt;strong&gt;as it moves through the pipeline&lt;/strong&gt; makes the source of the problem much easier to identify.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Logging Is More Important Than I Expected
&lt;/h2&gt;

&lt;p&gt;When everything works, logs don't seem particularly important.&lt;/p&gt;

&lt;p&gt;When something breaks, they become extremely useful.&lt;/p&gt;

&lt;p&gt;For a multi-stage AI workflow, I want to know:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request received
      ↓
Vision API called
      ↓
OCR response received
      ↓
Product identification completed
      ↓
Search completed
      ↓
Crawler extracted content
      ↓
GPT processing started
      ↓
Structured output generated
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the final result is incorrect, this gives me a way to trace the process.&lt;/p&gt;

&lt;p&gt;Without logs, debugging becomes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Something went wrong somewhere."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With logs, it becomes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The search stage returned irrelevant results, so the problem started here."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's a much more actionable problem.&lt;/p&gt;




&lt;h2&gt;
  
  
  9. Don't Put Everything Into One AI Call
&lt;/h2&gt;

&lt;p&gt;When building AI applications, there can be a temptation to give one model the entire problem.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Image → LLM → Final Answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sometimes that works.&lt;/p&gt;

&lt;p&gt;But breaking a complex problem into smaller stages can make the system easier to understand and improve.&lt;/p&gt;

&lt;p&gt;For example, my product information workflow separates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Visual Information
        ↓
Product Understanding
        ↓
Web Search
        ↓
Web Content Extraction
        ↓
Final Generation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now each component has a defined purpose.&lt;/p&gt;

&lt;p&gt;This also makes it easier to replace one component later.&lt;/p&gt;

&lt;p&gt;For example, I could improve the crawling stage without completely redesigning the product-generation stage.&lt;/p&gt;




&lt;h2&gt;
  
  
  10. Reusable Components Save Time
&lt;/h2&gt;

&lt;p&gt;Another lesson I've learned is that not everything should be rebuilt from scratch.&lt;/p&gt;

&lt;p&gt;If I have already created a useful API integration, utility function, crawler component, or retrieval module, I should think about whether it can be reused.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Project A → Custom crawler
Project B → Another custom crawler
Project C → Another custom crawler
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I would rather move toward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;             ┌── Project A
Reusable ────┼── Project B
Components   └── Project C
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This doesn't mean building an enormous framework before it's needed.&lt;/p&gt;

&lt;p&gt;It means recognizing useful patterns and turning them into reusable pieces when there is a genuine reason to do so.&lt;/p&gt;




&lt;h2&gt;
  
  
  11. Maintainability Matters
&lt;/h2&gt;

&lt;p&gt;A project can work perfectly and still be difficult to maintain.&lt;/p&gt;

&lt;p&gt;For me, maintainability means that I should be able to come back to the project later and understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Where the API is&lt;/li&gt;
&lt;li&gt;Where external services are called&lt;/li&gt;
&lt;li&gt;Where configuration is stored&lt;/li&gt;
&lt;li&gt;Where data is processed&lt;/li&gt;
&lt;li&gt;Where errors are handled&lt;/li&gt;
&lt;li&gt;Where the database is accessed&lt;/li&gt;
&lt;li&gt;Where the AI model is called&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is why I increasingly prefer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Clear naming
+
Small modules
+
Reusable functions
+
Predictable APIs
+
Version control
+
Documentation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;over putting everything into one large file simply because it is faster initially.&lt;/p&gt;




&lt;h2&gt;
  
  
  12. Version Control Is Part of the Engineering Process
&lt;/h2&gt;

&lt;p&gt;Git isn't just something to use when a project is finished.&lt;/p&gt;

&lt;p&gt;It is part of how I understand the evolution of a project.&lt;/p&gt;

&lt;p&gt;A meaningful commit history can show progression such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Initial project setup
        ↓
Add API integration
        ↓
Implement crawler
        ↓
Add vector storage
        ↓
Implement retrieval
        ↓
Add source metadata
        ↓
Connect frontend
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This becomes useful for debugging, experimenting, and going back to a previous working version.&lt;/p&gt;

&lt;p&gt;It also becomes increasingly important when working with other developers.&lt;/p&gt;




&lt;h2&gt;
  
  
  13. I Started Thinking More About Failure Modes
&lt;/h2&gt;

&lt;p&gt;One of the biggest changes in my development process has been asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What happens if this doesn't work?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For every important component, I try to think about the failure case.&lt;/p&gt;

&lt;h3&gt;
  
  
  What if the API fails?
&lt;/h3&gt;

&lt;p&gt;Have an error path.&lt;/p&gt;

&lt;h3&gt;
  
  
  What if the crawler returns nothing?
&lt;/h3&gt;

&lt;p&gt;Don't send empty content to the next stage.&lt;/p&gt;

&lt;h3&gt;
  
  
  What if retrieval returns no useful chunks?
&lt;/h3&gt;

&lt;p&gt;Don't generate an answer as if reliable context existed.&lt;/p&gt;

&lt;h3&gt;
  
  
  What if the LLM response isn't structured correctly?
&lt;/h3&gt;

&lt;p&gt;Validate and handle it.&lt;/p&gt;

&lt;h3&gt;
  
  
  What if the user provides unexpected input?
&lt;/h3&gt;

&lt;p&gt;Validate it before processing.&lt;/p&gt;

&lt;p&gt;This way of thinking has helped me move from simply building features toward building systems.&lt;/p&gt;




&lt;h2&gt;
  
  
  14. What I Would Do Differently Now
&lt;/h2&gt;

&lt;p&gt;If I rebuilt some of my earlier projects today, I would spend more time on the engineering around the AI.&lt;/p&gt;

&lt;p&gt;I would prioritize:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clear input and output contracts&lt;/li&gt;
&lt;li&gt;Better validation&lt;/li&gt;
&lt;li&gt;More structured error handling&lt;/li&gt;
&lt;li&gt;Logging&lt;/li&gt;
&lt;li&gt;Automated testing&lt;/li&gt;
&lt;li&gt;Better configuration management&lt;/li&gt;
&lt;li&gt;Reusable modules&lt;/li&gt;
&lt;li&gt;Cleaner API boundaries&lt;/li&gt;
&lt;li&gt;Documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These aren't necessarily the most exciting parts of an AI project.&lt;/p&gt;

&lt;p&gt;But they are what make the project easier to maintain.&lt;/p&gt;




&lt;h2&gt;
  
  
  15. The Biggest Mindset Shift
&lt;/h2&gt;

&lt;p&gt;The biggest change in my thinking can be summarized in two questions.&lt;/p&gt;

&lt;p&gt;Earlier:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Can I make this work?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Can I make this continue to work?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That difference is important.&lt;/p&gt;

&lt;p&gt;Building real-world AI applications has taught me that the AI model is only one part of the system.&lt;/p&gt;

&lt;p&gt;The surrounding engineering matters just as much.&lt;/p&gt;

&lt;p&gt;You need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Good Input
   ↓
Reliable Processing
   ↓
Good Data
   ↓
Useful Retrieval
   ↓
Controlled AI Generation
   ↓
Validated Output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal isn't just to create something that produces an impressive response.&lt;/p&gt;

&lt;p&gt;The goal is to create something that people can actually use.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Takeaway
&lt;/h2&gt;

&lt;p&gt;Working on AI and automation projects has changed how I approach software development.&lt;/p&gt;

&lt;p&gt;I still care about experimenting with new models and APIs.&lt;/p&gt;

&lt;p&gt;But I now pay much more attention to everything around them:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;architecture, validation, error handling, data quality, retrieval, logging, testing, and maintainability.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The most useful lesson I've learned is this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Don't focus only on making AI produce a good answer. Focus on building a system that can reliably produce useful answers.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's where an AI demo starts becoming an actual application.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I'm Exploring Next
&lt;/h2&gt;

&lt;p&gt;I'm continuing to explore how AI can be combined with traditional software engineering to build systems that are not only intelligent, but also reliable and maintainable.&lt;/p&gt;

&lt;p&gt;The next projects I build will focus less on simply demonstrating what an AI model can do and more on answering a different question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How can I turn AI capabilities into useful software systems?&lt;/strong&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  AI #ArtificialIntelligence #Python #Automation #SoftwareDevelopment #RAG #WebDevelopment #MachineLearning
&lt;/h1&gt;

</description>
      <category>llm</category>
      <category>career</category>
      <category>automation</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Building an AI-Powered Product Information Agent for E-Commerce</title>
      <dc:creator>Meet Dalsania</dc:creator>
      <pubDate>Fri, 21 Aug 2026 08:51:10 +0000</pubDate>
      <link>https://dev.to/meet_dalsania_4cfe420287e/building-an-ai-powered-product-information-agent-for-e-commerce-f6i</link>
      <guid>https://dev.to/meet_dalsania_4cfe420287e/building-an-ai-powered-product-information-agent-for-e-commerce-f6i</guid>
      <description>&lt;p&gt;A product image contains a surprising amount of information.&lt;/p&gt;

&lt;p&gt;A human can look at an image and often recognize the brand, product category, model, and several important characteristics within a few seconds. For an e-commerce system, however, that same image is just an image file.&lt;/p&gt;

&lt;p&gt;Turning that visual information into structured product data requires multiple steps: extracting text, understanding what the product is, finding additional context, and finally producing information that can actually be stored and used by another system.&lt;/p&gt;

&lt;p&gt;That was the problem I wanted to solve with an AI-powered product information agent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Problem&lt;/strong&gt;&lt;br&gt;
E-commerce and inventory systems depend heavily on structured product information.&lt;/p&gt;

&lt;p&gt;A typical product record might contain:&lt;/p&gt;

&lt;p&gt;Brand&lt;br&gt;
Product name&lt;br&gt;
Model&lt;br&gt;
Category&lt;br&gt;
Product descriptors&lt;br&gt;
Product summary&lt;br&gt;
Additional contextual information&lt;/p&gt;

&lt;p&gt;But when the input is a product image, most of that information isn't directly available as structured data.&lt;/p&gt;

&lt;p&gt;OCR can extract text, but text extraction alone doesn't tell you what the product is.&lt;/p&gt;

&lt;p&gt;Search can find relevant information, but search results can also contain unrelated pages, duplicate information, advertisements, and conflicting descriptions.&lt;/p&gt;

&lt;p&gt;An LLM can generate a description, but asking an LLM to work directly from an image without sufficient context can result in incomplete or unsupported information.&lt;/p&gt;

&lt;p&gt;So instead of trying to make one model do everything, I approached the problem as a pipeline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Goal&lt;/strong&gt;&lt;br&gt;
The goal was to build a system that could take a product image as input and produce structured, useful product information as output.&lt;/p&gt;

&lt;p&gt;The basic flow was:&lt;/p&gt;

&lt;p&gt;Product Image&lt;br&gt;
     ↓&lt;br&gt;
Google Vision&lt;br&gt;
     ↓&lt;br&gt;
Extracted Text / Visual Signals&lt;br&gt;
     ↓&lt;br&gt;
Gemini&lt;br&gt;
     ↓&lt;br&gt;
Product Understanding&lt;br&gt;
     ↓&lt;br&gt;
Google Search&lt;br&gt;
     ↓&lt;br&gt;
Additional Context&lt;br&gt;
     ↓&lt;br&gt;
Crawl4AI&lt;br&gt;
     ↓&lt;br&gt;
Relevant Page Content&lt;br&gt;
     ↓&lt;br&gt;
GPT-4&lt;br&gt;
     ↓&lt;br&gt;
Structured Product Information&lt;/p&gt;

&lt;p&gt;The important part of this architecture is that every component has a specific responsibility.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Multiple APIs?&lt;/strong&gt;&lt;br&gt;
One of the biggest design decisions was not trying to solve the entire problem with a single AI model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Google Vision — extracting visible information&lt;/strong&gt;&lt;br&gt;
The first step was extracting information directly from the image.&lt;/p&gt;

&lt;p&gt;Google Vision was useful for OCR and identifying visible signals from the product image.&lt;/p&gt;

&lt;p&gt;For example, an image might contain:&lt;/p&gt;

&lt;p&gt;SONY&lt;br&gt;
WH-1000XM5&lt;br&gt;
Wireless Headphones&lt;/p&gt;

&lt;p&gt;The OCR layer gives the downstream system something concrete to work with.&lt;/p&gt;

&lt;p&gt;However, OCR output isn't necessarily clean.&lt;/p&gt;

&lt;p&gt;Text can be duplicated, characters can be misread, and information can appear in an unexpected order.&lt;/p&gt;

&lt;p&gt;That meant OCR output had to be treated as an input signal rather than as the final answer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gemini — understanding the product&lt;/strong&gt;&lt;br&gt;
The next step was understanding what those extracted signals represented.&lt;/p&gt;

&lt;p&gt;Gemini helped interpret the information and identify the likely product, category, and relevant descriptors.&lt;/p&gt;

&lt;p&gt;This distinction is important.&lt;/p&gt;

&lt;p&gt;There is a difference between:&lt;/p&gt;

&lt;p&gt;"WH-1000XM5"&lt;/p&gt;

&lt;p&gt;and:&lt;/p&gt;

&lt;p&gt;Sony WH-1000XM5 wireless noise-cancelling headphones&lt;/p&gt;

&lt;p&gt;The first is extracted text.&lt;/p&gt;

&lt;p&gt;The second contains meaning.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Google Search — enriching the context&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
Once the system had a likely product identity, search could be used to find additional information.&lt;/p&gt;

&lt;p&gt;This step was necessary because the original image rarely contains everything required for a useful product record.&lt;/p&gt;

&lt;p&gt;Search could help locate product pages, manufacturer information, reviews, specifications, and other relevant context.&lt;/p&gt;

&lt;p&gt;But search introduces another problem: not every result is useful.&lt;/p&gt;

&lt;p&gt;A search query can return:&lt;/p&gt;

&lt;p&gt;Duplicate pages&lt;br&gt;
Unrelated products&lt;br&gt;
Marketplace listings&lt;br&gt;
SEO-generated pages&lt;br&gt;
Partial information&lt;br&gt;
Conflicting descriptions&lt;/p&gt;

&lt;p&gt;So search results needed another layer of processing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Crawl4AI — getting the actual content&lt;/strong&gt;&lt;br&gt;
Instead of relying only on search snippets, Crawl4AI was used to extract relevant content from pages.&lt;/p&gt;

&lt;p&gt;This allowed the pipeline to work with the actual page content rather than treating a search-result snippet as authoritative information.&lt;/p&gt;

&lt;p&gt;The crawler therefore acted as the bridge between:&lt;/p&gt;

&lt;p&gt;Search result&lt;br&gt;
      ↓&lt;br&gt;
Relevant webpage&lt;br&gt;
      ↓&lt;br&gt;
Usable content&lt;br&gt;
GPT-4 — producing the final structured output&lt;/p&gt;

&lt;p&gt;The final stage was turning all of these inputs into a clean product record.&lt;/p&gt;

&lt;p&gt;The model could combine:&lt;/p&gt;

&lt;p&gt;OCR output&lt;br&gt;
Product identification&lt;br&gt;
Search context&lt;br&gt;
Crawled content&lt;/p&gt;

&lt;p&gt;and produce a structured result.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;{&lt;br&gt;
  "brand": "Sony",&lt;br&gt;
  "model": "WH-1000XM5",&lt;br&gt;
  "category": "Wireless Headphones",&lt;br&gt;
  "descriptors": [&lt;br&gt;
    "over-ear",&lt;br&gt;
    "noise cancelling",&lt;br&gt;
    "wireless"&lt;br&gt;
  ],&lt;br&gt;
  "summary": "Sony WH-1000XM5 wireless over-ear headphones..."&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;The important point here is that the LLM wasn't expected to discover everything from scratch.&lt;/p&gt;

&lt;p&gt;It was the final reasoning and formatting layer of a larger information pipeline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Challenges&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The interesting part of the project wasn't making the happy path work.&lt;/p&gt;

&lt;p&gt;It was handling everything that didn't.&lt;/p&gt;

&lt;p&gt;OCR isn't perfect&lt;/p&gt;

&lt;p&gt;Images don't always contain clean, readable text.&lt;/p&gt;

&lt;p&gt;Different fonts, angles, lighting, reflections, and image quality can all affect OCR results.&lt;/p&gt;

&lt;p&gt;The downstream system therefore needed to tolerate imperfect input.&lt;/p&gt;

&lt;p&gt;Search results are noisy&lt;/p&gt;

&lt;p&gt;A product name doesn't guarantee that every search result refers to the same product.&lt;/p&gt;

&lt;p&gt;This became a problem when multiple versions or similar products existed.&lt;/p&gt;

&lt;p&gt;Search therefore had to be treated as an enrichment mechanism rather than unquestioned ground truth.&lt;/p&gt;

&lt;p&gt;Duplicate information&lt;/p&gt;

&lt;p&gt;The same product information can appear across multiple pages.&lt;/p&gt;

&lt;p&gt;Feeding all of that content into the final model increases noise without necessarily improving the answer.&lt;/p&gt;

&lt;p&gt;This made content selection and processing important.&lt;/p&gt;

&lt;p&gt;Prompt design&lt;/p&gt;

&lt;p&gt;The final model needs to understand exactly what information should be returned.&lt;/p&gt;

&lt;p&gt;If the output format isn't constrained, the result can vary significantly between requests.&lt;/p&gt;

&lt;p&gt;Structured prompting therefore became an important part of the pipeline.&lt;/p&gt;

&lt;p&gt;Maintaining reliable output&lt;/p&gt;

&lt;p&gt;An AI system is different from a traditional deterministic API.&lt;/p&gt;

&lt;p&gt;You can't assume that every response will have exactly the same wording or structure.&lt;/p&gt;

&lt;p&gt;For an application that eventually needs to store the result in a database, that is a serious engineering concern.&lt;/p&gt;

&lt;p&gt;The system therefore needs validation around the model output instead of blindly trusting it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why the Architecture Matters&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The biggest lesson from this project was the value of separating responsibilities.&lt;/p&gt;

&lt;p&gt;Instead of creating one large process:&lt;/p&gt;

&lt;p&gt;Image → AI → Answer&lt;/p&gt;

&lt;p&gt;the architecture became:&lt;/p&gt;

&lt;p&gt;Image&lt;br&gt;
 ↓&lt;br&gt;
Vision&lt;br&gt;
 ↓&lt;br&gt;
Product understanding&lt;br&gt;
 ↓&lt;br&gt;
Search&lt;br&gt;
 ↓&lt;br&gt;
Web extraction&lt;br&gt;
 ↓&lt;br&gt;
LLM processing&lt;br&gt;
 ↓&lt;br&gt;
Structured output&lt;/p&gt;

&lt;p&gt;That makes debugging significantly easier.&lt;/p&gt;

&lt;p&gt;If something goes wrong, you can ask:&lt;/p&gt;

&lt;p&gt;Was the OCR incorrect?&lt;br&gt;
Was the product identified incorrectly?&lt;br&gt;
Did search return poor results?&lt;br&gt;
Did the crawler extract the wrong content?&lt;br&gt;
Did the final prompt interpret the information incorrectly?&lt;/p&gt;

&lt;p&gt;Each stage becomes independently inspectable.&lt;/p&gt;

&lt;p&gt;What I Would Improve Next&lt;/p&gt;

&lt;p&gt;There are several directions I would take this system next.&lt;/p&gt;

&lt;p&gt;First, I would add stronger validation between pipeline stages.&lt;/p&gt;

&lt;p&gt;Second, I would improve product matching so that similar products and different product variants are handled more reliably.&lt;/p&gt;

&lt;p&gt;Third, I would introduce better deduplication and source ranking for crawled content.&lt;/p&gt;

&lt;p&gt;Finally, I would make the output schema stricter so that the generated information can be consumed directly by downstream inventory or catalog systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Final Thoughts&lt;/strong&gt;&lt;br&gt;
This project changed the way I think about AI applications.&lt;/p&gt;

&lt;p&gt;The interesting part isn't simply connecting an LLM to an application.&lt;/p&gt;

&lt;p&gt;The real engineering challenge is building the system around the model.&lt;/p&gt;

&lt;p&gt;Vision, search, crawling, retrieval, prompting, validation, and structured output all have different responsibilities.&lt;/p&gt;

&lt;p&gt;That architecture can be useful beyond this particular project.&lt;/p&gt;

&lt;p&gt;The same approach could support:&lt;/p&gt;

&lt;p&gt;Product catalog automation&lt;br&gt;
Inventory onboarding&lt;br&gt;
E-commerce data enrichment&lt;br&gt;
Internal product databases&lt;br&gt;
Automated product listing workflows&lt;/p&gt;

&lt;p&gt;For me, the biggest takeaway was simple:&lt;/p&gt;

&lt;p&gt;A useful AI system is usually a pipeline of smaller, well-defined components rather than one giant AI call.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>productivity</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
