高级 20 分钟API

使用Ollama掌握工具调用(Tool Calling) Python

工具调用(tool calling)将仅能生成文本的模型转变为能够触发实际代码执行的智能体:调用天气 API、查询数据库、执行计算。本指南将带您从头到尾掌握如何在 Python 中使用 Ollama 进行工具调用——包括工具的 JSON 格式、执行循环、工具调用的流式传输(0.17 系列版本),以及由直接应用于解码过程的 JSON Schema 约束的结构化输出。所有操作都在本地的 http://localhost:11434 上运行,无需 API 密钥,也不会泄露数据。

作者: Mohamed Meguedmi·更新于 2026-08-27·已在 Windows、macOS 和 Linux 上测试

#为何需要工具调用(tool calling)?

仅靠 LLM 本身,无法了解训练结束后的现实世界:它不知道今天的天气、账户余额,也不知道您数据库中的内容。工具调用填补了这一空白。您向模型说明有哪些可用函数,模型决定调用哪些函数、使用哪些参数,您的代码执行这些函数,再将结果返回给模型,让它根据这些信息撰写回答。

需要理解的关键点:模型本身从不执行任何操作。它仅生成一个结构化请求 —— 「调用 get_meteo,参数 ville='Lyon'」。由您的 Python 程序来执行该函数并保持完全控制权。这种分离机制使得工具调用安全且可预测。

最新数据
模型实时查询 API,而不是根据训练时记住的信息进行猜测。
具体操作
创建一个工单、发送一封邮件、写入数据库——LLM进行协调,你的代码负责执行。
可靠性
计算和精确检索交由确定性代码完成,而不是由模型凭空编造结果。
100 % 本地
使用Ollama,整个链路都保留在您的设备上:无需API密钥,无出站请求,也无需按token计费。

#Ollama 工具调用的工作原理

本地编程副驾驶套件

本指南带你上手模型。工具包则帮你用上能在你的编辑器中编写代码的编程助手。

  • 在线空间,终身可用
  • PDF + 文件
  • 终身更新

完整流程分为五个阶段。清晰地理解这一过程可避免最常见的误解:认为仅一次调用即可完成。实际上至少需要两次调用——一次用于获取工具请求,另一次用于获取最终响应。

  1. 01
    您发送问题及工具
    聊天请求包含用户消息和可用工具列表(tools 参数)
  2. 02
    模型返回工具请求
    不再以文本形式回答,而是返回一个或多个 tool_calls,包含函数名称和参数。
  3. 03
    您的代码执行该函数
    您获取 name 和 arguments,调用对应的实际 Python 函数,并取得结果。
  4. 04
    您将结果传回
    结果会作为角色为“tool”的消息添加到历史记录中,然后再次调用 chat。
  5. 05
    模型负责生成最终回答
    模型根据得到的结果,这次为用户生成自然语言回复。
i
至少两次调用
一个完整的工具调用周期 = 至少调用模型两次。如果模型接连调用多个工具,就循环执行,直到其回复中不再包含 tool_calls。

#先决条件

需要三个组件:正在运行的 Ollama 守护进程、真正支持工具调用的模型,以及官方 Python 库。请注意第二点——并非所有模型都能进行工具调用。请选择近期专门为此设计的模型系列。

Ollama 已更新至最新版本
要使用工具调用的流式输出,需使用 0.17 系列或更新版本。守护进程在 http://localhost:11434 上监听。
支持工具的模型
Qwen 3.5, Granite 4.2, Mistral Small, Devstral, gpt-oss。ollama.com/library 上标注「tools」的模型。
足够的 VRAM
一个小型模型(Qwen 3.5 4B ≈ 3.4 GB)足以用于测试;一个 Granite 4.2 8B(≈ 5.3 GB)或一个 Qwen 3.5 9B(≈ 6.6 GB)在多工具指令遵循方面表现更佳。RTX 3060 12 GB 作为入门级显卡。
Ollama库
pip install -U ollama。它能够直接根据带类型的 Python 函数构建工具的模式。
准备环境
# Le daemon Ollama doit tourner (souvent déjà lancé en service)
ollama serve

