---
title: 2. Create a basic agent
---

We want to use `await` so we're going to wrap all of our code in a `main` function, like this:

```typescript
// Your imports go here

async function main() {
  // the rest of your code goes here
}

main().catch(console.error);
```

For the rest of this guide we'll assume your code is wrapped like this so we can use `await`. You can run the code this way:

```bash
npx tsx example.ts
```

### Load your dependencies

First we'll need to pull in our dependencies. These are:

- The OpenAI class to use the OpenAI LLM
- tool to provide tools to our agent
- agent to create the single agent
- Settings to define some global settings for the library
- Dotenv to load our API key from the .env file
- Zod to define the schema for our tool

```javascript
import "dotenv/config";
import {
  agent,
  agentStreamEvent,
  openai,
} from "@llamaindex/workflow";
import {
  tool,
  Settings,
} from "llamaindex";
import {
  openai,
} from "@llamaindex/openai";
import { z } from "zod";
```

### Initialize your LLM

We need to tell our OpenAI class where its API key is, and which of OpenAI's models to use. We'll be using `gpt-4o`, which is capable while still being pretty cheap. This is a global setting, so anywhere an LLM is needed will use the same model.

```javascript
Settings.llm = openai({
  apiKey: process.env.OPENAI_API_KEY,
  model: "gpt-4o",
});
```

### Create a function

We're going to create a very simple function that adds two numbers together. This will be the tool we ask our agent to use.

```javascript
const sumNumbers = ({ a, b }) => {
  return `${a + b}`;
};
```

Note that we're passing in an object with two named parameters, `a` and `b`. This is a little unusual, but important for defining a tool that an LLM can use.

### Turn the function into a tool for the agent

This is the most complicated part of creating an agent. We need to define a `tool`. We have to pass in:

- The function itself (`sumNumbers`)
- A name for the function, which the LLM will use to call it
- A description of the function. The LLM will read this description to figure out what the tool does, and if it needs to call it
- A schema for function. We tell the LLM that the parameter is an `object`, and we tell it about the two named parameters we gave it, `a` and `b`. We describe each parameter as a `number`, and we say that both are required.
- You can see [more examples of function schemas](https://cookbook.openai.com/examples/how_to_call_functions_with_chat_models).

```javascript
const addTool = tool({
  name: "sumNumbers",
  description: "Use this function to sum two numbers",
  parameters: z.object({
    a: z.number({
      description: "First number to sum",
    }),
    b: z.number({
      description: "Second number to sum",
    }),
  }),
  execute: sumNumbers,
});
```

We then wrap up the tools into an array. We could provide lots of tools this way, but for this example we're just using the one.

```javascript
const tools = [addTool];
```

### Create the agent

With your LLM already set up and your tools defined, creating an agent is simple:

```javascript
const myAgent = agent({ tools });
```

### Ask the agent a question

We can use the `run` method to ask our agent a question, and it will use the tools we've defined to find an answer.

```javascript
const result = await myAgent.run("Sum 101 and 303");
console.log(result.data);
```
You will see the following output:

**_Output_**

```
{ result: 'The sum of 101 and 303 is 404.' }
```

To stream the response, you need to call `runStream`, which returns a stream of events. 
The `agentStreamEvent` provides chunks of the response as they become available. This allows you to display the response incrementally rather than waiting for the full response:

```javascript
const events = myAgent.runStream("Add 101 and 303");
for await (const event of events) {
  if (agentStreamEvent.include(event)) {
    process.stdout.write(event.data.delta);
  }
}
```

**_Streaming Output_**

```
The sum of 101 and 303 is 404.
```

Note that we're filtering for `agentStreamEvent` as an agent might return other events - more about that in the following section.

### Logging workflow events

To log the workflow events, you can check the event type and log the event data.

```javascript
const events = myAgent.runStream("Sum 202 and 404");
for await (const event of events) {
  if (agentStreamEvent.include(event)) {
    // Stream the response
    process.stdout.write(event.data.delta);
  } else {
    // Log other events
    console.log("\nWorkflow event:", JSON.stringify(event, null, 2));
  }
}
```

Let's see what running this looks like using `npx tsx agent.ts`

**_Output_**

```
Workflow event: {
  "data": {
    "userInput": "Sum 202 and 404"
  },
  "displayName": "StartEvent"
}

Workflow event: {
  "data": {
    "input": [
      {
        "role": "user",
        "content": "Sum 202 and 404"
      }
    ],
    "currentAgentName": "Agent"
  },
  "displayName": "AgentInput"
}

Workflow event: {
  "data": {
    "input": [
      {
        "role": "system",
        "content": "You are a helpful assistant. Use the provided tools to answer questions."
      },
      {
        "role": "user",
        "content": "Sum 202 and 404"
      }
    ],
    "currentAgentName": "Agent"
  },
  "displayName": "AgentSetup"
}

....

```

We're seeing several workflow events being logged:

1. `AgentToolCall` - Shows the agent preparing to call our tool with the numbers 202 and 404
2. `AgentToolCallResult` - Shows the result of calling the tool, which returned "606"
3. `AgentInput` - Shows the original user input
4. `AgentOutput` - Shows the agent's response

Great! We've built an agent that can understand requests and use tools to fulfill them. Next you can:

- [See the full code](https://github.com/run-llama/LlamaIndexTS/blob/main/examples/agentworkflow/blog-writer.ts)
- [Switch to a local LLM](3_local_model)
- Move on to [add Retrieval-Augmented Generation to your agent](4_agentic_rag)
