Knowledge Base
RAG (Retrieval-Augmented Generation), embeddings, and semantic search for document retrieval.
Installation
pnpm add @aicr/knowledge-baseConfiguration
Set the OPENAI_API_KEY environment variable for embeddings:
OPENAI_API_KEY=sk-xxxxxxxxxxxxQuick Start
import { createKnowledgeBase } from '@aicr/knowledge-base';
// Create knowledge base
const kb = createKnowledgeBase({
openaiApiKey: process.env.OPENAI_API_KEY,
embeddingModel: 'text-embedding-3-small',
chunkSize: 1000,
chunkOverlap: 200
});
// Ingest documents
await kb.ingest({
id: 'doc-1',
content: 'Your document content here...',
title: 'Product Manual',
source: 'https://example.com/manual.pdf'
});
// Query
const results = await kb.query('How do I reset the device?');
console.log(results[0].content); // Most relevant chunk
console.log(results[0].score); // Similarity score (0-1)API Reference
createKnowledgeBase(config)
Create a knowledge base client.
interface KnowledgeBaseConfig {
openaiApiKey?: string;
embeddingModel?: 'text-embedding-3-small' | 'text-embedding-3-large' | 'text-embedding-ada-002';
chunkSize?: number; // Default: 1000 characters
chunkOverlap?: number; // Default: 200 characters
vectorStore?: VectorStoreAdapter; // Custom vector store
debug?: boolean;
}kb.ingest(document)
Add a document to the knowledge base.
interface Document {
id: string; // Unique document ID
content: string; // Document content
title?: string; // Document title
source?: string; // Source URL or path
metadata?: Record<string, unknown>; // Custom metadata
tenantId?: string; // For multi-tenant isolation
}
interface IngestResult {
documentId: string;
chunkCount: number;
durationMs: number;
warnings?: string[];
}Example:
const result = await kb.ingest({
id: 'policy-2024',
content: policyDocument,
title: 'Company Policy 2024',
source: '/documents/policy-2024.pdf',
metadata: {
department: 'HR',
version: '2.0',
effectiveDate: '2024-01-01'
},
tenantId: 'tenant-123'
});
console.log(`Ingested ${result.chunkCount} chunks in ${result.durationMs}ms`);kb.query(queryText, options)
Search the knowledge base.
interface QueryOptions {
limit?: number; // Max results (default: 10)
minScore?: number; // Min similarity score (default: 0.5)
filter?: Record<string, unknown>; // Metadata filter
tenantId?: string; // Tenant isolation
includeContent?: boolean; // Include chunk content (default: true)
}
interface QueryResult {
id: string; // Chunk ID
documentId: string; // Parent document ID
content: string; // Chunk content
score: number; // Similarity score (0-1)
title?: string; // Document title
source?: string; // Document source
metadata?: Record<string, unknown>;
}Example:
const results = await kb.query('compensation policy for remote workers', {
limit: 5,
minScore: 0.7,
filter: { department: 'HR' },
tenantId: 'tenant-123'
});
results.forEach(r => {
console.log(`[${r.score.toFixed(2)}] ${r.title}`);
console.log(r.content.substring(0, 200) + '...');
});kb.deleteDocument(documentId)
Remove a document and its chunks.
await kb.deleteDocument('policy-2024');kb.getDocument(documentId)
Retrieve a document by ID.
const doc = await kb.getDocument('policy-2024');
if (doc) {
console.log(doc.title, doc.source);
}kb.listDocuments(options)
List all documents.
const docs = await kb.listDocuments({
tenantId: 'tenant-123',
limit: 20,
offset: 0
});kb.embed(text)
Generate an embedding vector for text.
const embedding = await kb.embed('sample text');
console.log(embedding.length); // 1536 for text-embedding-3-smallChunking
Documents are automatically split into chunks for optimal retrieval:
// Default chunking
const kb = createKnowledgeBase({
chunkSize: 1000, // ~1000 characters per chunk
chunkOverlap: 200 // 200 character overlap between chunks
});
// Custom chunking is automatic:
// - Preserves paragraph boundaries
// - Preserves sentence boundaries
// - Maintains overlap for contextVector Store Adapters
In-Memory (Default)
Good for development and small datasets:
const kb = createKnowledgeBase({
openaiApiKey: process.env.OPENAI_API_KEY
});
// Uses built-in in-memory storeCustom Adapter
Implement VectorStoreAdapter for production:
interface VectorStoreAdapter {
upsert(chunks: Chunk[]): Promise<void>;
search(embedding: number[], options: QueryOptions): Promise<QueryResult[]>;
deleteByDocument(documentId: string): Promise<void>;
deleteByFilter(filter: Record<string, unknown>): Promise<void>;
}
// Example: Pinecone adapter
const pineconeStore: VectorStoreAdapter = {
async upsert(chunks) {
// Store chunks in Pinecone
},
async search(embedding, options) {
// Query Pinecone
},
// ...
};
const kb = createKnowledgeBase({
openaiApiKey: process.env.OPENAI_API_KEY,
vectorStore: pineconeStore
});Multi-Tenant Isolation
Use tenantId for data isolation:
// Ingest for specific tenant
await kb.ingest({
id: 'doc-1',
content: '...',
tenantId: 'tenant-A'
});
// Query only returns tenant's data
const results = await kb.query('question', {
tenantId: 'tenant-A'
});Embedding Models
| Model | Dimensions | Best For |
|---|---|---|
text-embedding-3-small | 1536 | General use, cost-effective |
text-embedding-3-large | 3072 | Higher accuracy |
text-embedding-ada-002 | 1536 | Legacy compatibility |
Error Handling
try {
const results = await kb.query('question');
} catch (error) {
if (error.message === 'No embedding API key configured') {
console.error('OpenAI API key required for embeddings');
}
}