大模型function calling:让AI函数调用更智能、更高效

news2025/4/12 3:24:00

大模型function calling:让AI函数调用更智能、更高效

5e5a6b93-1b1f-425a-b01d-d15e85720563

随着大语言模型(LLM)的快速发展,其在实际应用中的能力越来越受到关注。Function Calling 是一种新兴的技术,允许大模型与外部工具或API进行交互,从而扩展了模型的功能边界。本文将深入探讨 Function Calling 的原理、应用场景以及如何通过优化实现更智能、高效的函数调用。

1. 什么是 Function Calling?

unction Calling 是一种机制,允许大语言模型动态调用外部函数或API,以完成特定任务。例如,当用户提问“今天的天气如何?”时,模型可以通过调用天气API获取实时数据并返回结果。这种方式不仅提升了模型的实用性,还使其能够处理复杂的多步骤任务。

  • 关键特点:
    • 动态调用:根据用户需求调用合适的函数。
    • 上下文感知:结合对话上下文选择最佳函数。
    • 扩展性强:支持多种API和服务集成。

2. Function Calling 的核心原理

workflow

  1. 用户输入问题或指令。
  2. 模型分析输入,生成 JSON 格式的函数调用请求。
  3. 后端解析请求,调用对应函数或API。
  4. 将函数返回的结果传递回模型。
  5. 模型基于结果生成最终回答。

3.基本操作

llm使用的是llama3.2:3b的模型,有一些模型是不支持tools的。

# 安装依赖包
pip isntall ollama
pip install yfinance
"""
1.将问题输出到llm模型中
2.模型解析问题中参数,将需要回调的函数和参数输出。
3.将回调结果作为上下文输入llm模型中验证
"""
import ollama
from ollama import Client

client = Client(
    host='http://192.168.3.203:11434/',
    headers={'x-some-header': 'some-value'}
)


def add_two_numbers(a: int, b: int) -> int:
    """
    Add two numbers

    Args:
      a (int): The first number
      b (int): The second number

    Returns:
      int: The sum of the two numbers
    """
    return int(a) + int(b)


def subtract_two_numbers(a: int, b: int) -> int:
    """
    Subtract two numbers
    """
    return int(a) - int(b)


# Tools can still be manually defined and passed into chat
subtract_two_numbers_tool = {
    'type': 'function',
    'function': {
        'name': 'subtract_two_numbers',
        'description': 'Subtract two numbers',
        'parameters': {
            'type': 'object',
            'required': ['a', 'b'],
            'properties': {
                'a': {'type': 'integer', 'description': 'The first number'},
                'b': {'type': 'integer', 'description': 'The second number'},
            },
        },
    },
}

messages = [{'role': 'user', 'content': 'What is three plus one?'}]
print('Prompt:', messages[0]['content'])

available_functions = {
    'add_two_numbers': add_two_numbers,
    'subtract_two_numbers': subtract_two_numbers,
}


def main():
    response = client.chat(
        'llama3.2:3b',
        messages=messages,
        tools=[add_two_numbers, subtract_two_numbers_tool],
    )

    if response.message.tool_calls:
        # There may be multiple tool calls in the response
        for tool in response.message.tool_calls:
            # Ensure the function is available, and then call it
            if function_to_call := available_functions.get(tool.function.name):
                print('Calling function:', tool.function.name)
                print('Arguments:', tool.function.arguments)
                output = function_to_call(**tool.function.arguments)
                print('Function output:', output)
            else:
                print('Function', tool.function.name, 'not found')

    # Only needed to chat with the model using the tool call results
    if response.message.tool_calls:
        # Add the function response to messages for the model to use
        messages.append(response.message)
        messages.append({'role': 'tool', 'content': str(output), 'name': tool.function.name})

        # Get final response from model with function outputs
        final_response = client.chat('llama3.2:3b', messages=messages)
        print('Final response:', final_response.message.content)

    else:
        print('No tool calls returned from model')


if __name__ == '__main__':
    main()

"""
结果
Prompt: What is three plus one?
Calling function: add_two_numbers
Arguments: {'a': 3, 'b': 1}
Function output: 4
"""
"""
1.通过llm模型获取解析回调函数和函数的参数
2.通过yfinance库获取公司最新股票信息
"""
from ollama import Client
import yfinance as yf

client = Client(
    host='http://192.168.3.203:11434',
    headers={'x-some-header': 'some-value'}
)


