Subchapter 20.1
references/agent-graph-reference.mdMarkdown13 KBView on GitHub
Out of scope for the main migration workflow. Read this only after a single-agent migration works end-to-end. The main
SKILL.mdworkflow stops at single-agent because multi-agent orchestration is a meaningful jump in complexity and is still evolving in the SDK.
Python is still the richer surface.
launchdarkly-server-sdk-ai(Python) has the fully-documented graph API used in the traversal pattern below.@launchdarkly/server-sdk-ai(Node) exposes Agent Graph Definitions and graph metric tracking — consult the js-core source for the current Node API shape before wiring Node graph code; the Python pattern in this doc is canonical.
An agent graph is a directed graph where each node is its own config (with its own instructions, model, parameters, and tools) and each edge carries routing metadata for handoffs. A supervisor node routes incoming requests to worker nodes based on the supervisor’s output; worker nodes may themselves route to other workers or terminate. The graph lives in LaunchDarkly — both its topology and each node’s config are managed as versioned resources and can be changed at runtime without redeploying.
Why use it:
The current Python API (verified against launchdarkly-server-sdk-ai main branch) exposes these methods on LDAIClient:
def agent_graph(self, key: str, context: Context) -> AgentGraphDefinition:
"""Retrieve an agent graph by key."""
async def create_agent_graph(
self,
key: str,
context: Context,
tools: Optional[ToolRegistry] = None,
default_ai_provider: Optional[str] = None,
) -> Optional[ManagedAgentGraph]:
"""Experimental — not production-ready. Returns a managed graph that can be invoked directly."""Use agent_graph for read-only traversal (you drive the loop). create_agent_graph + ManagedAgentGraph.run is experimental and carries explicit production-not-ready warnings in the SDK source. Stick with agent_graph for now.
graph_def: AgentGraphDefinition = ai_client.agent_graph("support-flow", context)
graph_def.is_enabled() -> bool
graph_def.root() -> Optional[AgentGraphNode]
graph_def.traverse(fn, execution_context=None) # callback over nodes from root
graph_def.reverse_traverse(fn, execution_context=None) # callback over nodes from terminals
graph_def.get_node(key: str) -> Optional[AgentGraphNode]
graph_def.get_child_nodes(node_key: str) -> List[AgentGraphNode]
graph_def.get_parent_nodes(node_key: str) -> List[AgentGraphNode]
graph_def.terminal_nodes() -> List[AgentGraphNode]
graph_def.get_tracker() -> Optional[AIGraphTracker]node.get_key() -> str
node.get_config() -> AIAgentConfig # the same shape as agent_config() returns
node.get_edges() -> List[Edge]
node.is_terminal() -> bool@dataclass
class Edge:
key: str
source_config: str
target_config: str
handoff: Optional[dict] # arbitrary dict; typically has a 'route' keytracker.track_invocation_success() -> None
tracker.track_invocation_failure() -> None
tracker.track_duration(duration: int) -> None # milliseconds, graph-level total
tracker.track_total_tokens(tokens: TokenUsage) -> None
tracker.track_path(path: List[str]) -> None # e.g. ["supervisor", "security", "support"]
tracker.track_redirect(source_key: str, redirected_target: str) -> None
tracker.track_handoff_success(source_key: str, target_key: str) -> None
tracker.track_handoff_failure(source_key: str, target_key: str) -> NoneThings that are NOT on the graph tracker:
track_node_invocation — not a public method. Use track_path(execution_path) at the end of traversal instead.track_tool_call(node_key, tool_name) — graph-level tool-call tracking does not exist. Track per-node tool calls via node_tracker.track_tool_call(tool_name) on each node’s tracker (obtained via node.get_config().create_tracker()). Trackers returned via a graph traversal are automatically bound to the right graph key — do not pass graph_key as a keyword.track_judge_response — does not exist on AIGraphTracker. Record judge results at the config level via LDAIConfigTracker.track_judge_result(result) instead.track_request(), no track_duration() per call — use track_duration(total_ms) once per traversal.If you see older devrel-agents-tutorial code that calls track_node_invocation, track_tool_call, or pokes graph_tracker._ld_client.track(...) directly, that code targets an earlier API shape and needs updating. A PR is in flight against launchdarkly-labs/devrel-agents-tutorial to align the tutorial with the current SDK.
from ldai.client import LDAIClient
from ldai.tracker import TokenUsage
async def execute_graph(ai_client: LDAIClient, graph_key: str, context, user_input: str):
graph = ai_client.agent_graph(graph_key, context)
if not graph.is_enabled():
raise ValueError(f"Agent graph '{graph_key}' is not enabled")
# Build a lookup from node key to AgentGraphNode so we can follow edges.
nodes: dict[str,
Do this in phases, not one big bang:
handoff.route metadata).ai_client.agent_graph(...) instead of assembling the pipeline by hand.track_path, track_duration, track_total_tokens, handoff success/failure counts) in addition to the per-node metrics.Each phase is reversible. If something breaks at phase 5, the supervisor can fall back to the hardcoded router while the graph issue is fixed.
@launchdarkly/server-sdk-ai source for the current API.create_agent_graph is experimental. Do not build production features on ManagedAgentGraph.run. Use the traversal pattern above.node.get_config().create_tracker() — the graph tracker handles totals only.visited and hop_count yourself.AIAgentGraphDefault. Each node’s AIAgentConfig still takes an AIAgentConfigDefault, but the graph itself has no aggregate fallback. If agent_graph fails, handle it at the app level — typically by falling back to the hardcoded pre-migration pipeline.packages/sdk/server-ai/src/ldai/agent_graph/__init__.py — AgentGraphDefinition and AgentGraphNodepackages/sdk/server-ai/src/ldai/tracker.py — AIGraphTracker (near the bottom of the file)packages/sdk/server-ai/src/ldai/client.py — LDAIClient.agent_graph and create_agent_graphtutorial/agent-graphs branch