Exactly-once mı, at-most-once mı? Bir AI ajanının tool çağrısı gerçekte neyi vaat edebilir
GNL ekibiside-effectsidempotencydurabilityexactly-once
Exactly-once tool çağrısı hedef gibi görünür. Süreç dışındaki bir etki için hiçbir kütüphane bunu tek başına vaat edemez. Vaat edilebilen şunlar, ve bunların ne zaman exactly-once'a vardığı.
Kısa cevap#
Bir AI ajanı, tool süreç dışında bir şeyi değiştiriyorsa exactly-once tool çağrısını tek başına vaat edemez. Vaat edebildiği şey at-most-once, yani en fazla bir kez: bir yan etki asla sessizce iki kez çalışmaz, sonuç bilinmiyorsa ajan durur ve bir insana sorar.
Dış sistem işe katılınca uçtan uca exactly-once olur: bir idempotency anahtarı kabul ediyorsa ya da ona ne olduğu sorulabiliyorsa. O zaman garanti ortaktır — ajanınki ve sağlayıcınınki — ve öyle söylenmelidir.
Bir yan etki için exactly-once neden imkânsız#
Süreçten dışarı çıkan bir tool çağrısının iki parçası var: etkinin kendisi ve olduğunun kaydı. E-posta gider, sonra sonuç yazılır. Deploy başlar, sonra sonuç yazılır. İkisinin arasında bir an var, ve bir çökme tam oraya denk gelebilir.
O çökmeden sonra süreç e-postanın gidip gitmediğini bilemez. Tool'u yeniden çalıştırmak onu iki kez gönderebilir. Atlamak hiç göndermeyebilir. Bu pencereyi hiçbir istemci tarafı kütüphane kapatamaz, çünkü cevap senin sisteminde değil, öteki sistemde.
Bir AI ajanının tool'u neden iki kez çalışır#
Pencere sebeplerden yalnızca biri. Pratikte üç sebep var:
- Yeniden deneme baştan başlar. Bir deploy ya da zaman aşımından sonra kuyruk işi yeniden teslim eder. Model yeniden planlar, zaten açılmış destek talebi bir kez daha açılır. Bu durum çökmeden sonra bir ajanı devam ettirmek rehberinde anlatılıyor.
- Model aynı işi yeniden planlar. Aynı çağrı, hiç çökme olmadan, yeni bir
toolCallIdile geri gelir. Çağrı kimliğine göre tekilleştiren hiçbir şey bunu göremez. Örüntü AI SDK tool'um yeniden denemede iki kez çalıştı rehberinde. - Çağrının içindeki çökme. Etki oldu, kayıt olmadı. Sürecin içinden kimsenin göremediği tek durum bu.
Bir tool çağrısı gerçekte neyi vaat edebilir#
Dört şey yapılabilir. Her biri ayrı bir sebebi kapatır:
- Kaydedilmiş bir yan etkiyi asla yeniden çalıştırma. Çalışma ilerledikçe her tool sonucu bir journal'a yazılır. Aynı
runIdile devam edildiğinde bitmiş çağrı çalıştırılmaz, kayıttan okunur. Baştan başlayan yeniden denemeyi bu kapatır. - Yeniden planlanan çağrıyı argümanlarına göre tekilleştir.
idempotency: 'args'ile aynı argümanlar aynı iş demektir; yeni birtoolCallIdile yeniden planlanan çağrı ilk çağrının sonucunu alır. Yeniden planlamayı bu kapatır. - Garantiyi sağlayıcıya kadar taşı. Tool, aşağıya iletmek için sabit bir
idempotencyKeyalır; böylece tekrarı sağlayıcının kendisi reddeder. Ya da tool'un, sağlayıcıya çağrının gidip gitmediğini soran birrecover()'ı olur. Pencere böyle cevap bulur — sağlayıcı bunu destekliyorsa. - Sonuç gerçekten bilinmiyorsa dur ve sor. Anahtar da
recover()da yoksa devam etmeSideEffectRetryBlockedErrorfırlatır. Tool'u yeniden çalıştırmaz, atlamaz da. Bir insan karar verir.
import { runDurable, gnlTool } from '@gnldev/durable';
import { SqliteStorage } from '@gnldev/durable/sqlite';
import { openai } from '@ai-sdk/openai';
import { tool } from 'ai';
import { z } from 'zod';
const chargeCard = gnlTool(
tool({
description: "Charge the customer's card",
inputSchema: z.object({ amount: z.number() }),
execute: async ({ amount }) => payments.charge(amount),
}),
{
sideEffect: true, // money moves — never replayed from the journal
idempotency: 'args', // and never re-run when the model re-plans it under a new toolCallId
},
);
const res = await runDurable({
runId: 'order-123', // the id of THIS WORK (an orderId) — never a sessionId
journal: new SqliteStorage('runs.db').runs,
model: openai('gpt-4o'),
tools: { chargeCard },
prompt: 'Cancel the order, suggest a similar product',
});Koddaki iki seçenek, bir ödemenin en fazla bir kez olması için yeterli. Aynı iki seçenek bir e-posta, bir destek talebi ya da bir deploy için de işe yarar: tool'u yan etki olarak işaretle, ve aynı argümanların aynı iş olduğunu söyle.
GNL'de "exactly-once effect" ne demek#
GNL'de "exactly-once effect", güvenli varsayılanı olan, çağrı kapsamlı tekilleştirme demek. Aynı tool çağrısı bir daha çalışmaz; idempotency: 'args' ile aynı argümanlar da bir daha çalışmaz. Bu garantinin tam adı yan etkiler için at-most-once.
Tool'un idempotencyKey'i anahtar kabul eden bir sağlayıcıya taşıdığında ya da recover()'a cevap verdiğinde uçtan uca exactly-once olur. Bu yoksa güvenli yön kazanır: devam etmek bazen bir insan gerektirse de, asla iki kez olmaz.
Vaadin bittiği yer#
- Farklı argüman farklı iştir. Model aynı iadeye bir not eklerse argümanların hash'i değişir ve iki kez çalışır. Bunun yerine çağrıyı işi tanımlayan alana göre anahtarla —
idempotencyKey: (args) => args.orderId— ve anahtara biridempotencyWindowver:'run','thread'ya da'cross-run'. - Aynı argümanlar, anahtar yok. Bir yan etki tool'unda ne
idempotency: 'args'ne de bir anahtar varsa, yeni bir çağrı kimliğiyle aynı argümanlarla gelen tekrar varsayılan olarak durdurulur ve bir insana sorulur. Anahtar olmadan hiçbir şey "aynı işin tekrarı" ile "bilerek yapılan iki aynı iş"i ayıramaz. - Hiçbir şey söylemeyen tool.
sideEffectbelirtmeyen bir tool yan etkili sayılır. Okuma yapan bir tool'dasideEffect: falseyaz; böylece gönül rahatlığıyla yeniden çalıştırılabilir.
Sırada#
Mekanizmanın tamamı ve API: exactly-once tool çağrıları. Sorun tarafından bakış: AI SDK tool'um yeniden denemede iki kez çalıştı. Bir çökmenin çalışmanın geri kalanına ne yaptığı: ajanın çökerse ne olur.
npm i @gnldev/durable ile ekle, ya da yeni bir projeye npm create gnl@latest my-agent ile başla.