Deep Dive into Function Calling for AI Agents: Enabling External Tool Integration

This article explains the concept of Function Calling in large language models, walks through defining function schemas, shows step‑by‑step API call flows, demonstrates multi‑tool orchestration, parallel execution, tool‑chain composition, and integrates Function Calling with LangChain, while providing best‑practice guidelines and code examples.

Coder Trainee
Coder Trainee
Coder Trainee
Deep Dive into Function Calling for AI Agents: Enabling External Tool Integration

1. What Is Function Calling?

Function Calling is a capability of large language models that lets the model decide when to invoke a function and generate arguments that match the function’s signature.

┌─────────────────────────────────────────────────────────────────┐
│            Function Calling Workflow                         │
├─────────────────────────────────────────────────────────────────┤
│                                                               │
│  User: "北京天气怎么样?帮我算一下 100+200"                     │
│                                                               ▼
│   ┌─────────────────────────────────────────────────────┐   │
│   │                     LLM Analysis                     │   │
│   │  Identifies two required calls:                     │   │
│   │   1. get_weather(city="北京")                       │   │
│   │   2. calculate(expression="100+200")               │   │
│   └─────────────────────────────────────────────────────┘   │
│                                                               │
│   ┌───────────────┼───────────────┐                         │
│   ▼               ▼               ▼                         │
│ ┌───────┐   ┌───────┐   ┌───────┐                         │
│ │Weather│   │Calculator│ │Other Tool│                         │
│ └───────┘   └───────┘   └───────┘                         │
│                                                               │
│                ▼                                            │
│          Return results to the user                         │
└───────────────────────────────────────────────────────────────┘

2. Function Calling vs. Normal Prompting

Normal Prompting : Tell the model "you can call functions"; the model generates a textual call. Simple but unreliable and hard to parse.

Function Calling : Native model support; the model returns structured JSON. Reliable and type‑safe, but requires a model that supports the feature.

3. Core Principles

3.1 Function Schema Definition

# Example function definition
functions = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "获取指定城市的天气信息",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {"type": "string", "description": "城市名称,如:北京、上海"},
                    "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
                },
                "required": ["city"]
            }
        }
    }
]

3.2 API Call Flow

import json
from openai import OpenAI

client = OpenAI(api_key="your-api-key")

def chat_with_functions(user_message):
    # 1. First call – let the model decide whether to call a function
    response = client.chat.completions.create(
        model="gpt-4",
        messages=[{"role": "user", "content": user_message}],
        tools=functions,
        tool_choice="auto"
    )
    message = response.choices[0].message

    # 2. If the model decided to call a function
    if message.tool_calls:
        for tool_call in message.tool_calls:
            function_name = tool_call.function.name
            arguments = json.loads(tool_call.function.arguments)
            # 3. Execute the actual function
            if function_name == "get_weather":
                result = get_weather(arguments["city"])
            # 4. Second call – return the function result to the model
            response = client.chat.completions.create(
                model="gpt-4",
                messages=[
                    {"role": "user", "content": user_message},
                    message,
                    {"role": "tool", "tool_call_id": tool_call.id, "content": result}
                ]
            )
            return response.choices[0].message.content
    return message.content or ""

4. Multi‑Tool Coordination

4.1 Tool Definitions

# tools.py
import json, requests
from datetime import datetime

# Weather query
def get_weather(city: str, unit: str = "celsius") -> str:
    """获取天气信息"""
    weather_data = {
        "北京": {"temp": 25, "condition": "晴", "humidity": 45},
        "上海": {"temp": 22, "condition": "多云", "humidity": 65},
        "深圳": {"temp": 28, "condition": "雨", "humidity": 85}
    }
    info = weather_data.get(city, {"temp": 20, "condition": "晴", "humidity": 50})
    unit_symbol = "°C" if unit == "celsius" else "°F"
    return f"{city}天气:{info['condition']},{info['temp']}{unit_symbol},湿度{info['humidity']}%"

# Mathematical calculation
def calculate(expression: str) -> str:
    """数学计算"""
    try:
        allowed_names = {"abs": abs, "round": round}
        result = eval(expression, {"__builtins__": {}}, allowed_names)
        return f"计算结果: {expression} = {result}"
    except Exception as e:
        return f"计算错误: {e}"