def get_current_stock_price(ticker_symbol):
    # 定义函数
    stock = yf.Ticker(ticker_symbol)
    current_price = stock.history(period='1d')['Close'].iloc[0]
    return current_price


# 本地测试
# data = get_current_stock_price("MSFT")
# print(data)

# 工具函数请求参数
tools = [{'type': 'function',
          'function': {
              'name': 'get_current_stock_price',
              'description': 'Get the current price for a stock',
              'parameters': {
                  'type': 'object',
                  'properties': {
                      'ticker_symbol': {
                          'type': 'string',
                          'description': 'The ticker symbol of the stock'
                      }
                  }
              },
              'required': ['ticker_symbol']
          }
          }]
response = client.chat(model='llama3.2:3b',
                       messages=[{
                           'role': 'user',
                           'content': 'What is the current price of MSFT'
                       }],
                       # provide a tool to get the current price of a stock
                       tools=tools
                       )
print(response['message']['tool_calls'])
# [ToolCall(function=Function(name='get_current_stock_price', arguments={'ticker_symbol': 'MSFT'}))]

# 模拟函数库
function_map = {'get_current_stock_price': get_current_stock_price}


def call_function_safely(response, function_map):
    # 模型结果回调函数
    tool_call = response['message']['tool_calls'][0]
    function_name = tool_call['function']['name']
    arguments = tool_call['function']['arguments']
    function_to_call = function_map.get(function_name)
    if function_to_call:
        try:
            result = function_to_call(**arguments)
            print(f"The current price of {arguments['ticker_symbol']} is : {result}")
        except TypeError as e:
            print(f"Argument error: {e}")
    else:
        print(f"{function_name} is not a recognized function")


call_function_safely(response, function_map)

4.实现自动发送邮件

import os
from dotenv import load_dotenv

import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

import json
from openai import OpenAI


GPT_MODEL = "llama3.2:3b"

load_dotenv(dotenv_path="/Users/wuzhibin/workspace/pythonDemo/ollama_demo/.env")
# 大模型密钥
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
# 邮箱授权码
AUTHORIZATION_CODE = os.getenv("AUTHORIZATION_CODE")
print("1111", OPENAI_API_KEY, AUTHORIZATION_CODE)
client = OpenAI(base_url="http://192.168.3.203:11434/v1", api_key=OPENAI_API_KEY)

tools = [
    {
        "type": "function",
        "function": {
            "name": "send_email",
            "description": "Send an email to the specified email with the subject and content",
            "parameters":{
                "type": "object",
                "properties": {
                    "FromEmail": {
                        "type": "string",
                        "description": "The email address, eg., rememeber0101@126.com",
                    },
                    "Subject": {
                        "type": "string",
                        "description": "Subject of the email",
                    },
                    "Body": {
                        "type": "string",
                        "description": "The content of the email",
                    },
                    "Recipients": {
                        "type": "string",
                        "description": "The recipients' email addresses",
                    }
                },
                "required": ["FromEmail", "Subject", "Body", "Recipients"],
            },
        }
    }
]


def chat_completion_request(messages, tools=None, tool_choice=None, model=GPT_MODEL):
    try:
        response = client.chat.completions.create(
            model=model,
            messages=messages,
            tools=tools,
            tool_choice=tool_choice,
        )
        return response
    except Exception as e:
        print("Unable to generate ChatCompletion response")
        print(f"Exception: {e}")
        return e


def send_email(sender_email, sender_authorization_code, recipient_email, subject, body):
    # 创建 MIMEMultipart 对象
    message = MIMEMultipart()
    message["From"] = sender_email
    message["To"] = recipient_email
    message["Subject"] = subject

    message.attach(MIMEText(body, "plain"))

    # 创建 SMTP_SSL 会话
    with smtplib.SMTP_SSL("smtp.126.com", 465) as server:
        server.login(sender_email, sender_authorization_code)
        text = message.as_string()
        server.sendmail(sender_email, recipient_email, text)