# Un modèle qui supporte les outils
ollama pull qwen3.5:4b

# La librairie Python officielle
pip install -U ollama
!
不支持工具的模型
向不支持 tools 参数的模型传入该参数,并不一定会产生明确的错误:模型可能会忽略工具并以文本形式回复,或在回复内容中返回伪 JSON。编写代码前,请务必检查模型的“Tools”标签。

#工具的JSON格式

工具通过与 OpenAI 规范严格一致的 JSON 结构来描述:一个 type: "function" 的对象,其中包含名称、描述和一个 JSON Schema 格式的 parameters 对象。描述至关重要——模型会读取它,以决定何时以及如何调用工具。请描述清楚。

工具定义(OpenAI格式)
{
  "type": "function",
  "function": {
    "name": "get_meteo",
    "description": "Renvoie la météo actuelle pour une ville donnée",
    "parameters": {
      "type": "object",
      "properties": {
        "ville": {
          "type": "string",
          "description": "Nom de la ville, ex : Lyon"
        },
        "unite": {
          "type": "string",
          "enum": ["celsius", "fahrenheit"],
          "description": "Unité de température souhaitée"
        }
      },
      "required": ["ville"]
    }
  }
}
→
让库生成模式(schema)
在 Python 中,您不必手动编写这段 JSON。如果直接传入一个带有类型注解和文档字符串(docstring)的函数,ollama 库会自动推导出其模式(名称、类型、描述)。这是避免 JSON 中出现拼写错误的最稳妥方法。

#用 Python 完成首次工具调用

我们从最简单的情况开始:一个函数、一个问题,然后观察模型如何决定。类型注解和文档字符串用于生成发送给模型的模式定义(schema)。

首次工具调用
import ollama

def get_meteo(ville: str, unite: str = "celsius") -> str:
    """Renvoie la météo actuelle pour une ville donnée.

    Args:
        ville: Nom de la ville (ex : Lyon).
        unite: Unité de température, celsius ou fahrenheit.
    """
    # Ici, un vrai appel à une API météo. On simule le retour.
    return f"21 degrés, ciel dégagé à {ville} ({unite})."

reponse = ollama.chat(
    model="qwen3.5:4b",
    messages=[{"role": "user", "content": "Quel temps fait-il à Lyon ?"}],
    tools=[get_meteo],  # la lib introspecte signature + docstring
)

# Le modèle n'a pas répondu en texte : il demande un outil
for appel in reponse.message.tool_calls or []:
    print(appel.function.name)       # -> get_meteo
    print(appel.function.arguments)  # -> {'ville': 'Lyon'}

此时 message.content 通常为空:模型已将请求返回至 message.tool_calls。每个 tool_call 提供 function.name(字符串)和 function.arguments(已由库自动反序列化为 Python 字典)。接下来只需执行并返回结果。

#完整的运行循环

以下是可复用的工具调用智能体代码框架:用一个字典将每个工具名称映射到对应的函数,执行模型请求的工具调用,将结果加入历史记录,然后再次调用模型以生成最终回答。将整个流程放入循环中,以处理模型连续调用多个工具的情况。

完整的智能体循环
import ollama

def get_meteo(ville: str, unite: str = "celsius") -> str:
    """Météo actuelle d'une ville."""
    return f"21 degrés, ciel dégagé à {ville}."

# Registre nom -> fonction réelle
OUTILS = {"get_meteo": get_meteo}

messages = [{"role": "user", "content": "Météo à Lyon puis à Marseille ?"}]

