Budowanie produkcyjnych systemów RAG wymaga uwzględnienia wydajności, skalowalności, monitoringu i bezpieczeństwa. To jak projektowanie całego ekosystemu - każdy element musi współgrać z innymi!
1┌─────────────────────────────────────────────────────────────────────┐
2│ Production RAG Architecture │
3├─────────────────────────────────────────────────────────────────────┤
4│ │
5│ ┌──────────┐ ┌──────────────┐ ┌──────────────┐ │
6│ │ Client │───▶│ Load Balancer│───▶│ API Gateway │ │
7│ └──────────┘ └──────────────┘ └──────────────┘ │
8│ │ │
9│ ▼ │
10│ ┌────────────────────────────────────────────────────────────┐ │
11│ │ RAG Service Layer │ │
12│ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │
13│ │ │ Retrieval │ │ Augment │ │ Generation │ │ │
14│ │ │ Service │ │ Service │ │ Service │ │ │
15│ │ └─────┬──────┘ └─────┬──────┘ └─────┬──────┘ │ │
16│ └────────┼───────────────┼───────────────┼──────────────────┘ │
17│ │ │ │ │
18│ ┌────────▼───────┐ ┌────▼────┐ ┌──────▼──────┐ │
19│ │ Vector Database│ │ Cache │ │ LLM APIs │ │
20│ │ (Pinecone/ │ │ (Redis) │ │ (OpenAI/ │ │
21│ │ Qdrant) │ │ │ │ Anthropic) │ │
22│ └────────────────┘ └─────────┘ └─────────────┘ │
23│ │
24│ ┌───────────────────────────────────────────────────────────────┐ │
25│ │ Observability Layer │ │
26│ │ Prometheus │ Grafana │ Jaeger │ LangSmith │ Weights & Biases │ │
27│ └───────────────────────────────────────────────────────────────┘ │
28└─────────────────────────────────────────────────────────────────────┘1from fastapi import FastAPI, HTTPException, BackgroundTasks
2from pydantic import BaseModel
3from typing import Optional
4import asyncio
5from contextlib import asynccontextmanager
6import uvicorn
7
8# Modele
9class QueryRequest(BaseModel):
10 question: str
11 top_k: int = 5
12 filters: Optional[dict] = None
13
14class QueryResponse(BaseModel):
15 answer: str
16 sources: list[dict]
17 latency_ms: float
18
19class RAGService:
20 """Produkcyjny serwis RAG."""
21
22 def __init__(self):
23 self.vector_store = None
24 self.llm = None
25 self.cache = None
26
27 async def initialize(self):
28 """Inicjalizacja połączeń."""
29 # Vector store
30 from qdrant_client import AsyncQdrantClient
31 self.vector_store = AsyncQdrantClient(host="qdrant", port=6333)
32
33 # Cache
34 import redis.asyncio as redis
35 self.cache = redis.Redis(host="redis", port=6379)
36
37 # LLM
38 from openai import AsyncOpenAI
39 self.llm = AsyncOpenAI()
40
41 async def query(self, request: QueryRequest) -> QueryResponse:
42 """Przetwarza zapytanie RAG."""
43 import time
44 start = time.time()
45
46 # 1. Sprawdź cache
47 cache_key = f"rag:{hash(request.question)}"
48 cached = await self.cache.get(cache_key)
49 if cached:
50 return QueryResponse.model_validate_json(cached)
51
52 # 2. Embedding
53 embedding = await self._get_embedding(request.question)
54
55 # 3. Retrieval
56 docs = await self._retrieve(embedding, request.top_k, request.filters)
57
58 # 4. Generation
59 answer = await self._generate(request.question, docs)
60
61 # 5. Response
62 response = QueryResponse(
63 answer=answer,
64 sources=[{"text": d.text, "score": d.score} for d in docs],
65 latency_ms=(time.time() - start) * 1000
66 )
67
68 # 6. Cache
69 await self.cache.setex(cache_key, 3600, response.model_dump_json())
70
71 return response
72
73 async def _get_embedding(self, text: str) -> list[float]:
74 """Generuje embedding."""
75 response = await self.llm.embeddings.create(
76 model="text-embedding-3-small",
77 input=text
78 )
79 return response.data[0].embedding
80
81 async def _retrieve(self, embedding, top_k, filters):
82 """Pobiera dokumenty."""
83 return await self.vector_store.search(
84 collection_name="documents",
85 query_vector=embedding,
86 limit=top_k,
87 query_filter=filters
88 )
89
90 async def _generate(self, question: str, docs) -> str:
91 """Generuje odpowiedź."""
92 context = "\n".join([d.payload["text"] for d in docs])
93
94 response = await self.llm.chat.completions.create(
95 model="gpt-4o-mini",
96 messages=[
97 {"role": "system", "content": f"Kontekst:\n{context}"},
98 {"role": "user", "content": question}
99 ]
100 )
101 return response.choices[0].message.content
102
103# FastAPI app
104rag_service = RAGService()
105
106@asynccontextmanager
107async def lifespan(app: FastAPI):
108 await rag_service.initialize()
109 yield
110
111app = FastAPI(title="RAG API", lifespan=lifespan)
112
113@app.post("/query", response_model=QueryResponse)
114async def query(request: QueryRequest):
115 try:
116 return await rag_service.query(request)
117 except Exception as e:
118 raise HTTPException(status_code=500, detail=str(e))1from prometheus_client import Counter, Histogram, Gauge
2import logging
3import structlog
4
5# Metryki Prometheus
6QUERY_COUNT = Counter(
7 "rag_queries_total",
8 "Total number of RAG queries",
9 ["status"]
10)
11
12QUERY_LATENCY = Histogram(
13 "rag_query_latency_seconds",
14 "Query latency in seconds",
15 buckets=[0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0]
16)
17
18RETRIEVAL_SCORE = Gauge(
19 "rag_retrieval_score",
20 "Average retrieval score"
21)
22
23# Structured logging
24structlog.configure(
25 processors=[
26 structlog.stdlib.add_log_level,
27 structlog.processors.TimeStamper(fmt="iso"),
28 structlog.processors.JSONRenderer()
29 ]
30)
31logger = structlog.get_logger()
32
33class MonitoredRAGService:
34 """RAG z monitoringiem."""
35
36 async def query(self, request: QueryRequest) -> QueryResponse:
37 with QUERY_LATENCY.time():
38 try:
39 response = await self._process_query(request)
40 QUERY_COUNT.labels(status="success").inc()
41
42 # Log
43 logger.info(
44 "query_completed",
45 question=request.question[:50],
46 latency_ms=response.latency_ms,
47 num_sources=len(response.sources)
48 )
49
50 return response
51 except Exception as e:
52 QUERY_COUNT.labels(status="error").inc()
53 logger.error("query_failed", error=str(e))
54 raise1from langsmith import Client, traceable
2from langsmith.wrappers import wrap_openai
3
4# Inicjalizacja
5client = Client()
6
7# Wrap OpenAI client
8from openai import OpenAI
9openai_client = wrap_openai(OpenAI())
10
11@traceable(name="RAG Query")
12async def traced_query(question: str) -> str:
13 """Zapytanie RAG z tracingiem."""
14
15 # Embedding
16 with client.trace("embedding") as span:
17 embedding = get_embedding(question)
18 span.metadata = {"model": "text-embedding-3-small"}
19
20 # Retrieval
21 with client.trace("retrieval") as span:
22 docs = retrieve(embedding)
23 span.metadata = {"num_docs": len(docs)}
24
25 # Generation
26 with client.trace("generation") as span:
27 response = generate(question, docs)
28 span.metadata = {"model": "gpt-4o-mini"}
29
30 return response
31
32# Ewaluacja w LangSmith
33from langsmith.evaluation import evaluate
34
35def evaluate_rag():
36 """Ewaluacja systemu RAG."""
37
38 # Dataset z pytaniami i oczekiwanymi odpowiedziami
39 dataset = client.read_dataset("rag-eval-dataset")
40
41 results = evaluate(
42 traced_query,
43 data=dataset,
44 evaluators=[
45 "qa_helpfulness",
46 "context_precision",
47 "faithfulness"
48 ]
49 )
50
51 return results1from fastapi import Depends, HTTPException
2from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
3import time
4
5# Rate limiter
6class RateLimiter:
7 """Prosty rate limiter."""
8
9 def __init__(self, requests_per_minute: int = 60):
10 self.rpm = requests_per_minute
11 self.requests: dict[str, list[float]] = {}
12
13 async def check(self, user_id: str) -> bool:
14 now = time.time()
15 minute_ago = now - 60
16
17 if user_id not in self.requests:
18 self.requests[user_id] = []
19
20 # Usuń stare requesty
21 self.requests[user_id] = [
22 t for t in self.requests[user_id] if t > minute_ago
23 ]
24
25 if len(self.requests[user_id]) >= self.rpm:
26 return False
27
28 self.requests[user_id].append(now)
29 return True
30
31rate_limiter = RateLimiter()
32security = HTTPBearer()
33
34async def verify_token(credentials: HTTPAuthorizationCredentials = Depends(security)):
35 """Weryfikacja tokenu."""
36 # W produkcji: JWT verification
37 if credentials.credentials == "invalid":
38 raise HTTPException(status_code=401, detail="Invalid token")
39 return credentials.credentials
40
41async def rate_limit(user_id: str = Depends(verify_token)):
42 """Rate limiting middleware."""
43 if not await rate_limiter.check(user_id):
44 raise HTTPException(status_code=429, detail="Too many requests")
45 return user_id
46
47@app.post("/query")
48async def query(request: QueryRequest, user: str = Depends(rate_limit)):
49 return await rag_service.query(request)1# Dockerfile
2FROM python:3.11-slim
3
4WORKDIR /app
5
6# Instalacja zależności
7COPY requirements.txt .
8RUN pip install --no-cache-dir -r requirements.txt
9
10# Kopiowanie kodu
11COPY . .
12
13# Uruchomienie
14CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]1# docker-compose.yml
2version: '3.8'
3
4services:
5 rag-api:
6 build: .
7 ports:
8 - "8000:8000"
9 environment:
10 - OPENAI_API_KEY=${OPENAI_API_KEY}
11 - QDRANT_HOST=qdrant
12 - REDIS_HOST=redis
13 depends_on:
14 - qdrant
15 - redis
16
17 qdrant:
18 image: qdrant/qdrant:latest
19 ports:
20 - "6333:6333"
21 volumes:
22 - qdrant_data:/qdrant/storage
23
24 redis:
25 image: redis:alpine
26 ports:
27 - "6379:6379"
28
29 prometheus:
30 image: prom/prometheus
31 ports:
32 - "9090:9090"
33 volumes:
34 - ./prometheus.yml:/etc/prometheus/prometheus.yml
35
36 grafana:
37 image: grafana/grafana
38 ports:
39 - "3000:3000"
40
41volumes:
42 qdrant_data:1"""
2Production RAG Best Practices:
3
41. RETRIEVAL
5 - Używaj hybrid search (vector + keyword)
6 - Implementuj reranking
7 - Filtruj po metadanych
8
92. CHUNKING
10 - Eksperymentuj z rozmiarem chunks
11 - Używaj overlap
12 - Rozważ semantic chunking
13
143. CACHING
15 - Cache embeddings
16 - Cache częstych zapytań
17 - Invalidacja przy aktualizacji dokumentów
18
194. MONITORING
20 - Śledź latency na każdym etapie
21 - Monitoruj jakość retrieval
22 - Alerting na anomalie
23
245. SECURITY
25 - Rate limiting
26 - Input sanitization
27 - PII detection i filtering
28
296. SCALABILITY
30 - Horizontal scaling API
31 - Sharding vector database
32 - Async processing
33"""Gratulacje! Poznałeś zaawansowane systemy RAG i Multi-Agent. W następnej lekcji poznasz LangGraph - framework do budowania złożonych przepływów agentów!