【Agent学习笔记2:深入理解Function Calling技术:从原理到实践】

从理论到实践:全面掌握 Agent、Function Calling、MCP 与 A2A 的协同开发指南 本文是一份从理论到实践的协同开发指南,旨在帮助开发者全面掌握构建复杂智能系统的核心技术栈。通过一个贯穿始终的旅行规划案例,详细拆解了Agent(智能体)、Function Calling(函数调用)、MCP(模型上下文协议)与A2A(智能体间通信)四大核心概念,并手把手演示了如何将它们组合成一个能自主规划、调用工具、协同工作的智能系统。 阅读详情

深入理解Function Calling技术:从原理到实践

引言

在人工智能和大语言模型(LLM)快速发展的今天,Function Calling(函数调用)技术已经成为扩展AI能力边界的关键技术之一。传统的AI模型虽然具备强大的自然语言理解和生成能力,但其知识库受限于训练数据,无法获取实时信息或执行具体操作。Function Calling技术的出现,完美解决了这一痛点,使得AI模型能够调用外部函数、访问实时数据、执行特定任务。

本文将通过一个完整的实际项目案例,详细讲解Function Calling技术的工作原理、实现方式以及在实际开发中的应用。我们将以阿里云DashScope API为例,演示如何让AI模型具备查询天气、写入文件等真实世界的能力。

一、Function Calling技术概述

1.1 什么是Function Calling

Function Calling是一种让大语言模型与外部工具、API或函数进行交互的技术。在传统的对话场景中,AI模型只能基于其训练数据来回答问题。但现实世界瞬息万变,股票价格、天气情况、新闻事件等实时信息都不在模型的训练数据范围内。Function Calling技术通过以下机制解决了这个问题:

  1. 模型识别需求:当用户提出需要外部信息或执行特定操作的请求时,模型能够识别并判断需要调用外部函数
  2. 参数提取:模型从用户输入中提取函数所需的参数
  3. 函数执行:外部系统执行实际函数调用,获取结果
  4. 结果整合:将函数执行结果返回给模型,由模型生成最终回复

这种机制使得AI模型从"知识库"转变为"智能助手",能够实时获取信息、执行操作,大大扩展了其应用场景。

1.2 Function Calling的应用场景

Function Calling技术的应用场景非常广泛:

  • 信息查询:查询实时天气、股票价格、航班信息、新闻资讯等
  • 数据操作:读写数据库、执行文件操作、管理系统资源
  • 第三方集成:调用第三方API、执行支付操作、发送通知等
  • 任务执行:预订机票酒店、安排日程、生成报告等

通过Function Calling,AI助手不再局限于"聊天",而是真正成为了能够帮助用户完成实际任务的智能助手。

1.3 技术原理简述

Function Calling的工作流程可以分为以下几个步骤:

  1. 用户输入:用户向AI助手提出请求
  2. 意图识别:AI模型分析用户请求,判断是否需要调用外部函数
  3. 参数提取:如果需要函数调用,模型从用户输入中提取相关参数
  4. 函数调用:外部系统执行实际函数,获取结果
  5. 结果处理:将函数结果整合到对话中,生成最终回复

这个过程的关键在于模型能够准确理解用户意图,并正确提取函数参数。这需要精心设计的系统提示词和函数定义。

二、项目整体架构

2.1 项目结构

项目包含一个主Python文件,实现了完整的Function Calling功能。项目结构如下:

项目目录/
├── aliyun_function_calling.py    # 主程序文件(包含Function Calling核心逻辑)
├── .env                          # 环境变量配置文件(存储API密钥)
└── 文档目录/
    └── 阿里云API函数调用与DeepSeek_API对比分析.md  # 技术对比文档

2.2 核心功能

本项目实现了两个核心功能演示:

  1. 天气查询功能:

    • 用户询问天气相关问题(如"明天去北京出差适合穿什么衣服?")
    • AI模型识别需要查询天气,自动调用天气API
    • 获取天气数据后,结合用户问题给出穿衣建议
  2. 文件写入功能:

    • 用户要求写入文件(如"请在Test2.txt文件中写入内容")
    • AI模型识别需要写入文件,自动调用文件操作函数
    • 将指定内容写入到用户指定的文件中

2.3 技术栈

  • 编程语言:Python 3.x
  • HTTP请求库:requests
  • 环境变量管理:python-dotenv
  • AI服务提供商:阿里云DashScope(通义千问模型)
  • 第三方天气API:高德地图天气API

三、核心代码解析

3.1 系统提示词设计

系统提示词(System Prompt)是引导AI模型行为的关键。在我们的项目中,系统提示词定义了AI助手的基本行为准则:

# 系统级提示词
system_prompt = """
	你是一名AI助手,具备函数调用的能力,但是如果提供的信息已经足够回答用户的问题,则不需要再进行函数调用。
	同时,请严格按照函数调用的方式进行处理,如果用户未提供函数所需参数,则必须询问,而不能自作主张。
"""

设计要点:

  1. 明确能力:告知模型具备函数调用能力
  2. 条件判断:如果现有信息足够,则不需要调用函数
  3. 参数完整性:如果参数不完整,必须询问用户而不是自行假设

这种设计确保了AI助手的行为是可预测和可控的,避免了因模型自行假设而导致的错误。

3.2 函数定义

函数定义是Function Calling的核心,它告诉AI模型有哪些函数可以调用、每个函数的用途以及参数要求。在阿里云API中,函数定义采用嵌套的JSON结构:

