头图

函数调用与工具使用在大语言模型中的应用

原文链接:An introduction to function calling and tool use in LLMs
作者:Apideck
译者:倔强青铜三

前言

大家好,我是倔强青铜三。作为一名热情的软件工程师,我热衷于分享和传播IT技术,致力于通过我的知识和技能推动技术交流与创新,欢迎关注我,微信公众号:倔强青铜三。欢迎点赞、收藏、关注,一键三连!!!

大语言模型如何从“说”转向“做”

函数调用使大型语言模型(LLMs)能够根据用户的输入与外部工具、API和函数进行交互。它不再局限于生成文本,而是可以确定需要执行特定操作,并请求由外部函数执行该操作。通过函数调用,用户可以使用自然语言与复杂系统交互,而LLMs则负责处理底层的函数执行,从而专注于解决实际问题,而不仅仅是生成文本。

例如,如果用户询问天气情况,模型可以调用天气API获取实时数据,而不仅仅是生成一个通用的回复。如果有可能下雨,它可能会提醒你带伞。

函数调用过程解析

让我们深入了解LLMs中的函数调用是如何工作的:

LLMs中的函数调用是如何工作的

  1. 用户查询:当用户提出问题或请求操作时(例如,“今天的天气如何?”或“检查产品X是否有库存”),整个过程便开始了。
  2. LLM处理:LLM分析查询内容,并识别出需要外部数据或操作来满足请求。例如:

    • 如果用户询问天气,LLM会识别出需要获取实时数据。
    • 如果用户想要了解库存信息,它会触发数据库查询。
  3. 函数调用决策:LLM决定执行函数调用,可以是以下两种之一:

    • API调用:连接到外部服务(例如,天气API以获取实时预报)。
    • 自定义函数:访问内部工具或数据库(例如,库存访问以检查库存水平)。
  4. 数据检索:函数获取所需数据(例如,从天气API获取温度,从仓库数据库获取产品可用性)。
  5. 数据整合:检索到的数据被发送回LLM,LLM处理这些数据并为用户生成一个上下文相关且准确的回复

函数调用的应用场景及其性能提升

通过能够调用函数,LLM不再局限于文本生成。它可以执行诸如检索实时数据或与其他软件交互等操作,这使得模型在实际应用中更加动态和有用,例如:

  • 提供最新信息:如果模型可以通过函数调用获取最新信息,它就能提供更准确的答案。例如,在没有函数调用的情况下回答有关当前事件的问题可能会导致信息过时,但通过访问新闻API,答案就能保持最新。
  • 自动化重复性任务:函数调用可以自动化重复性任务。例如,如果用户想要安排会议,LLM可以调用日历API自动添加事件,这节省了时间并减少了手动输入的需求。
  • 与其他服务连接:LLMs可以成为更大生态系统的一部分,与数据库、客户关系管理系统或其他企业系统连接。这使它们在专业环境中更加灵活多样。
  • 处理复杂工作流程:LLM不仅可以回答单个问题,还可以协调多个函数调用来解决多步骤问题。例如,通过不同的API规划旅行,包括检查航班可用性、预订酒店和租车。
  • 无需重新训练即可更新信息:随着新的函数或API的出现,LLM可以在不重新训练整个模型的情况下更新以使用它们。这使系统能够以最小的努力保持最新。

函数调用的一些示例

如果你使用过ChatGPT市场中的任何GPT,你可能已经看到或体验过函数调用。这些GPT执行自定义函数,让人们可以创建诸如待办事项列表构建器、提示增强器、应用连接器和问答机器人等专用工具。ChatGPT内置的“任务”功能也使用了这一点——它可以通过在特定时间触发函数来设置提醒。

Claude的模型上下文协议(MCP)也做了类似的事情。使用Sonnet 3.5,Claude可以激活诸如Brave Search获取网络结果、访问其图记忆系统或链接到其他应用等工具。这两个系统都展示了AI如何使用这些“函数调用”将其核心智能连接到现实世界工具。

支持函数调用的AI模型

注意:有时函数调用也被称为工具调用。这两个术语是相同的。

一些支持函数调用的模型包括:

  • GPT-4o
  • Llama 3.2
  • Google的Gemini 2.0 Flash Experimental
  • Claude
  • Mistral
  • Cohere的Command R

虽然还有更多,但这些模型可以通过API轻松访问,有些甚至可以通过Ollama在本地使用开源版本。

函数调用实战——使用Ollama创建AI搜索工具

我们正在构建一个搜索工具,其中Llama 3.2充当决策者——它首先分析查询是否需要实时网络数据。如果是,它会通过Ollama的工具调用API自动触发web\_search工具。这类似于Perplexity如何平衡AI推理与实时数据。

我们需要的工具

  1. Ollama:在本地托管Llama 3.2模型。
  2. Python 3.11+:需要用于async/await模式(对性能至关重要)。
  3. SearchAPI:免费套餐支持每天100次请求。(链接)

定义函数

注意:Ollama将此称为工具调用,与函数调用相同。这将是我们的工具,其参数基于SearchAPI返回的答案。
# 定义我们的搜索工具
search_tool = {
    'type': 'function',
    'function': {
        'name': 'web_search',
        'description': '在某个主题上搜索网络以获取当前信息',
        'parameters': {
            'type': 'object',
            'required': ['query'],
            'properties': {
                'query': {
                    'type': 'string',
                    'description': '要查找的搜索查询'
                }
            }
        }
    }
}

这将在我们的文件main.py中使用,我们将调用Ollama并尝试决定是否调用该工具。

我们的项目结构如下所示:

project_folder/
├── .env
├── search_tool.py
└── main.py

文件说明:

  • .env:将API密钥与代码隔离。
  • search\_tool.py:分离搜索逻辑以便重用。
  • main.py:专注于协调(模型↔工具交互)。

可以通过以下命令安装所需的软件包:

pip install ollama python-dotenv requests

主工作流程(main.py)

  1. AsyncClient:在不阻塞的情况下处理并发工具调用。
  2. 工具响应处理

    • 检查响应中的tool_calls
    • 使用messages数组维护对话状态。
  3. 错误传播:直接将身份验证/搜索错误返回给用户。

main.py的代码如下:

import asyncio
from ollama import AsyncClient
from search_tool import web_search, extract_content

async def process_query(query: str) -> str:
    client = AsyncClient()

    # 定义我们的搜索工具
    search_tool = {
        'type': 'function',
        'function': {
            'name': 'web_search',
            'description': '在某个主题上搜索网络以获取当前信息',
            'parameters': {
                'type': 'object',
                'required': ['query'],
                'properties': {
                    'query': {
                        'type': 'string',
                        'description': '要查找的搜索查询'
                    }
                }
            }
        }
    }

    # 首先,让Ollama决定是否需要搜索
    response = await client.chat(
        'llama3.2',
        messages=[{
            'role': 'user',
            'content': f'回答这个问题:{query}'
        }],
        tools=[search_tool]
    )

    # 初始化可用函数
    available_functions = {
        'web_search': web_search
    }

    # 检查Ollama是否想要使用搜索工具
    if response.message.tool_calls:
        print("正在搜索网络...")

        for tool in response.message.tool_calls:
            if function_to_call := available_functions.get(tool.function.name):
                # 调用搜索函数
                search_results = function_to_call(**tool.function.arguments)

                if "error" in search_results:
                    if search_results["error"] == "authentication_failed":
                        return "身份验证失败。请检查你的API密钥。"
                    return f"搜索错误:{search_results['error']}"

                # 提取相关内容
                content = extract_content(search_results)

                if not content:
                    return "未找到相关的信息。"

                # 将搜索结果添加到对话中
                messages = [
                    {'role': 'user', 'content': query},
                    response.message,
                    {
                        'role': 'tool',
                        'name': tool.function.name,
                        'content': content
                    }
                ]

                # 使用搜索结果从Ollama获取最终回复
                final_response = await client.chat(
                    'llama3.2',
                    messages=messages
                )

                return final_response.message.content

    # 如果没有工具调用,返回直接回复
    return response.message.content

async def main():
    question = input("你想了解什么?")
    print("\n正在处理你的问题...")
    answer = await process_query(question)
    print("\n答案:")
    print(answer)

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

现在,让我们专注于创建search_tool,我们需要保存函数以进行API调用并获取结果。代码基于此处提供的模式。

搜索实现(search\_tool.py)

  1. API错误处理:明确捕获401/429错误。
  2. 内容提取

    • 优先考虑answer_box(精选摘要)。
    • 限制为4个结果以避免令牌溢出。

search_tool.py的代码如下:

import os
import requests
from typing import Dict, Any
from dotenv import load_dotenv

# 使用SearchAPI搜索网络
def web_search(query: str) -> Dict[Any, Any]:
    load_dotenv()
    api_key = os.getenv('SEARCH_API_KEY')

    if not api_key:
        return {"error": "未在环境变量中找到API密钥"}

    url = "https://www.searchapi.io/api/v1/search"
    headers = {
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json"
    }

    params = {
        "engine": "google_news",
        "q": query,
        "num": 5
    }

    try:
        response = requests.get(url, headers=headers, params=params)

        if response.status_code == 401:
            return {"error": "无效的API密钥或身份验证失败"}
        elif response.status_code == 429:
            return {"error": "超出速率限制"}

        response.raise_for_status()
        return response.json()

    except requests.exceptions.RequestException as e:
        error_msg = f"获取搜索结果时出错:{e}"
        if hasattr(e, 'response') and e.response:
            try:
                error_details = e.response.json()
                error_msg = f"{error_msg} - {error_details.get('message', '')}"
            except:
                pass
        return {"error": error_msg}

# 从搜索结果中提取相关内容,因为API返回的数据量很大
def extract_content(search_results: dict) -> str:
    content = []

    if "organic_results" in search_results:
        for result in search_results["organic_results"][:4]:  # 取前4个结果
            if "snippet" in result:
                content.append(result["snippet"])

    if "answer_box" in search_results and search_results["answer_box"]:
        if "answer" in search_results["answer_box"]:
            content.insert(0, search_results["answer_box"]["answer"])

    return "\n\n".join(content)

创建一个.env文件,并在其中存储你的API密钥。在这里获取你的SearchAPI密钥。

SEARCH_API_KEY=ABCD123

然后只需通过以下命令开始:

python main.py

然后你可以问任何问题,Ollama将决定是否使用工具。

你可以在GitHub上找到完整代码。

结论

函数调用使AI模型能够不仅仅是“说”,它们现在可以通过连接到工具和API来触发操作,例如获取实时天气数据或检查库存。通过将自然语言与软件和现实世界数据连接起来,它们解决了实际问题,而不仅仅是提供通用答案。这将聊天机器人变成了动态助手,可以自动化任务、获取更新并与其它系统交互,重塑了我们日常使用AI的方式。

查看GitHub仓库:🌟 Function Calling on GitHub

参考文献和工具:

最后感谢阅读!欢迎关注我,微信公众号倔强青铜三。欢迎点赞收藏关注,一键三连!!!

倔强青铜三
41 声望0 粉丝