中文 ▾
获取 API 密钥

Python 中的 Ollama API 及托管替代方案

Python 中的 Ollama API 大约只需十行代码,且相同的代码结构也适用于托管接口。本文展示了两种方法,并解释了何时选择哪种方案。

更新于

要点

  • Ollama 监听 11434 端口,提供原生 API 以及 OpenAI 风格的 /v1 接口。
  • ollama 包、原生 requests 和 OpenAI SDK 均可使用;OpenAI SDK 的可移植性最强。
  • 切换到托管 API 意味着更改 base URL、密钥和模型名称。
  • 本地方案最适合离线工作和固定硬件;托管方案在没有 GPU 时更简单。
一台通过发光线缆连接到云服务器的本地计算机,代表本地和托管 LLM API

简短版本

如果你搜索 ollama api python,通常是为了实现以下三种需求之一:调用运行在你本地机器上的模型、从中获取流式 token,或者让同一脚本在其他环境中复用。Ollama 提供了所有这三种功能。它运行一个本地服务器,默认地址为 http://localhost:11434,你可以使用 ollama Python 包、原生 HTTP 或指向其 /v1 路径的任何 OpenAI 客户端与其交互。

缺点是模型必须在你的硬件上运行。没有合适 GPU 的笔记本电脑在处理较大模型时会很慢,而没有 GPU 的服务器也不适合。对于这种情况,托管的 OpenAI 兼容 API 是更简单的选择,并且由于请求格式相同,切换只需更改三行配置。下面涵盖了这两种路径,并附有可粘贴的代码。

使用 ollama 包调用本地模型

安装 Ollama,拉取一个模型,并确保服务器正在运行。然后安装 Python 客户端:

pip install ollama
ollama pull llama3.2

基本的聊天调用如下所示。模型名称是你拉取的任意名称;llama3.2 仅作为示例。

import ollama

response = ollama.chat(
    model="llama3.2",
    messages=[{"role": "user", "content": "Explain DNS in two sentences."}],
)
print(response["message"]["content"])

流式输出

传入 stream=True,你将获得一个块迭代器,而不是单个回复。每个块在 message.content 中携带答案的一部分。

import ollama

stream = ollama.chat(
    model="llama3.2",
    messages=[{"role": "user", "content": "Write a haiku about servers."}],
    stream=True,
)
for chunk in stream:
    print(chunk["message"]["content"], end="", flush=True)
print()

将客户端指向另一台机器

如果 Ollama 运行在不同的主机上,请创建带有显式地址的客户端,例如 ollama.Client(host="http://192.168.1.20:11434")。默认情况下,服务器仅监听本地机器,因此远程访问需要在服务器端进行明确设置。如果没有代理和身份验证,请勿将其暴露到公共互联网,因为原生 API 本身没有密钥检查。

使用 requests 进行原生 HTTP 请求

你完全不需要该包。原生聊天端点是 POST /api/chat。该端点默认开启流式输出,流数据为换行符分隔的 JSON,而非 SSE。如果你希望返回单个对象,请设置 "stream": false。

import requests

r = requests.post(
    "http://localhost:11434/api/chat",
    json={
        "model": "llama3.2",
        "messages": [{"role": "user", "content": "Name three HTTP status codes."}],
        "stream": False,
    },
    timeout=120,
)
r.raise_for_status()
print(r.json()["message"]["content"])

设置一个宽裕的超时时间。拉取后的第一次请求需要将模型加载到内存中,这可能需要比后续请求长得多的时间。

手动读取流只需多几行代码。调用 requests.post(..., stream=True),遍历 r.iter_lines(),跳过空行,并对每一行运行 json.loads。每个对象都包含回复的一小部分,最后一个对象包含 "done": true。这在最小化容器中很有用,因为你不想安装其他包,并且它清楚地展示了库为你做了什么。托管 API 的流式传输机制不同,以 data: 开头的 SSE 行,这也是让 OpenAI SDK 在可移植代码中为你解析流的一个理由。

在 Ollama 中使用 OpenAI SDK

Ollama 还在 /v1 下暴露了一个 OpenAI 风格的端点。这意味着官方的 openai 包可以工作,大多数允许你设置自定义 base URL 的软件也可以工作。SDK 需要 API 密钥,但本地 Ollama 会忽略它,因此任何字符串都行。

from openai import OpenAI

client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")
resp = client.chat.completions.create(
    model="llama3.2",
    messages=[{"role": "user", "content": "Give me a one-line summary of TCP."}],
)
print(resp.choices[0].message.content)

这是最有用的形式,因为它将你的代码与服务器解耦。同一个函数今天可以与本地模型对话,明天可以与别的东西对话,这就是下一节的内容。

针对托管 API 的相同代码

这里的托管选项是一个 OpenAI 兼容 API,只有一个模型 uncensored,由独立提供商运行,并针对合法成人用途的无内容拒绝回答进行了调整。它仅支持文本。base URL 是 https://api.ollamaapi.cc/v1,你使用来自 API 密钥页面 的 Bearer 密钥进行身份验证。

这是一个从环境变量读取目标的版本,托管值为默认值。要回到你的本地服务器,导出两个变量,无需更改其他内容。

import os
from openai import OpenAI

client = OpenAI(
    base_url=os.getenv("LLM_BASE_URL", "https://api.ollamaapi.cc/v1"),
    api_key=os.getenv("LLM_API_KEY", "YOUR_API_KEY"),
)
MODEL = os.getenv("LLM_MODEL", "uncensored")

resp = client.chat.completions.create(
    model=MODEL,
    messages=[
        {"role": "system", "content": "You are a concise assistant."},
        {"role": "user", "content": "List four uses of a message queue."},
    ],
    temperature=0.7,
)
print(resp.choices[0].message.content)