# 阿里云API需要的tools格式
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "查询某个城市的天气",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description": "中国的城市名称或区县的名称",
                    }
                },
                "required": ["city"]
            },
        }
    },
    {
        "type": "function",
        "function": {
            "name": "write_file",
            "description": "往某个文件中写入内容",
            "parameters": {
                "type": "object",
                "properties": {
                    "filename": {
                        "type": "string",
                        "description": "待写入的文件路径",
                    },
                    "content": {
                        "type": "string",
                        "description": "写入文件中的内容",
                    }
                },
                "required": ["filename", "content"]
            },
        }
    }
]

函数定义的关键字段:

字段说明设计要点
type工具类型固定为 "function"
function.name函数名称使用清晰的英文命名,见名知意
function.description函数描述详细说明函数用途,帮助模型理解何时调用
function.parameters参数定义包含参数类型、属性、描述和是否必需

设计原则:

  1. 描述要具体:函数描述应该让模型能够理解何时应该调用这个函数
  2. 参数要完整:每个参数都应有类型、描述,必要参数要标记在required中
  3. 命名要规范:使用清晰的英文命名,避免歧义

3.3 实际函数实现

函数定义告诉AI模型"有什么函数可以用",而实际的函数实现则是真正执行操作的代码:

# 定义函数用于通过高德天气接口查询天气
def get_weather(city):
    # 从环境变量获取高德地图API密钥
    amap_api_key = os.getenv("AMAP_API_KEY")
    if not amap_api_key:
        return {"error": "未配置高德地图API密钥"}
    
    url = f"https://restapi.amap.com/v3/weather/weatherInfo?city={city}&key={amap_api_key}&extensions=all"
    response = requests.get(url)
    return response.json()["forecasts"]

# 定义函数用于写入文件
def write_file(filename, content):
    # 使用当前目录或指定目录
    import os
    base_dir = os.path.dirname(os.path.abspath(__file__))
    file_path = os.path.join(base_dir, filename)
    
    with open(file_path, "w", encoding="utf-8") as file:
        file.write(content)
    
    return f"文件{filename}写入成功"

函数实现要点:

  1. 函数名称一致:函数名必须与tools定义中的name完全一致
  2. 参数匹配:函数的参数名和个数必须与函数定义中的参数一致
  3. 返回值清晰:函数应该返回清晰的结果,便于后续处理
  4. 错误处理:实际生产环境中应添加异常处理逻辑
  5. 路径处理:使用相对路径或动态获取路径,避免硬编码

3.4 API请求构建

send_messages函数负责构建和发送API请求到阿里云DashScope服务:

def send_messages(messages):
    # 阿里云大模型API地址
    url = "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation"
    
    # 构建请求数据
    payload = {
        "model": "qwen-plus",  # 可以根据需要替换为其他阿里云模型
        "input": {
            "messages": messages
        },
        "parameters": {
            "tools": tools,  # 使用阿里云API需要的tools格式
            "tool_choice": "auto",  # 自动选择工具
            "temperature": 0.7
        }
    }
    
    # 阿里云API密钥
    api_key = os.getenv("DASHSCOPE_API_KEY")
    
    # 检查API密钥是否设置
    if not api_key:
        print("错误: 未设置 DASHSCOPE_API_KEY 环境变量")
        # 如果响应格式不符合预期,返回一个空消息
        class EmptyMessage:
            def __init__(self):
                self.content = ""
                self.tool_calls = None
        
        return EmptyMessage()
    
    # 发送请求
    headers = {
        "Content-Type": "application/json",
        "Authorization": f"Bearer {api_key}"
    }
    
    try:
        response = requests.post(url, headers=headers, json=payload)
        print(f"API请求状态码: {response.status_code}")
        
        # 检查响应状态码
        if response.status_code != 200:
            print(f"API请求失败,状态码: {response.status_code}")
            print(f"错误信息: {response.text[:1000]}")  # 限制输出长度,避免截断
            # 如果响应格式不符合预期,返回一个空消息
            class EmptyMessage:
                def __init__(self):
                    self.content = ""
                    self.tool_calls = None
            
            return EmptyMessage()
        
        response_data = response.json()
        
        # 检查响应数据中是否有错误
        if "error" in response_data:
            print(f"API返回错误: {response_data['error']}")
            # 如果响应格式不符合预期,返回一个空消息
            class EmptyMessage:
                def __init__(self):
                    self.content = ""
                    self.tool_calls = None
            
            return EmptyMessage()
        
        # 处理响应
        if "output" in response_data and "choices" in response_data["output"]:
            choice = response_data["output"]["choices"][0]
            # 构建与原代码兼容的返回格式
            class Message:
                def __init__(self, content, tool_calls=None):
                    self.content = content
                    self.tool_calls = tool_calls
            
            class ToolCall:
                def __init__(self, function):
                    self.function = function
            
            class Function:
                def __init__(self, name, arguments):
                    self.name = name
                    self.arguments = arguments
            
            if "message" in choice:
                message = choice["message"]
                content = message.get("content", "")
                tool_calls = []
                
                if "tool_calls" in message:
                    for tool_call in message["tool_calls"]:
                        if "function" in tool_call:
                            func = tool_call["function"]
                            function_obj = Function(func.get("name"), func.get("arguments"))
                            tool_call_obj = ToolCall(function_obj)
                            tool_calls.append(tool_call_obj)
                
                return Message(content, tool_calls if tool_calls else None)
    except Exception as e:
        print(f"请求过程中发生错误: {str(e)}")
    
    # 如果响应格式不符合预期,返回一个空消息
    class EmptyMessage:
        def __init__(self):
            self.content = ""
            self.tool_calls = None
    
    return EmptyMessage()

