Dokümanlar menüsü
Semantik mükerrer-aday kapısı
Bu katman, anlamca benzer görünen geçmiş yan-etki işini bulur ("ABC ürününü oluştur" iki farklı şekilde söylenmiş — hash'in kör olduğu yer) ve YALNIZCA deterministik alan karşılaştırması da eşleştiğinde (araç adı, kimlik alanları, tutar alanları) ilk sonucu bir onay sorusunun yanına koyar.
Ne işe yarar / ne zaman kullanılır#
Katman sizin adınıza asla sessizce atlamaz, bloklamaz ya da modele "bu iş zaten yapıldı" demez; son söz her zaman deterministik alan eşitliği ve bir insandır. Best-effort ve fail-open çalışır: embedder'ınıza erişilemiyorsa ya da hiçbir aday eşiği geçmiyorsa davranış bugünkü davranıştır — ne bir gerileme, ne de bir garanti.
Karar hiyerarşisi: deterministik > insan kapısı > olasılıksal. Bu katman üçüncü sınıftır ve ilk ikisine bir aday bulucu olarak hizmet eder. Hash/claim/confirm/kritik profil katmanlarının yerine geçmez; onların ALTINDA koşar, ve tek çıkışı onay sorusu olduğu için onay kanalının olmadığı bir kurulumda hiç başlamaz — action: 'suspend' ve scope: 'thread' config-time zorunludur.
Nerede işe yaradığını ölçtük, ve ilk sandığımız yerde değil. Yetkin bir araç modeli sizin yerinize kanonikleştirir: "şu televizyondan bir tane daha" cümlesi çağrıya yine TV-42 olarak girer, yani birebir hash katmanı yakalar ve semantik basamakların sırası hiç gelmez. Bu katman, modelin metni yazıldığı gibi geçirdiği yerde kazanır: destek talebi başlıkları, müşteri ve firma adları, serbest metin açıklamalar.
Çift opt-in#
İki beyan da gerekir, biri eksikse katman tamamen atıldır: çalıştırma seviyesinde limits.sideEffectDuplicates.semantic bloğu, ve araç seviyesinde semanticIdentity beyanı. Kimliğini beyan etmeyen bir araç bu katmandan hiç geçmez — ve keys boş bırakılırsa config-time hata alırsınız, çünkü kurulmuş görünen ama işlemeyen bir kapı, engellemek için var olduğu yanlış güvenin ta kendisidir.
Zincir: adayı kim bulur, kararı kim verir#
Sıra sabittir ve her basamak bir öncekinin ıskaladığı artığı devralır. Basamakların hiçbiri tek başına bir şeye karar veremez:
exact-hashBirebir hash/marker önce konuşur. Aynı baytlarla gelen tekrar zaten orada yakalanır; bu katmanın sırası ancak hash ıskaladığında gelir.
toolName + threadAdaylar yalnız AYNI araçtan ve AYNI konuşmadan gelir — kayıtlar araç adı ve thread ile anahtarlanır.
minSimilarityKosinüs benzerliği minSimilarity eşiğini (varsayılan 0.6) geçen en iyi topK kayıt aday olur. Eşik bilinçli olarak cömert: bu tarafta kaçırmak güvenli yöndür, sorulmamış bir soru demektir.
identity / amount / discriminatorDeterministik faz kararı verir: normalize kimlik alanları eşit mi, tutar alanları farklı mı, ayırt edici alanlar (iptal/yön bayrakları) farklı mı. Olumsuzlama ve büyüklük burada çözülür, vektörde değil.
rulesKural merdiveni (opsiyonel, rules): kimlik alanları eşit ÇIKMAYAN adaylar için deterministik, bedava ve journal'lanan bir basamak. Normalleştiriciler soruya çıkarabilir, ayırıcılar adayı düşürebilir — her ikisi de algoritmadır, liste değil.
judgeYargıç (opsiyonel, judge): yalnızca gri artığa bakar ve tek bir soruya cevap verir — "bu iki kayıt aynı gerçek-dünya şeyini mi adlandırıyor?". 'same' cevabının satın aldığı tek şey, bir insana sorulmasıdır.
'suspend'Tek çıkış: suspend — soru insana düşer, onay ikinci işi gerçekten yaratır, ret yaratmaz. Diğer her sonuç bugünkü davranıştır ve journal'lanan bir sayaç/kayıt bırakır.
Kurulum / import#
Katman @gnldev/durable içinde yaşar; embed closure'ı, sağlayıcıyı ve faturayı siz sahiplenirsiniz:
import { createGnl, gnlTool, type RunLimits } from '@gnldev/durable';Adım adım kullanım#
Önce çalıştırma bloğu ve araç beyanı. embedModelId zorunlu bir damgadır: farklı modellerden gelen vektörler elma ile armuttur, damgası tutmayan kayıt karşılaştırmaya hiç girmez.
// Double opt-in: the run-level block AND the tool-level declaration — either absent, layer inert.
const limits: RunLimits = {
sideEffectDuplicates: {
action: 'suspend', scope: 'thread', // required — config-time throw otherwise
semantic: {
embed: myEmbed, // (texts: string[]) => Promise<number[][]>
embedModelId: 'text-embedding-3-small', // required stamp — mixed-model cosine is meaningless
minSimilarity: 0.6, // candidate threshold (recall side; misses are safe)
},
},
};
const createProduct = gnlTool(tool({ /* … */ }), {
sideEffect: true,
semanticIdentity: {
keys: ['sku'], // the business identity — REQUIRED, non-empty
amountFields: ['price'], // identity-equal + amounts differ → its own question
discriminatorFields: ['cancel'], // negation gate: differ → deterministically not a duplicate
describe: (args: any) => `create product ${args.sku}`, // the PII boundary: ONLY this reaches the embedder
},
});Kritik profil için önerilen kurulum yerel bir embedder: API faturasını, hız limitini ve PII sorusunu tek hamlede kaldırır. Damgaya kuantizasyonu da yazın — yeniden kuantize edilmiş bir model başka vektörler üretir. Aynı closure @gnldev/memory'nin semantik recall'ına da hizmet eder.
// npm i @huggingface/transformers (~150-400MB RAM at runtime, ~5-20ms per short sentence on CPU)
import { pipeline } from '@huggingface/transformers';
const extractor = await pipeline('feature-extraction', 'Xenova/multilingual-e5-small', { dtype: 'q8' });
const myEmbed = async (texts: string[]) => {
// e5 family quirk: inputs want a "query: " prefix — bake it into the adapter, never into callers.
const out = await extractor(texts.map((s) => `query: ${s}`), { pooling: 'mean', normalize: true });
return out.tolist();
};
// embedModelId: 'local:multilingual-e5-small@q8' ← stamp the QUANTIZATION too: a re-quantized model
// produces different vectors, and the stamp is what keeps old records out of the comparison.Kayıtlar SİZİN journal'ınızda, thread önekinin altında yaşar (kayıt başına ~3KB; vektör veritabanı yok, indeks yok) ve thread'le birlikte aynı purgeThread süpürmesinde ölür. Başarısız ya da askıya alınmış işe kayıt yazılmaz. Maliyet modeli: mutlu yolda korumalı yan-etki çağrısı başına ~1 embed çağrısı.
Yargıç sertifikası — sınavsız yargıç reddedilir#
Sertifika tören değildir. Aynı fikstürlerde, aynı promptla ölçtük: bir model parafraz çiftlerinin %43'ünü doğru cevapladı, bir başkası %100'ünü. Fikstürleri yazan aileyle akraba olmayan üçüncü bir model %93 aldı — yani aradaki fark modelden geliyor, kimin cümlelerini tanıdığından değil.
Bu yüzden ölçülmemiş bir yargıç, kurulu görünen ama olmayan bir katmandır. Eksik, zayıf (recall 0.70 altında ya da yanlış alarm 0.05 üstünde), model ya da prompt sürümü tutmayan bir sertifika config anında hata verir — v1'in boş keys hatasının kardeşi. Modeli değiştirmek ya da prompt sürümü atlamak sertifikayı geçersiz kılar ve sınav yeniden verilir.
semantic: {
embed: myEmbed, embedModelId: 'local:multilingual-e5-small@q8',
rules: true, // the deterministic ladder — the whole configuration
judge: {
// TRANSPORT ONLY: the framework renders the prompt, you own the model and the bill.
complete: async ({ system, user }) => (await myModel(system, user)).text,
judgeModelId: 'your-judge-model',
qualification: cert, // from @gnldev/semantic-qualify — REQUIRED
maxCallsPerRun: 10, // journal-backed slots; survive resume
timeoutMs: 8000,
},
},npx gnl-semantic-qualify --judge ./my-judge.mjs --model your-judge-model # → gnl-judge-cert.jsonTezgah kör değerlendirir: opak kimlikler, karıştırılmış sıra, etiketler modele hiç gönderilmez, ve barajlar çalışma zamanının zorladığı aynı sabitlerden okunur. Kendi fikstürlerinizi göstermek için --fixtures kullanın; yayınlanan set yayınlandığı anda saklı-küme olmaktan çıkar. Ve sertifika SINAV performansını damgalar, saha doğruluğunu değil.
Önce sayın, sonra yargıç açın. Studio'nun semantik kartı scan.grayCalls raporlar: gri artık üreten ÇAĞRI sayısı — yani bir yargıcın size neye mal olacağının fiyat teklifi. Ölçülen trafikte yargıç 12 kez konuştu (126 araç çağıran turda) ve hiçbiri bir soru doğurmadı; gri bant gerçekten farklı işlerden oluşuyordu. Bir süre rules açık, judge kapalı koşun, sayacı okuyun, sonra karar verin.
Dürüst sınırlar#
Beşi de bilinçli sınır ve hiçbiri sonradan keşfedilecek bir sürpriz olarak bırakılmadı:
scope: 'thread'Kapsam thread'dir: bu katman "bu iş DAHA ÖNCE BU KONUŞMADA yapıldı mı" sorusunu cevaplar. Konuşmalar arasındaki parafraz yakalanmaz. Kimlik normalize edilince eşit çıkıyorsa o vakayı XID kapatır — ayrı ve deterministik bir katman.
fail-openFail-open: embedder erişilemezse, hiçbir aday eşiği geçmezse ya da damga tutmuyorsa davranış bugünkü davranıştır. Katman best-effort'tur ve hash/claim/confirm/kritik profil katmanlarının yerine geçmez.
TOCTOUEşzamanlı parafraz ikizleri birbirini göremez: farklı hash, aynı kimlik, aynı anda uçuştalar — ikisi de koşar. Bu katmanın vaadi ZAMANA YAYILMIŞ tekrardır; eşzamanlılık alttaki hash/kilit katmanlarının işidir.
semanticIdentity.keysBeyanın kalitesi korumanın kalitesidir. Canlı bir örnek: iki farklı depoya giden iki sipariş kapıya birebir aynı göründü, çünkü createOrder'ın şemasında depo alanı yoktu. Kapı yalnızca araç çağrısının TAŞIDIĞINI görebilir — bu bir semanticIdentity düzeltmesinden önce bir şema düzeltmesidir.
describe()Karışık dilli terim çiftleri ölçülmüş bir kör noktadır: "Karanlık mod" ile "Dark mode" aday eşiğinin altında kalır, ve recall kapısının hiç yüzeye çıkarmadığı bir kaydı ne kurallar ne yargıç görür — kaçırma sessiz ve nihaidir. Çare, aynı terimi birden çok dilde taşıyabilen alanları describe() içinde TEK bir kanonik dile normalize etmektir; çerçeve bilinçli olarak sözlük göndermez.
API referansı#
SemanticDupConfigÇalıştırma seviyesindeki blok: embed (texts dizisi alan, vektör dizileri döndüren closure), embedModelId (zorunlu damga), minSimilarity (aday eşiği), topK, rules, judge.
SemanticIdentityAraç seviyesindeki beyan: keys (iş kimliği alanları — zorunlu ve boş olamaz), describe (kanonik cümle ve PII sınırı), amountFields (tutar kapısı), discriminatorFields (olumsuzlama kapısı).
SemanticJudgeConfigYargıç yapılandırması: complete (YALNIZCA taşıma — promptu çerçeve render eder, modeli siz sahiplenirsiniz), judgeModelId, qualification, maxCallsPerRun (varsayılan 10, journal'da tutulan slotlar), timeoutMs.
JudgeCert@gnldev/semantic-qualify tezgahının ürettiği sınav sonucu; config'e veri olarak yapıştırılır. Model kimliği ve prompt sürümü tutmuyorsa çalıştırma başlamaz.
RunLimits.sideEffectDuplicates.semanticÇalıştırma limitlerindeki semantik blok. action 'suspend' ve scope 'thread' zorunludur; başka bir değer katmanı sessiz ya da bloklayan bir karar mercii yapardı.
precision@suspend sayınızdır. Sentetik hiçbir ölçüm bir saha doğruluğu vaadi değildir.suspend'in yönlendirdiği onay akışı için human-in-loop-approvals sayfalarına bakın.