对于本地运行,设置 LLM_BASE_URL=http://localhost:11434/v1,LLM_API_KEY=ollama 和 LLM_MODEL=llama3.2。快速入门 提供了 curl 和 Node 的相同设置。

托管端的流式输出、JSON 模式和工具

托管接口支持通过 SSE 进行流式输出、JSON 模式、工具调用以及通常的采样参数(temperature、top_p、stop、seed 和惩罚)。使用 SDK 进行流式输出与你为任何 OpenAI 风格服务器编写的循环相同:

stream = client.chat.completions.create(
    model="uncensored",
    messages=[{"role": "user", "content": "Tell a two-paragraph story about a lighthouse."}],
    stream=True,
    stream_options={"include_usage": True},
)
for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)

对于结构化输出,设置 response_format={"type": "json_object"} 并在提示词中说明你想要 JSON。无论如何,始终在 try 块内解析;模型仍可能返回你未预料到的内容。

与本地使用相比,代码中有一个重要的区别:限制。完整密钥允许 8 个并发请求和每分钟 300 个请求,流式请求会保留其槽位直到流结束。如果你使用线程进行扇出,请将池限制为 8,并在出现 HTTP 429 时使用 Retry-After 标头进行重试(如果存在)。

保持对话继续

本地和托管聊天接口都不会在调用之间记住任何内容。服务器只看到你发送的消息,因此聊天机器人是一个你追加并重发的列表。该模式对两个目标都是相同的,这也是将 OpenAI 风格客户端作为默认值的另一个理由。

history = [{"role": "system", "content": "You are a helpful assistant."}]

def ask(text):
    history.append({"role": "user", "content": text})
    resp = client.chat.completions.create(model=MODEL, messages=history)
    answer = resp.choices[0].message.content
    history.append({"role": "assistant", "content": answer})
    return answer

print(ask("Pick a name for a hiking app."))
print(ask("Give me a tagline for it."))

两个实用提示。首先,本地模型有一个你设置的上下文长度,内存必须容纳它;如果历史记录超过该长度,较早的轮次会被截断或调用失败,具体取决于设置。托管模型接受 100,000 个 token 的窗口,包括补全,这比大多数聊天需要的要多得多,但长历史记录仍会在每一轮消耗输入 token。其次,一旦历史记录变长,请将最旧的轮次总结为一条短消息,而不是重新发送所有消息。这样可以保持任一后端的延迟和成本稳定。

礼貌地重试

在托管 API 上,临时过载会返回带有 Retry-After: 5 标头的 HTTP 429。一个小包装器处理它:

import time
from openai import RateLimitError

def chat_with_retry(messages, tries=4):
    for attempt in range(tries):
        try:
            return client.chat.completions.create(model=MODEL, messages=messages)
        except RateLimitError:
            time.sleep(5 * (attempt + 1))
    raise RuntimeError("still rate limited")

本地 Ollama 不会进行速率限制,但当模型繁忙时会排队请求,因此相同的包装器在那里也没有害处。

本地还是托管:如何决策

没有哪种选择在总体上更好。使用以下要点作为检查清单。

  • 选择本地 Ollama:如果你需要离线工作,如果数据绝不能离开本机,如果你已经拥有可用的 GPU,或者你想快速尝试许多不同的开放模型。
  • 选择托管 API,如果你没有 GPU、在小型 VPS 或 CI 环境中运行、多人或服务共享一个后端,或者想要长上下文窗口而无需自行管理内存。托管模型支持最多 100,000 token 的提示词加补全。
  • 在开发阶段同时使用两者:在本地迭代,然后通过环境变量将生产环境指向托管 URL。

托管使用按 token 计费,输入 token 为 $0.15/1M,输出 token 为 $0.50/1M,从预付额度中支付。定价页面列出了充值金额,新账户可一键领取 $0.50 试用额度,无需信用卡。提示词不用于训练。

你将遇到的错误

症状可能原因修复方法
11434 端口连接被拒绝Ollama 服务器未运行启动应用程序或运行 ollama serve
未找到模型模型从未下载,或者名称有拼写错误运行 ollama list,然后运行 ollama pull 你使用的名称
第一次调用超时模型仍在加载提高超时时间,或发送预热请求
托管 API 出现身份验证错误缺少或错误的 Bearer 令牌检查 LLM_API_KEY 和基础 URL
HTTP 429,code concurrency并发请求超过 8 个(试用密钥为 2 个)降低并发请求数
HTTP 429 错误,代码为 rate_limited超过每分钟请求数限制退避并重试

托管端失败或被拒绝的请求不会计费,因此重试逻辑不会让你为失败的请求付费。

问答

Ollama API 的默认地址是什么?

本地 Ollama 服务器在 http://localhost:11434 上监听。原生端点位于 /api 下,例如 /api/chat,OpenAI 风格的端点位于 /v1 下。

我可以将 OpenAI Python SDK 与 Ollama 配合使用吗?

是的。将 base_url 设置为 http://localhost:11434/v1 并将任意字符串作为 API 密钥传入。SDK 随后向你的本地模型发送标准的聊天补全请求。

如何从 Ollama 切换到托管 API?

更改 base URL、密钥和模型名称。如果你的代码从环境变量中读取这些值,则无需更改代码。

托管 API 是否可以在没有 GPU 的情况下运行?

可以。模型在提供商端运行,因此你的脚本只需要互联网连接和 Bearer 密钥。你根据预付额度按 token 付费。

只差一张表单,即可获得密钥

创建账户,复制密钥,更改基础 URL。这就是全部设置。

获取 API 密钥