代码结构说明:

  1. 请求构建:构建符合阿里云API规范的请求体
  2. 认证管理:从环境变量获取API密钥
  3. 错误处理:检查API密钥、响应状态码、错误信息
  4. 响应解析:从阿里云特定的响应结构中提取数据和函数调用信息
  5. 数据封装:将原始响应数据封装为自定义对象,便于后续处理

3.5 响应处理与函数执行

invoke函数是整个流程的调度中心,负责协调整个函数调用过程:

def invoke(content):
    messages = [{"role": "system", "content": system_prompt},
                {"role": "user", "content": content}]
    message = send_messages(messages)
    print(f"收到模型响应: {'有函数调用' if message.tool_calls else '无函数调用'}")

    # 如果响应中存在tool_calls不为空,则说明大模型返回了函数及参数,需要进行函数调用
    if message.tool_calls:
        # 提取函数调用信息:函数名称和函数的参数
        func_name = message.tool_calls[0].function.name
        func_args = eval(message.tool_calls[0].function.arguments)  # 将参数字符串解析为字典

        func = globals()[func_name]  # 将函数名称字符串转换为函数体
        func_response = func(**func_args)  # 利用**将字典对象解析为参数

        # 将查询到的天气信息再次与用户提问一同提交给大模型进行分析
        messages = [
            {"role": "user", "content": f"请基于该信息:{func_response}\n来回答以下问题,请直接回答问题,不需要再进行函数调用,直接返回结果\n{content}"}]
        message = send_messages(messages)
        print(message.content)  # 得到最终的穿衣建议
    else:
        # 如果没有函数调用,直接输出模型的回复
        print("无函数调用,模型回复内容为:", message.content)

执行流程详解:

  1. 构建消息:将系统提示词和用户输入组合成消息列表
  2. 发送请求:调用send_messages函数发送请求到AI服务
  3. 检查结果:判断响应中是否包含函数调用(tool_calls)
  4. 提取信息:如果需要函数调用,提取函数名称和参数
  5. 执行函数:通过字符串名称动态获取函数对象并执行
  6. 结果整合:将函数执行结果和原始问题一起发送给模型
  7. 生成回复:模型基于函数结果生成最终回复

技术要点:

  • globals()[func_name]:通过字符串名称动态获取函数对象
  • eval(message.tool_calls[0].function.arguments):将参数字符串转换为字典
  • func(**func_args):将字典解包为函数参数

3.6 主程序入口

if __name__ == '__main__':
    content = "明天去北京出差适合穿什么衣服?"
    invoke(content)
    content = "请在Test2.txt文件中写入内容:你好,Function Calling!"
    invoke(content)

主程序演示了两个典型的使用场景:

  1. 天气查询和穿衣建议
  2. 文件写入操作

3.7 环境变量配置

项目的正常运行需要配置以下环境变量,建议使用.env文件进行管理:

# 阿里云DashScope API密钥
# 获取地址:https://dashscope.console.aliyun.com/
DASHSCOPE_API_KEY=your_dashscope_api_key

# 高德地图天气API密钥
# 获取地址:https://lbs.amap.com/api/weather-api/list
AMAP_API_KEY=your_amap_api_key

环境变量管理最佳实践:

  1. 使用.env文件:将API密钥存储在.env文件中,避免硬编码
  2. 添加到.gitignore:确保.env文件不会被提交到版本控制系统
  3. 提供模板:创建.env.example文件作为配置模板

四、运行结果分析

4.1 天气查询场景

用户输入:明天去北京出差适合穿什么衣服?

执行过程:

  1. 模型识别需要查询天气信息
  2. 提取参数:city="北京"
  3. 调用get_weather("北京")
  4. 获取天气数据
  5. 模型基于天气数据生成穿衣建议

模型回复:

根据天气信息,明天北京天气为晴,白天温度5℃,夜间温度-5℃,风向为西南风,风力1-3级。
建议穿着:
- 白天:保暖外套(如羽绒服或厚呢大衣)、毛衣、围巾、手套;
- 夜间:需加强保暖,可加穿保暖内衣、帽子等。
整体以防寒为主,注意温差较大,建议采用叠穿方式便于调节。

4.2 文件写入场景

用户输入:请在Test2.txt文件中写入内容:你好,FunctionCalling!

执行过程:

  1. 模型识别需要写入文件
  2. 提取参数:filename="Test2.txt", content="你好,FunctionCalling!"
  3. 调用write_file("Test2.txt", "你好,FunctionCalling!")
  4. 执行文件写入操作
  5. 返回操作结果

执行结果:

  • 在项目目录下创建Test2.txt文件
  • 文件内容为"你好,FunctionCalling!"
  • 函数返回"文件Test2.txt写入成功"

4.3 完整运行日志

运行程序后,控制台输出类似以下内容:

API请求状态码: 200
收到模型响应: 有函数调用
API请求状态码: 200
根据天气信息,明天北京天气为晴,白天温度5℃,夜间温度-5℃,风向为西南风,风力1-3级。
建议穿着:
- 白天:保暖外套(如羽绒服或厚呢大衣)、毛衣、围巾、手套;
- 夜间:需加强保暖,可加穿保暖内衣、帽子等。
整体以防寒为主,注意温差较大,建议采用叠穿方式便于调节。

API请求状态码: 200
收到模型响应: 有函数调用
API请求状态码: 200

五、技术要点总结

5.1 函数定义的最佳实践

  1. 描述要详细:函数描述应该清晰说明函数的用途和适用场景
  2. 参数要完整:每个参数都应有类型定义和描述说明
  3. 必要参数明确:将必要的参数标记在required数组中
  4. 命名要规范:使用清晰的英文命名,便于模型理解

