跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
PythonAI算法

本地运行大语言模型:实现函数调用与工具自主使用

如何在本地运行 Ollama 并利用 Function Calling 功能扩展大语言模型的能力。通过定义 Python 函数作为工具,构建包含函数元数据的 Prompt,使模型能够根据用户意图自主判断并调用相应工具。文章详细展示了从函数定义、JSON 元数据生成、API 交互到结果执行的完整代码流程,并通过四个典型测试用例验证了模型在不同场景下的工具选择准确性。内容涵盖技术原理、代码实现细节、测试结果分析以及针对本地模型调用的注意事项与优化建议,旨在帮助开发者掌握本地 LLM 的工具调用开发模式。

刀狂发布于 2025/2/6更新于 2026/10/2488 浏览
本地运行大语言模型:实现函数调用与工具自主使用

本地运行大语言模型:实现函数调用与工具自主使用

在之前的文章中,我们已经介绍了如何在本地运行 Ollama 以及如何通过提供外部数据库的方式微调模型的答案。本篇文章将继续探索如何使用'函数调用(Function Calling)'功能以扩展模型能力,使其在'智能'的道路上越走越远。

Function Calling 介绍

根据 OpenAI 官方文档,Function Calling 是使得大型语言模型具备可以连接到外部工具的能力。简而言之,开发者事先给模型提供了若干工具(函数),在模型理解用户的问题后,自行判断是否需要调用工具以获得更多上下文信息,帮助模型更好地决策。

举个例子:在前文中我们是利用 Document Loaders 将事先准备好的文本作为上下文提供给模型,而使用 Function Calling 以后,我们只要提供一个'搜索函数'作为工具,模型即可自己通过搜索引擎进行搜索然后得出答案。

得益于最新的模型训练,现在的模型既能够检测何时应调用函数(取决于输入),还能够以比以前的模型更贴近函数签名的方式响应 JSON。

用途

Function Calling 有什么用?通常有以下三个主要应用场景:

  • 创建通过调用外部 API 回答问题的助手:例如查询天气、股票价格或系统状态。
  • 将自然语言转换为 API 调用:允许用户用口语描述需求,后端自动执行对应的程序逻辑。
  • 从文本中提取结构化数据:从非结构化文本中抽取特定字段并格式化为 JSON。

下面我们就一步一步来理解一下 Function Calling 的实现原理。

定义 Function

首先定义 4 个 function,这 4 个 function 有不同的使用场景,用于模拟不同的外部服务。

def get_gas_prices(city: str) -> float:
    """Get gas prices for specified cities."""
    print(f'Get gas prices for {city}.')

def github(project: str) -> str:
    '''Get the information such as author from github with the project name.'''
    print(f'Access the github for {project}.')

def get_weather(city: str) -> str:
    """Get the current weather given a city."""
    print(f'Getting weather for {city}.')

def get_directions(start: str, destination: str) -> float:
    """Get directions from Google Directions API.
    start: start address as a string including zipcode (if any)
    destination: end address as a string including zipcode (if any)"""
    print(f'Get directions for {start} {destination}.')

定义 Prompts

为了让模型知道这些函数的存在及其参数结构,我们需要构建一个包含函数元数据的 Prompt。

functions_prompt = f"""
You have access to the following tools:
{function_to_json(get_weather)}
{function_to_json(get_gas_prices)}
{function_to_json(get_directions)}
{function_to_json(github)}

You must follow these instructions:
Always select one or more of the above tools based on the user query
If a tool is found, you must respond in the JSON format matching the following schema:
{{
   "tools": {{
        "tool": "<name of the selected tool>",
        "tool_input": <parameters for the selected tool, matching the tool's JSON schema
   }}
}}
If there are multiple tools required, make sure a list of tools are returned in a JSON array.
If there is no tool that match the user request, you must respond empty JSON {{}}.

User Query:
    """

这是一个复杂的提示,让我们一步一步来看:

  1. 告知工具列表:告诉模型我提供了 4 个工具,让它自己去查询这 4 个工具的元数据。function_to_json 函数返回的内容如下:
{
  "name": "get_weather",
  "description": "Get the current weather given a city.",
  "parameters": {
    "type": "object",
    "properties": {
      "city": {
        "type": "str"
      }
    }
  },
  "returns": "str"
}

这一步就是让模型根据这几个函数的元数据来理解函数,尤其是 description 写的应该尽量详细,以便模型准确匹配意图。

  1. 指定行为规则:提示模型作出自己的判断,根据上面提供的工具选择一个或多个进行调用,并指定了模型返回数据格式——JSON 以及这个 JSON 的 Schema。

  2. 等待用户问题:最后留出位置等待用户的实际 Query。

