How Ax Fits Together The Ax stack on one page: signatures, ai(), ax(), flow(), agent(), optimizers, and the AxIR compiler — and how they compose. cpp quick-start how-ax-fits-together website/content-src/templates/how-ax-fits-together.md quick-start How Ax Fits Together

How Ax Fits Together

Ax starts with one signature and lets you add only the machinery the job needs. A typed call can become a workflow, a workflow can include agents, and an agent can become a tool for another agent. The model client stays a forward argument, so the same program can use a different provider on another call—or a different provider at each agent stage. Optimizers and playbooks can wrap any of those programs, while AxIR carries the shared contract into every supported language.

The Map

The stack is compositional rather than a set of competing APIs. Signatures define the boundary. ai() supplies a model when a program runs. ax(), flow(), and agent() are programs that can be nested and forwarded through the same interface. AxGEPA and playbook() improve those programs without changing their public input and output contract.

flowchart TB
  signature["s() signature"] --> gen["ax() typed call"]
  signature --> flowNode["flow() composition"]
  signature --> agentNode["agent() runtime pipeline"]
  client["ai() client per forward"] --> gen
  client --> flowNode
  client --> agentNode
  gen --> wrappers["optimize() and playbook()"]
  flowNode --> wrappers
  agentNode --> wrappers
  wrappers --> compiler["AxIR six language output"]

Every layer shares one programming contract: typed inputs go in, typed outputs come back, and the caller chooses the model client at run time.

s() — One Signature

A signature is the compact contract at the center of Ax. The same string is parsed at runtime and, in TypeScript, at the type level. Ax turns it into the prompt fields, output schema, validators, and host-language types used by the rest of the program.

flowchart LR
  text["Signature string"] --> prompt["Prompt fields"]
  text --> schema["Output schema"]
  text --> validation["Validators"]
  text --> types["Host types"]

Use s() when you want to inspect, reuse, or build that contract directly.

ai() — Any Model Behind One Client

ai() selects a provider, model, credentials, and deployment profile behind one client boundary. Programs do not have to own that choice: the client is supplied to forward(), which lets one program move between providers, routers, local models, and test clients without rewriting its signature.

flowchart LR
  openai["OpenAI profile"] --> client["ai() client"]
  claude["Claude profile"] --> client
  gemini["Gemini profile"] --> client
  local["Local profile"] --> client
  client --> program["Program forward"]

See the ai() subsystem for models, profiles, routing, media, and usage.

ax() — A Typed Call

ax() is the smallest executable Ax program. It renders a prompt from the signature, calls the supplied model, parses the response as it streams, validates every declared field, and returns typed output. If validation fails, Ax can retry with the exact error as model feedback.

flowchart LR
  input["Typed inputs"] --> render["Render prompt"]
  render --> model["Call model"]
  model --> parse["Streaming parse"]
  parse --> validate["Validate fields"]
  validate --> output["Typed outputs"]
  validate -->|retry with feedback| model

Start with the ax() subsystem when one model call can do the job.

flow() — Programs Composed Of Programs

flow() connects programs into an explicit graph. A node can run any AxProgrammable: an ax() generator, an agent(), or another nested flow. Inputs and outputs move through named state rather than a pile of ad hoc callbacks.

The flow grammar is text-complete, so flow(mermaidString) compiles a Mermaid diagram into a running program. The diagrams on this page use the same notation you can use to describe a flow.

flowchart TB
  request["Request"] --> genNode["AxGen node"]
  request --> agentNode["AxAgent node"]
  genNode --> nested["Nested AxFlow node"]
  agentNode --> nested
  nested --> merge["Typed result merge"]

See flow() composition for branches, parallel nodes, nested programs, and Mermaid compilation.

agent() — Three Stages On A Runtime

agent() adds a runtime-backed loop for work that needs tools, large data, or multiple steps. A distiller narrows the task and evidence, an executor writes small code steps against a runtime session, and a responder turns the gathered evidence into the declared typed output.

Data lives in the runtime session: a web-worker JavaScript runtime in TypeScript, goja in Go, and QuickJS-backed profiles in the other native packages. The model receives compact descriptors and tool results instead of raw datasets.

flowchart LR
  distiller["Distiller AxGen"] --> executor["Executor AxGen"]
  executor --> runtime["Runtime session"]
  runtime --> executor
  executor -->|evidence descriptor only| responder["Responder AxGen"]
  distiller -->|respond() skip executor| responder
  responder --> output["Typed result"]

The reveal is literal: the agent pipeline is itself an AxFlow of three AxGens. Agents also expose getFunction(), so one agent can become a typed tool of another. Read Agent Internals for the full boundary.

AxGEPA — Improve With Evals

Optimization means running a program against examples, scoring the results with a metric, reflecting on failures, and evolving instructions or demonstrations. GEPA keeps a Pareto frontier instead of hiding quality, cost, latency, and brevity behind one score.

flowchart TB
  program["Run program"] --> score["Score with metric"]
  score --> reflect["Reflect on failures"]
  reflect --> evolve["Evolve instructions"]
  evolve --> program
  score --> frontier["Pareto frontier"]

Because the optimizer accepts a programmable root, it can improve a generator, flow, or agent. See Optimization.

playbook() — Evolving Context

playbook() grows a curated set of strategies from program runs and feedback. Instead of replaying a raw history, it keeps verified, reusable guidance and injects the relevant part into later runs of any program.

flowchart LR
  runs["Program runs"] --> feedback["Outcomes and feedback"]
  feedback --> curate["Curate strategies"]
  curate --> playbook["Evolving playbook"]
  playbook --> next["Next program run"]

Use Playbooks when the system should learn durable operating guidance from experience.

AxIR — Compiled Six-Wide

TypeScript is the behavioral reference runtime. AxIR extracts and lowers the shared semantics into one core model, then emits native axllm-style APIs for Python, Java, C++, Go, and Rust. The result is not transpiled TypeScript: each package keeps native names, errors, builders, callbacks, transports, and runtime boundaries.

flowchart LR
  ts["TypeScript conformance"] --> verify["axir verify"]
  modules["AxIR modules"] --> core["Core runtime model"]
  core --> python["Python package"]
  core --> java["Java package"]
  core --> cpp["C++ package"]
  core --> go["Go package"]
  core --> rust["Rust package"]
  python --> verify
  rust --> verify

axir verify compiles targets, runs generated examples, checks capability manifests, and exercises conformance fixtures before a backend earns its place on the site.

Where To Go Next

Docs