示例:良好的函数定义

{
    "type": "function",
    "function": {
        "name": "search_products",
        "description": "根据关键词搜索商品,支持按价格区间和评分筛选",
        "parameters": {
            "type": "object",
            "properties": {
                "keyword": {
                    "type": "string",
                    "description": "搜索关键词,如:手机、耳机等"
                },
                "min_price": {
                    "type": "number",
                    "description": "最低价格,可选"
                },
                "max_price": {
                    "type": "number",
                    "description": "最高价格,可选"
                },
                "min_rating": {
                    "type": "number",
                    "description": "最低评分(0-5),可选"
                }
            },
            "required": ["keyword"]
        }
    }
}

5.2 错误处理策略

  1. API密钥检查:在发送请求前检查API密钥是否设置
  2. 状态码验证:检查HTTP响应状态码
  3. 错误信息处理:解析API返回的错误信息
  4. 异常捕获:使用try-except捕获可能的异常
  5. 返回值规范:定义统一的错误返回格式
def send_messages(messages):
    # 检查API密钥
    api_key = os.getenv("DASHSCOPE_API_KEY")
    if not api_key:
        print("错误: 未设置 DASHSCOPE_API_KEY 环境变量")
        return create_empty_response("未配置API密钥")
    
    # 发送请求并处理异常
    try:
        response = requests.post(url, headers=headers, json=payload, timeout=30)
        response.raise_for_status()  # 检查HTTP错误
        
        response_data = response.json()
        
        # 检查API业务错误
        if "error" in response_data:
            print(f"API返回错误: {response_data['error']}")
            return create_empty_response(f"API错误: {response_data['error']}")
        
        # 正常处理响应
        return parse_response(response_data)
        
    except requests.exceptions.Timeout:
        print("请求超时")
        return create_empty_response("请求超时,请稍后重试")
    except requests.exceptions.RequestException as e:
        print(f"请求失败: {str(e)}")
        return create_empty_response(f"网络请求失败: {str(e)}")

def create_empty_response(error_msg=None):
    class EmptyMessage:
        def __init__(self):
            self.content = error_msg or ""
            self.tool_calls = None
    return EmptyMessage()

5.3 响应解析要点

  1. 理解API响应结构:不同AI服务提供商的响应结构可能不同
  2. 提取工具调用:从响应中提取tool_calls信息
  3. 封装响应对象:将原始数据封装为易用的对象
  4. 处理边界情况:如无函数调用、响应格式异常等

阿里云API响应结构示例:

{
    "output": {
        "choices": [
            {
                "message": {
                    "content": "明天北京天气晴朗,适合穿轻薄外套。",
                    "tool_calls": [
                        {
                            "id": "call_123456",
                            "type": "function",
                            "function": {
                                "name": "get_weather",
                                "arguments": "{\"city\": \"北京\"}"
                            }
                        }
                    ]
                }
            }
        ]
    },
    "usage": {
        "total_tokens": 150,
        "output_tokens": 50,
        "input_tokens": 100
    }
}

5.4 安全注意事项

  1. API密钥管理:使用环境变量存储敏感信息,不要硬编码在代码中
  2. 输入验证:对用户输入进行必要的验证和清理
  3. 函数安全:确保调用的函数是安全的,避免执行危险操作
  4. 日志记录:记录关键操作日志,便于问题排查
  5. 访问控制:对敏感函数实施访问控制

安全最佳实践:

import os
import re

def validate_filename(filename):
    """验证文件名安全性"""
    # 阻止路径遍历攻击
    if ".." in filename or filename.startswith("/"):
        raise ValueError("非法的文件名")
    
    # 限制文件类型
    allowed_extensions = ['.txt', '.md', '.json', '.csv']
    if not any(filename.endswith(ext) for ext in allowed_extensions):
        raise ValueError("不支持的文件类型")
    
    # 限制文件名长度
    if len(filename) > 255:
        raise ValueError("文件名过长")
    
    return filename

def safe_write_file(filename, content):
    """安全的文件写入函数"""
    # 验证文件名
    safe_filename = validate_filename(filename)
    
    # 获取安全的路径
    base_dir = os.path.dirname(os.path.abspath(__file__))
    safe_path = os.path.join(base_dir, "user_files", safe_filename)
    
    # 确保目标目录存在
    os.makedirs(os.path.dirname(safe_path), exist_ok=True)
    
    # 写入文件
    with open(safe_path, "w", encoding="utf-8") as f:
        f.write(content)
    
    return f"文件{safe_filename}写入成功"

六、扩展与应用

6.1 添加新函数

要添加新的函数,只需要:

  1. 在tools数组中添加新的函数定义
  2. 实现对应的Python函数
  3. 确保函数名与定义中的name一致

示例:添加股票查询功能

# 1. 添加函数定义
tools = [
    # ... 现有函数 ...
    {
        "type": "function",
        "function": {
            "name": "get_stock_price",
            "description": "查询股票价格和相关信息",
            "parameters": {
                "type": "object",
                "properties": {
                    "stock_code": {
                        "type": "string",
                        "description": "股票代码,如:sh600519(上海)或 sz000001(深圳)",
                    }
                },
                "required": ["stock_code"]
            },
        }
    }
]

# 2. 实现函数
def get_stock_price(stock_code):
    """查询股票价格"""
    api_key = os.getenv("STOCK_API_KEY")
    if not api_key:
        return {"error": "未配置股票API密钥"}
    
    url = f"https://api.example.com/stock/quote?code={stock_code}"
    headers = {"Authorization": f"Bearer {api_key}"}
    
    response = requests.get(url, headers=headers)
    if response.status_code == 200:
        return response.json()
    else:
        return {"error": f"查询失败: {response.status_code}"}

