Section 13.6.2 : RAG et LangChain
🎯 Objectif pédagogique
Implémenter un système RAG (Retrieval-Augmented Generation) qui permet à un LLM de répondre à des questions en se basant sur vos propres documents, en utilisant LangChain pour orchestrer le pipeline.
Le problème : les LLMs ne connaissent pas vos données
Les modèles GPT ont été entraînés sur des données publiques jusqu'à une date de coupure. Ils ne connaissent pas :
- →Vos documents internes
- →Votre base de connaissances
- →Vos dernières données
RAG résout ce problème en injectant du contexte pertinent dans le prompt.
Le pipeline RAG en 4 étapes
# Étape 1 : Charger et découper les documents
class DocumentProcessor
CHUNK_SIZE = 500
CHUNK_OVERLAP = 50
def self.chunk(text)
words = text.split
chunks = []
i = 0
while i < words.length
chunk = words[i, CHUNK_SIZE].join(' ')
chunks << chunk
i += CHUNK_SIZE - CHUNK_OVERLAP
end
chunks
end
end
# Étape 2 : Générer les embeddings
class EmbeddingService
def self.generate(text)
response = OPENAI_CLIENT.embeddings(
parameters: {
model: 'text-embedding-3-small',
input: text
}
)
response.dig('data', 0, 'embedding')
end
end
# Étape 3 : Stocker dans une base vectorielle
class VectorStore
def self.store(chunk, embedding, metadata = {})
Document.create!(
content: chunk,
embedding: embedding,
metadata: metadata
)
end
def self.search(query_embedding, limit: 5)
Document.nearest_neighbors(:embedding, query_embedding, distance: :cosine)
.first(limit)
end
end
# Étape 4 : Générer la réponse
class RagService
def self.answer(question)
# 1. Embed la question
query_embedding = EmbeddingService.generate(question)
# 2. Chercher les documents pertinents
relevant_docs = VectorStore.search(query_embedding, limit: 5)
context = relevant_docs.map(&:content).join("\n\n---\n\n")
# 3. Construire le prompt enrichi
prompt = <<~PROMPT
Contexte (documents internes) :
#{context}
Question : #{question}
Réponds en te basant UNIQUEMENT sur le contexte fourni.
Si le contexte ne contient pas la réponse, dis-le.
PROMPT
# 4. Appeler le LLM
AiService.chat(prompt, system_message: 'Tu es un assistant qui répond en se basant uniquement sur le contexte fourni.')
end
end
LangChain : orchestrer les pipelines IA
LangChain est un framework qui simplifie la construction de pipelines IA complexes. Bien que principalement Python, le concept s'applique en Ruby :
# Exemple LangChain en Python (concept)
from langchain.document_loaders import DirectoryLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Chroma
from langchain.chains import RetrievalQA
from langchain.llms import OpenAI
# 1. Charger les documents
loader = DirectoryLoader('./docs/', glob="**/*.md")
docs = loader.load()
# 2. Découper en chunks
splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50
)
chunks = splitter.split_documents(docs)
# 3. Créer le vector store
embeddings = OpenAIEmbeddings()
vectorstore = Chroma.from_documents(chunks, embeddings)
# 4. Créer la chaîne RAG
qa_chain = RetrievalQA.from_chain_type(
llm=OpenAI(model_name="gpt-4"),
chain_type="stuff",
retriever=vectorstore.as_retriever(search_kwargs={"k": 5})
)
# 5. Poser des questions
answer = qa_chain.run("Quelle est la politique de remboursement ?")
RAG vs Fine-tuning
← Scroll horizontally →
Pourquoi découpe-t-on les documents en chunks avant de les stocker ?
Apprendre l’IA, du premier prompt aux agents