Product Search with Vector
Semantic product search with metadata filtering
Build a product search that understands natural language queries and filters by category, price, or other attributes.
The Pattern
Vector search finds semantically similar products. Combine with metadata filtering for precise results.
import { createAgent, type AgentContext } from '@agentuity/runtime';
import { z } from 'zod';
interface ProductMetadata {
name: string;
price: number;
category: string;
inStock: boolean;
}
const agent = createAgent('Product Search', {
description: 'Semantic search for products',
schema: {
input: z.object({
query: z.string().describe('Natural language search query'),
category: z.string().optional().describe('Filter by category'),
maxPrice: z.number().optional().describe('Maximum price filter'),
limit: z.number().min(1).max(50).default(10),
}),
output: z.object({
products: z.array(z.object({
id: z.string(),
name: z.string(),
description: z.string(),
price: z.number(),
category: z.string(),
relevance: z.number(),
})),
total: z.number(),
}),
},
handler: async (ctx: AgentContext, input) => {
ctx.logger.info('Searching products', {
query: input.query,
category: input.category,
maxPrice: input.maxPrice,
});
// Search with semantic similarity
const results = await ctx.vector.search<ProductMetadata>('products', {
query: input.query,
limit: input.limit * 2, // Fetch extra for filtering
similarity: 0.6,
});
// Apply metadata filters
let filtered = results;
if (input.category) {
filtered = filtered.filter(r =>
r.metadata?.category?.toLowerCase() === input.category?.toLowerCase()
);
}
if (input.maxPrice) {
filtered = filtered.filter(r =>
(r.metadata?.price ?? Infinity) <= input.maxPrice!
);
}
// Only show in-stock items
filtered = filtered.filter(r => r.metadata?.inStock !== false);
// Limit to requested count
const products = filtered.slice(0, input.limit).map(r => ({
id: r.key,
name: r.metadata?.name || 'Unknown',
description: r.document || '',
price: r.metadata?.price || 0,
category: r.metadata?.category || 'Uncategorized',
relevance: r.similarity,
}));
ctx.logger.info('Search complete', {
found: results.length,
afterFilters: products.length,
});
return {
products,
total: products.length,
};
},
});
export default agent;Indexing Products
Add products to the vector database:
// src/agent/product-indexer/agent.ts
import { createAgent, type AgentContext } from '@agentuity/runtime';
import { z } from 'zod';
const ProductSchema = z.object({
id: z.string(),
name: z.string(),
description: z.string(),
price: z.number(),
category: z.string(),
inStock: z.boolean().default(true),
});
const agent = createAgent('ProductIndexer', {
schema: {
input: z.object({
products: z.array(ProductSchema),
}),
output: z.object({
indexed: z.number(),
}),
},
handler: async (ctx: AgentContext, input) => {
for (const product of input.products) {
// Use description as the searchable document
await ctx.vector.upsert('products', {
key: product.id,
document: `${product.name}. ${product.description}`,
metadata: {
name: product.name,
price: product.price,
category: product.category,
inStock: product.inStock,
},
});
}
return { indexed: input.products.length };
},
});
export default agent;Route with Query Parameters
import { createRouter } from '@agentuity/runtime';
import productSearch from '@agent/product-search';
const router = createRouter();
router.get('/search', async (c) => {
const query = c.req.query('q') || '';
const category = c.req.query('category');
const maxPrice = c.req.query('maxPrice');
const limit = parseInt(c.req.query('limit') || '10');
const result = await productSearch.run({
query,
category,
maxPrice: maxPrice ? parseFloat(maxPrice) : undefined,
limit,
});
return c.json(result);
});
export default router;Example Usage
# Natural language search
curl "http://localhost:3500/products/search?q=comfortable%20office%20chair"
# With filters
curl "http://localhost:3500/products/search?q=laptop&category=electronics&maxPrice=1000"AI-Powered Recommendations
Enhance search results with AI-generated recommendations. Use generateObject to analyze matches and suggest the best option.
import { createAgent, type AgentContext } from '@agentuity/runtime';
import { generateObject } from 'ai';
import { openai } from '@ai-sdk/openai';
import { s } from '@agentuity/schema';
import { z } from 'zod';
interface ProductMetadata {
sku: string;
name: string;
price: number;
rating: number;
description: string;
feedback: string;
}
const agent = createAgent('Product Advisor', {
description: 'Semantic search with AI recommendations',
schema: {
input: s.object({
query: s.string(),
}),
output: s.object({
matches: s.array(
s.object({
sku: s.string(),
name: s.string(),
price: s.number(),
rating: s.number(),
similarity: s.number(),
})
),
recommendation: s.string(),
recommendedSKU: s.string(),
}),
},
handler: async (ctx: AgentContext, input) => {
// Semantic search for matching products
const results = await ctx.vector.search<ProductMetadata>('products', {
query: input.query,
limit: 3,
similarity: 0.3,
});
if (results.length === 0) {
return {
matches: [],
recommendation: 'No matching products found. Try a different search.',
recommendedSKU: '',
};
}
// Format matches for response
const matches = results.map((r) => ({
sku: r.metadata?.sku ?? '',
name: r.metadata?.name ?? '',
price: r.metadata?.price ?? 0,
rating: r.metadata?.rating ?? 0,
similarity: r.similarity,
}));
// Build context for AI recommendation
const context = results
.map((r) => {
const p = r.metadata;
return `${p?.name}: SKU ${p?.sku}, $${p?.price}, ${p?.rating} stars. "${p?.feedback}"`;
})
.join('\n');
// Generate personalized recommendation
const { object } = await generateObject({
model: openai('gpt-5-mini'),
system: 'You are a product consultant. Provide a brief 2-3 sentence recommendation based on the search results. Reference customer feedback when relevant.',
prompt: `Customer searched for: "${input.query}"\n\nMatching products:\n${context}`,
schema: z.object({
summary: z.string().describe('Brief recommendation explaining the best choice'),
recommendedSKU: z.string().describe('SKU of the recommended product'),
}),
});
return {
matches,
recommendation: object.summary,
recommendedSKU: object.recommendedSKU,
};
},
});
export default agent;When to Use AI Recommendations
Add AI recommendations when customers benefit from personalized guidance: comparing similar products, explaining trade-offs, or highlighting relevant features based on their search intent.
Example Response
{
"matches": [
{ "sku": "CHAIR-ERG-001", "name": "ErgoMax Pro", "price": 549, "rating": 4.8, "similarity": 0.89 },
{ "sku": "CHAIR-BUD-002", "name": "ComfortBasic", "price": 129, "rating": 4.2, "similarity": 0.76 }
],
"recommendation": "For a comfortable office chair, I recommend the ErgoMax Pro. Customers report significant back pain relief, and the premium lumbar support justifies the higher price for long work sessions.",
"recommendedSKU": "CHAIR-ERG-001"
}Key Points
- Semantic search finds products by meaning, not just keywords
- Metadata filtering narrows results by category, price, stock
- AI recommendations add personalized guidance based on search context
- Customer feedback in metadata helps AI make relevant suggestions
- Document field should include searchable text (name + description)
See Also
- Vector Storage for all vector operations
- Build a RAG Agent for question-answering
- AI SDK Integration for
generateObjectpatterns
Need Help?
Join our Community for assistance or just to hang with other humans building agents.
Send us an email at hi@agentuity.com if you'd like to get in touch.
Please Follow us on
If you haven't already, please Signup for your free account now and start building your first agent!