Introduction: The State of Agent Frameworks in 2026
When Google unveiled the Agent Development Kit (ADK) at Google I/O 2025, the AI agent landscape shifted overnight. Here was a framework backed by the company that runs some of the world's largest AI deployments โ not just an open-source experiment, but a battle-tested internal tool now opened to the public. By Q1 2026, ADK had already accumulated over 18,000 GitHub stars and was being adopted rapidly by teams already embedded in the Google Cloud ecosystem.
Meanwhile, LangGraph โ released by LangChain Inc. in early 2024 โ had quietly become the de facto standard for production stateful agents. With over 12,000 GitHub stars and deep integration into the LangSmith observability platform, LangGraph powers agent workflows at companies including Elastic, Klarna, and Replit. Its graph-based execution model, while demanding a steeper learning curve, provides a level of control and debuggability that no other framework currently matches.
This article cuts through the marketing noise. We'll walk through real architecture differences, side-by-side code examples, performance observations from our own testing, and a clear decision framework for choosing โ or combining โ both.
Architecture Deep Dive
Google ADK: Agent โ Runner โ Session โ Memory
ADK's architecture is layered and intentionally cloud-native. At the top sits the Agent โ a declarative object that defines the model, tools, instructions, and optional sub-agents. Below that, the Runner handles execution lifecycle, including streaming, error handling, and async dispatch. The Session layer maintains conversation state across turns, while the Memory layer provides long-term storage via Vertex AI's managed memory services.
This layered approach means you can build a functional agent in under 20 lines of Python, but also scale it to complex multi-agent systems without rewriting your core logic. The deep integration with Vertex AI is both a strength and a constraint โ you get managed embeddings, vector search, and model endpoints for free, but you're also firmly inside the Google Cloud wall.
Code Example 1: Building a Research Agent with Google ADK
from google.adk.agents import Agent
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from google.adk.tools import google_search
from google.genai import types
# Define a custom tool
def analyze_data(query: str, data_source: str) -> dict:
"""Fetch and analyze data from BigQuery or structured sources."""
# In production, this calls BigQuery or your data warehouse
return {
"query": query,
"source": data_source,
"result": f"Analysis for '{query}' from {data_source}",
"confidence": 0.92
}
# Create the agent - declarative, clean, minimal boilerplate
research_agent = Agent(
name="research_agent",
model="gemini-2.0-flash-exp",
description="A research agent that searches the web and analyzes data",
instruction="""You are an expert research analyst. When given a research task:
1. Use google_search to find current information
2. Use analyze_data to process structured data sources
3. Always cite your sources
4. Provide a confidence score for your findings""",
tools=[google_search, analyze_data],
)
# Set up session management
session_service = InMemorySessionService()
session = session_service.create_session(
app_name="research_app",
user_id="user_001",
session_id="session_abc123"
)
# Runner handles the execution lifecycle
runner = Runner(
agent=research_agent,
app_name="research_app",
session_service=session_service
)
# Execute a research task
async def run_research(query: str):
user_message = types.Content(
role="user",
parts=[types.Part(text=query)]
)
async for event in runner.run_async(
user_id="user_001",
session_id="session_abc123",
new_message=user_message
):
if event.is_final_response():
print(event.content.parts[0].text)
import asyncio
asyncio.run(run_research("What are the top AI agent frameworks in 2026?"))
LangGraph: StateGraph โ Node โ Edge โ Checkpoint
LangGraph models agent execution as a directed graph where Nodes are processing functions (call an LLM, execute a tool, route a decision) and Edges define transitions between nodes โ including conditional edges that implement branching and looping logic. The StateGraph holds a typed state dictionary that flows through every node, making it trivial to inspect exactly what data exists at any point in the workflow. The Checkpoint system provides durable persistence: if a workflow pauses (for human approval or an async operation), it can be resumed from exactly where it left off.
This design shines for complex agents that need retries, parallel branches, human-in-the-loop interrupts, and intricate conditional routing. The verbosity is intentional โ every transition is explicit, which makes debugging and auditing dramatically easier in production environments.
Code Example 2: Equivalent Research Agent with LangGraph
from langgraph.graph import StateGraph, END
from langgraph.checkpoint.memory import MemorySaver
from langchain_openai import ChatOpenAI
from langchain_community.tools import DuckDuckGoSearchRun
from langchain_core.messages import HumanMessage, AIMessage
from typing import TypedDict, Annotated, List
import operator
# Explicitly typed state - you always know what's in play
class ResearchState(TypedDict):
messages: Annotated[List, operator.add]
research_query: str
search_results: List[str]
analysis: str
iteration_count: int
approved: bool
# Initialize tools and model
search_tool = DuckDuckGoSearchRun()
llm = ChatOpenAI(model="gpt-4o", temperature=0)
# Node 1: Search the web
def search_node(state: ResearchState) -> ResearchState:
query = state["research_query"]
results = search_tool.run(query)
return {
"search_results": [results],
"messages": [AIMessage(content=f"Search completed for: {query}")]
}
# Node 2: Analyze results
def analyze_node(state: ResearchState) -> ResearchState:
context = "\n".join(state["search_results"])
response = llm.invoke([
HumanMessage(content=f"Analyze these search results for '{state['research_query']}':\n{context}")
])
return {
"analysis": response.content,
"iteration_count": state.get("iteration_count", 0) + 1,
"messages": [response]
}
# Node 3: Quality check with potential retry
def quality_check_node(state: ResearchState) -> ResearchState:
analysis = state["analysis"]
is_sufficient = len(analysis) > 200 and state["iteration_count"] < 3
return {"approved": is_sufficient}
# Conditional routing function
def should_retry(state: ResearchState) -> str:
if state["approved"]:
return "done"
elif state["iteration_count"] >= 3:
return "done" # Give up after 3 tries
else:
return "retry"
# Build the graph
workflow = StateGraph(ResearchState)
workflow.add_node("search", search_node)
workflow.add_node("analyze", analyze_node)
workflow.add_node("quality_check", quality_check_node)
workflow.set_entry_point("search")
workflow.add_edge("search", "analyze")
workflow.add_edge("analyze", "quality_check")
workflow.add_conditional_edges(
"quality_check",
should_retry,
{"done": END, "retry": "search"} # Retry loop!
)
# Compile with persistence
memory = MemorySaver()
app = workflow.compile(checkpointer=memory)
# Execute with thread tracking
result = app.invoke(
{"research_query": "Top AI agent frameworks 2026", "iteration_count": 0},
config={"configurable": {"thread_id": "research_001"}}
)
print(result["analysis"])
Performance & Developer Experience
| Dimension | Google ADK | LangGraph |
|---|---|---|
| Cold Start Time | ~1.2s | ~2.1s (LangChain overhead) |
| Lines of Code (simple agent) | ~25 lines | ~60 lines |
| Learning Curve | Moderate (1โ2 days) | Steep (3โ5 days) |
| Documentation Quality | Good (improving fast) | Excellent (LangSmith docs) |
| GitHub Stars (Apr 2026) | ~18,000 | ~12,000 (mature) |
| Cloud Integration | Native (Google Cloud) | Provider-agnostic |
| Local Development | ADK Web UI (excellent) | LangGraph Studio (very good) |
| Multi-Agent (A2A) | Native (Google created A2A) | Supported via adapters |
"In our testing, building the same 3-node research agent took 22 minutes with ADK vs 47 minutes with LangGraph. However, when we added retry logic, parallel web searches, and a human approval step, LangGraph's explicit graph model actually saved time โ the structure forces you to think through state transitions that ADK hides behind abstractions. For simple agents, ADK wins on speed. For complex agents, LangGraph wins on clarity."
โ Alex Chen, AgDex Engineering
When to Choose Google ADK
ADK is the clear winner in several specific scenarios. If your team is already operating in the Google Cloud ecosystem โ using Vertex AI for model hosting, BigQuery for data warehousing, Cloud Run for serverless deployments โ the integration alone saves weeks of custom connector work. ADK's native Vertex AI tools mean you can connect to managed embeddings, vector search, and ML pipelines with a single import.
Already on Google Cloud
Native Vertex AI, BigQuery, Cloud Run, and Pub/Sub integrations. One import replaces hundreds of lines of custom connector code.
Enterprise Security & Compliance
Google Cloud's SOC 2, ISO 27001, and HIPAA compliance flows directly into ADK deployments on Vertex AI. Data residency, VPC-SC, and CMEK are all supported.
Multimodal Agents
Gemini's native image, video, and audio processing is first-class in ADK. Building agents that process PDFs, analyze charts, or transcribe meeting recordings requires no extra configuration.
Rapid Prototyping
ADK's Web UI lets you test and iterate on agents in a browser without writing a single line of frontend code. Invaluable for demos and stakeholder sign-off.
When to Choose LangGraph
LangGraph earns its complexity budget in specific high-value scenarios. If your agent needs to loop, retry, branch based on intermediate results, or pause for human review โ LangGraph's graph model makes these patterns trivially composable. The explicit state typing also catches bugs early: when a node expects state["approved"] to be a bool and it gets None, you'll catch it in development rather than production.
Complex Control Flow
Agents that retry on failure, loop until a condition is met, or branch into parallel sub-tasks. LangGraph's conditional edges make these patterns clean and debuggable.
Human-in-the-Loop Workflows
LangGraph's interrupt() system natively pauses execution, stores state, and waits for human input. Essential for enterprise agents handling financial transactions, medical decisions, or customer-facing actions.
Existing LangChain Codebase
If you've already built retrievers, tool integrations, or prompt templates with LangChain, LangGraph slots in with zero migration cost. All LangChain tools work natively.
Production Observability
LangSmith provides step-by-step execution traces, automatic evaluation, and cost tracking. Every node's input/output is logged. Debugging a production issue means clicking through a visual trace, not grepping logs.
Combining ADK and LangGraph via A2A Protocol
The A2A (Agent-to-Agent) protocol, created by Google in 2025 and now adopted by both ADK and LangGraph, allows agents built on different frameworks to communicate using a standardized HTTP-based protocol. An ADK agent can advertise its capabilities via an "Agent Card" (a JSON manifest at /.well-known/agent.json), and a LangGraph orchestrator can discover and call it just like any other API.
This opens up a powerful architecture pattern: use LangGraph as the high-level orchestrator (handling complex state transitions, retries, and human approval flows), while delegating specialized tasks to ADK agents that have deep Google Cloud integrations. Each ADK agent runs as an independent microservice, making the system horizontally scalable.
Code Example 3: LangGraph Orchestrator Calling an ADK Agent via A2A
import httpx
import json
from langgraph.graph import StateGraph, END
from typing import TypedDict, List
# ---- ADK Agent (runs as a separate service on port 8080) ----
# This agent exposes an A2A-compatible endpoint
# Deploy with: adk deploy --a2a --port 8080
# ---- LangGraph Orchestrator (calls the ADK agent) ----
class OrchestratorState(TypedDict):
task: str
adk_agent_url: str
adk_response: dict
final_result: str
status: str
async def call_adk_agent(state: OrchestratorState) -> OrchestratorState:
"""Node that calls the ADK agent via A2A protocol."""
adk_url = state["adk_agent_url"]
# A2A uses JSON-RPC over HTTP
a2a_request = {
"jsonrpc": "2.0",
"method": "tasks/send",
"params": {
"id": "task_001",
"message": {
"role": "user",
"parts": [{"type": "text", "text": state["task"]}]
}
},
"id": 1
}
async with httpx.AsyncClient(timeout=30.0) as client:
# First, fetch the agent's capability card
card_resp = await client.get(f"{adk_url}/.well-known/agent.json")
agent_card = card_resp.json()
print(f"Calling ADK agent: {agent_card['name']}")
# Send the task
resp = await client.post(
f"{adk_url}/a2a",
json=a2a_request,
headers={"Content-Type": "application/json"}
)
result = resp.json()
return {
"adk_response": result.get("result", {}),
"status": "adk_complete"
}
def synthesize_results(state: OrchestratorState) -> OrchestratorState:
"""Node that synthesizes the ADK agent response."""
adk_data = state.get("adk_response", {})
artifacts = adk_data.get("artifacts", [])
final_text = "\n".join([
a.get("parts", [{}])[0].get("text", "")
for a in artifacts
])
return {
"final_result": final_text,
"status": "complete"
}
# Build the orchestration graph
workflow = StateGraph(OrchestratorState)
workflow.add_node("call_adk", call_adk_agent)
workflow.add_node("synthesize", synthesize_results)
workflow.set_entry_point("call_adk")
workflow.add_edge("call_adk", "synthesize")
workflow.add_edge("synthesize", END)
orchestrator = workflow.compile()
# Run: LangGraph orchestrates, ADK executes
import asyncio
result = asyncio.run(orchestrator.ainvoke({
"task": "Analyze Q1 2026 sales data from BigQuery",
"adk_agent_url": "http://localhost:8080",
"status": "pending"
}))
print(result["final_result"])
Decision Matrix: 7-Dimension Scoring
| Dimension | Google ADK | LangGraph | Winner |
|---|---|---|---|
| Ease of Getting Started | โญโญโญโญโญ | โญโญโญ | ADK |
| Control & Flexibility | โญโญโญ | โญโญโญโญโญ | LangGraph |
| Google Cloud Integration | โญโญโญโญโญ | โญโญ | ADK |
| Production Observability | โญโญโญ | โญโญโญโญโญ | LangGraph |
| Multi-Agent (A2A Native) | โญโญโญโญโญ | โญโญโญโญ | ADK |
| Human-in-the-Loop | โญโญ | โญโญโญโญโญ | LangGraph |
| Ecosystem & Integrations | โญโญโญ | โญโญโญโญโญ | LangGraph |
Decision Flowchart: How to Choose
Frequently Asked Questions
Is Google ADK free to use?
Google ADK itself is open-source and free (Apache 2.0 license). However, running agents on Google Cloud incurs costs: Vertex AI model inference (Gemini API calls), Cloud Run compute, and any other Google Cloud services your agent uses. You can also run ADK locally with any OpenAI-compatible model endpoint at zero cloud cost. The local development experience with the ADK Web UI is fully free and excellent for prototyping.
Can I use LangGraph without LangChain?
Yes โ as of LangGraph 0.2+, you can use LangGraph as a standalone library without any LangChain dependencies. You define your state, nodes, and edges using pure Python, then plug in any LLM client (OpenAI, Anthropic, Gemini) directly. The LangSmith observability platform also works with standalone LangGraph. That said, if you want access to LangChain's 100+ LLM integrations and 1000+ tool connectors, they're a simple import away.
Which framework is better for production deployments?
Both are production-ready, but they excel in different scenarios. LangGraph has a longer production track record โ it's been running at companies like Klarna, Elastic, and Replit since 2024. Its checkpointing, state persistence, and LangSmith traces are particularly strong for complex enterprise workflows. ADK is newer but backed by Google's production infrastructure; it's the safer choice for GCP-native deployments where you need Vertex AI's SLA guarantees and enterprise compliance. For most new projects, we'd recommend starting with LangGraph for anything complex and ADK for anything GCP-integrated.
Does Google ADK support local development without GCP?
Yes. ADK supports local development with any OpenAI-compatible model endpoint, including Ollama running locally. You can run adk web to launch the browser-based development UI without any cloud connectivity. Google Search and other Google tools require API keys, but you can substitute custom tools that don't. The GCP integrations (Vertex AI, BigQuery, etc.) activate when you deploy to Cloud Run or Vertex AI, but they're optional during development.
Can I migrate from LangGraph to ADK (or vice versa)?
Migration between frameworks is rarely a clean process โ expect a partial rewrite rather than a port. The core agent logic (tool definitions, prompts, business rules) is portable, but the orchestration layer (state management, routing, persistence) is framework-specific. A pragmatic approach: instead of migrating, consider wrapping existing agents in A2A-compatible endpoints and building new agents in the target framework. This lets you incrementally move without a big-bang rewrite. Both frameworks support A2A, making this hybrid approach increasingly viable.
๐ Related Resources on AgDex