Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 

Repository files navigation

🔍 Site Search Agent

AI-powered semantic product search using real vector embeddings. Anthropic Claude + Voyage AI embeddings + Neon pgvector.

New concepts vs Projects 1 and 2:

  • Real embeddings (Voyage AI voyage-3-lite, 512 dimensions)
  • pgvector cosine similarity search in Neon Postgres
  • Two-phase RAG pipeline (indexing + querying)
  • Vocabulary mismatch solved — "footwear for jogging" finds running shoes

Quick Start

# Step 1: Install
cd server && npm install
cd ../client && npm install

# Step 2: Configure
cd server
cp .env.example .env
# Fill in:
# ANTHROPIC_API_KEY=sk-ant-xxxx
# VOYAGE_API_KEY=pa-xxxxxxxxxxxx
# DATABASE_URL=postgresql://...

# Step 3: Seed database (run once)
npm run seed
# Embeds 50 products via Voyage AI → stores in pgvector
# Takes 2-3 minutes on free Voyage AI tier

# Step 4: Start
# Terminal 1:
cd server && npm run dev    # port 3003
# Terminal 2:
cd client && npm run dev    # port 5175

How It Works — Full Detailed Flow


PHASE 1 — Indexing (seed.js, runs once)

Product text sent to Voyage AI:

"Nike Air Zoom Pegasus 40.
 Lightweight daily running shoe with responsive cushioning.
 Tags: running, lightweight, comfort, daily trainer"

Voyage AI Request:

POST https://api.voyageai.com/v1/embeddings
Authorization: Bearer pa-xxxxxxxxxxxx

{
  "model":      "voyage-3-lite",
  "input":      ["Nike Air Zoom Pegasus 40. Lightweight daily running shoe..."],
  "input_type": "document"
}

Voyage AI Response:

{
  "data": [
    {
      "embedding": [0.023, 0.187, -0.045, 0.312, -0.089, 0.156, ...],
      "index": 0
    }
  ],
  "model": "voyage-3-lite",
  "usage": { "total_tokens": 48 }
}

(512 numbers per product)

Stored in Neon Postgres:

INSERT INTO products
  (product_id, name, description, category, price, rating, tags, embedding)
VALUES (
  'prod_001',
  'Nike Air Zoom Pegasus 40',
  'Lightweight daily running shoe...',
  'shoes',
  47.99,
  4.5,
  '["running", "lightweight", "comfort"]',
  '[0.023, 0.187, -0.045, 0.312, ...]'::vector
)

After seeding:

50 rows in products table
Each row has 512-dimensional vector
ivfflat index created for fast similarity search

PHASE 2 — Querying (every search)


User Input

"comfortable running shoes under $50"

System Prompt (sent every turn, never changes)

You are a helpful product search assistant for an online store.

When a user searches for products:
1. ALWAYS call search_products first with their natural language query
2. If user mentions price limits → call filter_results with max_price/min_price
3. If user asks about a specific product → call get_product_detail
4. Present results in a friendly, helpful way with key details

Available categories: shoes, electronics, clothing, sports, books

Tool Definitions (sent every turn, never changes)

[
  {
    "name": "search_products",
    "description": "Search catalog using semantic similarity. ALWAYS call first.",
    "input_schema": {
      "type": "object",
      "properties": {
        "query":    { "type": "string",  "description": "Natural language search query" },
        "category": { "type": "string",  "description": "shoes, electronics, clothing, sports, books" },
        "limit":    { "type": "number",  "description": "Max results (default 10)" }
      },
      "required": ["query"]
    }
  },
  {
    "name": "filter_results",
    "description": "Filter and sort results by price, rating, category.",
    "input_schema": {
      "type": "object",
      "properties": {
        "results":    { "type": "array"  },
        "max_price":  { "type": "number" },
        "min_price":  { "type": "number" },
        "min_rating": { "type": "number" },
        "sort_by":    { "type": "string", "description": "relevance, price_asc, price_desc, rating" },
        "top_n":      { "type": "number" }
      },
      "required": ["results"]
    }
  },
  {
    "name": "get_product_detail",
    "description": "Get full details of a specific product by ID.",
    "input_schema": {
      "type": "object",
      "properties": {
        "product_id": { "type": "string", "description": "e.g. prod_001" }
      },
      "required": ["product_id"]
    }
  }
]

