/** * Example 20: Retrieval-Augmented Generation (RAG) * * This example demonstrates a complete RAG (Retrieval-Augmented Generation) system. * It shows how to: * - Upload and index documents for semantic search * - Implement intelligent document retrieval with citations * - Use ThinkSignal for providing context to the LLM * - Handle search failures with graceful fallbacks * - Build a knowledge-based question answering system * * Key concepts: * - Document upload and indexing workflow * - Semantic search with Botpress file search * - Citation tracking and source attribution * - ThinkSignal for providing retrieved context * - RAG pattern implementation with LLMz */ import { Client } from '@botpress/client' import { z } from '@bpinternal/zui' import chalk from 'chalk' import { execute, ThinkSignal, Tool } from 'llmz' import { CLIChat } from '../utils/cli-chat' import { loading } from '../utils/spinner' import { RAG_TAG, uploadToRAG, waitUntilIndexed } from './rag' // Initialize Botpress client for LLM and file operations const client = new Client({ botId: process.env.BOTPRESS_BOT_ID!, token: process.env.BOTPRESS_TOKEN!, }) // Upload knowledge base documents for RAG // This uploads HR, IT, and Sales documentation to the search index await uploadToRAG(client, ['hr.md', 'it.md', 'sales.md']) console.log(chalk.green('✓') + ' Documents uploaded') // Wait for documents to be processed and indexed // Indexing is required for semantic search functionality await waitUntilIndexed(client, 600) console.log(chalk.green('✓') + ' Documents indexed for RAG') const chat = new CLIChat() // RAG search tool for retrieving relevant information // This implements the "Retrieval" part of Retrieval-Augmented Generation const rag = new Tool({ name: 'search', description: 'Searches in the knowledge base for relevant information.', input: z.string().describe('The query to search in the knowledge base.'), async handler(query) { // Show search progress to user loading(true, '🔍 Searching ...') // Perform semantic search across uploaded documents const { passages } = await client.searchFiles({ query, // User's search query tags: { purpose: RAG_TAG }, // Filter to only RAG documents limit: 20, // Maximum number of results contextDepth: 3, // Include surrounding context consolidate: true, // Merge similar passages }) loading(false) // Handle case where no relevant documents are found if (!passages.length) { throw new ThinkSignal( 'No results were found', 'No results were found in the knowledge bases. You can try rephrasing your question or asking something else. Do NOT answer the question as no results were found.' ) } // Build formatted response with citations const message: string[] = ['Here are the search results from the knowledge base:'] const { tag: example } = chat.citations.registerSource({}) // Process each retrieved passage with proper citation tracking for (const p of passages) { const { tag } = chat.citations.registerSource({ file: p.file.key }) message.push(`<${tag} file="${p.file.key}">`) message.push(`**${p.file.tags.title}**`) message.push(p.content) message.push(``) } // Use ThinkSignal to provide retrieved context to the LLM // This is the "Augmented Generation" part - the LLM will use this context throw new ThinkSignal( `We got the search results. When answering the question, you MUST add inline the citations used (eg: "Yes, the price is $10${example} ...")`, message.join('\n').trim() ) }, }) // Main conversation loop with RAG-enhanced responses while (await chat.iterate()) { await execute({ instructions: 'You are a helpful assistant that can answer questions based on the provided knowledge base. Use the search tool to find relevant information.', // Provide the RAG search tool for knowledge retrieval tools: [rag], client, chat, }) }