一、为什么选择 Ollama

近年来,大语言模型逐渐从云端 API 服务走向本地化部署。对于企业内部知识库、代码助手、数据分析平台等场景来说,直接调用第三方 API 往往存在几个问题:

  1. 企业数据需要上传到外部平台,存在隐私风险;
  2. API 调用成本会随着用户量增长;
  3. 网络抖动会影响系统稳定性;
  4. 模型调用过程缺乏可控性;
  5. 某些行业数据不能离开内网。

Ollama 是一个面向本地大语言模型运行的工具,它封装了模型下载、模型管理、推理服务和 API 调用等能力。开发者不需要手动配置复杂的推理框架,就可以在本地运行 Llama、Qwen、DeepSeek、Mistral 等模型。

Ollama 的核心价值可以概括为:

  • 使用命令快速运行模型;
  • 支持 CPU 和 GPU 推理;
  • 提供标准 HTTP API;
  • 支持流式输出;
  • 支持自定义 Modelfile;
  • 可以作为本地模型服务接入 Python、Java、Node.js 等应用。

本文将以 Ollama 为基础,完成从安装模型、调用 API,到构建流式问答服务和企业知识库问答系统的完整实践。


二、Ollama 的基本工作原理

Ollama 本质上是一个本地模型运行服务。安装完成后,它会在本机启动一个服务端,默认监听:

http://localhost:11434

客户端可以通过命令行与服务端交互:

ollama run qwen2.5:7b

也可以通过 HTTP API 调用:

POST http://localhost:11434/api/generate

一次完整的模型调用大致经过以下流程:

用户输入
   |
   v
应用程序
   |
   v
Ollama HTTP 服务
   |
   v
本地模型加载
   |
   v
模型推理
   |
   v
流式或非流式响应

需要注意的是,Ollama 不是模型本身,而是模型运行和管理工具。模型文件通常会占用较大的磁盘空间,模型参数量越大,对显存和内存的要求越高。


三、安装 Ollama

3.1 Windows 安装

Windows 用户可以从 Ollama 官网下载安装包。安装完成后,在 PowerShell 中检查版本:

ollama --version

如果安装成功,会输出类似:

ollama version is 0.5.x

3.2 Linux 安装

Linux 环境可以执行:

curl -fsSL https://ollama.com/install.sh | sh

启动服务:

ollama serve

在生产环境中,可以通过 systemd 管理:

sudo systemctl enable ollama
sudo systemctl start ollama

检查运行状态:

systemctl status ollama

3.3 Docker 部署

如果希望把模型服务部署在容器中,可以使用:

docker run -d \
  --name ollama \
  -p 11434:11434 \
  -v ollama:/root/.ollama \
  ollama/ollama

如果服务器支持 NVIDIA GPU,可以使用:

docker run -d \
  --gpus all \
  --name ollama \
  -p 11434:11434 \
  -v ollama:/root/.ollama \
  ollama/ollama

容器方式的优点是环境隔离,缺点是需要额外处理 GPU 驱动、模型持久化和资源限制。


四、下载并运行本地模型

4.1 下载模型

以 Qwen2.5 为例:

ollama pull qwen2.5:7b

查看本地模型:

ollama list

输出可能类似:

NAME             ID              SIZE
qwen2.5:7b       abcdef123456    4.7 GB

运行模型:

ollama run qwen2.5:7b

进入交互式终端后,可以直接提问:

>>> 请解释一下什么是向量数据库

退出交互模式:

/bye

4.2 模型选择建议

不同模型适合的应用场景不同:

模型规模 资源要求 适用场景
1B~3B 简单问答、文本分类、边缘设备
7B~8B 通用问答、代码生成、知识库问答
14B 较高 复杂推理、专业问答
32B 以上 高质量生成、复杂企业应用

本地部署不一定追求参数量最大。对实际项目而言,延迟、稳定性和成本通常比模型规模更加重要。


五、通过 HTTP API 调用 Ollama

Ollama 提供了多个接口,其中最常用的是 /api/generate/api/chat

5.1 使用 generate 接口

请求示例:

curl http://localhost:11434/api/generate -d '{
  "model": "qwen2.5:7b",
  "prompt": "请介绍一下RAG技术",
  "stream": false
}'

返回结果大致如下:

{
  "model": "qwen2.5:7b",
  "response": "RAG,即检索增强生成,是一种将外部知识检索与大语言模型生成结合起来的技术。",
  "done": true
}

Python 调用代码:

import requests

url = "http://localhost:11434/api/generate"

payload = {
    "model": "qwen2.5:7b",
    "prompt": "什么是知识图谱?",
    "stream": False
}

response = requests.post(url, json=payload, timeout=120)
response.raise_for_status()

