第02章 Ollama HTTP API 与流式响应作者亢AIRTC| 源码地址https://github.com/kang-airtc/ollama-mini-book在前一章中笔者完成了 Ollama 的安装与 CLI 验证。CLI 适合人机对话调试对应用集成来说仍然不够直接业务代码无法读取 CLI 进程的输出也无法控制并发与超时。Ollama 真正向应用开放的能力是常驻在 11434 端口的 HTTP 接口。本章把这条 HTTP 通道彻底走通让读者在不依赖任何 SDK 的前提下用 Python 的 requests 与 httpx 实现生成、对话与流式响应解码。完成本章后读者将能写出可被后端服务直接复用的 Ollama 调用函数理解 NDJSON 流式协议的特征并能在网络异常、模型未加载等边界场景下做出合理处理。2.1 接口家族与调用风格Ollama 暴露的 REST 接口都以 /api 为前缀覆盖生成、对话、嵌入、模型管理等场景。本书重点使用其中三个/api/generate 用于一次性生成、/api/chat 用于多轮对话、/api/embeddings 用于向量化。所有接口的认证方式相同本地服务默认不需要 Token远程访问则建议放在反向代理后做最简单的网络层鉴权。2.1.1 接口分类与场景对应笔者将本书会用到的 Ollama HTTP 接口与典型场景的对应关系总结于一表方便读者后续查阅。常用接口“如表2-1”所示。表 2-1 Ollama 常用 HTTP 接口路径方法用途流式支持/api/generatePOST单轮文本生成是/api/chatPOST多轮对话生成是/api/embeddingsPOST文本向量化否/api/tagsGET列出本地模型否/api/showPOST查看模型元数据否本章重点演示前两个第三个嵌入接口将在向量化章节展开。后两个查询类接口主要用于运维或调试场景本书不做专题展开读者可在 Ollama 官方文档中按需查阅。2.1.2 请求负载的通用字段Ollama 的生成与对话接口共享一套基础字段理解它们能让读者快速读懂示例代码。最常用的字段及作用“如表2-2”所示。表 2-2 生成与对话接口的常用字段字段类型作用modelstring指定使用的模型名需与本地已 pull 的名称一致promptstring单轮生成接口的输入文本messagesarray对话接口的消息列表元素为带 role 与 content 的对象systemstring系统提示约束模型角色或风格streambool是否以流式方式返回默认为 trueoptionsobject推理参数常见为 temperature、num_predictstream 字段在本章占据核心位置当其为 true 时Ollama 用 NDJSON 协议把生成片段逐条推送回客户端为 false 时则等待整段生成完成再一次性返回。后续章节中 SSE 流式后端正是建立在 streamtrue 之上的。注意messages 中的 role 字段只接受 system、user、assistant 三种值写错会导致 400 错误错误信息常被流式输出掩盖调试时建议先关闭流式确认请求格式。2.2 用 requests 调用对话接口requests 是 Python 同步 HTTP 库的事实标准本节先用它完成一次最小可行的 Ollama 调用。配套源码中的 test_ollama.py 就是这条路径的实现本节顺着它展开讲解。2.2.1 构造一次对话请求最小的对话请求包含模型名与消息列表两个必填项调用方式如下。importrequests responserequests.post(http://localhost:11434/api/chat,json{model:llama3.2:latest,messages:[{role:user,content:请介绍一下Ollama并用中文回答.}],},streamTrue,)代码中 streamTrue 起到双重作用HTTP 客户端层面允许逐块读取响应体Ollama 服务端层面则按 NDJSON 协议分片推送。两者必须配合否则要么解析失败要么白白等待完整响应。读者可以暂时把 prompt 中的请求语句替换成任意问题验证模型对中文与英文输入的响应差异从而对模型能力建立直观印象。2.2.2 解析 NDJSON 流式响应流式响应中每一行都是一个独立的 JSON 对象行末以换行符分隔整体不是合法 JSON 数组而是 Newline Delimited JSON简称 NDJSON。读取方式是按行迭代每行单独 json.loads。forlineinresponse.iter_lines():ifline:dataline.decode(utf-8)print(data)直接 print 出来的每一行结构与字段含义“如图2-1”所示。每一行包含若干字段其中 message.content 是本片增量文本done 表示本次生成是否结束。把所有非空 content 拼接起来就是模型完整回答。读者可在 test_ollama.py 基础上加一行 json.loads 与拼接逻辑得到等价于 CLI 输出的最终文本。注意流式响应的最后一行 done 为 true 且 content 通常为空循环中不要因为遇到空 content 而提前 break必须等到 done 为真才结束。2.2.3 健壮性与错误处理最小示例没有处理网络异常与服务错误正式代码需要补充三类防御连接异常、HTTP 状态码非 200、响应行解析失败。常见错误类型与建议处理“如表2-3”所示。表 2-3 Ollama HTTP 调用的常见错误错误类型触发原因建议处理ConnectionErrorOllama 服务未启动或端口不通提示用户启动 Ollama并在日志中给出 11434 端口检查命令404 model not found请求的模型未 pull 到本地在响应中给出可执行的 pull 命令Timeout模型加载或生成超时适当延长 timeout必要时改为异步调用JSONDecodeError响应行残缺或包含空字节跳过该行并记录日志不影响整体生成笔者建议把上述处理统一封装到一个 chat 函数里对外只暴露 prompt 与 model 两个参数函数内部完成 URL 拼接、错误分类、流式拼接。后续章节中 backend/main.py 的 stream_ollama_llm 函数正是这条思路的进一步演化。2.3 用 httpx 做异步流式调用requests 是同步库适合脚本与测试进入 FastAPI 后端服务时需要换用支持 async 的客户端避免阻塞事件循环。httpx 提供与 requests 相近的 API同时支持异步上下文与流式读取是 Ollama 在后端最常用的搭档。2.3.1 异步客户端与上下文管理httpx 推荐用 AsyncClient 作为异步上下文管理器进入时建立连接池退出时自动释放。下面示例改写自 backend/main.py 中的 stream_ollama_llm 函数。importhttpxasyncdefstream_chat(prompt:str,model:strllama3.2:latest):payload{model:model,messages:[{role:user,content:prompt}],stream:True,options:{temperature:0.7,num_predict:2000},}asyncwithhttpx.AsyncClient(timeout120.0)asclient:asyncwithclient.stream(POST,http://localhost:11434/api/chat,jsonpayload,)asresponse:response.raise_for_status()asyncforlineinresponse.aiter_lines():ifline.strip():yieldline整段代码以异步生成器形式对外提供每个 yield 出去的就是一段流式响应行。调用方可以再做一次 json 解析与片段拼接得到可推给前端的文本块。2.3.2 超时与取消的实践本地大模型生成耗时不稳定简单设置 timeout 不足以覆盖所有情况需要在三个层面综合处理。常见做法“如表2-4”所示。表 2-4 异步流式调用的稳定性手段手段作用建议值或写法连接超时防止 Ollama 未启动时长时间阻塞httpx.Timeout(connect5.0)读超时控制单次响应等待上限单独配置 read 参数典型 60 至 120 秒任务取消客户端断开时尽快中止后端生成用 anyio 或 asyncio 监听断连事件后续章节里 FastAPI 接收前端断开请求后会触发协程取消httpx 的 stream 上下文会一并退出最终关闭与 Ollama 的连接。这条链路读者目前只需要知道存在具体实现细节将在 RAG 后端章节再展开。注意把 timeout 设得过短会导致大模型推理被中途打断过长则前端长时间等不到任何反馈建议配合心跳事件或加载提示给出体验上的兜底。2.4 本章小结本章把 Ollama 的 HTTP 接口拆解清楚从最小 requests 示例理解 NDJSON 流式协议再升级到 httpx 异步流式调用以适配后端服务。读者现在已经具备直接驱动本地大模型的能力可以脱离 CLI把它当作一个普通的 HTTP 服务来用。下一章笔者将转向数据侧要让本地 LLM 能基于私有知识回答问题先要把原始文本切成大小适中的块。文本切片是检索增强生成链路上看似不起眼但至关重要的一环做得好与不好直接决定后续向量检索的效果。作者光谷老亢配套源码https://github.com/kang-airtc/ollama-mini-book
第02章 Ollama HTTP API 与流式响应
第02章 Ollama HTTP API 与流式响应作者亢AIRTC| 源码地址https://github.com/kang-airtc/ollama-mini-book在前一章中笔者完成了 Ollama 的安装与 CLI 验证。CLI 适合人机对话调试对应用集成来说仍然不够直接业务代码无法读取 CLI 进程的输出也无法控制并发与超时。Ollama 真正向应用开放的能力是常驻在 11434 端口的 HTTP 接口。本章把这条 HTTP 通道彻底走通让读者在不依赖任何 SDK 的前提下用 Python 的 requests 与 httpx 实现生成、对话与流式响应解码。完成本章后读者将能写出可被后端服务直接复用的 Ollama 调用函数理解 NDJSON 流式协议的特征并能在网络异常、模型未加载等边界场景下做出合理处理。2.1 接口家族与调用风格Ollama 暴露的 REST 接口都以 /api 为前缀覆盖生成、对话、嵌入、模型管理等场景。本书重点使用其中三个/api/generate 用于一次性生成、/api/chat 用于多轮对话、/api/embeddings 用于向量化。所有接口的认证方式相同本地服务默认不需要 Token远程访问则建议放在反向代理后做最简单的网络层鉴权。2.1.1 接口分类与场景对应笔者将本书会用到的 Ollama HTTP 接口与典型场景的对应关系总结于一表方便读者后续查阅。常用接口“如表2-1”所示。表 2-1 Ollama 常用 HTTP 接口路径方法用途流式支持/api/generatePOST单轮文本生成是/api/chatPOST多轮对话生成是/api/embeddingsPOST文本向量化否/api/tagsGET列出本地模型否/api/showPOST查看模型元数据否本章重点演示前两个第三个嵌入接口将在向量化章节展开。后两个查询类接口主要用于运维或调试场景本书不做专题展开读者可在 Ollama 官方文档中按需查阅。2.1.2 请求负载的通用字段Ollama 的生成与对话接口共享一套基础字段理解它们能让读者快速读懂示例代码。最常用的字段及作用“如表2-2”所示。表 2-2 生成与对话接口的常用字段字段类型作用modelstring指定使用的模型名需与本地已 pull 的名称一致promptstring单轮生成接口的输入文本messagesarray对话接口的消息列表元素为带 role 与 content 的对象systemstring系统提示约束模型角色或风格streambool是否以流式方式返回默认为 trueoptionsobject推理参数常见为 temperature、num_predictstream 字段在本章占据核心位置当其为 true 时Ollama 用 NDJSON 协议把生成片段逐条推送回客户端为 false 时则等待整段生成完成再一次性返回。后续章节中 SSE 流式后端正是建立在 streamtrue 之上的。注意messages 中的 role 字段只接受 system、user、assistant 三种值写错会导致 400 错误错误信息常被流式输出掩盖调试时建议先关闭流式确认请求格式。2.2 用 requests 调用对话接口requests 是 Python 同步 HTTP 库的事实标准本节先用它完成一次最小可行的 Ollama 调用。配套源码中的 test_ollama.py 就是这条路径的实现本节顺着它展开讲解。2.2.1 构造一次对话请求最小的对话请求包含模型名与消息列表两个必填项调用方式如下。importrequests responserequests.post(http://localhost:11434/api/chat,json{model:llama3.2:latest,messages:[{role:user,content:请介绍一下Ollama并用中文回答.}],},streamTrue,)代码中 streamTrue 起到双重作用HTTP 客户端层面允许逐块读取响应体Ollama 服务端层面则按 NDJSON 协议分片推送。两者必须配合否则要么解析失败要么白白等待完整响应。读者可以暂时把 prompt 中的请求语句替换成任意问题验证模型对中文与英文输入的响应差异从而对模型能力建立直观印象。2.2.2 解析 NDJSON 流式响应流式响应中每一行都是一个独立的 JSON 对象行末以换行符分隔整体不是合法 JSON 数组而是 Newline Delimited JSON简称 NDJSON。读取方式是按行迭代每行单独 json.loads。forlineinresponse.iter_lines():ifline:dataline.decode(utf-8)print(data)直接 print 出来的每一行结构与字段含义“如图2-1”所示。每一行包含若干字段其中 message.content 是本片增量文本done 表示本次生成是否结束。把所有非空 content 拼接起来就是模型完整回答。读者可在 test_ollama.py 基础上加一行 json.loads 与拼接逻辑得到等价于 CLI 输出的最终文本。注意流式响应的最后一行 done 为 true 且 content 通常为空循环中不要因为遇到空 content 而提前 break必须等到 done 为真才结束。2.2.3 健壮性与错误处理最小示例没有处理网络异常与服务错误正式代码需要补充三类防御连接异常、HTTP 状态码非 200、响应行解析失败。常见错误类型与建议处理“如表2-3”所示。表 2-3 Ollama HTTP 调用的常见错误错误类型触发原因建议处理ConnectionErrorOllama 服务未启动或端口不通提示用户启动 Ollama并在日志中给出 11434 端口检查命令404 model not found请求的模型未 pull 到本地在响应中给出可执行的 pull 命令Timeout模型加载或生成超时适当延长 timeout必要时改为异步调用JSONDecodeError响应行残缺或包含空字节跳过该行并记录日志不影响整体生成笔者建议把上述处理统一封装到一个 chat 函数里对外只暴露 prompt 与 model 两个参数函数内部完成 URL 拼接、错误分类、流式拼接。后续章节中 backend/main.py 的 stream_ollama_llm 函数正是这条思路的进一步演化。2.3 用 httpx 做异步流式调用requests 是同步库适合脚本与测试进入 FastAPI 后端服务时需要换用支持 async 的客户端避免阻塞事件循环。httpx 提供与 requests 相近的 API同时支持异步上下文与流式读取是 Ollama 在后端最常用的搭档。2.3.1 异步客户端与上下文管理httpx 推荐用 AsyncClient 作为异步上下文管理器进入时建立连接池退出时自动释放。下面示例改写自 backend/main.py 中的 stream_ollama_llm 函数。importhttpxasyncdefstream_chat(prompt:str,model:strllama3.2:latest):payload{model:model,messages:[{role:user,content:prompt}],stream:True,options:{temperature:0.7,num_predict:2000},}asyncwithhttpx.AsyncClient(timeout120.0)asclient:asyncwithclient.stream(POST,http://localhost:11434/api/chat,jsonpayload,)asresponse:response.raise_for_status()asyncforlineinresponse.aiter_lines():ifline.strip():yieldline整段代码以异步生成器形式对外提供每个 yield 出去的就是一段流式响应行。调用方可以再做一次 json 解析与片段拼接得到可推给前端的文本块。2.3.2 超时与取消的实践本地大模型生成耗时不稳定简单设置 timeout 不足以覆盖所有情况需要在三个层面综合处理。常见做法“如表2-4”所示。表 2-4 异步流式调用的稳定性手段手段作用建议值或写法连接超时防止 Ollama 未启动时长时间阻塞httpx.Timeout(connect5.0)读超时控制单次响应等待上限单独配置 read 参数典型 60 至 120 秒任务取消客户端断开时尽快中止后端生成用 anyio 或 asyncio 监听断连事件后续章节里 FastAPI 接收前端断开请求后会触发协程取消httpx 的 stream 上下文会一并退出最终关闭与 Ollama 的连接。这条链路读者目前只需要知道存在具体实现细节将在 RAG 后端章节再展开。注意把 timeout 设得过短会导致大模型推理被中途打断过长则前端长时间等不到任何反馈建议配合心跳事件或加载提示给出体验上的兜底。2.4 本章小结本章把 Ollama 的 HTTP 接口拆解清楚从最小 requests 示例理解 NDJSON 流式协议再升级到 httpx 异步流式调用以适配后端服务。读者现在已经具备直接驱动本地大模型的能力可以脱离 CLI把它当作一个普通的 HTTP 服务来用。下一章笔者将转向数据侧要让本地 LLM 能基于私有知识回答问题先要把原始文本切成大小适中的块。文本切片是检索增强生成链路上看似不起眼但至关重要的一环做得好与不好直接决定后续向量检索的效果。作者光谷老亢配套源码https://github.com/kang-airtc/ollama-mini-book