完整代码实现

整段代码展示了如何结合 Python 脚本、Ollama API 以及自定义函数来实现这一流程。

import inspect
import json
import requests
from typing import get_type_hints

def generate_full_completion(model: str, prompt: str) -> dict[str, str]:
    # 调用 Ollama 本地 API
    params = {"model": model, "prompt": prompt, "stream": False}
    response = requests.post(
        "http://localhost:11434/api/generate",
        headers={"Content-Type": "application/json"},
        data=json.dumps(params),
        timeout=60,
    )
    return json.loads(response.text)

def get_gas_prices(city: str) -> float:
    """Get gas prices for specified cities."""
    print(f'Get gas prices for {city}.')

def github(project: str) -> str:
    '''Get the information such as author from github with the project name.'''
    print(f'Access the github for {project}.')

def get_weather(city: str) -> str:
    """Get the current weather given a city."""
    print(f'Getting weather for {city}.')

def get_directions(start: str, destination: str) -> float:
    """Get directions from Google Directions API.
    start: start address as a string including zipcode (if any)
    destination: end address as a string including zipcode (if any)"""
    print(f'Get directions for {start} {destination}.')

def get_type_name(t):
    name = str(t)
    if "list" in name or "dict" in name:
        return name
    else:
        return t.__name__

def function_to_json(func):
    # 动态获取函数签名和类型注解,生成 JSON 描述
    signature = inspect.signature(func)
    type_hints = get_type_hints(func)

    function_info = {
        "name": func.__name__,
        "description": func.__doc__,
        "parameters": {"type": "object", "properties": {}},
        "returns": type_hints.get("return", "void").__name__,
    }

    for name, _ in signature.parameters.items():
        param_type = get_type_name(type_hints.get(name, type(None)))
        function_info["parameters"]["properties"][name] = {"type": param_type}

    return json.dumps(function_info, indent=2)

def execute_fuc(tool_data):
    # 根据模型返回的工具名称,动态查找并执行对应的 Python 函数
    func_name = tool_data["tool"]
    func_input = tool_data["tool_input"]

    # 获取全局命名空间中的函数对象
    func = globals().get(func_name)

    if func is not None and callable(func):
        # 如果找到了函数并且是可调用的,调用它
        func(**func_input)
    else:
        print(f"Unknown function: {func_name}")

def main():
    functions_prompt = f"""
You have access to the following tools:
{function_to_json(get_weather)}
{function_to_json(get_gas_prices)}
{function_to_json(get_directions)}
{function_to_json(github)}

You must follow these instructions:
Always select one or more of the above tools based on the user query
If a tool is found, you must respond in the JSON format matching the following schema:
{{
   "tools": {{
        "tool": "<name of the selected tool>",
        "tool_input": <parameters for the selected tool, matching the tool's JSON schema
   }}
}}
If there are multiple tools required, make sure a list of tools are returned in a JSON array.
If there is no tool that match the user request, you must respond empty JSON {{}}.

User Query:
    """

    GPT_MODEL = "mistral:7b-instruct-v0.2-q8_0"

    prompts = [
        "What's the weather like in Beijing?",
        "What is the distance from Shanghai to Hangzhou and how much do I need to fill up the gas in advance to drive from Shanghai to Hangzhou?",
        "Who's the author of the 'snake-game' on github?",
        "What is the exchange rate between US dollar and Japanese yen?",
    ]

    for prompt in prompts:
        print(f"❓{prompt}")
        question = functions_prompt + prompt
        response = generate_full_completion(GPT_MODEL, question)
        try:
            data = json.loads(response.get("response", response))
            # print(data)
            for tool_data in data["tools"]:
                execute_fuc(tool_data)
        except Exception:
            print('No tools found.')
        print(f"Total duration: {int(response.get('total_duration')) / 1e9} seconds")

if __name__ == "__main__":
    main()

测试与分析

整段代码就是用来测试模型对问题的理解能力以及是否能正确判断调用哪个工具的。

我们来看下设定的 4 个问题:

  1. What's the weather like in Beijing?
  2. What is the distance from Shanghai to Hangzhou and how much do I need to fill up the gas in advance to drive from Shanghai to Hangzhou?
  3. Who's the author of the 'snake-game' on github?
  4. What is the exchange rate between US dollar and Japanese yen?

按照我们的设想,如果模型理解了我提供的工具的功能以及读懂了用户的问题,应该按照以下规则选择工具:

  • 问题 1 应该对应的是 get_weather('Beijing')
  • 问题 2 应该对应的是 get_directions('Shanghai', 'Hangzhou') 和 get_gas_prices('Shanghai')
  • 问题 3 应该对应的是 github('snake-game')
  • 问题 4 应该没有对应的函数,模型不选择任何工具