result = response.json()
print(result["response"])

这里使用 raise_for_status() 检查 HTTP 状态码,避免模型服务不可用时程序继续执行。

5.2 使用 chat 接口

聊天接口支持多轮消息:

import requests

messages = [
    {
        "role": "system",
        "content": "你是一名专业的Python架构师,回答要准确、简洁。"
    },
    {
        "role": "user",
        "content": "如何设计一个可扩展的日志系统?"
    }
]

payload = {
    "model": "qwen2.5:7b",
    "messages": messages,
    "stream": False
}

response = requests.post(
    "http://localhost:11434/api/chat",
    json=payload,
    timeout=120
)

response.raise_for_status()
data = response.json()
print(data["message"]["content"])

generate 相比,chat 更适合构建多轮对话系统,因为它可以明确区分系统消息、用户消息和模型消息。


六、实现流式输出

如果模型生成一段较长的文本,用户通常不希望等待全部内容生成后才看到结果。流式输出可以显著改善交互体验。

import json
import requests

def stream_chat(prompt: str):
    payload = {
        "model": "qwen2.5:7b",
        "messages": [
            {"role": "user", "content": prompt}
        ],
        "stream": True
    }

    with requests.post(
        "http://localhost:11434/api/chat",
        json=payload,
        stream=True,
        timeout=120
    ) as response:
        response.raise_for_status()

        for line in response.iter_lines():
            if not line:
                continue

            data = json.loads(line.decode("utf-8"))
            content = data.get("message", {}).get("content", "")

            print(content, end="", flush=True)

            if data.get("done"):
                break

if __name__ == "__main__":
    stream_chat("请详细介绍Python中的异步编程")

Ollama 的流式响应通常是一行一个 JSON 对象。程序需要逐行读取,而不是直接调用 response.json()

在 Web 项目中,还可以使用 FastAPI 返回流式响应:

from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import requests
import json

app = FastAPI()

def generate_answer(question: str):
    payload = {
        "model": "qwen2.5:7b",
        "messages": [
            {"role": "user", "content": question}
        ],
        "stream": True
    }

    with requests.post(
        "http://localhost:11434/api/chat",
        json=payload,
        stream=True,
        timeout=120
    ) as response:
        response.raise_for_status()

        for line in response.iter_lines():
            if line:
                item = json.loads(line)
                text = item.get("message", {}).get("content", "")
                if text:
                    yield text

@app.get("/chat")
def chat(question: str):
    return StreamingResponse(
        generate_answer(question),
        media_type="text/plain; charset=utf-8"
    )

启动服务:

uvicorn app:app --reload --port 8000

浏览器访问:

http://localhost:8000/chat?question=什么是向量数据库

七、使用 Modelfile 创建专用模型

Ollama 支持通过 Modelfile 定义一个新的模型配置。下面创建一个 Python 编程助手。

新建文件 Modelfile

FROM qwen2.5:7b

SYSTEM """
你是一名资深Python工程师。
你的回答必须包含:
1. 问题分析
2. 可运行的代码
3. 边界条件
4. 性能和安全注意事项

如果问题信息不足,必须先指出缺失信息。
"""

PARAMETER temperature 0.2
PARAMETER num_ctx 8192
PARAMETER top_p 0.9

创建模型:

ollama create python-expert -f Modelfile

运行模型:

ollama run python-expert

其中几个关键参数含义如下:

  • temperature:控制输出随机性;
  • num_ctx:上下文窗口大小;
  • top_p:控制候选词采样范围;
  • SYSTEM:设置模型的长期角色和行为约束。

对于代码生成场景,通常可以把 temperature 设置得低一些,以减少不必要的随机输出。


八、构建简单的本地 RAG 系统

单独使用本地大模型时,模型只能依靠训练数据回答问题。如果需要让模型回答企业内部文档,就需要引入 RAG。

一个简单的 RAG 系统包括:

文档加载
   |
文本切分
   |
向量化
   |
向量检索
   |
拼接上下文
   |
Ollama生成答案

这里使用 Ollama 生成回答,使用本地向量库保存文档向量。

安装依赖:

pip install requests chromadb

8.1 文本入库

import chromadb
import requests

client = chromadb.PersistentClient(path="./chroma_data")
collection = client.get_or_create_collection("company_docs")

documents = [
    "公司的报销标准:单次差旅住宿标准不得超过500元。",
    "员工请假需要提前一天在系统中提交申请。",
    "生产系统发布必须经过测试、预发布和审批流程。"
]

ids = ["doc_1", "doc_2", "doc_3"]

