Přeskočit na obsah
_CORE
AI & Agentic Systems Core Informační Systémy Cloud & Platform Engineering Data Platforma & Integrace Security & Compliance QA, Testing & Observability IoT, Automatizace & Robotika Mobile & Digital Banky & Finance Pojišťovnictví Veřejná správa Obrana & Bezpečnost Zdravotnictví Energetika & Utility Telco & Média Průmysl & Výroba Logistika & E-commerce Retail & Loyalty
Reference Technologie Blog Knowledge Base O nás Spolupráce Kariéra
Pojďme to probrat

RAG architektura od nuly

01. 01. 2024 4 min čtení intermediate

RAG (Retrieval-Augmented Generation) architektura revolucionizuje způsob, jak velké jazykové modely pracují s externími daty. Tento průvodce vás provede od základních konceptů až po praktickou implementaci vlastního RAG systému.

Základy RAG architektury

RAG (Retrieval-Augmented Generation) představuje revoluční přístup k práci s velkými jazykovými modely, který kombinuje sílu generování textu s přesností informačního vyhledávání. Místo spoléhání pouze na parametrickou znalost modelu RAG dynamicky obohacuje kontext o relevantní informace z externí databáze.

Architektura RAG řeší zásadní problém současných LLM - omezenou a často zastaralou znalostní bázi. Zatímco standardní model může poskytnout pouze obecné odpovědi založené na trénovacích datech, RAG dokáže pracovat s aktuálními a specifickými informacemi z firemních dokumentů, databází nebo webového obsahu.

Komponenty RAG systému

Vector Database

Srdcem každého RAG systému je vector database, která ukládá dokumenty ve formě vektorových reprezentací (embeddings). Tyto vektory zachycují sémantický význam textu v multidimenzionálním prostoru, což umožňuje rychlé vyhledávání podobného obsahu.

import chromadb
from sentence_transformers import SentenceTransformer

# Inicializace vector databáze
client = chromadb.Client()
collection = client.create_collection("documents")

# Model pro vytváření embeddings
encoder = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')

def add_document(text, doc_id):
    embedding = encoder.encode([text])
    collection.add(
        embeddings=embedding.tolist(),
        documents=[text],
        ids=[doc_id]
    )

Embedding Model

Embedding model transformuje textové dokumenty i uživatelské dotazy do vektorové podoby. Pro český obsah doporučuji modely jako paraphrase-multilingual-MiniLM-L12-v2 nebo sentence-transformers/LaBSE, které dobře zvládají vícejazyčný obsah.

Implementace základního RAG workflow

Ingestion pipeline

První krok spočívá v načtení a zpracování dokumentů. Důležité je rozdělit dlouhé texty na menší chunky, které zachovají sémantickou souvislost.

from langchain.text_splitter import RecursiveCharacterTextSplitter

def process_documents(documents):
    splitter = RecursiveCharacterTextSplitter(
        chunk_size=500,
        chunk_overlap=50,
        separators=["\n\n", "\n", " ", ""]
    )

    chunks = []
    for doc in documents:
        text_chunks = splitter.split_text(doc.content)
        for i, chunk in enumerate(text_chunks):
            chunks.append({
                'text': chunk,
                'doc_id': f"{doc.id}_{i}",
                'metadata': doc.metadata
            })

    return chunks

def ingest_documents(chunks):
    for chunk in chunks:
        add_document(chunk['text'], chunk['doc_id'])

Retrieval mechanismus

Retrieval fáze vyhledává nejrelevantnější dokumenty na základě sémantické podobnosti s uživatelským dotazem. Klíčové je správné nastavení parametrů jako počet výsledků a threshold podobnosti.

def retrieve_documents(query, k=5):
    # Vytvoření embedding pro dotaz
    query_embedding = encoder.encode([query])

    # Vyhledání podobných dokumentů
    results = collection.query(
        query_embeddings=query_embedding.tolist(),
        n_results=k
    )

    return [
        {'text': doc, 'score': score}
        for doc, score in zip(results['documents'][0], results['distances'][0])
        if score < 0.8  # threshold pro relevanci
    ]

