Dokümanlar menüsü
Güvenli-varsayılan duplicate guard
Anahtarı olmayan yan-etkili bir araç, aynı istekteki daha önceki bir çağrıyla ÖZDEŞ argümanlarla YENİDEN çalışmak üzereyken merdiven (off → warn → reflect → block → suspend) devreye girer. Varsayılan olan suspend, tekrar çalışmadan önce bir insana sorar.
Ne işe yarar / ne zaman kullanılır#
Kafası karışmış bir model, aynı yan-etkili aracı ÖZDEŞ argümanlarla iki kez çağırabilir — çift tahsilat, çift e-posta, mükerrer sevkiyat. Duplicate guard varsayılan olarak açıktır ve varsayılan olarak bir insana sorar: bir yan-etki aracı (charge, sendEmail, …) aynı istekteki daha önceki bir çağrıyla özdeş argümanlarla yeniden yürümek üzereyse — ajanın kendi çağrıları ve altındaki her alt-ajanın çağrıları dahil — tekrar çalışmaz, Approvals akışında bekler. Anahtar yoksa "aynı iş yeniden" ile "bilerek iki tane aynısı" ayırt edilemez; bir insan ayırt edebilir. Kimliğini beyan eden bir araç (idempotencyKey, idempotency: 'args', bir pencere) bu guard'a hiç ulaşmaz: tekrarı anahtarı çözer. Guard ayrı istekler ARASINDA dedup YAPMAZ — onun için cross-run idempotency kullanın.
Merdiven#
Cevabı limits.sideEffectDuplicates ile belirlersiniz (varsayılan suspend; 0.10'dan önce warn idi). Seviyeler sertlik bakımından yükselir:
'off'Tespit yok — tekrar yürür.
'warn'Tekrarı yürütür ve her oluşumda adlandırılmış, journal'lanan bir incident yazar. 0.10'dan önceki varsayılan buydu; o davranışı korumak için bunu ayarlayın.
'reflect'Çalıştırmayı DURDURMAZ: modele journal'lanan TEK bir 'yeniden düşün' nudge'ı iletir ('reflected' durumlu kayıt); nudge'dan SONRAKİ özdeş tekrar block'a yükseltilir.
'block'Tekrarı DuplicateSideEffectError ile durdurur — bloklanan çağrı için hiçbir şey yazılmaz.
'suspend'Varsayılan. Askıya alınmış bir kayıt yazar → tekrar Approvals akışına düşer ve kararı bir insan verir. Aynı adımda paralel gönderilen özdeş çağrılar için de aynı şekilde sorulur. Bir insan tekrarı reddettikten sonra, aynı istekteki sonraki özdeş çağrı çalışmaz ve bir daha sorulmaz.
Hangi tekrar hangi katmana takılır#
Tekrar tek çeşit değildir ve tek bir katman hepsini göremez. İki eksen belirler: tekrarı KİM üretti (kafası karışan model mi, aynı işi yeniden isteyen kullanıcı mı) ve tekrar AYNI konuşmada mı geldi. Dört hücrenin üçünü GNL bugün kapatıyor; dördüncüsü bilinçle açık bırakılmış bir boşluk ve burada öyle yazıyor:
exact-hash / markerModel tekrarı, BİREBİR aynı argümanlar, aynı çalıştırma ya da aynı konuşma → bu sayfadaki merdiven. Tamamen deterministik: skor yok, embedder yok, argüman özeti yeter.
XIDKullanıcı tekrarı, kimlik alanları normalize edilince EŞİT — farklı konuşma, hatta farklı kanal dahil. XID deterministik ve kaynak (kişi) kapsamlıdır: aynı iş kimliği sohbetten, batch'ten ya da cron'dan gelmiş olsun tek bir O(1) okumada görünür, ve soru bunu taşır — bu iş 1 dakika önce, sohbet üzerinden tamamlanmış. Araç semanticIdentity beyan ettiyse ve çağrıda resourceId varsa bedavaya gelir; embedder istemez.
semanticIdentityFarklı yazım ya da parafraz, aynı konuşma → hash burada kördür. Devreye girebilen tek katman semantik kimliktir, ve o da yalnız ADAY bulur: kararı normalize kimlik alanları ve insan verir.
open gap (v2)Parafraz + farklı konuşma → AÇIK BOŞLUK. Semantik recall'ın kapsamı thread'dir; kimlik normalize edilince eşit çıkmıyorsa XID de göremez. Bu bilinçli bir sınır: konuşmalar-arası recall ve harici vektör indeksi, veri kapılı v2'ye bırakıldı.
Üçüncü satırın ayrıntısı — çift opt-in, karar hiyerarşisi, kural merdiveni ve yargıcın sertifikası — kendi sayfasında: semantic-duplicate-gate.
Kurulum / import#
Guard @gnldev/durable içinde yaşar ve çalıştırma başına RunLimits ile yapılandırılır:
import { createGnl, gnlTool, DuplicateSideEffectError, type RunLimits } from '@gnldev/durable';Adım adım kullanım#
gnl.run'a limits geçin. Hiçbir şey beyan etmeyen araç yan-etki sayılır; aynı istekteki ikinci özdeş çağrı block altında DuplicateSideEffectError fırlatır:
// 'charge' is unmarked → treated as a side effect (H7: sideEffect ?? idempotent !== true).
// A second identical charge in the SAME request (sub-agents included) is caught by the ladder.
// The default is 'suspend' (a person decides); 'block' throws instead.
const limits: RunLimits = { sideEffectDuplicates: 'block' };
try {
await gnl.run('pay', { runId: 'order-42', prompt: 'charge the customer', limits });
} catch (e) {
if (e instanceof DuplicateSideEffectError) {
// e.detail.firstToolCallId → the original successful call
console.log('duplicate blocked:', e.detail.toolName, e.detail.firstToolCallId);
}
}Modelin kendini toparlamasını tercih ediyorsanız reflect kullanın: tek bir nudge alır ve doğru şekilde devam edebilir; yalnızca inatçı bir özdeş tekrar o zaman bloklanır:
// 'reflect' does NOT stop the run: the model gets one journaled "reconsider" nudge
// (record status 'reflected'). An identical repeat AFTER the nudge escalates to a block.
const limits: RunLimits = { sideEffectDuplicates: 'reflect' };Okuma aracını sideEffect: false (veya idempotent: true) ile beyan edin ki guard onun için hiç ateşlenmesin (sorgular, durum yoklamaları). Aksi halde iki kez çağrılan beyansız bir okuma aracı da insan kararı bekler; soru bu çözümü adıyla söyler:
// Declare a read tool so the guard never fires for it — the suspend question names this fix:
const getWeather = gnlTool(weatherTool, { sideEffect: false });
// A plain tool object can say the same:
const getStatus = { execute: async (a) => fetchStatus(a), idempotent: true };
// The behaviour before 0.10 — the repeat runs and is journaled as an incident:
const permissive: RunLimits = { sideEffectDuplicates: 'warn' };API referansı#
RunLimits.sideEffectDuplicatesRunLimits alanı: 'off' | 'warn' | 'reflect' | 'block' | 'suspend' ya da { action, scope?, ttlMs? }. Varsayılan 'suspend' (0.10'dan önce 'warn' idi ve tekrarı çalıştırıyordu; o davranış için 'warn' verin). Aynı istekte özdeş argümanlarla tekrarlamak üzere olan, anahtarı olmayan bir yan-etki aracına uygulanır.
DuplicateSideEffectError'block' altında (ve yok sayılan bir 'reflect' nudge'ından sonra) fırlatılır. detail: { toolName, argsHash, firstToolCallId, toolCallId }.
AnyTool.sideEffect / idempotentBir araç varsayılan olarak yan-etkilidir (H7: sideEffect ?? idempotent !== true) — hiçbir şey beyan etmeyen araç yan-etki sayılır. Guard'dan muaf tutmak için sideEffect: false (veya idempotent: true) işaretleyin.
suspend yerine reflect seçin: nudge, kafası karışmış bir modelin çalıştırmayı bitirmeden kendini düzeltmesine izin verir; özdeş tekrardan sonra block'a yükselme ise tekrarın çalışmasını engeller. Nudge metni modele gider ve marka-nötrdür.suspend'dir: anahtarı olmayan tekrarlanan bir yan-etki çalışmaz, bir insanı bekler. Hiçbir şey beyan etmeyen araç yan-etki sayıldığı için iki kez çağrılan beyansız bir okuma aracı için de sorulur. 0.10 öncesi davranış için sideEffectDuplicates: 'warn' verin — tekrar çalışır ve journal'lanan bir incident olarak adlandırılır.exactly-once-tools, untrusted içerikte ateşlenen kardeş merdiven için taint-guard, suspend'in yönlendirdiği onay akışı için human-in-loop-approvals, ve hash'in kör kaldığı farklı-yazım vakası için semantic-duplicate-gate sayfalarına bakın.