AI & JSON 18 min read

Building AI Agents with JSON: Function Calling & Tool Use Guide

Learn how to build AI agents that can take actions using JSON function calling. Complete guide with OpenAI, Claude, and real-world examples for 2025.

#ai-agents #function-calling #tool-use #chatgpt #claude #json-schema #automation

AI agents are transforming how we build software—they can search databases, send emails, book flights, and execute code. The secret behind all of this? JSON function calling. This guide shows you exactly how to build AI agents that take real actions.

What Are AI Agents?

An AI agent is an AI system that can take actions in the real world—not just generate text. Instead of saying "you should search Google for X," an agent actually searches Google and returns the results.

🤖 Regular Chatbot

"To check the weather, you can visit weather.com or use a weather API..."

⚡ AI Agent

"Let me check that for you. The weather in Tokyo is 72°F and sunny."

What Can AI Agents Do?

  • Search & Retrieve: Query databases, search the web, fetch API data
  • Communication: Send emails, Slack messages, SMS notifications
  • Scheduling: Create calendar events, set reminders, book meetings
  • Data Management: Create records, update databases, manage files
  • E-Commerce: Process orders, check inventory, handle returns
  • Code Execution: Run scripts, execute calculations, deploy code

How Function Calling Works

Function calling is the mechanism that enables AI agents. Here's the flow:

  1. Define Tools: You describe available functions using JSON Schema
  2. User Request: User asks for something that requires action
  3. AI Decides: AI chooses which function to call and with what arguments
  4. Execute: Your code executes the function and returns results
  5. AI Responds: AI incorporates results into final response
Key Insight: The AI doesn't actually execute functions—it outputs JSON that describes which function to call and what arguments to pass. Your code does the actual execution.

Defining Tools with JSON Schema

Tools are defined using JSON Schema—a standard way to describe the structure of JSON data:

tool-definition.js
javascript
const weatherTool = {
  type: "function",
  function: {
    // Name must be unique and descriptive
    name: "get_current_weather",
    
    // Description helps the AI understand when to use this tool
    description: "Get the current weather in a specific location.",
    
    // Parameters define what arguments the function accepts
    parameters: {
      type: "object",
      properties: {
        location: {
          type: "string",
          description: "City and country, e.g., 'Tokyo, Japan'"
        },
        unit: {
          type: "string",
          enum: ["celsius", "fahrenheit"],
          description: "Temperature unit"
        }
      },
      required: ["location"]
    }
  }
};

JSON Schema Types You'll Use

  • string - Text values. Add enum for fixed options
  • number - Integers or decimals. Use minimum/maximum for ranges
  • boolean - True or false values
  • array - Lists of items. Define items schema
  • object - Nested objects with their own properties

OpenAI Function Calling

Here's a complete example of building an AI agent with OpenAI:

openai-agent.js
javascript
import OpenAI from 'openai';

const openai = new OpenAI();

// Define your tools
const tools = [
  {
    type: "function",
    function: {
      name: "get_weather",
      description: "Get current weather for a location",
      parameters: {
        type: "object",
        properties: {
          location: { type: "string", description: "City name" },
          unit: { type: "string", enum: ["celsius", "fahrenheit"] }
        },
        required: ["location"]
      }
    }
  },
  {
    type: "function", 
    function: {
      name: "search_web",
      description: "Search the web for information",
      parameters: {
        type: "object",
        properties: {
          query: { type: "string", description: "Search query" }
        },
        required: ["query"]
      }
    }
  }
];

// Implement your actual functions
const functionImplementations = {
  get_weather: async ({ location, unit = "celsius" }) => {
    // Call a real weather API here
    return { location, temperature: 22, unit, conditions: "sunny" };
  },
  search_web: async ({ query }) => {
    // Call a real search API here
    return { results: [{ title: "Result 1", snippet: "..." }] };
  }
};

// The agent loop
async function runAgent(userMessage) {
  const messages = [
    { role: "system", content: "You can check weather and search the web." },
    { role: "user", content: userMessage }
  ];

  while (true) {
    const response = await openai.chat.completions.create({
      model: "gpt-4o",
      messages,
      tools,
      tool_choice: "auto"
    });

    const assistantMessage = response.choices[0].message;
    messages.push(assistantMessage);

    if (assistantMessage.tool_calls) {
      for (const toolCall of assistantMessage.tool_calls) {
        const functionName = toolCall.function.name;
        const functionArgs = JSON.parse(toolCall.function.arguments);
        
        console.log(`Calling ${functionName} with:`, functionArgs);
        
        const functionResult = await functionImplementations[functionName](functionArgs);
        
        messages.push({
          role: "tool",
          tool_call_id: toolCall.id,
          content: JSON.stringify(functionResult)
        });
      }
    } else {
      return assistantMessage.content;
    }
  }
}

