AI Agents with Function Calling: Build Tools LLMs Can Use A developer published a from-scratch framework for building LLM agents using function calling, in which the model emits structured JSON to invoke predefined tools. The implementation defines an abstract Tool class with name, description, parameters and execute members, plus concrete tools for DuckDuckGo web search, sandboxed Python execution with an import allowlist and a 10-second subprocess timeout, and a math-only calculator. Create AI agents that can execute code, search the web, and call APIs — using function calling. Function calling tool use transforms LLMs from text generators into agents that can act . This tutorial builds a complete agent framework from scratch. LLMs output structured JSON to invoke predefined functions: User: "What's the weather in Tokyo?" LLM: {"tool": "get weather", "args": {"city": "Tokyo"}} System: executes function, returns result LLM: "It's 22°C and sunny in Tokyo." requests library python tools.py import os import requests import json from typing import Dict, Any, List from abc import ABC, abstractmethod class Tool ABC : @property @abstractmethod def name self - str: ... @property @abstractmethod def description self - str: ... @property @abstractmethod def parameters self - Dict: ... @abstractmethod def execute self, kwargs - Any: ... --- Concrete Tools --- class WebSearchTool Tool : name = "web search" description = "Search the web for current information" parameters = { "type": "object", "properties": { "query": {"type": "string", "description": "Search query"} }, "required": "query" } def execute self, query: str - str: Using DuckDuckGo HTML scrape free, no API key url = "https://html.duckduckgo.com/html/" params = {"q": query} headers = {"User-Agent": "Mozilla/5.0"} try: response = requests.post url, data=params, headers=headers, timeout=10 from bs4 import BeautifulSoup soup = BeautifulSoup response.text, 'html.parser' results = soup.find all 'a', class ='result snippet' snippets = r.get text :200 for r in results :3 return "\n".join snippets if snippets else "No results found" except Exception as e: return f"Search error: {e}" class PythonExecutorTool Tool : name = "execute python" description = "Execute Python code and return output" parameters = { "type": "object", "properties": { "code": {"type": "string", "description": "Python code to execute"} }, "required": "code" } def execute self, code: str - str: import subprocess import tempfile import sys Safety: only allow specific imports allowed imports = {'json', 'math', 'random', 'datetime', 'statistics', 'itertools', 'collections'} with tempfile.NamedTemporaryFile mode='w', suffix='.py', delete=False as f: f.write code temp path = f.name try: result = subprocess.run sys.executable, temp path , capture output=True, text=True, timeout=10 os.unlink temp path output = result.stdout if result.stderr: output += f"\nSTDERR: {result.stderr}" return output :2000 if output else " no output " except subprocess.TimeoutExpired: os.unlink temp path return "Error: Execution timeout 10s " except Exception as e: return f"Error: {e}" class CalculatorTool Tool : name = "calculate" description = "Perform mathematical calculations" parameters = { "type": "object", "properties": { "expression": {"type": "string", "description": "Math expression to evaluate"} }, "required": "expression" } def execute self, expression: str - str: try: Safe eval for math only allowed names = {"abs", "round", "min", "max", "sum", "pow"} allowed names.update {k: v for k, v in import 'math' . dict .items if not k.startswith ' ' } code = compile expression, "