6.2 支持多个AI服务提供商

通过封装可以支持多个AI服务提供商:

class LLMClient:
    """LLM客户端基类"""
    
    def __init__(self, api_key, api_endpoint):
        self.api_key = api_key
        self.api_endpoint = api_endpoint
    
    def format_functions(self, functions):
        """格式化函数定义"""
        raise NotImplementedError
    
    def parse_response(self, response):
        """解析响应"""
        raise NotImplementedError
    
    def send_message(self, messages, functions=None):
        """发送消息"""
        raise NotImplementedError


class AlibabaClient(LLMClient):
    """阿里云DashScope客户端"""
    
    def format_functions(self, functions):
        """阿里云格式的函数定义"""
        return [{"type": "function", "function": f} for f in functions]
    
    def parse_response(self, response):
        """解析阿里云响应"""
        return response["output"]["choices"][0]["message"]
    
    def send_message(self, messages, functions=None):
        """发送消息到阿里云"""
        payload = {
            "model": "qwen-plus",
            "input": {"messages": messages},
            "parameters": {
                "tools": self.format_functions(functions) if functions else [],
                "tool_choice": "auto"
            }
        }
        
        headers = {
            "Content-Type": "application/json",
            "Authorization": f"Bearer {self.api_key}"
        }
        
        response = requests.post(self.api_endpoint, headers=headers, json=payload)
        return self.parse_response(response.json())


class DeepSeekClient(LLMClient):
    """DeepSeek客户端"""
    
    def format_functions(self, functions):
        """DeepSeek格式的函数定义"""
        return [{"type": "function", "function": f} for f in functions]
    
    def parse_response(self, response):
        """解析DeepSeek响应"""
        return response["choices"][0]["message"]
    
    def send_message(self, messages, functions=None):
        """发送消息到DeepSeek"""
        payload = {
            "model": "deepseek-chat",
            "messages": messages,
            "tools": self.format_functions(functions) if functions else [],
            "tool_choice": "auto"
        }
        
        headers = {
            "Content-Type": "application/json",
            "Authorization": f"Bearer {self.api_key}"
        }
        
        response = requests.post(self.api_endpoint, headers=headers, json=payload)
        return self.parse_response(response.json())


# 使用示例
def create_client(provider):
    """工厂函数:创建指定提供商的客户端"""
    providers = {
        "alibaba": lambda: AlibabaClient(
            api_key=os.getenv("DASHSCOPE_API_KEY"),
            api_endpoint="https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation"
        ),
        "deepseek": lambda: DeepSeekClient(
            api_key=os.getenv("DEEPSEEK_API_KEY"),
            api_endpoint="https://api.deepseek.com/v1/chat/completions"
        )
    }
    
    if provider in providers:
        return providers[provider]()
    else:
        raise ValueError(f"不支持的提供商: {provider}")

6.3 高级功能

6.3.1 并发调用

支持同时调用多个函数:

import concurrent.futures

def execute_functions(tool_calls):
    """并发执行多个函数调用"""
    results = {}
    
    with concurrent.futures.ThreadPoolExecutor() as executor:
        # 准备所有函数调用任务
        future_to_func = {
            executor.submit(globals()[call.function.name], **eval(call.function.arguments)): call
            for call in tool_calls
        }
        
        # 收集结果
        for future in concurrent.futures.as_completed(future_to_func):
            call = future_to_func[future]
            try:
                result = future.result()
                results[call.function.name] = result
            except Exception as e:
                results[call.function.name] = {"error": str(e)}
    
    return results
6.3.2 函数链

支持多个函数按顺序调用:

def execute_function_chain(tool_calls, intermediate_results=None):
    """执行函数链,支持依赖传递"""
    if intermediate_results is None:
        intermediate_results = {}
    
    results = []
    
    for i, call in enumerate(tool_calls):
        func_name = call.function.name
        func_args = eval(call.function.arguments)
        
        # 替换依赖的参数
        for key, value in func_args.items():
            if isinstance(value, str) and value.startswith("$"):
                # 从中间结果中获取值
                ref_key = value[1:]
                if ref_key in intermediate_results:
                    func_args[key] = intermediate_results[ref_key]
        
        # 执行函数
        func = globals()[func_name]
        result = func(**func_args)
        
        # 保存结果供后续使用
        results.append({
            "function": func_name,
            "result": result
        })
        intermediate_results[func_name] = result
    
    return results
6.3.3 缓存机制

对重复调用进行缓存,提高效率:

from functools import lru_cache
import hashlib
import json

def cache_key(*args, **kwargs):
    """生成缓存键"""
    key_dict = {"args": args, "kwargs": kwargs}
    key_str = json.dumps(key_dict, sort_keys=True)
    return hashlib.md5(key_str.encode()).hexdigest()

class CachedFunctionCaller:
    """带缓存的函数调用器"""
    
    def __init__(self):
        self.cache = {}
        self.cache_ttl = 300  # 缓存5分钟
    
    def call_with_cache(self, func_name, **kwargs):
        """带缓存的函数调用"""
        key = cache_key(func_name, **kwargs)
        current_time = __import__('time').time()
        
        # 检查缓存
        if key in self.cache:
            cached_data = self.cache[key]
            if current_time - cached_data["time"] < self.cache_ttl:
                print(f"使用缓存: {func_name}")
                return cached_data["result"]
        
        # 执行函数
        func = globals()[func_name]
        result = func(**kwargs)
        
        # 保存到缓存
        self.cache[key] = {
            "time": current_time,
            "result": result
        }
        
        return result

