45-Function-Calling-让AI调用你的Python函数

文章目录

  • [【42.Python+AI】大模型Function Calling:让AI调用你的Python函数,从聊天机器人到智能助手](#【42.Python+AI】大模型Function Calling:让AI调用你的Python函数,从聊天机器人到智能助手)
    • 导入语
    • [1 ~> Function Calling的核心流程](#1 ~> Function Calling的核心流程)
    • [2 ~> 定义Tool:告诉AI你有哪些函数](#2 ~> 定义Tool:告诉AI你有哪些函数)
      • [2.1 JSON Schema规范](#2.1 JSON Schema规范)
      • [2.2 实际实现函数](#2.2 实际实现函数)
    • [3 ~> 完整实现:处理Function Calling循环](#3 ~> 完整实现:处理Function Calling循环)
    • [4 ~> 实战:AI助手------查数据库+发邮件](#4 ~> 实战:AI助手——查数据库+发邮件)
      • [4.1 定义多个Tool](#4.1 定义多个Tool)
      • [4.2 实现函数](#4.2 实现函数)
      • [4.3 完整对话处理](#4.3 完整对话处理)
    • [思考 && 总结](#思考 && 总结)
    • 结尾

【42.Python+AI】大模型Function Calling:让AI调用你的Python函数,从聊天机器人到智能助手

📖 文章简介: 本文深入拆解大模型Function Calling(函数调用)的完整机制。从Tool定义的JSON Schema规范讲起,到单轮与多轮对话中的函数调用链实现,再到一个可自动查数据库+发送邮件的AI助手完整实战。文中包含Mermaid时序图展示Function Calling的请求-响应-执行-反馈全链路,并对比了不同国产模型对Function Calling的支持现状,适合想让AI从"只会聊天"升级为"能执行操作"的开发者。


🎬 个人主页: 源码骑士

专栏传送门: 《Android开发基础》《python基础课程》

⭐️热衷从源码视角拆解技术底层原理,将复杂架构讲得通俗易懂


🎬 源码骑士的简介:

5年Android Framework系统开发经验,曾主导多项系统级性能优化专项

技术栈覆盖Android系统全链路(Binder/Handler/AMS/WMS/启动流程)及Java后端全家桶(Spring + MyBatis + Redis + Oracle)

累计产出原创技术文章100+篇,文章以流程图为特色,被读者评价为"看一篇胜过啃一周源码"


导入语

你花了两周搭了一个AI客服Bot,效果不错。但老板问:"它能查订单状态吗?能帮用户修改收货地址吗?"------你沉默了。

如果AI只能聊天、不能执行操作,它永远是一个"有趣的玩具"而不是"有用的工具"。Function Calling就是解决这个痛点的关键能力------让大模型决定"该调用哪个函数",你来实现这个函数,把结果返回给模型,模型再用自然语言告诉用户。

这篇文章就是带你从零实现一个能查数据库、发邮件的AI助手。整个过程你会彻底理解Function Calling的请求-响应-执行循环。


1 ~> Function Calling的核心流程

数据库/API 大模型 你的AI应用 用户 数据库/API 大模型 你的AI应用 用户 #mermaid-svg-3KPdVy2AK2sVDdMH{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-3KPdVy2AK2sVDdMH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3KPdVy2AK2sVDdMH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3KPdVy2AK2sVDdMH .error-icon{fill:#552222;}#mermaid-svg-3KPdVy2AK2sVDdMH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3KPdVy2AK2sVDdMH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3KPdVy2AK2sVDdMH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3KPdVy2AK2sVDdMH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3KPdVy2AK2sVDdMH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3KPdVy2AK2sVDdMH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3KPdVy2AK2sVDdMH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3KPdVy2AK2sVDdMH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3KPdVy2AK2sVDdMH .marker.cross{stroke:#333333;}#mermaid-svg-3KPdVy2AK2sVDdMH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3KPdVy2AK2sVDdMH p{margin:0;}#mermaid-svg-3KPdVy2AK2sVDdMH .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-3KPdVy2AK2sVDdMH text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-3KPdVy2AK2sVDdMH .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-3KPdVy2AK2sVDdMH .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-3KPdVy2AK2sVDdMH .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-3KPdVy2AK2sVDdMH .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-3KPdVy2AK2sVDdMH #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-3KPdVy2AK2sVDdMH .sequenceNumber{fill:white;}#mermaid-svg-3KPdVy2AK2sVDdMH #sequencenumber{fill:#333;}#mermaid-svg-3KPdVy2AK2sVDdMH #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-3KPdVy2AK2sVDdMH .messageText{fill:#333;stroke:none;}#mermaid-svg-3KPdVy2AK2sVDdMH .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-3KPdVy2AK2sVDdMH .labelText,#mermaid-svg-3KPdVy2AK2sVDdMH .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-3KPdVy2AK2sVDdMH .loopText,#mermaid-svg-3KPdVy2AK2sVDdMH .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-3KPdVy2AK2sVDdMH .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-3KPdVy2AK2sVDdMH .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-3KPdVy2AK2sVDdMH .noteText,#mermaid-svg-3KPdVy2AK2sVDdMH .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-3KPdVy2AK2sVDdMH .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-3KPdVy2AK2sVDdMH .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-3KPdVy2AK2sVDdMH .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-3KPdVy2AK2sVDdMH .actorPopupMenu{position:absolute;}#mermaid-svg-3KPdVy2AK2sVDdMH .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-3KPdVy2AK2sVDdMH .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-3KPdVy2AK2sVDdMH .actor-man circle,#mermaid-svg-3KPdVy2AK2sVDdMH line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-3KPdVy2AK2sVDdMH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 模型判断:需要调用 get_order_status函数 "帮我查订单 发送消息 + 可用函数列表 返回函数调用请求 function_name="get_order_status" arguments={"order_id":"12345"} 执行 get_order_status("12345") 返回 {"status":"已发货","tracking":"SF123456"} 把函数执行结果发送给模型 "您的订单 "您的订单


2 ~> 定义Tool:告诉AI你有哪些函数

2.1 JSON Schema规范

python 复制代码
# 定义一个"查天气"的函数描述
weather_tool = {
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "获取指定城市的天气信息。使用此函数当用户询问天气相关问题。",
        "parameters": {
            "type": "object",
            "properties": {
                "city": {
                    "type": "string",
                    "description": "城市名称,如'北京'、'上海'",
                },
                "unit": {
                    "type": "string",
                    "enum": ["celsius", "fahrenheit"],
                    "description": "温度单位",
                }
            },
            "required": ["city"],
        }
    }
}

2.2 实际实现函数

python 复制代码
def get_weather(city: str, unit: str = "celsius") -> dict:
    """实际查询天气的函数(这里用模拟数据)"""
    # 真实场景:调用天气API
    weather_data = {
        "北京": {"temp": 28, "humidity": 65, "condition": "晴"},
        "上海": {"temp": 32, "humidity": 80, "condition": "多云"},
    }
    data = weather_data.get(city, {"temp": 25, "humidity": 50, "condition": "未知"})
    return {
        "city": city,
        "temperature": data["temp"],
        "humidity": data["humidity"],
        "condition": data["condition"],
        "unit": unit,
    }

3 ~> 完整实现:处理Function Calling循环

python 复制代码
import json
from openai import OpenAI

client = OpenAI()

# 函数注册表
AVAILABLE_FUNCTIONS = {
    "get_weather": get_weather,
}


def run_conversation(user_message: str):
    """处理一轮对话,自动判断是否需要调用函数"""
    
    messages = [{"role": "user", "content": user_message}]
    tools = [weather_tool]
    
    # 第一轮:把用户消息和可用工具发给模型
    response = client.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=messages,
        tools=tools,
        tool_choice="auto",  # 让模型自己决定是否调用函数
    )
    
    response_message = response.choices[0].message
    tool_calls = response_message.tool_calls
    
    # 检查模型是否决定调用函数
    if tool_calls:
        # 把模型的回复加入消息历史
        messages.append(response_message)
        
        for tool_call in tool_calls:
            function_name = tool_call.function.name
            function_args = json.loads(tool_call.function.arguments)
            
            print(f"🔧 调用函数: {function_name}")
            print(f"   参数: {function_args}")
            
            # 执行函数
            function_to_call = AVAILABLE_FUNCTIONS[function_name]
            function_result = function_to_call(**function_args)
            
            # 把函数执行结果加入消息历史
            messages.append({
                "tool_call_id": tool_call.id,
                "role": "tool",
                "name": function_name,
                "content": json.dumps(function_result, ensure_ascii=False),
            })
        
        # 第二轮:把函数结果发给模型,让它生成最终回答
        second_response = client.chat.completions.create(
            model="gpt-3.5-turbo",
            messages=messages,
        )
        return second_response.choices[0].message.content
    
    # 模型不调用函数,直接返回
    return response_message.content


# 测试
print(run_conversation("北京今天天气怎么样?"))
print("---")
print(run_conversation("上海的温度是多少华氏度?"))

4 ~> 实战:AI助手------查数据库+发邮件

4.1 定义多个Tool

python 复制代码
import sqlite3
import smtplib
from email.mime.text import MIMEText

# Tool 1: 查询订单
order_tool = {
    "type": "function",
    "function": {
        "name": "query_order",
        "description": "根据订单号查询订单信息,包括状态、金额、收货地址",
        "parameters": {
            "type": "object",
            "properties": {
                "order_id": {"type": "string", "description": "订单号"}
            },
            "required": ["order_id"],
        }
    }
}

# Tool 2: 发送邮件
email_tool = {
    "type": "function",
    "function": {
        "name": "send_email",
        "description": "发送邮件给指定收件人",
        "parameters": {
            "type": "object",
            "properties": {
                "to": {"type": "string", "description": "收件人邮箱"},
                "subject": {"type": "string", "description": "邮件主题"},
                "body": {"type": "string", "description": "邮件正文"},
            },
            "required": ["to", "subject", "body"],
        }
    }
}

4.2 实现函数

python 复制代码
def query_order(order_id: str) -> dict:
    """从数据库查询订单"""
    conn = sqlite3.connect("orders.db")
    cursor = conn.execute(
        "SELECT * FROM orders WHERE id = ?", (order_id,)
    )
    row = cursor.fetchone()
    conn.close()
    
    if row:
        return {
            "order_id": row[0],
            "status": row[1],
            "amount": row[2],
            "address": row[3],
        }
    return {"error": f"订单{order_id}不存在"}


def send_email(to: str, subject: str, body: str) -> dict:
    """发送邮件"""
    try:
        msg = MIMEText(body, "plain", "utf-8")
        msg["Subject"] = subject
        msg["To"] = to
        
        # 实际发送(此处为示例,需配置SMTP)
        # with smtplib.SMTP("smtp.example.com") as server:
        #     server.send_message(msg)
        
        return {"success": True, "message": f"邮件已发送至 {to}"}
    except Exception as e:
        return {"success": False, "error": str(e)}


AVAILABLE_FUNCTIONS = {
    "query_order": query_order,
    "send_email": send_email,
}

ALL_TOOLS = [order_tool, email_tool]

4.3 完整对话处理

python 复制代码
def ai_assistant(user_message: str) -> str:
    """AI助手入口"""
    
    messages = [{"role": "user", "content": user_message}]
    
    # 循环处理:因为一个请求可能需要多次函数调用
    while True:
        response = client.chat.completions.create(
            model="gpt-3.5-turbo",
            messages=messages,
            tools=ALL_TOOLS,
            tool_choice="auto",
        )
        
        msg = response.choices[0].message
        
        # 没有函数调用 → 模型给出了最终回答
        if not msg.tool_calls:
            return msg.content
        
        # 有函数调用 → 执行并继续循环
        messages.append(msg)
        for tool_call in msg.tool_calls:
            func_name = tool_call.function.name
            func_args = json.loads(tool_call.function.arguments)
            
            result = AVAILABLE_FUNCTIONS[func_name](**func_args)
            
            messages.append({
                "tool_call_id": tool_call.id,
                "role": "tool",
                "name": func_name,
                "content": json.dumps(result, ensure_ascii=False),
            })


# 使用示例
print(ai_assistant("查一下订单#12345的状态,然后发邮件到admin@test.com通知负责人"))

思考 && 总结

  1. Function Calling把AI从"嘴"升级成"手": 模型只负责"决策"------调用哪个函数、传什么参数,函数的实际执行完全在你的控制之下。
  2. Tool定义的质量决定调用准确率: description不仅要写清楚函数做什么,还要告诉模型"什么时候该用"。写得模糊 → 模型乱调用。
  3. 函数调用可以串联: 上面的例子中,AI自动完成"先查订单→再发邮件"的串联操作。关键在于你用while True循环让模型多轮调用。
  4. 国产模型支持差异大: GPT和DeepSeek的Function Calling比较成熟,通义千问和GLM的支持在快速追赶。上线前务必测试目标模型的调用准确率。
  5. 安全是第一优先级: 模型调用什么函数、传什么参数完全是"AI决定"的。对敏感操作(删除、转账)必须加人工确认或权限校验。

Function Calling是AI应用从Demo走向Product的分水岭。


结尾

各位小伙伴,本文的内容到这里就全部结束了,源码骑士在这里再次感谢您的阅读!

源码骑士 --- Android Framework & 全栈开发

👀 关注:跟博主一起从源码视角深耕底层原理,见证每一次成长

❤️ 点赞:让优质内容被更多人看见,让知识传递更有力量

收藏:BatchProcessor代码存好,下个批量任务直接复用

💬 评论:你批量调API最高用过多少并发?评论区交流

🔄 一键四连:不要忘记给博主"一键四连"哦!今日并发调优达成!

🗡️ 寄语:慢不是模型的问题,是你的调用方式没有发挥它的并发能力。

结语:现在就把本文的Function Calling循环代码拷进你的项目,给你的AI装上第一个"技能"------查天气也好,查数据库也好。一旦跑通,你会打开一个全新的世界。不要忘记给博主"一键四连"哦!