Witaj w przedostatnim etapie Python Safari! Tak jak wielkie ssaki ewoluowały, rozwijając większe mózgi i lepszą pamięć, tak modele językowe ewoluują dzięki technologii RAG (Retrieval-Augmented Generation). To przełomowa technika, która daje AI dostęp do zewnętrznej wiedzy!
Modele językowe mają fundamentalne ograniczenia, podobnie jak zwierzęta przystosowane tylko do jednego środowiska:
RAG to architektura, która łączy wyszukiwanie informacji z generowaniem tekstu:
1┌─────────────────────────────────────────────────────────┐
2│ RAG Pipeline │
3├─────────────────────────────────────────────────────────┤
4│ │
5│ Pytanie użytkownika │
6│ │ │
7│ ▼ │
8│ ┌─────────────────┐ │
9│ │ Embedding │ ← Zamiana na wektor │
10│ └────────┬────────┘ │
11│ │ │
12│ ▼ │
13│ ┌─────────────────┐ ┌─────────────────┐ │
14│ │ Vector Search │────▶│ Vector Database │ │
15│ └────────┬────────┘ └─────────────────┘ │
16│ │ │
17│ ▼ │
18│ ┌─────────────────┐ │
19│ │ Retrieved Docs │ ← Top-K podobnych dokumentów │
20│ └────────┬────────┘ │
21│ │ │
22│ ▼ │
23│ ┌─────────────────┐ │
24│ │ LLM + Context │ ← Generowanie z kontekstem │
25│ └────────┬────────┘ │
26│ │ │
27│ ▼ │
28│ Odpowiedź │
29└─────────────────────────────────────────────────────────┘1from openai import OpenAI
2import numpy as np
3
4client = OpenAI()
5
6# Prosta baza wiedzy
7knowledge_base = [
8 "Python Safari to kurs programowania w Pythonie z 12 modułami.",
9 "RAG łączy wyszukiwanie dokumentów z generowaniem tekstu przez LLM.",
10 "Embeddings to wektorowe reprezentacje tekstu w przestrzeni semantycznej.",
11 "Vector databases przechowują i wyszukują embeddings efektywnie.",
12 "LlamaIndex to framework do budowania aplikacji RAG.",
13 "CrewAI umożliwia tworzenie systemów multi-agentowych."
14]
15
16def get_embedding(text: str) -> list[float]:
17 """Generuje embedding dla tekstu."""
18 response = client.embeddings.create(
19 model="text-embedding-3-small",
20 input=text
21 )
22 return response.data[0].embedding
23
24def cosine_similarity(vec1: list, vec2: list) -> float:
25 """Oblicza podobieństwo kosinusowe dwóch wektorów."""
26 vec1, vec2 = np.array(vec1), np.array(vec2)
27 return np.dot(vec1, vec2) / (np.linalg.norm(vec1) * np.linalg.norm(vec2))
28
29def retrieve_relevant_docs(query: str, docs: list, top_k: int = 3) -> list[str]:
30 """Wyszukuje najbardziej podobne dokumenty."""
31 query_embedding = get_embedding(query)
32
33 similarities = []
34 for doc in docs:
35 doc_embedding = get_embedding(doc)
36 similarity = cosine_similarity(query_embedding, doc_embedding)
37 similarities.append((doc, similarity))
38
39 # Sortuj po podobieństwie
40 similarities.sort(key=lambda x: x[1], reverse=True)
41
42 return [doc for doc, _ in similarities[:top_k]]
43
44def rag_query(question: str) -> str:
45 """Pełny pipeline RAG."""
46 # 1. Retrieve - znajdź relevantne dokumenty
47 relevant_docs = retrieve_relevant_docs(question, knowledge_base)
48
49 # 2. Augment - zbuduj prompt z kontekstem
50 context = "\n".join(relevant_docs)
51
52 prompt = f"""Odpowiedz na pytanie używając TYLKO poniższego kontekstu.
53Jeśli nie możesz odpowiedzieć na podstawie kontekstu, powiedz "Nie wiem".
54
55Kontekst:
56{context}
57
58Pytanie: {question}
59"""
60
61 # 3. Generate - wygeneruj odpowiedź
62 response = client.chat.completions.create(
63 model="gpt-4o-mini",
64 messages=[{"role": "user", "content": prompt}]
65 )
66
67 return response.choices[0].message.content
68
69# Test
70answer = rag_query("Co to jest RAG?")
71print(answer)Długie dokumenty trzeba podzielić na mniejsze fragmenty (chunks):
1from typing import Generator
2
3def simple_chunker(text: str, chunk_size: int = 500, overlap: int = 100) -> Generator[str, None, None]:
4 """Prosta funkcja do dzielenia tekstu na chunki."""
5 start = 0
6 while start < len(text):
7 end = start + chunk_size
8 chunk = text[start:end]
9 yield chunk
10 start = end - overlap # Overlap dla zachowania kontekstu
11
12def sentence_chunker(text: str, sentences_per_chunk: int = 5) -> list[str]:
13 """Dzieli tekst na chunki po zdaniach."""
14 import re
15
16 # Prosta segmentacja zdań
17 sentences = re.split(r'(?<=[.!?])\s+', text)
18
19 chunks = []
20 for i in range(0, len(sentences), sentences_per_chunk):
21 chunk = ' '.join(sentences[i:i + sentences_per_chunk])
22 chunks.append(chunk)
23
24 return chunks
25
26# Przykład użycia
27document = """
28Python to język programowania wysokiego poziomu. Został stworzony przez
29Guido van Rossuma. Jest bardzo popularny w Data Science i Machine Learning.
30RAG to technika łącząca retrieval z generowaniem. Pozwala modelom LLM
31korzystać z zewnętrznych źródeł wiedzy. Jest kluczowa dla enterprise AI.
32"""
33
34chunks = sentence_chunker(document, sentences_per_chunk=2)
35for i, chunk in enumerate(chunks):
36 print(f"Chunk {i}: {chunk[:50]}...")1from dataclasses import dataclass
2
3@dataclass
4class RAGMetrics:
5 """Metryki do ewaluacji systemu RAG."""
6
7 def relevance_score(self, query: str, retrieved_doc: str) -> float:
8 """Czy dokument jest relevantny do pytania?"""
9 # W praktyce używamy LLM do oceny
10 pass
11
12 def faithfulness_score(self, answer: str, context: str) -> float:
13 """Czy odpowiedź opiera się na kontekście?"""
14 # Sprawdza halucynacje
15 pass
16
17 def answer_correctness(self, answer: str, ground_truth: str) -> float:
18 """Czy odpowiedź jest poprawna?"""
19 # Porównanie z ground truth
20 pass
21
22# Przykład prostej ewaluacji
23def evaluate_retrieval(query: str, retrieved: list[str], relevant: list[str]) -> dict:
24 """Oblicza precision i recall dla retrieval."""
25 retrieved_set = set(retrieved)
26 relevant_set = set(relevant)
27
28 true_positives = len(retrieved_set & relevant_set)
29
30 precision = true_positives / len(retrieved_set) if retrieved_set else 0
31 recall = true_positives / len(relevant_set) if relevant_set else 0
32
33 f1 = 2 * precision * recall / (precision + recall) if (precision + recall) > 0 else 0
34
35 return {
36 "precision": precision,
37 "recall": recall,
38 "f1": f1
39 }RAG to fundament nowoczesnych aplikacji AI enterprise. W następnej lekcji poznasz embeddings - serce całego systemu wyszukiwania!