GNL
Dokümanlar menüsü
Core · Ücretsiz@gnldev/durable

Deterministik replay & çökme kurtarma

Journal'a yazılan her model/araç adımı sayesinde yarıda kesilen bir run aynı runId ile kaldığı yerden, prompt'u yeniden vermeden deterministik olarak devam eder.

Ne işe yarar / ne zaman kullanılır#

Uzun süren bir agent koşusu ortasında process çöker, deploy yeniden başlatır ya da bir araç onay bekleyip askıya alınır (bkz. human-in-loop-approvals). Klasik generateText bu noktadan sonra hiçbir şey bilmez — konuşmayı ve tüm araç sonuçlarını yeniden üretmeniz gerekir. runDurable her model adımını ve her araç çağrısının sonucunu journal'a yazar; aynı runId ile tekrar çağrıldığında (veya resumeRun ile) daha önce üretilmiş adımları modele YENİDEN sormaz — journal'dan okur, kaldığı yerden devam eder. Sonuç: yan-etkili bir araç (ör. ödeme tahsili) çökme sonrası ikinci kez çalışmaz, model aynı token'ları ikinci kez harcamaz.

Kurulum / import#

Paket tek girişten dışa açılır — alt-yol gerekmez (yalnız kalıcı depolama adaptörleri @gnldev/durable/sqlite ve @gnldev/durable/postgres ayrı export'tur, bkz. storage-adapters).

import { runDurable, resumeRun, InMemoryJournal } from '@gnldev/durable';

Adım adım kullanım#

Aşağıdaki örnek gerçek test senaryosundan alınmıştır: 5000 birim tahsilat isteyen bir araç çağrısı, guard tarafından onaya (require-approval) düşürülür — run askıya alınır. Prompt hiçbir yerde tekrar yazılmaz; resumeRun yalnızca runId ve onay bilgisiyle çağrılır.

1. İlk çalıştırma — araç onay bekleyip askıya alınır
const journal = new InMemoryJournal();

const r1 = await runDurable({
  runId: 'o1',
  journal,
  model: makeModel(),
  tools: makeTools(counter),
  guard,
  prompt: 'charge 5000',
  stopWhen: stepCountIs(6),
});

// nothing has been charged yet the guard suspended it
console.log(counter.charges);       // 0
console.log(r1.interrupts.length);  // 1
2. Çökme / yeniden başlatma sonrası devam — prompt YENİDEN verilmiyor
const r2 = await resumeRun('o1', {
  journal,
  model: makeModel(),
  tools: makeTools(counter),
  guard,
  approvals: { 'call-c': true },
  stopWhen: stepCountIs(6),
});

console.log(counter.charges); // 1 exactly once
console.log(r2.text);         // 'Done.'

resumeRun içeride, ilk çağrıda journal'a yazılmış girdiyi (prompt/messages/system) okuyup runDurable'ı aynı runId ile tekrar çağırır — self-contained resume budur. Kayıtlı girdi yoksa (bilinmeyen runId) anlamlı bir hata fırlatır.

API referansı#

fnrunDurable

generateText'in yerine geçer: model + araçları durable sarmalayıcılarla çalıştırır, her adımı journal'a yazar. Aynı runId ile tekrar çağrılırsa kayıtlı adımları yeniden çalıştırmaz.

fnresumeRun

runId'ye kayıtlı girdiyi (prompt/messages/system) journal'dan okuyup runDurable'ı tekrar çağırır — prompt'u yeniden vermeye gerek yok, sadece runId + agent config + approvals.

typeResumeAgentConfig

resumeRun için agent yapılandırması: model, tools?, guard?, stopWhen?, replay? — Studio embed ve resume çağrılarında ortak kesit.

fnreconstructState

Saf fonksiyon: journal entry'lerinden (JournalEntry[]) belirli bir adıma (uptoStep) kadar konuşma durumunu materyalize eder — time-travel ve fork'un temeli (bkz. time-travel-fork).

classDivergenceError

replay: 'strict' modunda, replay sırasında bir aracın yeniden üretilen argümanları kayıtlı argsHash ile uyuşmazsa fırlatılır (non-determinizm tespiti). Varsayılan 'lenient' modda yalnız uyarı basar.

classRunBusyError

Aynı runId eşzamanlı başka bir process/çağrı tarafından çalıştırılıyorsa (opt-in run-level lock veya tool execute in-flight) fırlatılır.

fnloadReplayCache

Dahili optimizasyon: journal bir readRun (JournalReader) sağlıyorsa (ör. SQLite/Postgres) resume'da tüm model/araç kayıtlarını tek sorguda önceden yükler. runDurable/resumeRun tarafından otomatik kullanılır — genelde doğrudan çağırmanız gerekmez.

classInMemoryJournal

Test/geliştirme için bellek-içi journal (Journal + JournalReader). Prod'da kalıcılık için SQLite/Postgres adaptörleri aynı arayüzü uygular (bkz. storage-adapters).

Lenient vs strict
runDurable'a verilen replay: 'strict' seçeneği, replay sırasında bir aracın girdisi kayıtlı hash'iyle uyuşmazsa (ör. model prompt değişmiş, non-deterministik bir üretim yapmış) çalışmayı DivergenceError ile durdurur. Varsayılan 'lenient' modda drift yalnızca konsola uyarı olarak düşer, kayıtlı çıktı yine de döner.
Yan-etkili araçlar
Yeniden deneme (retry) korumasız bir araç, journal'da failed kaydıyla kalan yan-etkili (ör. ödeme) bir çağrıyı otomatik tekrar çalıştırmaz — çift tahsilat riskine karşı kullanıcının açıkça approvals ile onay vermesi gerekir. Aynı davranış aynı runId içindeki her araç çağrısı için geçerlidir — bkz. exactly-once-tools.