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:
- Define Tools: You describe available functions using JSON Schema
- User Request: User asks for something that requires action
- AI Decides: AI chooses which function to call and with what arguments
- Execute: Your code executes the function and returns results
- AI Responds: AI incorporates results into final response
Defining Tools with JSON Schema
Tools are defined using JSON Schema—a standard way to describe the structure of JSON data:
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. Addenumfor fixed optionsnumber- Integers or decimals. Useminimum/maximumfor rangesboolean- True or false valuesarray- Lists of items. Defineitemsschemaobject- Nested objects with their own properties
OpenAI Function Calling
Here's a complete example of building an AI agent with OpenAI:
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:
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
Security Best Practices
- Validate All Inputs: Never trust AI-generated arguments. Validate with Zod before execution.
- Limit Capabilities: Only give agents the minimum permissions needed.
- Require Approval: For high-risk actions, require human confirmation.
- Log Everything: Maintain audit logs of all agent actions.
- Set Rate Limits: Prevent runaway agents with iteration limits.
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
- Write Detailed Descriptions: The AI relies on your function descriptions to decide when to use them
- Use Enums When Possible: Constrain possible values to reduce errors
- Handle Partial Failures: Some steps may fail in multi-step workflows—design for graceful handling
- Set Reasonable Timeouts: Function calls can hang—set timeouts for both AI and function calls
- Test Edge Cases: Test what happens with unexpected arguments or function errors
- 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!