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

Uzak ajanlar (A2A)

Başka bir sunucuda koşan bir ajanı sıradan bir tool gibi çağırın — ağ üzerinden exactly-once, çünkü uzak taraf deterministik bir runId'yi tekrar oynatır.

Ne işe yarar#

createAgentTool yerel bir ajanı tool'a çevirir. createA2ATool aynısını başka bir sunucudaki ajan için yapar: o sunucunun REST ucuna POST atar ve cevabı tool sonucu olarak döner. Modelin açısından hiçbir fark yoktur.

Bunu bir HTTP çağrısından fazlası yapan şey runId'dir. Çağrıdan deterministik olarak türetilir, yani tekrarlanan bir POST'un ikinci bir yan etkisi olmaz — uzak runDurable aynı çalıştırmayı tekrar oynatır ve kayıtlı sonucu döner. Tool bir ebeveyn runDurable'ın içindeyse ebeveyn de onu journal'lar: ebeveyn resume edildiğinde uzak çağrı tümden atlanır.

Bu, zincirdeki son sınırdır. Bir tool çağrısı, bir alt-ajan, bir ağ router'ı, kuyruğa alınmış bir iş, bir cron tetiklemesi ve şimdi de başka bir makineye yapılan çağrı — garanti her birinde geçerlidir.

Adım adım#

Uzak sunucuyu ve ajanı gösterin. Sonuç; cevap metnini, varsa kesintileri, uzak ajan adını ve uzak tarafın hangi runId altında tekrar oynattığını taşır — o son alan exactly-once tutamağıdır ve loglanmaya değer.

uzak bir ajana devret
import { createA2ATool } from '@gnldev/a2a';

const research = createA2ATool({
  endpoint: 'https://research.internal',
  agentName: 'researcher',
  timeoutMs: 30_000,
  secret: process.env.A2A_SECRET,   // opt-in HMAC signing
});

const res = await runDurable({
  runId: 'brief-9',
  journal,
  model,
  tools: { research },
  prompt: 'Summarise the competitor landscape',
});

// the tool result carries the remote handle:
// { text, interrupts, runId: 'a2a:brief-9:call-3', remoteAgent: 'researcher' }

runId nereden geliyor#

runDurable içinde sarmalayıcı, zaten ebeveyn çalıştırmaya kapsanmış bir idempotencyKey verir — {parentRunId}:{toolCallId}, args modunda {parentRunId}:{toolName}:{hash} — ve uzak runId bundan türetilir. Mesele tam da bu kapsamlamadır: küresel olarak benzersizdir.

Dayanıklı bağlam dışında böyle bir anahtar yoktur, o yüzden ham toolCallId kullanılır ve o yalnız tek bir çalıştırma içinde benzersizdir. Bazı sağlayıcılar call_1 gibi kısa kimlikler üretir; ilgisiz iki çalıştırma aynısını üretebilir ve o zaman uzak taraf ikinci çağrı için birinci çalıştırmanın kayıtlı cevabını tekrar oynatır. Bu tool'a runDurable üzerinden ulaşmak bunu engeller; çıplak bir AI SDK döngüsünde kullanmak kimlik benzersizliğini sizin sorumluluğunuza bırakır.

İmzalama ve zaman aşımı#

İmzalama opt-in'dir. Bir secret verirseniz istek x-gnl-signature ile gider — timestamp + '.' + gövde üzerinden HMAC-SHA256 — ve yanında x-gnl-timestamp bulunur; böylece zaman damgası imzalananın parçası olur ve yakalanmış bir gövde süresiz tekrar edilemez. Alıcı sunucu bunu createRestApi({ a2aSecret }) ile doğrular. Secret yoksa istek imzasızdır; bu eski davranıştır ve değişmemiştir.

timeoutMs varsayılanı 30 saniyedir. Zaman aşımında hata bir adım zaman aşımı biçiminde şekillenir: saran durableTool bir failed kaydı yazar ve model sessiz bir askıda kalma yerine gerçek hatayı görür.

createRestApi({ a2aSecret })
// on the receiving server verifies x-gnl-signature over timestamp + '.' + body
app.route('/api', createRestApi(config, { title: 'Research', a2aSecret: process.env.A2A_SECRET }));

API#

fncreateA2ATool

Uzak bir ajana devreden bir AI SDK tool'u kurar. Seçenekler: endpoint, agentName, description?, headers?, fetchImpl?, timeoutMs? (varsayılan 30_000), secret?.

typeA2AResult

{ text, interrupts, runId, remoteAgent } — runId, uzak tarafın tekrar oynattığı deterministik kimliktir.

typea2aSecret

Alıcı tarafta createRestApi({ a2aSecret }), x-gnl-signature / x-gnl-timestamp çiftini doğrular.

typebudgetGuard

budgetGuard — uzak fetch'ten ÖNCE çağrılan isteğe bağlı bir kanca; böylece bir kota kontrolü, aşımı sonradan fark etmek yerine çağrıyı reddedebilir. a2a kotayı kendi içinde taşımaz, host enjekte eder: ör. () => assertBudget(journal, { orgId, fallback }). Verilmezse hiç kota kontrolü yapılmaz.

typeStepTimeoutError

Zaman aşımında fırlatılan hata: { detail: { label, timeoutMs } }. Import edilmek yerine yerel tanımlanmıştır, çünkü @gnldev/durable burada yalnız devDependency'dir — şekli eşleştiği için durableTool sarmalayıcısı onu yine tanır ve failed kaydı yazar.

Tercihen runDurable içinden çağırın
Ebeveyne kapsanmış idempotency anahtarını veren şey odur ve uzak runId'yi küresel olarak benzersiz kılan da odur. Çıplak bir AI SDK döngüsünde tool yine çalışır, ama yukarıda anlatılan kimlik çakışması mümkün hale gelir ve bundan kaçınmak size kalır.
Varsayılan olarak imzasız
Secret yoksa imza da yoktur — bu seçenek var olmadan önceki davranışın aynısı ve özel bir ağda sorun değil. Bir güven sınırını geçen her yolda burada secret, alıcı sunucuda a2aSecret verin; aksi hâlde uca ulaşabilen her şey orada bir çalıştırma başlatabilir.