Contextator
ENTR

İşletim

Gömme Modelleri

Varsayılan local model, OpenAI'a geçiş, modeli güvenle değiştirmek, arama sonuçlarının nasıl puanlandığı ve tamamen air-gapped çalıştırma.

Güncelleme:

Bir embedding modeli, metni bir vektöre dönüştürür; böylece benzer anlamlar birbirine yakın düşer. Contextator varsayılan olarak birini CPU’nuzda, local olarak çalıştırır — API key yok, hesap yok, makineden çıkan hiçbir şey yok — ve isterseniz onun yerine OpenAI kullanabilirsiniz.

Varsayılan

Model Xenova/multilingual-e5-small
Boyut 384
Diller Türkçe dahil 100. Soruyu cevabın yazıldığı dilde sorun: diller arası eşleşme ölçülmüş bir model sınırıdır, açılıp kapanan bir ayar değil
İndirme Bir kere ~470 MB (fp32), bir Docker volume’unda önbelleklenir
Çalıştığı yer CPU. GPU kullanılmaz ve gerekmez

Daha küçük ve daha hızlı seçenekler

EMBEDDING_DTYPE=q8                       # aynı model, ~120 MB, quantized
EMBEDDING_MODEL=Xenova/all-MiniLM-L6-v2  # yalnızca İngilizce, ~90 MB, yine 384 boyut
EMBEDDING_DIMENSIONS=384

Herhangi bir transformers.js feature-extraction modeli çalışır. EMBEDDING_DIMENSIONS, modelin ürettiğiyle eşleşmek zorundadır — eşleşmezse sunucu size doğru sayıyı söyler.

Bunun yerine OpenAI kullanmak

EMBEDDING_PROVIDER=openai
OPENAI_API_KEY=sk-…
OPENAI_EMBEDDING_MODEL=text-embedding-3-small
EMBEDDING_DIMENSIONS=1536
RESET_VECTORS=1        # yalnızca değişiklikten sonraki ilk başlatma için — aşağıya bakın

Uzun parçalarda daha iyi getirme ve çok daha büyük bir girdi penceresi; bedeli, indekslenen her parça ve sorulan her sorgu için bir API çağrısıdır. Dokümantasyon metniniz OpenAI’a gönderilir; bu kabul edilemezse local kalın.


Modeli değiştirmek

Aynı boyut (örn. 384 boyutlu local modeller arasında)

  1. EMBEDDING_MODEL’i değiştirin.
  2. Yeniden başlatın.
  3. Her projeyi yeniden indeksleyin.

Contextator, her projeyi hangi modelin indekslediğini kaydeder. Sunucunun modeli farklıysa, sonraki çalışma otomatik olarak tam bir yeniden indeksleme olur ve search_docs, bu gerçekleşene kadar o projede aramayı reddeder — kendinden emin ama yanlış sonuçlar döndürmek yerine, tam olarak bunu söyleyen bir mesajla.

Farklı boyut (örn. OpenAI’ın 1536’sına geçiş)

Vektör kolonunun genişliği veritabanı oluşturulduğunda sabitlenir, bu yüzden bu tek seferlik yıkıcı bir adımdır:

  1. Provider’ı, key’i ve EMBEDDING_DIMENSIONS’ı ayarlayın.
  2. Bir kere RESET_VECTORS=1 ile başlatın. Her parça ve belge düşürülür, kolon yeniden tipenir (re-typed).
  3. RESET_VECTORS’ı kaldırın (ya da 0’a geri ayarlayın).
  4. Her projeyi yeniden indeksleyin.

Bayrak olmadan sunucu başlamayı reddeder ve şu talimatı yazdırır:

The database was created with EMBEDDING_DIMENSIONS=384 but the current config says 1536.
Either set EMBEDDING_DIMENSIONS=384, or start once with RESET_VECTORS=1
(drops every indexed chunk; all projects must be re-indexed), or wipe the pgdata volume.

Arama gerçekte nasıl çalışır

Arama hibrittir: anlam ve birebir sözcük aynı anda aranır ve sıralarıyla birleştirilir.

  1. Sorunuz, belgeleri indeksleyen aynı modelle gömülür.
  2. PostgreSQL, o projeyle sınırlı olarak iki sıralama üretir — bir HNSW index üzerinde cosine similarity ile en yakın parçalar ve bir GIN index üzerinde tsvector anahtar sözcük eşleşmesi — ve bunları reciprocal rank fusion ile birleştirir; böylece bir ortam değişkeni de en az bir soru kadar kolay bulur sayfasını.
  3. En iyi eşleşmeler; dosya yolu, başlık kırıntısı ve skorlarıyla döndürülür.

