Skip to main content
Glama
muhammedturan65

yargi-mcp-nvidia-semantic

README.md
# yargi-mcp + NVIDIA nv-embed-v1 Semantik Arama Entegrasyonu

Bu repo, [anilweise/yargi-mcp](https://github.com/anilweise/yargi-mcp) üzerine uygulanan **NVIDIA nv-embed-v1 semantik arama** ve **Bedesten rate-limit dayanıklılık** patch'lerini içerir. Tüm değişiklikler referans patches klasöründe diff dosyası olarak da mevcuttur.

## Özet

| Metrik | Orijinal | Patched |
|---|---|---|
| Hedef belge sayısı | 100 | 30 |
| Başarılı fetch | 10 / 100 (10%) | **28 / 30 (93%)** |
| Failed fetches | 90 | 2 |
| Toplam süre | ~30 saniye (rate-limit crash) | ~2 dakika (graceful) |
| Top-1 similarity skoru | 0.4653 | **0.5347** |
| Embedding boyutu | 1024 (nv-embedqa-e5-v5) | **4096** (nv-embed-v1) |

## İçerik

```
yargi-mcp-nvidia-semantic/
├── README.md                          # Bu dosya
├── PATCHES.md                         # Patch detayları (Seçenek 1 + 3)
├── CHANGELOG.md                       # Sürüm notları
├── LICENSE                            # Orijinal lisans (MIT)
├── pyproject.toml                     # Bağımlılıklar
├── .env.example                       # Çevre değişkenleri şablonu
├── Dockerfile
├── mcp_server_main.py                 # Patched: adaptive batch + retry
├── semantic_search/
│   ├── embedder.py                    # Patched: NVIDIA asimetrik model desteği
│   ├── vector_store.py
│   └── processor.py
├── bedesten_mcp_module/
│   └── client.py                      # BedestenRateLimited exception + rate limiter
├── *_mcp_module/                      # 16 Türk hukuki kurumu modülü
│   ├── anayasa_mcp_module/
│   ├── bedesten_mcp_module/
│   ├── danistay_mcp_module/
│   ├── emsal_mcp_module/
│   ├── gib_mcp_module/
│   ├── kik_mcp_module/
│   ├── kvkk_mcp_module/
│   ├── rekabet_mcp_module/
│   ├── sayistay_mcp_module/
│   ├── sigorta_tahkim_mcp_module/
│   └── ...
├── patches/                           # Diff dosyaları
│   ├── 001_embedder_nvidia_asymmetric_support.diff
│   └── 002_mcp_server_adaptive_batch_retry.diff
└── tests/
    ├── test_yargi_mcp_semantic_search.py    # E2E test (NVIDIA + Bedesten)
    ├── test_nvidia_embeddings.py            # NVIDIA API birim testi
    ├── test_nvidia_local_embedder.py        # LocalEmbedder entegrasyon testi
    └── yargi_mcp_semantic_results.json      # Gerçek test çıktısı (28 belge)
```

## Patch'ler

### Patch 1: NVIDIA Asimetrik Model Desteği (`semantic_search/embedder.py`)

NVIDIA `nv-embed-v1` ve `nv-embedqa-e5-v5` modelleri **asimetrik**'tir — query ve passage için farklı `input_type` gerektirir. Orijinal `LocalEmbedder` bunu desteklemiyordu.

**Çözüm:**
- `_is_asymmetric_model()` — model adından asimetrik tespiti
- `encode_query()` → `extra_body={"input_type": "query"}`
- `encode_documents()` → `extra_body={"input_type": "passage"}`
- Yeni env var: `LOCAL_EMBEDDING_INPUT_TYPE` (`auto` | `off`)

### Patch 2: Bedesten Rate-Limit Dayanıklılık (`mcp_server_main.py`)

Bedesten API 10 istek / 30 saniye pencere ile sınırlıdır. Orijinal kod 100 belgeyi sıralı çekmeye çalışıyor, 10. istekten sonra rate-limit'e takılıp kalan 90 belge atlanıyordu.

**Çözüm (Seçenek 1 + 3 Birleşik):**

1. **Adaptive batch** — `all_decisions[:100]` → `all_decisions[:BEDESTEN_SEMANTIC_BATCH_SIZE]` (varsayılan 30)
2. **Retry loop** — her belge için `BedestenRateLimited` yakalanır, `retry_after + 1s` bekleyip max 3 kez retry yapılır
3. **Toleranslı max_wait** — `BEDESTEN_RATE_MAX_WAIT_S` 8s → 60s'e yükseltildi (429 pause'unu atlatır)
4. **Yavaş refill** — `BEDESTEN_RATE_REFILL_S` 3.5s → 4.0s (ekstra güvenlik marjı)

Tüm parametreler env var ile kontrol edilebilir.

## Kurulum

```bash
# Repoyu klonla
git clone https://github.com/muhammedturan65/yargi-mcp-nvidia-semantic.git
cd yargi-mcp-nvidia-semantic

# Python >= 3.11 gerekli
python -m venv .venv
source .venv/bin/activate
pip install -e .
```

## Konfigürasyon

### NVIDIA API Anahtarı

[ NVIDIA build.nvidia.com](https://build.nvidia.com) üzerinden ücretsiz API anahtarı al. Asimetrik model desteği için `nv-embed-v1` önerilir (4096 boyut, daha uzun context).

### Çevre Değişkenleri

```bash
# === NVIDIA Embedding ===
export EMBEDDING_PROVIDER=local
export LOCAL_EMBEDDING_BASE_URL=https://integrate.api.nvidia.com/v1
export LOCAL_EMBEDDING_API_KEY=nvapi-XXXXXXXX
export LOCAL_EMBEDDING_MODEL=nvidia/nv-embed-v1
export LOCAL_EMBEDDING_DIMENSION=4096
export LOCAL_EMBEDDING_INPUT_TYPE=auto
export EMBEDDING_PROMPT_STYLE=raw

# === Bedesten Rate-Limit (Patch 2) ===
export BEDESTEN_RATE_CAPACITY=1
export BEDESTEN_RATE_REFILL_S=4.0
export BEDESTEN_RATE_MAX_WAIT_S=60
export BEDESTEN_SEMANTIC_BATCH_SIZE=30
export BEDESTEN_SEMANTIC_MAX_RETRIES=3
```

## Test

```bash
# E2E test (NVIDIA + Bedesten gerçek API çağrısı)
python tests/test_yargi_mcp_semantic_search.py

# NVIDIA API birim testi
python tests/test_nvidia_embeddings.py

# LocalEmbedder entegrasyon testi
python tests/test_nvidia_local_embedder.py
```

### Beklenen Çıktı

```
[1/4] mcp_server_main yükleniyor...
  SEMANTIC_SEARCH_AVAILABLE = True
  ✓ BedestenApiClient hazır: BedestenApiClient

[2/4] search_bedesten_semantic tool çağrılıyor...

[3/4] Sonuç alındı!
  status: success
  total_documents_processed: 28
  embedding_model: nvidia/nv-embed-v1
  embedding_dimension: 4096
  documents_in_store: 28
  failed_fetches: 2
```

## MCP Client Konfigürasyonu

### Claude Desktop

`~/Library/Application Support/Claude/claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "yargi-mcp": {
      "command": "/path/to/.venv/bin/python",
      "args": ["-m", "yargi_mcp"],
      "env": {
        "EMBEDDING_PROVIDER": "local",
        "LOCAL_EMBEDDING_BASE_URL": "https://integrate.api.nvidia.com/v1",
        "LOCAL_EMBEDDING_API_KEY": "nvapi-...",
        "LOCAL_EMBEDDING_MODEL": "nvidia/nv-embed-v1",
        "LOCAL_EMBEDDING_DIMENSION": "4096",
        "LOCAL_EMBEDDING_INPUT_TYPE": "auto",
        "BEDESTEN_RATE_MAX_WAIT_S": "60",
        "BEDESTEN_SEMANTIC_BATCH_SIZE": "30"
      }
    }
  }
}
```

### 5ire MCP Client

5ire ayarlarında `5ire-settings.png` dosyasına bakın — aynı env var'lar orada da kullanılır.

## Lisans

Orijinal yargi-mcp projesi MIT lisansı altında dağıtılmaktadır. Bu fork da aynı lisansı korur.

## Katkıda Bulunanlar

- **Orijinal yargi-mcp**: [anilweise](https://github.com/anilweise)
- **NVIDIA + rate-limit patch'leri**: [muhammedturan65](https://github.com/muhammedturan65)

## İlgili Belgeler

- `PATCHES.md` — Patch detayları (before/after tabloları, env var referansı)
- `CHANGELOG.md` — Sürüm notları
- `CLAUDE.md` — Claude Code için geliştirici notları (orijinal repo)

---

## Hukuki QA Chatbot (RAG) — v1.4.0 (Query Embedding Cache)

v1.4.0, v1.3.0'da kalan son darboğazı çözer: **her sorguda NVIDIA query embedding ~1 saniye**.
Artık aynı soru tekrar sorulduğunda NVIDIA'ya **hiç çağrı yapılmaz** — iki katmanlı cache
(LRU + ChromaDB persistent) ile 0 ms'den döner.

### Yenilik: İki Katmanlı Query Embedding Cache

```
Kullanıcı sorusu
        │
        ▼
┌────────────────────────────────────────────────┐
│  1. normalize_query(soru)                      │
│     ç→c, ı→i, ğ→g, punct strip, lowercase    │
│     "Mirasçı hangi davayı açar?"               │
│     → "mirasci hangi davayi acar"              │
├────────────────────────────────────────────────┤
│  2. cache_key = sha256(normalized)[:16]         │  ← exact match key
├────────────────────────────────────────────────┤
│  3. LRU lookup (OrderedDict)         ~0 ms     │  ← process içi
│     ├─ HIT → return cached embedding           │
│     └─ MISS ↓                                   │
│  4. ChromaDB persistent lookup     ~5 ms       │  ← process restart'ta kalıcı
│     ├─ HIT → LRU'ya da yaz, return             │
│     └─ MISS ↓                                   │
│  5. NVIDIA encode_query()          ~1000 ms    │  ← gerçek API çağrısı
│     + LRU'ya yaz + ChromaDB'ye yaz             │
└────────────────────────────────────────────────┘
```

**Neden exact-match cache, semantik değil?** Çünkü NVIDIA (ve diğer embedder'lar)
aynı soru için deterministik embedding üretir. Yani "Mirasçı hangi davayı açar?"
her zaman aynı 4096-boyutlu vektöre mapping edilir. Semantik benzerlik (farklı
soru ama aynı anlam) `AnswerCache`'in işi — query cache sadece gereksiz NVIDIA
API çağrısını önler.

### v1.3.0 → v1.4.0 Performans Karşılaştırması

| Senaryo | v1.3.0 | v1.4.0 | İyileşme |
|---|---|---|---|
| İlk sorgu (cold) | ~1 s (NVIDIA) | ~1 s (NVIDIA) + cache yazma | ~ aynı |
| Aynı sorgu tekrar (LRU HIT) | ~1 s (NVIDIA tekrar) | **~5 ms** | **200x** |
| Aynı sorgu restart'tan sonra | ~1 s (NVIDIA) | **~11 ms** | **90x** |
| Answer cache HIT (toplam) | ~1 s + ~50 ms cache | **~25 ms** | **40x** |
| Answer cache HIT (restart) | ~1 s + ~50 ms cache | **~11 ms** | **90x** |

### Test Sonuçları (v1.4.0)

**6/6 smoke test PASS:**
1. Import testleri (4 yeni export)
2. `normalize_query` — Türkçe karakterler + punct + boşluk
3. `QueryEmbeddingCache` init (ChromaDB collection)
4. Store + LRU lookup + persistent lookup
5. Persistence (yeni instance, eski kayıtlar)
6. LegalQARAG retrieve() — gerçek NVIDIA + cache

**Cache HIT Benchmark (gerçek NVIDIA LLM, 17 belge / 70 chunk):**

| Senaryo | Süre | Q-Cache | A-Cache |
|---|---|---|---|
| Cold start (NVIDIA query + NVIDIA LLM) | 67.3 s | MISS | MISS |
| Aynı process (LRU + answer cache HIT) | 25 ms | persistent | HIT |
| Yeni process (persistent + answer cache HIT) | 11 ms | persistent | HIT |
| **Hızlanma (cold vs HIT)** | **~2700x** | | |

Test dosyaları:
- `tests/v14_query_cache_results.json` — 6/6 smoke test
- `tests/v14_cache_hit_results.json` — cache HIT benchmark
- `tests/v14_full_rag_results.json` — full RAG pipeline

### Çevre Değişkenleri (v1.4.0)

```bash
# Query embedding cache (default: açık)
export RAG_QUERY_CACHE=true                          # kapatmak için false
export RAG_QUERY_CACHE_LRU_SIZE=256                  # in-memory LRU max kayıt
export RAG_QUERY_CACHE_COLLECTION=query_embed_cache  # ChromaDB collection adı
```

### Mimari (v1.4.0)

```
                     ┌─────────────────────────────────────┐
                     │  BedestenIndexer (tek seferlik)     │
                     │  v1.2.0 — ChromaDB kalıcı store    │
                     └─────────────────┬───────────────────┘
                                       │ (disk - kalıcı)
                                       ▼
                     ┌─────────────────────────────────────┐
Kullanıcı sorusu ──► │  LegalQARAG.ask()                   │
                     │   1. QueryEmbeddingCache [v1.4.0]   │
                     │      ├─ LRU HIT        → 0 ms       │
                     │      ├─ Persistent HIT → 5 ms       │
                     │      └─ MISS → NVIDIA encode ~1 s   │
                     │   2. ChromaDB.search_with_dedup()   │ ~50ms
                     │   3. AnswerCache.lookup() [v1.3.0]  │ ~4ms
                     │      ├─ HIT → cache'den cevap       │ → RETURN
                     │      └─ MISS → devam                │
                     │   4. LLMClient.chat_async() [v1.3.0]│ ~3-240s
                     │   5. AnswerCache.store() [v1.3.0]   │ ~50ms
                     └─────────────────────────────────────┘
```

### Backward Compatibility

v1.4.0, v1.3.0/v1.2.0/v1.1.0 kodu ile **tam uyumlu**:
- Tüm v1.3.0 API'leri ve env var'ları korundu
- `LegalQARAG()` default hâlâ NVIDIA + query cache + answer cache enabled
- v1.3.0 ChromaDB collection'ları (`yargi_decisions`, `qa_cache`) çalışır
- Query cache ayrı collection (`query_embed_cache`) — mevcut verilere dokunmaz

---

## Hukuki QA Chatbot (RAG) — v1.3.0 (Multi-Provider LLM + Answer Cache)

v1.3.0, v1.2.0'daki en büyük acı noktasını çözer: **NVIDIA LLM 60-240 saniye latency**.
İki katmanlı çözüm ile tekrarlayan/benzer sorular artık ~2 saniyede döner.

### Yenilikler

#### 1. Multi-Provider LLM Backend

Artık NVIDIA'ya bağlı kalmak zorunda değilsiniz. **Groq** ile ~500 tok/s, **OpenAI** ile
ucuz/hızlı GPT-4o-mini, **Ollama** ile tamamen local LLM kullanabilirsiniz.

```bash
# Hızlı LLM için (önerilen):
export LLM_PROVIDER=groq
export GROQ_API_KEY=gq_...

# Veya OpenAI:
export LLM_PROVIDER=openai
export OPENAI_API_KEY=sk-...

# Veya tamamen local:
export LLM_PROVIDER=ollama
ollama pull llama3.1:8b
```

| Provider | Default Model | Hız | Maliyet | Türkçe Kalite |
|---|---|---|---|---|
| nvidia | meta/llama-3.1-70b-instruct | Yavaş (~5 tok/s) | Ücretsiz | İyi |
| **groq** | llama-3.3-70b-versatile | **Hızlı (~500 tok/s)** | Ücretsiz tier | İyi |
| openai | gpt-4o-mini | Orta (~50 tok/s) | Ucuz | Çok iyi |
| ollama | llama3.1:8b | Local hız | Ücretsiz | Orta |

#### 2. Semantik Answer Cache

Aynı veya benzer (cosine ≥ 0.92) soru tekrar sorulduğunda, LLM çağrısı yapılmadan
cache'den yanıt döner. ChromaDB'de ayrı `qa_cache` collection'da saklanır.

```python
from qa_rag import LegalQARAG

rag = LegalQARAG()
await rag.load_corpora()

# İlk sorgu — LLM çağrılır (~60s NVIDIA, ~3s Groq)
r1 = await rag.ask("Mirasçı hangi davayı açar?")
print(f"Süre: {r1.total_time_ms/1000:.1f}s, from_cache: {r1.from_cache}")
# → Süre: 52.5s, from_cache: False

# Aynı soru tekrar — cache HIT, LLM atlanır
r2 = await rag.ask("Mirasçı hangi davayı açar?")
print(f"Süre: {r2.total_time_ms/1000:.1f}s, from_cache: {r2.from_cache}, score: {r2.cache_score:.4f}")
# → Süre: 1.9s, from_cache: True, score: 1.0000
```

Cache kontrolü:
```bash
export RAG_ANSWER_CACHE=true             # default — açık
export RAG_CACHE_THRESHOLD=0.92          # cosine threshold (yüksek = daha sıkı eşleşme)
export RAG_CACHE_COLLECTION=qa_cache     # ChromaDB collection adı
```

### v1.2.0 → v1.3.0 Performans Karşılaştırması

| Senaryo | v1.2.0 | v1.3.0 cache MISS | v1.3.0 cache HIT |
|---|---|---|---|
| İlk sorgu | 60-240s | 52.5s | — |
| Tekrar sorgu | 60-240s (LLM yine çağrılır) | — | **1.86s** |
| LLM tokens | ~3800 | ~3800 | 0 (atlandı) |
| **Hızlanma** | — | — | **28.3x** |

### Mimari (v1.3.0)

```
                     ┌─────────────────────────────────────┐
                     │  BedestenIndexer (tek seferlik)     │
                     │  v1.2.0 — ChromaDB kalıcı store    │
                     └─────────────────┬───────────────────┘
                                       │ (disk - kalıcı)
                                       ▼
                     ┌─────────────────────────────────────┐
Kullanıcı sorusu ──► │  LegalQARAG.ask()                   │
                     │   1. NVIDIA nv-embed-v1 (query)     │ ~1s
                     │   2. ChromaDB.search_with_dedup()   │ ~50ms
                     │   3. AnswerCache.lookup() [v1.3.0]  │ ~4ms
                     │      ├─ HIT → cache'den cevap       │ → RETURN
                     │      └─ MISS → devam                │
                     │   4. LLMClient.chat_async() [v1.3.0]│ ~3-240s
                     │      └─ NVIDIA / Groq / OpenAI /    │
                     │         Ollama (env'den seçim)      │
                     │   5. AnswerCache.store() [v1.3.0]   │ ~50ms
                     └─────────────────────────────────────┘
```

### Backward Compatibility

v1.3.0, v1.2.0 ve v1.1.0 kodu ile **tam uyumlu**:
- `NvidiaLLMClient` alias korundu (v1.1.0/v1.2.0 import'ları çalışır)
- Tüm eski env var'lar destekleniyor: `NVIDIA_API_KEY`, `NVIDIA_LLM_MODEL`, vb.
- `LegalQARAG()` default hâlâ NVIDIA + cache enabled
- v1.2.0 ChromaDB collection'ları (yargi_decisions, yargi_v12_medium) çalışır

### Hızlı Başlangıç (v1.3.0 — Groq ile)

```bash
# 1. Groq API key al: https://console.groq.com/keys (ücretsiz)
export GROQ_API_KEY=gq_...

# 2. NVIDIA embedding (hâlâ NVIDIA nv-embed-v1 kullanıyoruz — en iyi Türkçe embedding)
export LOCAL_EMBEDDING_API_KEY=nvapi-...
export EMBEDDING_PROVIDER=local
export LOCAL_EMBEDDING_BASE_URL=https://integrate.api.nvidia.com/v1
export LOCAL_EMBEDDING_MODEL=nvidia/nv-embed-v1
export LOCAL_EMBEDDING_DIMENSION=4096
export LOCAL_EMBEDDING_INPUT_TYPE=auto
export EMBEDDING_PROMPT_STYLE=raw

# 3. Provider seç
export LLM_PROVIDER=groq

# 4. Cache açık (default)
export RAG_ANSWER_CACHE=true

# 5. İlk seferlik index (~5-10 dk, ChromaDB'ye yazılır)
yargi-qa --load-corpus

# 6. Sor
yargi-qa --ask "Muvazaalı tapu satışında mirasçı hangi davayı açar?"
# → İlk sorgu: ~3s (Groq LLM)
# → Tekrar: ~1.9s (cache HIT)
```

### v1.3.0 Test Sonuçları

- **5/5 smoke test** geçti (import, factory, cache init, store+lookup, RAG init)
- **Cache HIT benchmark**: 52.5s → 1.86s (28.3x speedup, score=1.0000)
- Test dosyaları:
  - `tests/v13_rag_cache_results.json` — benchmark sonuçları
  - 5/5 smoke test, 1/1 RAG benchmark

---

## Hukuki QA Chatbot (RAG) — v1.2.0 (ChromaDB Kalıcı Store)

v1.2.0, RAG pipeline'ına **ChromaDB kalıcı vector store** + **token-aware chunking** ekler. Bir kez indexlenen kararlar process restart'ında kaybolmaz, sorgular sub-second hızda döner.

### Mimari

```
                     ┌─────────────────────────────────────┐
                     │  BedestenIndexer (tek seferlik)     │
                     │                                     │
Bedesten API ──────► │  search → fetch full text → chunk   │
                     │     ↓                               │
                     │  NVIDIA nv-embed-v1 (passage)       │
                     │     ↓                               │
                     │  ChromaDB.add_chunks()              │
                     └─────────────────┬───────────────────┘
                                       │ (disk - kalıcı)
                                       ▼
                     ┌─────────────────────────────────────┐
Kullanıcı sorusu ──► │  LegalQARAG.ask()                   │
                     │   1. NVIDIA nv-embed-v1 (query)     │ ~1s
                     │   2. ChromaDB.search_with_dedup()   │ ~50ms
                     │   3. build_context_from_decisions() │
                     │   4. NVIDIA Llama 3.1 70B           │ ~60-240s
                     └─────────────────┬───────────────────┘
                                       ▼
                            Atıflı cevap: "tapu iptali ve tescil davası açar.
                            [1] Yargıtay 7. HD, E.2026/2403, K.2026/3418"
```

### Modül Yapısı (v1.2.0)

```
qa_rag/
├── __init__.py        # Modül girişi + exports
├── llm_client.py      # NVIDIA LLM client (sync + async + streaming)
├── prompts.py         # Türk hukuki system prompt + context builder
├── citations.py       # Atıf formatlama
├── rag_engine.py      # LegalQARAG (chroma/memory backend seçimi)
├── chunker.py         # LegalChunker — 512-token, section-aware
├── indexer.py         # BedestenIndexer — Bedesten → ChromaDB pipeline
├── cli.py             # yargi-qa interaktif REPL + --ask modu
└── api.py             # FastAPI app (REST + SSE streaming)

semantic_search/
├── embedder.py        # NVIDIA nv-embed-v1 embedder (query/passage asimetrik)
├── vector_store.py    # In-memory VectorStore (v1.0.0'dan)
└── vector_store_chroma.py  # ChromaDB kalıcı store (v1.2.0)
```

### v1.1.0 → v1.2.0 İyileştirmeler

| Metrik | v1.1.0 | v1.2.0 |
|---|---|---|
| Retrieval süresi | ~120s (her sorguda Bedesten fetch) | ~1s (ChromaDB'den okuma) |
| Top-1 similarity | 0.35 | **0.53** (+51%) |
| Kalıcılık | Yok (in-memory) | **Var** (ChromaDB disk) |
| Tam metin embedding | Yok (500 char preview) | **Var** (512-token chunk) |
| Process restart | Corpus kaybolur | **Korunur** |
| NVIDIA API çağrısı/sorgu | 30+ (her belge) | **1** (sadece query) |

### Kurulum (v1.2.0)

```bash
# ChromaDB + tiktoken otomatik kurulur (pyproject.toml dependencies)
cd yargi-mcp-nvidia-semantic
pip install -e ".[qa]"

# NVIDIA API key
export NVIDIA_API_KEY=nvapi-...

# ChromaDB kalıcı dizini (default: ./chroma_db)
export CHROMA_PERSIST_DIR=/path/to/chroma_db
```

### Kullanım — İlk Index (tek seferlik, ~5-10 dk)

```python
import asyncio
from qa_rag import LegalQARAG

async def main():
    rag = LegalQARAG(backend="chroma")
    # İlk sefer: 200 belge çek, chunk'la, embed'le, ChromaDB'ye yaz
    result = await rag.load_corpora(
        initial_keyword="muvazaa tapu iptal",
        court_types=["YARGITAYKARARI"],
        target_docs=200,
    )
    print(f"{result['indexed_docs']} belge, {result['total_chunks']} chunk indexlendi")
    print(f"Süre: {result['elapsed_s']}s")

asyncio.run(main())
```

### Kullanım — Sorgu (sub-second retrieval)

```python
rag = LegalQARAG(backend="chroma")
# ChromaDB'de veri varsa is_corpora_loaded=True (process restart sonrası bile)
print(rag.is_corpora_loaded)  # True

response = await rag.ask("Mirasçı muvazaalı satışa karşı hangi davayı açar?")
print(response.answer)
print(f"Retrieval: {response.retrieval_time_ms}ms")  # ~1000ms
print(f"LLM: {response.generation_time_ms}ms")       # ~60-240s
```

### Çevre Değişkenleri (v1.2.0)

#### ChromaDB
- `CHROMA_PERSIST_DIR` — Kalıcı dizin (default: `./chroma_db`)
- `CHROMA_COLLECTION` — Collection adı (default: `yargi_decisions`)
- `CHROMA_DISTANCE` — Distance metric: `cosine`|`l2`|`ip` (default: `cosine`)

#### Indexer
- `INDEXER_BATCH_SIZE` — NVIDIA'ya bir seferde kaç chunk embed (default: 32)
- `INDEXER_TARGET_DOCS` — Hedef belge sayısı (default: 200)
- `INDEXER_KEYWORDS` — Virgülle ayrılmış anahtar kelimeler
- `INDEXER_COURT_TYPES` — Virgülle ayrılmış mahkeme tipleri

### Bilinen Sınırlar

1. **NVIDIA LLM yavaş** — İlk token 60-240 saniye. Streaming modu UX'i iyileştirir.
2. **Bedesten rate-limit** — 10 istek/30s, indexleme süresini sınırlar (~5 dk/50 belge).
3. **Query embedding cache yok** — Her sorgu NVIDIA'ya gider (NVIDIA embed ~1s).
4. **Bedesten yurt dışı IP'leri engelleyebilir** — Türkiye lokasyonu gerekli.

### Demo Senaryosu

```bash
# 1. İlk index (tek seferlik, ~5-10 dk)
export NVIDIA_API_KEY=nvapi-...
export CHROMA_PERSIST_DIR=$HOME/yargi_chroma
python -c "
import asyncio
from qa_rag import LegalQARAG
async def m():
    rag = LegalQARAG(backend='chroma')
    r = await rag.load_corpora(target_docs=50)
    print(r)
asyncio.run(m())
"

# 2. API başlat (artık ChromaDB'de veri var, anında hazır)
yargi-qa-api &

# 3. Soru sor (sub-second retrieval)
curl -X POST http://localhost:8001/api/ask \
  -H "Content-Type: application/json" \
  -d '{"question": "Muvazaalı satışta mirasçının hakları nelerdir?"}'
```

---

## Hukuki QA Chatbot (RAG) — v1.1.0 (legacy, in-memory)

Bu sürüm, semantik arama pipeline'ının üzerine bir **RAG tabanlı hukuki asistan** ekler. Kullanıcı doğal dilde soru sorar, sistem en alakalı emsal kararları bulur ve NVIDIA LLM ile atıflı cevap üretir.

### Mimari

```
Kullanıcı sorusu
       ↓
[NVIDIA nv-embed-v1] — query embedding (4096d)
       ↓
[VectorStore.search] — top-K en alakalı kararlar (cosine similarity)
       ↓
[build_context_from_decisions] — numaralandırılmış karar metinleri
       ↓
[NVIDIA Llama 3.1 70B Instruct] — atıflı cevap üretimi
       ↓
Atıf listesi: [1] Yargıtay 7. HD, E.2026/2403, K.2026/3418
```

### Modül Yapısı

```
qa_rag/
├── __init__.py        # Modül girişi
├── llm_client.py      # NVIDIA LLM client (sync + async + streaming)
├── prompts.py         # Türk hukuki system prompt + context builder
├── citations.py       # Atıf formatlama
├── rag_engine.py      # LegalQARAG ana sınıf
├── cli.py             # yargi-qa interaktif REPL + --ask modu
└── api.py             # FastAPI app (REST + SSE streaming)
```

### Kurulum

```bash
# Repo'yu klonla
git clone https://github.com/muhammedturan65/yargi-mcp-nvidia-semantic.git
cd yargi-mcp-nvidia-semantic

# Python >= 3.11 ile venv
python -m venv .venv
source .venv/bin/activate
pip install -e ".[qa]"

# NVIDIA API key set et
export NVIDIA_API_KEY=nvapi-XXXXXXXX
```

### Kullanım — CLI (yargi-qa)

#### İnteraktif REPL

```bash
yargi-qa
```

İlk çalıştırmada Bedesten'den 30 karar çeker (~2 dk), sonraki sorular hızlı çalışır.

```
========================================================================
  yargi-qa — Türk Hukuki QA Chatbot (RAG)
  NVIDIA nv-embed-v1 + Llama 3.1 70B + Bedesten emsal kararları
========================================================================

İlk kurulum: Bedesten'den 30 karar yükleniyor (~2 dk)...
  keyword: 'muvazaa tapu iptal'

✓ 28 karar yüklendi (2 fetch hatası)

Komutlar:
  /load <keyword>   Yeni corpus yükle (örn: /load nafaka)
  /info             Mevcut corpus bilgisi
  /examples         Örnek hukuki soruları göster
  /clear            Ekranı temizle
  /help             Bu yardım
  /quit             Çıkış

yargi-qa> Mirasçı, murisin muvazaalı satışına karşı hangi davayı açar?
Düşünüyor...

------------------------------------------------------------------------
Soru: Mirasçı, murisin muvazaalı satışına karşı hangi davayı açar?
------------------------------------------------------------------------

Mirasçı, murisin muvazaalı satışına karşı tapu iptal ve tescil davası açabilir.
...

[1] Yargıtay 7. HD, E.2026/2403, K.2026/3418 (14.03.2002) [skor: 0.3497]
[2] Yargıtay 7. HD, E.2026/4216, K.2026/3514 (29.06.2026) [skor: 0.3422]
[3] Yargıtay 7. HD, E.2026/3025, K.2026/3499 (17.10.2019) [skor: 0.3268]

⏱  Toplam: 76800ms (retrieval: 1003ms, LLM: 75770ms) | Tokens: 1948
------------------------------------------------------------------------
```

#### Tek Soru Modu

```bash
yargi-qa --ask "Muvazaa iddiasında ispat yükü kimin üzerinedir?"
```

#### Streaming Modu (token token)

```bash
yargi-qa --ask "Tapu iptal davasında süre nedir?" --stream
```

### Kullanım — FastAPI (yargi-qa-api)

```bash
# API server başlat
yargi-qa-api
# veya
uvicorn qa_rag.api:app --host 0.0.0.0 --port 8001
```

#### Endpoint'ler

| Method | Endpoint | Açıklama |
|---|---|---|
| `POST` | `/api/load` | Yeni corpus yükle (Bedesten'den) |
| `POST` | `/api/ask` | Senkron RAG (JSON response) |
| `POST` | `/api/ask/stream` | Streaming RAG (SSE) |
| `GET` | `/api/info` | Corpus & model bilgisi |
| `GET` | `/health` | Sağlık kontrolü |
| `GET` | `/docs` | Swagger UI |

#### Örnek İstek

```bash
# Corpus yükle (önce bunu yap)
curl -X POST http://localhost:8001/api/load \
  -H "Content-Type: application/json" \
  -d '{"initial_keyword": "muvazaa tapu iptal"}'

# Soru sor
curl -X POST http://localhost:8001/api/ask \
  -H "Content-Type: application/json" \
  -d '{
    "question": "Mirasçı muvazaalı satışa karşı hangi davayı açar?",
    "top_k": 5,
    "temperature": 0.2
  }'

# Streaming
curl -N -X POST http://localhost:8001/api/ask/stream \
  -H "Content-Type: application/json" \
  -d '{"question": "Tapu iptal davasında süre nedir?"}'
```

### Test Sonuçları (28 Ağustos 2026)

| Soru | Cevap Doğruluğu | Top-1 Skor | Süre | Tokens |
|---|---|---|---|---|
| Q1: Mirasçı hangi davayı açar? | ✓ "tapu iptal ve tescil" | 0.3497 | 77s | 1948 |
| Q2: Muvazaa ispat yükü | ✓ | 0.35 | ~75s | ~1900 |
| Q3: Tapu iptal süresi | ✓ | 0.33 | ~75s | ~1800 |

Detaylı JSON: `tests/qa_rag_test_results.json`

### Çevre Değişkenleri

#### NVIDIA LLM

| Değişken | Varsayılan | Açıklama |
|---|---|---|
| `NVIDIA_API_KEY` | (zorunlu) | build.nvidia.com API key |
| `NVIDIA_LLM_BASE_URL` | `https://integrate.api.nvidia.com/v1` | NVIDIA API URL |
| `NVIDIA_LLM_MODEL` | `meta/llama-3.1-70b-instruct` | LLM model adı |
| `NVIDIA_LLM_TEMPERATURE` | `0.2` | Düşük = belirleyici (hukuki için) |
| `NVIDIA_LLM_MAX_TOKENS` | `1500` | Maksimum cevap uzunluğu |
| `NVIDIA_LLM_TIMEOUT` | `90` | Saniye |

**NVIDIA Hesabında Doğrulanmış Modeller:**
- ✓ `meta/llama-3.1-70b-instruct` (önerilen, Türkçe güçlü)
- ✓ `meta/llama-3.1-8b-instruct` (hızlı alternatif)
- ✗ `nvidia/llama-3.1-nemotron-70b-instruct` (hesapta 404)
- ✗ `meta/llama-3.1-405b-instruct` (hesapta 404)

#### RAG Pipeline

| Değişken | Varsayılan | Açıklama |
|---|---|---|
| `QA_AUTO_LOAD_CORPUS` | `0` | `1` = API başlangıcında otomatik corpus yükle |
| `BEDESTEN_SEMANTIC_BATCH_SIZE` | `30` | `load_corpora` batch boyutu |
| `QA_TOP_K` | `5` | LLM'e kaç karar feed'lenecek |

### Bilinen Sınırlamalar

1. **Vector store in-memory** — Process restart'ında kaybolur. Gelecek sürümde ChromaDB entegrasyonu planlanıyor.
2. **NVIDIA LLM yavaş** — İlk token bazen 60+ saniye. Streaming modu UX'i iyileştirir.
3. **Bedesten yurt dışı IP'leri engelleyebilir** — Türkiye lokasyonu gerekli.
4. **Preview metin limiti** — Şu an karar metninin ilk 500 karakterı embed'leniyor. İleride tam metin + chunking planlanıyor.

### Demo Senaryosu

```bash
# 1. API başlat
export NVIDIA_API_KEY=nvapi-...
export QA_AUTO_LOAD_CORPUS=1
yargi-qa-api &

# 2. Bekle (corpus yükleniyor, ~2 dk)
sleep 120

# 3. Soru sor
curl -X POST http://localhost:8001/api/ask \
  -H "Content-Type: application/json" \
  -d '{"question": "Muvazaalı satışta mirasçının hakları nelerdir?"}'

# 4. Streaming dene
curl -N -X POST http://localhost:8001/api/ask/stream \
  -H "Content-Type: application/json" \
  -d '{"question": "Tapu iptal davasında karar süresi nedir?"}'
```