Why RAG over a simple FAQ bot
Agent 01 demonstrated grounding from a static policy document embedded in a system prompt. That works for short, fixed content. But a product catalogue with hundreds of SKUs, descriptions, materials, dimensions, and care instructions cannot fit in a context window — and it changes frequently.
The objective was to build a retrieval-augmented system that dynamically fetches only the relevant products at query time — making the agent scalable, accurate, and updatable without rebuilding the prompt.
The Problem
A static system prompt cannot hold a full product catalogue. It becomes outdated as inventory changes and returns irrelevant results as the catalogue grows.
The Objective
A dynamic RAG pipeline that retrieves the most relevant products at query time from a vector database, cites its sources, and answers only from what it finds.
How the pipeline is orchestrated
The n8n workflow wires the full pipeline end-to-end — from receiving the webhook to returning the grounded answer. Each node maps directly to a pipeline stage: query embedding, Pinecone retrieval, context assembly, Claude generation, and structured response. Error handling is wired at every step so failures surface cleanly rather than silently.
How the pipeline works
Every query passes through a four-stage pipeline before an answer is generated. Each stage is independently auditable — you can see exactly which products were retrieved and why the answer was produced.
User asks a product question
OpenAI converts query to a 1536-dimension vector
Pinecone returns top-3 nearest product vectors
Claude answers using only retrieved products
// 1. User query arrives query = "Do you have any lavender candles?" // 2. Embed via OpenAI text-embedding-3-small vector = openai.embed(query) // → [0.023, -0.412, ...] 1536 dims // 3. Semantic search in Pinecone matches = pinecone.query(vector, topK=3) // → CND-004 Lavender & Chamomile (score: 0.91) // → CND-009 Eucalyptus & Mint (score: 0.74) // → CND-007 Oud & Rose (score: 0.68) // 4. Claude generates answer from retrieved context only answer = claude.chat(system_prompt, retrieved_products, query) // Strict rule: only answer from what was retrieved
Building the vector store
The catalogue contains 100 products across 10 categories. Each product is converted into a single richly structured text chunk — combining all fields into a coherent, searchable passage. This one-product-per-chunk strategy ensures that retrieval always returns complete, coherent records rather than fragmented snippets.
Product: Lavender & Chamomile Sleep Candle (SKU: CND-004) Category: Candles | Price: £26.00 | Stock: In Stock Description: Calming lavender and chamomile blend formulated to promote relaxation. Ideal for bedrooms. 45-hour burn. Materials: 100% soy wax, cotton wick Dimensions: H 9cm × Ø 9cm Colours: Pale Lavender Care: Extinguish 30 minutes before sleep. Tags: sleep, lavender, relaxing, bedroom, soy
Each chunk is embedded using OpenAI text-embedding-3-small (1536 dimensions) and upserted into a Pinecone serverless index with full metadata stored alongside the vector — so retrieval returns the complete product record, not just an ID.
What this agent teaches
Full end-to-end retrieval-augmented generation — embed, retrieve, generate — running live in the browser via a serverless proxy.
Queries and documents converted to high-dimensional vectors. Semantic similarity — not keyword matching — drives retrieval.
100 product vectors stored in a Pinecone serverless index. Cosine similarity search returns the top-3 nearest products at query time.
Claude is instructed to answer only from retrieved products. If Pinecone returns weak results, the agent says so — no hallucination.
Every answer shows the exact products retrieved from Pinecone — making the RAG pipeline visible and auditable to the user.
All three API keys (Anthropic, OpenAI, Pinecone) are stored in Cloudflare Worker environment variables — never exposed in the browser.
What I'd do differently
One chunk per product is the right unit for a catalogue. Splitting product descriptions into smaller chunks (sentences or paragraphs) leads to fragmented retrievals — you get a care instruction without the price, or a colour option without the description. Full-product chunks return complete, useful records.
Tags and structured fields dramatically improve retrieval quality. Including tags like "sleep, lavender, bedroom, soy" means a query like "help me relax at bedtime" retrieves the right candle even without the word "candle" in the query. Semantic overlap matters.
Top-3 retrieval is a deliberate product decision. Returning more products increases recall but reduces answer precision — Claude starts hedging between too many options. Three is the sweet spot for a helpful, specific answer without overwhelming the user.
Showing retrieved sources is a trust mechanism, not just a demo feature. When users can see which products were retrieved, they understand why the answer is what it is — and they trust it more. In production, this transparency reduces support escalations.
The Cloudflare Worker as a multi-action proxy is a reusable pattern. Routing embed, retrieve, and generate through a single Worker endpoint — differentiated by an action field — means the browser never holds secrets, and the same infrastructure scales to Agent 03 and beyond.