Migration von Google ADK
Konzept-Mapping und Code-Vergleiche für die Migration von Google ADK-Agenten zu JamJet — erhalten Sie dauerhafte Ausführung, Replay und integriertes Eval.
Konzept-Mapping
| Google ADK | JamJet |
|---|---|
LlmAgent | @workflow.step (Python) oder type: model Node (YAML) |
SequentialAgent | YAML-Node-Chain mit next: oder linearer Python-Workflow |
ParallelAgent | type: parallel Node |
LoopAgent | Zyklus mit Condition-Node |
session.state (flaches Dict) | Typisierter Pydantic State mit Validierung |
output_key | output_key in YAML oder State-Zuweisung in Python |
FunctionTool / einfache Funktion | MCP-Tool-Server oder @tool-Decorator |
ToolContext.state | state-Parameter in Step-Funktion |
ToolContext.actions.transfer_to_agent | Coordinator-Node mit Routing |
Runner + SessionService | JamJetClient + Rust-Runtime |
InMemorySessionService | Nicht erforderlich — Runtime ist immer durable |
DatabaseSessionService | Eingebaut — Events standardmäßig persistiert |
adk web | jamjet inspect CLI + Web Companion |
AgentEvaluator | Eingebauter Eval-Node (type: eval) + Scorer |
adk eval | jamjet eval mit --fail-under |
| Keine Crash-Recovery | Event-sourced durable Execution (Standard) |
| Kein Replay | jamjet replay <execution_id> |
| Kein Human-in-the-Loop-Primitive | type: wait Node (durable) |
to_a2a() | Native A2A-Unterstützung in Runtime |
LiteLlm(model="...") | Direkte Multi-Model-Unterstützung (keine Adapter-Schicht) |
Side-by-Side-Beispiel
Ein Research-Assistent, der das Web durchsucht, Ergebnisse analysiert und eine qualitätsbewertete Zusammenfassung generiert.
Google ADK
from google.adk.agents import LlmAgent, SequentialAgent
from google.adk.tools import FunctionTool
def web_search(query: str) -> dict:
"""Searches the web for information on the given query."""
# Implementation
return {"results": ["result 1", "result 2"]}
search_agent = LlmAgent(
name="searcher",
model="gemini-2.5-flash",
instruction="Search for: {query}",
tools=[web_search],
output_key="search_results",
)
analyst = LlmAgent(
name="analyst",
model="gemini-2.5-pro",
instruction=(
"Analyze these results: {search_results}. "
"Write a comprehensive summary with key findings."
),
output_key="summary",
)
pipeline = SequentialAgent(
name="research_assistant",
sub_agents=[search_agent, analyst],
)
# Pipeline ausführen
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from google.genai import types
runner = Runner(
agent=pipeline,
app_name="research_app",
session_service=InMemorySessionService(),
)
session = runner.session_service.create_session(
app_name="research_app", user_id="user1"
)
events = runner.run(
user_id="user1",
session_id=session.id,
new_message=types.Content(
parts=[types.Part(text="durable AI workflow orchestration")]
),
)
for event in events:
if event.is_final_response():
print(event.content.parts[0].text)JamJet (YAML)
id: research-assistant
version: "0.1.0"
nodes:
search:
type: tool
server: brave-search
tool: web_search
arguments:
query: "{{ state.query }}"
output_key: search_results
retry:
max_attempts: 3
backoff: exponential
next: analyze
analyze:
type: model
model: claude-sonnet-4-6
prompt: |
Analyze these results: {{ state.search_results }}.
Write a comprehensive summary with key findings.
output_key: summary
next: evaluate
evaluate:
type: eval
scorers:
- type: llm_judge
model: claude-haiku-4-5-20251001
rubric: "Is the summary accurate, well-structured, and comprehensive?"
fail_under: 4.0JamJet (Python)
from openai import OpenAI
from pydantic import BaseModel
from jamjet import Workflow
client = OpenAI()
class State(BaseModel):
query: str
search_results: str = ""
summary: str = ""
wf = Workflow("research-assistant")
@wf.state
class ResearchState(State):
pass
@wf.step
async def search(state: ResearchState) -> ResearchState:
# In production: type: tool + MCP server handles this
results = f"[results for: {state.query}]"
return state.model_copy(update={"search_results": results})
@wf.step
async def analyze(state: ResearchState) -> ResearchState:
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": (
"You are an expert research analyst. "
"Write a comprehensive summary with key findings."
)},
{"role": "user", "content": (
f"Analyze these results: {state.search_results}"
)},
],
)
return state.model_copy(update={"summary": resp.choices[0].message.content or ""})
result = wf.run_sync(ResearchState(query="durable AI workflow orchestration"))
print(result.state.summary)Zentrale Unterschiede
State-Management
ADK verwendet ein flaches session.state-Dictionary – jeder Key kann jederzeit gelesen oder geschrieben werden, ohne Schema-Validierung. Template-Interpolation ({key}) in Anweisungen macht es einfach, State durchzureichen, aber es gibt keine Compile-Time-Garantie, dass ein Key existiert oder den richtigen Typ hat.
JamJet verwendet Pydantic-Modelle. Jedes Feld hat einen Typ, einen Default-Wert und optionale Validatoren. Wenn ein Step einen State mit einem fehlenden Feld oder falschen Typ zurückgibt, erhältst du direkt an diesem Step einen Fehler – nicht erst drei Steps später durch stille Datenverfälschung.
# ADK: alles geht
session.state["results"] = 42 # sollte das eine Liste sein?
session.state["resutls"] = [...] # Tippfehler — kein Fehler, stiller Bug
# JamJet: sofort erkannt
class State(BaseModel):
results: list[str] = []
state.model_copy(update={"results": 42}) # ValidationErrorDauerhaftigkeit
ADKs InMemorySessionService verliert den gesamten Zustand beim Beenden des Prozesses. DatabaseSessionService speichert den Sitzungszustand zwischen Anfragen, aber es gibt keine automatische Wiederherstellung, wenn der Agent während der Ausführung abstürzt. Wenn Ihr SequentialAgent bei Sub-Agent 3 von 5 fehlschlägt, starten Sie von vorne.
JamJets Rust-Runtime erstellt Event-Sourcing für jeden Knotenübergang. Absturz bei Knoten 3 von 5? Die Runtime spielt das Event-Log ab und setzt bei Knoten 3 fort. Das ist das Standardverhalten — keine Konfiguration erforderlich.
# Jede frühere Ausführung wiederholen
jamjet replay exec-abc123
# Von einem bestimmten Schritt abzweigen, um einen anderen Pfad zu testen
jamjet replay exec-abc123 --fork-at analyzeTools
ADK-Tools sind einfache Python-Funktionen mit Docstrings. Die Funktionssignatur und der Docstring werden an das Modell zur Tool-Auswahl gesendet. Das ist praktisch für Prototyping, aber jedes Tool ist an die Codebasis Ihres Agents gekoppelt.
JamJet verwendet MCP (Model Context Protocol) — einen offenen Standard. Tools sind unabhängige Server, mit denen sich jeder Agent verbinden kann. Ein Brave-Search-MCP-Server funktioniert mit JamJet, Claude Desktop und jedem anderen MCP-Client. Kein Vendor-Lock-in, keine Neuimplementierung.
# ADK: Tool lebt im Code Ihres Agents
# def web_search(query: str) -> dict:
# """Durchsucht das Web."""
# JamJet: Tool ist ein unabhängiger MCP-Server
nodes:
search:
type: tool
server: brave-search # jeder MCP-kompatible Server
tool: web_search
arguments:
query: "{{ state.query }}"
retry:
max_attempts: 3
backoff: exponentialTests und Evaluierung
ADK bietet AgentEvaluator als separates Test-Framework – Sie schreiben Testfälle, führen adk eval aus und überprüfen die Ergebnisse außerhalb des Ausführungsflusses Ihres Agenten. Evaluierung ist eine von der Agent-Logik getrennte Komponente.
JamJet macht Evaluierung zu einem vollwertigen Workflow-Knoten. Ein eval-Knoten läuft als Teil Ihres Graphen und gibt Ihnen Qualitäts-Gates direkt in der Agent-Pipeline. Sie können auch Batch-Evaluierungen über die CLI mit Pass/Fail-Schwellenwerten für CI ausführen:
jamjet eval run evals/research.jsonl \
--workflow research-assistant \
--fail-under 0.85Multi-Modell-Unterstützung
ADK ist um Gemini herum gebaut. Andere Modelle sind über LiteLlm verfügbar, was eine Adapter-Schicht mit eigener Konfiguration und eigenen Fehlermodi hinzufügt. JamJet ist von Grund auf modell-agnostisch – geben Sie jedes Modell direkt in Ihrem YAML- oder Python-Code an. Keine Adapter-Schicht, kein Wrapper, keine zusätzliche Abhängigkeit.
nodes:
fast-search:
type: model
model: claude-haiku-4-5-20251001 # Anthropic
# ...
deep-analysis:
type: model
model: gpt-4o # OpenAI
# ...
local-draft:
type: model
model: ollama/llama3 # Local
# ...Schnelleinstieg Migration
pip install jamjet- Bilden Sie Ihre Agenten auf Knoten ab. Jeder
LlmAgentwird zu einemtype: model-Knoten.SequentialAgentwird zu einer Kette von Knoten, die mitnext:verknüpft sind.ParallelAgentwird zu einemtype: parallel-Knoten.LoopAgentwird zu einem Zyklus mit einer Bedingung. - Konvertieren Sie Tools zu MCP-Servern. Einfache Python-Tools werden zu MCP-Tool-Knoten, oder nutzen Sie den
@tool-Dekorator für eine schnelle Migration. Vorhandene MCP-Server (Brave Search, GitHub, Postgres) funktionieren sofort. - Ersetzen Sie
session.statedurch typisiertes State. Definieren Sie ein Pydantic-BaseModelmit expliziten Feldern anstelle eines flachen Dictionaries. Template-Interpolation ({key}) wird zu Jinja ({{ state.key }}). - Entfernen Sie das Runner-Boilerplate. Kein
SessionService, keinsession.create_session(), keine Event-Iteration. Führen Sie lokal mitwf.run_sync(State(...))aus oder in Produktion mitjamjet dev. - Führen Sie es aus.
jamjet devgibt Ihnen dauerhafte Ausführung, Replay, Kosten-Tracking und eine Event-Timeline – alles automatisch.
Tipp: Beginnen Sie mit dem Claims-Processing-Beispiel für einen realen mehrstufigen Workflow oder erkunden Sie die vollständige Dokumentation.