Demonstrates how to get unified distributed traces when two LangGraph agents communicate via the A2A (Agent-to-Agent) protocol.
Agent A (Port 2024) Agent B (Port 8000)
┌──────────────────────┐ ┌──────────────────────┐
│ create_agent │ │ create_agent │
│ │ A2A JSON-RPC │ │
│ call_agent_b tool ──┼──────────────────► │ context manager │
│ │ + langsmith-trace │ tracing_context │
│ │ + baggage headers │ + parent (nesting) │
│ │ + workspace-id │ + client (routing) │
└──────────────────────┘ └──────────────────────┘
│ │
└──────────── Unified trace in LangSmith ────┘
(works across workspaces!)
Unlike RemoteGraph which has a built-in distributed_tracing=True flag, A2A is a standard HTTP protocol with no automatic trace propagation. The trick is to manually include LangSmith trace headers in the A2A HTTP request:
Agent A (caller) - Propagates trace context + workspace info in the A2A call:
run_tree = ls.get_current_run_tree()
if run_tree:
trace_headers = run_tree.to_headers()
headers.update(trace_headers)
# Send workspace ID so Agent B can route traces back to us
headers["langsmith-workspace-id"] = os.getenv("LANGSMITH_WORKSPACE_ID")
headers["langsmith-project"] = os.getenv("LANGSMITH_PROJECT")
httpx.post(f"{url}/a2a/{assistant_id}", json=payload, headers=headers)Agent B (callee) - Uses a cross-workspace Client to write traces to the caller's workspace:
@contextmanager
def graph(config):
conf = config.get("configurable", {})
caller_workspace_id = conf.get("langsmith-workspace-id")
if caller_workspace_id and cross_workspace_key:
# Route traces to the caller's workspace
client = Client(
api_key=cross_workspace_key,
workspace_id=caller_workspace_id,
)
with tracing_context(
parent=conf.get("langsmith-trace"),
client=client,
project_name=conf.get("langsmith-project"),
):
yield compiled_graphThe LangGraph API server's A2A endpoint automatically forwards HTTP headers through to config["configurable"], making langsmith-trace, langsmith-workspace-id, and langsmith-project available to the context manager.
You need three terminal windows:
Terminal 1 - Start Agent B (port 8000):
cd agent_b
uv sync
uv run langgraph dev --port 8000 --no-browserTerminal 2 - Start Agent A (port 2024):
cd agent_a
uv sync
uv run langgraph dev --port 2024 --no-browserTerminal 3 - Run the test:
uv sync
uv run python test_a2a_tracing.pyThen check LangSmith for a unified trace where Agent B's execution is nested under Agent A's trace.
This example supports cross-workspace tracing — Agent B can write traces to Agent A's workspace even when they belong to different workspaces/organizations.
- Agent A sends its
LANGSMITH_WORKSPACE_IDas a header alongside the trace headers - Agent B reads the workspace ID from
config["configurable"] - Agent B creates a
langsmith.Clientusing a cross-workspace API key (LS_CROSS_WORKSPACE_KEY) targeting the caller's workspace tracing_context(parent=..., client=..., project_name=...)both nests the trace under Agent A and routes it to Agent A's workspace
Add these to your .env:
# A cross-workspace personal API key (must have access to both workspaces)
LS_CROSS_WORKSPACE_KEY="lsv2_pt_..."
# Agent A's workspace ID (find in LangSmith Settings > Workspace)
LANGSMITH_WORKSPACE_ID="your-workspace-id-here"Without LANGSMITH_WORKSPACE_ID set, the example falls back to same-workspace tracing (the original behavior).