When RAG is configured in an agent, the SDK automatically creates a searchKnowledge tool that the LLM can decide when to use. This is more efficient than always searching, as the LLM only searches when necessary.
const agent = new Agent({ name: 'Support Agent', instructions: 'You are a helpful support agent.', model: openai('gpt-4o'), rag: { vectorStore: 'support-docs', k: 5, threshold: 0.7, // Custom search prompt - guides when to search searchPrompt: `Use searchKnowledge tool when user asks about:- Technical problems- Process questions- Specific informationDon't use for greetings or casual chat.`, toolDescription: 'Search in support documentation for solutions', },});// Agent automatically has 'searchKnowledge' tool// LLM decides when to search (not always - more efficient!)const result = await agent.process({ message: 'How do I reset my password?',});
Available since SDK 1.3.2 (platform July 2026). For small files addFile still works — async ingestion is the recommended path for anything big.
addFile processes the file synchronously — fine for a manual or an FAQ, but a 50k-row catalog would hold the HTTP request open for minutes. ingestFile uploads the file, returns in seconds with a job id, and the platform embeds everything in the background with batched embeddings and checkpoint resume (if a worker restarts mid-job, ingestion continues from where it stopped instead of starting over).
import * as fs from 'fs';import { Knowledge } from '@runflow-ai/sdk';const knowledge = new Knowledge({ vectorStore: 'product-catalog' });// Fire-and-forget: returns as soon as the job is accepted (HTTP 202)const { jobId } = await knowledge.ingestFile( fs.readFileSync('./catalog.csv'), 'catalog.csv', { mimeType: 'text/csv' });// Poll whenever you wantconst job = await knowledge.getIngestionJob(jobId);console.log(job.status, `${job.processedChunks}/${job.totalChunks}`);
CSV files are ingested one document per row — ideal for product catalogs and structured data. Each row becomes a searchable Column: value document. You can control which columns are embedded and which go to metadata:
await knowledge.ingestFile(fs.readFileSync('./catalog.csv'), 'catalog.csv', { mimeType: 'text/csv', csv: { delimiter: ';', // sniffed automatically when omitted contentColumns: ['name', 'brand'], // embedded (default: all columns) metadataColumns: ['sku', 'price'], // copied to each document's metadata },});
Optional cleanup applied server-side before embedding — useful when the source data carries HTML, URLs, or placeholder values that hurt search quality:
getIngestionJob(jobId) (and the onProgress callback) return:
Field
Description
status
queued → processing → completed | failed
progress
Completion ratio 0..1 (0 while the worker is still planning)
processedChunks / totalChunks
Embedded units vs. total planned
documentId
Set when completed — the parent document id
error
Set when failed
waitForCompletion throws if the job fails or the timeout (default 30 min) elapses — on timeout the job keeps running server-side and you can keep polling.