Skorlar cosine similarity’dir: yüksek olan daha iyidir ve varsayılan modelde yüksek ve birbirine yakın dururlar. Sevk edilen yapılandırmada ortalama skor 0.881’dir ve yanlış bir hit, doğru olandan yalnızca 0.02 kadar aşağıdadır. Yani skor bir doğruluk sinyali değildir ve üründe hiçbir şey bir karar için skor okumaz.

İlgi eşiği — varsayılanı SEARCH_SCORE_FLOOR=0.82 — bambaşka bir şeye karşı ölçüldü: dokümantasyonunuzla hiç ilgisi olmayan sorulara. Onların en iyi sonucu ancak 0.829’a çıkıyor, gerçek bir sorunun en düşük en iyi sonucu ise 0.833. Eşiğin iddiasının tamamı bu aralıktır. Eşiğin yakalamadığı şey, ürününüz gibi görünen ama cevabı hiçbir yerde yazılı olmayan sorudur — onlar tam olarak gerçek soruların aldığı skorları alır ve hiçbir eşik ikisini ayırmaz. Eşiğin altında search_docs, bir ajanın alıntılayacağı bir sonucu uzatmak yerine iyi eşleşme yok der.

Skorlar bir proje ve bir model içinde karşılaştırılabilirdir, farklı modeller arasında anlamsızdır — EMBEDDING_MODEL’i değiştirirseniz eşik npm run eval ile yeniden ölçülmelidir.

SEARCH_SCORE_FLOOR (varsayılanı 0.82) sunucunun eşiğidir. Bir proje, kendi korpusu farklı ölçtüğünde — düzyazı ağırlıklı korpuslar genelde daha düşük bir eşik ister — sorgu panelinden kendi eşiğini ayarlayabilir, ya da eşiği kendisi için tamamen kapatabilir. Panel, bir eşiğin, uygulanmadan önce zaten kaydedilmiş aramalara ne yapacağını gösterir, ve sunucunun SEARCH_SCORE_FLOOR=0’ı yine de her projenin eşiğini kapatır. Bkz. Yapılandırma.

Birleştirilen sıralamalardır, skorlar değil. Cosine similarity ve PostgreSQL’in ts_rank’i karşılaştırılabilir nicelikler değildir, bu yüzden füzyon her parçanın her iki tarafta tuttuğu sıralamayı birleştirir — göründüğü listeler üzerinden Σ 1/(60 + sıra) (reciprocal rank fusion) — embedding modeli her değiştiğinde yeniden öğrenilmesi gereken ağırlıklı bir toplam yerine. Sonuç: bir sonucun skoru artık konumunu açıklamaz — 0.86 skorlu biri 0.88 skorlu birinin üstünde oturabilir. GET /api/projects/:id/search ve pano, her sonucun her iki tarafta tuttuğu sıralamayı D3 L1 olarak gösterir; bir L’si olup bir D’si olmayan bir sonuç, salt vektör aramanın tek başına göremeyeceği bir tanımlayıcıdır. Altın kümede, sıralama füzyonu tanımlayıcı biçimli otuz soruyu recall@5 %76,7’den %83,3’e taşıdı. Doğal dilde sorulan otuz dört soruyu ise hiç kıpırdatmadı; recall@1 %78,1’den %75,0’a düştü — bu, füzyonun yaptığı takas: yalnızca bir yarı tarafından bulunan bir parça, ikisi tarafından da makul biçimde bulunan birinin önüne geçemez.

Varsayılan model asimetriktir: bir arama sorgusunun önüne query: , indekslenen bir pasajın önüne ise passage: konularak eğitilmiştir, bu yüzden sunucu onları otomatik olarak ekler — indekslediğiniz ya da aradığınız hiçbir şey onlardan hiç bahsetmez, ve önekleri olmayan bir model bir sorguyu ve bir pasajı hiçbir bedel ödemeden özdeş biçimde kodlar. Retrieval korpusunda önekler bir soruyu sıra 1’de kazandırdı ve recall@5’te hiçbir şey değiştirmedi; EMBEDDING_PASSAGE_PREFIX=none ile birlikte EMBEDDING_QUERY_PREFIX=none onları kapatır ve önceki model kimliğini tam olarak geri getirir.

Anahtar sözcük yarısı varsayılan olarak her kaynağı PostgreSQL’in simple yapılandırmasında indeksler — yazıldığı gibi sözcükler, hiçbir stemming olmadan — ki bu referans materyali için doğru seçimdir: HALYARD_DISPATCH_TIMEOUT ve HLY-4015, oldukları dizeler olarak indekste hayatta kalır. Bir kaynak bunun yerine kendi dilini adlandırabilir (kaynak formundaki Language alanı), ki bu onu stemmed bir yapılandırmaya geçirir — anahtarı hakkında Türkçe sorulan bir soru o zaman anahtarın diyen bir sayfayı bulur, simple’ın ilişkisiz saydığı iki dize. İkisi bir projede birbirine karışmadan birlikte yaşar: her kaynak kendi yapılandırmasında aranır ve tek bir sorgu hepsine ulaşır, her biri füzyona kendi sıralı listesiyle katkıda bulunur. Bir kaynağın dilini değiştirmek onu yeniden indeksler.