LLM Request — Turn 1

{
  "model":      "claude-sonnet-4-6",
  "max_tokens": 1024,
  "system":     "...system prompt above...",
  "tools":      [...3 tool definitions above...],
  "messages": [
    {
      "role":    "user",
      "content": "comfortable running shoes under $50"
    }
  ]
}

LLM Response — Turn 1

{
  "stop_reason": "tool_use",
  "content": [
    {
      "type": "text",
      "text": "Let me search for comfortable running shoes for you."
    },
    {
      "type":  "tool_use",
      "id":    "tool_001",
      "name":  "search_products",
      "input": {
        "query":    "comfortable running shoes",
        "category": "shoes",
        "limit":    10
      }
    }
  ],
  "usage": {
    "input_tokens":  420,
    "output_tokens": 85
  }
}

stop_reason: "tool_use" → loop continues


RAG Execution — search_products

Step 1 — Embed the query:

POST https://api.voyageai.com/v1/embeddings

{
  "model":      "voyage-3-lite",
  "input":      ["comfortable running shoes"],
  "input_type": "query"
}
{
  "data": [
    {
      "embedding": [0.021, 0.185, -0.041, 0.308, -0.087, ...]
    }
  ]
}

Step 2 — pgvector cosine similarity search:

SELECT
  product_id,
  name,
  price,
  rating,
  1 - (embedding <=> '[0.021, 0.185, -0.041, 0.308, ...]'::vector) AS similarity
FROM products
WHERE category = 'shoes'
ORDER BY embedding <=> '[0.021, 0.185, ...]'::vector
LIMIT 10

Results with similarity scores:

[
  { "product_id": "prod_001", "name": "Nike Air Zoom Pegasus 40", "price": 47.99, "similarity": 0.9421 },
  { "product_id": "prod_005", "name": "Brooks Ghost 15",           "price": 44.99, "similarity": 0.9187 },
  { "product_id": "prod_004", "name": "Hoka Clifton 9",            "price": 49.99, "similarity": 0.9043 },
  { "product_id": "prod_003", "name": "New Balance Fresh Foam",    "price": 64.99, "similarity": 0.8876 },
  { "product_id": "prod_002", "name": "Adidas Ultraboost 23",      "price": 89.99, "similarity": 0.8654 },
  { "product_id": "prod_007", "name": "ASICS Gel-Nimbus 25",       "price": 74.99, "similarity": 0.8521 },
  { "product_id": "prod_006", "name": "Saucony Endorphin Speed",   "price": 99.99, "similarity": 0.8234 },
  { "product_id": "prod_008", "name": "Vans Old Skool",            "price": 39.99, "similarity": 0.6123 },
  { "product_id": "prod_009", "name": "Converse Chuck Taylor",     "price": 34.99, "similarity": 0.5876 },
  { "product_id": "prod_010", "name": "Merrell Moab 3 Hiking",     "price": 79.99, "similarity": 0.5234 }
]

LLM Request — Turn 2

{
  "model":   "claude-sonnet-4-6",
  "system":  "...same system prompt...",
  "tools":   [...same tools...],
  "messages": [
    {
      "role":    "user",
      "content": "comfortable running shoes under $50"
    },
    {
      "role": "assistant",
      "content": [
        { "type": "text",     "text": "Let me search for comfortable running shoes." },
        { "type": "tool_use", "id": "tool_001", "name": "search_products",
          "input": { "query": "comfortable running shoes", "category": "shoes" } }
      ]
    },
    {
      "role": "user",
      "content": [
        {
          "type":        "tool_result",
          "tool_use_id": "tool_001",
          "content":     "{\"found\": 10, \"products\": [{\"name\": \"Nike Air Zoom...\", ...}]}"
        }
      ]
    }
  ]
}

LLM Response — Turn 2