def main():
    messages = []
    
    while True:
        msg = input("【You】: ")
        messages.append({"role": "user", "content": msg})
        response = chat_completion_request(
            messages=messages,
            tools=tools
        )
        print(response)
        if content := response.choices[0].message.content:
            print(f"【AI】: {content}")
            messages.append({"role": "assistant", "content": content})
        else:
            fn_name = response.choices[0].message.tool_calls[0].function.name
            fn_args = response.choices[0].message.tool_calls[0].function.arguments
            # print(f"【Debug info】: fn_name - {fn_name}")
            # print(f"【Debug info】: fn_args - {fn_args}")
        
            if fn_name == "send_email":
                try:
                    args = json.loads(fn_args)
                    # 返回将要发送的邮件内容给用户确认
                    print("【AI】: 邮件内容如下:")
                    print(f"发件人: {args['FromEmail']}")
                    print(f"收件人: {args['Recipients']}")
                    print(f"主题: {args['Subject']}")
                    print(f"内容: {args['Body']}")
                    
                    confirm = input("AI: 确认发送邮件吗? (yes/no): ").strip().lower()
                    if confirm == "yes":
                        send_email(
                            sender_email=args["FromEmail"],
                            sender_authorization_code=AUTHORIZATION_CODE, 
                            recipient_email=args["Recipients"], 
                            subject=args["Subject"], 
                            body=args["Body"],
                        )
                        print("邮件已发送,还需要什么帮助吗?")
                        messages.append({"role": "assistant", "content": "邮件已发送,还需要什么帮助吗?"})
                    else:
                        print("邮件发送已取消,还需要什么帮助吗?")
                        messages.append({"role": "assistant", "content": "邮件发送已取消,还需要什么帮助吗?"})
                except Exception as e:
                    print(f"发送邮件时出错:{e}")
                    messages.append({"role": "assistant", "content": "抱歉,功能异常!"})    


if __name__ == "__main__":
    main()


# 帮我发送一封邮件 发件人: xxxxxxx@126.com, 收信人:xxxxxxx@qq.com, 发送内容:写着一封来自未来胖虎的问候邮件,主题:来自未来的问候

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2308998.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机毕业设计SpringBoot+Vue.js贸易行业CRM系统(源码+文档+PPT+讲解)

温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…

图像分类项目2:鸟类图像分类

1 数据集处理 1.1数据集下载 数据集来源:kaggle,网址:https://www.kaggle.com/,点击进入网站,左侧选择Datasets。 进入后搜索栏搜索关键词bird。此时出现很多数据集可以选择,推荐选择第一个或者第三个。…

Redis数据结构-List列表

1.List列表 列表类型适用于存储多个有序的字符串(这里的有序指的是强调数据排列顺序的重要,不是升序降序的意思),列表中的每个字符串称为元素(element),一个列表最多可以存储2^32-1个元素。在R…

启动你的RocketMQ之旅(三)-Producer启动和发送流程(上)

前言: 👏作者简介:我是笑霸final。 📝个人主页: 笑霸final的主页2 📕系列专栏:java专栏 📧如果文章知识点有错误的地方,请指正!和大家一起学习,一…

Unity UGUI SuperScrollView介绍

先铺垫一下ScrollView Unity中常用的ScrollView 是 Unity 中的一个常见 UI 组件,主要用于创建可滚动的视图。当内容超过其显示区域时,ScrollView 可以让用户通过滚动查看全部内容。它通常包含一个显示区域和一个内容区域,内容区域可以超过显…

【STM32安全性研究】STM32F103RCT6固件读取

最近从飞哥那买了个stm32固件提取器,效果很好。下面记录对某产品主控STM32F103RCT6固件的提取过程,说明提取时的注意事项。 注意本文的目的仅用于stm32安全性研究,不提供涉及产品本身的内容,包括固件、软件等。 stm32固件提取可参考论坛https://www.aisec.fraunhofer.de/en…

SpringMVC学习(初识与复习Web程序的工作流程)(1)

目录 一、SpringMVC(框架)的简要概述。 &#xff08;1&#xff09;SpringMVC与Servlet。 &#xff08;2&#xff09;技术方向。 &#xff08;3&#xff09;最终学习目标。 二、Web程序的基本工作流程。 &#xff08;1&#xff09;工作流程。 <1>浏览器。前后端任务。 <…

【领域】百度OCR识别

一、定义 OCR&#xff08;Optical Character Recognition&#xff0c;光学字符识别&#xff09;是计算机视觉重要方向之一。传统定义的OCR一般面向扫描文档类对象&#xff0c;现在我们常说的OCR一般指场景文字识别&#xff08;Scene Text Recognition&#xff0c;STR&#xff…

Docker 学习(一)

