MemoDokümantasyon
TR

RAG Hafıza Nasıl Çalışır?

Memo'nun hafıza sistemi, her konuşma için ilgili bağlamı otomatik olarak hatırlamak amacıyla Retrieval-Augmented Generation (RAG) kullanır. Bu sayfa, mesajdan hafıza erişimine kadar olan teknik işlem hattını açıklar.

İşlem Hattına Genel Bakış

Kullanıcı mesajı
    │
    ▼
┌──────────────────────┐
│ 1. Gömme             │  → Metni vektöre dönüştür (nomic-embed-text-v1.5)
└──────────────────────┘
    │
    ▼
┌──────────────────────┐
│ 2. Anlamsal Arama    │  → Vektör benzerliği (kosinüs) + FTS5 anahtar kelime araması
└──────────────────────┘
    │
    ▼
┌──────────────────────┐
│ 3. RRF Birleştirme   │  → Reciprocal Rank Fusion — vektör + FTS sonuçlarını birleştir
└──────────────────────┘
    │
    ▼
┌──────────────────────┐
│ 4. Bağlam Paketleme  │  → En iyi N anıyı sistem prompt'una ekle
└──────────────────────┘
    │
    ▼
┌──────────────────────┐
│ 5. LLM Yanıtı        │  → Model hafıza bağlamı ile yanıt üretir
└──────────────────────┘
    │
    ▼
┌──────────────────────┐
│ 6. Kalıcılaştır      │  → Gelecek erişim için yeni etkileşimi sakla
└──────────────────────┘

Adım 1: Gömme

Her mesaj, anlamsal benzerlik için özelleşmiş 137M parametreli bir model olan nomic-embed-text-v1.5 kullanılarak yoğun bir vektöre (gömme) dönüştürülür. Bu model dahili llama.cpp sunucusu aracılığıyla yerel olarak çalışır.

Mesaj Parçalama

Uzun mesajlar, 50 kelimelik örtüşme penceresi ile örtüşen 300 kelimelik parçalara bölünür:

// internal/memory/chunker.go
type Chunk struct {
    Text    string
    Index   int
    WordLen int
}

func ChunkMessage(text string) []Chunk {
    // Kelime sınırlarında böler, parça başına 300 kelime, 50 kelime örtüşme
}

Her parça bağımsız olarak gömülür ve saklanır. Önceden uzun mesajlar tek blok olarak saklanırdı — ortadaki bilgiler sıklıkla kaçırılırdı.

Birleşik Gömme

Saklama sırasında gömme vektörü hem kullanıcı mesajı hem de asistan yanıtının birleştirilmesinden hesaplanır. Bu, "fiyatlandırma hakkında ne önermiştin?" gibi sorguların, kullanıcı mesajı tek başına yanıtı içermese bile doğru anıyı bulmasını sağlar.

Adım 2: Hibrit Arama

Hafıza erişimi eşzamanlı olarak iki arama stratejisi kullanır:

Vektör Arama (Anlamsal)

Yaklaşık en yakın komşu (ANN) indeksi olarak vec0 sanal tablosu ile sqlite-vec kullanır:

SELECT rowid, distance
FROM memory_vec
WHERE embedding MATCH ?
ORDER BY distance ASC
LIMIT ?

Uzaklık kosinüs benzerliği kullanılarak hesaplanır. Sonuçlar anlamsal eşleşmelerdir — tam kelimeler farklı olsa bile kavramsal olarak ilişkili içerik.

Tam Metin Arama (Anahtar Kelime)

unicode61 belirteçleyici ile SQLite FTS5 kullanır:

SELECT rowid, rank
FROM memory_fts
WHERE content MATCH ?
ORDER BY rank
LIMIT ?

Her kelime bağımsız olarak eşleştirilir (ifade olarak değil). Bu, tam terimleri, isimleri, tarihleri ve teknik jargonu yakalar.

Dinamik Aday Havuzu

Erişim aday havuzu dinamik olarak boyutlandırılır:

candidatePool := min(max(topK * 5, 20), 100)

Daha önce 15'te sabitti — birkaç bin girdinin ötesindeki büyük hafıza tabanları için fazlasıyla küçüktü.

Konuşma Farkında Erişim

Son 3 kullanıcı mesajı ek bağlam olarak eklenir. "Bunun hakkında ne demiştik?" gibi kısa takipler artık hiçbir şey döndürmek yerine ilgili anıları bulur.

Çoklu Sorgu Erişimi

7 kelimeden uzun sorgular için, ilk 5 kelimeden konu çapası olarak ikinci bir gömme vektörü hesaplanır. Her iki gömme vektörü ayrı ayrı aranır ve RRF ile birleştirilir, böylece ana konu cümle ortasında gömülü olduğunda geri çağırma iyileşir.

Adım 3: Reciprocal Rank Fusion (RRF)

Vektör arama ve FTS sonuçları Reciprocal Rank Fusion kullanılarak birleştirilir:

RRF_skor(d) = Σ 1 / (k + sıra_i(d))

