Comprehensive design resources for building aesthetically pleasing, accessible websites. ## Phase 3 Overview: 75 New Resources - 35 UI components (overlays, inputs, layouts, utilities) - 12 integrations (media, icons, fonts, design tools) - 11 tools (image optimization, color utilities, accessibility) - 14 MCPs (AI design automation, Figma sync, documentation) - 3 skills (figma-developer, asset-manager, prototype-designer) ## Phase 3.1: Image Management Suite Integrations (3): - Cloudinary: CDN with URL-based transformations, face detection - Imgix: Real-time image processing via URL parameters - Unsplash: Stock photo API with search and collections Components (6): - Image: Optimized lazy-loading with srcset/sizes, blur placeholder - Gallery: Lightbox gallery with grid/masonry layout, keyboard navigation - Carousel: Touch-enabled carousel with autoplay, indicators - ImageUpload: Drag-and-drop uploader with preview and validation - Avatar: User avatar with fallback initials and status indicator - MediaViewer: Full-screen media viewer with zoom/pan Tools (3): - image-optimizer-tool: Sharp-based optimization (compression, resizing) - responsive-image-generator-tool: Generate srcset and sizes attributes - webp-converter-tool: Convert images to WebP format ## Phase 3.2: Icon & Font Systems Integrations (5): - Heroicons: 500+ icons by Tailwind team (outline/solid variants) - Lucide: 1000+ beautiful open-source icons - Font Awesome: Thousands of icons with multiple styles - Google Fonts: 1400+ fonts with pairing suggestions - Adobe Fonts: Premium web font service integration Components (2): - Icon: Universal icon component supporting all libraries - IconPicker: Icon search and selection interface with previews MCPs (2): - icon-library-mcp: Icon search, management, and SVG generation - font-optimizer-mcp: Font subsetting, optimization, and loading Tools (2): - font-pairing-tool: AI-powered font combination suggestions - type-scale-generator-tool: Generate modular typography scales ## Phase 3.3-3.5: Advanced UI Components (35 components) All with TypeScript, Tailwind CSS + CVA, WCAG 2.1 AA accessibility, full keyboard navigation. Overlays & Dialogs (5): - Tooltip: 4 placements, portal rendering, ARIA describedby - Popover: Floating popover with click/hover triggers - Accordion: Collapsible sections with single/multi-expand modes - Drawer: Slide-out panel from 4 sides with focus trap - Dialog: Accessible modal with backdrop and ESC handling Advanced Inputs (5): - Slider: Range slider with marks, tooltips, dual handles - ColorPicker: HEX/RGB/HSL modes with presets and alpha - DatePicker: Calendar picker with range selection, min/max - TimePicker: 12/24hr formats with keyboard input - FileUpload: Drag-and-drop with preview and validation Feedback & Navigation (6): - Progress: Linear/circular with determinate/indeterminate states - Badge: Notification badge with dot/count variants - Alert: Message alerts (info, success, warning, error) - Breadcrumb: Breadcrumb navigation with custom separators - Stepper: Step-by-step progress indicator - Rating: Star rating with half-star support Layout Components (10): - Grid: CSS Grid wrapper with responsive columns - Container: Max-width container with responsive padding - Stack: Flexbox stack (vertical/horizontal) with spacing - Spacer: Flexible spacing component - Divider: Visual divider (horizontal/vertical) - AspectRatio: Maintain aspect ratio wrapper - Center: Center content horizontally and vertically - Switch: Toggle switch with ARIA switch pattern - Checkbox: Checkbox with indeterminate state - Radio: Radio button with RadioGroup Enhanced Existing (9): - Pagination: Intelligent page number display with ellipsis - Select: Enhanced with search and multi-select - Tabs: Compound component with controlled/uncontrolled modes - Table: Sortable with ARIA table semantics - Skeleton: Loading placeholder (enhanced version) - Button: Added loading states and variants - Card: Enhanced with composable sections - Input: Added password toggle and validation states - Modal: Enhanced with focus trap and body scroll lock ## Phase 3.6: AI & Design Tool MCPs (7 MCPs) - image-generator-mcp: DALL-E, Midjourney, Stable Diffusion integration - svg-generator-mcp: Programmatic SVG generation with templates - illustration-generator-mcp: AI-powered custom illustrations - figma-sync-mcp: Figma-to-code with token extraction - design-handoff-mcp: Automated design handoff workflows - theme-builder-mcp: Generate complete themes from base colors - dark-mode-converter-mcp: Light-to-dark palette transformation ## Phase 3.7: Design Tool Integrations (4 integrations + 3 skills) Integrations: - Figma: Complete REST API client with token extraction - Sketch: Sketch API integration for design assets - Adobe XD: Adobe XD API for prototypes and specs - Canva: Canva API for template and asset management Skills: - figma-developer: Figma-to-code workflows and automation - asset-manager: Asset organization, optimization, brand libraries - prototype-designer: Interactive prototypes and user testing ## Phase 3.8: Design Tools & Utilities (6 tools + 5 MCPs) Tools: - color-palette-generator-tool: Generate harmonious palettes with WCAG - contrast-checker-tool: WCAG 2.1 AA/AAA contrast validation - color-scheme-tool: Complementary, analogous, triadic schemes - breakpoint-analyzer-tool: Analyze responsive breakpoints - color-blindness-simulator-tool: Simulate deuteranopia, protanopia, tritanopia - touch-target-validator-tool: Validate 44px minimum touch targets MCPs: - storybook-generator-mcp: Auto-generate Storybook stories - props-documenter-mcp: Component props documentation - typography-analyzer-mcp: Typography analysis and optimization - asset-library-mcp: Design asset library management - responsive-preview-mcp: Multi-device preview generation ## Registry Updates (Phase 3.9) Updated all META registries with Phase 3 resources: - component-registry.json: 35 → 70 components (+35) - integration-registry.json: 13 → 25 integrations (+12) - tool-registry.json: 13 → 24 tools (+11) - mcp-registry.json: 34 → 48 MCPs (+14) - skill-registry.json: 42 → 45 skills (+3) - .claude/CLAUDE.md: Updated resource counts (150 → 216) - README.md: Updated resource counts New Categories: - Components: media (6) - Integrations: media-services (3), icon-libraries (3), font-services (2), design-tools (4) - Tools: media-tools (3), design-tools (8) - MCPs: design category expanded (2 → 16) ## Also Included - 7 comprehensive playbooks (RAG, multi-agent, MCP dev, component dev, monitoring, performance, security) - 4 observability tools (logger, metrics, tracer, error-tracker) - 4 workflow orchestration components (task-queue, workflow-orchestrator, state-manager, event-bus) - 3 MCP server handler components (tool-handler, resource-handler, prompt-handler) - Phase 2 CLI components and tools (auth, forms, errors, feedback) Total Resources: 216 (was 150) All resources discoverable via CLI and relationship mapping system. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
37 KiB
RAG Implementation Playbook
Complete guide for implementing Retrieval-Augmented Generation (RAG) systems from scratch.
Table of Contents
- Overview
- Prerequisites
- Architecture
- Vector Database Setup
- Document Ingestion Pipeline
- Embedding Generation
- Query Processing
- Retrieval Pipeline
- Response Generation
- Evaluation & Metrics
- Production Deployment
- Common Pitfalls
- Troubleshooting
Overview
Retrieval-Augmented Generation (RAG) combines the power of large language models with external knowledge bases to provide accurate, grounded responses. This playbook guides you through implementing a production-ready RAG system.
What is RAG?
RAG enhances LLM responses by:
- Retrieving relevant documents from a knowledge base
- Augmenting the prompt with retrieved context
- Generating responses grounded in retrieved information
Use Cases
- Document Q&A systems
- Customer support chatbots
- Knowledge base search
- Technical documentation assistants
- Legal/medical document analysis
- Enterprise knowledge management
Architecture Diagram
graph TB
A[User Query] --> B[Query Processing]
B --> C[Embedding Generation]
C --> D[Vector Search]
D --> E[Document Retrieval]
E --> F[Reranking]
F --> G[Context Assembly]
G --> H[LLM Generation]
H --> I[Response]
J[Document Sources] --> K[Document Loading]
K --> L[Text Chunking]
L --> M[Embedding Generation]
M --> N[Vector Storage]
N --> D
Prerequisites
Required Skills
- rag-implementer
- knowledge-base-manager
Required MCPs
- vector-database-mcp
- embedding-generator-mcp
- semantic-search-mcp
Dependencies
{
"dependencies": {
"@pinecone-database/pinecone": "^1.1.0",
"langchain": "^0.1.0",
"openai": "^4.20.0",
"cohere-ai": "^7.0.0",
"pdf-parse": "^1.1.1",
"mammoth": "^1.6.0",
"tiktoken": "^1.0.0",
"zod": "^3.22.0"
}
}
Environment Setup
# Required API keys
export OPENAI_API_KEY="sk-..."
export PINECONE_API_KEY="..."
export PINECONE_ENVIRONMENT="us-west1-gcp"
export COHERE_API_KEY="..." # Optional, for reranking
# Optional: For local development
export CHROMA_HOST="localhost:8000"
Architecture
System Components
graph LR
subgraph Ingestion Pipeline
A[Document Loader] --> B[Text Chunker]
B --> C[Embedding Generator]
C --> D[Vector Store]
end
subgraph Query Pipeline
E[Query] --> F[Query Rewriter]
F --> G[Embedding Generator]
G --> H[Retrieval]
H --> I[Reranker]
I --> J[LLM]
end
D --> H
Key Design Decisions
-
Vector Database Selection
- Pinecone: Managed, scalable, expensive
- Weaviate: Self-hosted, feature-rich
- Chroma: Lightweight, local development
- Qdrant: High performance, Rust-based
- PgVector: PostgreSQL extension, simple
-
Embedding Model Selection
- OpenAI text-embedding-3-small (1536 dims, $0.02/1M tokens)
- OpenAI text-embedding-3-large (3072 dims, $0.13/1M tokens)
- Cohere embed-english-v3.0 (1024 dims, free tier)
- Local models (sentence-transformers, free)
-
Chunking Strategy
- Fixed-size: Predictable, fast
- Recursive: Respects structure
- Semantic: AI-powered boundaries
- Sentence-based: Natural boundaries
- Markdown-aware: Preserves headers
Vector Database Setup
Pinecone Setup
import { Pinecone } from '@pinecone-database/pinecone';
// Initialize Pinecone client
const pinecone = new Pinecone({
apiKey: process.env.PINECONE_API_KEY!,
environment: process.env.PINECONE_ENVIRONMENT!
});
// Create index
await pinecone.createIndex({
name: 'documents',
dimension: 1536, // Match embedding dimension
metric: 'cosine', // cosine, euclidean, or dotproduct
spec: {
serverless: {
cloud: 'aws',
region: 'us-west-2'
}
}
});
// Get index
const index = pinecone.Index('documents');
// Upsert vectors
await index.upsert([
{
id: 'doc1',
values: [0.1, 0.2, ...], // 1536-dimensional vector
metadata: {
text: 'Document content...',
source: 'file.pdf',
page: 1
}
}
]);
// Query vectors
const results = await index.query({
vector: [0.1, 0.2, ...],
topK: 5,
includeMetadata: true,
filter: {
source: { $eq: 'file.pdf' }
}
});
Weaviate Setup
import weaviate, { WeaviateClient } from 'weaviate-ts-client';
// Initialize client
const client: WeaviateClient = weaviate.client({
scheme: 'http',
host: 'localhost:8080',
apiKey: new weaviate.ApiKey(process.env.WEAVIATE_API_KEY!)
});
// Create schema
await client.schema
.classCreator()
.withClass({
class: 'Document',
description: 'Document chunks for RAG',
vectorizer: 'text2vec-openai',
moduleConfig: {
'text2vec-openai': {
model: 'text-embedding-3-small',
modelVersion: '001',
type: 'text'
}
},
properties: [
{
name: 'text',
dataType: ['text'],
description: 'Document content'
},
{
name: 'source',
dataType: ['string'],
description: 'Source file'
},
{
name: 'page',
dataType: ['int'],
description: 'Page number'
}
]
})
.do();
// Import data
await client.data
.creator()
.withClassName('Document')
.withProperties({
text: 'Document content...',
source: 'file.pdf',
page: 1
})
.do();
// Query
const result = await client.graphql
.get()
.withClassName('Document')
.withFields('text source page _additional { distance }')
.withNearText({ concepts: ['search query'] })
.withLimit(5)
.do();
Chroma Setup (Local)
import { ChromaClient } from 'chromadb';
// Initialize client
const client = new ChromaClient({
path: 'http://localhost:8000'
});
// Create collection
const collection = await client.createCollection({
name: 'documents',
metadata: { 'hnsw:space': 'cosine' }
});
// Add documents
await collection.add({
ids: ['doc1', 'doc2'],
embeddings: [[0.1, 0.2, ...], [0.3, 0.4, ...]],
metadatas: [
{ source: 'file1.pdf', page: 1 },
{ source: 'file2.pdf', page: 1 }
],
documents: ['First document...', 'Second document...']
});
// Query
const results = await collection.query({
queryEmbeddings: [[0.1, 0.2, ...]],
nResults: 5,
where: { source: 'file1.pdf' }
});
Using VectorStoreClient Component
import { VectorStoreClient, createVectorStoreClient } from '../COMPONENTS/rag-pipelines/vector-store-client';
// Create client (auto-detects from environment)
const client = createVectorStoreClient('pinecone', {
apiKey: process.env.PINECONE_API_KEY,
environment: 'us-west1-gcp'
});
// Create collection
await client.createCollection('documents', {
dimension: 1536,
metric: 'cosine'
});
// Batch upsert with progress
await client.batchUpsert('documents', vectors, {
batchSize: 100,
onProgress: (current, total) => {
console.log(`Upserting: ${current}/${total}`);
}
});
// Search with filters
const results = await client.search('documents', queryVector, {
topK: 10,
scoreThreshold: 0.7,
filter: {
category: 'tutorial',
language: 'en'
},
includeMetadata: true
});
Document Ingestion Pipeline
Document Loading
import { DocumentLoader } from '../COMPONENTS/rag-pipelines/document-loader';
// Initialize loader
const loader = new DocumentLoader({
maxFileSize: 10 * 1024 * 1024, // 10MB
supportedFormats: ['txt', 'md', 'html', 'json', 'csv', 'pdf', 'docx'],
concurrency: 5,
retries: 3
});
// Load from files
const documents = await loader.loadFromFiles([
'./docs/guide.md',
'./docs/api.pdf',
'./docs/tutorial.docx'
]);
// Load from directory
const allDocs = await loader.loadFromDirectory('./docs', {
recursive: true,
extensions: ['md', 'pdf'],
ignore: ['node_modules', '.git']
});
// Load from URLs
const webDocs = await loader.loadFromUrls([
'https://example.com/docs/page1.html',
'https://example.com/docs/page2.html'
]);
// Custom loader for specific format
const customDocs = await loader.loadCustom([
{
path: './data/custom.json',
parser: async (content) => {
const data = JSON.parse(content);
return data.articles.map(article => ({
content: article.body,
metadata: {
title: article.title,
author: article.author,
date: article.publishedAt
}
}));
}
}
]);
console.log(`Loaded ${documents.length} documents`);
documents.forEach(doc => {
console.log(`- ${doc.metadata.source}: ${doc.content.length} chars`);
});
Text Chunking
import { TextChunker, ChunkingUtils } from '../COMPONENTS/rag-pipelines/text-chunker';
// Analyze document to get recommendations
const analysis = ChunkingUtils.analyzeText(document.content);
console.log('Recommended strategy:', analysis.strategy);
console.log('Recommended chunk size:', analysis.recommendedChunkSize);
console.log('Document stats:', analysis.stats);
// Create chunker with recommended settings
const chunker = new TextChunker({
chunkSize: analysis.recommendedChunkSize,
chunkOverlap: analysis.recommendedOverlap,
strategy: analysis.strategy,
tokenizer: 'tiktoken', // or 'simple', 'recursive'
preserveContext: true
});
// Chunk the text
const chunks = await chunker.chunkText(document.content, {
metadata: document.metadata
});
// Validate chunks
chunks.forEach((chunk, i) => {
const validation = ChunkingUtils.validateChunk(chunk);
if (!validation.valid) {
console.warn(`Chunk ${i} has issues:`, validation.issues);
console.warn(`Quality score: ${validation.score}/100`);
}
});
// Merge small chunks
const mergedChunks = ChunkingUtils.mergeSmallChunks(chunks, 100);
console.log(`Created ${chunks.length} chunks`);
console.log(`After merging: ${mergedChunks.length} chunks`);
Advanced Chunking Strategies
// Fixed-size chunking (fast, predictable)
const fixedChunker = new TextChunker({
strategy: 'fixed',
chunkSize: 1000,
chunkOverlap: 200
});
// Recursive chunking (respects structure)
const recursiveChunker = new TextChunker({
strategy: 'recursive',
chunkSize: 1000,
chunkOverlap: 200,
separators: ['\n\n', '\n', '. ', ' ']
});
// Semantic chunking (AI-powered)
const semanticChunker = new TextChunker({
strategy: 'semantic',
chunkSize: 1000,
embeddingModel: 'text-embedding-3-small',
similarityThreshold: 0.8
});
// Sentence-based chunking
const sentenceChunker = new TextChunker({
strategy: 'sentence',
chunkSize: 1000,
chunkOverlap: 200
});
// Markdown-aware chunking
const markdownChunker = new TextChunker({
strategy: 'markdown',
chunkSize: 1000,
chunkOverlap: 200,
preserveHeaders: true
});
Embedding Generation
OpenAI Embeddings
import { EmbeddingPipeline } from '../COMPONENTS/rag-pipelines/embedding-pipeline';
// Create embedding pipeline
const pipeline = new EmbeddingPipeline({
provider: 'openai',
model: 'text-embedding-3-small',
apiKey: process.env.OPENAI_API_KEY,
batchSize: 100,
cache: true,
onProgress: ({ current, total, stage }) => {
console.log(`${stage}: ${current}/${total}`);
}
});
// Embed single text
const queryEmbedding = await pipeline.embedText('What is RAG?');
console.log('Embedding dimensions:', queryEmbedding.length);
// Embed documents in batch
const embeddings = await pipeline.embedDocuments(chunks.map(c => c.content));
console.log(`Generated ${embeddings.length} embeddings`);
console.log(`Cache hit rate: ${pipeline.getCacheStats().hitRate}%`);
Multi-Provider Support
// Cohere embeddings
const cohereEmbeddings = new EmbeddingPipeline({
provider: 'cohere',
model: 'embed-english-v3.0',
apiKey: process.env.COHERE_API_KEY,
inputType: 'search_document' // or 'search_query'
});
// HuggingFace embeddings (local)
const hfEmbeddings = new EmbeddingPipeline({
provider: 'huggingface',
model: 'sentence-transformers/all-MiniLM-L6-v2',
device: 'cpu' // or 'cuda'
});
Advanced Embedding Features
import { SimilarityCalculator, EmbeddingCacheManager } from '../COMPONENTS/rag-pipelines/embedding-pipeline';
// Compute similarity matrix
const vectors = embeddings.map(e => e.embedding);
const similarityMatrix = SimilarityCalculator.computeSimilarityMatrix(vectors);
// Find most similar documents
const similar = SimilarityCalculator.findMostSimilar(
queryEmbedding,
vectors,
5 // top K
);
console.log('Most similar documents:', similar);
// Cluster documents
const clusters = SimilarityCalculator.clusterVectors(vectors, 3);
console.log('Document clusters:', clusters);
// Advanced cache management
const cacheManager = new EmbeddingCacheManager(
10000, // max size
7 * 24 * 60 * 60 * 1000 // 7 days TTL
);
// Use cache
const cached = cacheManager.get('some text');
if (!cached) {
const embedding = await pipeline.embedText('some text');
cacheManager.set('some text', embedding);
}
// Export/import cache
const cacheJson = cacheManager.export();
fs.writeFileSync('cache.json', JSON.stringify(cacheJson));
// Later...
const loadedCache = JSON.parse(fs.readFileSync('cache.json', 'utf-8'));
cacheManager.import(loadedCache);
// Clean expired entries
const cleaned = cacheManager.cleanExpired();
console.log(`Cleaned ${cleaned} expired entries`);
Query Processing
Query Rewriting
import { QueryRewriter } from '../COMPONENTS/rag-pipelines/retrieval-pipeline';
const originalQuery = 'How to implement authentication?';
// Expand with synonyms
const expandedQueries = QueryRewriter.expandWithSynonyms(originalQuery);
console.log('Expanded queries:', expandedQueries);
// Output: [
// 'How to implement authentication?',
// 'How to implement auth?',
// 'How to implement login?',
// 'How to implement user verification?'
// ]
// Decompose complex query
const decomposed = QueryRewriter.decomposeQuery(
'What are the security implications of OAuth 2.0 and how do I implement it in Node.js?'
);
console.log('Decomposed queries:', decomposed);
// Output: [
// 'What are the security implications of OAuth 2.0?',
// 'How do I implement OAuth 2.0 in Node.js?'
// ]
// Simplify query (remove stop words)
const simplified = QueryRewriter.simplifyQuery(originalQuery);
console.log('Simplified query:', simplified);
// Output: 'implement authentication'
// HyDE (Hypothetical Document Embeddings)
const hypotheticalDoc = await QueryRewriter.generateHypotheticalDocument(
originalQuery,
llm
);
console.log('Hypothetical document:', hypotheticalDoc);
// Output: 'To implement authentication, you need to...'
Query Expansion with LLM
import OpenAI from 'openai';
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY
});
async function expandQuery(query: string): Promise<string[]> {
const response = await openai.chat.completions.create({
model: 'gpt-3.5-turbo',
messages: [
{
role: 'system',
content: 'Generate 3 alternative phrasings of the user query that preserve the same meaning.'
},
{
role: 'user',
content: query
}
],
temperature: 0.7
});
const alternatives = response.choices[0].message.content!.split('\n').filter(Boolean);
return [query, ...alternatives];
}
const expandedQueries = await expandQuery('How to secure an API?');
console.log(expandedQueries);
Retrieval Pipeline
Basic Retrieval
import { RetrievalPipeline } from '../COMPONENTS/rag-pipelines/retrieval-pipeline';
// Create retrieval pipeline
const retrieval = new RetrievalPipeline({
vectorStore: client,
searchType: 'hybrid', // 'vector', 'keyword', or 'hybrid'
topK: 20,
rerank: true,
fusionMethod: 'rrf', // 'rrf', 'weighted', or 'max'
scoreThreshold: 0.7
});
// Retrieve documents
const results = await retrieval.retrieve('What is RAG?');
console.log(`Found ${results.documents.length} documents`);
results.documents.forEach((doc, i) => {
console.log(`${i + 1}. Score: ${results.scores[i]}`);
console.log(` Source: ${doc.metadata.source}`);
console.log(` Preview: ${doc.pageContent.substring(0, 100)}...`);
});
Advanced Retrieval with Reranking
import { ResultReranker } from '../COMPONENTS/rag-pipelines/retrieval-pipeline';
// Retrieve initial results
let results = await retrieval.retrieve(query, { topK: 20 });
// Rerank by keywords
const keywordReranked = ResultReranker.rerankByKeywords(
query,
results.documents.map((doc, i) => [doc, results.scores[i]])
);
// Rerank by metadata (prefer recent documents)
const metadataReranked = ResultReranker.rerankByMetadata(
keywordReranked,
{ category: 'tutorial' }
);
// Rerank by recency
const recencyReranked = ResultReranker.rerankByRecency(
metadataReranked,
0.3 // 30% weight on recency
);
// Apply diversity (MMR)
const diverseResults = ResultReranker.rerankByDiversity(
recencyReranked,
0.7 // Balance: 70% relevance, 30% diversity
);
// Extract final documents
const finalDocuments = diverseResults.map(([doc]) => doc);
Hybrid Search Implementation
async function hybridSearch(
query: string,
vectorStore: VectorStoreClient,
topK: number = 10
) {
// 1. Vector search
const queryEmbedding = await pipeline.embedText(query);
const vectorResults = await vectorStore.search('documents', queryEmbedding, {
topK: topK * 2 // Retrieve more for fusion
});
// 2. Keyword search (BM25)
const keywordResults = await performKeywordSearch(query, topK * 2);
// 3. Fusion using Reciprocal Rank Fusion (RRF)
const fused = fuseResults(vectorResults, keywordResults, 'rrf');
// 4. Return top K
return fused.slice(0, topK);
}
function fuseResults(
vectorResults: any[],
keywordResults: any[],
method: 'rrf' | 'weighted' | 'max'
): any[] {
if (method === 'rrf') {
const k = 60; // RRF constant
const scores = new Map<string, number>();
vectorResults.forEach((doc, rank) => {
const id = doc.id;
scores.set(id, (scores.get(id) || 0) + 1 / (k + rank + 1));
});
keywordResults.forEach((doc, rank) => {
const id = doc.id;
scores.set(id, (scores.get(id) || 0) + 1 / (k + rank + 1));
});
return Array.from(scores.entries())
.sort((a, b) => b[1] - a[1])
.map(([id, score]) => ({ id, score }));
}
// Implement other fusion methods...
throw new Error(`Fusion method ${method} not implemented`);
}
Response Generation
Basic Generation with Context
import OpenAI from 'openai';
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY
});
async function generateResponse(
query: string,
context: string[]
): Promise<string> {
const contextText = context
.map((doc, i) => `[${i + 1}] ${doc}`)
.join('\n\n');
const response = await openai.chat.completions.create({
model: 'gpt-4-turbo-preview',
messages: [
{
role: 'system',
content: `You are a helpful assistant. Answer the user's question based ONLY on the provided context. If the answer is not in the context, say "I don't have enough information to answer that question."`
},
{
role: 'user',
content: `Context:\n${contextText}\n\nQuestion: ${query}`
}
],
temperature: 0.7,
max_tokens: 500
});
return response.choices[0].message.content!;
}
// Usage
const retrievedDocs = results.documents.map(doc => doc.pageContent);
const answer = await generateResponse(query, retrievedDocs);
console.log('Answer:', answer);
Advanced Generation with Citations
async function generateResponseWithCitations(
query: string,
documents: Array<{ content: string; source: string; page?: number }>
): Promise<{ answer: string; citations: string[] }> {
const contextText = documents
.map((doc, i) => `[${i + 1}] (Source: ${doc.source}${doc.page ? `, Page: ${doc.page}` : ''})\n${doc.content}`)
.join('\n\n');
const response = await openai.chat.completions.create({
model: 'gpt-4-turbo-preview',
messages: [
{
role: 'system',
content: `You are a helpful assistant. Answer the user's question based on the provided context.
Include citations using [1], [2], etc. to reference the source documents.
If the answer is not in the context, say "I don't have enough information to answer that question."`
},
{
role: 'user',
content: `Context:\n${contextText}\n\nQuestion: ${query}`
}
],
temperature: 0.7,
max_tokens: 500
});
const answer = response.choices[0].message.content!;
// Extract citations
const citationRegex = /\[(\d+)\]/g;
const citationMatches = answer.matchAll(citationRegex);
const citationIndices = new Set(
Array.from(citationMatches).map(match => parseInt(match[1]) - 1)
);
const citations = Array.from(citationIndices)
.map(i => `[${i + 1}] ${documents[i].source}${documents[i].page ? ` (Page ${documents[i].page})` : ''}`)
.sort();
return { answer, citations };
}
// Usage
const { answer, citations } = await generateResponseWithCitations(query, results.documents);
console.log('Answer:', answer);
console.log('\nCitations:');
citations.forEach(citation => console.log(citation));
Streaming Responses
async function generateStreamingResponse(
query: string,
context: string[]
): Promise<AsyncIterable<string>> {
const contextText = context.map((doc, i) => `[${i + 1}] ${doc}`).join('\n\n');
const stream = await openai.chat.completions.create({
model: 'gpt-4-turbo-preview',
messages: [
{
role: 'system',
content: 'You are a helpful assistant. Answer based ONLY on the provided context.'
},
{
role: 'user',
content: `Context:\n${contextText}\n\nQuestion: ${query}`
}
],
stream: true
});
return stream;
}
// Usage
const stream = await generateStreamingResponse(query, retrievedDocs);
for await (const chunk of stream) {
const content = chunk.choices[0]?.delta?.content || '';
process.stdout.write(content);
}
Evaluation & Metrics
Retrieval Metrics
import { RetrievalMetrics } from '../COMPONENTS/rag-pipelines/retrieval-pipeline';
// Ground truth: relevant document IDs
const relevantDocs = ['doc1', 'doc2', 'doc3'];
// Retrieved document IDs
const retrievedIds = results.documents.map(d => d.metadata.id);
// Precision@K
const precision5 = RetrievalMetrics.precisionAtK(retrievedIds, relevantDocs, 5);
const precision10 = RetrievalMetrics.precisionAtK(retrievedIds, relevantDocs, 10);
console.log(`Precision@5: ${precision5.toFixed(3)}`);
console.log(`Precision@10: ${precision10.toFixed(3)}`);
// Recall@K
const recall5 = RetrievalMetrics.recallAtK(retrievedIds, relevantDocs, 5);
const recall10 = RetrievalMetrics.recallAtK(retrievedIds, relevantDocs, 10);
console.log(`Recall@5: ${recall5.toFixed(3)}`);
console.log(`Recall@10: ${recall10.toFixed(3)}`);
// Mean Reciprocal Rank (MRR)
const mrr = RetrievalMetrics.meanReciprocalRank(retrievedIds, relevantDocs);
console.log(`MRR: ${mrr.toFixed(3)}`);
// NDCG@K (with relevance scores)
const relevanceScores = new Map([
['doc1', 3], // Highly relevant
['doc2', 2], // Relevant
['doc3', 1] // Somewhat relevant
]);
const ndcg5 = RetrievalMetrics.ndcgAtK(retrievedIds, relevanceScores, 5);
const ndcg10 = RetrievalMetrics.ndcgAtK(retrievedIds, relevanceScores, 10);
console.log(`NDCG@5: ${ndcg5.toFixed(3)}`);
console.log(`NDCG@10: ${ndcg10.toFixed(3)}`);
Generation Metrics
// ROUGE scores (for summarization)
import * as rouge from 'rouge';
function calculateRouge(reference: string, generated: string) {
const scores = rouge.n(reference, generated, { n: 2 });
return {
rouge1: scores.rouge1,
rouge2: scores.rouge2,
rougeL: scores.rougeL
};
}
// Faithfulness check (is response grounded in context?)
async function checkFaithfulness(
answer: string,
context: string[]
): Promise<number> {
const contextText = context.join('\n');
const response = await openai.chat.completions.create({
model: 'gpt-4-turbo-preview',
messages: [
{
role: 'system',
content: 'Rate how well the answer is supported by the context. Return only a number from 0 (not supported) to 10 (fully supported).'
},
{
role: 'user',
content: `Context:\n${contextText}\n\nAnswer: ${answer}`
}
],
temperature: 0
});
return parseFloat(response.choices[0].message.content!);
}
// Relevance check (does answer address the query?)
async function checkRelevance(query: string, answer: string): Promise<number> {
const response = await openai.chat.completions.create({
model: 'gpt-4-turbo-preview',
messages: [
{
role: 'system',
content: 'Rate how well the answer addresses the query. Return only a number from 0 (irrelevant) to 10 (perfectly relevant).'
},
{
role: 'user',
content: `Query: ${query}\n\nAnswer: ${answer}`
}
],
temperature: 0
});
return parseFloat(response.choices[0].message.content!);
}
End-to-End Evaluation
interface EvaluationResult {
query: string;
answer: string;
precision: number;
recall: number;
faithfulness: number;
relevance: number;
latency: number;
}
async function evaluateRAG(
testQueries: Array<{ query: string; relevantDocs: string[] }>,
rag: RAGOrchestrator
): Promise<EvaluationResult[]> {
const results: EvaluationResult[] = [];
for (const { query, relevantDocs } of testQueries) {
const startTime = Date.now();
// Run query
const result = await rag.query(query);
const retrievedIds = result.documents.map(d => d.metadata.id);
const answer = await generateResponse(
query,
result.documents.map(d => d.pageContent)
);
const latency = Date.now() - startTime;
// Calculate metrics
const precision = RetrievalMetrics.precisionAtK(retrievedIds, relevantDocs, 5);
const recall = RetrievalMetrics.recallAtK(retrievedIds, relevantDocs, 5);
const faithfulness = await checkFaithfulness(
answer,
result.documents.map(d => d.pageContent)
);
const relevance = await checkRelevance(query, answer);
results.push({
query,
answer,
precision,
recall,
faithfulness,
relevance,
latency
});
}
return results;
}
// Run evaluation
const testQueries = [
{ query: 'What is RAG?', relevantDocs: ['doc1', 'doc2'] },
{ query: 'How to implement embeddings?', relevantDocs: ['doc3', 'doc4'] }
];
const evalResults = await evaluateRAG(testQueries, rag);
// Calculate averages
const avgPrecision = evalResults.reduce((sum, r) => sum + r.precision, 0) / evalResults.length;
const avgRecall = evalResults.reduce((sum, r) => sum + r.recall, 0) / evalResults.length;
const avgFaithfulness = evalResults.reduce((sum, r) => sum + r.faithfulness, 0) / evalResults.length;
const avgRelevance = evalResults.reduce((sum, r) => sum + r.relevance, 0) / evalResults.length;
const avgLatency = evalResults.reduce((sum, r) => sum + r.latency, 0) / evalResults.length;
console.log('Evaluation Results:');
console.log(`Average Precision@5: ${avgPrecision.toFixed(3)}`);
console.log(`Average Recall@5: ${avgRecall.toFixed(3)}`);
console.log(`Average Faithfulness: ${avgFaithfulness.toFixed(1)}/10`);
console.log(`Average Relevance: ${avgRelevance.toFixed(1)}/10`);
console.log(`Average Latency: ${avgLatency.toFixed(0)}ms`);
Production Deployment
Complete RAG Orchestrator
import { RAGOrchestrator } from '../COMPONENTS/rag-pipelines/rag-orchestrator';
// Initialize orchestrator
const rag = new RAGOrchestrator({
vectorStore: client,
embeddingProvider: 'openai',
embeddingModel: 'text-embedding-3-small',
chunkSize: 1000,
chunkOverlap: 200,
searchType: 'hybrid',
rerank: true,
onProgress: (progress) => {
console.log(`${progress.stage}: ${progress.current}/${progress.total}`);
}
});
// Index documents
const indexResult = await rag.indexDocuments([
{ path: './docs/guide.md', type: 'md' },
{ path: './docs/api.pdf', type: 'pdf' }
]);
console.log(`Indexed ${indexResult.chunksCreated} chunks in ${indexResult.timeMs}ms`);
// Query
const queryResult = await rag.query('What is RAG?', {
topK: 5,
rerank: true,
scoreThreshold: 0.7
});
console.log(`Found ${queryResult.documents.length} relevant documents`);
Monitoring with RAGMonitor
import { RAGMonitor } from '../COMPONENTS/rag-pipelines/rag-orchestrator';
const monitor = new RAGMonitor();
// Record queries
const startTime = Date.now();
const result = await rag.query('test query');
const executionTime = Date.now() - startTime;
monitor.recordQuery(executionTime, false);
// Record errors
try {
await rag.query('bad query');
} catch (error) {
monitor.recordError(error.message);
}
// Get metrics
const metrics = monitor.getMetrics();
console.log('Query metrics:', {
queryCount: metrics.queryCount,
avgQueryTime: metrics.avgQueryTime,
cacheHitRate: metrics.cacheHitRate,
errorCount: metrics.errors.length
});
// Export for analysis
const metricsJson = monitor.exportMetrics();
fs.writeFileSync('metrics.json', JSON.stringify(metricsJson, null, 2));
Docker Deployment
# Dockerfile
FROM node:18-alpine
WORKDIR /app
# Install dependencies
COPY package*.json ./
RUN npm ci --production
# Copy application
COPY . .
# Build TypeScript
RUN npm run build
# Expose port
EXPOSE 3000
# Start application
CMD ["node", "dist/index.js"]
# docker-compose.yml
version: '3.8'
services:
rag-api:
build: .
ports:
- "3000:3000"
environment:
- OPENAI_API_KEY=${OPENAI_API_KEY}
- PINECONE_API_KEY=${PINECONE_API_KEY}
- PINECONE_ENVIRONMENT=${PINECONE_ENVIRONMENT}
- NODE_ENV=production
volumes:
- ./data:/app/data
restart: unless-stopped
chroma:
image: chromadb/chroma:latest
ports:
- "8000:8000"
volumes:
- chroma-data:/chroma/chroma
restart: unless-stopped
volumes:
chroma-data:
API Server
import express from 'express';
const app = express();
app.use(express.json());
// Initialize RAG
const rag = new RAGOrchestrator({ /* config */ });
await rag.indexDocuments(/* sources */);
// Query endpoint
app.post('/query', async (req, res) => {
try {
const { query, topK = 5 } = req.body;
if (!query) {
return res.status(400).json({ error: 'Query is required' });
}
const startTime = Date.now();
const result = await rag.query(query, { topK });
const latency = Date.now() - startTime;
const answer = await generateResponse(
query,
result.documents.map(d => d.pageContent)
);
res.json({
answer,
documents: result.documents.map(d => ({
content: d.pageContent,
source: d.metadata.source,
score: d.metadata.score
})),
latency
});
} catch (error) {
console.error('Query error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Health check
app.get('/health', (req, res) => {
res.json({ status: 'healthy' });
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`RAG API running on port ${PORT}`);
});
Common Pitfalls
1. Chunking Too Large or Too Small
Problem: Chunks that are too large lose semantic focus; chunks that are too small lack context.
Solution: Use ChunkingUtils.analyzeText() to get recommendations. Typical sizes: 500-1500 tokens.
const analysis = ChunkingUtils.analyzeText(document);
const chunker = new TextChunker({
chunkSize: analysis.recommendedChunkSize,
chunkOverlap: analysis.recommendedOverlap
});
2. Not Using Overlap
Problem: Important information split across chunk boundaries.
Solution: Use 10-20% overlap between chunks.
const chunker = new TextChunker({
chunkSize: 1000,
chunkOverlap: 200 // 20% overlap
});
3. Ignoring Metadata
Problem: Retrieved documents lack context for filtering and ranking.
Solution: Include rich metadata during indexing.
await vectorStore.upsert([
{
id: 'doc1',
values: embedding,
metadata: {
text: 'Document content',
source: 'guide.pdf',
page: 1,
category: 'tutorial',
language: 'en',
date: '2024-01-01',
author: 'John Doe'
}
}
]);
4. Not Reranking Results
Problem: Initial vector search may not capture semantic relevance accurately.
Solution: Always rerank top results.
const retrieval = new RetrievalPipeline({
vectorStore,
rerank: true,
topK: 20 // Retrieve more for reranking
});
5. Poor Query Quality
Problem: User queries are often vague or poorly phrased.
Solution: Implement query rewriting and expansion.
const expandedQueries = QueryRewriter.expandWithSynonyms(query);
const simplified = QueryRewriter.simplifyQuery(query);
6. No Caching
Problem: Repeated queries waste API calls and time.
Solution: Enable caching at multiple levels.
// Embedding cache
const pipeline = new EmbeddingPipeline({
provider: 'openai',
cache: true
});
// Tool cache
const toolHandler = new MCPToolHandler({
cache: { enabled: true, ttl: 300000 }
});
7. Ignoring Evaluation
Problem: No way to measure system performance or improvements.
Solution: Implement comprehensive evaluation.
const evalResults = await evaluateRAG(testQueries, rag);
console.log('Average Precision:', avgPrecision);
Troubleshooting
Issue: Low Retrieval Accuracy
Symptoms: Irrelevant documents returned for queries.
Diagnosis:
// Check embedding quality
const queryEmbedding = await pipeline.embedText(query);
console.log('Embedding dimensions:', queryEmbedding.length);
// Check vector search results
const results = await vectorStore.search('documents', queryEmbedding, {
topK: 10,
includeMetadata: true
});
console.log('Top results:');
results.forEach((r, i) => {
console.log(`${i + 1}. Score: ${r.score}, Source: ${r.metadata.source}`);
});
Solutions:
- Improve chunking strategy
- Use hybrid search instead of pure vector search
- Implement query rewriting
- Add metadata filters
- Use reranking
Issue: Slow Query Performance
Symptoms: Queries take >2 seconds.
Diagnosis:
const startTime = Date.now();
const result = await rag.query(query);
console.log('Query time:', Date.now() - startTime, 'ms');
Solutions:
- Enable caching
- Reduce
topKvalue - Use approximate nearest neighbor search
- Optimize vector database configuration
- Use smaller embedding model
- Implement request batching
Issue: Out of Memory
Symptoms: Application crashes with OOM errors.
Diagnosis:
console.log('Memory usage:', process.memoryUsage());
Solutions:
- Batch document processing
- Stream large files
- Clear cache periodically
- Reduce chunk overlap
- Use pagination for large result sets
Issue: Poor Answer Quality
Symptoms: LLM generates incorrect or hallucinated answers.
Diagnosis:
const faithfulness = await checkFaithfulness(answer, context);
console.log('Faithfulness score:', faithfulness);
Solutions:
- Improve retrieval accuracy
- Add explicit instructions in system prompt
- Increase context window
- Use citation-based generation
- Implement answer validation
Issue: High API Costs
Symptoms: Unexpected high costs for embeddings/LLM.
Diagnosis:
const metrics = monitor.getMetrics();
console.log('API calls:', metrics.queryCount);
console.log('Cache hit rate:', metrics.cacheHitRate);
Solutions:
- Enable aggressive caching
- Use smaller/cheaper models
- Implement rate limiting
- Batch API calls
- Use local embeddings for development
Related Resources
Skills
- rag-implementer
- knowledge-base-manager
- semantic-search
MCPs
- vector-database-mcp
- embedding-generator-mcp
- semantic-search-mcp
- knowledge-base-mcp
Components
/COMPONENTS/rag-pipelines/- All RAG components/COMPONENTS/workflows/- Workflow orchestration/TOOLS/langchain-tools/- LangChain integration tools
Further Reading
Last Updated: 2025-10-27 Version: 1.0.0