while True:
    reponse = ollama.chat(model="qwen3.5:4b", messages=messages, tools=[get_meteo])
    messages.append(reponse.message)  # on garde la demande dans l'historique

    if not reponse.message.tool_calls:
        # Plus d'outil demandé : c'est la réponse finale
        print(reponse.message.content)
        break

    for appel in reponse.message.tool_calls:
        fonction = OUTILS.get(appel.function.name)
        if fonction is None:
            resultat = f"Erreur : outil inconnu '{appel.function.name}'"
        else:
            resultat = fonction(**appel.function.arguments)
        messages.append({
            "role": "tool",
            "tool_name": appel.function.name,
            "content": str(resultat),
        })
!
永远不要信任传入的参数
参数来自模型本身:它们可能不完整、类型错误或超出范围。在执行函数前必须验证这些参数,特别是当函数涉及文件系统、数据库或shell命令时。未经验证的工具调用可能引发注入攻击。

结果消息包含tool角色以及tool_name字段,用于标识其对应的调用。content必须为字符串:在返回前请对对象进行序列化(json.dumps)。模型会将该内容视为对世界的观察。

#与 OpenAI 对齐:使用 openai 客户端运行相同代码

Ollama 提供一个兼容 OpenAI 的端点,位于 /v1。如果您的代码已经使用了 openai 客户端,几乎无需修改:将 base_url 指向 Ollama,并设置一个虚假的 API 密钥。工具和 tool_calls 的格式完全一致——这种「OpenAI 兼容性」使得迁移变得简单。

通过 openai 客户端实现工具调用
from openai import OpenAI

client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")

reponse = client.chat.completions.create(
    model="qwen3.5:4b",
    messages=[{"role": "user", "content": "Météo à Lyon ?"}],
    tools=[{
        "type": "function",
        "function": {
            "name": "get_meteo",
            "description": "Météo actuelle d'une ville",
            "parameters": {
                "type": "object",
                "properties": {"ville": {"type": "string"}},
                "required": ["ville"],
            },
        },
    }],
)

print(reponse.choices[0].message.tool_calls)
i
需要了解的一个区别
使用 openai 客户端时,function.arguments 会以 JSON 字符串的形式返回(需用 json.loads 解析),而原生 ollama 库返回的已经是字典。从一个客户端迁移到另一个客户端时,请注意这一点。

#工具调用的流式传输(0.17 系列版本)

过去,启用流式传输会禁用工具调用,两者只能二选一。自 0.17 系列起,Ollama 可以在生成过程中流式传输工具调用。具体来说,您会在流中的数据块(chunks)里收到 tool_calls,以及可能同时出现的文本,从而可以一边流畅地显示回复,一边触发工具。

工具调用支持流式传输
import ollama

flux = ollama.chat(
    model="qwen3.5:4b",
    messages=[{"role": "user", "content": "Météo à Nice ?"}],
    tools=[get_meteo],
    stream=True,
)

for morceau in flux:
    # Le texte arrive token par token
    if morceau.message.content:
        print(morceau.message.content, end="", flush=True)
    # Les appels d'outils arrivent aussi dans le flux
    for appel in morceau.message.tool_calls or []:
        print("\n[outil]", appel.function.name, appel.function.arguments)
→
何时启用流式传输
流式传输在对话界面中表现优异,用户可以实时看到答案逐步生成。对于批量处理或数据提取任务,建议使用非流式模式:这样更易管理,且能一次性获取完整回答。

#结构化输出:强制使用JSON Schema

工具调用用于执行操作;结构化输出用于保证回答的格式。通过 format 参数,您传入一个 JSON Schema,Ollama 会在解码时应用该模式:模型在生成每一个 token 时都受到约束,只能生成符合该模式的有效输出。无需再脆弱地解析格式不规范的 JSON——生成机制本身就保证了输出结构。

在 Python 中,最方便的方法是用 Pydantic 模型描述结构,再通过 model_json_schema() 生成相应的模式(schema)。之后,您会得到一个具有明确类型且经过验证的对象。

经 Pydantic 验证的结构化输出
from pydantic import BaseModel
import ollama

class Facture(BaseModel):
    numero: str
    montant_ttc: float
    devise: str
    lignes: list[str]