Generation s kontextem

Finální krok kombinuje vyhledané dokumenty s původním dotazem do promptu pro LLM. Správně strukturovaný prompt je klíčový pro kvalitu odpovědi.

import openai

def generate_answer(query, retrieved_docs):
    context = "\n\n".join([doc['text'] for doc in retrieved_docs])

    prompt = f"""
Odpověz na následující otázku na základě poskytnutého kontextu.
Pokud odpověď v kontextu nenajdeš, řekni to přímo.

Kontext:
{context}

Otázka: {query}

Odpověď:
"""

    response = openai.chat.completions.create(
        model="gpt-4",
        messages=[{"role": "user", "content": prompt}],
        temperature=0.1
    )

    return response.choices[0].message.content

Pokročilé techniky a optimalizace

Kombinace dense (vector) a sparse (keyword) vyhledávání často přináší lepší výsledky než každá metoda samostatně. Sparse vyhledávání excelize při přesných termínech, dense při sémantické podobnosti.

from rank_bm25 import BM25Okapi

class HybridRetriever:
    def __init__(self):
        self.bm25 = None
        self.documents = []

    def index_documents(self, docs):
        self.documents = docs
        tokenized_docs = [doc.split() for doc in docs]
        self.bm25 = BM25Okapi(tokenized_docs)

    def hybrid_search(self, query, k=5, alpha=0.7):
        # Vector search
        vector_results = retrieve_documents(query, k)

        # BM25 search
        bm25_scores = self.bm25.get_scores(query.split())

        # Kombinace skórů
        final_scores = {}
        for i, doc in enumerate(self.documents):
            vector_score = next((r['score'] for r in vector_results 
                               if r['text'] == doc), 1.0)
            bm25_score = bm25_scores[i]

            final_scores[doc] = alpha * (1 - vector_score) + (1 - alpha) * bm25_score

        return sorted(final_scores.items(), key=lambda x: x[1], reverse=True)[:k]

Reranking

Po initial retrieval můžete použít specializovaný reranking model pro lepší seřazení výsledků podle relevance k dotazu.

from sentence_transformers import CrossEncoder

reranker = CrossEncoder('cross-encoder/ms-marco-MiniLM-L-6-v2')

def rerank_documents(query, documents, top_k=3):
    pairs = [(query, doc['text']) for doc in documents]
    scores = reranker.predict(pairs)

    # Seřazení podle skóre
    ranked_docs = sorted(
        zip(documents, scores), 
        key=lambda x: x[1], 
        reverse=True
    )

    return [doc for doc, score in ranked_docs[:top_k]]

Monitoring a evaluace

RAG systémy vyžadují kontinuální monitoring kvality. Klíčové metriky zahrnují retrieval precision, context relevance a answer faithfulness.

def evaluate_rag_performance(test_queries):
    metrics = {
        'retrieval_precision': 0,
        'answer_relevance': 0,
        'response_time': 0
    }

    for query_data in test_queries:
        start_time = time.time()

        # Retrieval
        retrieved = retrieve_documents(query_data['query'])

        # Generation
        answer = generate_answer(query_data['query'], retrieved)

        end_time = time.time()

        # Výpočet metrik
        metrics['response_time'] += end_time - start_time
        # Další evaluační logika...

    return metrics

Shrnutí

RAG architektura představuje praktický způsob, jak rozšířit možnosti LLM o aktuální a specifické znalosti. Úspěšná implementace vyžaduje pečlivou volbu embedding modelu, správnou segmentaci dokumentů a optimalizaci retrieval mechanismů. S pokročilými technikami jako hybrid search a reranking můžete dosáhnout produkční kvality systému, který spolehlivě odpovídá na dotazy z vaší znalostní báze. Klíčem k úspěchu je iterativní přístup s kontinuálním monitoringem a vylepšováním na základě reálného použití.

ragvector dbembeddings