Observability with LangSmith for LangGraph Agents
LangSmith traces LangGraph runs as nested spans so you can see which node ran, what the model emitted, which tools fired, and how state evolved.
For graph agents, that node-by-node view is the difference between "it failed" and "the router sent us to escalate after a bad tool observation."
Summary
Set LANGSMITH_TRACING=true and LANGSMITH_API_KEY, run your compiled graph as usual, open the run in LangSmith Details/Messages views, and wrap non-LangChain I/O with traceable / SDK wrappers so tool and HTTP spans nest under the correct node.
Recipe
- Create a LangSmith API key and project (verify UI steps at build).
- Export env vars in every runtime (local, CI, prod workers):
LANGSMITH_TRACING=trueLANGSMITH_API_KEY=...- optional
LANGSMITH_PROJECT,LANGSMITH_WORKSPACE_ID, regionalLANGSMITH_ENDPOINT
- Use LangChain chat models/tools inside nodes when you want automatic nesting.
- For raw OpenAI/other SDKs, wrap clients (
wrap_openai) and decorate tools with@traceable. - Invoke/stream the graph with meaningful
thread_id/ metadata tags for searchability. - In LangSmith, inspect the graph path, token usage, and errors per node.
- Promote traces into datasets/evals for regressions on critical paths.
- Redact secrets; never log raw credentials inside messages or tool args.
Working Example
import os
from typing import Literal
from langchain.messages import HumanMessage
from langchain_openai import ChatOpenAI # or another provider package
from langchain.tools import tool
from langgraph.graph import StateGraph, MessagesState, START, END
from langgraph.prebuilt import ToolNode
# Required outside the snippet:
# export LANGSMITH_TRACING=true
# export LANGSMITH_API_KEY=...
# export OPENAI_API_KEY=... # or provider key
@tool
def search(query: str) -> str:
"""Demo web search tool."""
if "sf" in query.lower():
return "60F and foggy"
return "90F and sunny"
tools = [search]
tool_node = ToolNode(tools)
model = ChatOpenAI(model="gpt-4o-mini").bind_tools(tools) # verify model id at build
def call_model(state: MessagesState):
response = model.invoke(state["messages"])
return {"messages": [response]}
def should_continue(state: MessagesState) -> Literal["tools", "__end__"]:
last = state["messages"][-1]
if getattr(last, "tool_calls", None):
return "tools"
return "__end__"
workflow = StateGraph(MessagesState)
workflow.add_node("agent", call_model)
workflow.add_node("tools", tool_node)
workflow.add_edge(START, "agent")
workflow.add_conditional_edges("agent", should_continue, {"tools": "tools", "__end__": END})
workflow.add_edge("tools", "agent")
app = workflow.compile()
final = app.invoke(
{"messages": [HumanMessage(content="weather in sf?")]},
config={"configurable": {"thread_id": "obs-demo-1"}},
)
print(final["messages"][-1].content)
# Open LangSmith UI -> find the trace for this runWithout LangChain models, keep the graph and wrap the SDK:
from langsmith import traceable
from langsmith.wrappers import wrap_openai
import openai
client = wrap_openai(openai.Client())
@traceable(run_type="tool", name="Search Tool")
def search(query: str) -> str:
return "demo"Deep Dive
What good LangGraph traces show
- Order of nodes and conditional branches taken
- Model messages and tool calls with arguments
- Latency and error stacks per span
- Correlation across a
thread_idfor multi-turn agents
Use Details view for the tree; Messages view for conversation-shaped summaries.
Metadata and tagging
Attach tags/metadata via runnable config so you can filter prod vs staging, tenant, or agent version.
Stable thread_id values make "show me this user's last failed run" possible.
Pairing with checkpoints
Traces explain why; checkpoints recover where.
On incidents, open the LangSmith trace first, then get_state on the thread if you need to resume or re-run from a safe point.
Evals
Pull failing traces into a dataset.
Run offline evals when you change prompts, routers, or models so path mix and tool-error rates stay visible.
Privacy
Agents dump CRM fields and tokens into tool observations.
Configure masking, drop high-sensitivity projects from default retention, and keep PII policies aligned with your OpenRouter/provider data filters when those apply.
Gotchas
- Tracing env not set in workers - local works, prod is blind.
- Wrong regional endpoint - auth fails silently from the app's perspective.
- Unwrapped custom HTTP - tools look like black boxes.
- Huge binary payloads in state - traces and checkpoints bloat; store references.
- Thread id = user id only - collisions across concurrent sessions; include session suffix.
- Secrets in prompts - they will appear in traces; inject via secure config.
- Assuming LangSmith replaces metrics - still need latency/error/cost SLOs in your APM.
Alternatives
| Approach | Strength | Weakness |
|---|---|---|
| LangSmith native | Deep LangGraph nesting | SaaS dependency |
| Langfuse / OTEL stacks | Open standards | More wiring for graph semantics |
| Custom structured logs | Full control | Rebuild UI and join logic |
| Print debugging | Instant | Useless in multi-node prod |
| Provider dashboards only | Token bills | No graph path visibility |
FAQs
Do I need LangChain to trace LangGraph?
No, but LangChain integrations auto-nest. Custom code needs traceable / wrappers.
Will stream tokens show up?
Yes when models emit streamed tokens under a traced context. Confirm your stream mode and model integration support.
How do I trace only 10% of production traffic?
Use sampling/project settings available in LangSmith (verify current controls at build) or gate env vars per canary deployment.
Can I self-host?
LangSmith deployment options evolve; check current LangSmith docs for self-hosted/enterprise if required.
What is the difference between this page and the observability section's LangSmith article?
This page focuses on LangGraph wiring and graph-native reading. The agent-observability LangSmith recipe sits with broader monitoring choices.
How do interrupts appear?
Paused runs and resume turns should show as linked activity on the thread; exact UI chrome changes over time, so confirm with a HITL demo trace.
Should CI enable tracing?
Yes for integration suites with a dedicated project and low retention, so flaky tool paths are inspectable.
What metrics should alerts use beyond traces?
Task success rate, tool error rate, p95 node latency, cost per successful thread, and interrupt wait time.
Related
Related: LangSmith for LangChain/LangGraph-Native Tracing
Related: Agent Observability Basics
Related: LangGraph 1.0's Per-Node Timeouts and v2 Streaming
Related: Persistence and Checkpoints
Related: LangChain & LangGraph Best Practices
Stack versions: Pins from the category manifest (verify at build): OpenRouter (~315+ models, July 2026 pricing/fees); LangGraph 1.0+; CrewAI 1.14+; Microsoft Agent Framework 1.0; Vercel AI SDK 6; Pydantic AI (latest); LlamaIndex (latest); OpenAI Agents SDK (latest + MCP); MCP (Linux Foundation governance); A2A (HTTP+SSE+JSON-RPC 2.0); Solana
@solana/web3.js+@solana/spl-token.