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

Zaman yolculuğu & run çatallama (fork)

Bir run'ın journal'ından herhangi bir adımdaki durumu yeniden kur (reconstructState) veya o noktadan yeni bir dala çatalla (forkRun) — hata ayıklama ve 'what-if' için.

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

Bir agent run'ı journal'a adım adım (model ve tool girdileri olarak) yazılır. Zaman zaman "step 3'te model tam olarak ne görüyordu?" ya da "bu adımdan itibaren farklı bir tool/model ile ne olurdu?" sorularını yanıtlamak gerekir.

reconstructState journal entry'lerini SAF (yan etkisiz) olarak yürüyüp belirli bir adıma kadarki mesaj listesini ve henüz sonuçlanmamış (pending) tool-call'ları materyalize eder — hata ayıklama/inceleme (Studio inspector, log analizi) için idealdir.

forkRun ise bunun bir adım ötesi: kaynak run'ın ilk N model adımını + ilişkili tool kayıtlarını + orijinal girdisini YENİ bir runId'ye kopyalar. N'den sonrası kopyalanmaz — resumeRun ile devam ettirildiğinde prefix journal'dan replay edilir (side effect'ler TEKRAR çalışmaz), adım N'den itibaren ise CANLI koşar. Böylece "aynı noktadan farklı model/tool ile ne olurdu" senaryosu (what-if / A-B hata ayıklama) kaynak run'ı bozmadan denenebilir.

Kurulum / import#

Her iki fonksiyon da @gnldev/durable ana giriş noktasından dışa açılır — ayrı bir alt-yol gerekmez:

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

Adım adım kullanım#

Önce normal bir runDurable çağrısıyla journal'a adımlar yazılır (aşağıdaki örnekte model bir charge aracını çağırıp sonra metinle bitiriyor):

1) normal run — journal doldurulur
await runDurable({
  runId: 'r',
  journal,
  model: srcModel(),
  tools: tools(),
  stopWhen: stepCountIs(6),
  prompt: 'x',
});

Adım 1'den (ilk model adımından sonra) yeni bir dala çatalla — kaynağın model:0 girdisi ve ona bağlı tool kaydı + orijinal :input yeni fork1 runId'sine kopyalanır, model:1 ve sonrası KOPYALANMAZ:

2) forkRun — step 1'den yeni dal
const fork = await forkRun(journal, 'r', 1, 'fork1');
// fork => { newRunId: 'fork1', copiedModel: 1, copiedTool: 1 }

Ardından fork'u FARKLI bir model/tool mantığıyla resumeRun ile devam ettir: adım 0 (charge tool-call'ı) journal'dan replay edilir — yan etkisi (ör. tekrar ücretlendirme) TEKRAR çalışmaz — adım 1'den itibaren ise gerçekten canlı çalışır ve kaynaktan farklı bir sonuca varabilir:

3) resumeRun(fork1, ...) — step 1'den canlı
const r2 = await resumeRun('fork1', {
  journal,
  model: forkModel(),
  tools: tools(),
  stopWhen: stepCountIs(6),
});
// r2.text may differ (e.g. 'FORK-final'); the charge counter does NOT increase (replay).

Sadece durumu incelemek (fork açmadan) için reconstructState yeterlidir — journal entry dizisini ve istenen adımı verir:

reconstructState — durumu materyalize et
const entries = await journal.readRun('r');
const state = reconstructState(entries, 1);
// state => { step: 1, messages: [...], pending: [{ toolCallId: 'c1', toolName: 'pay' }] }

// Pass a seed to prepend the original user input as well:
const withSeed = reconstructState(entries, 3, { prompt: 'ode' });

API referansı#

fnreconstructState

Journal entry dizisini SAF olarak yürüyüp uptoStep'e kadarki mesajları ve pending tool-call'ları döner; opsiyonel seed ile orijinal girdi başa eklenir.

fnforkRun

srcRunId'in ilk `step` model adımını + ilgili tool kayıtlarını + :input'u newRunId altına kopyalar; sonrası kopyalanmaz, resumeRun ile step'ten canlı devam eder.

typeReconstructedState

reconstructState dönüş tipi: { step, messages, pending }.

typeReconstructSeed

reconstructState için opsiyonel seed girdisi: { system?, messages?, prompt? }.

typeForkResult

forkRun dönüş tipi: { newRunId, copiedModel, copiedTool }.

Dikkat
forkRun, journal'ın hem yazma hem okuma (Journal & JournalReader) sağladığı bir kaynak ister — kopyalanan prefix journal'dan replay edildiği için adım N'den önceki side effect'ler (ör. ücretlendirme) fork'ta TEKRAR çalışmaz; yalnız N'den itibaren gerçekten canlı koşar.