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
# 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 5175Product 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
"comfortable running shoes under $50"
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
[
{
"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"]
}
}
]{
"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"
}
]
}{
"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
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 10Results 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 }
]{
"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...\", ...}]}"
}
]
}
]
}{
"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
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
{
"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]}"
}
]
}
]
}{
"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
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.
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
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)
| 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 | ✅ | ✅ | ✅ |
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