Burada k = 60 (sıra farklılıklarını yumuşatan bir sönümleme sabiti). Her iki sonuç kümesinde de görünen anılar en üste çıkarılır. Bir kalite filtresi, eşiğin altındaki düşük güvenli eşleşmeleri atar.

Adım 4: Bağlam Oluşturma

En iyi N eşleşen anı (genellikle 5–10, yapılandırılabilir) biçimlendirilir ve sistem prompt'una eklenir:

[İlgili Anılar]
- 2024-06-15: Kullanıcı, ücretsiz katmanlı kullanım tabanlı fiyatlandırmaya karar verdi.
  (eşleşme: hibrit, önem: 5)
- 2024-06-10: Kullanıcı, koltuk fiyatlandırmasının yerel-öncelikli bir araç için yanlış hissettirdiğini belirtti.
  (eşleşme: vektör, önem: 4)

Her anı, zaman damgasını, eşleşme türünü ("vektör", "fts" veya "hibrit") ve önem puanını içerir.

Adım 5: Yanıt Üretimi

LLM tam prompt'u alır — sistem prompt'u + ilgili anılar + konuşma geçmişi + kullanıcı mesajı — ve geçmiş bağlamla bilgilendirilmiş bir yanıt üretir.

Adım 6: Kalıcılık

Konuşma turundan sonra, alışveriş (kullanıcı mesajı + asistan yanıtı):

  1. Parçalanır (uzunsa)
  2. Yerel model aracılığıyla gömülür
  3. data/memory/memory.db dosyasında üç tabloda saklanır:
    • memories — ham metin, üst veri, önem puanı
    • memory_vec — vektör gömmeleri (vec0 sanal tablosu)
    • memory_fts — tam metin arama indeksi (FTS5 sanal tablosu)

Üç tablo da tek bir işlemde atomik olarak yazılır.

Veri Depolama

SQLite Şeması

-- Metin depolama
CREATE TABLE memories (
    id TEXT PRIMARY KEY,
    content TEXT NOT NULL,
    timestamp TEXT NOT NULL,
    importance INTEGER DEFAULT 1,
    retrieval_count INTEGER DEFAULT 0,
    pinned INTEGER DEFAULT 0,
    soft_deleted INTEGER DEFAULT 0,
    deleted_at TEXT
);

-- Vektör indeksi (sqlite-vec vec0)
CREATE VIRTUAL TABLE memory_vec USING vec0(
    embedding FLOAT[768]
);

-- Tam metin indeksi (FTS5)
CREATE VIRTUAL TABLE memory_fts USING fts5(
    content,
    tokenize='unicode61'
);

Önem ve Düşüş

Her anının bir önem puanı vardır (1–5):

  • Açıkça sabitlenmiş anılar (/remember) → önem 5, asla düşürülmez
  • Sık erişilen anılar → yükseltilir (erişim başına +1, 5'te sınırlı)
  • Eski ve kullanılmayan anılar → kademeli olarak düşürülür
  • 7 gün boyunca önem 0 → yumuşak silinir

Düşüş döngüsü günlük arka plan işi olarak çalışır.

Hafıza Birleştirme

Günlük bir arka plan süreci yakın-benzer birikimi önler:

  1. Gömme vektörleriyle en son 200 anıyı yükler
  2. Tüm çiftler arasında ikili kosinüs benzerliğini hesaplar
  3. Benzerlik ≥ 0.92 olan çiftleri belirler
  4. Her çifti AI sağlayıcısına birleştirilmiş tek bir girdi haline getirmesi için gönderir
  5. Birleştirilmiş girdinin yazıldığı onaylandıktan sonra iki orijinali yumuşak siler

Bu, düşüş döngüsünden sonra çalışır ve sohbet yanıtlarını asla engellemez.

Hata Ayıklama ve İnceleme

Ayarlar → Hafıza → Hata Ayıklama Araması: Herhangi bir sorgu yazın ve puanlar, zaman damgaları ve eşleşme türü rozetleriyle en iyi erişilen anıları görün.

Hafıza Analitik Paneli şunları gösterir:

  • Toplam anı, sabitlenmiş sayısı, bu haftaki eklemeler, süresi dolan yumuşak silmeler
  • Erişim sayısına göre en çok erişilen 5 anı

Komutlar

Komut Eylem
/remember <metin> Maksimum önemle sabitlenmiş bir anı kaydet
/forget <desen> Desenle eşleşen anıları sil

Performans

İşlem Tipik Gecikme
Gömme (300 kelimelik parça) ~50–100ms
Vektör arama (10K anı) ~5–15ms
FTS5 arama (10K anı) ~1–3ms
RRF birleştirme ~1ms
Tam işlem hattı (sorgu → sonuçlar) ~60–120ms


Performans, vec0 ANN indeksi sayesinde saklanan anı sayısı ile logaritmik olarak ölçeklenir. 100K girişlik bir hafıza tabanı, 10K'nın yaklaşık 2 katı arama gecikmesi ekler.