reponse = ollama.chat(
    model="qwen3.5:4b",
    messages=[{
        "role": "user",
        "content": "Extrais numéro, montant TTC, devise et lignes de : "
                   "Facture F-2026-0042, total 149,90 EUR, "
                   "prestations : audit, rédaction.",
    }],
    # Le schéma est appliqué au décodage : sortie garantie conforme
    format=Facture.model_json_schema(),
)

facture = Facture.model_validate_json(reponse.message.content)
print(facture.montant_ttc)  # -> 149.9
i
format="json" 与完整 JSON Schema 的对比
format="json" 只强制输出有效的 JSON,不限定其结构。传入完整的 JSON Schema 则能施加更严格的约束:它会在解码时约束字段、类型和允许的值。只要您知道预期的结构,就应始终优先使用明确的 schema。
→
最佳组合
通过工具调用获取数据,通过结构化输出规范地返回数据。一个先调用 API、再返回经过验证的 Pydantic 对象的智能体,比仅要求模型“用 JSON 回答”、然后寄希望于它能成功的做法稳健得多。

#错误处理及常见陷阱

工具调用很少出现明显失败:大多数情况下,模型会静默地‘脱轨’。以下是常见故障及处理方法。

未返回任何tool_call
模型以文本形式作答,而应调用工具。请优化工具描述或更换模型:小模型常无法正确判断是否需要调用工具。
参数缺失或错误
function.arguments 可能遗漏标记为 required 的字段,也可能给字段传入错误类型的值。调用实际函数前,请用 Pydantic 或 try/except 验证参数,并将错误作为工具结果返回给模型。
模型凭空编造的工具
模型会生成一个不存在的函数名称。因此使用 OUTILS.get(name) 可返回错误信息而非崩溃——模型可在下一轮中自行修正。
工具调用无限循环
模型可能无限次重复请求同一工具。请添加最大迭代次数计数器(例如5次),以中断循环,避免空转。
上下文截断
Ollama 有时默认将上下文限制为 2048 个 token,这会导致长时间会话中的工具历史记录被截断。请通过模型选项增大 num_ctx。
未序列化的结果
直接返回原始 Python 对象作为 content 会破坏请求。始终以字符串形式(json.dumps 或 str)序列化后再添加到消息中。
工具的防御性执行
import json

def executer_outil(appel, registre, garde_fou=5):
    nom = appel.function.name
    fonction = registre.get(nom)
    if fonction is None:
        return f"Erreur : outil inconnu '{nom}'."
    try:
        resultat = fonction(**appel.function.arguments)
    except TypeError as e:
        return f"Erreur d'arguments pour {nom} : {e}"
    except Exception as e:
        return f"Échec de {nom} : {e}"
    return json.dumps(resultat, ensure_ascii=False, default=str)

核心原则:绝不让工具错误导致智能体崩溃。将错误消息作为结果返回给模型。好的模型看到“未知工具”或“缺少参数”后,会自行调整下一次调用。


#深入了解

您现在已经掌握如何在 Python 中使用 Ollama 进行工具调用:工具的 JSON 格式、执行循环、与 OpenAI 的功能对等、流式输出,以及受 schema 约束的结构化输出。这些指南可帮助您进一步了解这一主题。

Ollama的REST API
《通过 REST API 将 Ollama 集成到 Python 应用中》——介绍 :11434 端点、流式输出和 JSON 模式的基础知识,是本指南全部内容的基础。
基于LangChain的智能体
“使用 LangChain 和 Ollama,以 Python 创建本地 AI 智能体”——在基础工具调用之上,编排多个工具和记忆。
选择量化方案
《选择量化方式(Q4、Q5、Q8、FP16)》——为负责调用您工具的模型平衡显存需求与质量。
这份指南对您有帮助吗?

有反馈、发现了错误,或想补充说明?请告诉我们,让这份指南对每个人都更有帮助。