Ajanın çökerse ne olur?
GNL ekibidurabilitycrash-recoveryside-effectsapprovals
İş baştan başlar, bir e-posta iki kez gider, verilen onay kaybolur. Her birinin bir çözümü var. Tek bir anın ise çözümü yok, yalnızca dürüst bir cevabı var.
Kısa cevap#
Çoğu ajanda, çalışırken gelen bir çökme o çalışmayı çöpe atar. Yeniden deneme en baştan başlar. Model yeniden planlar, tool'lar yeniden çalışır. Süreçten dışarı çıkan her şey — bir e-posta, bir destek talebi, bir ödeme — ikinci kez olabilir.
GNL'de aynı runId ile tekrar çağırırsın. Biten adımlar kayıttan gelir, yalnızca yarım kalan çalışır. Dış dünyada bir şeyi değiştiren tool en fazla bir kez çalışır. Çalışıp çalışmadığını bilemediği durumda GNL tahmin etmez: durur ve bir insana sorar.
Ne bozuluyor#
Bir kuyruğun arkasında çalışan bir destek ajanı düşün. Her mesaj için müşteriyi bulur, bir destek talebi açar ve müşteriye e-posta atar. Bir deploy, bir zaman aşımı ya da bellek yetmezliği onu bu adımların herhangi ikisinin arasında durdurabilir.
import { generateText, stepCountIs } from 'ai';
// A queue worker runs this for each incoming message.
await generateText({ model, tools, stopWhen: stepCountIs(10), prompt: job.message });
// The process dies after createTicket, before sendEmail. The retry starts from
// the top, and the model decides again whether to open a ticket.- İş baştan başlar. Yeniden deneme her model adımını tekrar çalıştırır. Onlara iki kez ödersin, ve model ikinci seferde başka bir yol seçebilir.
- Bir yan etki iki kez olur. Destek talebi çökmeden önce açılmıştı. Yeniden deneme bir tane daha açar. Müşteri iki e-posta alır.
- Verilen onay kaybolur. Bir insan evet dedi, tool çalıştı, ve cevap kaydedilmeden süreç öldü. Sonraki çağrı onaylanmış ama sonucu olmayan bir çağrı görür ve onu yeniden çalıştırır.
Her adımın kaydı#
GNL çalışma ilerledikçe her model adımını ve her tool sonucunu kendi veritabanındaki bir journal'a yazar. Journal'a yalnızca eklenir: ne olduğunu, sırasıyla tutar.
Kaldığı yerden devam etmek, aynı fonksiyonu aynı runId ile yeniden çağırmaktır. Journal'da olan adımlar çalıştırılmaz, kayıttan okunur. Journal'da olmayan ilk adım, çalışmanın devam ettiği yerdir.
import { runDurable, gnlTool } from '@gnldev/durable';
import { PostgresStorage } from '@gnldev/durable/postgres';
const journal = new PostgresStorage({ connectionString: process.env.DATABASE_URL! }).runs;
// Mark the tools that change something outside the agent.
const createTicket = gnlTool(createTicketTool, { sideEffect: true, idempotency: 'args' });
const sendEmail = gnlTool(sendEmailTool, { sideEffect: true, idempotency: 'args' });
// The first attempt and every retry run this same call.
export async function handle(job: { id: string; message: string }) {
return runDurable({
runId: `support:${job.id}`, // the id of THIS job — never a session id
journal,
model,
tools: { lookupCustomer, createTicket, sendEmail },
stopWhen: stepCountIs(10),
prompt: job.message,
});
}runId konuşmayı değil, işi adlandırır. Burada kuyruk mesajının kimliği; böylece ilk deneme ve her yeniden deneme aynı çalışmadır. Ajanda başka hiçbir şey değişmez: aynı model, aynı tool'lar, aynı prompt. Adım adım anlatım için: çökmeden sonra bir generateText ajanını devam ettirmek.
Yan etkiler bir kez çalışır#
Kaydedilmiş bir sonucu geri okumak her tool için güvenlidir. Bir tool'u yeniden çalıştırmak ise değildir. Bu yüzden ajanın dışında bir şeyi değiştiren tool'ları sideEffect: true ile işaretlersin. İşaretli ve bitmiş bir tool, devam ederken bir daha çalışmaz.
Modeller yeniden de planlar: aynı çağrı yeni bir çağrı kimliğiyle geri gelebilir. idempotency: 'args', aynı argümanların aynı iş olduğunu söyler; aynı argümanlarla yeniden planlanan çağrı, ilk çağrının sonucunu alır. Bu durum AI SDK tool'um yeniden denemede iki kez çalıştı rehberinde anlatılıyor.
Kimsenin kapatamadığı tek pencere#
Hiçbir framework'ün göremediği bir an var: tool işini yaptı, ve sonuç yazılmadan önce süreç öldü. E-posta gitti mi? Sürecin içinden bunu bilmenin yolu yok.
GNL burada tahmin etmez. Devam ederken çalışmayı SideEffectRetryBlockedError ile durdurur ve bir karar bekler; tool'u yeniden çalıştırıp umut etmez. Dış sistem soruyu cevaplayabiliyorsa — bir idempotency anahtarı kabul ediyorsa ya da ona ne olduğu sorulabiliyorsa — tool bunu söyleyebilir, ve garanti o sisteme kadar uzanır.
Onaylar çökmeden sağ çıkar#
Bir tool çalışmadan önce bir insanı bekleyebilir. GNL'de soru da cevap da journal'da durur; doğru anda kaydetmen gereken bir mesaj dizisinde değil.
Böylece sorudan cevaba kadar geçen saatler de güvende, evetten hemen sonraki an da: onaylanan çağrı çalışınca kaydedilir, ondan sonra gelen bir çökme onu yeniden çalıştırmaz. Kodu bir AI SDK tool'u çalışmadan önce insan onayı rehberinde.
Dene#
Elinde bir AI SDK ajanı varsa generateText yerine runDurable kullan ve ona bir journal ver. Çekirdeğin tamamı bu. Daha fazlasını istediğinde — bir sunucu, çalışmaları izleyip onaylamak için Studio, workflow'lar, hafıza — adım adım ekle: çekirdekten bütün framework'e.
npm create gnl@latest my-agent ile başla, ya da elindeki ajana çekirdeği npm i @gnldev/durable ile ekle.