JamJet
Migrate

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 ADKJamJet
LlmAgent@workflow.step (Python) oder type: model Node (YAML)
SequentialAgentYAML-Node-Chain mit next: oder linearer Python-Workflow
ParallelAgenttype: parallel Node
LoopAgentZyklus mit Condition-Node
session.state (flaches Dict)Typisierter Pydantic State mit Validierung
output_keyoutput_key in YAML oder State-Zuweisung in Python
FunctionTool / einfache FunktionMCP-Tool-Server oder @tool-Decorator
ToolContext.statestate-Parameter in Step-Funktion
ToolContext.actions.transfer_to_agentCoordinator-Node mit Routing
Runner + SessionServiceJamJetClient + Rust-Runtime
InMemorySessionServiceNicht erforderlich — Runtime ist immer durable
DatabaseSessionServiceEingebaut — Events standardmäßig persistiert
adk webjamjet inspect CLI + Web Companion
AgentEvaluatorEingebauter Eval-Node (type: eval) + Scorer
adk evaljamjet eval mit --fail-under
Keine Crash-RecoveryEvent-sourced durable Execution (Standard)
Kein Replayjamjet replay <execution_id>
Kein Human-in-the-Loop-Primitivetype: 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.0

JamJet (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})  # ValidationError

Dauerhaftigkeit

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 analyze

Tools

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: exponential

Tests 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.85

Multi-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
  1. Bilden Sie Ihre Agenten auf Knoten ab. Jeder LlmAgent wird zu einem type: model-Knoten. SequentialAgent wird zu einer Kette von Knoten, die mit next: verknüpft sind. ParallelAgent wird zu einem type: parallel-Knoten. LoopAgent wird zu einem Zyklus mit einer Bedingung.
  2. 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.
  3. Ersetzen Sie session.state durch typisiertes State. Definieren Sie ein Pydantic-BaseModel mit expliziten Feldern anstelle eines flachen Dictionaries. Template-Interpolation ({key}) wird zu Jinja ({{ state.key }}).
  4. Entfernen Sie das Runner-Boilerplate. Kein SessionService, kein session.create_session(), keine Event-Iteration. Führen Sie lokal mit wf.run_sync(State(...)) aus oder in Produktion mit jamjet dev.
  5. Führen Sie es aus. jamjet dev gibt 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.

On this page