İçeriğe geç

Mimari

RAG’in üç işini gördük: bul, ekle, yaz. Bulabilmek için dokümanların önceden hazırlanmış olması gerekir. Kim hazırlıyor, ne zaman, nereye koyuyor?

bir kere (ingest)Doküman8 dokümanYükle1-load.tsTemizle2-clean.tsChunk’la3-chunk.tsEmbed4-embed.tsbge-m3embedding modeliVector DB5-store.tsher sorudaHow many working days ofannual leave does an employeewith 7 years of service get?SoruEmbedaynı modelAraen yakın 56-retrieve.tsRerank7-rerank.tsPromptCevap8-answer.ts22 iş günü [1]
  1. Dokümanlar

    Kraken Air’in 8 dokümanı (7 politika ve 1 duyuru, 2025 sürümü), Markdown olarak. Her birinin başında kendisi hakkında bilgi var: hangi doküman, hangi sürüm, kim okuyabilir. Üst şerit onları aranabilir hâle getirir ve bir kere çalışır.

  2. Yükle, temizle, böl

    1-load.ts dosyaları okur, baştaki bilgiyi metadata’ya çevirir. 2-clean.ts sayfa gürültüsünü atar, kırık satırları birleştirir. 3-chunk.ts dokümanları bölüm başlıklarından chunk’lara böler: arama bütün dokümanı değil, chunk’ları bulur.

  3. Embed

    4-embed.ts her chunk’ı bge-m3 ile 1024 sayılık bir embedding’e çevirir.

  4. Vector DB

    5-store.ts her chunk’ı vektörü, metni ve metadata’sıyla Chroma’ya yazar: bizim vector DB’miz.

  5. Soru, aynı modelle

    Her soruda alt şerit çalışır. Soru da üst şeritteki modelin aynısıyla 1024 sayıya çevrilir; başka bir model, karşılaştırılamayan başka bir uzay demektir.

  6. Ara ve rerank

    6-retrieve.ts soruya en yakın 5 chunk’ı bulur. Metadata filtresi eski sürümleri ve soranın okuyamayacağı dokümanları dışarıda tutar. 7-rerank.ts her chunk’ı sohbet modeline puanlatır ve yeniden sıralar. Buna rerank denir.

  7. Prompt ve cevap

    8-answer.ts kuralları, en iyi 3 kaynağı ve soruyu tek bir metinde birleştirir. Buna prompt denir. 7 yıllık kıdem için model 22 iş günü der. Kaynağı [1]: Annual Leave Policy, §3.

  8. İki şerit, iki buluşma noktası

    Şeritler yalnızca iki yerde buluşur: vector DB ve aynı embedding modeli. Sohbet modeli yalnızca alt şeritte çalışır: rerank ve cevap.

Adım adım bölümünde her sayfanın başında bu çizimin küçük hâli durur ve o sayfanın kutusu yanar. Örneğin chunking sayfasında:

bir kere (ingest)her sorudaYükleTemizleChunk'laEmbedVector DBSoruEmbedAraRerankPromptCevap
  • Dizinsrc/
    • Dizinsteps/ her kutu için bir dosya. Her birinin nasıl çalıştırılacağı aşağıdaki tabloda
      • 0-bare.ts adım 0: model tek başına, doküman yok
      • 1-load.ts politika dosyalarını ve her birinin üst bilgisini okur
      • 2-clean.ts sayfa başlıklarını atar, kırık satırları birleştirir
      • 3-chunk.ts her dokümanı chunk’lara böler
      • 4-embed.ts her chunk’ı 1024 sayıya çevirir
      • 5-store.ts chunk’ları vector DB’ye yazar
      • 6-retrieve.ts soruyu sayılara çevirir, en yakın 5 chunk’ı bulur
      • 7-rerank.ts bu 5 chunk’ı yeniden puanlar, en iyi 3’ünü tutar
      • 8-answer.ts prompt’u kurar, cevabı kaynaklarıyla yazar
    • Dizinlib/
      • types.ts adımdan adıma taşınan kayıtlar
      • ollama.ts modele giden iki çağrı: embed ve generate
      • similarity.ts cosine, L2 ve dot. C sayfasında dot()’u siz yazarsınız
      • store.ts iki store için tek arayüz
      • chroma.ts Chroma’ya yazma ve arama
      • store-json.ts aynı iş, JSON dosyasıyla
      • metrics.ts eval’in yazdığı skorlar: hit@1, recall@k, MRR
      • config.ts bütün ayarlar tek yerde
    • Dizincli/
      • step.ts tek bir adımı çalıştırır: npm run step – n
      • ingest.ts üst şerit: adım 1–5
      • ask.ts alt şerit: adım 6–8
      • question.ts günün sorusu ve defteri
      • eval.ts kaliteyi ölçer
      • doctor.ts kurulumu kontrol eder
      • catchup.ts geride kalırsanız yetişmenize yardım eder
      • capture.ts bu sitedeki bütün log’ları kaydeder
    • Dizinexplore/
      • similar.ts hangi metinler yakın?
      • map.ts vektör uzayı, 2 boyutta
      • pca.ts harita için 1024 boyutu 2’ye indirir
  • Dizincorpus/ Kraken Air politikaları, 2025 ve 2026
    • …
  • Dizindata/ her adımın çıktısı buraya yazılır — açıp bakın
    • …

