Knowledge Base
Ingest documents, search with hybrid queries, and manage knowledge programmatically
The Knowledge Base module gives your agents grounded, accurate answers from your actual documentation. Ingest PDFs, web pages, and text entries, then search with hybrid vector+text queries. Chanl handles chunking, embedding, and retrieval so you do not have to build a RAG pipeline from scratch.
Installation
npm install @chanl/sdkSetup
import { Chanl } from '@chanl/sdk';
const chanl = new Chanl({
apiKey: process.env.CHANL_API_KEY,
});Create an entry
There are three source types. Text entries process synchronously (immediate). URL and file entries process asynchronously (return a task ID you can poll).
Text entry (sync)
const entry = await chanl.knowledge.create({
title: 'Return Policy',
source: 'text',
content: `
Our return policy allows returns within 30 days of purchase.
Items must be unused and in original packaging.
Refunds are processed within 5-7 business days.
`,
metadata: { category: 'policies', tags: ['returns', 'refunds'] },
});
console.log('Created:', entry.id);
console.log('Status:', entry.processingStatus); // "completed"URL entry (async)
const result = await chanl.knowledge.create({
title: 'Help Center Article',
source: 'url',
url: 'https://help.example.com/getting-started',
chunkingOptions: { chunkSizeTokens: 512, overlapTokens: 50 },
});
console.log('Document ID:', result.id);
console.log('Task ID:', result.taskId); // Poll this for statusFile upload (async)
import fs from 'fs';
const result = await chanl.knowledge.create({
title: 'Product Manual v2',
source: 'file',
file: fs.readFileSync('./product-manual.pdf'),
filename: 'product-manual.pdf',
});
// Poll for completion
let status = await chanl.knowledge.getTask(result.taskId);
while (status.status === 'processing') {
await new Promise((r) => setTimeout(r, 2000));
status = await chanl.knowledge.getTask(result.taskId);
}
console.log('Completed:', status.documentId);Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
title | string | Yes | Entry title |
source | string | Yes | text, url, or file |
content | string | For text source | Document content |
url | string | For url source | Web page URL |
file | Buffer | For file source | File contents |
filename | string | For file source | File name |
metadata | object | No | Custom metadata |
tags | string[] | No | Tags for filtering |
folderId | string | No | Folder to place the entry in |
chunkingOptions | object | No | Custom chunking settings |
Returns: Promise<KnowledgeEntry> (text) or Promise<{ id: string; taskId: string }> (url/file)
Search
Query your knowledge base with hybrid (vector + text), pure vector, or pure text search.
Hybrid search (recommended)
Combines semantic understanding with keyword matching. Best general-purpose option.
const results = await chanl.knowledge.search({
query: 'What is your return policy for electronics?',
mode: 'hybrid',
limit: 5,
minScore: 0.7,
});
results.forEach((result) => {
console.log(`[${result.score.toFixed(2)}] ${result.title}`);
console.log(` ${result.content.substring(0, 100)}...`);
});Vector search
Pure semantic search for conceptual queries where exact keywords may not match.
const results = await chanl.knowledge.search({
query: 'Can I get my money back?',
mode: 'vector',
limit: 3,
});Text search
Exact keyword matching for specific identifiers or technical terms.
const results = await chanl.knowledge.search({
query: 'SKU-12345 warranty',
mode: 'text',
limit: 5,
});Search with filters
const results = await chanl.knowledge.search({
query: 'shipping options',
mode: 'hybrid',
limit: 10,
filters: {
tags: ['shipping'],
source: 'faq',
folderId: 'folder_shipping',
},
});Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
query | string | Yes | Search query |
mode | string | No | hybrid (default), vector, or text |
limit | number | No | Max results to return |
minScore | number | No | Minimum relevance score (0-1) |
filters | object | No | Filter by tags, source, or folder |
Returns: Promise<KnowledgeSearchResult[]>
List and filter entries
// List all entries
const { entries, pagination } = await chanl.knowledge.list();
// With filters
const { entries } = await chanl.knowledge.list({
source: 'document',
tags: ['policy'],
folderId: 'folder_policies',
page: 1,
limit: 20,
});
// Search by title
const { entries } = await chanl.knowledge.list({
search: 'return policy',
});
entries.forEach((entry) => {
console.log(`${entry.title} (${entry.source})`);
console.log(` Tags: ${entry.tags?.join(', ')}`);
});Get, update, and delete
// Get entry details
const entry = await chanl.knowledge.get('kb_abc123');
console.log('Title:', entry.title);
console.log('Chunks:', entry.chunkCount);
// Update entry metadata
await chanl.knowledge.update('kb_abc123', {
title: 'Updated Return Policy',
tags: ['policy', 'returns', '2026'],
metadata: { version: '2.0', lastReviewed: new Date().toISOString() },
});
// Delete entry
await chanl.knowledge.delete('kb_abc123');
// Delete with associated files
await chanl.knowledge.delete('kb_abc123', { deleteFiles: true });Folders
Organize entries into folders and subfolders.
// Create folder
const folder = await chanl.knowledge.createFolder({
name: 'Policies',
description: 'Company policies and procedures',
parentId: null, // root level
});
// Create nested folder
const subfolder = await chanl.knowledge.createFolder({
name: 'Return Policies',
parentId: folder.id,
});
// List folders
const { folders } = await chanl.knowledge.listFolders();
folders.forEach((f) => {
console.log(`${f.name} (${f.entryCount} entries)`);
});
// Move entry to folder
await chanl.knowledge.move('kb_abc123', { folderId: folder.id });
// Delete folder (moves contents to root)
await chanl.knowledge.deleteFolder('folder_abc123', {
moveContentsToRoot: true,
});URL sync
Keep URL-sourced entries up to date automatically.
// Enable auto-sync
await chanl.knowledge.updateSyncConfig('kb_abc123', {
enabled: true,
frequency: 'daily', // 'manual' | 'daily' | 'weekly' | 'monthly'
});
// Trigger manual sync
const task = await chanl.knowledge.sync('kb_abc123');
console.log('Sync started:', task.taskId);
// Check sync config
const config = await chanl.knowledge.getSyncConfig('kb_abc123');
console.log('Next Sync:', config.nextSyncAt);Processing tasks
Monitor the status of async operations (URL ingestion, file uploads, syncs).
// Get task status
const task = await chanl.knowledge.getTask('task_abc123');
console.log('Status:', task.status); // pending, processing, completed, failed
console.log('Progress:', task.progress);
if (task.status === 'failed') {
console.log('Error:', task.error);
}
// List active tasks
const { tasks } = await chanl.knowledge.listTasks({
status: 'processing',
});Statistics and configuration
// Get usage stats
const stats = await chanl.knowledge.getStats();
console.log('Total Entries:', stats.totalEntries);
console.log('Total Chunks:', stats.totalChunks);
console.log('Storage Used:', stats.storageBytes);
// Get config limits
const config = await chanl.knowledge.getConfig();
console.log('Max Documents:', config.maxDocuments);
console.log('Max File Size:', config.maxFileSize);
// Update config
await chanl.knowledge.updateConfig({
searchDefaults: { mode: 'hybrid', limit: 10, minScore: 0.6 },
chunkingConfig: { chunkSize: 1200, chunkOverlap: 250 },
});Batch operations
Bulk upload
import fs from 'fs';
import path from 'path';
const files = [
{ path: './docs/manual.pdf', title: 'Product Manual' },
{ path: './docs/faq.md', title: 'FAQ' },
{ path: './docs/policy.txt', title: 'Policy' },
];
const tasks = await Promise.all(
files.map((file) =>
chanl.knowledge.uploadFile({
file: fs.readFileSync(file.path),
filename: path.basename(file.path),
title: file.title,
tags: ['batch-upload'],
})
)
);
console.log('Queued:', tasks.length, 'documents');Bulk delete
const { entries } = await chanl.knowledge.list({
tags: ['old-version'],
});
await Promise.all(
entries.map((entry) => chanl.knowledge.delete(entry.id))
);Full example
Build a customer support knowledge retrieval helper.
import { Chanl } from '@chanl/sdk';
const chanl = new Chanl({
apiKey: process.env.CHANL_API_KEY,
});
class SupportKB {
constructor(private client: Chanl) {}
async findAnswer(question: string): Promise<string | null> {
const results = await this.client.knowledge.search({
query: question,
mode: 'hybrid',
limit: 3,
minScore: 0.75,
});
return results.length > 0 ? results[0].content : null;
}
async addFAQ(question: string, answer: string) {
return this.client.knowledge.create({
title: question,
source: 'text',
content: answer,
tags: ['faq', 'customer-support'],
});
}
async syncHelpCenter(urls: string[]) {
return Promise.all(
urls.map((url) =>
this.client.knowledge.upload({
type: 'url',
url,
source: 'webpage',
tags: ['help-center'],
syncConfig: { enabled: true, frequency: 'daily' },
})
)
);
}
async getHealth() {
const stats = await this.client.knowledge.getStats();
const config = await this.client.knowledge.getConfig();
return {
documentsUsed: stats.totalEntries,
documentsLimit: config.maxDocuments,
utilizationPercent: (stats.totalEntries / config.maxDocuments) * 100,
storageUsed: stats.storageBytes,
chunksIndexed: stats.totalChunks,
};
}
}
// Usage
const kb = new SupportKB(chanl);
const answer = await kb.findAnswer('How do I track my order?');
if (answer) {
console.log('Found answer:', answer);
} else {
console.log('No relevant answer found');
}Types
import type {
KnowledgeEntry,
KnowledgeSource,
KnowledgeFolder,
KnowledgeTask,
KnowledgeSearchResult,
KnowledgeStats,
KnowledgeConfig,
CreateKnowledgeRequest,
UpdateKnowledgeRequest,
SearchKnowledgeRequest,
UploadKnowledgeRequest,
SyncConfig,
} from '@chanl/sdk';