cd /news/ai-agents/ax-google-s-open-agentic-orchestrato… · home topics ai-agents article
[ARTICLE · art-135691] src=dev.to ↗ pub= topic=ai-agents verified=true sentiment=↑ positive

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.

by read6 min views3 publishedSep 21, 2026

Originally published at adityarawas.in

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:

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:

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:

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:

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:

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.

── more in #ai-agents 4 stories · sorted by recency
── more on @google 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/ax-google-s-open-age…] indexed:0 read:6min 2026-09-21 ·