# Current time
def get_current_time(timezone: str = "Asia/Shanghai") -> str:
    """获取当前时间"""
    now = datetime.now()
    return now.strftime("%Y年%m月%d日 %H:%M:%S")

# HTTP request (GET only)
def http_request(url: str, method: str = "GET") -> str:
    """发送 HTTP 请求"""
    try:
        if method.upper() == "GET":
            resp = requests.get(url, timeout=5)
            return f"状态码: {resp.status_code}
响应: {resp.text[:200]}"
        return "只支持 GET 请求"
    except Exception as e:
        return f"请求失败: {e}"

# Simulated email sending
def send_email(recipient: str, subject: str, content: str) -> str:
    """发送邮件(模拟)"""
    print(f"
📧 [邮件模拟] 发送至: {recipient}")
    print(f"   主题: {subject}")
    print(f"   内容: {content}")
    return f"邮件已发送至 {recipient}"

# Simulated database query
def query_database(sql: str) -> str:
    """执行 SQL 查询(模拟)"""
    mock_data = {
        "select * from users": "[{'id':1,'name':'张三'},{'id':2,'name':'李四'}]",
        "select * from orders": "[{'id':100,'amount':299}]"
    }
    return mock_data.get(sql.lower(), f"查询结果: 模拟数据 for '{sql}'")

4.2 Complete Function‑Calling Agent

# function_calling_agent.py
import json
from openai import OpenAI
from tools import TOOLS, FUNCTION_MAP

class FunctionCallingAgent:
    """支持 Function Calling 的 Agent"""
    def __init__(self, api_key: str, model: str = "gpt-4"):
        self.client = OpenAI(api_key=api_key)
        self.model = model
        self.tools = TOOLS
        self.function_map = FUNCTION_MAP
        self.messages = []

    def add_message(self, role: str, content: str, tool_calls=None, tool_call_id=None):
        """添加消息到对话历史"""
        message = {"role": role, "content": content}
        if tool_calls:
            message["tool_calls"] = tool_calls
        if tool_call_id:
            message["tool_call_id"] = tool_call_id
        self.messages.append(message)

    def execute_tool(self, tool_call):
        """执行工具调用"""
        function_name = tool_call.function.name
        arguments = json.loads(tool_call.function.arguments)
        print(f"
🔧 调用工具: {function_name}")
        print(f"📥 参数: {arguments}")
        if function_name in self.function_map:
            result = self.function_map[function_name](**arguments)
            print(f"📤 结果: {result}")
            return str(result)
        return f"未知工具: {function_name}"

    def chat(self, user_input: str, max_tool_calls: int = 5) -> str:
        """与 Agent 对话"""
        self.add_message("user", user_input)
        tool_calls_count = 0
        while tool_calls_count < max_tool_calls:
            response = self.client.chat.completions.create(
                model=self.model,
                messages=self.messages,
                tools=self.tools,
                tool_choice="auto"
            )
            message = response.choices[0].message
            self.messages.append(message.model_dump())
            if not getattr(message, "tool_calls", None):
                return message.content or ""
            for tool_call in message.tool_calls:
                result = self.execute_tool(tool_call)
                self.add_message("tool", result, tool_call_id=tool_call.id)
                tool_calls_count += 1
        final_response = self.client.chat.completions.create(
            model=self.model,
            messages=self.messages
        )
        return final_response.choices[0].message.content or ""

    def reset(self):
        """重置对话历史"""
        self.messages = []

def interactive_mode():
    """交互式模式"""
    import os
    from dotenv import load_dotenv
    load_dotenv()
    agent = FunctionCallingAgent(api_key=os.getenv("OPENAI_API_KEY"))
    print("
" + "="*60)
    print("🔧 Function Calling Agent")
    print("="*60)
    print("可用工具:")
    print("   • get_weather - 天气查询")
    print("   • calculate - 数学计算")
    print("   • get_current_time - 当前时间")
    print("   • send_email - 发送邮件(模拟)")
    print("-"*60)
    print("输入 'exit' 退出,'reset' 重置对话")
    print("="*60)
    while True:
        try:
            user_input = input("
👤 你: ").strip()
            if user_input.lower() in ["exit", "quit", "q"]:
                print("👋 再见!")
                break
            if user_input.lower() == "reset":
                agent.reset()
                print("✅ 对话已重置")
                continue
            if not user_input:
                continue
            response = agent.chat(user_input)
            print(f"
🤖 Agent: {response}")
        except KeyboardInterrupt:
            print("
👋 再见!")
            break
        except Exception as e:
            print(f"❌ 错误: {e}")

if __name__ == "__main__":
    interactive_mode()

5. Parallel Function Calls

# parallel_calls.py
import json, asyncio
from openai import AsyncOpenAI

class ParallelFunctionCallingAgent:
    """支持并行函数调用的 Agent"""
    def __init__(self, api_key: str):
        self.client = AsyncOpenAI(api_key=api_key)
        self.tools = TOOLS
        self.function_map = FUNCTION_MAP

    async def execute_tool_async(self, tool_call):
        """异步执行单个工具"""
        function_name = tool_call.function.name
        arguments = json.loads(tool_call.function.arguments)
        print(f"🔧 并行执行: {function_name}")
        if function_name in self.function_map:
            result = self.function_map[function_name](**arguments)
            return (tool_call.id, str(result))
        return (tool_call.id, f"未知工具: {function_name}")

    async def chat(self, user_input: str) -> str:
        """并行执行多个工具调用"""
        messages = [{"role": "user", "content": user_input}]
        response = await self.client.chat.completions.create(
            model="gpt-4",
            messages=messages,
            tools=self.tools,
            tool_choice="auto"
        )
        message = response.choices[0].message
        if not getattr(message, "tool_calls", None):
            return message.content or ""
        tasks = [self.execute_tool_async(tc) for tc in message.tool_calls]
        results = await asyncio.gather(*tasks)
        messages.append(message.model_dump())
        for tool_call_id, result in results:
            messages.append({"role": "tool", "tool_call_id": tool_call_id, "content": result})
        final_response = await self.client.chat.completions.create(
            model="gpt-4",
            messages=messages
        )
        return final_response.choices[0].message.content or ""

async def main():
    agent = ParallelFunctionCallingAgent(api_key="your-key")
    result = await agent.chat("查一下北京和上海的天气")
    print(result)

if __name__ == "__main__":
    asyncio.run(main())

6. Function Calling in LangChain

# langchain_tools.py
from langchain.tools import tool
from langchain.agents import AgentExecutor, create_tool_calling_agent
from langchain_openai import ChatOpenAI
from langchain.prompts import ChatPromptTemplate

@tool
def get_weather(city: str) -> str:
    """获取指定城市的天气"""
    return f"{city}:晴,25°C"

@tool
def calculate(expression: str) -> str:
    """数学计算"""
    return str(eval(expression))

llm = ChatOpenAI(model="gpt-4", temperature=0)
tools = [get_weather, calculate]

prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个智能助手,可以使用工具。"),
    ("human", "{input}"),
    ("placeholder", "{agent_scratchpad}")
])

agent = create_tool_calling_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

result = executor.invoke({"input": "北京天气怎么样?再算一下 100+200"})

7. Best Practices

7.1 Function Description Optimization

Name : Use clear, verb‑starting names such as get_weather, calculate_sum.

Description : Explain the purpose and usage scenario, e.g., "获取城市天气,用于出行建议".

Parameters : Specify type and description, e.g., city: string, 城市名称如'北京'.

7.2 Error Handling

def safe_execute_tool(tool_func, **kwargs):
    """安全执行工具"""
    try:
        result = tool_func(**kwargs)
        return {"success": True, "result": result}
    except Exception as e:
        return {"success": False, "error": str(e)}

7.3 Cost Control

# Limit the number of tool calls
MAX_TOOL_CALLS = 5
# Simple cache to avoid duplicate calls
cache = {}

8. Upcoming Episode Preview

AI Agent 智能体从入门到实战(七):多 Agent 协作

Multi‑Agent architecture patterns

AutoGen / CrewAI practical demo

Task decomposition and coordination

Human‑AI collaboration

我是老 J,下期见。

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

PythonAI Agentstool integrationLangChainBest PracticesOpenAIfunction callingparallel execution
Coder Trainee
Written by

Coder Trainee

Experienced in Java and Python, we share and learn together. For submissions or collaborations, DM us.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.