Agents & Automation

Building a Smart AI Agent with the OpenAI Node.js SDK

Learn to create an intelligent agent that detects your location, checks the weather, and recommends local activities using OpenAI functions and the Node.js SDK. This browser-friendly tutorial uses simple tools like IP API and Open Meteo—no server required. Build it step-by-step and see AI take real actions!

J

Jennifer Yu

Workflow Automation Specialist

December 26, 2025 min read
Share:

Building a Smart AI Agent with the OpenAI Node.js SDK

OpenAI functions empower your applications to perform real-world actions based on user prompts, such as web searches, email sending, or ticket bookings—elevating it beyond a basic chatbot.

This guide walks you through constructing an app leveraging OpenAI functions and the newest Node.js SDK. It operates directly in the browser, requiring just a code editor and something like VS Code Live Server for local testing. Or, dive in instantly via this Scrimba code playground.

What you will build

You'll craft a straightforward agent that recommends activities nearby. It utilizes two functions: getLocation() and getCurrentWeather(), allowing it to determine your position and current conditions.

Key point: OpenAI doesn't run code for you—it simply instructs your app on which functions to invoke, leaving execution to your code.

With location and weather in hand, the agent draws on GPT's knowledge to propose fitting activities in your area.

Importing the SDK and authenticating with OpenAI

Begin by importing the OpenAI SDK in your JavaScript file and setting up authentication via an environment variable for your API key.

import OpenAI from "openai";
 
const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
  dangerouslyAllowBrowser: true,
});

For browser execution like in Scrimba, include dangerouslyAllowBrowser: true to acknowledge client-side request risks. In production, shift these to a Node server.

Creating our two functions

Now, define the functions. getLocation leverages the IP API to fetch user location details.

async function getLocation() {
  const response = await fetch("https://ipapi.co/json/");
  const locationData = await response.json();
  return locationData;
}

The IP API provides extensive location info, including latitude and longitude, which feed into getCurrentWeather. This one queries the Open Meteo API for weather details:

async function getCurrentWeather(latitude, longitude) {
  const url = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&hourly=apparent_temperature`;
  const response = await fetch(url);
  const weatherData = await response.json();
  return weatherData;
}

Describing our functions for OpenAI

To inform OpenAI about these functions, define them in a tools array. Each entry is an object with type: "function" and a function sub-object containing name, description, and parameters.

const tools = [
  {
    type: "function",
    function: {
      name: "getCurrentWeather",
      description: "Get the current weather in a given location",
      parameters: {
        type: "object",
        properties: {
          latitude: {
            type: "string",
          },
          longitude: {
            type: "string",
          },
        },
        required: ["longitude", "latitude"],
      },
    }
  },
  {
    type: "function",
    function: {
      name: "getLocation",
      description: "Get the user's location based on their IP address",
      parameters: {
        type: "object",
        properties: {},
      },
    }
  },
];

Setting up the messages array

Prepare a messages array to track the conversation history between your app and OpenAI.

The initial entry must have role: "system" to set the AI's behavior guidelines.

const messages = [
  {
    role: "system",
    content:
      "You are a helpful assistant. Only use the functions you have been provided with.",
  },
];

Creating the agent function

The core logic resides in the async agent function, which accepts userInput.

First, append the user input to messages with role: "user".

async function agent(userInput) {
  messages.push({
    role: "user",
    content: userInput,
  });
  const response = await openai.chat.completions.create({
    model: "gpt-4",
    messages: messages,
    tools: tools,
  });
  console.log(response);
}

This invokes the Chat Completions endpoint using chat.completions.create(), passing a config object with:

  • model: The AI model, here "gpt-4".
  • messages: Full conversation history.
  • tools: Available functions from our tools array.

Running our app with a simple input

Test with an input needing a function:

agent("Where am I located right now?");

The console shows OpenAI's response:

{
    id: "chatcmpl-84ojoEJtyGnR6jRHK2Dl4zTtwsa7O",
    object: "chat.completion",
    created: 1696159040,
    model: "gpt-4-0613",
    choices: [{
        index: 0,
        message: {
            role: "assistant",
            content: null,
            tool_calls: [
              id: "call_CBwbo9qoXUn1kTR5pPuv6vR1",
              type: "function",
              function: {
                name: "getLocation",
                arguments: "{}"
              }
            ]
        },
        logprobs: null,
        finish_reason: "tool_calls" // OpenAI wants us to call a function
    }],
    usage: {
        prompt_tokens: 134,
        completion_tokens: 6,
        total_tokens: 140
    }
     system_fingerprint: null
}

finish_reason: "tool_calls" signals a function call. The function name is at response.choices[0].message.tool_calls[0].function.name: "getLocation".

Turning the OpenAI response into a function call

Map the string function name to execution by grouping functions in availableTools:

const availableTools = {
  getCurrentWeather,
  getLocation,
};

Access via bracket notation: availableTools["getLocation"]().

const { finish_reason, message } = response.choices[0];
 
if (finish_reason === "tool_calls" && message.tool_calls) {
  const functionName = message.tool_calls[0].function.name;
  const functionToCall = availableTools[functionName];
  const functionArgs = JSON.parse(message.tool_calls[0].function.arguments);
  const functionArgsArr = Object.values(functionArgs);
  const functionResponse = await functionToCall.apply(null, functionArgsArr);
  console.log(functionResponse);
}

Parse arguments from message.tool_calls[0].function.arguments (none needed here).

Running yields location data, e.g., for Oslo, Norway:

{ip: "193.212.60.170", network: "193.212.60.0/23", version: "IPv4", city: "Oslo", region: "Oslo County", region_code: "03", country: "NO", country_name: "Norway", country_code: "NO", country_code_iso3: "NOR", country_capital: "Oslo", country_tld: ".no", continent_code: "EU", in_eu: false, postal: "0026", latitude: 59.955, longitude: 10.859, timezone: "Europe/Oslo", utc_offset: "+0200", country_calling_code: "+47", currency: "NOK", currency_name: "Krone", languages: "no,nb,nn,se,fi", country_area: 324220, country_population: 5314336, asn: "AS2119", org: "Telenor Norge AS"}

Add this result to messages with role: "function":

messages.push({
  role: "function",
  name: functionName,
  content: `The result of the last function was this: ${JSON.stringify(
    functionResponse
  )}
  `,
});

role: "function" indicates function output in content.

Send an updated request to OpenAI. To handle multiple rounds, use a loop (not hardcoded).

Creating the loop

In agent, add a for-loop (max 5 iterations):

for (let i = 0; i < 5; i++) {
  const response = await openai.chat.completions.create({
    model: "gpt-4",
    messages: messages,
    tools: tools,
  });
  const { finish_reason, message } = response.choices[0];
 
  if (finish_reason === "tool_calls" && message.tool_calls) {
    const functionName = message.tool_calls[0].function.name;
    const functionToCall = availableTools[functionName];
    const functionArgs = JSON.parse(message.tool_calls[0].function.arguments);
    const functionArgsArr = Object.values(functionArgs);
    const functionResponse = await functionToCall.apply(null, functionArgsArr);
 
    messages.push({
      role: "function",
      name: functionName,
      content: `
          The result of the last function was this: ${JSON.stringify(
            functionResponse
          )}
          `,
    });
  } else if (finish_reason === "stop") {
    messages.push(message);
    return message.content;
  }
}
return "The maximum number of iterations has been met without a suitable answer. Please try again with a more specific input.";

On tool_calls, append result and continue. On stop, return answer. Otherwise, after 5 loops, error message.

Running the final app

Test with a full query:

const response = await agent(
  "Please suggest some activities based on my location and the current weather."
);
console.log(response);

Console output (formatted):

Based on your current location in Oslo, Norway and the weather (15°C and snowy),
here are some activity suggestions:
 
1. A visit to the Oslo Winter Park for skiing or snowboarding.
2. Enjoy a cosy day at a local café or restaurant.
3. Visit one of Oslo's many museums. The Fram Museum or Viking Ship Museum offer interesting insights into Norway’s seafaring history.
4. Take a stroll in the snowy streets and enjoy the beautiful winter landscape.
5. Enjoy a nice book by the fireplace in a local library.
6. Take a fjord sightseeing cruise to enjoy the snowy landscapes.
 
Always remember to bundle up and stay warm. Enjoy your day!

Logging messages reveals sequence: First getLocation, then getCurrentWeather with extracted lat/long.

{"role":"assistant","content":null,"tool_calls":[{"id":"call_Cn1KH8mtHQ2AMbyNwNJTweEP","type":"function","function":{"name":"getLocation","arguments":"{}"}}]}
{"role":"assistant","content":null,"tool_calls":[{"id":"call_uc1oozJfGTvYEfIzzcsfXfOl","type":"function","function":{"name":"getCurrentWeather","arguments":"{\n\"latitude\": \"10.859\",\n\"longitude\": \"59.955\"\n}"}}]}

You've built an AI agent with OpenAI functions and Node.js SDK! Challenge: Add a function for local events.

Happy coding!

Complete code

import OpenAI from "openai";
 
const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
  dangerouslyAllowBrowser: true,
});
 
async function getLocation() {
  const response = await fetch("https://ipapi.co/json/");
  const locationData = await response.json();
  return locationData;
}
 
async function getCurrentWeather(latitude, longitude) {
  const url = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&hourly=apparent_temperature`;
  const response = await fetch(url);
  const weatherData = await response.json();
  return weatherData;
}
 
