How to Build an AI Agent with Function Calling in Node.js Using Google Gemini

agentic loopai agentconversational interfacefrankfurter.appfunction callinggoogle geminimulti-step queriesnode.jsopen-meteotool integration

How to Build an AI Agent with Function Calling in Node.js Using Google Gemini

Last year, a client asked me to add a conversational interface to their internal reporting tool. Staff would type a question, and the system would pull a live answer from the database. I had the first version running in a day—single questions worked. But a week in, a tester typed: "What is the weather in Berlin, and how much would 500 EUR convert to in USD right now?" The model called the weather function, returned that answer, and ignored the second half of the question entirely.

That is the gap between a chatbot and an agent. A chatbot works from training data—that's its limit. An agent doesn't have that limit. It calls a tool, reads what came back, and decides whether to keep going. Most questions resolve in one or two tool calls. Multi-step ones take a few more. Remove that loop, and they all break.

This tutorial shows you how to build that loop with Google Gemini's function calling API and Node.js. You'll build an agent that can call real external tools: Open-Meteo for live weather, frankfurter.app for live currency rates, and a math evaluator for calculations. All three are completely free. The only API key you need is Gemini, which is also free on Google AI Studio at 1,500 requests per day as of 2026.

Everything is on GitHub: github.com/ziaongit/nodejs-gemini-agent.

Table of Contents

How Function Calling Works

Function calling allows a language model to request the execution of external functions. Rather than the model generating a final answer on its own, it outputs a structured request that your application fulfills. The model then receives the function's response and uses it to formulate a complete answer. This pattern is central to building AI agents that can interact with real-world data and services.

What We're Building

We will create a Node.js application that uses Google Gemini's function calling to handle multi-step queries. The agent will be capable of calling three external tools:

  • Weather Tool: Uses the Open-Meteo API to fetch current weather data for any city.
  • Currency Converter Tool: Uses frankfurter.app to convert amounts between currencies using live exchange rates.
  • Math Evaluator Tool: Evaluates mathematical expressions (e.g., "500 + 300 * 2").

The agent will parse user queries, determine which tools to call in sequence (if multiple steps are needed), and compile a coherent response.

Prerequisites

Before starting, ensure you have:

  • Node.js (version 18 or later) installed.
  • A Google AI Studio account to obtain a Gemini API key (get your key here—free tier includes 1,500 requests per day).
  • Basic familiarity with Node.js and asynchronous programming.

Project Setup

  1. Initialize your project:

    mkdir gemini-agent && cd gemini-agent
    npm init -y
    
  2. Install dependencies:

    npm install @google-ai/generativelanguage dotenv axios
    

    We use @google-ai/generativelanguage for the Gemini API, dotenv to manage environment variables, and axios for HTTP requests to external APIs.

  3. Create a .env file:

    GEMINI_API_KEY=your_actual_key_here
    

    Replace your_actual_key_here with the key from Google AI Studio.

  4. Create the main file:

    touch index.js
    

Defining the Tools

In index.js, start by importing the required packages and setting up the Gemini model. Then define your tools as part of the Gemini API configuration.

require('dotenv').config();
const { GoogleGenerativeAI } = require('@google-ai/generativelanguage');
const axios = require('axios');

const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY);
const model = genAI.getGenerativeModel({ model: 'gemini-1.5-pro' });

// Define tools in the API-compatible format
const tools = [
  {
    functionDeclarations: [
      {
        name: 'get_current_weather',
        description: 'Get the current weather in a given city',
        parameters: {
          type: 'OBJECT',
          properties: {
            city: { type: 'STRING', description: 'The city name' },
          },
          required: ['city'],
        },
      },
      {
        name: 'convert_currency',
        description: 'Convert an amount from one currency to another using live rates',
        parameters: {
          type: 'OBJECT',
          properties: {
            amount: { type: 'NUMBER', description: 'Amount to convert' },
            from: { type: 'STRING', description: 'Source currency code (e.g., EUR)' },
            to: { type: 'STRING', description: 'Target currency code (e.g., USD)' },
          },
          required: ['amount', 'from', 'to'],
        },
      },
      {
        name: 'evaluate_math',
        description: 'Evaluate a mathematical expression',
        parameters: {
          type: 'OBJECT',
          properties: {
            expression: { type: 'STRING', description: 'Mathematical expression to evaluate' },
          },
          required: ['expression'],
        },
      },
    ],
  },
];

Implementing the Tool Functions

Now implement the actual logic for each tool. These functions will be called by your agent when Gemini requests them.

async function getCurrentWeather(city) {
  try {
    const response = await axios.get(
      `https://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41¤t_weather=true`
    );
    // In a full implementation, you would geocode the city.
    // For this example, we use Berlin coordinates as a placeholder.
    return `Current weather in ${city}: ${response.data.current_weather.temperature}°C, ${response.data.current_weather.weathercode}`;
  } catch (error) {
    return `Error fetching weather: ${error.message}`;
  }
}

async function convertCurrency(amount, from, to) {
  try {
    const response = await axios.get(
      `https://api.frankfurter.app/latest?amount=${amount}&from=${from}&to=${to}`
    );
    const rate = response.data.rates[to];
    return `${amount} ${from} = ${rate} ${to}`;
  } catch (error) {
    return `Error converting currency: ${error.message}`;
  }
}

function evaluateMath(expression) {
  try {
    // Securely evaluate—consider using a library like mathjs for production
    const result = Function(''use strict'; return (' + expression + ')')();
    return `Result: ${result}`;
  } catch (error) {
    return `Error evaluating expression: ${error.message}`;
  }
}

Building the Agentic Loop

The core of the agent is a loop that sends the user's query to Gemini, processes any function call requests, executes them, and continues until the model produces a final answer without function calls.

async function agent(query) {
  const chat = model.startChat({ tools });
  let result = await chat.sendMessage(query);
  let response = result.response;

  while (response.functionCalls) {
    const functionCalls = response.functionCalls;
    const functionResponses = await Promise.all(
      functionCalls.map(async (fc) => {
        let result;
        switch (fc.name) {
          case 'get_current_weather':
            result = await getCurrentWeather(fc.args.city);
            break;
          case 'convert_currency':
            result = await convertCurrency(fc.args.amount, fc.args.from, fc.args.to);
            break;
          case 'evaluate_math':
            result = evaluateMath(fc.args.expression);
            break;
          default:
            result = 'Unknown function';
        }
        return {
          name: fc.name,
          response: { result },
        };
      })
    );

    // Send function responses back to the model
    result = await chat.sendMessage(functionResponses);
    response = result.response;
  }

  return response.text;
}

// Example usage
agent("What's the weather in Berlin and convert 500 EUR to USD?")
  .then(response => console.log(response))
  .catch(error => console.error(error));

This loop ensures the agent handles multi-step queries, calling tools sequentially as needed until it produces a final, coherent response to the user.

With this foundation, you can expand the agent by adding more tools, improving error handling, or integrating with a web server for a full conversational interface.

via FreeCodeCamp

Related