七、总结与展望

7.1 技术总结

通过本文的讲解和实际代码示例,我们了解了:

  1. 技术原理:Function Calling如何让AI模型与外部世界交互
  2. 实现方式:如何在阿里云DashScope API中实现函数调用
  3. 代码结构:如何组织Function Calling的代码结构
  4. 最佳实践:函数定义、错误处理、响应解析等方面的最佳实践
  5. 扩展应用:如何添加新函数、支持多个服务提供商

7.2 应用前景

Function Calling技术的应用远不止本文所述的天气查询和文件操作。在实际开发中,您可以将其应用于:

  • 智能客服系统:自动查询订单状态、产品信息
  • 自动化工作流:按顺序执行多个操作步骤
  • 数据分析助手:查询数据库、生成报表
  • 智能家居控制:控制设备、查询状态
  • 企业应用集成:连接CRM、ERP等企业系统

7.3 未来发展方向

随着AI技术的不断发展,Function Calling技术也将持续演进:

  1. 更智能的函数选择:模型能够更准确地选择合适的函数
  2. 更强的参数理解:能够处理更复杂的参数提取场景
  3. 更丰富的工具生态:更多预定义的工具和API集成
  4. 更好的安全性:更严格的访问控制和权限管理

7.4 学习建议

对于想要深入学习Function Calling技术的开发者,建议:

  1. 理解原理:先理解Function Calling的基本原理和工作流程
  2. 动手实践:跟随本文示例动手实现一个简单的Function Calling应用
  3. 阅读文档:深入阅读阿里云、OpenAI等平台的官方文档
  4. 参与社区:加入AI开发者社区,了解最新动态和最佳实践
  5. 探索创新:尝试将Function Calling应用到自己的项目中

Function Calling技术是现代AI应用开发中不可或缺的技术之一。通过这项技术,AI不再仅仅是"聊天机器人",而是真正能够与现实世界交互、帮助用户完成实际任务的智能助手。希望本文能够帮助您更好地理解和应用这项技术,开启AI应用开发的新篇章。

关键词标签:Function Calling、AI、大语言模型、阿里云、Python、API集成

技术分类:人工智能、API开发、Python编程

Dify从入门到精通 第32天 合运用工作流、知识库与Function Calling的完整实践 AI技术赋能智能旅行规划 本文介绍如何构建一个智能旅行规划助手,通过AI技术实现一键生成个性化旅行方案。该系统综合运用工作流引擎、知识库和Function Calling三大核心技术:工作流协调任务执行顺序,知识库提供目的地推荐和旅行攻略,Function Calling实现实时天气和交通信息查询。文章详细讲解了系统架构设计、核心功能实现和关键技术细节,包括意图识别、信息检索优化、API集成等,并提供了代码示例。该智能助手能够理解用户自然语言输入,基于知识库推荐目的地,通过外部API获取实时数据,最终生成完 阅读详情

相关推荐

AI Agent协议栈解析:从Function Calling到多Agent通信的技术演进

在人工智能领域,协议作为系统组件间交互的标准化约定,是实现高效协同与互操作性的技术基石。其核心原理在于定义统一的通信语法与数据格式,解决组件间的信息交换问题。从技术价值看,协议能够提升系统可组合性、降低集成成本,并促进生态繁荣。在AI Agent应用场景中,协议尤其关键,它支撑着从工具调用到多智能体协作的复杂交互。本文聚焦于AI Agent生态中的协议栈,深入剖析了以Function Calling为代表的工具调用协议,以及多Agent通信中的标准化挑战与实践方案,为开发者构建可扩展的智能体系统提供技术参考

weixin_33754065的博客 427

智能体Agent深度解析:基于Function Calling的工具调用全指南!

AI智能体是指具备一定自主性、能感知环境并通过智能决策执行特定任务的软件或硬件实体。它结合了人工智能技术(如机器学习、自然语言处理、计算机视觉等),能够独立或协作完成目标。基于大语言模型(LLM)的Function Calling可以令智能体实现有效的工具使用和与外部API的交互。并非所有的LLM模型都支持Function Calling。支持Function Calling的模型(如gpt-4,qwen-plus等)能够检测何时需要调用函数,并输出调用函数的函数名和所需参数的JSON格式结构化数据。

2401_85390073的博客 1809

大模型工具调用原理与实践:从Function Calling到工程实现

在人工智能应用开发中,大语言模型(LLM)与外部系统的交互能力是构建智能代理(Agent)的核心。这种交互通常通过工具调用(Tool Calling)机制实现,其本质是让纯文本模型能够理解和生成结构化指令,从而操作API、数据库等外部资源。从技术原理看,这涉及意图识别、参数提取和流程控制等多个环节,其中OpenAI的Function Calling协议已成为行业事实标准。该协议定义了模型如何通过tools参数声明可用工具,并以tool_calls结构返回调用请求,开发者则需构建执行引擎来解析、路由和执行这些

weixin_30568591的博客 390

LangGraph之Tool Calling Agent 中ToolNode的使用

在LangGraph的代理架构体系中,Router Agent是基础形式,在此之上构建的Tool Calling Agent(工具调用代理)极大地拓展了大模型的应用能力。它允许大模型根据用户输入自主选择并调用合适工具,有效解决与外部系统交互时自然语言与特定输入模式不匹配的问题,显著增强了模型处理复杂任务的能力。

qq_43588095的博客 2003

1.2 揭秘 OpenAI Function Calling 内部原理,手写第一个文件搜索工具

