Tutorial - Construindo uma Aplicação RAG
Este tutorial mostra como construir uma aplicação RAG (Retrieval Augmented Generation) do zero.
Tutorial: Construindo uma Aplicação RAG
Este tutorial mostra como construir uma aplicação RAG (Retrieval Augmented Generation) do zero.
Arquitetura
Pré-requisitos
- Node.js (v16+) e npm/yarn
- MongoDB Atlas com Vector Search habilitado
- Conta OpenAI ou outro provedor de LLM
- Redis (opcional, para chat memory)
Etapa 1: Configuração Inicial
Começamos criando o projeto e instalando as dependências necessárias.
# Criar diretório do projeto
mkdir rag-app
cd rag-app
# Inicializar projeto
npm init -y
# Instalar dependências
npm install express mongodb openai redis dotenv cors
npm install typescript ts-node @types/node @types/express @types/cors --save-dev
# Inicializar TypeScript
npx tsc --initConfigurar arquivo .env:
# API Keys e Conexões
OPENAI_API_KEY=sk-...
MONGODB_URI=mongodb+srv://...
REDIS_URL=redis://...
# Configurações de Aplicação
PORT=3000
VECTOR_DIMENSION=1536Etapa 2: Estrutura do Projeto
Organizar os arquivos do projeto:
mkdir -p src/{config,models,services,controllers,routes,utils}src/
├── config/ # Configurações da aplicação
├── models/ # Interfaces e tipos
├── services/ # Lógica de negócio
├── controllers/ # Handlers de requisição
├── routes/ # Definição de rotas
├── utils/ # Utilitários
└── index.ts # Ponto de entradaEtapa 3: Configuração de Conexões
Criar arquivo de configuração para conexões externas:
// src/config/database.ts
import { MongoClient } from 'mongodb';
import dotenv from 'dotenv';
dotenv.config();
const uri = process.env.MONGODB_URI;
const client = new MongoClient(uri);
export async function connectToDatabase() {
try {
await client.connect();
console.log("Connected to MongoDB");
return client.db("rag_database");
} catch (error) {
console.error("Failed to connect to MongoDB:", error);
process.exit(1);
}
}
// src/config/openai.ts
import { OpenAI } from "openai";
import dotenv from 'dotenv';
dotenv.config();
export const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
// src/config/redis.ts
import { createClient } from 'redis';
import dotenv from 'dotenv';
dotenv.config();
export async function connectToRedis() {
const client = createClient({
url: process.env.REDIS_URL
});
client.on('error', (err) => {
console.error('Redis error:', err);
});
await client.connect();
console.log('Connected to Redis');
return client;
}Etapa 4: Definindo Modelos
Criar os tipos e interfaces necessários:
// src/models/document.ts
export interface Document {
content: string;
metadata: {
source: string;
author?: string;
created?: Date;
title?: string;
category?: string;
[key: string]: any;
};
}
export interface EmbeddedDocument extends Document {
embedding: number[];
}
// src/models/chat.ts
export interface ChatMessage {
role: 'user' | 'assistant' | 'system';
content: string;
timestamp?: Date;
}
// src/models/query.ts
export interface QueryResult {
answer: string;
sources: {
content: string;
metadata: any;
score: number;
}[];
}Etapa 5: Implementando Serviços
Criando os serviços principais:
// src/services/embeddingService.ts
import { openai } from '../config/openai';
import { Document, EmbeddedDocument } from '../models/document';
export async function generateEmbedding(text: string): Promise<number[]> {
const response = await openai.embeddings.create({
model: "text-embedding-ada-002",
input: text,
});
return response.data[0].embedding;
}
export async function embedDocument(doc: Document): Promise<EmbeddedDocument> {
const embedding = await generateEmbedding(doc.content);
return {
...doc,
embedding
};
}
// src/services/vectorStoreService.ts
import { Db, Collection } from 'mongodb';
import { EmbeddedDocument, Document } from '../models/document';
import { generateEmbedding } from './embeddingService';
export class VectorStore {
private collection: Collection;
constructor(db: Db) {
this.collection = db.collection('documents');
}
async initialize() {
// Verificar se o índice existe
const indexes = await this.collection.listIndexes().toArray();
const hasVectorIndex = indexes.some(idx => idx.name === 'vector_index');
if (!hasVectorIndex) {
console.log('Creating vector index...');
await this.collection.createIndex(
{ embedding: "vector" },
{
name: "vector_index",
vectorSize: parseInt(process.env.VECTOR_DIMENSION || "1536"),
vectorSearchOptions: { similarity: "cosine" }
}
);
}
}
async addDocument(doc: Document): Promise<void> {
const embedding = await generateEmbedding(doc.content);
await this.collection.insertOne({
content: doc.content,
metadata: doc.metadata,
embedding,
created_at: new Date()
});
}
async search(query: string, limit = 5): Promise<any[]> {
const embedding = await generateEmbedding(query);
return await this.collection.aggregate([
{
$vectorSearch: {
queryVector: embedding,
path: "embedding",
numCandidates: 100,
limit
}
},
{
$project: {
_id: 0,
content: 1,
metadata: 1,
score: { $meta: "vectorSearchScore" }
}
}
]).toArray();
}
}Este tutorial continua com mais implementações de serviços, controladores, rotas e o servidor principal. O código completo está disponível na documentação expandida.
Testando a Aplicação
# Iniciar o servidor
npm run dev
# Em outro terminal, adicionar um documento
curl -X POST http://localhost:3000/api/documents \
-H "Content-Type: application/json" \
-d '{"content": "MongoDB Atlas is a fully-managed cloud database service for modern applications.", "metadata": {"source": "manual-entry", "category": "database"}}'
# Fazer uma consulta
curl -X POST http://localhost:3000/api/query \
-H "Content-Type: application/json" \
-d '{"question": "What is MongoDB Atlas?", "sessionId": "test-session"}'Próximos Passos
Para expandir a aplicação:
- Adicionar autenticação
- Implementar frontend
- Adicionar monitoramento
- Melhorar manejo de erros
- Adicionar testes
Este tutorial mostrou como construir uma aplicação RAG completa, conectando todos os componentes que discutimos anteriormente.