{
  "stop_reason": "tool_use",
  "content": [
    {
      "type":  "tool_use",
      "id":    "tool_002",
      "name":  "filter_results",
      "input": {
        "results":   ["...10 products from search..."],
        "max_price": 50,
        "sort_by":   "relevance",
        "top_n":     3
      }
    }
  ],
  "usage": {
    "input_tokens":  890,
    "output_tokens": 120
  }
}

stop_reason: "tool_use" → loop continues


filter_results Execution

Input: 10 products + max_price: 50

Filter price <= $50:
  Nike Air Zoom Pegasus  $47.99  ✅ keep  (similarity: 0.9421)
  Brooks Ghost 15        $44.99  ✅ keep  (similarity: 0.9187)
  Hoka Clifton 9         $49.99  ✅ keep  (similarity: 0.9043)
  New Balance 1080       $64.99  ❌ removed (too expensive)
  Adidas Ultraboost      $89.99  ❌ removed (too expensive)
  ASICS Gel-Nimbus       $74.99  ❌ removed (too expensive)
  Saucony Endorphin      $99.99  ❌ removed (too expensive)
  Vans Old Skool         $39.99  ❌ removed (low similarity: 0.61)
  Converse Chuck Taylor  $34.99  ❌ removed (low similarity: 0.58)
  Merrell Moab Hiking    $79.99  ❌ removed (too expensive)

Return top 3 by relevance:
  Nike Air Zoom, Brooks Ghost, Hoka Clifton

LLM Request — Turn 3

{
  "model":   "claude-sonnet-4-6",
  "system":  "...same system prompt...",
  "tools":   [...same tools...],
  "messages": [
    { "role": "user",      "content": "comfortable running shoes under $50" },
    { "role": "assistant", "content": [ "...search tool_use..." ] },
    { "role": "user",      "content": [ "...search tool_result..." ] },
    { "role": "assistant", "content": [ "...filter tool_use..." ] },
    {
      "role": "user",
      "content": [
        {
          "type":        "tool_result",
          "tool_use_id": "tool_002",
          "content":     "{\"filtered_count\": 3, \"products\": [Nike, Brooks, Hoka]}"
        }
      ]
    }
  ]
}

LLM Response — Turn 3 (Final)

{
  "stop_reason": "end_turn",
  "content": [
    {
      "type": "text",
      "text": "Here are 3 comfortable running shoes under $50:\n\n1. Nike Air Zoom Pegasus 40 — $47.99 ⭐ 4.5\n   Lightweight daily trainer with responsive cushioning. Perfect for everyday runs.\n\n2. Brooks Ghost 15 — $44.99 ⭐ 4.4\n   Versatile everyday shoe with soft cushioning. Works for all types of runners.\n\n3. Hoka Clifton 9 — $49.99 ⭐ 4.7\n   Maximum cushion road shoe. Soft and lightweight, great for recovery runs."
    }
  ],
  "usage": {
    "input_tokens":  1240,
    "output_tokens": 185
  }
}

stop_reason: "end_turn" → loop exits → response shown to user


Vocabulary Mismatch — The Key Test

Keyword search (Projects 1 & 2):
  Query: "footwear for jogging"
  Looks for words: "footwear" "jogging"
  Found in products: NONE ❌
  Result: empty

Vector search (this project):
  Query: "footwear for jogging"
  Voyage AI embeds → [0.019, 0.181, -0.038, ...]
  Compare against stored vectors:
    Nike Air Zoom   → similarity: 0.89 ✅
    Brooks Ghost    → similarity: 0.86 ✅
    Hoka Clifton    → similarity: 0.84 ✅
  Result: running shoes found ✅

Same meaning, different words — vector search handles it.

What's in the Log Files

Every session creates server/logs/session-*.txt:

╔══════════════════════════════════════════════════════════╗
║        SITE SEARCH AGENT — SESSION LOG                  ║
╚══════════════════════════════════════════════════════════╝
Started   : 2026-07-28T10:30:00.000Z
Provider  : Anthropic Claude (claude-sonnet-4-6)
Embeddings: Voyage AI (voyage-3-lite)
DB        : Neon Postgres + pgvector

── SYSTEM PROMPT ──────────────────────────────────────────
...

── USER QUERY ─────────────────────────────────────────────
comfortable running shoes under $50

