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:
// 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:
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
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.
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.
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
andb
. We describe each parameter as anumber
, and we say that both are required. - You can see more examples of function schemas.
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.
const tools = [addTool];
Create the agent
With your LLM already set up and your tools defined, creating an agent is simple:
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.
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:
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.
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:
AgentToolCall
- Shows the agent preparing to call our tool with the numbers 202 and 404AgentToolCallResult
- Shows the result of calling the tool, which returned "606"AgentInput
- Shows the original user inputAgentOutput
- Shows the agent's response
Great! We've built an agent that can understand requests and use tools to fulfill them. Next you can: