The Inter-Agent Protocol Problem
Every major agent framework is incompatible with the others. Your protocol choice today determines whether you can swap workers tomorrow.
Join the DZone community and get the full member experience.
Join For FreeEvery major agent framework now has a story for multi-agent systems. Most of them are incompatible with each other. An agent built in AutoGen cannot natively receive a task from a deepagents orchestrator. An OpenAI Agents SDK cannot talk to a LangGraph subgraph. A CrewAI crew cannot delegate to a Pydantic AI team without custom glue code.
This is the inter-agent protocol problem: We have multi-agent frameworks, but no agreed-upon protocol for agents to communicate across frameworks. This post breaks down the four main approaches, compares them, and examines the need for standardization.
Why Is Inter-Agent Communication Hard?
Single-agent systems have a clean interface: you send a prompt, you get a response. Multi-agent systems need to express:
- Task delegation – what work is being handed off and to whom
- Context transfer – what is the background the receiving agent needs
- State propagation – what the delegating agent needs back
- Error and cancellation – what happens when the receiving agent fails
- Streaming – how partial results flow back during long tasks

Most frameworks solve some of these, but with different schemas, different transport assumptions, and no interoperability.
The 4 Current Approaches
1. ACP (Agent Communication Protocol): deepagents
Installation:
pip install deepagents-acp
# Spec: https://github.com/langchain-ai/deepagents/tree/main/libs/acp
# deepagents-acp version: 0.0.6 (requires agent-client-protocol>=0.8.0)
ACP is an open protocol: a published JSON schema for agent-to-agent communication. Langchain's deepagents ships deepagents-acp, a Python client and server implementation, as a separate package, so any framework can implement it.

ACP Message Flow
A deepagents orchestrator routing a task to an ACP-compatible worker:
from deepagents import create_deep_agent
from deepagents.middleware import AsyncSubAgentMiddleware, AsyncSubAgent
# Declare an ACP-compatible worker agent
research_worker = AsyncSubAgent(
name="research",
description="Deep research agent — use for tasks requiring web search and synthesis",
url="http://research-agent:8080", # ACP server endpoint
)
# The orchestrator routes tasks to workers via the ACP protocol automatically
orchestrator = create_deep_agent(
model="anthropic:claude-sonnet-4-6",
middleware=[
AsyncSubAgentMiddleware(subagents=[research_worker]),
],
)
Serving a deepagents agent as an ACP endpoint:
import asyncio
from acp import run_agent as run_acp_agent
from deepagents import create_deep_agent
from deepagents_acp.server import AgentServerACP
agent = create_deep_agent(model="anthropic:claude-haiku-4-5")
# Wrap the compiled graph and expose it over ACP
acp_agent = AgentServerACP(agent=agent)
asyncio.run(run_acp_agent(acp_agent))
What makes ACP different: It's an open schema, not a framework-internal call. Any framework can implement an ACP server or client, which means a CrewAI crew could delegate to a deepagents worker over ACP without any shared code.
Current limitation: Adoption is early. As of v0.9.0, the primary implementations are deepagents-native. Support for other frameworks requires each to implement the server interface independently.
2. Handoffs: OpenAI Agents SDK
Installation:
pip install openai-agents
# Docs: https://openai.github.io/openai-agents-python/handoffs/
OpenAI's SDK handles delegation through handoffs: an agent declares which other agents it can hand off to. At runtime, the orchestrator agent decides when to delegate and execution transfers.

from agents import Agent, Runner, handoff
research_agent = Agent(
name="ResearchAgent",
instructions="You are an expert at web research. Answer research questions thoroughly.",
tools=[web_search_tool],
)
code_agent = Agent(
name="CodeAgent",
instructions="You write and review Python code.",
tools=[run_code_tool],
)
orchestrator = Agent(
name="Orchestrator",
instructions=(
"Route tasks to the right specialist. "
"Use ResearchAgent for questions requiring web search. "
"Use CodeAgent for programming tasks."
),
handoffs=[
handoff(research_agent),
handoff(code_agent, tool_name_override="delegate_to_coder"),
],
)
result = await Runner.run(orchestrator, "Write a Python script to fetch weather data")
You can also add context and filters to handoffs:
from agents import handoff, RunContextWrapper
def on_handoff_to_research(ctx: RunContextWrapper, input_data: str) -> None:
print(f"Handing off to research agent with: {input_data}")
research_handoff = handoff(
research_agent,
on_handoff=on_handoff_to_research,
input_filter=lambda inp: inp, # transform input before handoff
)
Limitation: Handoffs are framework-internal. A handoff target must be an Agent instance from the same SDK. There's no published schema, no HTTP transport, no cross-framework interoperability. If your orchestrator is a LangGraph graph and your specialist is an OpenAI agent, handoffs can't bridge them.
3. Agent Delegation via Tools: Pydantic AI
Installation:
pip install pydantic-ai
# Docs: https://pydantic.dev/docs/ai/guides/multi-agent-applications/
Pydantic AI does not have a dedicated inter-agent messaging primitive. The idiomatic multi-agent pattern is agent delegation via tools: a specialist agent is called from within a tool of the orchestrator agent, and the result is returned to the orchestrator like any other tool return value.