下一步,我们也写好了代码进行测试,我们在工具函数里进行参数打印,以查看是否达到我们的预期。

测试结果

❓What's the weather like in Beijing?
Getting weather for Beijing.
Total duration: 3.481918084 seconds

❓What is the distance from Shanghai to Hangzhou and how much do I need to fill up the gas in advance to drive from Shanghai to Hangzhou?
Get directions for Shanghai, China Hangzhou, China.
Get gas prices for Shanghai.
Total duration: 5.467253959 seconds

❓Who's the author of the 'snake-game' on github?
Access the github for snake-game.
Total duration: 2.510993791 seconds

❓What is the exchange rate between US dollar and Japanese yen?
{}
No tools found.
Total duration: 0.20056292 seconds

深入理解与注意事项

1. 函数元数据的重要性

在 function_to_json 中,我们使用了 inspect 模块来获取函数的签名和文档字符串。这对于本地模型尤为重要,因为本地模型通常不如云端大模型那样经过广泛的指令微调。详细的 description 能显著提高模型识别意图的准确率。建议为每个工具编写清晰、具体的中文或英文描述,明确输入参数的含义。

2. JSON Schema 约束

Prompt 中定义的 JSON Schema 必须严格遵循模型期望的格式。在上述示例中,我们要求模型返回嵌套的 JSON 对象。如果模型返回的格式不符合预期(例如缺少引号或括号),解析时会抛出异常。在实际生产中,建议增加健壮的错误处理机制,例如尝试多次重试或使用正则表达式提取 JSON 块。

3. 本地模型的局限性

虽然 Mistral 等模型表现良好,但不同量化版本(如 q8_0, q4_k_m)在函数调用能力上可能存在差异。较小的模型可能无法严格遵守复杂的 JSON 结构。如果遇到模型频繁返回错误格式的情况,可以尝试简化 Prompt 中的 Schema 描述,或者更换参数量更大的模型。

4. 扩展性设计

目前的 execute_fuc 函数通过 globals() 动态查找函数。在生产环境中,建议使用注册表模式(Registry Pattern)来管理工具,避免全局变量污染,同时便于权限控制和日志记录。此外,可以将 print 语句替换为真实的 HTTP 请求,例如调用真实的天气 API 或 GitHub API,从而实现完整的自动化工作流。

总结

本篇文章详细介绍了使用 Function Calling 使得 Ollama 模型具备调用外部工具的能力。通过对定义函数、构建 Prompt、编写执行逻辑以及测试结果的完整分析,展示了本地大模型实现自主工具调用的可行路径。

希望本系列文章能使大家对本地运行大语言模型有一些深入的了解。后续我们将继续探讨更多本地 LLM 的实用场景,包括知识库检索增强生成(RAG)以及多模态交互等内容。

目录

  1. 本地运行大语言模型:实现函数调用与工具自主使用
  2. Function Calling 介绍
  3. 用途
  4. 定义 Function
  5. 定义 Prompts
  6. 完整代码实现
  7. 测试与分析
  8. 测试结果
  9. 深入理解与注意事项
  10. 1. 函数元数据的重要性
  11. 2. JSON Schema 约束
  12. 3. 本地模型的局限性
  13. 4. 扩展性设计
  14. 总结

更多推荐文章

查看全部
  • C++ STL string 类从零实现详解
  • DooTask:AI 赋能的开源项目协作工具部署与使用指南
  • MySQL 表约束核心指南:从基础到外键实战
  • 鸿蒙金融理财应用:运维监控、性能优化与安全加固
  • 数据结构:单链表基础与核心操作实现
  • 大模型基础知识与核心架构解析
  • Ollama 架构与对话处理流程解析
  • 高性能 C++ 单机任务调度器设计与实现
  • Gateway 设备令牌不匹配问题排查指南
  • 天然气管道内检测机器人检测节机械结构设计
  • 深度学习模型优化策略与实战调参
  • Ubuntu 部署 OpenClaw 完整指南
  • 动态规划示例:统计字符串中 shy 子序列数量
  • AI 产品经理的十大核心技能
  • 如何从零开始训练大型语言模型
  • Git 版本控制从入门到精通
  • SpringAI Agent 开发实战:基于 Skills 实现代码评审
  • 本地部署大语言模型实践(2):集成外部知识库详细步骤
  • WeTTY 运维监控实战:构建企业级 Web 终端管理
  • GitHub Copilot、Trae 与 Cursor AI 编程工具对比评测

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • RSA密钥对生成器

    生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online

  • Mermaid 预览与可视化编辑

    基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online

  • 随机西班牙地址生成器

    随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • curl 转代码

    解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online