Diller arası arama bilinen bir sınırdır ve düzeltilmeyecek

Soruyu, onu cevaplaması gereken dokümantasyonla aynı dilde yazın. Türkçe sayfalar Türkçe biçimli ifadeler için yüzeye çıkar. İngilizce sayfalar İngilizce biçimli ifadeler için yüzeye çıkar. O sınırı geçmek çoğunlukla hiçbir şey döndürmez, ve bu açılabilecek bir ayar değil, embedding modelinin bir özelliğidir. Her MCP client’ı bir dil modelidir, ve list_topics bir kaynağın dilini adlandırırken instructions çağıran agent’a search_docs sorgusunu ona uyacak şekilde yazmasını yönlendirir — arama mekanizmasının kendisinde bir değişiklik değil, çağırana verilen bir yönergedir.

Ölçüm eval/BASELINE.md’dedir ve fark kıl payı değil: otuz soru korpustan, her biri sorunun ifade edilmediği dildeki bir sayfa hakkında yazıldı — her yön için on beş. Dördü ilk beşte cevaplanıyor. Otuzdan yirmi yedisi bunun yerine sıra 1’de ifadenin kendi dilindeki bir sayfayı döndürüyor: model neyin sorulduğunu anlamakta başarısız olmuyor, ifadenin dilini cevabının üstünde sıralıyor. İki yön de eşit biçimde başarısız oluyor.

Tanımlayıcılar istisnadır. HALYARD_DISPATCH_TIMEOUT, HLY-4015, X-Halyard-Signature her iki dilde de aynı dizedir, bu yüzden aramanın anahtar sözcük yarısı onları ne olursa olsun bulur. Kümedeki bir tanımlayıcı adlandıran dokuz diller-arası sorudan dördü cevaplanıyor; sıradan sorular olarak ifade edilen yirmi birinden ise hiçbiri cevaplanmıyor.

Bir düzeltme beklemek yerine ne yapılır. Her dili kendi kaynağında tutun ve bir agent’ın aramasını source, path_prefix ya da version ile kapsamlandırmasına izin verin — her ikisi de iyi cevap veren iki kaynak, her iki dili de kötü cevaplayan tek bir koleksiyondan iyidir. Bilinen iki çözüm de zaten ölçülmüştür: hibrit arama (yukarıda) diller-arası tanımlayıcı getirimini kurtardı ve hiçbir doğal dil sorusunu kurtarmadı; SEARCH_RERANK arkasındaki çok dilli bir cross-encoder rerank’i diller-arası recall@5’i %13,3’ten %33,3’e taşıdı — yine de bundan önceki gömme modelinin ulaştığı %42,9’un altında — ve on üç sıradan sorunun sıra-1 cevabına ve bir aramanın gecikmesinin 12 ms’den 1,2 sn’ye çıkmasına mal oldu. Varsayılan olarak kapalıdır ve kapalı kalmalıdır. Ek bir index olarak ikinci, çeviri üzerine eğitilmiş bir encoder bunu gerçekten düzeltirdi — kabaca dört katı bir indirme, ikinci bir vektör kolonu, her kurulumda tam bir yeniden indeksleme — ve bu bedel bir dokümantasyon sunucusu için ödemeye değmez. Diller arası arama korpusunuz için gerekliyse, bu, bu ürünü beklemek için değil, farklı bir araç seçmek için bir nedendir.

Air-gapped kurulumlar

Varsayılan bir kurulumun yaptığı tek dışa giden istek, tek seferlik model indirmesidir. Onu da kaldırmak için:

  1. Models volume’unu (/app/.cache/models) model dosyalarıyla önceden doldurun — örneğin bağlı bir host’ta bir kere çalıştırıp volume’u kopyalayarak.
  2. EMBEDDING_OFFLINE=1 ayarlayın.

Sunucu bundan sonra hiçbir zaman indirme denemez ve model eksikse gürültülü biçimde başarısız olur.

Performans notları

  • Gömme CPU’ya bağlıdır ve indekslemenin yavaş kısmıdır; aynı anda tek bir projenin indekslenmesinin nedeni budur.
  • EMBEDDING_BATCH_SIZE (varsayılan 16), her çağrıda kaç parçanın gömüleceğini denetler.
  • Bir sorgu tam olarak tek bir kısa metni gömer, bu yüzden arama gecikmesine model değil veritabanı hakimdir.
  • Model, başlangıçta arka planda yüklenir — pano yüklenirken de çalışır, ama indeksleme ve arama onu bekler.

Gezinmek için ok tuşları, açmak için Enter.