本文深入解析OpenAI Function Calling的内部机制,揭示模型如何理解函数定义、选择调用函数并生成正确参数。通过训练数据的秘密、Token级别解析和决策过程分析,阐明模型的工作原理。随后,文章指导读者从零开始构建一个功能完整的文件搜索工具,包含list_files、read_file和search_content三个核心函数的设计与实现。项目涵盖文件操作、错误处理及性能优化,帮助开发者掌握Function Calling的精髓,实现从原理到实践的完整闭环。

yonggeit的博客 2966

【大模型应用开发 动手做AI Agent】什么是Function Calling

【大模型应用开发 动手做AI Agent】什么是Function Calling 1. 背景介绍 在AI应用开发中,函数调用(Function Calling)是一项基本但重要的技术。大模型的应用开发离不开对函数调用的灵活应用和深入理解。本文

欢迎来到我的优快云空间!这里聚焦AI大模型应用实战,分享前沿技术、实战案例与开发经验。 906

深入解析Trae-Agent的Function Calling机制:从原理到实践

Function Calling是大语言模型(LLM)从文本生成迈向实际应用的核心技术,它通过将自然语言指令映射到预定义函数调用,实现了AI的“感知-思考-行动”闭环。其原理基于LLM对工具描述的理解和决策能力,技术价值在于让模型从被动应答转变为主动执行,极大扩展了AI在自动化流程、智能助手、数据分析等场景的应用边界。本文以Trae-Agent框架为例,深入剖析其以“工具”为中心的智能体模型设计,详解工具注册、上下文组装、LLM决策、安全执行等关键环节,并对比LangChain等主流方案的差异化实现。通过聚

weixin_33714884的博客 455

谷歌Gemini模型免费API调用全攻略:从注册到实战(附代码示例)

本文详细介绍了如何免费调用谷歌Gemini模型的API,从注册获取API密钥到实战应用的全过程。内容涵盖环境准备、基础API调用、多模态处理、高级功能及生产环境实践,帮助开发者高效利用Gemini模型进行代码生成和复杂任务处理。附有代码示例,适合AI开发者和技术爱好者。

weixin_29212429的博客 1597

【值得收藏】深入理解Agent+Function Calling+RL:构建能学习、会办事的AI系统

本文详解Agent、Function Calling和强化学习三大技术如何协同,让AI从"会说话"升级为"会办事、会学习"的完整体系。Agent负责思考和规划,Function Calling提供执行能力,RL使AI持续优化。文章深入探讨各技术的概念、作用和应用场景,并提供主流RL微调框架选型指南,助读者构建真正的智能系统。

嘴巴吃糖了 1267

ZYNQ实战:如何用AXI-stream FIFO解决跨时钟域数据同步难题(附完整代码)

本文深入探讨了在ZYNQ项目中,如何利用AXI-Stream FIFO有效解决PS与PL之间的跨时钟域数据同步难题。文章详细分析了异步FIFO的架构设计、关键配置参数,并提供了PS端软件策略与PL端逻辑设计的完整代码示例,旨在帮助开发者构建稳定可靠的数据流通道,优化系统性能。

nginx7reverse的博客 754

FPGA人脸识别检测(先获取人脸图像以及灰度二值化)

由于图片有空洞等缺陷,所以后面要进行中值滤波、腐蚀、膨胀等算法处理,然后再对人脸进行框选,最后显示出来。最终的成像应该是下面这样的。(后面有时间再接着做哈哈哈)5、通过modelsim将操作后的图片数据写入gray_img.txt里面后,用matlab将txt转成图片。1、先用matlab将图片转换成txt数据,代码如下。读取图片的方式按照博客咸鱼里的方式来做,如下。2、FPGA读取txt数据。4、FPGA顶层模块及仿真。3、FPGA灰度二值化。

weixin_46312715的博客 2529

深入理解LLM三大核心技术:Function Calling、MCP与A2A实战指南

本文深入解析了大型语言模型(LLM)的三大核心技术:Function Calling通过监督微调实现意图识别和参数提取;MCP作为标准化工具交互协议;A2A支持AI智能体协作。重点阐述了Function Calling的训练方法,包括数据集构建、基础模型选择和微调过程,并详细说明了函数描述的标准化格式。同时提供了系统学习LLM的路径和资源,帮助开发者快速掌握相关技术。文章还解答了常见问题,如训练数据格式、工具接入方法等,为LLM技术实践提供了实用指导。

2401_84494441的博客 780

ARIMA模型的R实现

1.模型简介 ARIMA模型,全称差分自回归移动平均模型。可以看作是差分运算和ARMA模型的结合。主要是针对具有线性趋势的非平稳的时间序列,来进行建模预测。 ARIMA模型的形式如下: Φ(B)∇

heirenmin的博客 1万+

一文搞懂 Agent、Function Calling 与强化学习

本篇系统讲解 Agent、Function Calling 与强化学习是什么、以及三者如何协同,让大模型从被动工具进化为能自主完成任务、自我学习进化的智能体。

fufan_LLM的博客 1358

Android 开发实战:从零到一集成 espeak-ng 实现中文离线 TTS(无需账号开箱即用)

本文详细介绍了如何在 Android 项目中集成 espeak-ng,实现中文离线语音播报。通过下载 so 库、编写 Java 接口、封装管理类,开发者可快速构建无需账号的 TTS 功能。文章覆盖从零到一的开发步骤、调试技巧及性能优化,适合中高级 Android 开发者参考。

全栈 3316

C#上位机面试必问30题:机器视觉+工业通信+性能优化(含答案解析)

