Zum Inhalt springen

Routing, Failover und Multi-Modal

Welcher Provider ein Feature ausführt, welche Fallback-Kette greift und ob ein Teil des Verkehrs auf einen Kandidaten läuft, steht im Contract — nicht im Anwendungscode. Diese Seite zeigt, wie das SDK aus dieser Konfiguration routet und welche der Defaults und Optionen dabei wirken.

Aus dem Contract kommt ein primärer Provider plus eine Fallback-Kette. Der Aufruf-Code benennt keinen Provider; er ruft nur asm.invoke mit der featureId auf. Welche Modelle dahinterstehen, entscheidet die Provider-Konfiguration der freigegebenen Contract-Version.

Pro Aufruf arbeitet das SDK die Slot-Kette der Reihe nach ab — beginnend beim primären Provider, dann jeder Fallback-Slot. Ein Slot gilt als gescheitert und löst den Wechsel zum nächsten aus, wenn:

  • der Provider mit einem 5xx-Status antwortet (z. B. 500, 503),
  • der Provider die failoverDeadlineMs überschreitet (Default 5 s),
  • ein Netzfehler auftritt.

Bei 429 (Rate-Limit) wartet das SDK zunächst mit exponentiellem Backoff und versucht denselben Provider erneut, bevor es auf den nächsten Slot wechselt. Zusätzlich gilt pro Provider die harte Obergrenze invokeTimeoutMs (Default 30 s); die failoverDeadlineMs wird intern auf diese Obergrenze begrenzt.

Der Wechsel ist transparent: Der Aufruf-Code ändert sich nicht, wenn ein Fallback einspringt. result.data enthält in jedem Fall das gegen das Output-Schema geprüfte Ergebnis.

import { createAsmClient } from '@ai-systems-manager/sdk'
// Die Provider-Konfiguration der freigegebenen Contract-Version legt OpenAI
// als primären Provider und Anthropic als Fallback fest. Im Code steht davon
// nichts — nur die failoverDeadlineMs lässt sich pro Client oder pro Aufruf
// nachregeln.
const asm = createAsmClient({
baseUrl: process.env.ASM_BASE_URL!,
token: process.env.ASM_FEATURE_TOKEN!,
failoverDeadlineMs: 5_000,
})
const result = await asm.invoke<{ category: string; urgency: 'low' | 'high' }>(
'feat_email_triage_01HZQ900000000000000000000',
{ email: 'Need quote for project ASAP', subject: 'urgent quote request' },
)
console.log('Triage:', result.data)

Scheitern alle Slots, wirft das SDK eine AsmError mit dem code ALL_PROVIDERS_EXHAUSTED. Das Feld details.providerChain listet jeden Versuch mit provider, model, latencyMs, success und — bei Fehlschlag — error (mit code, message und optional statusCode).

import { AsmError } from '@ai-systems-manager/sdk'
try {
const result = await asm.invoke('feat_email_triage_01HZQ900000000000000000000', input)
console.log(result.data)
} catch (err) {
if (err instanceof AsmError && err.code === 'ALL_PROVIDERS_EXHAUSTED') {
for (const attempt of err.details?.providerChain ?? []) {
console.error(attempt.provider, attempt.model, attempt.success, attempt.error?.code)
}
}
}

Die Failover-Optionen lassen sich sowohl am Client als auch pro Aufruf setzen. Die Aufruf-Option hat Vorrang.

OptionDefaultBedeutung
failoverDeadlineMs5_000 (5 s)Frist für den primären Provider, bevor das SDK zum nächsten Slot wechselt.
invokeTimeoutMs30_000 (30 s)Harte Obergrenze pro Provider-Aufruf. Begrenzt zugleich die wirksame failoverDeadlineMs.
primaryTimeoutMsgleich invokeTimeoutMsPer-Aufruf-Override der harten Obergrenze für den primären Slot.

Trägt die Provider-Konfiguration des Contracts ein providerConfig.routing.trafficSplit mit candidatePct und candidate, verteilt das SDK den Anteil candidatePct der Aufrufe deterministisch auf den Kandidaten; der Rest läuft auf dem primären Provider.

Die Zuordnung ist deterministisch und klebrig (sticky): Das SDK hasht den splitKey aus den Aufruf-Optionen — oder, wenn keiner gesetzt ist, die interne requestId — mit FNV-1a und ordnet den Bucket der Variante zu. Derselbe splitKey landet damit über Sessions hinweg immer auf derselben Variante. Das eignet sich für A/B-Vergleiche pro Benutzer: Übergib eine stabile User-ID als splitKey.

import { createAsmClient } from '@ai-systems-manager/sdk'
const asm = createAsmClient({
baseUrl: process.env.ASM_BASE_URL!,
token: process.env.ASM_FEATURE_TOKEN!,
})
const userIds = ['user-A', 'user-B', 'user-C']
for (const userId of userIds) {
const result = await asm.invoke<{ summary: string }>(
'feat_summarizer_01HZQ900000000000000000000',
{ text: 'Long document …' },
{ splitKey: userId },
)
// Dieselbe userId erzeugt immer dieselbe Variante (sticky Routing).
console.log(`${userId}: ${result.data.summary.slice(0, 60)}`)
}

Scheitert der Kandidat, gibt es in V1 keine eigene Fallback-Kette für ihn: Der Verkehr fällt in die Fallback-Kette des primären Providers zurück (flache Topologie). Den Trafficsplit selbst (Anteil und Kandidat) konfigurierst du im App-Cockpit unter Rollout — diese Oberfläche ist derzeit eine Demo.

Für Bild- oder PDF-Eingaben übergibst du im input ein binäres Feld. Das SDK erkennt automatisch, dass die Eingabe nicht rein textuell ist, und wechselt auf den Messages-Pfad des Vercel AI SDK (eine content-Array-Nachricht mit Text- und Binär-Teilen). Erkannt werden drei Formen eines Feldwerts:

  • ein Objekt mit mimeType, data (ein Buffer) und optional filename,
  • direkt ein Buffer (bzw. Uint8Array),
  • eine URL-Instanz.

Alle übrigen, skalaren Felder (string, number, boolean, null) bleiben für die Text-Ersetzung im Prompt-Template verfügbar.

import { readFile } from 'node:fs/promises'
import { createAsmClient } from '@ai-systems-manager/sdk'
const png = await readFile('./receipt.png')
const asm = createAsmClient({
baseUrl: process.env.ASM_BASE_URL!,
token: process.env.ASM_FEATURE_TOKEN!,
})
const result = await asm.invoke<{
vendor: string
total: number
currency: string
}>('feat_receipt_analysis_01HZQ900000000000000000000', {
context: 'expense report',
image: { mimeType: 'image/png', data: png, filename: 'receipt.png' },
})
console.log('Receipt:', result.data)

Der primäre Provider — und ein zur Eingabe passender Fallback bzw. Kandidat — muss die nötige Fähigkeit unterstützen: vision für Bilder, pdf für application/pdf. Prüft das SDK vorab, dass eine Fähigkeit fehlt, wirft es eine AsmError:

FallCode
Primärer Provider unterstützt die Fähigkeit nichtMULTI_MODAL_NOT_SUPPORTED_BY_PROVIDER
Kandidat (aus routing.trafficSplit) unterstützt sie nichtMULTI_MODAL_NOT_SUPPORTED_BY_FALLBACK

Primärer Provider, Fallback-Kette und Trafficsplit sind Teil der Provider-Konfiguration des Contracts und werden auf der Plattform gepflegt — siehe Modelle und Provider. Im SDK-Code stehen weder Provider noch Modelle; der Aufruf bleibt unverändert, wenn sich das Routing ändert. Pro Aufruf lässt sich nur das Verhalten nachregeln: failoverDeadlineMs, primaryTimeoutMs, splitKey und ein signal (siehe SDK einrichten und Fehlerbehandlung).