Complete Guide to AI Integration in Web Applications with Vercel AI SDK
A comprehensive guide covering 17+ AI integration scenarios, step-by-step implementation of an e-commerce chatbot with AI agents, and a RAG chat application. Includes practical examples using Vercel AI SDK, AI Elements, and AI Gateway with Next.js and React.
Complete Guide to AI Integration in Modern Web Applications
Executive Summary
This comprehensive guide demonstrates how to integrate AI capabilities into your applications using the Vercel AI SDK. We'll explore 15+ practical scenarios showing traditional approaches versus AI-enhanced solutions, provide detailed integration instructions for Next.js and React apps, and build two complete projects: an AI-powered e-commerce chatbot with agents and a RAG (Retrieval-Augmented Generation) chat application.
Table of Contents
- 15+ AI Integration Scenarios
- Getting Started with Vercel AI SDK
- Vercel AI Components: SDK, Elements, and Gateway
- Project 1: E-commerce AI Chatbot with Agents
- Project 2: RAG Chat Application
- Best Practices and Deployment
Part 1: 15+ AI Integration Scenarios
Scenario 1: User Authentication & Account Creation
Traditional Approach: Users navigate through multiple forms with separate pages for registration, email verification, password creation, and profile setup. This typically involves:
- Manual form filling with 8-12 fields
- Multiple validation steps
- Email verification process
- Password strength requirements
- Profile completion wizard
- Average time: 5-10 minutes
AI Integration: Implement a conversational AI agent that guides users through account creation naturally:
// AI Agent handles account creation conversationally
const createAccountAgent = async (userMessage: string) => {
const result = await streamText({
model: openai("gpt-4"),
tools: {
validateEmail: tool({
description: "Validate email format and availability",
parameters: z.object({ email: z.string() }),
execute: async ({ email }) => {
// Check email validity and availability
},
}),
createAccount: tool({
description: "Create new user account",
parameters: z.object({
email: z.string(),
name: z.string(),
preferences: z.object({}),
}),
execute: async (data) => {
// Create account in database
},
}),
},
});
};Improvements:
- Reduces registration time by 60-70%
- Natural language input eliminates form fatigue
- Intelligent validation catches errors in real-time
- Personalized onboarding based on conversation
- Accessibility improvements for users with disabilities
- Multi-language support without separate forms
Scenario 2: Product Search & Discovery
Traditional Approach: Users interact with:
- Search bar with keyword matching
- Category filters (dropdowns, checkboxes)
- Price range sliders
- Sort options
- Pagination through results
- Problem: Users must know exact product names or categories
AI Integration: Natural language product discovery with semantic search:
const productSearchAgent = {
searchProducts: tool({
description: "Search products using natural language",
parameters: z.object({
query: z.string(),
preferences: z.object({
priceRange: z.string().optional(),
style: z.string().optional(),
}),
}),
execute: async ({ query, preferences }) => {
// Use vector embeddings for semantic search
const embedding = await embed(query);
const products = await vectorSearch(embedding, preferences);
return products;
},
}),
};Improvements:
- "Find me a laptop for video editing under $1500" works instantly
- Understands context: "something similar but cheaper"
- Learns from browsing behavior
- 40% faster product discovery
- Higher conversion rates (studies show 25-35% improvement)
- Reduces bounce rate from poor search results
Scenario 3: Customer Support
Traditional Approach:
- Static FAQ pages
- Email support (24-48 hour response time)
- Live chat during business hours
- Support ticket systems
- Knowledge base articles users must search manually
AI Integration: 24/7 intelligent support agent with context awareness:
const supportAgent = {
answerQuery: tool({
description: "Answer customer support questions",
parameters: z.object({
question: z.string(),
orderId: z.string().optional(),
userId: z.string(),
}),
execute: async ({ question, orderId, userId }) => {
// RAG system searches knowledge base + order history
const context = await retrieveContext(question, userId);
const answer = await generateAnswer(question, context);
return answer;
},
}),
escalateToHuman: tool({
description: "Escalate complex issues to human support",
execute: async ({ issue }) => {
// Create support ticket
},
}),
};Improvements:
- Instant responses 24/7
- Reduces support tickets by 60-80%
- Handles multiple languages automatically
- Context-aware answers using order history
- Seamless escalation to human agents when needed
- Cost reduction: $6-8 per ticket vs $15-25 for human support
Scenario 4: Content Generation for E-commerce
Traditional Approach:
- Hire copywriters for product descriptions
- Manual creation of SEO metadata
- Template-based descriptions
- Time: 30-60 minutes per product
- Inconsistent tone and quality
AI Integration: Automated content generation system:
const contentGenerator = tool({
description: "Generate product content",
parameters: z.object({
productData: z.object({
name: z.string(),
features: z.array(z.string()),
category: z.string(),
}),
}),
execute: async ({ productData }) => {
const content = await generateText({
model: openai("gpt-4"),
prompt: `Generate compelling product description, SEO title,
meta description, and bullet points for: ${JSON.stringify(productData)}`,
});
return content;
},
});Improvements:
- Generate 100+ product descriptions in minutes
- Consistent brand voice across all products
- SEO-optimized content automatically
- A/B test multiple versions instantly
- Multi-language content generation
- Cost: $0.01-0.05 per description vs $10-50 for copywriter
Scenario 5: Inventory Management Predictions
Traditional Approach:
- Manual stock level monitoring
- Basic reorder point calculations
- Historical sales data spreadsheets
- Seasonal adjustments done manually
- Frequent stockouts or overstock situations
AI Integration: Predictive inventory management system:
const inventoryAgent = {
predictDemand: tool({
description: "Predict product demand",
parameters: z.object({
productId: z.string(),
timeframe: z.string(),
}),
execute: async ({ productId, timeframe }) => {
const historicalData = await getHistoricalSales(productId);
const trends = await analyzeTrends(historicalData);
const prediction = await aiModel.predict({
data: historicalData,
trends,
seasonality: true,
externalFactors: ["weather", "events"],
});
return prediction;
},
}),
autoReorder: tool({
description: "Automatically create purchase orders",
execute: async ({ predictions }) => {
// Generate optimal reorder quantities
},
}),
};Improvements:
- Reduces stockouts by 50-70%
- Decreases excess inventory by 30-40%
- Considers external factors (weather, events, trends)
- Optimizes warehouse space utilization
- Saves 15-20 hours per week in manual analysis
- ROI: 200-300% within first year
Scenario 6: Personalized Email Marketing
Traditional Approach:
- Segment users into broad categories
- Send same email to entire segment
- Manual A/B testing
- Generic subject lines and content
- Average open rate: 15-20%
AI Integration: Hyper-personalized email generation:
const emailMarketingAgent = {
generatePersonalizedEmail: tool({
description: "Create personalized marketing email",
parameters: z.object({
userId: z.string(),
campaignType: z.string(),
}),
execute: async ({ userId, campaignType }) => {
const userData = await getUserProfile(userId);
const browsingHistory = await getBrowsingHistory(userId);
const purchaseHistory = await getPurchaseHistory(userId);
const email = await generateText({
model: openai("gpt-4"),
prompt: `Create personalized email for user who:
- Interests: ${userData.interests}
- Recently viewed: ${browsingHistory}
- Past purchases: ${purchaseHistory}
Campaign: ${campaignType}`,
});
return email;
},
}),
optimizeSendTime: tool({
description: "Predict optimal send time",
execute: async ({ userId }) => {
// Analyze user engagement patterns
},
}),
};Improvements:
- Open rates increase to 35-45%
- Click-through rates improve by 100-150%
- Each email uniquely tailored to recipient
- Optimal send time for each user
- Dynamic content based on real-time behavior
- Conversion rates improve by 50-80%
Scenario 7: Document Processing & Data Extraction
Traditional Approach:
- Manual data entry from invoices, receipts
- Prone to human error (5-10% error rate)
- Time-consuming: 5-10 minutes per document
- Requires training for each document type
- Difficult to scale
AI Integration: Intelligent document processing system:
const documentProcessor = {
extractData: tool({
description: "Extract structured data from documents",
parameters: z.object({
documentUrl: z.string(),
documentType: z.string(),
}),
execute: async ({ documentUrl, documentType }) => {
const document = await fetchDocument(documentUrl);
const extraction = await generateObject({
model: openai("gpt-4-vision"),
schema: z.object({
invoiceNumber: z.string(),
date: z.string(),
vendor: z.string(),
items: z.array(
z.object({
description: z.string(),
quantity: z.number(),
price: z.number(),
})
),
total: z.number(),
}),
prompt: `Extract all relevant data from this ${documentType}`,
});
return extraction;
},
}),
validateData: tool({
description: "Validate extracted data",
execute: async ({ data }) => {
// Cross-reference and validate
},
}),
};Improvements:
- Processing time: 10-30 seconds per document
- Error rate:
<1% - Handles various document formats automatically
- No training required for new document types
- Processes handwritten text
- Cost savings: 90% reduction in manual labor
- Scales to process thousands of documents per hour
Scenario 8: Intelligent Form Filling
Traditional Approach:
- Users manually fill every field
- Repetitive information entry
- No context awareness
- Validation only after submission
- High abandonment rates (68% for long forms)
AI Integration: Smart form assistant with autocomplete:
const formAssistant = {
predictFields: tool({
description: "Predict and auto-fill form fields",
parameters: z.object({
formType: z.string(),
userContext: z.object({}),
}),
execute: async ({ formType, userContext }) => {
// Use previous submissions + profile data
const predictions = await generateObject({
model: openai("gpt-4"),
schema: formSchema,
prompt: `Based on user profile and previous forms, suggest values for ${formType}`,
});
return predictions;
},
}),
validateInRealTime: tool({
description: "Validate fields as user types",
execute: async ({ field, value }) => {
// Intelligent validation with helpful suggestions
},
}),
};Improvements:
- Reduces form completion time by 70%
- Form abandonment drops to 20-25%
- Real-time error correction
- Context-aware suggestions
- Natural language input option
- Accessibility improvements
Scenario 9: Code Review & Bug Detection
Traditional Approach:
- Manual code reviews (2-4 hours per PR)
- Inconsistent review quality
- Bugs often slip through
- Relies on reviewer expertise
- Delayed feedback
AI Integration: Automated code review assistant:
const codeReviewAgent = {
analyzePR: tool({
description: "Analyze pull request for issues",
parameters: z.object({
prNumber: z.string(),
files: z.array(z.string()),
}),
execute: async ({ prNumber, files }) => {
const analysis = await generateText({
model: openai("gpt-4"),
prompt: `Review this code for:
- Security vulnerabilities
- Performance issues
- Best practice violations
- Potential bugs
- Code style consistency
Files: ${files.join(", ")}`,
});
return analysis;
},
}),
suggestFixes: tool({
description: "Generate code fix suggestions",
execute: async ({ issue }) => {
// Generate corrected code snippets
},
}),
};Improvements:
- Instant feedback on push
- Catches 80-90% of common issues
- Consistent review standards
- Frees developers for complex reviews
- Reduces bugs in production by 40-60%
- Review time reduced to 15-30 minutes
Scenario 10: Sentiment Analysis for Reviews
Traditional Approach:
- Manual reading of customer reviews
- Basic star rating aggregation
- No actionable insights
- Delayed response to negative feedback
- Miss trending issues
AI Integration: Real-time sentiment analysis and alerting:
const sentimentAgent = {
analyzeReview: tool({
description: "Analyze customer review sentiment",
parameters: z.object({
reviewText: z.string(),
productId: z.string(),
}),
execute: async ({ reviewText, productId }) => {
const analysis = await generateObject({
model: openai("gpt-4"),
schema: z.object({
sentimentScore: z.number(),
emotions: z.array(z.string()),
topics: z.array(z.string()),
urgency: z.enum(["low", "medium", "high", "critical"]),
suggestedResponse: z.string(),
}),
prompt: `Analyze this product review: "${reviewText}"`,
});
if (analysis.urgency === "critical") {
await alertCustomerService(productId, analysis);
}
return analysis;
},
}),
identifyTrends: tool({
description: "Identify trending issues across reviews",
execute: async ({ productId, timeframe }) => {
// Aggregate sentiment data and identify patterns
},
}),
};Improvements:
- Real-time sentiment tracking
- Automatic escalation of critical issues
- Identifies specific pain points
- Suggests personalized responses
- Trends visible within hours vs weeks
- Proactive issue resolution
- Customer satisfaction improvement: 20-30%
Scenario 11: Meeting Summarization
Traditional Approach:
- Manual note-taking during meetings
- Post-meeting summary writing (30-60 minutes)
- Inconsistent detail levels
- Missed action items
- Delayed distribution
AI Integration: Automatic meeting transcription and summarization:
const meetingAgent = {
transcribeAndSummarize: tool({
description: "Transcribe and summarize meeting",
parameters: z.object({
audioUrl: z.string(),
meetingType: z.string(),
}),
execute: async ({ audioUrl, meetingType }) => {
const transcription = await transcribeAudio(audioUrl);
const summary = await generateObject({
model: openai("gpt-4"),
schema: z.object({
keyPoints: z.array(z.string()),
actionItems: z.array(
z.object({
task: z.string(),
assignee: z.string(),
deadline: z.string(),
})
),
decisions: z.array(z.string()),
followUpNeeded: z.array(z.string()),
}),
prompt: `Summarize this ${meetingType} meeting transcription`,
});
return summary;
},
}),
extractActionItems: tool({
description: "Extract and assign action items",
execute: async ({ summary }) => {
// Create tasks in project management system
},
}),
};Improvements:
- Summaries available within 5 minutes of meeting end
- 100% capture rate of action items
- Searchable meeting history
- No dedicated note-taker needed
- Time saved: 30-60 minutes per meeting
- Improved accountability
- Better meeting follow-through
Scenario 12: Dynamic Pricing Optimization
Traditional Approach:
- Static pricing or simple rules (time-based discounts)
- Competitor price checking (manual, weekly)
- Seasonal price adjustments (quarterly)
- Limited data consideration
- Missed revenue opportunities
AI Integration: Real-time dynamic pricing engine:
const pricingAgent = {
optimizePrice: tool({
description: "Calculate optimal price",
parameters: z.object({
productId: z.string(),
timestamp: z.string(),
}),
execute: async ({ productId, timestamp }) => {
const factors = await gatherPricingFactors({
demandLevel: await getCurrentDemand(productId),
competitorPrices: await getCompetitorPrices(productId),
inventory: await getInventoryLevel(productId),
historicalSales: await getSalesHistory(productId),
seasonality: await getSeasonalFactors(timestamp),
userSegment: await getCurrentUserSegment(),
});
const optimalPrice = await generateObject({
model: openai("gpt-4"),
schema: z.object({
price: z.number(),
confidence: z.number(),
reasoning: z.string(),
}),
prompt: `Calculate optimal price considering: ${JSON.stringify(factors)}`,
});
return optimalPrice;
},
}),
};Improvements:
- Revenue increase: 15-25%
- Prices updated multiple times per day
- Considers 20+ factors in real-time
- Personalized pricing per customer segment
- Automatic competitive matching
- Maximizes profit margins
- Reduces lost sales from overpricing
Scenario 13: Chatbot for HR Onboarding
Traditional Approach:
- HR staff manually guide each new hire
- Paper forms and documentation
- Multiple meetings for orientation
- Inconsistent information delivery
- Time requirement: 10-15 hours per employee
AI Integration: Intelligent onboarding assistant:
const onboardingAgent = {
guideEmployee: tool({
description: "Guide new employee through onboarding",
parameters: z.object({
employeeId: z.string(),
stage: z.string(),
}),
execute: async ({ employeeId, stage }) => {
const employeeData = await getEmployeeData(employeeId);
const requirements = await getOnboardingRequirements(stage);
const guidance = await streamText({
model: openai("gpt-4"),
tools: {
completeTask: tool({
description: "Mark onboarding task as complete",
execute: async ({ taskId }) => {
// Update onboarding progress
},
}),
scheduleOrientation: tool({
description: "Schedule orientation sessions",
execute: async ({ sessions }) => {
// Book calendar slots
},
}),
answerPolicyQuestion: tool({
description: "Answer questions about company policies",
execute: async ({ question }) => {
// RAG system searches policy documents
},
}),
},
});
return guidance;
},
}),
};Improvements:
- Self-service onboarding reduces HR time by 70%
- Consistent information delivery
- 24/7 availability for questions
- Personalized onboarding pace
- Automatic compliance tracking
- New hire satisfaction increases 40%
- HR staff can focus on strategic tasks
Scenario 14: Image Recognition for Quality Control
Traditional Approach:
- Manual visual inspection
- Inconsistent defect detection
- Fatigue-related errors
- Inspector throughput: 200-300 items/hour
- Training time: 2-4 weeks
AI Integration: Automated visual quality inspection:
const qualityControlAgent = {
inspectProduct: tool({
description: "Inspect product for defects",
parameters: z.object({
imageUrl: z.string(),
productType: z.string(),
}),
execute: async ({ imageUrl, productType }) => {
const inspection = await generateObject({
model: openai("gpt-4-vision"),
schema: z.object({
defectsFound: z.array(
z.object({
type: z.string(),
severity: z.enum(["minor", "major", "critical"]),
location: z.string(),
confidence: z.number(),
})
),
overallQuality: z.enum(["pass", "fail"]),
reasoning: z.string(),
}),
messages: [
{
role: "user",
content: [
{ type: "text", text: `Inspect this ${productType} for defects` },
{ type: "image", image: imageUrl },
],
},
],
});
return inspection;
},
}),
};Improvements:
- Throughput: 1000+ items/hour
- Consistency: 99%+ accuracy
- Never fatigues or loses focus
- Detects micro-defects invisible to human eye
- Instant feedback loop to manufacturing
- Reduces defect rate by 60-80%
- ROI payback: 6-12 months
Scenario 15: Smart Recommendation Engine
Traditional Approach:
- "Customers also bought" (simple co-purchase)
- Category-based suggestions
- Bestseller lists
- Limited personalization
- Click-through rate: 2-5%
AI Integration: Multi-factor personalized recommendations:
const recommendationAgent = {
generateRecommendations: tool({
description: "Generate personalized product recommendations",
parameters: z.object({
userId: z.string(),
context: z.object({
currentPage: z.string(),
sessionHistory: z.array(z.string()),
}),
}),
execute: async ({ userId, context }) => {
const userProfile = await getUserProfile(userId);
const behaviorPatterns = await analyzeBehavior(userId);
const similarUsers = await findSimilarUsers(userId);
const trendingProducts = await getTrendingProducts();
const recommendations = await generateObject({
model: openai("gpt-4"),
schema: z.object({
products: z.array(
z.object({
productId: z.string(),
relevanceScore: z.number(),
reasoning: z.string(),
})
),
}),
prompt: `Generate recommendations considering:
- User preferences: ${JSON.stringify(userProfile)}
- Behavior: ${JSON.stringify(behaviorPatterns)}
- Similar users bought: ${JSON.stringify(similarUsers)}
- Current context: ${JSON.stringify(context)}`,
});
return recommendations;
},
}),
};Improvements:
- Click-through rate: 15-25%
- Conversion rate improvement: 150-200%
- Real-time adaptation to user behavior
- Cross-category discovery
- Considers time, location, weather, events
- Average order value increase: 30-50%
Scenario 16: Legal Document Analysis
Traditional Approach:
- Lawyer reviews contracts manually
- Time: 2-4 hours per contract
- Cost: $300-1000 per review
- Risk of missing critical clauses
- Inconsistent risk assessment
AI Integration: Automated contract review system:
const legalAgent = {
reviewContract: tool({
description: "Analyze legal document for risks",
parameters: z.object({
documentUrl: z.string(),
contractType: z.string(),
}),
execute: async ({ documentUrl, contractType }) => {
const document = await fetchDocument(documentUrl);
const analysis = await generateObject({
model: openai("gpt-4"),
schema: z.object({
riskLevel: z.enum(["low", "medium", "high", "critical"]),
issues: z.array(
z.object({
clause: z.string(),
risk: z.string(),
severity: z.string(),
recommendation: z.string(),
})
),
missingClauses: z.array(z.string()),
summary: z.string(),
requiresLawyerReview: z.boolean(),
}),
prompt: `Analyze this ${contractType} contract for legal risks and issues`,
});
return analysis;
},
}),
};Improvements:
- Review time: 5-10 minutes
- Cost: $5-20 per review
- 24/7 availability
- Consistent risk assessment
- Flags 95%+ of critical issues
- Allows lawyers to focus on complex negotiations
- Faster deal closing
Scenario 17: Voice-Activated Ordering
Traditional Approach:
- Manual product search and selection
- Multiple clicks/taps to checkout
- Requires screen interaction
- Not accessible while multitasking
AI Integration: Conversational voice commerce:
const voiceOrderingAgent = {
processVoiceOrder: tool({
description: "Process voice-based order",
parameters: z.object({
audioInput: z.string(),
userId: z.string(),
}),
execute: async ({ audioInput, userId }) => {
const transcription = await transcribeAudio(audioInput);
const orderProcessing = await streamText({
model: openai("gpt-4"),
tools: {
searchProducts: tool({
description: "Search for products mentioned",
execute: async ({ query }) => {
return await searchProductsCatalog(query);
},
}),
confirmOrder: tool({
description: "Confirm order details",
execute: async ({ products, deliveryAddress }) => {
return await createOrder(userId, products, deliveryAddress);
},
}),
processPayment: tool({
description: "Process payment",
execute: async ({ paymentMethodId, amount }) => {
return await chargePayment(paymentMethodId, amount);
},
}),
},
messages: [
{
role: "user",
content: transcription,
},
],
});
return orderProcessing;
},
}),
};Improvements:
- Hands-free ordering
- Natural conversation flow
- Multi-language support
- Accessible to visually impaired users
- Order completion time: 30-60 seconds
- Perfect for busy users (cooking, driving, etc.)
- Increases impulse purchases
Part 2: Getting Started with Vercel AI SDK
Installation
# Install Vercel AI SDK
npm install ai @ai-sdk/openai @ai-sdk/anthropic
# Install React dependencies
npm install @vercel/ai react
# For Next.js App Router
npm install ai nextEnvironment Setup
Create a .env.local file:
# OpenAI
OPENAI_API_KEY=your_openai_key
# Anthropic (Claude)
ANTHROPIC_API_KEY=your_anthropic_key
# Optional: For custom models
CUSTOM_MODEL_API_KEY=your_keyBasic Configuration
// lib/ai-config.ts
import { createOpenAI } from "@ai-sdk/openai";
import { createAnthropic } from "@ai-sdk/anthropic";
export const openai = createOpenAI({
apiKey: process.env.OPENAI_API_KEY,
compatibility: "strict", // or 'compatible'
});
export const anthropic = createAnthropic({
apiKey: process.env.ANTHROPIC_API_KEY,
});
// Default model configurations
export const defaultChatModel = openai("gpt-4-turbo");
export const defaultEmbeddingModel = openai.embedding("text-embedding-3-small");Part 3: Vercel AI Components
1. Vercel AI SDK Core
The foundation for building AI-powered applications:
import { generateText, generateObject, streamText } from "ai";
import { openai } from "@ai-sdk/openai";
import { z } from "zod";
// Generate text
const { text } = await generateText({
model: openai("gpt-4-turbo"),
prompt: "Explain quantum computing",
});
// Generate structured output
const { object } = await generateObject({
model: openai("gpt-4-turbo"),
schema: z.object({
name: z.string(),
age: z.number(),
email: z.string().email(),
}),
prompt: "Generate a user profile",
});
// Stream responses
const result = await streamText({
model: openai("gpt-4-turbo"),
prompt: "Write a long story",
});
for await (const chunk of result.textStream) {
process.stdout.write(chunk);
}2. Vercel AI SDK UI Components
Pre-built React hooks and components:
'use client';
import { useChat, useCompletion } from 'ai/react';
export function ChatComponent() {
const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat({
api: '/api/chat',
initialMessages: [],
});
return (
<div>
{messages.map(m => (
<div key={m.id} className={m.role}>
{m.content}
</div>
))}
<form onSubmit={handleSubmit}>
<input
value={input}
onChange={handleInputChange}
disabled={isLoading}
placeholder="Type your message..."
/>
<button type="submit" disabled={isLoading}>
Send
</button>
</form>
</div>
);
}3. AI Elements (New)
Pre-built, customizable AI UI components:
import { ChatWindow, MessageList, InputBar } from '@vercel/ai-elements';
export function AdvancedChat() {
return (
<ChatWindow
theme="dark"
position="bottom-right"
// Pre-configured with best practices
features={{
streaming: true,
markdown: true,
codeHighlight: true,
fileUpload: true,
}}
>
<MessageList
showTimestamps
showAvatars
enableMarkdown
/>
<InputBar
placeholder="Ask me anything..."
suggestions={[
"How can I help you today?",
"Tell me about your products",
]}
/>
</ChatWindow>
);
}Key AI Elements Features:
- Pre-styled Components: Beautiful UI out of the box
- Accessibility: WCAG 2.1 Level AA compliant
- Responsive Design: Mobile, tablet, desktop optimized
- Theming: Light/dark mode, custom themes
- File Uploads: Drag-and-drop support
- Code Highlighting: Syntax highlighting for code blocks
- Markdown Rendering: Full markdown support
4. AI Gateway (New)
Centralized AI request management:
// next.config.js
module.exports = {
experimental: {
aiGateway: {
enabled: true,
// Caching configuration
cache: {
enabled: true,
ttl: 3600, // 1 hour
},
// Rate limiting
rateLimit: {
requests: 100,
window: "1m",
},
// Cost tracking
analytics: {
enabled: true,
logRequests: true,
},
// Model routing
routing: {
default: "openai/gpt-4-turbo",
fallback: "anthropic/claude-3-sonnet",
},
},
},
};AI Gateway Features:
1. Intelligent Caching
import { gatewayFetch } from "@vercel/ai-gateway";
// Automatically cached based on prompt + parameters
const response = await gatewayFetch({
model: "gpt-4-turbo",
prompt: "Explain AI",
cache: {
strategy: "semantic", // semantic or exact
ttl: 3600,
},
});2. Cost Management
// Track and limit AI spending
const response = await gatewayFetch({
model: "gpt-4-turbo",
prompt: "Generate content",
budget: {
maxCost: 0.5, // USD
onExceed: "fallback", // or 'reject'
fallbackModel: "gpt-3.5-turbo",
},
});3. Rate Limiting
// Per-user rate limiting
export async function POST(req: Request) {
const userId = await getUserId(req);
const response = await gatewayFetch({
model: "gpt-4-turbo",
prompt: await req.text(),
rateLimit: {
key: userId,
limit: 10,
window: "1m",
},
});
return Response.json(response);
}4. Model Fallback
// Automatic fallback on errors
const response = await gatewayFetch({
model: "gpt-4-turbo",
prompt: "Complex query",
fallback: ["gpt-4", "claude-3-opus", "gpt-3.5-turbo"],
retries: 3,
});5. Analytics Dashboard
// View in Vercel Dashboard:
// - Request volume
// - Cost per endpoint
// - Average latency
// - Cache hit rate
// - Error rates
// - Model usage breakdownPart 4: Project 1 - E-commerce AI Chatbot with Agents
Project Overview
Build a complete e-commerce chatbot that can:
- Create user accounts
- Browse products
- Add items to cart
- Process checkout
- Handle customer service inquiries
Step 1: Project Setup
# Create Next.js project
npx create-next-app@latest ecommerce-ai-bot
cd ecommerce-ai-bot
# Install dependencies
npm install ai @ai-sdk/openai zod
npm install prisma @prisma/client
npm install stripe
npm install bcrypt jsonwebtokenStep 2: Database Schema
// prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(cuid())
email String @unique
name String
password String
createdAt DateTime @default(now())
orders Order[]
cart CartItem[]
}
model Product {
id String @id @default(cuid())
name String
description String
price Float
category String
stock Int
imageUrl String
cartItems CartItem[]
orderItems OrderItem[]
}
model CartItem {
id String @id @default(cuid())
userId String
productId String
quantity Int
user User @relation(fields: [userId], references: [id])
product Product @relation(fields: [productId], references: [id])
}
model Order {
id String @id @default(cuid())
userId String
total Float
status String
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id])
items OrderItem[]
}
model OrderItem {
id String @id @default(cuid())
orderId String
productId String
quantity Int
price Float
order Order @relation(fields: [orderId], references: [id])
product Product @relation(fields: [productId], references: [id])
}Step 3: AI Agent Tools
// lib/agents/ecommerce-tools.ts
import { tool } from "ai";
import { z } from "zod";
import { prisma } from "@/lib/prisma";
import bcrypt from "bcrypt";
import jwt from "jsonwebtoken";
export const ecommerceTools = {
// User Management
createAccount: tool({
description: "Create a new user account",
parameters: z.object({
email: z.string().email(),
name: z.string(),
password: z.string().min(8),
}),
execute: async ({ email, name, password }) => {
try {
// Check if user exists
const existing = await prisma.user.findUnique({
where: { email },
});
if (existing) {
return {
success: false,
message: "Email already registered",
};
}
// Hash password
const hashedPassword = await bcrypt.hash(password, 10);
// Create user
const user = await prisma.user.create({
data: {
email,
name,
password: hashedPassword,
},
});
// Generate JWT token
const token = jwt.sign({ userId: user.id }, process.env.JWT_SECRET!, {
expiresIn: "7d",
});
return {
success: true,
message: `Account created successfully! Welcome, ${name}!`,
userId: user.id,
token,
};
} catch (error) {
return {
success: false,
message: "Failed to create account",
error: error.message,
};
}
},
}),
// Product Search
searchProducts: tool({
description: "Search for products by name, category, or description",
parameters: z.object({
query: z.string(),
category: z.string().optional(),
maxPrice: z.number().optional(),
minPrice: z.number().optional(),
}),
execute: async ({ query, category, maxPrice, minPrice }) => {
try {
const products = await prisma.product.findMany({
where: {
AND: [
{
OR: [
{ name: { contains: query, mode: "insensitive" } },
{ description: { contains: query, mode: "insensitive" } },
],
},
category ? { category: { equals: category } } : {},
maxPrice ? { price: { lte: maxPrice } } : {},
minPrice ? { price: { gte: minPrice } } : {},
{ stock: { gt: 0 } }, // Only in-stock items
],
},
take: 10,
});
if (products.length === 0) {
return {
success: false,
message: "No products found matching your criteria",
};
}
return {
success: true,
message: `Found ${products.length} products`,
products: products.map((p) => ({
id: p.id,
name: p.name,
description: p.description,
price: p.price,
category: p.category,
inStock: p.stock > 0,
})),
};
} catch (error) {
return {
success: false,
message: "Failed to search products",
error: error.message,
};
}
},
}),
// Get Product Details
getProductDetails: tool({
description: "Get detailed information about a specific product",
parameters: z.object({
productId: z.string(),
}),
execute: async ({ productId }) => {
try {
const product = await prisma.product.findUnique({
where: { id: productId },
});
if (!product) {
return {
success: false,
message: "Product not found",
};
}
return {
success: true,
product: {
id: product.id,
name: product.name,
description: product.description,
price: product.price,
category: product.category,
stock: product.stock,
imageUrl: product.imageUrl,
},
};
} catch (error) {
return {
success: false,
message: "Failed to fetch product details",
};
}
},
}),
// Cart Management
addToCart: tool({
description: "Add a product to user cart",
parameters: z.object({
userId: z.string(),
productId: z.string(),
quantity: z.number().min(1),
}),
execute: async ({ userId, productId, quantity }) => {
try {
// Check product availability
const product = await prisma.product.findUnique({
where: { id: productId },
});
if (!product || product.stock < quantity) {
return {
success: false,
message: "Product not available in requested quantity",
};
}
// Check if item already in cart
const existingItem = await prisma.cartItem.findFirst({
where: {
userId,
productId,
},
});
if (existingItem) {
// Update quantity
await prisma.cartItem.update({
where: { id: existingItem.id },
data: { quantity: existingItem.quantity + quantity },
});
} else {
// Add new item
await prisma.cartItem.create({
data: {
userId,
productId,
quantity,
},
});
}
return {
success: true,
message: `Added ${quantity}x ${product.name} to cart`,
};
} catch (error) {
return {
success: false,
message: "Failed to add item to cart",
};
}
},
}),
// View Cart
viewCart: tool({
description: "View all items in user cart",
parameters: z.object({
userId: z.string(),
}),
execute: async ({ userId }) => {
try {
const cartItems = await prisma.cartItem.findMany({
where: { userId },
include: { product: true },
});
if (cartItems.length === 0) {
return {
success: true,
message: "Your cart is empty",
items: [],
total: 0,
};
}
const total = cartItems.reduce(
(sum, item) => sum + item.product.price * item.quantity,
0
);
return {
success: true,
items: cartItems.map((item) => ({
id: item.id,
productName: item.product.name,
quantity: item.quantity,
price: item.product.price,
subtotal: item.product.price * item.quantity,
})),
total: total.toFixed(2),
};
} catch (error) {
return {
success: false,
message: "Failed to fetch cart",
};
}
},
}),
// Checkout
checkout: tool({
description: "Process checkout and create order",
parameters: z.object({
userId: z.string(),
paymentMethodId: z.string(),
shippingAddress: z.object({
street: z.string(),
city: z.string(),
state: z.string(),
zipCode: z.string(),
country: z.string(),
}),
}),
execute: async ({ userId, paymentMethodId, shippingAddress }) => {
try {
// Get cart items
const cartItems = await prisma.cartItem.findMany({
where: { userId },
include: { product: true },
});
if (cartItems.length === 0) {
return {
success: false,
message: "Cart is empty",
};
}
// Calculate total
const total = cartItems.reduce(
(sum, item) => sum + item.product.price * item.quantity,
0
);
// Create order
const order = await prisma.order.create({
data: {
userId,
total,
status: "pending",
items: {
create: cartItems.map((item) => ({
productId: item.productId,
quantity: item.quantity,
price: item.product.price,
})),
},
},
include: { items: { include: { product: true } } },
});
// Process payment (simplified - use Stripe in production)
// const payment = await stripe.paymentIntents.create({ ... });
// Update order status
await prisma.order.update({
where: { id: order.id },
data: { status: "confirmed" },
});
// Update product stock
for (const item of cartItems) {
await prisma.product.update({
where: { id: item.productId },
data: {
stock: { decrement: item.quantity },
},
});
}
// Clear cart
await prisma.cartItem.deleteMany({
where: { userId },
});
return {
success: true,
message: `Order placed successfully! Order ID: ${order.id}`,
orderId: order.id,
total: total.toFixed(2),
estimatedDelivery: "3-5 business days",
};
} catch (error) {
return {
success: false,
message: "Checkout failed",
error: error.message,
};
}
},
}),
// Order Status
getOrderStatus: tool({
description: "Get status of an order",
parameters: z.object({
orderId: z.string(),
}),
execute: async ({ orderId }) => {
try {
const order = await prisma.order.findUnique({
where: { id: orderId },
include: {
items: { include: { product: true } },
},
});
if (!order) {
return {
success: false,
message: "Order not found",
};
}
return {
success: true,
order: {
id: order.id,
status: order.status,
total: order.total,
createdAt: order.createdAt,
items: order.items.map((item) => ({
productName: item.product.name,
quantity: item.quantity,
price: item.price,
})),
},
};
} catch (error) {
return {
success: false,
message: "Failed to fetch order status",
};
}
},
}),
};Step 4: Chat API Route
// app/api/chat/route.ts
import { streamText } from "ai";
import { openai } from "@ai-sdk/openai";
import { ecommerceTools } from "@/lib/agents/ecommerce-tools";
export const runtime = "edge";
export async function POST(req: Request) {
const { messages, userId } = await req.json();
const result = await streamText({
model: openai("gpt-4-turbo"),
system: `You are a helpful e-commerce shopping assistant. You can help users:
1. Create accounts
2. Search and browse products
3. Get product details
4. Add items to cart
5. View cart
6. Checkout and place orders
7. Check order status
Always be friendly, helpful, and guide users through the shopping process.
When users want to buy something, help them search for products, show details,
and guide them through adding to cart and checkout.
Current user ID: ${userId || "Not logged in"}
If user is not logged in and tries to add to cart or checkout, suggest they create an account first.`,
messages,
tools: ecommerceTools,
maxToolRoundtrips: 5,
});
return result.toDataStreamResponse();
}Step 5: Frontend Chat Interface
// app/page.tsx
'use client';
import { useChat } from 'ai/react';
import { useState } from 'react';
export default function Home() {
const [userId, setUserId] = useState<string | null>(null);
const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat({
api: '/api/chat',
body: { userId },
onFinish: (message) => {
// Check if message contains userId (from account creation)
const userIdMatch = message.content.match(/userId: ([a-z0-9-]+)/);
if (userIdMatch) {
setUserId(userIdMatch[1]);
localStorage.setItem('userId', userIdMatch[1]);
}
},
});
return (
<div className="flex flex-col h-screen bg-gray-50">
{/* Header */}
<header className="bg-white border-b px-6 py-4">
<div className="max-w-4xl mx-auto flex justify-between items-center">
<h1 className="text-2xl font-bold text-gray-900">
AI Shopping Assistant
</h1>
{userId && (
<span className="text-sm text-gray-600">
Logged in
</span>
)}
</div>
</header>
{/* Chat Messages */}
<div className="flex-1 overflow-y-auto px-6 py-8">
<div className="max-w-4xl mx-auto space-y-6">
{messages.length === 0 && (
<div className="text-center py-12">
<h2 className="text-3xl font-bold text-gray-900 mb-4">
Welcome to AI Shopping! 🛍️
</h2>
<p className="text-gray-600 mb-8">
I can help you shop, manage your cart, and checkout.
</p>
<div className="grid md:grid-cols-3 gap-4">
<button
onClick={() => handleInputChange({ target: { value: 'Create an account for me' } } as any)}
className="p-4 bg-white rounded-lg border hover:border-blue-500 transition"
>
<div className="text-lg mb-2">👤</div>
<div className="font-semibold">Create Account</div>
</button>
<button
onClick={() => handleInputChange({ target: { value: 'Show me laptops under $1000' } } as any)}
className="p-4 bg-white rounded-lg border hover:border-blue-500 transition"
>
<div className="text-lg mb-2">🔍</div>
<div className="font-semibold">Browse Products</div>
</button>
<button
onClick={() => handleInputChange({ target: { value: 'Show my cart' } } as any)}
className="p-4 bg-white rounded-lg border hover:border-blue-500 transition"
>
<div className="text-lg mb-2">🛒</div>
<div className="font-semibold">View Cart</div>
</button>
</div>
</div>
)}
{messages.map((message) => (
<div
key={message.id}
className={`flex ${message.role === 'user' ? 'justify-end' : 'justify-start'}`}
>
<div
className={`max-w-3xl px-6 py-4 rounded-2xl ${
message.role === 'user'
? 'bg-blue-600 text-white'
: 'bg-white border shadow-sm'
}`}
>
{message.content}
</div>
</div>
))}
{isLoading && (
<div className="flex justify-start">
<div className="bg-white border shadow-sm px-6 py-4 rounded-2xl">
<div className="flex space-x-2">
<div className="w-2 h-2 bg-gray-400 rounded-full animate-bounce" />
<div className="w-2 h-2 bg-gray-400 rounded-full animate-bounce delay-100" />
<div className="w-2 h-2 bg-gray-400 rounded-full animate-bounce delay-200" />
</div>
</div>
</div>
)}
</div>
</div>
{/* Input Bar */}
<div className="border-t bg-white px-6 py-4">
<form onSubmit={handleSubmit} className="max-w-4xl mx-auto">
<div className="flex space-x-4">
<input
type="text"
value={input}
onChange={handleInputChange}
placeholder="Ask me anything... (e.g., 'Show me wireless headphones')"
className="flex-1 px-6 py-3 border rounded-full focus:outline-none focus:ring-2 focus:ring-blue-500"
disabled={isLoading}
/>
<button
type="submit"
disabled={isLoading || !input.trim()}
className="px-8 py-3 bg-blue-600 text-white rounded-full hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed transition"
>
Send
</button>
</div>
</form>
</div>
</div>
);
}Step 6: Seed Database with Sample Products
// prisma/seed.ts
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
async function main() {
// Create sample products
await prisma.product.createMany({
data: [
{
name: 'MacBook Pro 16"',
description: "Powerful laptop for professionals",
price: 2499.99,
category: "Electronics",
stock: 15,
imageUrl: "/products/macbook.jpg",
},
{
name: "Sony WH-1000XM5",
description: "Premium noise-cancelling headphones",
price: 399.99,
category: "Electronics",
stock: 30,
imageUrl: "/products/sony-headphones.jpg",
},
{
name: "iPhone 15 Pro",
description: "Latest flagship smartphone",
price: 1199.99,
category: "Electronics",
stock: 25,
imageUrl: "/products/iphone.jpg",
},
{
name: 'Samsung 55" OLED TV',
description: "4K OLED Smart TV",
price: 1499.99,
category: "Electronics",
stock: 10,
imageUrl: "/products/samsung-tv.jpg",
},
{
name: "Nike Air Max 270",
description: "Comfortable running shoes",
price: 150.0,
category: "Shoes",
stock: 50,
imageUrl: "/products/nike-shoes.jpg",
},
],
});
console.log("Database seeded!");
}
main()
.catch(console.error)
.finally(() => prisma.$disconnect());Run seed:
pnpm dlx prisma db push
npx ts-node prisma/seed.ts
Step 7: Testing the E-commerce Bot
Test conversations:
1. Account Creation:
User: "I want to create an account"
Bot: "I'd be happy to help you create an account! I'll need a few details:
1. Your email address
2. Your full name
3. A secure password (at least 8 characters)"
User: "My email is john@example.com, name is John Doe, password is SecurePass123"
Bot: "Account created successfully! Welcome, John Doe! 🎉"
2. Product Search:
User: "Show me laptops under $2000"
Bot: "I found 2 laptops under $2000:
1. MacBook Pro 16" - $2,499.99 (Electronics)
Powerful laptop for professionals
✓ In Stock
Would you like details on any of these?"
3. Add to Cart:
User: "Add the MacBook to my cart"
Bot: "Added 1x MacBook Pro 16" to cart! Your cart total is now $2,499.99.
Would you like to continue shopping or proceed to checkout?"
4. Checkout:
User: "I want to checkout"
Bot: "Great! To complete your order, I need:
1. Shipping address
2. Payment method
What's your shipping address?"
User: "123 Main St, New York, NY 10001, USA"
Bot: "Perfect! And which payment method would you like to use?"
User: "Credit card ending in 1234"
Bot: "Order placed successfully! Order ID: order_abc123
Total: $2,499.99
Estimated delivery: 3-5 business days 📦"
Part 5: Project 2 - RAG Chat Application
Project Overview
Build a RAG (Retrieval-Augmented Generation) chat application that:
- Ingests custom knowledge base documents
- Creates vector embeddings
- Retrieves relevant context for queries
- Generates accurate answers using context
- Supports document upload and management
Step 1: Project Setup
# Create project
npx create-next-app@latest rag-chat-app
cd rag-chat-app
# Install dependencies
npm install ai @ai-sdk/openai zod
npm install @pinecone-database/pinecone
npm install pdf-parse mammoth
npm install multerStep 2: Vector Database Setup (Pinecone)
// lib/vector-db.ts
import { Pinecone } from "@pinecone-database/pinecone";
import { embed, embedMany } from "ai";
import { openai } from "@ai-sdk/openai";
const pinecone = new Pinecone({
apiKey: process.env.PINECONE_API_KEY!,
});
const index = pinecone.index("rag-knowledge-base");
export async function embedDocument(
text: string,
metadata: {
source: string;
title: string;
chunkIndex: number;
}
) {
// Split text into chunks
const chunks = chunkText(text, 1000);
// Generate embeddings for each chunk
const { embeddings } = await embedMany({
model: openai.embedding("text-embedding-3-small"),
values: chunks,
});
// Store in Pinecone
const vectors = chunks.map((chunk, i) => ({
id: `${metadata.source}-chunk-${i}`,
values: embeddings[i],
metadata: {
text: chunk,
source: metadata.source,
title: metadata.title,
chunkIndex: i,
},
}));
await index.upsert(vectors);
return vectors.length;
}
export async function searchSimilar(query: string, topK = 5) {
// Generate query embedding
const { embedding } = await embed({
model: openai.embedding("text-embedding-3-small"),
value: query,
});
// Search in Pinecone
const results = await index.query({
vector: embedding,
topK,
includeMetadata: true,
});
return results.matches.map((match) => ({
text: match.metadata?.text as string,
source: match.metadata?.source as string,
title: match.metadata?.title as string,
score: match.score,
}));
}
function chunkText(text: string, chunkSize: number): string[] {
const chunks: string[] = [];
const sentences = text.match(/[^.!?]+[.!?]+/g) || [text];
let currentChunk = "";
for (const sentence of sentences) {
if ((currentChunk + sentence).length > chunkSize && currentChunk) {
chunks.push(currentChunk.trim());
currentChunk = sentence;
} else {
currentChunk += " " + sentence;
}
}
if (currentChunk) {
chunks.push(currentChunk.trim());
}
return chunks;
}Step 3: Document Processing
// lib/document-processor.ts
import pdfParse from "pdf-parse";
import mammoth from "mammoth";
import { embedDocument } from "./vector-db";
export async function processDocument(
file: Buffer,
filename: string,
fileType: string
) {
let text = "";
try {
if (fileType === "application/pdf") {
const pdf = await pdfParse(file);
text = pdf.text;
} else if (
fileType ===
"application/vnd.openxmlformats-officedocument.wordprocessingml.document"
) {
const result = await mammoth.extractRawText({ buffer: file });
text = result.value;
} else if (fileType === "text/plain") {
text = file.toString("utf-8");
} else {
throw new Error("Unsupported file type");
}
// Clean text
text = text.replace(/\s+/g, " ").trim();
// Embed document
const chunksCreated = await embedDocument(text, {
source: filename,
title: filename.replace(/\.[^/.]+$/, ""),
chunkIndex: 0,
});
return {
success: true,
message: `Processed ${filename}: ${chunksCreated} chunks created`,
chunksCreated,
};
} catch (error) {
return {
success: false,
message: `Failed to process ${filename}`,
error: error.message,
};
}
}Step 4: RAG Agent Tools
// lib/agents/rag-tools.ts
import { tool } from "ai";
import { z } from "zod";
import { searchSimilar } from "@/lib/vector-db";
export const ragTools = {
searchKnowledgeBase: tool({
description: "Search the knowledge base for relevant information",
parameters: z.object({
query: z.string(),
topK: z.number().default(5),
}),
execute: async ({ query, topK }) => {
try {
const results = await searchSimilar(query, topK);
if (results.length === 0) {
return {
success: false,
message: "No relevant information found in knowledge base",
};
}
return {
success: true,
results: results.map((r, i) => ({
rank: i + 1,
content: r.text,
source: r.title,
relevanceScore: r.score?.toFixed(3),
})),
};
} catch (error) {
return {
success: false,
message: "Failed to search knowledge base",
error: error.message,
};
}
},
}),
};Step 5: RAG Chat API
// app/api/rag-chat/route.ts
import { streamText } from "ai";
import { openai } from "@ai-sdk/openai";
import { ragTools } from "@/lib/agents/rag-tools";
export const runtime = "edge";
export async function POST(req: Request) {
const { messages } = await req.json();
const result = await streamText({
model: openai("gpt-4-turbo"),
system: `You are a knowledgeable assistant with access to a custom knowledge base.
When answering questions:
1. ALWAYS search the knowledge base first using the searchKnowledgeBase tool
2. Base your answers primarily on the retrieved information
3. If the knowledge base doesn't have relevant information, clearly state that
4. Cite sources when providing information from the knowledge base
5. Be accurate and avoid hallucinating information
6. If you're uncertain, say so
Format your responses clearly and cite sources like this:
[Source: Document Title]`,
messages,
tools: ragTools,
maxToolRoundtrips: 3,
});
return result.toDataStreamResponse();
}Step 6: Document Upload API
// app/api/upload/route.ts
import { NextRequest, NextResponse } from "next/server";
import { processDocument } from "@/lib/document-processor";
export async function POST(req: NextRequest) {
try {
const formData = await req.formData();
const file = formData.get("file") as File;
if (!file) {
return NextResponse.json({ error: "No file provided" }, { status: 400 });
}
// Convert to buffer
const bytes = await file.arrayBuffer();
const buffer = Buffer.from(bytes);
// Process document
const result = await processDocument(buffer, file.name, file.type);
return NextResponse.json(result);
} catch (error) {
return NextResponse.json(
{ error: "Failed to upload document", details: error.message },
{ status: 500 }
);
}
}Step 7: Frontend with Upload
// app/page.tsx
'use client';
import { useChat } from 'ai/react';
import { useState, useRef } from 'react';
export default function RAGChat() {
const [uploading, setUploading] = useState(false);
const [uploadedFiles, setUploadedFiles] = useState<string[]>([]);
const fileInputRef = useRef<HTMLInputElement>(null);
const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat({
api: '/api/rag-chat',
});
const handleFileUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (!file) return;
setUploading(true);
try {
const formData = new FormData();
formData.append('file', file);
const response = await fetch('/api/upload', {
method: 'POST',
body: formData,
});
const result = await response.json();
if (result.success) {
setUploadedFiles(prev => [...prev, file.name]);
alert(`✅ ${result.message}`);
} else {
alert(`❌ ${result.message}`);
}
} catch (error) {
alert('Failed to upload file');
} finally {
setUploading(false);
if (fileInputRef.current) {
fileInputRef.current.value = '';
}
}
};
return (
<div className="flex h-screen bg-gray-50">
{/* Sidebar */}
<div className="w-80 bg-white border-r flex flex-col">
<div className="p-6 border-b">
<h2 className="text-xl font-bold text-gray-900 mb-4">
Knowledge Base
</h2>
<button
onClick={() => fileInputRef.current?.click()}
disabled={uploading}
className="w-full px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50"
>
{uploading ? 'Uploading...' : '📄 Upload Document'}
</button>
<input
ref={fileInputRef}
type="file"
accept=".pdf,.docx,.txt"
onChange={handleFileUpload}
className="hidden"
/>
</div>
<div className="flex-1 overflow-y-auto p-6">
<h3 className="text-sm font-semibold text-gray-700 mb-3">
Uploaded Documents
</h3>
{uploadedFiles.length === 0 ? (
<p className="text-sm text-gray-500">
No documents uploaded yet
</p>
) : (
<div className="space-y-2">
{uploadedFiles.map((file, i) => (
<div
key={i}
className="px-3 py-2 bg-gray-50 rounded text-sm truncate"
title={file}
>
📄 {file}
</div>
))}
</div>
)}
</div>
<div className="p-6 border-t">
<div className="text-xs text-gray-500">
<p className="mb-2">Supported formats:</p>
<ul className="list-disc list-inside space-y-1">
<li>PDF (.pdf)</li>
<li>Word (.docx)</li>
<li>Text (.txt)</li>
</ul>
</div>
</div>
</div>
{/* Chat Area */}
<div className="flex-1 flex flex-col">
{/* Header */}
<header className="bg-white border-b px-6 py-4">
<h1 className="text-2xl font-bold text-gray-900">
RAG Chat Assistant
</h1>
<p className="text-sm text-gray-600 mt-1">
Ask questions about your uploaded documents
</p>
</header>
{/* Messages */}
<div className="flex-1 overflow-y-auto px-6 py-8">
<div className="max-w-4xl mx-auto space-y-6">
{messages.length === 0 && (
<div className="text-center py-12">
<div className="text-6xl mb-4">📚</div>
<h2 className="text-2xl font-bold text-gray-900 mb-2">
Welcome to RAG Chat
</h2>
<p className="text-gray-600 mb-8">
Upload documents and ask questions about them
</p>
<div className="grid md:grid-cols-2 gap-4 max-w-2xl mx-auto">
<div className="p-4 bg-white rounded-lg border">
<div className="text-lg mb-2">📤</div>
<div className="font-semibold mb-1">1. Upload</div>
<p className="text-sm text-gray-600">
Upload your PDF, Word, or text documents
</p>
</div>
<div className="p-4 bg-white rounded-lg border">
<div className="text-lg mb-2">💬</div>
<div className="font-semibold mb-1">2. Ask</div>
<p className="text-sm text-gray-600">
Ask questions about the content
</p>
</div>
</div>
</div>
)}
{messages.map((message) => (
<div
key={message.id}
className={`flex ${
message.role === 'user' ? 'justify-end' : 'justify-start'
}`}
>
<div
className={`max-w-3xl px-6 py-4 rounded-2xl ${
message.role === 'user'
? 'bg-blue-600 text-white'
: 'bg-white border shadow-sm'
}`}
>
<div className="prose prose-sm max-w-none">
{message.content}
</div>
</div>
</div>
))}
{isLoading && (
<div className="flex justify-start">
<div className="bg-white border shadow-sm px-6 py-4 rounded-2xl">
<div className="flex space-x-2">
<div className="w-2 h-2 bg-gray-400 rounded-full animate-bounce" />
<div className="w-2 h-2 bg-gray-400 rounded-full animate-bounce delay-100" />
<div className="w-2 h-2 bg-gray-400 rounded-full animate-bounce delay-200" />
</div>
</div>
</div>
)}
</div>
</div>
{/* Input */}
<div className="border-t bg-white px-6 py-4">
<form onSubmit={handleSubmit} className="max-w-4xl mx-auto">
<div className="flex space-x-4">
<input
type="text"
value={input}
onChange={handleInputChange}
placeholder="Ask a question about your documents..."
className="flex-1 px-6 py-3 border rounded-full focus:outline-none focus:ring-2 focus:ring-blue-500"
disabled={isLoading || uploadedFiles.length === 0}
/>
<button
type="submit"
disabled={isLoading || !input.trim() || uploadedFiles.length === 0}
className="px-8 py-3 bg-blue-600 text-white rounded-full hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed transition"
>
Send
</button>
</div>
{uploadedFiles.length === 0 && (
<p className="text-center text-sm text-gray-500 mt-2">
Upload documents to start asking questions
</p>
)}
</form>
</div>
</div>
</div>
);
}Step 8: Enhanced RAG with Agent Capabilities
Add more sophisticated RAG features:
// lib/agents/advanced-rag-tools.ts
import { tool } from "ai";
import { z } from "zod";
import { searchSimilar } from "@/lib/vector-db";
export const advancedRAGTools = {
searchKnowledgeBase: tool({
description: "Search the knowledge base with advanced filtering",
parameters: z.object({
query: z.string(),
topK: z.number().default(5),
minRelevanceScore: z.number().default(0.7),
sourceFilter: z.string().optional(),
}),
execute: async ({ query, topK, minRelevanceScore, sourceFilter }) => {
try {
let results = await searchSimilar(query, topK * 2);
// Filter by relevance score
results = results.filter((r) => (r.score || 0) >= minRelevanceScore);
// Filter by source if specified
if (sourceFilter) {
results = results.filter((r) =>
r.source.toLowerCase().includes(sourceFilter.toLowerCase())
);
}
// Take top K after filtering
results = results.slice(0, topK);
if (results.length === 0) {
return {
success: false,
message: "No relevant information found with current filters",
};
}
return {
success: true,
results: results.map((r, i) => ({
rank: i + 1,
content: r.text,
source: r.title,
relevanceScore: r.score?.toFixed(3),
})),
};
} catch (error) {
return {
success: false,
message: "Failed to search knowledge base",
};
}
},
}),
summarizeDocument: tool({
description: "Generate a summary of a specific document",
parameters: z.object({
documentName: z.string(),
}),
execute: async ({ documentName }) => {
try {
// Retrieve all chunks from this document
const results = await searchSimilar(documentName, 20);
const documentChunks = results
.filter((r) => r.source === documentName)
.map((r) => r.text)
.join("\n\n");
if (!documentChunks) {
return {
success: false,
message: `Document "${documentName}" not found`,
};
}
return {
success: true,
content: documentChunks,
message: `Retrieved content from "${documentName}" for summarization`,
};
} catch (error) {
return {
success: false,
message: "Failed to retrieve document",
};
}
},
}),
compareDocuments: tool({
description: "Compare information across multiple documents",
parameters: z.object({
topic: z.string(),
documentNames: z.array(z.string()),
}),
execute: async ({ topic, documentNames }) => {
try {
const comparisons = await Promise.all(
documentNames.map(async (docName) => {
const results = await searchSimilar(`${topic} ${docName}`, 3);
const relevantChunks = results
.filter((r) => r.source === docName)
.map((r) => r.text);
return {
document: docName,
content: relevantChunks.join("\n"),
};
})
);
return {
success: true,
comparisons,
message: `Retrieved "${topic}" information from ${documentNames.length} documents`,
};
} catch (error) {
return {
success: false,
message: "Failed to compare documents",
};
}
},
}),
citeSources: tool({
description: "Get exact citations for a specific claim",
parameters: z.object({
claim: z.string(),
}),
execute: async ({ claim }) => {
try {
const results = await searchSimilar(claim, 3);
const citations = results.map((r, i) => ({
citationNumber: i + 1,
source: r.title,
excerpt: r.text.substring(0, 200) + "...",
relevanceScore: r.score?.toFixed(3),
}));
return {
success: true,
citations,
};
} catch (error) {
return {
success: false,
message: "Failed to find citations",
};
}
},
}),
};Part 6: Best Practices and Deployment
Performance Optimization
1. Response Streaming
Always use streamText for better UX:
const result = await streamText({
model: openai("gpt-4-turbo"),
messages,
onChunk: ({ chunk }) => {
// Track progress
},
});2. Caching Leverage AI Gateway caching:
// Semantic caching for similar queries
const response = await gatewayFetch({
model: "gpt-4-turbo",
prompt: userQuery,
cache: {
strategy: "semantic",
ttl: 3600,
},
});3. Rate Limiting Implement per-user limits:
export async function POST(req: Request) {
const userId = await getUserId(req);
const rateLimitResult = await checkRateLimit(userId, {
limit: 10,
window: "1m",
});
if (!rateLimitResult.allowed) {
return new Response("Rate limit exceeded", { status: 429 });
}
// Process request
}Security Best Practices
1. Input Validation
import { z } from "zod";
const userInputSchema = z.object({
message: z.string().max(1000),
userId: z.string().uuid(),
});
const validated = userInputSchema.parse(input);2. Prompt Injection Prevention
const sanitizedPrompt = input
.replace(/<\|system\|>/g, "")
.replace(/system:/gi, "")
.trim();3. Content Filtering
import { moderateContent } from "@/lib/moderation";
const moderation = await moderateContent(userMessage);
if (moderation.flagged) {
return Response.json({ error: "Content policy violation" });
}Deployment to Vercel
1. Environment Variables
# .env.local
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...
PINECONE_API_KEY=...
DATABASE_URL=postgresql://...
JWT_SECRET=...2. Vercel Configuration
// vercel.json
{
"functions": {
"app/api/**/*.ts": {
"maxDuration": 60
}
},
"build": {
"env": {
"ENABLE_AI_GATEWAY": "true"
}
}
}3. Deploy Commands
# Install Vercel CLI
npm i -g vercel
# Deploy
vercel --prod
# Set environment variables
vercel env add OPENAI_API_KEYMonitoring and Analytics
1. Track AI Usage
// lib/analytics.ts
export async function trackAIRequest(data: {
userId: string;
model: string;
tokensUsed: number;
cost: number;
latency: number;
}) {
await prisma.aiUsage.create({ data });
}2. Error Tracking
import * as Sentry from '@sentry/nextjs';
try {
const result = await streamText({ ... });
} catch (error) {
Sentry.captureException(error, {
tags: { feature: 'ai-chat' },
extra: { userId, model },
});
}3. Cost Monitoring
// Monitor AI costs per user
const monthlyCost = await prisma.aiUsage.aggregate({
where: {
userId,
createdAt: {
gte: startOfMonth,
},
},
_sum: {
cost: true,
},
});Testing
1. Unit Tests
import { describe, it, expect } from "vitest";
import { ecommerceTools } from "@/lib/agents/ecommerce-tools";
describe("E-commerce Tools", () => {
it("should search products", async () => {
const result = await ecommerceTools.searchProducts.execute({
query: "laptop",
maxPrice: 2000,
});
expect(result.success).toBe(true);
expect(result.products.length).toBeGreaterThan(0);
});
});2. Integration Tests
import { POST } from "@/app/api/chat/route";
describe("Chat API", () => {
it("should handle account creation", async () => {
const response = await POST(
new Request("http://localhost/api/chat", {
method: "POST",
body: JSON.stringify({
messages: [{ role: "user", content: "Create an account" }],
}),
})
);
expect(response.status).toBe(200);
});
});Scaling Considerations
1. Vector Database Scaling
- Use Pinecone's serverless for auto-scaling
- Implement batch embedding for bulk uploads
- Cache frequently accessed embeddings
2. Model Selection Strategy
// Use faster/cheaper models for simple tasks
const modelSelection = {
"simple-query": "gpt-3.5-turbo",
"complex-reasoning": "gpt-4-turbo",
"code-generation": "gpt-4",
chat: "gpt-3.5-turbo",
};3. Load Balancing
- Distribute requests across multiple API keys
- Implement fallback to alternative providers
- Use AI Gateway for automatic routing
Conclusion
This guide has covered:
- ✅ 17 practical AI integration scenarios
- ✅ Complete Vercel AI SDK setup
- ✅ AI Elements and Gateway features
- ✅ Full e-commerce chatbot with agents
- ✅ Complete RAG application
- ✅ Best practices for production
Next Steps
- Customize for Your Use Case: Adapt the agents and tools to your specific business needs
- Extend Functionality: Add more tools like payment processing, analytics, notifications
- Improve RAG: Implement hybrid search, re-ranking, multi-modal support
- Monitor Performance: Set up comprehensive monitoring and alerting
- Gather Feedback: Use analytics to continuously improve AI responses
Resources
- Vercel AI SDK Documentation
- AI Elements Documentation
- AI Gateway Documentation
- OpenAI API Reference
- Pinecone Documentation
© 2024 Your Software Development Agency