工业通信:围绕“稳定性”展开,重点答异常处理、断线重连、资源释放;机器视觉:围绕“精度+速度”展开,重点答预处理、ROI、参数优化;性能优化:围绕“UI不卡、内存不泄、效率不低”展开,重点答异步、限流、资源复用;面试加分项:结合实际项目案例(如“我在XX项目中用信号量解决了多设备并发卡顿问题”)。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 1505

MCP、Function Calling,到底有什么区别?它们和 AI Agent 有关系吗?

MCP(模型条件化提示)和Function Calling是大模型应用中的两项关键技术。MCP通过嵌入条件约束优化模型输出,适用于文本生成场景;Function Calling则让模型直接调用函数接口,实现系统操作自动化。二者在AI Agent中协同工作:MCP提升"思考"能力,Function Calling增强"执行"能力。实际应用中需注意提示设计、接口定义等挑战,通过调试验证和异常处理确保稳定性。这两项技术共同构成了现代智能体的核心能力框架。

霍格沃兹测试学院的博客 1070

电子电器架构 --- OEM企标中规定ECU启动时间的目的

电子电器架构 --- OEM企标中规定ECU启动时间的目的

Soly_kun的博客 223

Tool Calling 与 Function Call 深度指南

AI Agent通过Tool Calling赋予大语言模型(LLM)连接外部世界的能力。本文解析了两种实现方式:早期不稳定的Prompt Engineering和现代标准化的Function Calling。后者通过JSON Schema定义工具,LLM生成结构化指令,由外部程序执行具体操作。文章详细展示了工作流程、数据结构,并提供了LangChain实现的代码示例,说明如何让LLM识别数学计算需求、调用乘法工具并返回结果。随着技术发展,AI正从单一工具调用向自主规划的多轮Agent交互演进。

nvd11的专栏 3308

手把手教你学Simulink--电动物流车预充电路控制及主继电器粘连检测电机负载仿真

摘要:本文基于Simulink仿真平台,研究了电动物流车预充电路控制策略及主继电器粘连检测技术。通过构建预充电路、主继电器检测及电机负载模型,分析了不同工况下的系统特性。研究表明,优化的预充控制策略可缩短预充时间至0.5秒内,降低电流峰值;主继电器粘连检测模型能准确识别故障;电机负载变化对系统性能具有显著影响。仿真结果验证了所提方法的有效性,为提升电动物流车安全性与可靠性提供了理论支持和技术参考。

MHD0815的博客 61

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

深入理解 Function Calling

本文介绍了FunctionCalling机制如何让大语言模型(LLM)具备调用外部工具的能力。文章首先解释了FunctionCalling的原理:LLM作为决策中心,通过定义工具(tools)让模型在需要时触发特定函数调用,开发者负责执行并返回结果。接着展示了完整的天气查询案例,包括工具定义、函数实现、调用分发和主流程控制。重点分析了工具定义规范、消息结构变化等关键细节,并提供了并行工具调用和企业级数据库查询的进阶示例。最后总结了常见问题和解决方案,强调参数校验、异常处理和清晰描述的重要性。Function

weixin_42533856的博客 779

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

MCP、Function_Calling和A2A大解析:一文带你深入理解这三个关键概念!

文章介绍了AI Agent如何让大模型从单纯对话的"学霸"转变为能执行实际任务的"万能助手"。重点解析了三大核心技术:MCP(Agent与工具的交互协议)、Function Calling(Agent与模型的工具调用)和A2A(Agent之间的协作协议)。这些技术使AI Agent能够感知环境、使用工具、执行任务,实现从"思想巨人"到"行动巨人"的转变,为AGI发展奠定基础。

2401_84495872的博客 869

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

深入理解Agent:从0实现function call

Function的调用时Agent实现很重要的一步,只有理解了function call这个原理才可以更好的创建Agent。

雷哥AI工程化 3018

深入理解 Function Calling、MCP 与 Skills:AI Agent 的三层能力架构

本文深入探讨了AI Agent的工具调用体系,从底层机制到上层抽象,涵盖Function Calling、MCP协议和Skills三层架构。首先指出大语言模型(LLM)的三大局限(知识截止、无状态、纯文本),强调工具调用的必要性。Function Calling作为底层原语,通过JSON Schema定义工具接口,实现模型决策与宿主执行的解耦。MCP协议进一步标准化工具交互,提供统一发现机制与传输层,支持Tools、Resources和Prompts三类能力。最后介绍Skills作为可复用的能力包,以及Pl

higerwy的博客 425

AI Agent核心技术解析:从Function Calling到多Agent系统

AI Agent作为智能体技术的核心,通过Function Calling机制实现与外部工具的高效交互。这一技术不仅涉及意图识别和参数提取,还需解决工程实践中的冷启动、参数冲突等典型问题。多Agent系统通过Sub Agent架构(如管道模式、委员会模式)进一步提升复杂任务的处理能力,在电商客服等场景中显著提升效率与准确性。记忆系统的混合实现(短期记忆、长期记忆、流程状态存储)和工具调用优化是Agent落地的关键。随着大模型技术的发展,AI Agent正成为自动化流程和智能决策的重要基础设施。

weixin_29044713的博客 217

深入理解 Agent 相关协议:从单体 Agent 到 Multi-Agent、MCP、A2A 与 Agentic AI 的系统化实践

当大模型从“对话式问答”升级为“可调用工具、可操作系统、能自主规划任务”的智能体(Agent)后,整条 AI 应用链路发生了质变...

往者之不谏 来者犹可追 1334
上一篇: 【Agent学习笔记1:Python调用Function Calling,阿里云API函数调用与DeepSeek API对比分析】
下一篇: 【Agent学习笔记3:使用Python开发简单MCP服务】
Stream_Silver
博客等级 码龄5年 73粉丝 58原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值