Published on January 12, 2025
Creating AI Agents in Node Using the AI SDK
In this blog post, we’ll explore how to create AI agents in Node.js using the AI SDK. We’ll focus on a practical example where an activity agent first checks the weather and time of day, then suggests an activity based on these conditions. This approach is useful for developers working on SaaS platforms who want to integrate AI-driven decision-making into their applications.
Setting Up the Environment
First, we need to set up our development environment. We’ll use Node.js and install necessary packages. Start by creating a new directory for your project and initializing it with npm. Then, install the required packages:
npm init -y
npm install @ai-sdk/openai ai zod node-fetch
This command installs the AI SDK, OpenAI integration, Zod for schema validation, and node-fetch for making HTTP requests. These packages provide the tools we need to build our AI agent.
Defining the Weather Fetching Function
Next, we’ll define a function to fetch weather information. This function will use the OpenWeatherMap API to get the current weather conditions for a specified city. Here’s how you can implement it:
async function getWeatherInfo(city) {
const apiKey = 'YOUR_OPENWEATHERMAP_API_KEY';
const response = await fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=metric`
);
const data = await response.json();
if (data.cod === '404') {
throw new Error('City not found');
}
return {
temperature: `${data.main.temp}°C`,
description: data.weather[0].description
};
}
This function takes a city name as input, makes an API call to retrieve the weather data, and returns the temperature and weather description. Make sure to replace YOUR_OPENWEATHERMAP_API_KEY
with your actual API key. This simple function allows our agent to gather real-time weather data, which is crucial for making informed activity suggestions.
Adding a Time of Day Check
To make our activity agent more versatile, we’ll add a function to check if it’s daytime. This function will help the agent adjust its activity suggestions based on the time of day:
function isDaytime() {
const now = new Date();
const hours = now.getHours();
return hours > 6 && hours < 20; // Assuming daytime is between 6 AM and 8 PM
}
This function checks the current hour and returns true if it’s between 6 AM and 8 PM, which we’ll consider as daytime. By incorporating this check, our agent can tailor its recommendations to be more relevant to the time of day.
Creating the Activity Recommendation Agent
Now, let’s create the core of our application: the activity recommendation agent. This agent will use the AI SDK to generate activity suggestions based on the city, weather, and time of day. The agent will first check the weather, then the time of day, and finally suggest an activity:
async function getActivityRecommendation(city) {
const { text: activity } = await generateText({
model: openai('gpt-4o-2024-08-06', { structuredOutputs: true }),
tools: {
getWeather: tool({
description: 'Check the current weather for the given city',
parameters: z.object({
city: z.string().describe('The city to check weather for')
}),
execute: async ({ city }) => await getWeatherInfo(city)
}),
checkTime: tool({
description: 'Check if it is daytime',
parameters: z.object({}),
execute: async () => ({
isDaytime: isDaytime()
})
})
},
maxSteps: 5,
system:
'You are an activity planner. ' +
'First, use the getWeather tool to check the weather in the specified city. ' +
'Then, use the checkTime tool to see if it is daytime. ' +
'Finally, suggest an activity based on the weather and time of day.',
prompt: `Recommend an activity for someone in ${city}.`
});
return activity;
}
This function uses the generateText
method from the AI SDK to create an activity suggestion. It starts by checking the weather using the getWeather
tool, then checks the time of day with the checkTime
tool, and finally suggests an activity based on these conditions. This sequential approach ensures that the agent’s recommendations are as relevant and accurate as possible.
Running the Activity Agent
Finally, we’ll create a main function to run our activity agent. This function will call getActivityRecommendation
and handle any errors that might occur:
async function runActivityAgent(city) {
try {
const activitySuggestion = await getActivityRecommendation(city);
console.log(`Activity Suggestion for ${city}: ${activitySuggestion}`);
} catch (error) {
console.error('Error in activity recommendation:', error);
}
}
// Usage
runActivityAgent('Tokyo');
This code will run the activity agent for Tokyo and print the suggested activity to the console. It’s a simple way to test and see the results of our agent’s decision-making process.
Expanding the Example: More Complex Scenarios
The example we’ve covered is a simple demonstration of how to create an AI agent that considers weather and time of day. However, the possibilities for more complex scenarios are vast. Here are some ideas for expanding this concept:
Chaining Multiple Tools
In our example, we used two tools: getWeather
and checkTime
. You can chain more tools together to gather additional data and refine your agent’s decision-making process. For instance, you could add a getTraffic
tool to check traffic conditions, or a getEvent
tool to find local events. Here’s how you might incorporate these:
async function getActivityRecommendation(city) {
const { text: activity } = await generateText({
model: openai('gpt-4o-2024-08-06', { structuredOutputs: true }),
tools: {
getWeather: tool({
description: 'Check the current weather for the given city',
parameters: z.object({
city: z.string().describe('The city to check weather for')
}),
execute: async ({ city }) => await getWeatherInfo(city)
}),
checkTime: tool({
description: 'Check if it is daytime',
parameters: z.object({}),
execute: async () => ({
isDaytime: isDaytime()
})
}),
getTraffic: tool({
description: 'Check the current traffic conditions in the given city',
parameters: z.object({
city: z.string().describe('The city to check traffic for')
}),
execute: async ({ city }) => await getTrafficInfo(city)
}),
getEvent: tool({
description: 'Find local events in the given city',
parameters: z.object({
city: z.string().describe('The city to find events in')
}),
execute: async ({ city }) => await getEventInfo(city)
})
},
maxSteps: 10,
system:
'You are an activity planner. ' +
'First, use the getWeather tool to check the weather in the specified city. ' +
'Then, use the checkTime tool to see if it is daytime. ' +
'Next, use the getTraffic tool to check traffic conditions. ' +
'Finally, use the getEvent tool to find local events. ' +
'Suggest an activity based on all these conditions.',
prompt: `Recommend an activity for someone in ${city}.`
});
return activity;
}
In this expanded version, the agent first checks the weather, then the time of day, followed by traffic conditions, and finally local events. This sequence allows the agent to make a more comprehensive recommendation, taking into account multiple factors that could affect the user’s experience.
Conditional Logic and Decision Trees
You can also implement conditional logic within your agent to handle different scenarios. For example, if the weather is bad, the agent might skip checking for outdoor events and focus on indoor activities. Here’s how you might structure this:
async function getActivityRecommendation(city) {
const { text: activity } = await generateText({
model: openai('gpt-4o-2024-08-06', { structuredOutputs: true }),
tools: {
getWeather: tool({
description: 'Check the current weather for the given city',
parameters: z.object({
city: z.string().describe('The city to check weather for')
}),
execute: async ({ city }) => await getWeatherInfo(city)
}),
checkTime: tool({
description: 'Check if it is daytime',
parameters: z.object({}),
execute: async () => ({
isDaytime: isDaytime()
})
}),
getIndoorActivity: tool({
description: 'Suggest an indoor activity based on the weather',
parameters: z.object({
weather: z.string().describe('The current weather conditions')
}),
execute: async ({ weather }) =>
await getIndoorActivitySuggestion(weather)
}),
getOutdoorActivity: tool({
description: 'Suggest an outdoor activity based on the weather',
parameters: z.object({
weather: z.string().describe('The current weather conditions')
}),
execute: async ({ weather }) =>
await getOutdoorActivitySuggestion(weather)
})
},
maxSteps: 10,
system:
'You are an activity planner. ' +
'First, use the getWeather tool to check the weather in the specified city. ' +
'If the weather is bad, use the getIndoorActivity tool to suggest an indoor activity. ' +
'If the weather is good, use the getOutdoorActivity tool to suggest an outdoor activity. ' +
'Then, use the checkTime tool to see if it is daytime and adjust the suggestion accordingly.',
prompt: `Recommend an activity for someone in ${city}.`
});
return activity;
}
In this example, the agent uses conditional logic to decide whether to suggest an indoor or outdoor activity based on the weather. This approach allows for more nuanced and context-aware recommendations.
Tools as AI Agents
In our example, we’ve used tools as simple functions that fetch data or perform basic operations. However, these tools themselves can be AI agents, capable of more complex decision-making and interactions. Here’s how you can transform a tool into an AI agent:
Example: Weather Tool as an AI Agent
Instead of a simple function that fetches weather data, we can create a weather agent that not only fetches the data but also interprets it and provides a detailed analysis. Here’s how you might implement this:
async function getWeatherAnalysis(city) {
const weatherData = await getWeatherInfo(city);
const { text: analysis } = await generateText({
model: openai('gpt-4o-2024-08-06', { structuredOutputs: true }),
system:
'You are a weather analyst. ' +
'Provide a detailed analysis of the weather conditions, including temperature, weather description, and any recommendations for activities based on the weather.',
prompt: `Analyze the weather in ${city}. The current conditions are: ${JSON.stringify(weatherData)}.`
});
return analysis;
}
In this example, getWeatherAnalysis
is an AI agent that first fetches the weather data using getWeatherInfo
, then uses the AI SDK to generate a detailed analysis of the weather conditions. This analysis can include recommendations for activities based on the weather, making it more useful for the main activity recommendation agent.
Example: Event Tool as an AI Agent
Similarly, the getEvent
tool can be transformed into an AI agent that not only fetches event data but also filters and prioritizes events based on user preferences or other criteria. Here’s how you might implement this:
async function getEventRecommendations(city, preferences) {
const events = await getEventInfo(city);
const { text: recommendations } = await generateText({
model: openai('gpt-4o-2024-08-06', { structuredOutputs: true }),
system:
'You are an event planner. ' +
"Filter and prioritize the list of events based on the user's preferences. " +
'Provide a list of recommended events, including details like event name, time, and location.',
prompt: `Recommend events in ${city}. The available events are: ${JSON.stringify(events)}. User preferences are: ${JSON.stringify(preferences)}.`
});
return recommendations;
}
In this example, getEventRecommendations
is an AI agent that fetches event data using getEventInfo
, then uses the AI SDK to filter and prioritize events based on user preferences. This provides a more tailored and useful recommendation for the main activity recommendation agent.
Integrating AI Agent Tools
By turning tools into AI agents, you can create a more sophisticated and interactive system. The main activity recommendation agent can then use these AI agent tools to gather more detailed and context-aware data, leading to better recommendations. Here’s how the main agent might use these AI agent tools:
async function getActivityRecommendation(city, preferences) {
const { text: activity } = await generateText({
model: openai('gpt-4o-2024-08-06', { structuredOutputs: true }),
tools: {
getWeatherAnalysis: tool({
description: 'Analyze the current weather for the given city',
parameters: z.object({
city: z.string().describe('The city to analyze weather for')
}),
execute: async ({ city }) => await getWeatherAnalysis(city)
}),
checkTime: tool({
description: 'Check if it is daytime',
parameters: z.object({}),
execute: async () => ({
isDaytime: isDaytime()
})
}),
getEventRecommendations: tool({
description: 'Recommend events based on user preferences',
parameters: z.object({
city: z.string().describe('The city to find events in'),
preferences: z
.object()
.describe('User preferences for filtering events')
}),
execute: async ({ city, preferences }) =>
await getEventRecommendations(city, preferences)
})
},
maxSteps: 10,
system:
'You are an activity planner. ' +
'First, use the getWeatherAnalysis tool to get a detailed analysis of the weather in the specified city. ' +
'Then, use the checkTime tool to see if it is daytime. ' +
'Next, use the getEventRecommendations tool to find events based on user preferences. ' +
'Suggest an activity based on all these conditions.',
prompt: `Recommend an activity for someone in ${city}. User preferences are: ${JSON.stringify(preferences)}.`
});
return activity;
}
In this example, the main activity recommendation agent uses AI agent tools to gather detailed weather analysis and event recommendations, leading to more informed and personalized activity suggestions.
Going Further
Another common strategy when building AI agents is to give them access to contextual information such as documents, databases, and other sources of data. I have written a separate blog post on storing and querying documents with Postgres for RAG use cases that you can check out if you’re interested in learning more about this.
Conclusion
As you can see, it’s easy to define tools that either fetch data or perform basic or even complex operations and provide them to the AI SDK to create an agent that can perform complex tasks. Each tool can go through it’s own chain whether it’s a simple function or a more complex AI agent to perform a task or gather data and then pass the result to the next tool. This makes it easy to automate complex workflows and tasks that previously required a lot of manual code and logic.
This is a very powerful way to enrich SaaS applications with AI-driven decision-making behind the scenes to provide a better user experience and reduce the amount of manual tasks that need to be done by the user.