Appearance
LearnguAI Data Architecture & Mining Strategy
1. Core Data Models (Schema)
PostgreSQL/Supabase. İki yazım kaynağı var:
- Pipeline →
cards,packs,passages,language_pairs(statik, immutable) - Runtime →
users,user_progress(dinamik, kullanıcı başına)
users
typescript
interface UserProfile {
id: string; // UUID
email: string;
native_lang: string; // 'tr' | 'es' | 'ru' ...
target_lang: string; // şimdilik her zaman 'en'
// Progress
level: 'A1' | 'A2' | 'B1' | 'B2' | 'C1' | 'C2';
xp: number;
streak_current: number;
streak_best: number;
last_active_at: string; // ISO Date
// Settings
daily_goal_minutes: number;
is_pro: boolean;
hard_mode_enabled: boolean; // Katman 3: yazarak cevap
}cards
Pipeline'ın çıktısı. Bir kez yazılır, hiç değişmez.
typescript
// Pedagoji Katman 1 & 2 kart tipleri
type CardType =
| 'quiz' // Kelime anlamı (çoktan seçmeli)
| 'fillGap' // Boşluk doldurma
| 'sentenceBuilder' // Kelime sıralama
| 'idiom' // Kalıp & deyim
| 'errorCorrection' // Hata tespiti (algoritmik üretim, $0)
| 'reading'; // Paragraf + anlama sorusu (Katman 2, B1+)
interface Card {
id: string; // "c_1024"
pack_id: string; // "p_travel_a2"
// Seviye & Zorluk
level: 'A1' | 'A2' | 'B1' | 'B2' | 'C1' | 'C2';
complexity_score: number; // 0.0–1.0, word frequency bazlı
quality_score: number; // 0.0–1.0, pipeline quality gate çıktısı
// Tip & Konu
type: CardType;
topic_domain: string; // "Travel & Transport"
topic_leaf: string; // "Airport > Check-in & Boarding"
topic_tags: string[]; // ["airport", "travel", "check-in"]
// İçerik (Polymorphic JSONB)
content: {
// quiz | idiom
question?: string;
// fillGap | idiom
sentence?: string; // "I would like to _____ a ticket."
// sentenceBuilder
words?: string[]; // ["book", "to", "like", "I"]
// quiz | fillGap | idiom | errorCorrection
options?: string[]; // ["book", "buy", "sell"]
answer: string;
// errorCorrection — hangi kural test ediliyor
grammar_rule?: string;
// "third_person_s" | "past_simple" | "article_countable" |
// "preposition_time" | "modal_base" | "question_word_order" ...
// reading — passage_id'ye referans
passage_id?: string; // passages tablosundaki paragraf
};
// Yerelleştirme (Lazy Language Pair mimarisi)
// Key = native_lang kodu, pipeline her dil çifti için doldurur
translations: {
[native_lang: string]: {
question: string; // Soru/cümle çevirisi
answer: string; // Cevap çevirisi
context: string; // Bağlam açıklaması
};
};
// Örnek:
// translations.tr = { question: "Geçici", ... }
// translations.es = { question: "Efímero", ... } ← ES-EN açıldığında eklenir
}passages
reading tipi kartların bağlandığı paragraf tablosu.
typescript
interface Passage {
id: string; // "pass_512"
level: 'B1' | 'B2' | 'C1' | 'C2';
topic_domain: string;
topic_leaf: string;
// İçerik
text_en: string; // 3–6 cümlelik İngilizce paragraf
word_count: number;
source: string; // "wikipedia" | "bbc_learning" | "gemini_generated"
// Yerelleştirme
translations: {
[native_lang: string]: {
text: string; // Paragrafın anadilde özeti/çevirisi
};
};
}
// Not: Bir passage birden fazla reading kartına kaynak olabilir
// (farklı sorular, aynı paragraf)packs
Kart grupları. Curriculum graph'ın birimleri.
typescript
interface Pack {
id: string; // "p_travel_a2"
title: string; // "Airport & Travel"
level: 'A1' | 'A2' | 'B1' | 'B2' | 'C1' | 'C2';
topic_domain: string;
topic_leaf: string;
prerequisites: string[]; // Bu pack'ler %75 tamamlanmadan açılmaz
card_count: number;
estimated_minutes: number;
is_free: boolean; // Freemium kapısı
}
// Pack içi kart sırası:
// 1. quiz (kelime tanıtımı)
// 2. fillGap + idiom (bağlamda kullanım)
// 3. sentenceBuilder + errorCorrection + reading (üretim & doğrulama)language_pairs
Lazy Language Pair mimarisi için durum tablosu. TR-EN elle oluşturulur. Diğer tüm çiftler ilk kullanıcı talebinde tetiklenir.
typescript
interface LanguagePair {
native_lang: string; // 'es' | 'ru' | 'ja' ... (PK)
target_lang: string; // şimdilik her zaman 'en' (PK)
status: 'pending' // talep alındı, sırada
| 'generating' // pipeline çalışıyor
| 'ready'; // tüm çeviriler tamamlandı
triggered_by: string; // FK → users.id (ilk isteyen kullanıcı)
triggered_at: string; // ISO Date
generated_at: string | null; // tamamlanma zamanı
card_count: number; // kaç karta çeviri eklendi
cost_usd: number; // üretim maliyeti (log)
}user_progress
Her kullanıcının her kartla FSRS durumu. En çok büyüyecek tablo.
typescript
interface UserCardProgress {
user_id: string; // FK → users.id (composite PK)
card_id: string; // FK → cards.id (composite PK)
// FSRS Parametreleri
// Bkz: R(t,S) = 0.9^(t/S)
stability: number; // S: kaç günde %90 → %81'e düşer
difficulty: number; // D: kartın kalıcı zorluğu (1–10)
// Retrievability (R) hesaplanan değer, stored değil:
// R = 0.9^( (now - last_review_days) / stability )
// Durum
status: 'new' | 'learning' | 'review' | 'mastered';
last_review: string; // ISO Date
next_review: string; // ISO Date ← INDEX'LENMELI
lapses: number; // kaç kez unutuldu
// Katman 2: Zayıf Alan Hedefleme
// errorCorrection kartları için hangi grammar_rule'da hata yapıldı
grammar_rule_failures: Record<string, number>;
// Örnek: { "article_countable": 4, "third_person_s": 1 }
// Bu veri adaptive difficulty seçiminde kullanılır
}
// Kritik index:
// CREATE INDEX idx_user_progress_review
// ON user_progress (user_id, next_review);
// 1B satırda bu index olmadan → full scan → felaket
// Bu index ile → milisaniye2. Low-Cost Data Mining Algorithm ("The Deep Drill")
The goal is to generate high-quality language content without expensive per-user LLM calls. We use a "Miner -> Refiner -> Vault" pipeline.
Philosophy: "Write Once, Serve Millions"
Instead of asking an AI to generate a quiz for every user (Cost: $$$$), we mine 10,000 sentences once, process them into millions of static cards, and serve them from a cheap database (Cost: $).
The Algorithm Steps
Step 1: Raw Ore Extraction (Zero Cost)
Target public domain datasets that contain sentence pairs.
- Source A: Tatoeba Project: Millions of high-quality sentence pairs (En-Tr, En-Es).
- Source B: Project Gutenberg: Public domain books.
- Source C: Wiktionary: Word definitions and usage examples.
Step 2: The Sieve (Heuristic Filtering)
Use purely algorithmic (CPU-only) filters to remove junk. No AI yet.
- Length Filter: Discard sentences < 3 words or > 20 words.
- Complexity Grading: Assign CEFR level (A1-C2) based on word frequency lists (e.g., "Oxford 3000").
- If sentence contains 'ephemeral' -> Level C1.
- If sentence contains only top 500 words -> Level A1.
Step 3: The Refinery (Two Paths)
Path A — Gemini Flash (for quiz, fillGap, idiom, reading)
- Batching: Don't send 1 sentence. Send 50 sentences in one prompt.
- Task per card:
- Identify the hardest target word
- Generate 3 plausible distractors
- Verify / generate native language translation
- Assign topic_domain + topic_leaf
- Return confidence score (quality gate için)
- Cost Efficiency:
- 1M Input Tokens (~$0.075 on Gemini Flash) can process ~20,000 sentences.
- Total cost to build a course with 5,000 cards: ~$0.03.
Lazy Language Pair için ek çalışma: Yeni bir dil çifti (örn. ES-EN) ilk kez talep edildiğinde pipeline mevcut 235K kartın content alanını yeniden işlemez — sadece translations JSONB'ye yeni key ekler:
BATCH PROMPT (500 kart):
"For each English card, generate a Spanish (es) translation entry:
{ question, answer, context }. Return as JSON array."
Maliyet: 235K kart × ~50 token = ~12M token → ~$0.90 (tek seferlik)Path B — Algorithmic (for errorCorrection) — Zero AI Cost
- Take a correct sentence from Tatoeba (already filtered and verified).
- Apply deterministic error transformation rules:
third_person_s: "She runs" → "She run"past_simple: "I went" → "I have went" / "I did went" / "I was went"article: "a/an/the" insertion/removal errorspreposition: swap with common wrong preposition ("interested in" → "interested about")
- Generate 3 wrong variants + 1 correct = 1 complete card.
- Cost: $0.00 per card, infinitely scalable.
Step 4: crystallize (Static JSON)
The output is saved as the static cards table.
- It never changes.
- It requires no API keys at runtime.
- It works offline.
Example Workflow
- Input: "The sun sets in the west." (From Tatoeba)
- Algorithm:
- Identify target word: "sets" (Verb).
- Calculate Level: A1 (Common word).
- Refiner AI: Generates distractors ["rises", "falls", "stays"].
- Output Card:json
{ "id": "c_99", "level": "A1", "type": "fillGap", "content": "The sun _____ in the west.", "options": ["sets", "rises", "falls", "stays"], "answer": "sets" }
This approach allows LearnguAI to scale infinitely with near-zero marginal content cost.
3. Content Scale Analysis (TR → EN, A1–C2)
Source Data Capacity
| Kaynak | Ham Veri | Kullanılabilir Kart Tipleri |
|---|---|---|
| Tatoeba (EN-TR çiftleri) | ~450K cümle çifti | quiz, fillGap, sentenceBuilder, errorCorrection |
| Project Gutenberg (EN metinler) | ~5M cümle | sentenceBuilder, errorCorrection |
| Wiktionary (EN tanımlar + örnekler) | ~200K kelime girişi | quiz |
| COCA Phrasal Verb List | ~5,000+ ifade | idiom |
| BNC / Macmillan Idiom Lists | ~3,000+ deyim | idiom |
Filtre Sonrası Kullanılabilir Miktar (Tatoeba)
450,000 EN-TR cümle çifti
│
▼ uzunluk filtresi (3–20 kelime)
~280,000
│
▼ kalite filtresi (dilbilgisel, özel isim yoğun değil, temiz çeviri)
~180,000 kullanılabilir cümle ← tüm card üretiminin omurgasıKart Tipi Başına Gerçekçi Tahmin
| Tip | Ana Kaynak | Üretim Yöntemi | Tahmini Kart |
|---|---|---|---|
quiz | Tatoeba + Wiktionary | Gemini batch | ~30,000 |
fillGap | Tatoeba | Gemini batch | ~100,000 |
sentenceBuilder | Tatoeba (5–12 kelime) | Gemini batch | ~40,000 |
idiom | COCA + BNC + Gemini üretimi | Gemini batch | ~15,000 |
errorCorrection | Tatoeba + Gutenberg | Algoritmik ($0) | ~50,000 |
reading | Wikipedia + BBC Learning + Gemini | Gemini batch (B1+) | ~5,000 |
| TOPLAM | ~240,000 |
Not:
errorCorrectionvesentenceBuilderiçin Gutenberg'den gelen İngilizce metinler de eklendikçe bu rakam 400,000'e ulaşabilir. Gutenberg kartları TR çevirisi gerektirmediğinden (İngilizce gramer testi) Gemini maliyeti artmaz.
CEFR Dağılımı
| Seviye | Hedef Kelime Havuzu | Tahmini Kart Payı | Tahmini Kart |
|---|---|---|---|
| A1 | ~500 temel kelime | %8 | ~18,800 |
| A2 | ~1,200 kelime | %12 | ~28,200 |
| B1 | ~2,500 kelime | %22 | ~51,700 |
| B2 | ~5,000 kelime | %28 | ~65,800 |
| C1 | ~8,000 kelime | %20 | ~47,000 |
| C2 | sınırsız | %10 | ~23,500 |
| Toplam | %100 | ~235,000 |
Üretim Maliyeti (Tüm 235K Kart)
quiz + fillGap + idiom (Gemini):
~185,000 kart × ortalama 40 token/kart = ~7.4M token
$0.075 / 1M token (Gemini Flash input) → ~$0.56
sentenceBuilder (Gemini, daha kısa prompt):
~40,000 kart → ~$0.12
errorCorrection (algoritmik):
~50,000 kart → $0.00
Topic classification (Gemini, Step 3'e eklenir, minimal):
Batch içinde ek ~10 token/kart → ~$0.09
┌─────────────────────┐
│ TOPLAM: ~$0.77 │
│ 235,000 kart için │
└─────────────────────┘Gerçekçi İçerik Yayın Planı
MVP Launch → 1,000 kart (5 tip × A1–B1 × 4 konu)
Phase 1 → 10,000 kart (tüm tipler × A1–B2 × 20 konu)
Phase 2 → 50,000 kart (tüm tipler × A1–C2 × 50 konu)
Full Content → 235,000 kart (tek pipeline run, tam kaynak)4. Pipeline Eksik Parçaları (Scaling İçin Gereken)
Mevcut pipeline 5 demo kartı çalıştırıyor. 235,000 karta ve anlamlı bir öğrenme deneyimine ulaşmak için şu 5 bileşen eksik veya tanımsız:
4.1 Konu Taksonomisi (Topic Taxonomy)
Problem: Kartlar şu an sadece CEFR seviyesiyle etiketleniyor. "Travel A2" gibi bir pack oluşturmak için her kartın konu etiketi olmalı. Etiket olmadan pack sistemi çalışamaz, kullanıcı ilerleme haritası kurulamaz.
Çözüm: 3 katmanlı taksonomi. Pipeline Step 3'e konu sınıflandırma eklenir (mevcut Gemini batch prompt'una ~10 token eklemek yeterli).
Domain → Topic → Subtopic
Daily Life
├── Food & Drink
│ ├── At a Restaurant
│ ├── Cooking & Recipes
│ └── Groceries & Shopping
├── Home & Living
│ ├── Furniture & Rooms
│ └── Chores & Household
└── Health & Body
├── Body Parts & Symptoms
└── Doctor & Pharmacy
Travel & Transport
├── Airport
│ ├── Check-in & Boarding
│ └── Immigration & Customs
├── Accommodation
│ ├── Hotel
│ └── Airbnb & Hostel
└── Directions & Navigation
Work & Career
├── Job Search
│ ├── CV & Cover Letter
│ └── Interviews
├── Office Life
└── Business Communication
├── Email & Meetings
└── Presentations
Technology
├── Computers & Software
├── Internet & Social Media
└── AI & Automation
Society & Culture
├── Customs & Traditions
├── Current Events & News
└── Environment & Climate
Academic
├── Science & Research
├── Literature & Art
└── Philosophy & Ethics
(~80–100 leaf topic toplam)Schema güncellemesi (cards tablosuna eklenir):
typescript
topic_domain: string; // "Travel & Transport"
topic_leaf: string; // "Airport > Check-in & Boarding"
topic_tags: string[]; // ["airport", "travel", "check-in", "boarding", "flight"]Prompt güncellemesi (mevcut Gemini batch'e eklenir):
"For each sentence, also return:
- topic_domain: one of [Daily Life, Travel, Work, Technology, Society, Academic]
- topic_leaf: the most specific subtopic"4.2 Gramer Hata Kataloğu (errorCorrection İçin)
Problem: errorCorrection kartları algoritmik üretilecek ama hangi kuralların nasıl mutasyon üreteceği tanımlanmamış. Bu katalog olmadan bu tip kart üretilemiyor.
Çözüm: Türk öğrencilerin en sık yaptığı İngilizce hatalarına dayalı kural seti.
typescript
interface GrammarRule {
id: string;
name_tr: string;
level: 'A1' | 'A2' | 'B1' | 'B2' | 'C1';
frequency: 'high' | 'medium' | 'low'; // Türk öğrencilerde görülme sıklığı
detect: (tokens: string[]) => boolean; // Bu kural bu cümlede test edilebilir mi?
mutate: (sentence: string) => string[]; // 3 sistematik yanlış varyant üret
}
// İlk 10 kural (toplam ~25 kural hedefleniyor):
{ id: 'third_person_s',
name_tr: '3. tekil şahıs -s eki',
level: 'A2', frequency: 'high',
// "She runs fast." → ["She run fast.", "She is run fast.", "She runned fast."]
}
{ id: 'past_simple_irregular',
name_tr: 'Düzensiz geçmiş zaman çekimi',
level: 'A2', frequency: 'high',
// "I went home." → ["I goed home.", "I have went home.", "I was go home."]
}
{ id: 'present_perfect_vs_past',
name_tr: 'Present perfect / past simple karışıklığı',
level: 'B1', frequency: 'high',
// "I saw him yesterday." → "I have seen him yesterday."
// (yesterday ile perfect kullanımı Türklerin klasik hatası)
}
{ id: 'article_countable',
name_tr: 'Sayılabilir isimde article (a/an/the)',
level: 'A2', frequency: 'high',
// "I need a pen." → ["I need pen.", "I need the pen.", "I need an pen."]
}
{ id: 'article_uncountable',
name_tr: 'Sayılamaz isimde article yokluğu',
level: 'B1', frequency: 'high',
// "I need information." → "I need an information." / "I need the informations."
}
{ id: 'preposition_in_on_at_time',
name_tr: 'Zaman edatları: in/on/at',
level: 'A2', frequency: 'high',
// "on Monday" → "in Monday" / "at Monday"
}
{ id: 'preposition_in_on_at_place',
name_tr: 'Yer edatları: in/on/at',
level: 'A2', frequency: 'high',
// "at the station" → "in the station" / "on the station"
}
{ id: 'modal_base_form',
name_tr: 'Modal fiil sonrası yalın mastar',
level: 'A2', frequency: 'medium',
// "She can swim." → "She can swims." / "She can to swim." / "She can swimming."
}
{ id: 'question_word_order',
name_tr: 'Soru cümlesi yardımcı fiil sırası',
level: 'A2', frequency: 'high',
// "Where are you going?" → "Where you are going?" / "Where you going?"
}
{ id: 'comparative_form',
name_tr: '-er / more karşılaştırma yapısı',
level: 'A2', frequency: 'medium',
// "more fast" → "faster" / "most fast" → "fastest"
}
{ id: 'passive_voice_be',
name_tr: 'Edilgen yapıda be fiili',
level: 'B1', frequency: 'medium',
// "It was built in 1900." → "It builded in 1900." / "It is built in 1900."
}
// ... devam eden kurallar: conditional, reported speech,
// gerund vs infinitive, preposition after adjective, vb.Her kural otomatik olarak Tatoeba corpus'undaki uygun cümleleri tarar ve sistematik yanlış varyantlar üretir. Maliyet: $0.00.
4.3 Deduplication Pipeline
Problem: 180K cümleden kart üretilince anlamsal tekrarlar kaçınılmaz. "The cat sat on the mat" ve "A cat is sitting on the mat" neredeyse aynı fillGap kartını üretir. 235K kartın %15–20'si potansiyel duplicate.
Çözüm: Crystallize adımından önce iki aşamalı dedup.
AŞAMA 1 — Exact Hash Dedup (sıfır maliyet)
hash = sha256(type + answer + normalize(sentence))
Eğer hash daha önce görüldüyse → at
Süre: ~2 dakika / 235K kart
AŞAMA 2 — Semantic Dedup (aynı tip, aynı seviye kartlar arasında)
Her kartın cümlesi için sentence embedding üret
(text-embedding-3-small, $0.02 / 1M token → tüm corpus: ~$0.50)
Cosine similarity > 0.92 → ikisinden kalite skoru düşük olanı at
Süre: ~30 dakika / 235K kart
Maliyet: ~$0.50 (tek seferlik)4.4 Kalite Skoru
Problem: Her üretilen kart iyi değil. Kötü kart türleri:
- Distractors çok bariz yanlış → kart çok kolay, öğrenme değeri düşük
- Birden fazla seçenek doğru olabilir → ambiguous, kullanıcıyı yanıltır
- Gemini çevirisi hatalı → yanlış öğretir
Çözüm: Her kart için otomatik kalite skoru, threshold altındakiler atılır.
typescript
interface CardQualityScore {
// Distractors ne kadar yanıltıcı?
// Yöntem: doğru cevap ile her distractor arasındaki edit distance
// Çok kısa distance → çok benzer → plausible (iyi)
// Çok uzun distance → çok farklı → obvious wrong (kötü)
distractor_plausibility: number; // 0.0 – 1.0
// Doğru cevap gerçekten tek doğru mu?
// Yöntem: distractors içinde de doğru kabul edilebilecek var mı? (rule-based)
answer_uniqueness: number; // 0.0 – 1.0
// Gemini'nin kendi confidence skoru (response'dan parse edilir)
translation_confidence: number; // 0.0 – 1.0
overall: number; // weighted average
}
// Eşikler:
// overall < 0.55 → at (discard)
// overall 0.55–0.75 → 'needs_review' flag ile kaydet (human spot-check için)
// overall > 0.75 → direkt crystallizePratikte bu eşikler ilk 1,000 kartlık pilot run sonrası kalibre edilir. Hedef: needs_review oranı < %10.
4.5 Curriculum Graph (Pack Önkoşulları)
Problem: 235,000 kart düz bir yığın halinde sunulursa öğrenme sistematik olmaz. Kullanıcı A1 bitmeden C1 kartına denk gelebilir. Duolingo vs Anki farkı tam olarak bu: yapılandırılmış ilerleme.
Çözüm: Pack'ler arasında prerequisite ilişkisi + tamamlanma kapıları.
typescript
interface Pack {
id: string;
title: string;
level: 'A1' | 'A2' | 'B1' | 'B2' | 'C1' | 'C2';
topic_domain: string;
topic_leaf: string;
prerequisites: string[]; // Bu pack'ler %75 tamamlanmadan açılmaz
card_count: number;
is_free: boolean;
estimated_minutes: number; // tahmini tamamlama süresi
}Curriculum graph (TR→EN için önerilen iskelet):
FREE ──────────────────────────────────────────────────────────
[A1 Greetings & Basics]
│
├──► [A1 Numbers & Time]
│
└──► [A1 Family & People]
│
▼
[A2 Daily Routines]──► [A2 Food & Drink]
│
▼
[A2 Shopping]──────── [A2 Health Basics]
│
PRO ────────────────┼──────────────────────────────────────────
▼
[B1 Travel]──► [B1 Transport & Directions]
│
├──► [B1 Work & Career]
│
└──► [B1 Technology]
│
▼
[B2 Business]──► [B2 Society]
│
└──► [B2 Academic Writing]
│
▼
[C1 Advanced]──► [C1 Idioms Mastery]
│
└──► [C2 Native-Level]Her pack içinde kart sırası da rastgele değil:
quizkartları önce (kelime tanıtımı)fillGapveidiomortada (bağlamda kullanım)sentenceBuilderveerrorCorrectionsonda (üretim ve doğrulama)
5. Güncellenmiş Tam Pipeline
┌─────────────────────────────────────────────────────────────────┐
│ STEP 0: RAW DATA INGESTION │
│ Tatoeba EN-TR + Gutenberg + Wiktionary + COCA/BNC │
└─────────────────────────┬───────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ STEP 1: THE SIEVE (CPU only, $0) │
│ • Uzunluk filtresi (3–20 kelime) │
│ • CEFR seviye tahmini (word frequency listesi) │
│ • Kart tipi uygunluk tespiti (cümle uzunluğu, yapı) │
│ • Dil algılama (sadece İngilizce cümleler) │
└─────────────────────────┬───────────────────────────────────────┘
│
┌────────────┴────────────┐
│ │
▼ ▼
┌─────────────────────┐ ┌─────────────────────────────────────┐
│ PATH B │ │ PATH A │
│ errorCorrection │ │ quiz / fillGap / sentenceBuilder / │
│ (Algoritmik, $0) │ │ idiom (Gemini Flash) │
│ │ │ │
│ Gramer hata kataloğ│ │ BATCH PROMPT (50 cümle): │
│ kuralları uygulanır│ │ 1. En zor hedef kelimeyi bul │
│ 3 yanlış varyant │ │ 2. 3 plausible distractor üret │
│ üretilir │ │ 3. TR çevirisini doğrula │
│ │ │ 4. Konu etiketini belirle │
│ $0.00 / kart │ │ 5. Kalite skoru döndür │
└──────────┬──────────┘ └──────────────┬──────────────────────┘
│ │
└──────────────┬──────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ STEP 3: QUALITY GATE │
│ • overall_score hesapla │
│ • < 0.55 → discard │
│ • 0.55–0.75 → needs_review flag │
│ • > 0.75 → pass │
└─────────────────────────┬───────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ STEP 4: DEDUPLICATION │
│ • Exact hash dedup (~2 dakika, $0) │
│ • Semantic similarity dedup (~30 dakika, ~$0.50 one-time) │
└─────────────────────────┬───────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ STEP 5: CRYSTALLIZE │
│ • cards tablosuna yaz (immutable) │
│ • Pack'lere ata (topic + level bazlı) │
│ • Curriculum graph'taki yerine yerleştir │
│ • CDN cache'e al (offline erişim için) │
└─────────────────────────────────────────────────────────────────┘
Toplam süre (235K kart): ~4–6 saat (single run)
Toplam maliyet: ~$1.30
Tekrar ne zaman: Yeni kaynak eklendikçe, yılda 1–2 kez