# How to Create Agents with Tools Using the ReAct Pattern in Ax

> Learn to create agents with tools using the ReAct pattern in Ax. Integrate LLMs with custom functions for intelligent decision-making and efficient tool execution.

- Repository: [Ax/ax](https://github.com/ax-llm/ax)
- Tags: tutorial
- Published: 2026-02-25

---

**The ReAct pattern in Ax enables LLMs to interleave reasoning with tool execution by defining `AxFunction` objects with JSON schemas and async implementations, then passing them via the `functions` option to `ax()` generators or `agent()` instances, where the runtime in [`src/ax/prompts/agent.ts`](https://github.com/ax-llm/ax/blob/main/src/ax/prompts/agent.ts) automatically handles the loop of decision-making, tool invocation, and result integration.**

The ax-llm/ax framework implements the ReAct (Reasoning + Acting) pattern to let large language models dynamically invoke external tools while maintaining a coherent thought process. By combining structured function definitions with Ax's runtime session management, developers can create agents that reason about user requests, execute JavaScript functions, and synthesize results into natural language responses. This approach centralizes tool logic in `AxFunction` objects while delegating the orchestration loop to the agent runtime.

## Define Tool Functions with AxFunction

Every tool in Ax is defined as an `AxFunction` object that specifies the contract between the LLM and your code. Each function requires a `name`, natural language `description`, JSON Schema `parameters`, and an async `func` implementation.

The description is critical—it tells the LLM when to invoke the tool. The parameters schema follows JSON Schema conventions with `type`, `properties`, `required`, and `enum` constraints. According to the source code in [`src/examples/react.ts`](https://github.com/ax-llm/ax/blob/main/src/examples/react.ts) (lines 8-28), the function implementation receives typed arguments and returns a result that the runtime feeds back into the LLM context.

```typescript
import { type AxFunction } from '@ax-llm/ax';

const weatherTool: AxFunction = {
  name: 'getCurrentWeather',
  description: 'Get current weather for a location',
  parameters: {
    type: 'object' as const,
    properties: {
      location: { 
        type: 'string', 
        description: 'City name, e.g., Tokyo' 
      },
      units: {
        type: 'string',
        enum: ['imperial', 'metric'],
        default: 'imperial',
        description: 'Temperature units',
      },
    },
    required: ['location'],
  },
  func: async (args: Readonly<{ location: string; units: string }>) => {
    return `The weather in ${args.location} is 72 degrees`;
  },
};

```

## Attach Tools to Generators and Agents

Once defined, tools are attached via the `functions` option when creating a generator with `ax()` or a full-featured agent. This attachment point, shown in [`src/examples/react.ts`](https://github.com/ax-llm/ax/blob/main/src/examples/react.ts) (lines 34-36), separates the tool definitions from the signature contract while making them available to the LLM's reasoning process.

```typescript
import { ax, ai } from '@ax-llm/ax';

// Generator with tool access
const gen = ax('question:string -> answer:string', { 
  functions: [weatherTool] 
});

const llm = ai({ name: 'openai', apiKey: process.env.OPENAI_APIKEY! });
const result = await gen.forward(llm, { 
  question: 'What is the weather like in Tokyo?' 
});

```

The signature `'question:string -> answer:string'` defines the user-to-assistant data flow, while the `functions` array injects capabilities without polluting the input/output contract.

## The ReAct Execution Flow

The ReAct loop is orchestrated by [`src/ax/prompts/agent.ts`](https://github.com/ax-llm/ax/blob/main/src/ax/prompts/agent.ts), which builds a runtime session and manages the cycle of reasoning, acting, and observation.

### Runtime Session and Tool Globals

When `gen.forward()` is called, Ax initializes a runtime session and injects **tool globals**—references to the provided functions—into the execution context. As implemented in [`src/ax/prompts/agent.ts`](https://github.com/ax-llm/ax/blob/main/src/ax/prompts/agent.ts) (lines 16-18), these globals enable the LLM to reference tools by name during its reasoning phase.

### Recursive Reasoning Loop

The core ReAct cycle occurs in the runtime's query handling logic ([`src/ax/prompts/agent.ts`](https://github.com/ax-llm/ax/blob/main/src/ax/prompts/agent.ts), lines 69-78 and the `runSingleLlmQuery` recursion in lines 5-30). The process follows this sequence:

1. The LLM receives the user prompt and available tool schemas
2. The model either returns a final answer or outputs a `function_call` message specifying tool name and arguments
3. Ax routes the call to the matching `func` implementation
4. The result is serialized and appended to the conversation context
5. The LLM receives the updated context and continues reasoning until reaching a final answer

This recursion continues until the LLM produces a response that does not include tool calls, at which point the runtime returns the final output.

## Parallel Tool Execution

Ax supports parallel function calling, allowing the LLM to request multiple tool invocations simultaneously when a query requires diverse data sources. This capability, documented in [`docs/EXAMPLES.md`](https://github.com/ax-llm/ax/blob/main/docs/EXAMPLES.md) (lines 96-140), enables the runtime to execute independent tool calls concurrently and merge results before the next reasoning step.

When the LLM outputs multiple `function_call` messages in one turn, Ax's internal tool-globals handling dispatches all calls together. This is particularly effective for comparisons across multiple locations or data sources.

```typescript
const functions: AxFunction[] = [
  {
    name: 'getCurrentWeather',
    description: 'Get current weather for a location',
    func: async ({ location }) => ({ location, temperature: '22C' }),
    parameters: { /* schema */ },
  },
  {
    name: 'getCurrentTime',
    description: 'Get current time for a location',
    func: async ({ location }) => ({ location, time: '14:30' }),
    parameters: { /* schema */ },
  },
];

const agent = ax(
  'query:string -> report:string "Create a report from weather and time data"',
  { functions }
);

// LLM calls both tools in parallel for each location
const result = await agent.forward(
  ai({ name: 'google-gemini' }),
  { query: 'Compare weather and time in Tokyo, New York, and London.' }
);

```

## Complete Implementation Examples

### Single Tool ReAct Agent

This minimal example from [`src/examples/react.ts`](https://github.com/ax-llm/ax/blob/main/src/examples/react.ts) demonstrates a weather lookup agent:

```typescript
import { ai, ax } from '@ax-llm/ax';

const values = { question: 'What is the weather like in tokyo?' };

const functions = [
  {
    name: 'getCurrentWeather',
    description: 'Get current weather for a location',
    parameters: {
      type: 'object' as const,
      properties: {
        location: { type: 'string', description: 'City name' },
        units: {
          type: 'string',
          enum: ['imperial', 'metric'],
          default: 'imperial',
        },
      },
      required: ['location'],
    },
    func: async (args: Readonly<{ location: string; units: string }>) => {
      return `The weather in ${args.location} is 72 degrees`;
    },
  },
];

const gen = ax('question:string -> answer:string', { functions });
const llm = ai({ name: 'openai', apiKey: process.env.OPENAI_APIKEY! });
const res = await gen.forward(llm, values);

console.log(res); // → { answer: 'The weather in tokyo is 72 degrees' }

```

### Multi-Tool Reasoning Agent

For complex queries requiring multiple data sources, combine several `AxFunction` definitions:

```typescript
import { ax, ai, type AxFunction } from '@ax-llm/ax';

const functions: AxFunction[] = [
  {
    name: 'getCurrentWeather',
    description: 'Get current weather',
    parameters: { /* schema */ },
    func: async ({ location }) => ({ 
      temp: 72, 
      condition: 'sunny', 
      location 
    }),
  },
  {
    name: 'searchNews',
    description: 'Search recent news articles',
    parameters: {
      type: 'object',
      properties: {
        query: { type: 'string' },
        limit: { type: 'number', default: 5 },
      },
      required: ['query'],
    },
    func: async ({ query, limit }) =>
      Array.from({ length: limit }).map((_, i) => 
        `News ${i + 1} about ${query}`
      ),
  },
];

const assistant = ax(
  'question:string -> answer:string "Combine tool results into a comprehensive response"',
  { functions }
);

const result = await assistant.forward(
  ai({ name: 'openai' }),
  { question: 'What is the weather in Tokyo and any news about it?' }
);

```

## Summary

- **Tool Definition**: Create `AxFunction` objects with JSON Schema parameters and async implementations to define callable capabilities.
- **Attachment Pattern**: Pass tools via the `functions` option to `ax()` or `agent()` calls, keeping the signature clean while extending capabilities.
- **Runtime Orchestration**: [`src/ax/prompts/agent.ts`](https://github.com/ax-llm/ax/blob/main/src/ax/prompts/agent.ts) manages the ReAct loop through tool-globals injection (lines 16-18) and recursive query handling (lines 5-30, 69-78).
- **Parallel Execution**: Ax supports concurrent tool calls when the LLM requests multiple functions in one turn, aggregating results before continuing reasoning.
- **Traceability**: Every tool invocation is logged as a `GEN_AI_TOOL_MESSAGE` via [`src/ax/trace/trace.ts`](https://github.com/ax-llm/ax/blob/main/src/ax/trace/trace.ts) for debugging and observability.

## Frequently Asked Questions

### What is the ReAct pattern in Ax?

The ReAct pattern in Ax refers to the iterative cycle where an LLM **Re**asons about a task, **Act**s by calling JavaScript functions defined as `AxFunction` objects, and then observes the results to continue reasoning. According to the ax-llm/ax source code, this loop is implemented in [`src/ax/prompts/agent.ts`](https://github.com/ax-llm/ax/blob/main/src/ax/prompts/agent.ts) through recursive `runSingleLlmQuery` calls that continue until the model produces a final answer without requesting additional tool calls.

### How do I define a tool for an Ax agent?

Define a tool by creating an `AxFunction` object with four required properties: `name` (machine-readable identifier), `description` (natural language explanation for the LLM), `parameters` (JSON Schema defining arguments), and `func` (the async implementation). As shown in [`src/examples/react.ts`](https://github.com/ax-llm/ax/blob/main/src/examples/react.ts) (lines 8-28), the `func` receives typed arguments based on the schema and returns data that the runtime feeds back into the LLM context.

### Can Ax agents call multiple tools at once?

Yes, Ax supports parallel function calling. When a query requires data from multiple sources, the LLM can output several `function_call` messages in a single turn. The Ax runtime, as documented in [`docs/EXAMPLES.md`](https://github.com/ax-llm/ax/blob/main/docs/EXAMPLES.md) (lines 96-140), executes these calls concurrently and merges the results before the next reasoning iteration, enabling efficient data gathering across different APIs or databases.

### How does Ax route tool calls to the correct function?

Ax maintains an internal **tool-globals** map during the runtime session ([`src/ax/prompts/agent.ts`](https://github.com/ax-llm/ax/blob/main/src/ax/prompts/agent.ts), lines 16-18). When the LLM outputs a `function_call` message containing a tool name and arguments, the runtime looks up the corresponding `AxFunction` in the globals registry, validates the arguments against the JSON Schema, executes the async `func`, and appends the result to the conversation history for the next reasoning step.