一、Docker 核心概念 Docker 是一个开源的容器化平台&#xff0c;允许开发者将应用及其所有依赖&#xff08;代码、运行时、系统工具、库等&#xff09;打包成一个轻量级、可移植的“容器”&#xff0c;实现 “一次构建&#xff0c;随处运行”。 1、容器&#xff08;Container…

【vscode-解决方案】vscode 无法登录远程服务器的两种解决办法

解决方案一&#xff1a; 查找原因 命令 ps ajx | grep vscode 可能会看到一下这堆信息&#xff08;如果没有大概率不是这个原因导致&#xff09; 这堆信息的含义&#xff1a;当你使用 vscode 远程登录服务器时&#xff0c;我们远程机器服务端要给你启动一个叫做 vscode serv…

5个GitHub热点开源项目!!

1.自托管 Moonlight 游戏串流服务&#xff1a;Sunshine 主语言&#xff1a;C&#xff0c;Star&#xff1a;14.4k&#xff0c;周增长&#xff1a;500 这是一个自托管的 Moonlight 游戏串流服务器端项目&#xff0c;支持所有 Moonlight 客户端。用户可以在自己电脑上搭建一个游戏…

基于SpringBoot的美妆购物网站系统设计与实现现(源码+SQL脚本+LW+部署讲解等)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

计算机毕业设计Hadoop+Spark+DeepSeek-R1大模型音乐推荐系统 音乐数据分析 音乐可视化 音乐爬虫 知识图谱 大数据毕业设计

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

mysql5.7离线安装及问题解决

这次主要是讲解mysql5.7离线安装教程和一主一从数据库配置 1、去官网下载自己对应的mysql https://downloads.mysql.com/archives/community/2、查看需要安装mysql服务器的linux的类型 uname -a第二步看一下系统有没有安装mysql rpm -qa|grep -i mysql3、上传安装包 用远程…

Redis 实战篇 ——《黑马点评》(下)

《引言》 &#xff08;下&#xff09;篇将记录 Redis 实战篇 最后的一些学习内容&#xff0c;希望大家能够点赞、收藏支持一下 Thanks♪ (&#xff65;ω&#xff65;)&#xff89;&#xff0c;谢谢大家。 传送门&#xff08;上&#xff09;&#xff1a;Redis 实战篇 ——《黑马…

蓝桥杯自我复习打卡

总复习&#xff0c;打卡1. 一。排序 1。选段排序 太可恶了&#xff0c;直接全排输出&#xff0c;一个测试点都没过。 AC 首先&#xff0c;这个【l,r】区间一定要包含p,或者q&#xff0c;pq一个都不包含的&#xff0c;[l,r]区间无论怎么变&#xff0c;都对ans没有影响。 其次&…

Java零基础入门笔记:(6)面向对象

前言 本笔记是学习狂神的java教程&#xff0c;建议配合视频&#xff0c;学习体验更佳。 【狂神说Java】Java零基础学习视频通俗易懂_哔哩哔哩_bilibili 第1-2章&#xff1a;Java零基础入门笔记&#xff1a;(1-2)入门&#xff08;简介、基础知识&#xff09;-CSDN博客 第3章…

【3天快速入门WPF】13-MVVM进阶

目录 1. 窗体设置2. 字体图标3. 控件模板4. 页面逻辑4.1. 不使用MVVM4.2. MVVM模式实现本篇我们开发一个基于MVVM的登录页面,用来回顾下之前学习的内容 登录页面如下: 窗体取消了默认的标题栏,调整为带阴影的圆角窗体,左侧放一张登录背景图,右边自绘了一个关闭按钮,文本框…

【MongoDB】在Windows11下安装与使用

官网下载链接&#xff1a;Download MongoDB Community Server 官方参考文档&#xff1a;https://www.mongodb.com/zh-cn/docs/manual/tutorial/install-mongodb-on-windows/#std-label-install-mdb-community-windows 选择custom类型&#xff0c;其他默认 注意&#xff0c;此选…

deepseek使用记录18——文化基因美食篇

子篇&#xff1a;薪火相传的味觉辩证法——从燧人氏到预制菜的文化突围 一、石器时代的启蒙&#xff1a;食物探索中的原始辩证法 在贾湖遗址的陶罐残片上&#xff0c;碳化稻米与蜂蜜的结晶层相互交叠&#xff0c;这是9000年前先民对"甘"与"饱"的首次辩证…