// Usage
const response = await runAgent("What's the weather in Paris?");
// "The current weather in Paris is 22°C and sunny."

Claude Tool Use

Anthropic's Claude uses a similar API:

claude-agent.js
javascript
import Anthropic from '@anthropic-ai/sdk';

const anthropic = new Anthropic();

const tools = [
  {
    name: "get_weather",
    description: "Get current weather for a location",
    input_schema: {
      type: "object",
      properties: {
        location: { type: "string", description: "City name" },
        unit: { type: "string", enum: ["celsius", "fahrenheit"] }
      },
      required: ["location"]
    }
  }
];

async function runClaudeAgent(userMessage) {
  let messages = [{ role: "user", content: userMessage }];

  while (true) {
    const response = await anthropic.messages.create({
      model: "claude-3-5-sonnet-20241022",
      max_tokens: 4096,
      tools,
      messages
    });

    if (response.stop_reason === "tool_use") {
      const toolUseBlock = response.content.find(b => b.type === "tool_use");
      
      console.log(`Claude calling: ${toolUseBlock.name}`);
      
      const result = await executeFunction(toolUseBlock.name, toolUseBlock.input);
      
      messages.push({ role: "assistant", content: response.content });
      messages.push({
        role: "user",
        content: [{
          type: "tool_result",
          tool_use_id: toolUseBlock.id,
          content: JSON.stringify(result)
        }]
      });
    } else {
      const textBlock = response.content.find(b => b.type === "text");
      return textBlock?.text || "";
    }
  }
}

Real-World Agent Examples

1. Customer Support Agent

Tools: lookup_order, check_shipping, process_refund, create_ticket

Example: User asks "Where's my order #12345?" → Agent looks up order and provides tracking info.

2. Code Assistant Agent

Tools: read_file, write_file, search_codebase, run_tests

Example: User asks "Fix the failing tests" → Agent reads tests, identifies issues, writes fixes.

3. Data Analysis Agent

Tools: query_database, run_python, create_chart, export_csv

Example: User asks "Top 10 products by revenue" → Agent queries DB and generates report.

Security Considerations

⚠️ Critical: AI agents can take real actions. A poorly secured agent could delete data, send unauthorized emails, or expose sensitive information.

Security Best Practices

  1. Validate All Inputs: Never trust AI-generated arguments. Validate with Zod before execution.
  2. Limit Capabilities: Only give agents the minimum permissions needed.
  3. Require Approval: For high-risk actions, require human confirmation.
  4. Log Everything: Maintain audit logs of all agent actions.
  5. Set Rate Limits: Prevent runaway agents with iteration limits.
validation.ts
typescript
import { z } from 'zod';

// Validate before executing any function
const EmailSchema = z.object({
  to: z.string().email(),
  subject: z.string().max(200),
  body: z.string().max(10000)
});

// This throws if validation fails
EmailSchema.parse(aiGeneratedArgs);

Best Practices

  1. Write Detailed Descriptions: The AI relies on your function descriptions to decide when to use them
  2. Use Enums When Possible: Constrain possible values to reduce errors
  3. Handle Partial Failures: Some steps may fail in multi-step workflows—design for graceful handling
  4. Set Reasonable Timeouts: Function calls can hang—set timeouts for both AI and function calls
  5. Test Edge Cases: Test what happens with unexpected arguments or function errors
  6. Implement Rate Limiting: Set a maximum number of iterations per conversation

Conclusion

AI agents with function calling represent a paradigm shift in software development. Instead of writing complex conditional logic, you define what's possible and let the AI figure out how to accomplish the user's goal.

Key takeaways:

  • Define tools with clear, detailed JSON Schema descriptions
  • Implement proper error handling and validation
  • Build security measures from day one
  • Start simple and add capabilities incrementally

Use our JSON tools to format and validate your function schemas. The Schema Generator can help you create JSON Schema from examples!

About the Author

AT

Adam Tse

Founder & Lead Developer · 10+ years experience

Full-stack engineer with 10+ years of experience building developer tools and APIs. Previously worked on data infrastructure at scale, processing billions of JSON documents daily. Passionate about creating privacy-first tools that don't compromise on functionality.

JavaScript/TypeScript Web Performance Developer Tools Data Processing