def get_embedding(text: str):
    response = requests.post(
        "http://localhost:11434/api/embeddings",
        json={
            "model": "nomic-embed-text",
            "prompt": text
        },
        timeout=120
    )
    response.raise_for_status()
    return response.json()["embedding"]

embeddings = [get_embedding(doc) for doc in documents]

collection.add(
    ids=ids,
    documents=documents,
    embeddings=embeddings
)

先下载嵌入模型:

ollama pull nomic-embed-text

8.2 检索并生成回答

def ask_with_rag(question: str):
    query_embedding = get_embedding(question)

    result = collection.query(
        query_embeddings=[query_embedding],
        n_results=3
    )

    contexts = result["documents"][0]
    context_text = "\n".join(contexts)

    prompt = f"""
你是企业内部知识库助手。
请严格根据下方资料回答问题。
如果资料中没有答案,请明确说明“知识库中没有找到相关信息”,不要编造。

资料:
{context_text}

问题:
{question}
"""

    response = requests.post(
        "http://localhost:11434/api/generate",
        json={
            "model": "qwen2.5:7b",
            "prompt": prompt,
            "stream": False
        },
        timeout=120
    )

    response.raise_for_status()
    return response.json()["response"]

print(ask_with_rag("差旅住宿报销标准是多少?"))

这个例子虽然简单,但已经体现出 RAG 的核心思想:先检索相关信息,再将信息交给大模型生成回答。


九、本地部署中的性能优化

9.1 控制上下文长度

上下文越长,推理消耗越大。不要把整个知识库直接拼接到 Prompt 中,而应该先检索,再截取最相关的内容。

9.2 合理选择模型

如果只是分类、摘要或简单问答,不需要使用超大模型。7B 或 8B 模型通常已经可以覆盖大量业务场景。

9.3 使用量化模型

量化可以降低显存占用。例如,4-bit 量化模型在消费级显卡上更容易运行,但模型精度可能略有下降。

9.4 避免频繁加载模型

应用启动时预热模型,可以减少第一次请求延迟:

import requests

requests.post(
    "http://localhost:11434/api/generate",
    json={
        "model": "qwen2.5:7b",
        "prompt": "你好",
        "stream": False,
        "keep_alive": "10m"
    },
    timeout=120
)

keep_alive 可以让模型在一段时间内继续驻留内存,减少重复加载。

9.5 增加请求超时和错误处理

生产代码不能假设模型服务永远可用:

def call_model(prompt: str, retries: int = 3):
    for attempt in range(retries):
        try:
            response = requests.post(
                "http://localhost:11434/api/generate",
                json={
                    "model": "qwen2.5:7b",
                    "prompt": prompt,
                    "stream": False
                },
                timeout=180
            )
            response.raise_for_status()
            return response.json()["response"]
        except requests.RequestException as exc:
            if attempt == retries - 1:
                raise RuntimeError("Ollama服务调用失败") from exc

十、生产环境需要关注的问题

本地运行成功不代表系统可以直接上线。生产环境还需要考虑:

  1. 模型服务是否支持并发;
  2. 是否有请求队列;
  3. 是否限制单次输入长度;
  4. 是否记录调用日志;
  5. 是否对敏感数据脱敏;
  6. 是否配置访问认证;
  7. 是否监控显存和内存;
  8. 是否设置模型超时;
  9. 是否区分模型服务和业务服务;
  10. 是否保留模型版本。

可以在业务服务外层增加统一接口:

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel

app = FastAPI()

class ChatRequest(BaseModel):
    question: str

@app.post("/api/assistant")
def assistant(request: ChatRequest):
    if len(request.question) > 4000:
        raise HTTPException(
            status_code=400,
            detail="问题长度不能超过4000个字符"
        )

    answer = call_model(request.question)
    return {
        "question": request.question,
        "answer": answer,
        "model": "qwen2.5:7b"
    }

需要特别注意,Ollama 默认主要面向本机服务。如果要开放给其他机器访问,应当在网络层、反向代理层和应用层增加认证与访问控制,不能直接把未保护的模型服务暴露到公网。


十一、总结

Ollama 降低了本地运行大语言模型的门槛。它不仅适合个人开发者进行模型实验,也适合企业搭建内网 AI 应用。

一个完整的本地大模型应用通常包含以下部分:

Ollama模型服务
   +
业务API服务
   +
Prompt模板
   +
向量数据库
   +
日志和监控

如果项目只需要简单问答,可以直接调用 /api/chat。如果需要企业知识库,则应当结合嵌入模型和向量数据库构建 RAG。如果需要复杂的多步骤任务,还可以进一步接入 LangChain、LangGraph 或 MCP。

Ollama 的真正价值不只是“在本地运行一个模型”,而是为开发者提供了一层稳定、简单、可编程的模型基础设施。

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