开始之前
你需要按第 1 课的做法让 Ollama 运行起来,并且硬盘上至少有一个模型。
示例用的是 gemma4,Ollama 的 API 示例大多也用它,所以先把它拉下来,或者换成你已有的任何标签。写代码还需要 Python 3 和两个包:ollama 和 openai。
这里说的 API,是一组接收 JSON、返回 JSON 的 Web 地址。本机服务器在 11434 端口上应答。来自本机的请求不需要密钥,而且服务器只监听 127.0.0.1,所以在你修改 OLLAMA_HOST 之前,别的电脑连不上它。
Ollama 没有给这个 API 做严格的版本管理。它的介绍页面说,API 预期会保持稳定、向后兼容,官方也提供了 Python 和 JavaScript 两个库。
入口有两个,选哪个决定了你能用到什么。/api 下的原生路由开放了 Ollama 的全部能力,包括单次请求的参数、keep_alive 和思考控制。/v1 路由照搬 OpenAI 的格式,为 OpenAI 写的代码和工具不用改就能跑,只是少几个字段。
同一套 API 也在 Ollama 的云端运行。调用云端模型时,基础地址换成 https://ollama.com/api,每个请求都要带 Authorization: Bearer 请求头,里面是你在账号设置里拿到的 OLLAMA_API_KEY。
这个密钥只能放在服务器上,不能写进浏览器代码,也不能提交到代码仓库。下面的内容都用本机服务器,那里根本没有密钥可以泄露。
这套 API 是一步步长出来的,每一步都在 Ollama 的博客上公布过。早于某一步写成的教程不会提到它,所以拿教程的日期对照一下这张时间线。
ollama pull gemma4
pip install ollama openaiAnthropic API 兼容
从 v0.14.0 起,Claude Code 可以通过 Anthropic 风格的 Messages API 在 Ollama 上运行。
来源:Ollama blog: Claude Code with Anthropic API compatibility (2026年11月5日 查阅)
支持 Claude Desktop
在 Ollama 应用里选中 Claude 并打开开关,网关会自动配置好,本地模型和云端模型都能用。
来源:Ollama blog: Claude Desktop support with Ollama (2026年11月5日 查阅)
用 curl 发出第一个请求
从最简单的接口开始。/api/generate 接收一个提示词,返回一个回答。加上 "stream": false,服务器会等生成完,只发回一个 JSON 对象,第一次看会清楚很多。/api/chat 接收的则是一组消息,每条消息都有 role 和对应的 content。
两个接口都不记得之前的调用。用 /api/chat 时,每次都要发送整段对话:之前的问题、助手之前的回答,最后是新问题。漏掉哪一轮,模型就看不到哪一轮。每多一轮,提示词就更长一些,每次回复里的 prompt_eval_count 会显示它在增长。
两个接口能接收的不只是文字。options 对象放的是单次请求的设置:temperature、top_p、让输出可复现的 seed、stop 停止序列、限制回复长度的 num_predict,以及上下文长度 num_ctx。能看图的模型,可以在 images 里传入一组 base64 编码的文件。
思考型模型还多一个字段。用 think 可以要求模型给出推理过程,也可以关掉它。有些模型,比如 gpt-oss,接受的是 "low" 这样的档位,而不是 true 或 false。推理内容放在单独的 thinking 字段里,和回答分开。某个模型接受哪些值,可以问 /api/show。
只带模型名、别的什么都不带的请求也有用。它会载入模型但不生成内容,这样第一个真正的问题就不用等载入。
# is the server up, and which models does it have?
curl http://localhost:11434/api/tags
# one prompt, one answer
curl http://localhost:11434/api/generate -d '{
"model": "gemma4",
"prompt": "Why is the sky blue?",
"stream": false
}'
# a conversation: send the messages so far
curl http://localhost:11434/api/chat -d '{
"model": "gemma4",
"messages": [
{
"role": "user",
"content": "why is the sky blue?"
}
],
"stream": false
}'- 运行
/api/tags那条命令。它返回一个models列表,里面有每个模型的名字、以字节计的硬盘占用,以及parameter_size和quantization_level等详情。这一步报错,说明服务器没在运行。 - 运行
/api/generate请求。回答在response字段里,done为true。 - 运行
/api/chat请求。回答这次在message.content里,role是assistant。 - 把这条助手消息和一个追问追加到
messages里,再发一次请求。模型现在能引用它自己的第一个回答了。 - 从任一请求里删掉
"stream": false,再运行一次。回复会分成很多行到达,每行一个 JSON 对象,各带一段文字。
从 Python 调用:Ollama 的库,或者 OpenAI SDK
官方 Python 库包装的是同一批接口。chat() 接收模型名和一组消息。加上 stream=True,调用返回的是一段段数据块,而不是一个完整回答,每块的 message.content 里是回复的下一段。
下面的循环一收到就打印,所以模型还没写完,前面的字就已经出来了。
流式输出是服务器的默认行为,格式是按行分隔的 JSON:每行一个完整的 JSON 对象,一有新文字就发出来。普通的数据块带着一段回答和 done: false。最后一块是 done: true,带有 stop 这样的 done_reason,以及整次运行的统计数据,流式输出页面有示例。
速度方面的问题,大多能从这些统计里找到答案。所有时长都以纳秒计。常说的每秒 token 数,等于 eval_count 除以 eval_duration,再乘以十亿。token 是一小段文字,常常是半个词。
generate 参考文档完整列出了一次回复作为示例。它来自 gemma4,结尾是 done_reason: stop、prompt_eval_count: 11 和 eval_count: 18,也就是那个提示词用了十一个输入 token,回答用了十八个。
chat 只是众多调用之一。这个库还有 generate、list、show、create、copy、delete、pull、push、embed 和 ps,覆盖了命令行能做的事,所以脚本可以先拉取模型再使用它。
要连其他地址上的服务器,就自己建一个客户端:Client(host='http://localhost:11434') 可以传入地址和额外的请求头。
很多工具本来就用 OpenAI 的 API,而 Ollama 在 /v1 下应答同样的请求。你只改 base URL,其余代码照旧。客户端没有 API 密钥就不肯启动,Ollama 又会忽略你传的值,所以随便填一个字符串就行。
第二段代码就是兼容性页面自己的示例,只把模型换成了 gemma4。页面列出的路由有 /v1/chat/completions、/v1/completions、/v1/models、/v1/embeddings 和 /v1/responses,另外还有语音合成、转写和翻译的音频路由。
有几样功能不支持。Chat completions 不处理 logprobs、tool_choice、logit_bias、user 和 n,图片只能是 base64 数据,不能是 URL。Responses 路由是无状态的,所以 previous_response_id 和 conversation 用不了,每次都要重新发送历史。
OpenAI 的 API 也没有设置上下文长度的字段。变通办法是用 Modelfile 建一个 num_ctx 更大的新模型,任何 OpenAI 客户端都能在 model 里写上它的名字。
JavaScript 也一样。那边的 OpenAI 客户端用 baseURL: "http://localhost:11434/v1/" 和 apiKey: "ollama",调用的其余部分都不用改。
为 Claude 写的代码也有路可走。Ollama 支持 Anthropic Messages API 的一部分,所以把 Anthropic 客户端的 base URL 设成 http://localhost:11434 就能用,客户端会自己在地址后面加上 /v1/messages。
from ollama import chat
stream = chat(
model='gemma4',
messages=[{'role': 'user', 'content': 'Why is the sky blue?'}],
stream=True,
)
for chunk in stream:
print(chunk['message']['content'], end='', flush=True)
# the same model through the OpenAI SDK
from openai import OpenAI
client = OpenAI(
base_url='http://localhost:11434/v1/',
api_key='ollama', # required but ignored
)
chat_completion = client.chat.completions.create(
messages=[
{
'role': 'user',
'content': 'Say this is a test',
}
],
model='gemma4',
)
print(chat_completion.choices[0].message.content)| 字段 | 含义 |
|---|---|
prompt_eval_count | 提示词里的输入 token 数 |
prompt_eval_cached_count | 直接从缓存读取、没有重新处理的提示词 token 数 |
eval_count | 模型生成的 token 数 |
load_duration | 载入模型花的时间,冷启动时会体现在这里 |
prompt_eval_duration | 处理未缓存的提示词 token 花的时间 |
eval_duration | 生成回答花的时间 |
total_duration | 整个请求花的时间 |
把系统提示词存进 Modelfile
Modelfile 是一个简短的文本文件,用你已有的模型构建一个新模型。FROM 指定基础模型。PARAMETER 设置选项,比如以 token 计的上下文长度 num_ctx,以及控制措辞随机程度的 temperature。SYSTEM 写的是系统提示词,发给新模型的每个请求都会带上它。
ollama create 读取这个文件并注册新名字。之后,凡是能写模型名的地方,新模型都能用:ollama run、/api/chat,还有 OpenAI SDK。SYSTEM 那一行和别的提示词没什么两样,提示词工程课程里的系统提示词那一课讲了怎样写好它。
从 Hugging Face 拉取的模型,用另一种方式做同样的设置:仓库里名为 system 的文件设定系统提示词,名为 params 的 JSON 文件设定参数。
Modelfile 参考文档还列了其他指令:TEMPLATE 设置完整的提示词模板,MESSAGE 写入消息历史,LICENSE 写许可证,REQUIRES 指定所需的最低 Ollama 版本。
FROM gemma4
PARAMETER num_ctx 8192
PARAMETER temperature 0.3
SYSTEM You answer questions about the user's notes in plain English. If the notes do not say, answer that you do not know.- 把上面四行保存成一个空目录里的
Modelfile。 - 运行
ollama create notes-helper -f ./Modelfile。 - 运行
ollama run notes-helper,问一个你的笔记里不会有答案的问题。 - 用
ollama show --modelfile notes-helper打印你刚建好的模型。对gemma4运行同一条命令,能看到你的文件继承了哪些模板和参数。
Georgi Gerganov 谈本地模型为什么看起来不好用
Georgi Gerganov 在构建 ggml-org,也就是 llama.cpp 所在的 GitHub 组织,同时是 Hugging Face 的工程师。三月,他在 X 上的一条回复里回答了一个关于本地模型的问题。
他推荐 Qwen 3.5,因为它的尺寸覆盖面广。接着他提醒,人们遇到的大多数问题并不在模型本身,而在运行框架、对话模板和提示词的拼法上。
他的建议是:先用你的硬件装得下的全精度模型,并弄清楚你的运行框架到底做了什么。他写道,把 Claude Code 或 Codex 接到一个本地模型上就指望奇迹发生,是行不通的。等效果出来了,再为速度去做量化。他自己测试时,大多数模型都选了 Q8_0 版本,以保住大部分原始质量。
这套 API 把模板交到了你手里。/api/chat 会把模型自己的对话模板套在你的消息上,而 /api/generate 的 raw 会跳过模板,所以除非你自己重建模板,否则别开 raw。ollama show --modelfile 会打印模型用的模板。本地模型做不好某个任务时,先读一读这个模板和你的客户端发出的请求,再去怪模型。
我认为大家的共识是,Qwen3.5 是一次跨越,所以目前我会推荐去试试它,因为它的尺寸范围适合各种设备。
X 官方嵌入卡片的截图,拍摄于 2026 年 11 月 5 日,时间为 UTC。X 用“Show more”截断了这条帖子。点击截图可到 X 上打开原帖。
常见错误
- 把流式回复当成一个 JSON 文档来解析。没有
"stream": false时,响应体是很多个对象,每行一个,要逐行读取。 - 只把最新一条消息发给
/api/chat。服务器不保存对话,你漏掉哪一轮,模型就忘了哪一轮。 - 在网页或浏览器扩展里调用 API,结果被拦下。Ollama 默认只接受来自
127.0.0.1和0.0.0.0的浏览器请求,其他来源要加进OLLAMA_ORIGINS,比如chrome-extension://*。 - 不看
done_reason。被num_predict限住的回复可能停在半句话上,而done_reason正是说明回复为什么结束的字段。 - 批量任务跑到一半,模型被卸载了。每个请求都可以带
keep_alive:负数让模型一直驻留,0让它在回答后马上卸载。 - 把云端密钥放进桌面应用或网页里。调用
https://ollama.com/api需要OLLAMA_API_KEY,所以要从你自己的服务器发这些请求。 - 只在提示词里要求输出 JSON,然后碰运气。把
format设成"json",或者设成一个 JSON schema 对象,回复就会按这个结构返回。 - 在
/v1代码里写了一个ollama ls列表里没有的模型名。本机服务器只能运行硬盘上的模型,所以解决办法是ollama pull,而不是换一个密钥。
自己试一试
- 把同一个提示词发给
/api/generate两次,一次带"stream": false,一次不带。流式回复一共有多少行? - 用某次响应里的
eval_count和eval_duration算出每秒 token 数。 - 在 Python 里用库的
list调用列出你的模型,并打印每个名字。 - 把 OpenAI 示例里的
base_url在 Ollama 和某个云服务之间来回切换。哪几行变了? - 在 Modelfile 里加上
PARAMETER num_ctx,重新创建模型,运行时看看ollama ps的 CONTEXT 列。 - 在请求里带上
"keep_alive": 0,看模型在回答之后马上从ollama ps里消失。 - 对一个思考型模型,把
think分别打开和关掉,问同一个问题,对比thinking字段和回答。
接下来:LM Studio 在一个桌面应用里运行同样的本地服务器,自带模型浏览器,也有自己的 OpenAI 兼容接口。第 3 课会把它装好,并拿它的 lms 命令行和你现在熟悉的 Ollama 命令做对比。
自测一下
非流式的 /api/chat 响应里,回答在哪里?
查看答案
B. message.content chat 接口返回的是一条助手消息,所以文字在 message.content 里,/api/generate 用的才是 response。
第二次调用 /api/chat 时,模型不记得第一个问题了,缺了什么?
查看答案
C. messages 列表里之前的回合 服务器不保存对话,模型要看到的历史,每次调用都得带上。
用 OpenAI SDK 连 Ollama 时,API 密钥填什么?
查看答案
A. 随便一个字符串,Ollama 会忽略它 客户端要求必须有一个值,而本机服务器会忽略它。
怎样让 OpenAI 风格的客户端用上更长的上下文窗口?
查看答案
D. 用带 PARAMETER num_ctx 的 Modelfile 建一个模型 OpenAI 的 API 没有上下文长度字段,所以这个设置要放进一个客户端能指定名字的新模型里。
常见问题
怎样在 PowerShell 里调用 Ollama API?
用 Invoke-WebRequest,Windows 页面的写法是:(Invoke-WebRequest -method POST -Body '{"model":"llama3.2", "prompt":"Why is the sky blue?", "stream": false}' -uri http://localhost:11434/api/generate ).Content | ConvertFrom-json。把 llama3.2 换成你拉取过的模型。请求体里要保留 "stream": false,因为 ConvertFrom-json 要的是一个 JSON 对象,而不是每行一个。
局域网里的其他电脑能用我的 Ollama API 吗?
能。用 OLLAMA_HOST=0.0.0.0:11434 让它绑定所有网卡,这个变量要设在应用或服务读取环境的地方,然后重启 Ollama。之后谁能连上这个端口,谁就能发请求,所以要加一条只放行你自己机器的防火墙规则。
Ollama API 支持工具调用吗?
支持,通过 /api/chat。在 tools 里传入函数定义,回复里出现 tool_calls 时,运行对应的函数,再用 role 为 tool 的消息把结果发回去。chat 参考文档用 qwen3 和一个 get_current_weather 函数演示了这个过程。模型只是提出调用请求,要不要真的运行,由你的代码决定。
Ollama API 能生成嵌入向量吗?
能。先拉取 embeddinggemma 这样的嵌入模型,再把文字发给 /v1/embeddings,或者调用 Python 库里的 embed。每段文字都会变成一串数字,这正是搜索索引存储的形式,RAG 课程就建立在它之上。
资料来源
- Ollama Docs: API introduction https://docs.ollama.com/api/introduction
- Ollama Docs: Generate a response https://docs.ollama.com/api/generate
- Ollama Docs: Generate a chat message https://docs.ollama.com/api/chat
- Ollama Docs: List models https://docs.ollama.com/api/tags
- Ollama Docs: Streaming https://docs.ollama.com/api/streaming
- Ollama Docs: OpenAI compatibility https://docs.ollama.com/api/openai-compatibility
- Ollama Docs: Anthropic compatibility https://docs.ollama.com/api/anthropic-compatibility
- Ollama Docs: Modelfile reference https://docs.ollama.com/modelfile
- Ollama Docs: FAQ https://docs.ollama.com/faq
- Ollama Docs: Windows https://docs.ollama.com/windows
- GitHub: ollama/ollama-python https://github.com/ollama/ollama-python
- Ollama blog: OpenAI compatibility https://ollama.com/blog/openai-compatibility
- Ollama blog: Tool support https://ollama.com/blog/tool-support
- Ollama blog: Structured outputs https://ollama.com/blog/structured-outputs
- Ollama blog: Streaming responses with tool calling https://ollama.com/blog/streaming-tool
- Ollama blog: Claude Code with Anthropic API compatibility https://ollama.com/blog/claude
- Ollama blog: Claude Desktop support with Ollama https://ollama.com/blog/claude-desktop
- Georgi Gerganov on X: the main issues with local models https://x.com/ggerganov/status/2038674698809102599