Küçük yardımcı dosyalar listede yok: data/ okuma-yazma, log, hata mesajları, toggle sıfırlama ve repo kontrolleri.

Kutu Dosya Çalıştırma komutu Yazdığı
Yükle src/steps/1-load.ts npm run step -- 1 data/1-docs.json
Temizle src/steps/2-clean.ts npm run step -- 2 data/2-clean.json
Chunk’la src/steps/3-chunk.ts npm run step -- 3 data/3-chunks.json
Embed src/steps/4-embed.ts npm run step -- 4 data/4-vectors.json
Vector DB src/steps/5-store.ts npm run step -- 5 data/5-store.json
Embed + Ara src/steps/6-retrieve.ts npm run step -- 6 data/6-retrieved.json
Rerank src/steps/7-rerank.ts npm run step -- 7 data/7-reranked.json
Prompt + Cevap src/steps/8-answer.ts npm run step -- 8 data/8-answer.json

İki şerit, iki komut. Üst şerit npm run ingest:

src/cli/ingest.ts
// npm run ingest [-- --edition 2026] [--store json] [--reset]
// The offline half, steps 1–5 in one go: load → clean → chunk → embed → store.
// Runs once, or whenever the documents change. No chat model involved.
// --edition 2026 read corpus/2026/ (added next to the editions already stored)
// --reset empty the store first
import { cli, config } from "../lib/config.js";
import { openStore } from "../lib/store.js";
import { run as load } from "../steps/1-load.js";
import { run as clean } from "../steps/2-clean.js";
import { run as chunk } from "../steps/3-chunk.js";
import { run as embed } from "../steps/4-embed.js";
import { run as store } from "../steps/5-store.js";
import { exitOnError } from "./errors.js";
try {
if (cli.flags.reset) await openStore().reset();
await load();
await clean();
await chunk();
await embed();
await store();
console.log(`\nIngest done (edition ${config.edition}). Now: npm run question\n`);
} catch (e) {
exitOnError(e);
}

Alt şerit npm run ask -- "question":

src/cli/ask.ts
// npm run ask -- "question" [--prompt] [--bare] [--k 8]
// The online half, steps 6–8 for any question: retrieve → rerank → answer.
// --prompt print the exact text the model receives
// --bare first ask the model with NO sources (step 0), for comparison
// --k N how many chunks the retriever hands on
import { cli } from "../lib/config.js";
import { run as bare } from "../steps/0-bare.js";
import { run as retrieve } from "../steps/6-retrieve.js";
import { run as rerank } from "../steps/7-rerank.js";
import { run as answer } from "../steps/8-answer.js";
import { exitOnError } from "./errors.js";
const question = cli.positionals[0];
if (!question) {
console.error('usage: npm run ask -- "How much is the international per diem?" [--prompt] [--bare] [--k 8]');
process.exit(1);
}
try {
if (cli.flags.bare) await bare(question);
await retrieve(question);
await rerank();
await answer();
} catch (e) {
exitOnError(e);
}

“Aynı model” kuralı kodda bir kontrol: vector DB başka bir embedding modeliyle doldurulduysa arama hiç başlamaz.

src/steps/6-retrieve.ts
/**
* What it does: reads the step 5 receipt and stops if the store used a different embedding model.
*
* Guard: the store must hold vectors from the current data and the same embedding model.
*/
export async function storeReceipt(): Promise<StoreReceipt> {
const receipt = (await readStep<StoreReceipt>("5-store", "4-vectors")).data;
if (receipt.embedModel !== config.embedModel) {
throw new StaleInputError(
`The store was filled with ${receipt.embedModel} vectors, but questions would be embedded with ${config.embedModel}. ` +
`Vectors from two models live in different spaces — run steps 4 and 5 again.`,
);
}
return receipt;
}
Bir kere hazırla, her soruda ara.