── EMBEDDING REQUEST ──────────────────────────────────────
Model      : voyage-3-lite
Input type : query
Text       : "comfortable running shoes"

── EMBEDDING RESPONSE ─────────────────────────────────────
Vector dimensions : 512
First 5 values    : [0.0213, 0.1854, -0.0412, 0.3082, -0.0871...]

── LLM REQUEST Turn 1 ─────────────────────────────────────
Messages in ctx : 1
Full request body: { messages: [...], tools: [...] }

── LLM RESPONSE Turn 1 ────────────────────────────────────
stop_reason    : tool_use
Input tokens   : 420
Output tokens  : 85
Full response  : { content: [...] }

── LLM DECISION Turn 1 ────────────────────────────────────
Decision : CALL TOOL (search_products)
Input    : { query: "comfortable running shoes", category: "shoes" }

── TOOL CALL : search_products ────────────────────────────
── VECTOR SEARCH ──────────────────────────────────────────
Query   : "comfortable running shoes"
Results : 10 products found

── VECTOR RESULTS ─────────────────────────────────────────
  1. [prod_001] Nike Air Zoom Pegasus 40 — $47.99 — score: 0.9421
  2. [prod_005] Brooks Ghost 15 — $44.99 — score: 0.9187
  3. [prod_004] Hoka Clifton 9 — $49.99 — score: 0.9043
  ...

── TOOL RESULT : search_products ──────────────────────────
{ found: 10, products: [...] }

── LLM REQUEST Turn 2 ─────────────────────────────────────
Messages in ctx : 3

── LLM RESPONSE Turn 2 ────────────────────────────────────
stop_reason    : tool_use
Input tokens   : 890
Output tokens  : 120

── LLM DECISION Turn 2 ────────────────────────────────────
Decision : CALL TOOL (filter_results)
Input    : { max_price: 50, sort_by: "relevance", top_n: 3 }

── TOOL CALL : filter_results ─────────────────────────────
── TOOL RESULT : filter_results ───────────────────────────
{ filtered_count: 3, products: [Nike, Brooks, Hoka] }

── LLM REQUEST Turn 3 ─────────────────────────────────────
Messages in ctx : 5

── LLM RESPONSE Turn 3 ────────────────────────────────────
stop_reason    : end_turn
Input tokens   : 1240
Output tokens  : 185

── FINAL RESPONSE ─────────────────────────────────────────
Here are 3 comfortable running shoes under $50...

══ SESSION COMPLETE ═══════════════════════════════════════
Turns          : 3
Total input    : 2550 tokens
Total output   : 390 tokens
Approx cost    : $0.000014

Project Structure

site-search-agent/
  ├── server/
  │   ├── index.js       # Express server (port 3003)
  │   ├── agent.js       # Agentic loop + Anthropic SDK
  │   ├── rag.js         # pgvector search + filter/rank
  │   ├── embeddings.js  # Voyage AI embedding calls
  │   ├── db.js          # Neon Postgres + pgvector schema
  │   ├── seed.js        # Seed 50 products with embeddings
  │   ├── logger.js      # Full session logging
  │   └── .env.example
  └── client/
      └── src/
          └── App.jsx    # React UI with product cards (port 5175)

What's New vs Previous Projects

Project 1 Calendar Project 2 IT Support Project 3 Site Search
RAG ❌ None ✅ JSON keyword ✅ Vector semantic
Embeddings ✅ Voyage AI
Vector DB ✅ Neon pgvector
Similarity Keyword score ✅ Cosine similarity
Vocab match ✅ Solved
Tools 2 5 3
Token cost log

Try These Searches

running shoes                              ← exact match
running shoes under $50                   ← price filter
footwear for jogging                      ← vocabulary mismatch test
something to listen to music on the go    ← natural language
I want to get fit at home                 ← vague intent
gifts under $20 for fitness lovers        ← budget + category
warm layers for cold weather hiking       ← multi-concept

About

AI-powered semantic product search agent using Voyage AI embeddings, pgvector cosine similarity, and Anthropic Claude — solves vocabulary mismatch so "footwear for jogging" finds running shoes. Node.js + React.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages