Claude Agent SDK gegen OpenAI Agents SDK gegen Google ADK: Ein Agent, drei Implementierungen
Claude Agent SDK gegen OpenAI Agents SDK gegen Google ADK: Ein Nährwertkennzeichnungs-Agent wurde dreimal entwickelt – mit Aufgaben, die jedes Tool übernimmt, und dem, was man selbst noch schreiben muss.
Jeder große Anbieter von KI liefert heute ein Agent-SDK aus, und die Verkaufsargumente sind nahezu identisch. Geben Sie uns Ihre Tools sowie einen Prompt, und wir führen den Prozess durch: Toolaufrufe, strukturierte Ausgaben, Sitzungen sowie Trace-Informationen sind inklusive. Wenn die Marketingstrategien so ähnlich sind – was unterscheidet diese Kits wirklich voneinander, und auf welchem sollten Sie aufbauen?
Um das herauszufinden, habe ich denselben Agent dreimal entwickelt: mit Anthropics Claude Agent SDK, mit dem OpenAI Agents SDK sowie mit Googles Agent Development Kit (ADK). Der Agent ist kein Spielzeug – er handelt wie ein Untersucher von Nährwertangaben, liest ein Foto, nutzt mehrere Tools zur Analyse, gibt ein Urteil ab und verbessert sich durch immer mehr Beispiele. Mit einem Agent und drei verschiedenen SDKs liegen alle Unterschiede im SDK selbst und nicht am Problem.
Kurz gesagt: Die drei Ansätze stimmen fast vollständig in Bezug auf den schwierigen Teil, nämlich den Schlussfolgerungszyklus, überein, unterscheiden sich aber in allem, was damit verbunden ist. Sie unterscheiden sich darin, wie ein Tool deklariert wird, wie ein Bild eingefügt wird und ob überhaupt textbasierte Ausgaben erzeugt werden. Am aufschlussreichsten ist jedoch, dass sie voneinander abweichen, was keiner von ihnen für den Benutzer übernimmt. Am Ende werden Sie wissen, welches Set für welche Aufgabe geeignet ist und wie viel Arbeit Ihnen nach der Auswahl noch bleibt.
Ein weiterer Aspekt verbindet alle drei Versionen miteinander. Der Agent wurde so konzipiert, dass er aus Erfahrungen lernt. Er erinnert sich an Produkte, die er bereits untersucht hat, sowie an die Namen der Zusatzstoffe, deren Bedeutung er erkannt hat, und speichert diese Informationen auf der Festplatte. Dadurch verlässt er sich bei späteren Ausführungen etwas weniger auf das Modell, arbeitet schneller und verhält sich deterministischer. Die Gewichte des Modells ändern sich niemals. Der Agent ist es, der Fähigkeiten ansammelt. Keines der SDKs bietet dies bereits vorgefertigt, was es zu einem nützlichen Werkzeug macht, um zu erkennen, wo die vorgefertigten Lösungen enden und Ihre eigene Ingenieursarbeit beginnt.
Zunächst: Was gilt als Agent?
Ein Modell für Alltagssprache ist kein Agent. Man kann es sich als eine Funktion vorstellen, die Text und Bilder entgegennimmt und wieder Text zurückgibt. Zwischen den Aufrufen wird nichts übertragen, es kann keinen Code ausführen oder eine Seite abrufen, und es kann sich nicht einmal selbst erneut aufrufen. Geben Sie ihm ein Nährwertetikett, und es wird Ihnen erklären, wie es vorgehen würde, um es zu untersuchen – doch die Untersuchung findet niemals statt.
Ein Agent hingegen ist ein Modell, das in einer Schleife mit Werkzeugen arbeitet. Das Muster ändert sich nie. Man gibt dem Modell ein Ziel sowie eine Liste der Werkzeuge, die es verwenden darf. Es antwortet entweder mit einem endgültigen Ergebnis oder mit einer Anfrage, ein Werkzeug aufzurufen, wie zum Beispiel „E211 abfragen“. Wenn es nach einem Werkzeug fragt, wird dieses ausgeführt und das Ergebnis zurückgegeben, woraufhin das Modell erneut vorgeht. Der Kreislauf wiederholt sich, bis eine endgültige Antwort vorliegt.
Dieser Kreislauf ist die gesamte Idee: Das Modell trifft eine Entscheidung, ein Tool wird ausgeführt, das Ergebnis kehrt zurück, das Modell trifft erneut eine Entscheidung. Unser Untersucher durchläuft den Prozess mehrmals – einmal zur Produktsuche, einmal für jedes Zusatzstoff, anschließend zur Überprüfung der „Gesünderen Wahl“, zur Bewertung sowie zur endgültigen Synthese. Bei jedem Schritt wählt das Modell seinen nächsten Schritt anhand dessen, was es gerade gelernt hat. Das macht das System handlungsfähig – und erklärt, warum eine einzige API-Aufruf, der in einer for-Schleife eingebettet ist, nicht ausreicht.
Achten Sie darauf, was das Modell tatsächlich ausgibt: nur „Ich möchte check_sfa_additive mit diesen Argumenten aufrufen.“ Etwas anderes muss diesen Wunsch in einen echten Funktionsaufruf umwandeln und alles in Bewegung halten. Das ist die Steuerungseinheit, und sie enthält mehr unscheinbare Mechanismen, als die meisten Menschen erwarten. Sie lässt sich in drei Schichten einteilen. Eine Entwicklungs‑Schicht ist der Ort, an dem man den Agenten erstellt. Eine Laufzeit‑Schicht ist der Ort, an dem man einen Agenten in der Produktion betreibt. Eine Steuerungsplattform ist der Ort, an dem man eine ganze Flotte steuert. Die Entwicklungs‑Schicht ist es, die den Kreislauf überhaupt in Gang setzt, und dort befinden sich auch diese SDKs.
Beginnen Sie mit der Entwicklungsschicht, in der ein einzelner Agent erstellt wird. Sie besteht aus dem eigentlichen Schleifenmechanismus (Modell aufrufen, Tool-Aufrufe erkennen, solange wiederholen, bis die Aufgabe abgeschlossen ist, oder bei Erreichen einer Turnanzahl oder eines Budgets sicher abbrechen) sowie der Persona, also dem Systemprompt, der dem Modell mitteilt, wer es ist und wie es handeln soll. Um diese Elemente herum befinden sich die Tool-Schemata, die jedes Tool im vom Modell erwarteten Format beschreiben, sowie die Tool-Ausführung, die tatsächlich Ihre Funktionen ausführt – sei es über einen MCP-Server oder eingebaute Funktionen wie das Lesen von Dateien – und dabei die Ausgabe erfasst. Ein MCP-Client ermöglicht es dem Agenten, auf Server Dritter zuzugreifen. Die Kontextverwaltung speichert den laufenden Transkriptinhalt, kümmert sich um Bilder und kürzt diesen, wenn er zu lang wird. Die Anbindung an Anbieter umfasst die Authentifizierung, Endpunkte sowie die Anfrageformate für einen bestimmten Anbieter. Darüber hinaus gibt es strukturierte Ausgaben, die die Endlösung an eine feste Form anpassen und überprüfen, sowie mehrere-Agenten-Arbeitsabläufe, die Agenten nacheinander, parallel oder in Schleifen miteinander verknüpfen.
Die SDKs machen all das einfach. Vom Ausführen eines Skripts zum Betreiben eines Services übergeht man, wobei die Runtime-Schicht sowie eine längere Liste von Anforderungen zum Vorschein kommen. Der Agent benötigt eine gehostete Ausführung, d.h. einen Endpunkt mit Sitzungsisolation, den andere Systeme aufrufen können. Zudem braucht er ein Tool-Gateway sowie Mechanismen für Wiederholungsversuche, Zeitlimits und das Verwalten von Zugangsdaten, damit Anrufe an Tools auch dann zuverlässig bleiben, wenn eine übergeordnete API ausfällt. Ebenso ist verwaltete Speicherung erforderlich, damit Sitzungs- und Langzeitdaten in einem Speicher gespeichert werden, der auch nach Beendigung des Prozesses erhalten bleibt und zwischen Instanzen geteilt werden kann. Der Agent benötigt außerdem eine Arbeitslastidentität, die bei der Aufrufung von Tools vorgelegt wird, damit diese Anrufe nicht mit jemandes persönlicher Schlüssel verwendet werden. Schließlich ist Observabilität notwendig, mit Spuren und Zeitintervallen, die zeigen, welche Tools welche Aktionen ausgelöst haben und warum. Fügen Sie einen Sandbox für den eigenen Code des Agents oder Browser-Tools hinzu, langlebige Schleifen, die nach einem Absturz von dem letzten abgeschlossenen Schritt weitermachen, sowie Skalierung durch Konkurrenz und Automatisches Skalieren hinter dem Endpunkt.
Unter diesem befindet sich die Control Plane, die mehrere Agenten gleichzeitig steuert. Ein Registry-System ermöglicht es, Agenten zur Laufzeit zu veröffentlichen und zu entdecken, anstatt sie im Code miteinander zu verbinden. Jeder Agent benötigt eine Identität als eigenständige, nachvollziehbare Einheit im Verzeichnis sowie eine streng durchgesetzte Berechtigungsverwaltung, die nicht einfach angenommen wird. Im Nachhinein benötigt man ein Audit- und Konformitätsprotokoll, das zeigt, was die Agentengruppe getan hat, eine Datenverwaltung, die festlegt, welche gemeinsamen Speicherbereiche verwendet und welche Informationen freigegeben werden dürfen, sowie Kosteneinschränkungen und Quoten mit Budgets und Geschwindigkeitsbeschränkungen. Die Agenten können über ein offenes Protokoll wie A2A miteinander kommunizieren. Da die Ausgabe der Tools direkt in das Modell fließt, sind Schutzmechanismen und Abwehrmaßnahmen gegen Manipulationen wichtig, und die Bewertung zeigt an, ob die Antworten korrekt sind – und nicht nur, ob die Ausführung abgeschlossen wurde. Ein Registry für Tools und MCP-Server kümmert sich um Katalogisierung, Versionierung, Auffindbarkeit, Pools sowie Authentifizierung pro Server. Die schnelle Versionierung ermöglicht es, Prompts zu fixieren, zu vergleichen und rückgängig zu machen, damit die Ausführungen reproduzierbar bleiben. Kurz gesagt: Jedes dieser Kits ist ein fertig konfiguriertes System. Sowohl Claude, OpenAI als auch Google stellen den gesamten Ablauf, die Tool-Infrastruktur, die Kontextverwaltung sowie die Anbindung an Anbieter bereit, sodass Sie sich auf die interessanten Teile konzentrieren können – Ihre Tools, Ihre Prompts sowie die Form Ihres Ausgangstextes. ADK ist dabei der ungewöhnliche Name, erfüllt jedoch dieselbe Funktion; daher umfassen die unten genannten „SDKs“ im Großen und Ganzen alle drei.
Es gibt Meinungsverschiedenheiten darüber, welche Komponenten des Toolssets bereitgestellt werden, wie eigensinnig jede Komponente ist und wo das Toolset einfach endet und Ihr Code übernehmen muss. Die klarste Art, dies konkret zu machen, ist, in jedem Fall einen Agenten zu entwickeln. Sobald alle drei vorhanden sind, kann das vollständige Toolset strukturiert werden und angezeigt werden, was jedes Kit liefert und was es Ihnen überlässt.
Der getestete Agent: ein Untersucher von Nährwertangaben
Ein fairer Test muss diese Kits durch die Aufgaben führen, für die sie bestimmt sind: einen logischen Ablauf über mehrere Tools, wobei jeder nächste Schritt von den Ergebnissen des vorherigen abhängt. Ein einziger Modellaufruf innerhalb eines Frameworks zählt nicht. Der Untersucher erledigt die eigentliche Arbeit. Wenn man ihm ein Foto einer Nährwertangabe zeigt, führt er fünf Schritte durch.
- Es liest die Etikette mithilfe der eigenen KI, um den Produktnamen, die vollständige Liste der Zutaten und Zusatzstoffe, den Zuckergehalt sowie den gesättigten Fettgehalt pro 100 ml sowie herauszufinden, ob es sich um ein Getränk handelt. Das Foto ist die einzige zuverlässige Quelle, da es keine Produktdatenbank gibt, auf die man zurückgreifen kann.
- Für jeden gefundenen Zusatzstoff, egal ob auf der Etikette
E471oder „Soy Lecithin“ steht, prüft es die von der SFA in Singapur festgelegte Liste der zulässigen Zusatzstoffe und gibt den Status sowie ggf. Warnungen für Verbraucher an. - Es durchsucht das HPB-Datensatz auf data.gov.sg, um herauszufinden, ob das Produkt das Healthier Choice Symbol trägt.
- Bei Getränken ermittelt es auf Basis von Zuckergehalt und gesättigtem Fettgehalt die erwartete Nutri-Grade-Einstufung A/B/C/D.
- Es fasst alles zu einem differenzierten Urteil zusammen – GRÜN, AMBER oder ROT – wobei für jede Erkenntnis die jeweilige Quelle wie Tool oder Etikette angegeben wird und die Begründung der KI zuerst dargestellt wird.
Es beginnt auch nicht jedes Mal von Null aus. Es erinnert sich an das, was es bereits gesehen hat, und lernt im Laufe der Zeit – daher stehen sowohl ein gestern untersuchtes Produkt als auch der Name eines Zusatzstoffs, den es einmal nicht zuordnen konnte, beim nächsten Mal zur Verfügung. Mehr dazu später.
Das Testumfeld bietet somit einen variabel langen Tool-Loop, eine echte Datenquelle aus Singapur, Bildeingaben, ein Urteil, das man in einem Supermarktregal überprüfen kann, sowie einen Agenten, der bei jedem Lauf etwas besser wird.
Die Gerechtigkeit des Vergleichs gewährleisten
Alles, was nicht zum SDK gehört, musste unverändert bleiben. Alle drei Demos rufen einen LiteLLM-Proxy auf – ein mit OpenAI kompatibles Gateway, das außerdem einen Anthropic /v1/messages-Endpunkt für das Claude SDK bereitstellt. Eine einzige Wurzel-Datei .env enthält den Schlüssel, die Basis-URL sowie den Modellnamen für jedes Modell, wobei es sich um Claude Sonnet 4.6, GPT-5.4 und Gemini 3.5 Flash handelt. Die Art und Weise, wie jedes SDK auf diesen Proxy verweist, verrät viel über seine Konfiguration.
Jede Demo befindet sich in ihrem eigenen uv-Projekt, sodass Sie eine ausführen können, ohne die Abhängigkeiten der anderen installieren zu müssen. Die SFA-Additivtabelle wurde einmalig aus dem von Singapurs Lebensmittelbehörde veröffentlichten PDF „Permitted-Additives“ erstellt und ergibt sfa_additives.json (etwa 540 Einträge, davon etwa 270 mit E-Nummern), bevor sie unverändert in jedes Projekt kopiert wurde. Auch die Testbilder waren identisch: zwei echte Produkte aus Singapur, ein Milo 3in1-Beutel und eine Marigold HL-Milchpackung.
Das lässt nur noch das übrig, was tatsächlich eine Entscheidung des SDKs ist: wie ein Tool definiert wird, wie ein Bild eingefügt wird, wie das Modell konfiguriert wird, ob typisierte Ausgaben vorhanden sind und wie man sieht, was passiert ist.
Eine Hinweis vor dem Code: Die untenstehenden Beispiele enthalten ein recall_product-Tool sowie ein e_number_hint-Argument. Sie sind für die im weiteren Verlauf erläuterte Merkfähigkeit und Selbstverbesserung verantwortlich, daher können Sie sie vorerst ignorieren.
Anthropics Claude Agent SDK
Das Claude Agent SDK basiert direkt auf Claude Code – dem Agenten, den viele Menschen bereits in einer Terminalumgebung nutzen – und diese Entscheidung prägt alles. Sie bauen keinen Loop aus Primitiven zusammen, sondern steuern einen vollständigen, erprobten Agenten und stellen ihm Ihre Tools sowie Anweisungen zur Verfügung. Integrierte Tools (Read, Write, Edit, Bash, WebFetch) sind bereits vorhanden, das Berechtigungsmodell sowie die Kontextverwaltung sind bereits gelöst, und Ihre eigenen Tools werden über einen MCP-Server hinzugefügt, der innerhalb Ihres eigenen Prozesses läuft. Alles läuft asynchron über anyio.
Was Sie kaufen, ist ein echter Computer-Agent, der sofort einsatzbereit ist. Alles, was Dateien lesen, Shell-Befehle ausführen, Quellcode bearbeiten oder im Internet surfen muss, wird bereits am ersten Tag in einer Produktionsqualität bereitgestellt – schließlich handelt es sich um dieselbe Technologie, die auch Claude Code antreibt. MCP genießt einen ersten Klasse-Status, sodass ein von Ihnen entwickeltes benutzerdefiniertes Tool unverändert in Claude Code, in Claude Desktop sowie in Ihrer eigenen Anwendung funktioniert. Von den drei Optionen verfügt diese über den am weitesten entwickelten Agent-Runtime.
Die Kosten entstehen durch die Tools, in denen Sie den Namen, die Beschreibung sowie das Schema manuell eingeben müssen:
@tool(
"check_sfa_additive",
"Check whether a food additive is permitted by the Singapore Food Agency, "
"using the full SFA permitted-additives list parsed from the official PDF. "
"Accepts an E-number ('E211') OR a name ('Sodium Benzoate', 'Soy Lecithin'). "
"If you pass a name and also know its E/INS number, set e_number_hint — an "
"unrecognised name that the hint resolves is REMEMBERED for next time.",
{"additive": str, "e_number_hint": str},
)
async def check_sfa_additive(args: dict) -> dict:
raw = args["additive"].strip()
hint = args.get("e_number_hint", "").strip()
entry = _resolve_additive(raw)
if not entry and hint:
entry = _resolve_additive(hint)
if entry: # learn: this label name -> this number, persisted to disk
num = entry["e_number"] or entry.get("ins") or ""
_MEMORY["learned_aliases"][_norm(raw)] = re.sub(r"\(.*\)", "", num).lower()
_save_memory(_MEMORY)
return _ok(_format_entry(entry, raw))
Dieses {arg: type}-Dictionary erfordert mehr Eingabezeit als die anderen beiden Kits, lässt aber keinen Spielraum für Unklarheiten. Die Tools werden anschließend über einen in-Prozess-MCP-Server bereitgestellt, und das Modell bezieht sich auf sie mit dem Präfix mcp__<server>__<tool>, das Sie beim Ausfüllen von allowed_tools berücksichtigen müssen:
server = create_sdk_mcp_server(
name="sg-nutrition-tools",
version="1.0.0",
tools=[recall_product, check_sfa_additive, check_hcs, calculate_nutri_grade],
)
options = ClaudeAgentOptions(
system_prompt=SYSTEM_PROMPT,
mcp_servers={"sg": server},
allowed_tools=[
"mcp__sg__recall_product",
"mcp__sg__check_sfa_additive",
"mcp__sg__check_hcs",
"mcp__sg__calculate_nutri_grade",
],
model=CLAUDE_MODEL,
permission_mode="bypassPermissions",
max_turns=20,
# Point the bundled CLI at the LiteLLM proxy's Anthropic endpoint.
env={
**os.environ,
"ANTHROPIC_BASE_URL": BASE_URL,
"ANTHROPIC_AUTH_TOKEN": API_KEY,
},
)
Bilder werden auf dieselbe selbstgemachte Weise eingefügt. Es gibt kein image=-Argument. query() akzeptiert ein asynchrones Iterierbares aus Nachrichtendictionaries, sodass Sie die rohen, von Claude verwendeten Inhaltsteile selbst erstellen – einen Textblock zusammen mit einem Bildblock:
async def message_stream():
yield {
"type": "user",
"message": {"role": "user", "content": [
{"type": "text", "text": "Investigate this nutrition label."},
{"type": "image", "source": {"type": "base64", "media_type": mime, "data": b64}},
]},
}
async with ClaudeSDKClient(options=options) as client:
await client.query(message_stream())
async for message in client.receive_response():
... # stream ToolUseBlock / TextBlock as they arrive
Die scharfe Kante ist die Modellverkabelung. Das SDK steuert die mitgelieferte Claude Code CLI, die das Nachrichtenformat von Anthropic verwendet, weshalb Sie es über Umgebungsvariablen (ANTHROPIC_BASE_URL und ANTHROPIC_AUTH_TOKEN) an den Proxy weiterleiten. Es gibt kein Client-Objekt, das übergeben werden muss. Der Vorteil ist eine vollständig sichtbare Schleife: Jeder ToolUseBlock wird beim Eintreffen ausgegeben, sodass Sie den Prozess im Echtzeit-Modus verfolgen können. Hier ist ein Ausführungsbeispiel mit dem Milo-Sachet:
sausheong claude-demo % uv run python main.py ../milo.jpeg
Investigating image: ../milo.jpeg
────────────────────────────────────────────────────────────
I'll start by reading the label carefully, then run all checks in parallel.
**Reading from the label:**
- **Product:** Nestlé MILO 3in1
- **Net weight:** 27g powder sachet (add hot water to prepare — this is a **powder, not a ready-to-drink beverage**)
- **Ingredients:** Sugar, PROTOMAL® Malt Extract (Contains Barley), Skimmed Milk (Cow's Milk), Cocoa, Palm Oil, Minerals (Dicalcium Phosphate, Disodium Phosphate, Ferric Pyrophosphate), Resistant Dextrin, Flavourings, Vitamins (C, B3, D, B6, B2, B12)
- **Identifiable additives:** Dicalcium Phosphate (E341), Disodium Phosphate (E339), Ferric Pyrophosphate; Flavourings (generic — cannot identify specific ones)
Kicking off recall + HCS check + all additive checks simultaneously:
→ tool: recall_product({'product_name': 'Nestlé MILO 3in1'})
→ tool: check_hcs({'product_name': 'MILO 3in1'})
→ tool: check_sfa_additive({'additive': 'Dicalcium Phosphate', 'e_number_hint': 'E341'})
→ tool: check_sfa_additive({'additive': 'Disodium Phosphate', 'e_number_hint': 'E339'})
→ tool: check_sfa_additive({'additive': 'Ferric Pyrophosphate', 'e_number_hint': 'E450'})
All checks returned. Note: this is a **powder**, not a ready-to-drink beverage, so Nutri-Grade (which requires per-100ml liquid values) does **not apply** — I will skip `calculate_nutri_grade`.
---
## Reasoning
**Step 1 — Product recall.** This product has been seen before. The prior verdict is consistent with what I read now — I'll treat this fresh investigation as the authoritative one and use the prior as a cross-check.
**Step 2 — Label reading.**
- MILO 3in1 is a **27g powder sachet** — you add hot water to make a drink. It is classified as a "Tonic Food Drink" on the label. Because it is sold as a dry powder (not a pre-packaged ready-to-drink liquid), Nutri-Grade per-100ml calculation does not apply.
- Nutritional values per serving (27g): Energy 110 kcal, Total Fat ~2.4g, Saturated Fat ~1.2g, Total Sugars ~13.8g (of which Added Sugars ~10g), Sodium ~38mg.
- **Sugar is the #1 ingredient** — it appears first in the ingredient list, before the malt extract.
**Step 3 — Additives.**
- **Dicalcium Phosphate (E341):** SFA-permitted ✅ — used as a mineral/calcium source.
- **Disodium Phosphate (E339):** SFA-permitted ✅ — used as an acidity regulator/mineral.
- **Ferric Pyrophosphate:** SFA-permitted ✅ — used as an iron supplement/mineral source.
- **Resistant Dextrin:** A dietary fibre ingredient (prebiotic fibre), not a regulated additive — no E number, no SFA check needed.
- **Flavourings:** Generic label — cannot identify specific flavouring compounds; SFA check not possible.
- **Vitamins (C, B3, D, B6, B2, B12):** Added as nutritional fortification, not food additives in the regulatory sense. Riboflavin (B2) carries E101 when used as a colourant, but here it's explicitly declared as a vitamin supplement — standard and expected.
**Step 4 — Healthier Choice Symbol.** HCS certified ✅ (MILO 3in1 Activ-Go appears multiple times in the HPB dataset — `check_hcs`).
**Step 5 — Sugar content reality check.** Each 27g sachet contains ~13.8g total sugars (~10g added sugars). That's more than **half the sachet's weight is sugar**, and exceeds the WHO daily added-sugar guideline of 25g in less than one cup. This is label-read, not a tool finding.
---
## Verdict
### 🟢 GREEN — No Concern
- **All three declared additives are SFA-permitted** (Dicalcium Phosphate E341, Disodium Phosphate E339, Ferric Pyrophosphate) — `check_sfa_additive` ×3.
- **Carries the Healthier Choice Symbol** — HPB has certified MILO 3in1 Activ-Go — `check_hcs`.
- **Vitamins and minerals fortified** — B2, B3, B6, B12, C, D, plus calcium and iron — label.
- **Contains barley malt extract** — a whole-grain source of energy — label.
- **Resistant Dextrin** (prebiotic dietary fibre) is a functional ingredient, not a concern — label.
### 🟡 AMBER — Worth Knowing
- **Palm Oil present** — a saturated fat source. Saturated fat is 1.2g per sachet (~4.4g per 100g of powder). Not alarming at one serving, but worth knowing if you have multiple cups a day — label.
- **"Flavourings" are not specified** — cannot determine whether natural or artificial. No SFA check was possible — label.
- **Nutri-Grade does not apply here** — this is sold as a powder. If you reconstitute it and compare the drink to ready-to-drink alternatives, it would likely land in Grade C or D territory based on sugar content, but no regulatory grade is required on the packet.
### 🔴 RED — Genuinely Concerning
- **Sugar is the #1 ingredient** — it appears first in the list, meaning it is the dominant ingredient by weight. Each sachet delivers **~13.8g total sugars / ~10g added sugars** — label. That's 40% of the WHO's recommended 25g/day added-sugar limit in a single cup. If you're drinking 2–3 sachets a day (as Nestlé's own label suggests for daily energy), you are consuming **20–30g of added sugar from MILO alone**, likely exceeding the daily limit.
- **Marketing framing vs. nutritional reality:** The packet prominently says "Nourishing Energy" and shows vitamins, but the sugar load is the dominant nutritional feature. The fortification (vitamins, minerals) does not offset this — label.
**Bottom line for a Singapore shopper:** MILO 3in1 is safe from an additives and regulatory standpoint — everything checks out with SFA and HPB. But it is a **sugar-heavy product** dressed up in health language. Treat it as an occasional treat rather than a daily nutritious breakfast drink, especially for children.
Wo es Probleme gibt. Es gibt keine eingebauten, strukturierten Ausgaben, sodass man mit Text arbeiten muss, den man entweder selbst analysieren oder gezielt nach der gewünschten Struktur fragen muss – das ist der größte Alltagsschied im Vergleich zum OpenAI SDK. Die Schemata werden manuell erstellt, wodurch für jede Funktion mehr zu schreiben und im Einklang zu halten ist. Die Verknüpfung der Modelle erfolgt über die Umgebung, und der Proxy, auf den man abzielt, muss einen Anthropic-formatierten /v1/messages-Endpunkt anbieten, denn ein alleiniger OpenAI-kompatibler Endpunkt reicht nicht aus. Betrachten Sie die allowed_tools-Liste als vorherige Freigabe und niemals als Sandbox: Sie bedeutet „Überspringen Sie die Bestätigungsanfrage für diese Tools“, nicht „Der Agent darf nur diese Tools sehen“, und Neulinge, die sie als Whitelist verstehen, werden überrascht sein. Vergessen Sie das mcp__sg__-Präfix – dann bleibt das Tool einfach ungenehmigt, ohne Fehlermeldung. Der Ressourcenverbrauch ist außerdem höher, da im Hintergrund der Claude Code-Runtime gestartet wird und es sich nicht um einen reinen API-Aufruf handelt.
Wählen Sie diese Option, wenn der Agent eine Maschine steuern muss. Das bedeutet das Lesen und Schreiben von Dateien, das Ausführen von Shell-Befehlen, das Bearbeiten einer Codebasis oder das Surfen – jede Aufgabe, bei der die integrierten Claude Code-Tools Sie davon befreien, einen Sandbox selbst aufzubauen. Es ist auch die natürliche Wahl, wenn Sie bereits mit MCP arbeiten oder etwas erstellen, das wie Claude Code wirken soll. Der Streaming-Zyklus ist leicht zu verfolgen, was für langlaufende Agenten vorteilhaft ist.
Wählen Sie es, wenn Sie einen schlanken, programmierten Funktionsaufruf-Loop benötigen, der ein strukturiertes Objekt zurückgibt – schließlich ist das der Bereich, in dem das OpenAI SDK am besten geeignet ist. Wählen Sie stattdessen eine andere Lösung, wenn Sie keinen Endpoint im Anthropic-Format bereitstellen können oder wenn ein geringer Abhängigkeitsumfang wichtig ist. Eine einfache Aufgabe wie „Drei Tools aufrufen, ein Pydantic-Objekt zurückgeben“ erfordert weitaus weniger Ressourcen.
OpenAI Agents SDK
OpenAI’s SDK ist eine schlanke, bewusst pythonische Verpackung um den Loop und setzt sich voll und ganz für die Sprache selbst ein. Typhinweise werden zu Tool-Schemata, Docstrings zu Tool-Beschreibungen und Pydantic-Modelle zum Ausgabevertrag. Es gibt keine Vorgaben bezüglich der Projektstruktur, keinen separaten Laufzeitumfeld und keinen Umweg über MCP. Man schreibt Funktionen und Modelle, erstellt einen Agent und gibt ihn an Runner.run weiter. Das Ziel ist der kürzeste Weg zwischen einer Python-Funktion und einem funktionsfähigen Agenten.
Sein größter Vorteil ist die automatische Rückgabe in formatierter Form. Geben Sie output_type=YourModel an – dann erhalten Sie ein validiertes Python-Objekt statt eines Blob-Datens, von dem man annimmt, es sei JSON. Das SDK formuliert die Anfrage, analysiert die Antwort, validiert sie und wendet sich bei Unstimmigkeiten erneut an das Modell. Durch Schichten, die direkt aus Typhinweisen entstehen, sowie ein kostenloses Überwachungsdashboard auf OpenAI’s eigenem Backend ist dies der schnellste Weg zu einem strukturierten, beobachtbaren Agenten. Wenn die Aufgabe darin besteht, „diese Eingabe entgegenzunehmen und mir ein formatiertes Ergebnis zu liefern“, gibt es kein anderes Tool, das so direkt ist.
Die Tools erfordern keinerlei Erstellung von Schichten:
@function_tool
async def check_sfa_additive(additive: str, e_number_hint: str = "") -> str:
"""Check whether a food additive is permitted by the Singapore Food Agency.
Looks the additive up in the full SFA permitted-additives list (parsed from
the official SFA PDF). Accepts either an E-number or a plain-English name as
printed on a Singapore label.
Args:
additive: An E-number ('E211', 'e211', 'en:e211') OR an additive name
('Sodium Benzoate', 'Soy Lecithin', 'MSG').
e_number_hint: Optional. If you pass a name and also know its E/INS
number, supply it here. If the name isn't recognised but the number
is, the mapping is REMEMBERED so the name resolves directly next time. """
...
Der Dekorator liest die Typhinweise sowie den Args-Abschnitt der Dokumentation und erstellt das JSON-Schema, sodass nichts mehr manuell gepflegt werden muss.
Am meisten bringt sich das bei der Ausgabe zum Tragen. Das Ergebnis wird als Pydantic-Modell deklariert:
class NutritionVerdict(BaseModel):
reasoning: str # declared first: the model works out loud before committing
product_name: str
summary: str
green: list[Finding]
amber: list[Finding]
red: list[Finding]
recommendation: str
Mit output_type=NutritionVerdict gibt eine Ausführung ein validiertes Objekt zurück – ohne Parsing-Schritt und ohne Risiko. Es ist ein cleverer Trick, reasoning zuerst anzugeben: Die strukturierte Ausgabe dient gleichzeitig als Notizblock, sodass das Modell laut überlegt, bevor es zu Ergebnissen kommt, und Sie das ordentliche Objekt zusammen mit der Begründung beibehalten.
Die Verkabelung des Modells sowie die Eingabe von Bildern sind von den dreien am übersichtlichsten:
client = AsyncOpenAI(base_url=BASE_URL, api_key=API_KEY)
model = OpenAIChatCompletionsModel(model=OPENAI_MODEL, openai_client=client)
agent = Agent(name="SG Nutrition Investigator", model=model,
instructions=INSTRUCTIONS, output_type=NutritionVerdict,
tools=[recall_product, check_sfa_additive, check_hcs, calculate_nutri_grade])
user_input = [{"role": "user", "content": [
{"type": "input_text", "text": "Investigate this nutrition label."},
{"type": "input_image", "image_url": _data_url(image_path)}, # base64 data URL
]}]
result = await Runner.run(agent, input=user_input)
verdict: NutritionVerdict = result.final_output
Das ist ein AsyncOpenAI-Client, der für den Proxy konzipiert ist und in OpenAIChatCompletionsModel eingebettet ist; dabei wird ein input_image-Element verwendet, um das Foto zu übertragen. Keine Komplikationen.
Achtungspunkte. Toolaufrufe werden standardmäßig parallel ausgeführt, sodass das Modell gerne mehrere check_sfa_additive-Aufrufe gleichzeitig startet. Unsere Aufrufe sind asynchron und haben keine Nebeneffekte, was ein Vorteil ist. Tools, die den gemeinsamen Zustand verändern, eine nicht-reentranten Verbindung nutzen oder von einer bestimmten Reihenfolge abhängen, verhalten sich falsch – die Fehler treten intermittierend auf und hängen vom Zeitpunkt ab, was das Schlimmste ist. Entweder machen Sie die Tools konkurrierensicher oder beschränken Sie das Modell.
Das Tracing hat seine eigenen Einschränkungen. Das Dashboard ist hervorragend, doch die Traces werden an OpenAI’s eigenes Eingabepunkt gesendet, und hinter einem Drittanbieter-Proxy kann der vom Proxy ausgestellte Schlüssel dort keine Authentifizierung durchführen. Daher führt set_tracing_disabled(True) dazu, dass Ihr bestes Tool für die Überwachbarkeit mit nur einer Zeile verschwindet. Außerhalb von api.openai.com müssen Sie auf OpenTelemetry oder selbst entwickelte Protokollierungslösungen zurückgreifen. Weitere Probleme treten auf: Hinter einem Proxy benötigen Sie OpenAIChatCompletionsModel, da der standardmäßige Responses-API-Pfad auf OpenAI’s Endpunkte verweist und eine falsche Wahl verwirrende Fehler verursacht. Das output_type-Schema muss den Regeln im strengen Modus entsprechen, wodurch Modelle mit tiefem Verzweigungsgrad oder vielen Unionen zu Wiederholungsversuchen bei der Validierung führen können; aus diesem Grund bleiben Finding und NutritionVerdict einfach strukturiert. Und es liegen keine Tools zum Computergebrauch im Lieferumfang vor, sodass es keinen Dateisystemzugriff, keine Shell oder kein Surfen gibt, es sei denn, Sie fügen sie hinzu.
Hier ist die Ausgabe für das Milo-Säckchen:
sausheong openai-demo % uv run python main.py ../milo.jpeg
Investigating image: ../milo.jpeg
────────────────────────────────────────────────────────────
Product: Nestlé MILO 3 in 1 Activ-Go
Reasoning: I read the sachet as **Nestlé MILO 3 in 1 Activ-Go**. The front says “goodness of malt, milk & cocoa” and the back says “MILO Ionic Food Drink,” so this is a **powdered drink mix**, not a ready-to-drink beverage. Because it is not sold as a beverage with per-100ml nutrition for the prepared drink as consumed, I did **not** calculate Nutri-Grade; that tool is only for beverages.
I first checked whether this product had been investigated before. A prior record existed, and it broadly matched what I can read here: a sweetened powdered malt-cocoa-milk drink sachet with permitted phosphate additives, relatively high in sugar for a single sachet, and not found in the HCS database. I used that only as background and then re-read this label fresh.
From the nutrition panel, I can read:
- Serving size: **1 sachet (27g)**
- Servings per package: **1**
- Per 100g / per serving shown side by side
- **Total sugars: 50.1g per 100g; 13.8g per serving**
- **Saturated fat: 0.5g per 100g; 1.2g per serving**
This is a powder, so the relevant direct reading is per 100g, not per 100ml.
I can read the ingredient list as:
**Sugar, Protomalt™ Malt Extract (contains barley), skimmed milk/cow’s milk, cocoa, palm oil, minerals (dicalcium phosphate, disodium phosphate), fibre (pyrophosphate), resistant dextrin, flavourings, vitamins (C, B3, D, B6, B2, B12).**
For additives, I checked each identifiable additive individually where possible:
- **Dicalcium phosphate**: checked and found **permitted by SFA**. That lands in GREEN.
- **Disodium phosphate**: checked and found **permitted by SFA**. That also lands in GREEN.
- The label also contains the broader terms **“phosphates”** and **“pyrophosphate”/“fibre (pyrophosphate)”** in wording around ingredients, but the image does not clearly identify a specific additive code for the pyrophosphate entry, and the generic umbrella term “phosphates” is not reliable enough on its own. I therefore do not treat the generic term as confirmed permitted just from the broad word alone. I only rely on the specifically named phosphates I could check.
- **Flavourings** is too generic to identify a specific additive, so I could not check it individually.
- The vitamins and minerals are nutrients rather than red-flag food additives in the SFA additive sense.
I also checked for the **Healthier Choice Symbol** using the product name. It was **not found** in the HCS database.
Main nutritional takeaways from the label:
- Sugar is the **first ingredient**, which usually means it is the largest ingredient by weight.
- The product contains **50.1g sugar per 100g** powder, and **13.8g sugar per 27g sachet**. That is quite a lot for one small sachet drink.
- Saturated fat is **1.2g per sachet**, which is not extreme, but the bigger concern here is sugar.
So the overall picture is: the identifiable phosphate additives appear permitted, but nutritionally this is still a **sweetened convenience drink mix** rather than a lower-sugar everyday option.
Summary: This is a powdered MILO drink sachet, not a ready-to-drink beverage. The clearly identifiable phosphate additives on the label are permitted by SFA, but sugar is the first ingredient and the product contains 50.1g sugar per 100g, or 13.8g per 27g sachet. It was not found in the Healthier Choice Symbol database.
🟢 Dicalcium phosphate is permitted by SFA. [check_sfa_additive]
🟢 Disodium phosphate is permitted by SFA. [check_sfa_additive]
🟢 This is a powdered drink mix rather than a ready-to-drink beverage, so Nutri-Grade was not applicable here. [label]
🟡 Generic terms such as “flavourings” and the unclear “fibre (pyrophosphate)” wording are not specific enough to confirm exact additive identities from the image alone. [label]
🟡 The product was not found in the Healthier Choice Symbol database. [check_hcs]
🟡 Saturated fat is 1.2g per 27g sachet (0.5g per 100g listed on the label panel image). [label]
🔴 Sugar is the first ingredient on the label. [label]
🔴 Total sugars are 50.1g per 100g and 13.8g per 27g sachet, which is high for a small single-serve drink mix. [label]
Recommendation: Fine as an occasional sachet drink if convenience matters, but not the best everyday choice if you are trying to reduce sugar. If you drink MILO often, consider using plain MILO powder with your own milk and less sugar, or choose a lower-sugar version.
Wählen Sie es, wenn die Aufgabe in strukturierte Schritte unterteilt ist, wie z. B. Klassifizierung, Extraktion, Bewertung oder Urteilsfindung – überall, wo typisierte Objekte der Prosa vorgezogen werden und Sie möglichst wenig Python verwenden möchten. Teams, die Typhinweise schätzen, werden es genießen, und dank von OpenAI bereitgestelltem Backend macht das kostenlose Tracing das Debuggen eines mehrschrittigen Agents angenehm.
Vermeiden Sie es, wenn Ihre Tools nicht konkurrenzsicher gestaltet werden können und Sie sich nicht mit dem parallelen Standard auseinandersetzen möchten, wenn Sie Überwachungsfunktionen benötigen, aber hinter einem nicht-OpenAI-Gateway arbeiten (dann verschwindet das Tracing), oder wenn der Agent standardmäßig Dateisystem- und Shell-Tools verwenden muss – was in Claudes Bereich fällt. Für mehrere zusammenarbeitende Agents unter explizitem Kontrollfluss eignet sich ADK besser, und das ist das dritte Kit.
Google’s Agent Development Kit (ADK)
Von den dreien ist ADK das, was tatsächlich ein Framework darstellt – mit klaren Vorgaben – während die anderen eher schlicht bleiben. Es bietet Agenten, Sitzungen, einen Runner, einen SessionService sowie explizite Mehr-Agenten-Arbeitsabläufe über SequentialAgent, ParallelAgent und Schleifenagenten. Eine vorgegebene Projektstruktur ermöglicht es den Tools, den jeweiligen Agenten zu finden, und adk web öffnet eine lokale Chat-Schnittstelle mit einem Ereignisinspektor. Die Tools sind einfache Python-Funktionen, die in Google-stillem Docstring-Format dokumentiert sind, ohne Dekoratoren oder Schema-Dictionarys – und der LiteLlm-Wrapper macht es zur optionierten Lösung, die am wenigsten von bestimmten Anbietern abhängt.
Es geht um Architektur für Systeme und nicht um Skripte. Wenn „der Agent“ tatsächlich mehrere Agenten bedeutet – wie beispielsweise einen Planer, der Aufgaben an Arbeiter weiterleitet, eine sequentielle Pipeline oder eine parallele Verteilung – bietet ADK diese als komponierbare, erstklassige Komponenten an und erspart Ihnen einen manuell erstellten Kontrollfluss. Sessions sind bereits integriert, die Debug-Oberfläche ist die beste im Vergleich zu anderen Lösungen, und die an Anbieter ungebundene Modellschicht verhindert, dass Sie an einen bestimmten Anbieter gebunden bleiben. Die Plattform ist dafür konzipiert, zu einer komplexen, zustandsbasierten, mehragentigen Anwendung zu wachsen.
Die Definitionen der Tools sind hier am einfachsten gehalten:
async def check_sfa_additive(additive: str, e_number_hint: str = "") -> dict:
"""Check whether a food additive is permitted under Singapore Food Agency rules.
Looks the additive up in the full SFA permitted-additives list (parsed from
the official SFA PDF). Accepts either an E-number or a plain-English name as
printed on a Singapore label.
Args:
additive: An E-number ('E211', 'e211', 'en:e211') OR an additive name
('Sodium Benzoate', 'Soy Lecithin', 'MSG').
e_number_hint: Optional E/INS number for when you pass a name. If the
name isn't recognised but the number is, the mapping is REMEMBERED
so the name resolves directly next time.
Returns:
dict with the input, resolved e_number/name, permitted status, schedule
and any consumer note. found=False if it is not in the SFA list.
"""
...
Das Schema wird auf der Grundlage jener Google-ähnlichen Args- und Returns-Docstrings erstellt, wodurch ihre Qualität große Bedeutung hat. Eine vage Docstring schadet direkt der Werkzeugauswahl des Modells, weshalb die Docstring der eigentliche Vertrag darstellt.
Damit adk web Ihren root_agent finden kann, benötigt das Projekt eine bestimmte Paketstruktur:
adk-demo/
sg_nutrition_investigator/
__init__.py # from . import agent
agent.py # tools + root_agent
main.py # standalone Runner + InMemorySessionService
root_agent = LlmAgent(
name="sg_nutrition_investigator",
model=LiteLlm(model="openai/" + GEMINI_MODEL, api_base=BASE_URL, api_key=API_KEY),
instruction=INSTRUCTION,
tools=[FunctionTool(recall_product), FunctionTool(check_sfa_additive),
FunctionTool(check_hcs), FunctionTool(calculate_nutri_grade)],
)
# image input — the cleanest of the three:
message = types.Content(role="user", parts=[
types.Part(text="Investigate this nutrition label."),
types.Part.from_bytes(data=Path(image_path).read_bytes(), mime_type=mime),
])
Das Modell wird über ADKs LiteLlm-Wrapper geleitet, wobei ein openai/-Präfix dazu führt, dass LiteLlm das mit OpenAI kompatible Protokoll zum Proxy verwendet. Das Übergeben von Bildern mit types.Part.from_bytes ist die beste Methode der drei, und adk web bietet in diesem Vergleich die beste integrierte Entwicklungsumgebung.
Achtungspunkte. Am meisten betrifft dies das Gewicht. Außerhalb von adk web muss man vor dem Versenden der ersten Nachricht einen Runner, einen SessionService sowie explizite IDs für Anwendung, Benutzer und Sitzung einbinden – das bedeutet viele zusätzliche Schritte im Vergleich zu den wenigen Zeilen, die andere Lösungen benötigen. Auch die Struktur spielt eine entscheidende Rolle. Die Zeile from . import agent in __init__.py ist der Weg, auf dem adk web root_agent entdeckt, und jede Abweichung von dieser Struktur stört die Entdeckung. litellm erzeugt Warnungen für nicht genutzte Backend-Dienste (unschädliche Meldungen zu Bedrock und Sagemaker sowie „no module named botocore“-Warnungen), während ADK eine [EXPERIMENTAL]-Schema-Warnung hinzufügt – dadurch müssen beide bei der Importierung ausgeschaltet werden. Eine strukturierte Ausgabe ist außerdem aufwendiger, da Geminis response_schema hinter einem Proxy empfindlicher ist als OpenAIs output_type; deshalb verwendet diese Demo eine textbasierte Bewertung auf Basis einer Anfrage. Ehrlich gesagt liegt das Problem genauso sehr am Proxy wie am ADK. Noch ein Punkt: Auf dem LiteLlm zusammen mit dem Gemini-Flash-Path schilderte der Agent bei derselben Anfrage weniger aus seinen Erinnerungen, was zeigt, dass sich das Verhalten vom Modell und der Implementierung abhängt – nicht nur vom SDK.
Die Ausgabe für das Milo-Sachet:
sausheong adk-demo % uv run python main.py ../milo.jpeg
Investigating image: ../milo.jpeg
────────────────────────────────────────────────────────────
→ tool: recall_product({'product_name': 'Nestle MILO 3 in 1'})
→ tool: check_sfa_additive({'e_number_hint': 'E341', 'additive': 'Dicalcium Phosphate'})
→ tool: check_sfa_additive({'additive': 'Disodium Phosphate', 'e_number_hint': 'E339'})
→ tool: check_sfa_additive({'additive': 'Ferric Pyrophosphate'})
→ tool: check_hcs({'product_name': 'Nestle MILO 3 in 1'})
→ tool: calculate_nutri_grade({'saturated_fat_per_100ml': 0.5, 'sugar_per_100ml': 6.1})
## Reasoning
We investigated the label of **Nestle MILO 3 in 1 (with Activ-Go)**. Here is our step-by-step analysis:
1. **Product Categorisation & Reconstitution**:
* This is a dry beverage powder sold in single-serving 27g sachets.
* The nutritional information panel provides two columns: "Per Serving 27g" (the dry sachet) and "Per 100ml*" (as reconstituted by adding 200ml of hot water).
* For the **Singapore Nutri-Grade** system, the grading is calculated based on the beverage *as reconstituted* according to the directions.
2. **Sugar & Saturated Fat Analysis (Per 100ml prepared)**:
* **Total Sugars**: **6.1g per 100ml** (of which 4.8g is Added Sugar).
* **Saturated Fat**: **0.5g per 100ml**.
* Feeding these values into the Nutri-Grade calculator yields **Nutri-Grade C** (due to the sugar content being > 5g/100ml). Grade C beverages are classified as "less healthy" and must display the Nutri-Grade label at retail.
3. **Ingredients & Additives Analysis**:
* **Sugar is the #1 Ingredient**: Refined sugar is listed first on the ingredients list, meaning it is the largest ingredient by weight in this packet—even ahead of malt extract and milk powder.
* **Additives**:
* **Dicalcium Phosphate** (E341(ii)) and **Disodium Phosphate** (E339(ii)) are SFA-approved food additives used as stabilizers and emulsifiers.
* **Ferric Pyrophosphate** is an SFA-approved mineral nutrient used to fortify the drink with Iron.
* **Healthier Choice Symbol (HCS)**: The packet displays the red HCS logo. This is because it is formulated to be "25% lower in sugar" *compared to regular malted beverages*. However, in absolute terms, it still carries enough sugar to land in Nutri-Grade C.
---
## Verdict
### 🟢 Green Findings
* **Fortified with Vitamins & Minerals** `[label]`: Contains B-vitamins (B2, B3, B6, B12), Vitamin C, Vitamin D, Calcium, and Iron (via *Ferric Pyrophosphate* `[check_sfa_additive]`).
* **Contains Soluble Fibre** `[label]`: Features *Resistant Dextrin*, a prebiotic soluble dietary fibre (1.1g per serving) that supports gut health.
* **SFA-Approved Additives** `[check_sfa_additive]`: Phosphates used for stabilizing and emulsifying (*Dicalcium Phosphate* and *Disodium Phosphate*) are fully permitted.
* **Healthier Choice Certified** `[check_hcs]`: Carries Singapore's HCS logo for having at least 25% less sugar than standard malted chocolate drinks.
### 🟡 Amber Findings
* **Nutri-Grade C** `[calculate_nutri_grade]`: When prepared as instructed, it gets a **Grade C** because of its **6.1g of sugar per 100ml**. Grade C drinks are in the "less healthy" bracket.
* **Contains Palm Oil** `[label]`: Palm oil is added to give a rich mouthfeel, contributing **1.2g of saturated fat per serving** (0.5g/100ml).
### 🔴 Red Findings
* **Sugar is the Main Ingredient** `[label]`: Sugar is listed as the very first ingredient on the label, which means there is more plain sugar by weight than malt extract (*PROTOMALT*) or milk powder.
* **High Sugar Load per Cup** `[label]`: A single small 27g sachet contains **13.8g of total sugar** (equivalent to about **3 teaspoons of sugar**), of which **10.8g** (approx. 2.5 teaspoons) is added refined sugar. Drinking multiple cups a day can quickly max out your recommended daily limit for added sugars.
Wählen Sie ADK, wenn Sie ein System entwickeln: mehrere zusammenarbeitende Agenten, explizite sequenzielle, parallele oder schleifenbasierte Arbeitsabläufe, echter Sitzungszustand sowie die Möglichkeit, Modelle zwischen verschiedenen Anbietern auszutauschen. Der adk web Inspector amortisiert sich bereits während der Entwicklung, und die Architektur zahlt sich durch die weitere Erweiterung des Projekts aus.
Vermeiden Sie es, wenn es nur einen einfachen Agenten gibt. Der Runner sowie die Standardvorlagen für Sitzungen und das vorgegebene Layout stellen Overhead dar, den Sie niemals wieder hereinholen können. Für das schnellste Ergebnis durch Eingabe verwenden Sie das OpenAI SDK, und für einen computergestützten Agenten mit integrierten Tools das von Claude. ADK rechtfertigt seine Komplexität erst in größeren Skalen – darunter ist es mehr Framework, als Sie benötigen.
Ausführung der Demos
Sobald alle drei Agenten erstellt sind, zeigt dies, wie man sie ausführt. Jeder ist ein uv-Projekt und liest die Wurzeldatei .env ein, die die Proxy-Keys, die Basis-URL sowie die Namen der drei Modelle enthält:
cd openai-demo # or claude-demo, or adk-demo
uv run python main.py ../milo.jpeg
uv run python main.py ../hl.jpeg
Um die SFA-Tabelle aus dem PDF neu zu erzeugen, führen Sie uv run --with pdfplumber python build_additives.py aus. Geben Sie dabei den Pfad zu Ihrem eigenen Etikettenfoto an. Die beiden mitgelieferten Etiketten sind echte Produkte aus Singapur und erzielen die erwarteten Bewertungen: Die Milch erhält die Bewertung Nutri-Grade B, während Milo als Pulver nach der Zubereitung die Bewertung C erhält.
Mehr als nur eine einzige Ausführung
Eine Demo, die nur eine saubere Ausführung zeigt, verbirgt die interessanten Aspekte. Drei Ergänzungen, die in allen SDKs in Form identisch sind, sorgen dafür, dass die Agenten realistischer wirken, und jede von ihnen gibt Aufschluss über das zugrunde liegende SDK.
Reale Daten. Die Prüfung auf Zusatzstoffe ist kein Spielwörterbuch mit sieben Einträgen. build_additives.py analysiert das offizielle SFA-PDF, dessen drei Tabellen drei unterschiedliche Spaltenlayouts verwenden (was für sich genommen schon eine Herausforderung darstellt), und erzeugt dabei etwa 540 Zusatzstoffe. Beim Laden erstellt jede Demo einen normalisierten Namensindex, sodass der Bezeichnung „Monosodium Glutamat“ die formelle Bezeichnung aus dem PDF „Monosodium L-Glutamat“ entspricht. Ein Index mit E/INS-Nummern, der nach der Grundnummer sortiert ist, ermöglicht es, dass „500“ weiterhin auf „500(ii)“ verweisen kann. Ein kurzer Übersichtsplan behandelt echte Umgangssprachbegriffe, die im PDF fehlen, wie zum Beispiel „MSG“ und „Vitamin C“. Alles, was tatsächlich nicht auf der Liste steht, wird als einfacher „nicht auf der Liste“-Eintrag angezeigt – niemals als falscher „erlaubt“.
Gedächtnis und Lernen. Diese beiden Aspekte lassen sich am besten gemeinsam betrachten:
- Erklärung. Jeder Agent zeigt seine Begründung an – in OpenAIs Fall als eingegebenes
reasoning-Feld und bei Claude sowie ADK als einleitender## Reasoning-Abschnitt. - Aufruf. Zunächst wird ein
recall_product-Tool ausgeführt, das alle früheren Entscheidungen zu demselben Produkt anzeigt; jede Ausführung speichert zudem ihre eigene Entscheidung. Wenn ein Etikett zweimal überprüft wird, wird bei der zweiten Überprüfung die Ergebnis mit dem ersten Vergleich abgeglichen. - Lernen. Wenn ein Etikett einen Namen anzeigt, den die Tabelle nicht erkennt, und das Modell die richtige Zahl über
e_number_hintliefert, wird die Zuordnung von Name zu Zahl in einer kleinen Datei namensagent_memory.jsongespeichert. Bei der nächsten Ausführung wird dieser Name ohne Anfrage an das Modell direkt abgerufen.
Das ist Lernen auf Systemebene. Die Gewichte ändern sich niemals, doch der Agent verbessert aufgrund seiner Erfahrungen seine eigenen Daten und Werkzeuge. Es handelt sich dabei um eine praktische, erreichbare Form des Lernens, im Gegensatz zum Feintuning, das keines dieser Tools während der Ausführung durchführt. Jedes Tool beinhaltet außerdem eine eingebaute Sitzungsschicht, die man für die Produktivspeicherung verwenden kann (OpenAI’s SQLiteSession, Claudes Sitzungsspeicher, ADK’s SessionService), und die JSON-Datei sorgt einfach dafür, dass die Demo portabel und leicht überprüfbar bleibt.
Je mehr Etiketten es liest, desto besser wird es. Ein Foto, dessen Zusatzstoffbezeichnungen das Modell in Zahlen umwandelt, lehrt die Abfrage einen neuen Alias, und jedes untersuchte Produkt wird zu etwas, das bei der nächsten Ausführung abgerufen werden kann. Wenn „Disodiumphosphat“ zum ersten Mal auftaucht, verlässt man sich darauf, dass das Modell es mit E339 abgleicht; danach kennt die deterministische Tabelle dies bereits, wodurch die Prozesse schneller, günstiger und ohne Modellaufruf wiederholbar sind. Über mehrere Produktreihen hinweg entwickelt sich agent_memory.json zu einem Alias-Wörterbuch sowie zu einer von dem Agenten selbst erstellten Historie von Entscheidungen. Weist man eine Flotte auf eine gemeinsame Speicherdatei hin, wird ein von einem Agenten gelernter Name allen bekannt. Das Modell bleibt unverändert, während der Agent sich weiterentwickelt.
Ein letzter Detail aus den tatsächlichen Tests: Als gefordert wurde, eine E-Nummer zusammen mit ihrem Namen auszugeben, hielten alle drei Modelle zuverlässig daran fest und wandelten „Disodium Phosphate“ ohne Hilfe in E339 um. Das ist das Regelwerk, das seine Aufgabe erfüllt – durch allgemeines Modellwissen, das das von der deterministischen Abfrage Übersehene ergänzt, sowie durch das System, das sich das Ergebnis merkt.
Wer liefert was
Sobald alle drei Agenten entwickelt, getestet und erweitert wurden, kann das gesamte System dargestellt und markiert werden, indem angezeigt wird, was jedes SDK tatsächlich bereitstellt und was manuell erstellt werden musste. Stellen Sie sich ein Gitter vor, bei dem Grün bedeutet, dass das SDK es bereitstellt, Gelb bedingt und Rot, dass man es selbst erstellen muss. Die Komponenten fallen in dieselben drei Schichten: Entwicklung zur Erstellung der Agenten, Laufzeit zur Steuerung einzelner Agenten sowie Control Plane zur Verwaltung einer Flotte. Die SDKs befinden sich in der ersten Schicht.
Blickt man auf die Entwicklungsschicht, ist fast alles grün; die Tools sind sich bei der Erstellung eines Agenten fast vollständig einig. Schaut man sich die beiden darunterliegenden Schichten an, kehrt sich das Muster um: Einige grüne oder bernsteinfarbene Elemente an den Rändern (ADKs Workflows und Bewertungsmethoden sowie OpenAIs Trace-Funktionen) befinden sich in einem von allen drei geteilten Meer aus Rot. Dieser Kontrast ist die eigentliche Erkenntnis. Bei der Erstellung eines Agenten sind sie sich einig, während das Betreiben und Steuern desselben Ihnen überlassen bleibt.
Die Lücken in den SDKs
Die Entwicklungsschicht ist das, was die SDKs vereinfachen. Die Laufzeit- und Steuerungsplattform fehlen ihnen größtenteils – und realistisch gesehen sind das auch nicht die Dinge, die die SDKs liefern sollen.
Beginnen wir mit den Problemen, die bereits während des Demo-Baus auftraten. Eines davon ist das Tool-Gateway, das die Laufzeitschicht von einer einfachen Tool-Aufrufung unterscheidet. Das Harness ruft Ihr Tool zuverlässig auf, tut aber nichts, um dessen Zuverlässigkeit zu gewährleisten. Die data.gov.sg HCS API antwortet beispielsweise manchmal mit 503 zusammen mit einem HTML-Inhalt, und ein naiver Aufruf von response.json() führte zum Abbruch des gesamten Ablaufs – eine schmerzhafte Lektion. Ein Hilfsfunktion _safe_json, zehn Sekunden lange Timeout-Zeiten sowie Fallback-Mechanismen vom Typ „nicht in der Liste“ lösten das Problem, sodass instabile Quellsysteme lediglich als Fehler erkannt werden und der Agent nicht mehr abgestoppt wird. Wiederholte Versuche, Backoff-Strategien sowie die Formatierung von Fehlern innerhalb der Tools sind alle Sache des Entwicklers – genau die Aufgaben, die eigentlich von einem Gateway übernommen werden sollten.
Ein weiterer Unterschied betrifft die verwaltete Speicherung. Alle drei Ansätze beinhalten eine Sitzungsschicht, die den Gesprächszustand auf einem Knoten speichert, doch dies ist nicht dasselbe wie eine Anwendung, die Fakten in einer Speicherstelle hinterlegt, auf die eine ganze Gruppe von Agenten zugreifen kann. Die hier erstellte agent_memory.json-Datei (die erlernten Aliase, die recall_product-Historie sowie die Rückschreibfunktion, die den Agenten mit jeder Kennzeichnung weiter verbessert) enthält die domänenspezifische Logik; „je öfter man sie nutzt, desto besser wird sie“ ist somit keine Eigenschaft eines solchen Tools. Auch die Verwendung echter Daten verhält sich ähnlich. Das Parsen des SFA-PDFs in über 540 strukturierte Elemente, das Erstellen des normalisierten Indexes sowie die Abfrage über Basiszahlen gehören nicht zum Aufgabenbereich eines SDKs – und das Bereitstellen echter Daten an einen Agenten ist ein von einem Programmierer konfiguriertes Verfahren, kein einfach zu aktivierender Schalter.
Die Konkurrenzsicherheit von Tools ist schlimmer als ein einfaches Unbehobenes, da das Harness selbst die Gefahr verursacht. Standardmäßig führt das OpenAI SDK unabhängige Toolaufrufe parallel aus, und es liegt an Ihnen, Ihre Tools unter diesen Bedingungen sicher zu gestalten. Noch ein alltäglicher Punkt: Das Auswerten des Ergebnisses und dessen Speichern an einer beliebigen Stelle sind gewöhnlicher Anwendungscode, den kein SDK berührt.
Wenn man sich die Laufzeitschicht ansieht, benötigt der Agent zunächst eine hostbasierte Ausführung – eine Möglichkeit, von etwas anderem als einer Terminalsession aufgerufen zu werden, wobei mehrere Ausführungen gleichzeitig stattfinden und die Infrastruktur skaliert. Er braucht außerdem eine Arbeitslastidentität, d.h. Zugangsdaten, die an Tools übergeben werden, damit die Anrufe als benannte Arbeitslast erfolgen und nicht über eine in einer Umgebungsvariable versteckte API-Schlüssel. Zudem sind dauerhafte, fortsetzbare Schleifen erforderlich, damit ein Absturz mitten im Lauf fortgesetzt werden kann, anstatt die gesamte Ausführung neu zu starten. Keines dieser Elemente existiert in einem Skript auf einem Laptop, und auch das SDK verspricht nicht, sie bereitzustellen. Die Laufzeitumgebung muss außerdem mit steigender Last die Agenten skalieren können.
Im Steuerungsflächenbereich leisten alle drei Tools kaum etwas. Bewertung ist der erste Fall: „Es wurde fehlerfrei ausgeführt“ bedeutet nicht automatisch „Es war richtig“, und die ordnungsgemäße Messung der Agentqualität hängt größtenteils von Ihnen ab. ADK ist dank seines Bewertungsframeworks die einzige Ausnahme, während die anderen beiden Tools diese Frage offen lassen.
Dann gibt es noch Schutzmechanismen und Schutz vor Eingabefälschung. Jedes Toolergebnis stellt eine unzuverlässige Eingabe dar, die an das Modell zurückgesendet wird, und ein Etikettenfoto oder eine API-Antwort könnte Anweisungen wie „Ignoriere deine Regeln“ enthalten. Die Abdeckung ist ungleichmäßig und nichts geschieht automatisch, weshalb das Bedrohungsmodell in Ihren Händen liegt.
Dann kommt die Agentenidentität mit Genehmigungsverwaltung. Die Tools ermöglichen es, eine Persona festzulegen, doch sie geben keine Informationen darüber preis, welche Rolle der Agent als Kataloghauptkonto einnimmt und wozu er Zugriff hat; daher liegt es an Ihnen, ihn mit einem echten Besitzer zu verknüpfen und seinen Zugriff einzuschränken.
Der Anschluss an einen MCP-Server ist eine andere Geschichte, da alle drei eine entsprechende Client-Lösung bieten. Ein Tool- und MCP-Register, das Tools katalogisiert und versioniert sowie eine Flotte von MCP-Servern mit Funktionen wie Erkennung, Poolsystemen, Server-spezifischen Zugangsdaten und Gesundheitsüberprüfungen verwalten soll, bleibt jedoch erneut Ihnen überlassen.
Das gleiche Problem tritt auf einer höheren Ebene im Agentenregister auf, wo Agenten veröffentlicht werden, damit andere sie zur Laufzeit entdecken und aufrufen können – und nicht durch fest programmierte Verbindungen. ADK ist eine teilweise Ausnahme, da es A2A-Verbindungen sowie Agentenkarten unterstützt, während die anderen beiden Lösungen dies Ihnen überlassen.
Zum Schluss gibt es noch die Kosten- und Quotenverwaltung, ein Audit-Log, der zeigt, was die Flotte getan hat, die Verwaltung des gemeinsamen Datenspeichers sowie ein schnelles Versionieren. Der Systemprompt ist lediglich eine von Ihnen kontrollierte Zeichenkette und das Modell ist stochastisch, weshalb das Festlegen, Vergleichen und Zurücksetzen von Prompts eine Disziplin ist, die Sie selbst durchsetzen müssen.
Das ist keine Kritik. Bei der Agentenlogik und den damit verbundenen Komponenten ist es sinnvoll, eine Grenze zu ziehen. Es bedeutet jedoch, dass die Wahl eines SDK wohl bereits die Hälfte dessen abdeckt, was ein Produktionsagent benötigt.
Welches SDK sollten Sie also verwenden?
Zurück zu den Kits selbst: Hier eine Zusammenfassung jedes davon in einem Satz. Wählen Sie das Claude Agent SDK, wenn der Agent einen Computer bedienen, mit Dateien arbeiten, eine Shell nutzen, Code ausführen und im Internet surfen soll – es handelt sich um die am weitesten entwickelte Laufzeitumgebung, die durch manuelle Schemata bezahlt wird, keine typisierten Ausgaben bietet und keine Verknüpfung zu Umgebungsvariablen vorsieht. Wählen Sie das OpenAI Agents SDK für strukturierte Eingaben und Ausgaben mit minimalem Python-Verbrauch – es ermöglicht die schnellste typisierte Kommunikation, sofern Sie sich mit der Standardverwendung paralleler Tools sowie dem Verlust der Überwachung hinter einem Proxy abfinden können. Wählen Sie das Google ADK, wenn mehrere Agenten durch explizite Arbeitsabläufe unter Verwendung echter Sitzungen zusammenarbeiten müssen – seine Entwickler-Oberfläche ist die stärkste und die Auswahl an Modellen am umfangreichsten, doch ein einfacher einzelner Agent wird es als überladen empfinden.
Im Grunde handelt es sich dabei um verschiedene Werkzeuge und nicht um drei Ausführungen derselben Lösung auf einer Skala. Derselbe Agent kann in allen implementiert werden, da der Kern – der Überlegungsprozess bezüglich der Werkzeuge – gemeinsam ist. Die Unterschiede liegen im umliegenden Aufbau, und dieser sollte Ihre Wahl bestimmen.
Was die dreifache Implementierung klar gemacht hat
Durch die dreifache Erstellung des Agenten wurde eine Schlussfolgerung offensichtlich: Der Agent-Loop ist ein bereits gelöstes, standardisiertes Problem, und alle drei Kits liefern eine saubere Umsetzung. Die eigentlichen Unterschiede liegen im Aufbau – bei handgeschriebenen gegenüber abgeleiteten Schemata, freiem Text gegenüber formatiertem Ausgabeformat sowie bei einem integrierten Laufzeitumfeld gegenüber einem leichten Client oder einem vollständigen Framework. Es geht dabei um Passform und nicht um Qualität. Passen Sie das SDK zur Aufgabe an – sei es Claude zur Steuerung eines Computers, OpenAI für formatierte Ergebnisse oder ADK für Mehr-Agenten-Systeme – dann wird Ihnen jede der drei Optionen gut dienen.
Die zentrale Lektion betrifft die Grenzen, und die drei Schichten zeigen an, wo diese liegen. Die Kits besitzen die Entwicklungsschicht und liefern sie Ihnen bereits am Nachmittag. Die Laufzeit- und Steuerungsplattform – also das Betreiben eines Agenten in der Praxis sowie die Verwaltung einer Flotte – gehören Ihnen, und dort liegt auch der größte Teil der ingenieurtechnischen Arbeit. Der Untersucher wurde erst nützlich, nachdem das, was kein SDK bereitstellt, hinzugefügt wurde: die analysierte SFA-Tabelle zur Orientierung, das Fehlerbehandlungssystem, das normalerweise von einem Tool-Gateway gesteuert wird, sowie das Gedächtnis, das mit jeder Markierung schärfer wird. Wählen Sie daher ein SDK aufgrund seiner Passform zur Entwicklungsschicht und planen Sie anschließend die Kosten für die beiden darunterliegenden Schichten ein.
Hinter dieser Geschichte verbirgt sich eine größere, die in den unteren Schichten liegt. Da der Aufbau mit einzelnen Agenten mittlerweile zur Standardpraxis geworden ist, hat sich der Wettbewerb auf die Frage verlagert, wer die Agenten betreibt und wer sie steuert. Dort entstehen neue Produkte: Laufzeiten wie AWS Bedrock AgentCore und Google’s Gemini Enterprise Agent Runtime, die alles beherbergen können, was mit den drei genannten Tools erstellt wird, sowie Steuerungsplattformen wie Microsoft’s Agent 365 und Google Enterprise, die ganze Flotten registrieren, identifizieren und überwachen. Sie verkaufen praktisch Zeile für Zeile die „roten Zellen“ dieses Systems. Das Problem ist, dass jeder Cloud-Anbieter alle drei Schichten haben möchte, wodurch das SDK, das man heute wählt, einen morgen bereits zu einem bestimmten Laufzeitumfeld und einer bestimmten Steuerungsstrategie eines Anbieters neigt.
Verwandte Artikel
- Selbstkorrigierende Agenten mit LangGraph erstellen: Fünf praktische Tipps — Erstellen Sie einen LangGraph-Suchagenten, der bei Fehlern erneut versucht, wobei Zustände, bedingte Kanten, Checkpointing, menschliche Freigabe sowie kleine Knoten anhand von funktionierendem Code erklärt werden.
- 10 AI-Agent-Frameworks, die man 2026 kennen sollte: LangGraph, CrewAI und mehr — Zehn Agent-Frameworks, sortiert nach ihrer Art der Koordination von Agenten, inklusive Vor- und Nachteilen, besten Anwendungsfällen sowie einer Entscheidungshilfe zur Auswahl des passenden Frameworks für Ihr Team.