from pydantic_ai import Agent, RunContext
research_agent = Agent(
"anthropic:claude-haiku-4-5",
system_prompt="You are a research specialist. Answer questions with citations.",
)
code_agent = Agent(
"anthropic:claude-sonnet-4-6",
system_prompt="You are a Python expert. Write clean, tested code.",
)
orchestrator = Agent(
"anthropic:claude-sonnet-4-6",
system_prompt="Route tasks to the right specialist tool.",
)
@orchestrator.tool
async def research(ctx: RunContext[None], query: str) -> str:
"""Delegate a research question to the research specialist."""
result = await research_agent.run(query, usage=ctx.usage)
return result.output
@orchestrator.tool
async def write_code(ctx: RunContext[None], task: str) -> str:
"""Delegate a coding task to the code specialist."""
result = await code_agent.run(task, usage=ctx.usage)
return result.output
result = await orchestrator.run(
"Research quantum computing and write a Python simulation"
)
Passing usage=ctx.usage propagates token accounting from the delegate run back to the parent, so result.usage() that the orchestrator covers all sub-agent calls.
Limitation: Delegation is framework-internal: all agents must be Pydantic AI Agent instances. There is no pub-sub bus, no HTTP transport, and no cross-framework protocol. For true cross-framework delegation, a pattern like ACP is required.
4. AgentProtocol Backbone: Agno
Installation:
pip install agno
# Docs: https://docs.agno.com/introduction
Agno takes the most ambitious approach: an AgentProtocol backbone that acts as a multi-framework adapter. The goal is to let agents from different frameworks (LangGraph, DSPy, Claude's SDK) plug into the same Agno team:
from agno.agent import Agent
from agno.team import Team
from agno.models.anthropic import Claude
# Native Agno agent
research_agent = Agent(
name="Researcher",
model=Claude(id="claude-haiku-4-5"),
tools=[web_search_tool],
description="Specializes in web research and synthesis.",
)
# Native Agno agent
code_agent = Agent(
name="Coder",
model=Claude(id="claude-sonnet-4-6"),
tools=[python_repl_tool],
description="Specializes in Python development.",
)
team = Team(
name="FullStackTeam",
mode="coordinate",
members=[research_agent, code_agent],
model=Claude(id="claude-sonnet-4-6"),
db=agno_db_storage, # sessions + memory persistence
enable_agentic_state=True,
)
team.print_response(
"Research quantum computing trends and prototype a simulation in Python"
)
Limitation: The multi-framework adapter is still maturing. Most production Agno deployments use native Agno agents. The cross-framework vision is real, but the published adapter surface for LangGraph and DSPy is sparse at the time of writing.
Side-by-Side Comparison

Dimension |
ACP (deepagents) | Handoffs (OpenAI) | Agent delegation (Pydantic AI) | AgentProtocol (Agno) |
|---|---|---|---|---|
| Published open schema | ✓ | ✗ | ✗ | Partial |
| HTTP transport | ✓ | ✗ | ✗ | ✓ |
| Streaming support | ✓ (SSE) | ✓ (run-level) | ✗ | ✓ |
| Cross-framework workers | ✓ (any ACP server) | ✗ | ✗ | Partial |
| Context/metadata passing | ✓ | ✓ (input_filter) | ✓ (usage propagation) | ✓ |
| Error/cancellation schema | ✓ | Partial | ✗ | ✓ |
| Built-in state persistence | ✗ (LangGraph handles it) | ✗ | ✗ | ✓ (Agno DB) |
| Production deployments | Early | Growing | Mature | Growing |
The Fragmentation Cost in Practice
Consider this real scenario: you have a research pipeline where:
- The orchestrator is a deepagents agent (LangGraph-backed)
- The research worker is a CrewAI crew (good at parallel research tasks)
- The code worker is an OpenAI agent (good at code + sandboxed execution)
Today, wiring this up requires:
# Option 1: Wrap every non-deepagents agent as a plain function tool
# Loses: streaming, cancellation, structured error handling
@tool
def run_crewai_research(query: str) -> str:
crew = ResearchCrew()
result = crew.kickoff(inputs={"query": query})
return str(result) # no streaming, no structured output
# Option 2: Host each agent as an HTTP service and call it manually
# Loses: shared context, standard error handling, progress tracking
import httpx
@tool
async def call_openai_agent(task: str) -> str:
async with httpx.AsyncClient() as client:
response = await client.post(
"http://openai-agent-service/run",
json={"task": task},
)
return response.json()["result"]
With ACP, the same cross-framework delegation looks like this:
from deepagents.middleware import AsyncSubAgentMiddleware, AsyncSubAgent
# Any ACP-compliant server — regardless of what framework runs inside
orchestrator = create_deep_agent(
model="anthropic:claude-sonnet-4-6",
middleware=[
AsyncSubAgentMiddleware(subagents=[
AsyncSubAgent(name="research", url="http://crewai-acp-server:8080"),
AsyncSubAgent(name="coder", url="http://openai-acp-server:8081"),
]),
],
)
The wrapping frameworks are invisible. The protocol standardizes streaming, cancellation, and structured error handling.
Where Is This Heading?
The inter-agent space is actively consolidating around a few patterns:
- ACP is the most explicit attempt at standardization. It bets that an open schema survives framework churn. You can swap the worker implementation without changing the orchestrator.
- Handoffs are winning on simplicity within the OpenAI ecosystem. For teams already on the OpenAI SDK, they're ergonomic and production-proven. The cross-framework limitation only matters if you leave the ecosystem.
- The agent-delegation model (Pydantic AI via agent-as-tool, AutoGen's event-driven redesign) is a better fit for peer networks where no single orchestrator coordinates everything.
- Framework-native protocols will likely remain dominant for the near term. Cross-framework standardization requires enough pain from fragmentation to motivate all players -> we're getting there, but not quite yet.
If you're building a multi-agent system today and expect to stay within one framework, pick that framework's native protocol. If you're building infrastructure that needs to orchestrate agents across frameworks or you expect your team to evaluate multiple frameworks, investing in ACP-compatible interfaces from the start gives you the most flexibility.
Opinions expressed by DZone contributors are their own.
Comments