AX: Google's Open Agentic Orchestrator Explained — Building Production AI Agent Workflows A developer published a technical breakdown of AX, Google's open-source agentic orchestrator, which models AI agents as stateful graphs with explicit transitions, retries, and checkpoints rather than prompt-chaining sequences. The writeup details AX's core primitives, a TypeScript/Node.js implementation using a Redis state store, and a feature comparison against LangChain and CrewAI, positioning AX as a production workflow engine for multi-step agent tasks. Originally published at adityarawas.in https://adityarawas.in/blog/ax-googles-open-agentic-orchestrator-explained-building-production-ai-agent-workflows Every framework promising to "orchestrate agents" eventually collapses into the same three problems: state management across long-running tasks, tool-call reliability, and observability when something silently breaks at 3am. AX — Google's open agentic orchestrator — is the first framework in this space that treats those three problems as first-class citizens instead of afterthoughts bolted onto a prompt-chaining library. If you've shipped anything with LangChain or a homegrown agent loop, you already know why that matters. This is a practical breakdown of what AX actually does, how it's architected, and how to wire it into a real Node.js/TypeScript backend with Docker for deployment. AX is an open-source orchestration layer for AI agents that sits between your LLM calls and your application logic. Unlike prompt-chaining libraries that treat agents as a sequence of function calls, AX models agents as stateful graphs with explicit transitions, retries, and checkpoints. The core primitives: This is closer to a workflow engine think Temporal or AWS Step Functions than a chatbot SDK — which is exactly the gap it fills. Most agent frameworks are great for demos and terrible for anything that needs to survive a network blip or a rate-limited API three steps into a 20-step task. If you're building agentic features into a product — not a research notebook — you care about things AX explicitly designs for: That last point is the sleeper feature. You don't need to rewrite your Go microservices in Python to make them agent-callable — AX treats any HTTP/gRPC endpoint as a valid executor. npm install @google/ax-orchestrator npm install @google/ax-store-redis A minimal agent graph in TypeScript: js import { Graph, Executor, RetryPolicy } from '@google/ax-orchestrator'; import { RedisStateStore } from '@google/ax-store-redis'; const store = new RedisStateStore { url: process.env.REDIS URL , } ; const fetchData: Executor = { id: 'fetch-user-data', type: 'tool', run: async ctx = { const res = await fetch https://api.internal/users/${ctx.input.userId} ; if res.ok throw new Error Upstream failed: ${res.status} ; return res.json ; }, policy: RetryPolicy.exponentialBackoff { maxAttempts: 3, baseMs: 500 } , }; const summarize: Executor = { id: 'summarize-with-llm', type: 'llm', model: 'gemini-2.5-pro', prompt: ctx = Summarize this user profile in 2 sentences:\n${JSON.stringify ctx.state.fetchData } , }; const graph = new Graph { store } .addNode fetchData .addNode summarize .connect 'fetch-user-data', 'summarize-with-llm' ; export async function runAgent userId: string { const run = await graph.start { input: { userId } } ; return run.result; } Notice there's no manual try/catch retry loop, no ad-hoc setTimeout backoff — that's the whole point. The policy is declared once, attached to the node, and the orchestrator enforces it. | Feature | AX | LangChain | CrewAI | |---|---|---|---| | State persistence | Durable, pluggable store | In-memory by default | In-memory by default | | Crash recovery | Resumes from checkpoint | Restarts from scratch | Restarts from scratch | | Retry policies | Per-node, declarative | Manual wrapping | Limited | | Language interop | Any HTTP/gRPC service | Python/JS SDKs only | Python only | | Observability | Built-in trace export OpenTelemetry | Requires LangSmith | Minimal | | Deployment model | Stateless orchestrator + external store | Embedded in app process | Embedded in app process | | Best fit | Production multi-step workflows | Prototyping, RAG pipelines | Role-based agent teams | The architectural bet AX makes is that agents are workflows, not chat sessions . That reframing is why it plugs so cleanly into existing DevOps tooling — checkpoints are just rows in Postgres, traces are just OTel spans, and scaling is just adding more orchestrator replicas behind a load balancer. Real-world use cases rarely involve a single LLM call. Here's a research-and-report pipeline with conditional branching: js import { Graph, Executor, Condition } from '@google/ax-orchestrator'; const searchWeb: Executor = { id: 'search-web', type: 'tool', run: async ctx = searchAPI ctx.input.query , }; const validateResults: Executor = { id: 'validate-results', type: 'llm', model: 'gemini-2.5-flash', prompt: ctx = Are these search results relevant to "${ctx.input.query}"? Answer YES or NO.\n${JSON.stringify ctx.state.searchWeb } , }; const draftReport: Executor = { id: 'draft-report', type: 'llm', model: 'gemini-2.5-pro', prompt: ctx = Write a report using:\n${JSON.stringify ctx.state.searchWeb } , }; const refineQuery: Executor = { id: 'refine-query', type: 'llm', model: 'gemini-2.5-flash', prompt: ctx = Rewrite this search query to be more specific: "${ctx.input.query}" , }; const graph = new Graph .addNode searchWeb .addNode validateResults .addNode draftReport .addNode refineQuery .connect 'search-web', 'validate-results' .connectConditional 'validate-results', { onTrue: 'draft-report', onFalse: 'refine-query', } .connect 'refine-query', 'search-web' ; // loop back This loop-back edge is where most naive agent implementations fall apart — infinite loops without a cap. AX solves it with built-in cycle guards : js const graph = new Graph { maxCycles: 5, onCycleLimitExceeded: 'fail-with-partial-result', } ; AX ships OpenTelemetry instrumentation out of the box. Every node execution emits a span with input/output/token-usage metadata: js import { AXTracer } from '@google/ax-orchestrator'; const tracer = new AXTracer { exporter: 'otlp', endpoint: process.env.OTEL COLLECTOR URL, } ; graph.attachTracer tracer ; Pipe that into Grafana/Tempo and you get a flame graph of your agent's decision tree — which node retried, how many tokens each LLM call burned, and where latency actually lives. This is the difference between debugging an agent by re-reading chat transcripts versus debugging it like you'd debug a distributed system, because that's what it is. Because the orchestrator is stateless, containerizing it is straightforward: FROM node:20-alpine AS builder WORKDIR /app COPY package .json ./ RUN npm ci COPY . . RUN npm run build FROM node:20-alpine WORKDIR /app ENV NODE ENV=production COPY --from=builder /app/dist ./dist COPY --from=builder /app/node modules ./node modules EXPOSE 3000 CMD "node", "dist/server.js" docker-compose.yml for local development with Redis as the state store: version: "3.9" services: ax-orchestrator: build: . ports: - "3000:3000" environment: - REDIS URL=redis://redis:6379 - OTEL COLLECTOR URL=http://otel-collector:4318 depends on: - redis redis: image: redis:7-alpine ports: - "6379:6379" otel-collector: image: otel/opentelemetry-collector:latest ports: - "4318:4318" Scale horizontally by just increasing replicas — since state lives in Redis/Postgres, any orchestrator instance can pick up a checkpointed run. docker compose up --scale ax-orchestrator=3 The pattern that separates toy agents from production agents is explicit failure handling per node: js const paymentExecutor: Executor = { id: 'process-payment', type: 'tool', run: async ctx = chargeCard ctx.state.amount, ctx.state.cardToken , policy: { retry: { maxAttempts: 2, baseMs: 1000 }, onFailure: 'compensate', compensate: async ctx = { await refundIfCharged ctx.state.transactionId ; }, }, }; That compensate hook is essentially the Saga pattern from distributed transactions, applied to agent workflows. If you've built payment or booking systems, this should feel immediately familiar — because it's solving the same problem. AX is overkill for: It earns its complexity when you have: | Store | Best for | Tradeoff | |---|---|---| | Redis | Low-latency, ephemeral workflows | No long-term durability guarantees without persistence config | | Postgres | Audit trails, long-running workflows days | Higher write latency per checkpoint | | GCS/S3 | Very large state payloads large documents, embeddings | Higher latency, not ideal for high-frequency checkpoints | Pick based on workflow duration and payload size — don't default to Postgres for a workflow that completes in under 10 seconds; Redis will save you real latency. compensate hook makes AX suitable for workflows with real side effects like payments or bookings.