const tools = [
  {
    type: "function",
    function: {
      name: "getCurrentWeather",
      description: "Get the current weather in a given location",
      parameters: {
        type: "object",
        properties: {
          latitude: {
            type: "string",
          },
          longitude: {
            type: "string",
          },
        },
        required: ["longitude", "latitude"],
      },
    }
  },
  {
    type: "function",
    function: {
      name: "getLocation",
      description: "Get the user's location based on their IP address",
      parameters: {
        type: "object",
        properties: {},
      },
    }
  },
];
 
const availableTools = {
  getCurrentWeather,
  getLocation,
};
 
const messages = [
  {
    role: "system",
    content:
      "You are a helpful assistant. Only use the functions you have been provided with.",
  },
];

async function agent(userInput) {
  messages.push({
    role: "user",
    content: userInput,
  });

  for (let i = 0; i < 5; i++) {
    const response = await openai.chat.completions.create({
      model: "gpt-4",
      messages: messages,
      tools: tools,
    });
    const { finish_reason, message } = response.choices[0];

    if (finish_reason === "tool_calls" && message.tool_calls) {
      const functionName = message.tool_calls[0].function.name;
      const functionToCall = availableTools[functionName];
      const functionArgs = JSON.parse(message.tool_calls[0].function.arguments);
      const functionArgsArr = Object.values(functionArgs);
      const functionResponse = await functionToCall.apply(null, functionArgsArr);

      messages.push({
        role: "function",
        name: functionName,
        content: `
            The result of the last function was this: ${JSON.stringify(
              functionResponse
            )}
            `,
      });
    } else if (finish_reason === "stop") {
      messages.push(message);
      return message.content;
    }
  }
  return "The maximum number of iterations has been met without a suitable answer. Please try again with a more specific input.";
}
The #1 Newsletter in AI

Stay ahead of the AI curve

The most important updates, news, and content — delivered in one weekly newsletter.

No spam. Unsubscribe anytime. Privacy policy

openai
nodejs
agents
function-calling
sdk
ai-agents
J

About Jennifer Yu

Workflow Automation Specialist

Jennifer covers workflow strategy, no-code platforms, and clear implementation guidance for teams adopting automation.

Comments (0)