Ollama

Ollama 本地部署 Deepseek R1 模型

Ollama项目介绍

Ollama是在Github上的一个开源项目,其项目定位是:一个本地运行 大模型 的集成框架 ,目前主要针对主流的LLaMA架构的开源大模型设计,通过将模型权重、配置文件和必要数据封装进由Modelfile定义的包中,从而实现大模型的下载、启动和本地运行的自动化部署及推理流程。此外,Ollama内置了一系列针对大模型运行和推理的优化策略,目前作为一个非常热门的大模型托管平台,基本主流的大模型应用开发框架如LangChainAutoGenMicrosoft GraphRAG及热门项目AnythingLLMOpenWebUI等高度集成。

Ollama通过将大模型运行的所有必要组件(如权重文件、配置设置和相关数据)封装在一个单一的文件或包中,`Modelfile 允许用户更容易地下载、安装、配置和启动模型。这种方法类似于其他软件或应用程序的安装包,它们将所有必要的文件打包在一起,以便用户可以通过简单的安装过程将软件添加到他们的系统中。

Ollama官方地址:https://ollama.com/

Ollama Github开源地址:https://github.com/ollama/ollama

Ollama项目本地安装

Ollama项目本地安装的方法极为简单,这里我们以Linux系统为例,先进入命令行终端,执行如下一条命令行即可自动化完成:

复制代码
curl -fsSL https://ollama.com/install.sh | sh

Ollama下载 DeepSeek R1 及启动

需要说明的一点是:Ollama项目虽然提供了本地化大模型的能力,但这并不意味着所有大模型都可以通过它下载和使用,其支持的大模型的详细列表可在Ollama的官方模型库页面查看:https://ollama.com/library

复制代码
ollama run deepseek-r1:32b

Ollama 多GPU部署及serve启动

如果想加载多张显卡且做到负载均衡,可以去修改 ollamaSystemD配置服务,首先找到当前服务器上GPUID,执行命令如下:

复制代码
nvidia-smi

如果想加载多张显卡且做到负载均衡,可以去修改 ollamaSystemD配置服务,执行如下代码:

复制代码
systemctl edit ollama.service

编辑并填写如下内容:
    Environment="CUDA_VISIBLE_DEVICES=0,1,2,3"    # 这里根据你自己实际的 GPU标号来进行修改   
    Environment="OLLAMA_SCHED_SPREAD=1"           # 这个参数是做负载均衡

保存退出后,重新加载systemd并重新启动Ollama服务使其配置生效,执行如下命令:

复制代码
systemctl daemon-reload
    systemctl restart ollama

Ollama REST API 服务启动及调用

Ollama run xxx命令启动模型后,不仅仅是可以在命令行终端与启动的大模型进行对话,更重要的是它还会同步启动Ollama REST API,这个REST API服务简单理解:我们可以通过某种方式在代码环境中调用到使用Ollama模型启动的大模型,从而和大模型进行对话。默认绑定的 IP + Port 是:http://localhost:11434,所以,如果启动Ollama的服务和当前的代码环境是同一台机器的话,可以使用如下代码进行快速的调用测试:

复制代码
from openai import OpenAI

client = OpenAI(
    base_url='http://localhost:11434/v1/',      
    api_key='ollama',  # 这里随便写,但是api_key字段一定要有
)

chat_completion = client.chat.completions.create(
    model='deepseek-r1:32b',       # 这里要修改成 你 ollama 启动模型的名称
    messages=[
        {
            'role': 'user',
            'content': '你好,请你介绍一下你自己',
        }
    ],
)

print(chat_completion)

Ollama每个命令参数非常容易理解,大家可以自行进行尝试,其参数说明如下所示:

|--------|--------------------|
| 命令 | 描述 |
| serve | 启动 Ollama 服务 |
| create | 从 Modelfile 创建一个模型 |
| show | 显示模型的信息 |
| run | 运行一个模型 |
| stop | 停止正在运行的模型 |
| pull | 从注册表中拉取一个模型 |
| push | 将一个模型推送到注册表 |
| list | 列出所有模型 |
| ps | 列出正在运行的模型 |
| cp | 复制一个模型 |
| rm | 删除一个模型 |
| help | 显示关于任何命令的帮助信息 |

Ollama REST API

Ollama 服务启动后会提供一系列原生 REST API 端点。通过这些Endpoints可以在代码环境下与ollama启动的大模型进行交互、管理模型和获取相关信息。其中两个endpoint 是最重要的,分别是:

  • POST /api/generate
  • POST /api/chat

其他端点情况:

  • POST /api/create
  • POST /api/tags
  • POST /api/show
  • POST /api/copy
  • DELETE /api/delete
  • POST /api/pull
  • POST /api/push
  • POST /api/embed
  • GET /api/ps

/api/generate 接口参数概览

常规参数

|------------|--------|--------------------------------------------|
| 参数名 | 类型 | 描述 |
| model | (必需) | 模型名称,必须遵循 model:tag 格式,如果不提供,则将默认为 latest。 |
| prompt | (必需) | 用于生成响应的提示。 |
| suffix | (可选) | 模型响应后的文本。 |
| images | (可选) | base64 编码图像的列表(适用于多模态模型,如 llava)。 |

高级参数 (可选)

|----------------|--------|----------------------------------------------|
| 参数名 | 类型 | 描述 |
| format | (可选) | 返回响应的格式。格式可以是 json 或 JSON 模式。最主要的问题是避免产生大量空格 |
| options | (可选) | 文档中列出的其他模型参数,例如 temperature。 |
| system | (可选) | 系统消息,用于覆盖 Modelfile 中定义的内容。 |
| template | (可选) | 要使用的提示模板,覆盖 Modelfile 中定义的内容。 |
| stream | (可选) | 如果为 false,响应将作为单个响应对象返回,而不是对象流。 |
| raw | (可选) | 如果为 true,则不会对提示应用格式。 |
| keep_alive | (可选) | 控制模型在请求后保持加载的时间(默认:5分钟)。 |
| context | (可选) | (已弃用) 从先前请求返回的上下文参数,用于保持简短的对话记忆。 |

复制代码
import requests # type: ignore
import json

# 设置 API 端点
generate_url = "http://192.168.110.131:11434/api/generate"    # 这里需要根据实际情况进行修改

# 示例数据
generate_payload = {
    "model": "deepseek-r1:7b",   # 这里需要根据实际情况进行修改
    "prompt": "请生成一个关于人工智能的简短介绍。",  # 这里需要根据实际情况进行修改
    "stream": False,       # 默认使用的是True,如果设置为False,则返回的是一个完整的响应,而不是一个流式响应
}

# 调用生成接口
response_generate = requests.post(generate_url, json=generate_payload)
if response_generate.status_code == 200:
    generate_response = response_generate.json()
    print("生成响应:", json.dumps(generate_response, ensure_ascii=False, indent=2))
else:
    print("生成请求失败:", response_generate.status_code, response_generate.text)

api/chat 接口详解

该接口使用提供的模型在聊天中生成下一条消息。与 /api/generate 的参数基本一致,但是在请求的参数上会根据聊天场景进行调整。主要调整的是:

  • 不再使用 prompt 参数,而是使用 messages 参数。
  • 新增了 tools 参数,用于支持工具调用。

常规参数

|--------------|--------|--------------------------|
| 参数名 | 类型 | 描述 |
| model | (必需) | 模型名称。 |
| messages | (必需) | 聊天的消息,用于保持聊天记忆。 |
| tools | (可选) | JSON 中的工具列表,供模型使用(如果支持)。 |

消息对象字段

|----------------|-----------------------------------------|
| 字段名 | 描述 |
| role | 消息的角色,可以是 system、user、assistant 或 tool。 |
| content | 消息的内容。 |
| images | (可选) 要在消息中包含的图像列表(适用于多模态模型,如 llava)。 |
| tool_calls | (可选) 模型希望使用的 JSON 中的工具列表。 |

高级参数 (可选)

|----------------|---------------------------------|
| 参数名 | 描述 |
| format | 返回响应的格式。格式可以是 json 或 JSON 模式。 |
| options | 文档中列出的其他模型参数,例如 temperature。 |
| stream | 如果为 false,响应将作为单个响应对象返回,而不是对象流。 |
| keep_alive | 控制模型在请求后保持加载的时间(默认:5分钟)。 |

复制代码
# 提取 <think> 标签中的内容
think_start = generate_response["response"].find("<think>")
think_end = generate_response["response"].find("</think>")

if think_start != -1 and think_end != -1:
    think_content = generate_response["response"][think_start + len("<think>"):think_end].strip()
else:
    think_content = "No think content found."

# 提取正常的文本内容
normal_content = generate_response["response"][think_end + len("</think>"):].strip()

# 打印结果
print("思考内容:\n", think_content)
print("\n正常内容:\n", normal_content)

OpenAI Compatibility

本节内容我们来看一下 OpenAI CompatibilityOpenAIAPI 接口是大模型应用开发中最常用、且集成度最高的 API 接口规范,其兼容接口主要包括:

  • chat/completions
  • completions
  • models
  • embeddings

我们上两节课程内容中介绍的/api/generate/api/chat 接口,其实就是 Ollama 兼容 OpenAIREST API 接口的底层实现。其中:

  • /api/generate 接口对应 OpenAIcompletions 接口;
  • /api/chat 接口对应 OpenAIchat/completions 接口;

因此我们现在再来看ollama 中的OpenAI compatibilityAPI 接口调用,就非常容易理解了。

复制代码
import requests
import json

# 设置 API 端点
chat_url = "http://192.168.110.131:11434/api/chat"    # 这里需要根据实际情况进行修改

# 示例数据
chat_payload = {
    "model": "deepseek-r1:32b",   # 这里需要根据实际情况进行修改
    "messages": [
        {
            "role": "user",  # 消息角色,用户发送的消息
            "content": "请生成一个关于人工智能的简短介绍。"  # 用户的消息内容
        }
    ],
    "tools": [],  # 如果有工具可以在这里添加
    "stream": False,  # 默认使用的是True,如果设置为False,则返回的是一个完整的响应,而不是一个流式响应
}

# 调用聊天接口
response_chat = requests.post(chat_url, json=chat_payload)
if response_chat.status_code == 200:
    chat_response = response_chat.json()
    print("生成响应:", json.dumps(chat_response, ensure_ascii=False, indent=2))
else:
    print("生成请求失败:", response_chat.status_code, response_chat.text)

OpenAI Compatibility 规范下,目前Ollama 支持的模型参数如下:

支持的功能

|--------|----------------------|
| 功能 | 描述 |
| 聊天完成 | Chat completions |
| 流媒体 | Streaming |
| JSON模式 | JSON mode |
| 可再现的输出 | Reproducible outputs |
| 视觉 | Vision |
| 工具 | Tools |

支持的请求字段

|-------------------|----------|
| 请求字段 | 描述 |
| model | 模型 |
| messages | 消息 |
| frequency_penalty | 频率惩罚 |
| presence_penalty | 存在惩罚 |
| response_format | 响应格式 |
| seed | 种子 |
| stop | 停止 |
| stream | 流式输出 |
| stream_options | 流式选项 |
| include_usage | 包含使用情况 |
| temperature | 温度 |
| top_p | Top-p 采样 |
| max_tokens | 最大令牌数 |
| tools | 工具 |

Ollama 服务接口压力测试

对于企业级应用来说,尤其是后台服务,考虑的因素会非常多。比如大模型问答的响应速度,系统服务的稳定性,业务请求的错误率,资源的利用率等等多个方面。不同应用场景,考虑的因素也会有所不同。像我们正在做的智能客服问答功能,更关注响应速度和稳定性,这就导致高吞吐量和高并发能力比较重要,直接影响服务承载能力和效率,往往是优化的重点。吞吐量通常指系统在单位时间内处理的请求数量,而并发量则是系统同时处理的请求数。

因为企业需要处理大量用户或设备的请求,尤其是在高峰时段。如果服务吞吐量低,可能导致延迟增加,用户体验下降,甚至服务崩溃。如果并发量不足,用户可能会遇到等待或超时;吞吐量低的话,处理速度慢,整体效率低下。

我们基于Ollama 模型服务启动的 REST API接口,每秒生成的 Token 数量可以被视为系统的吞吐量,因此我们需要一些方法,来根据实际的业务需求来评估当前的硬件资源是否满足需求,或者应该如何去采购硬件资源。

我们测试 Ollama 模型服务的吞吐量和并发量,需要核心关注的是以下几点:

  1. 使用REST API接口进行测试,可以尝试使用/api/generate 或者/api/chat,真实模拟用户在实际使用中的请求模式,帮助评估系统在真实场景下的表现。
  2. Ollama 原生的REST API接口支持多个控制Ollama行为的参数,可以更灵活的控制测试流程,其中:
    1. num_predict 参数来控制生成的token数量
    2. keep_alive 设置为0,使用完模型后立即卸载
    3. temperature 参数来控制生成文本的多样性,很多情况下,希望生成的文本尽可能保持一致,会将其设置为0,
  1. 根据OllamaREST API接口返回响应体中的eval_counteval_duration 来计算每秒生成的Token 数量,即吞吐量,而不是用 resquest 发起和接收到响应的时间差值来计算,将模型服务和网络延迟解耦,更准确的评估模型服务的吞吐量。

      单次调用的伪代码如下:

    python 复制代码
                # 调用 ollama 的 generate 接口
                async with session.post(
                    f"{self.url}/api/generate",
                    json={
                        "model": self.model,
                        "prompt": prompt,  # 使用随机选择的问题
                        "stream": False,
                        # "keep_alive":0,   # 使用完模型后立即卸载
                        "options": {
                            "temperature": 0.7, 
                            "num_predict": 300,   # 限制生成token数量,以尽可能保证单个请求的生成时间一致
                        }
                    }
                ) as response:
                    result = await response.json()
                    # 从响应中获取性能指标
                    eval_count = result.get("eval_count", 0)  # 生成的token数
                    eval_duration = result.get("eval_duration", 0)  # 生成时间(纳秒)
                    total_duration = result.get("total_duration", 0)  # 总时间(纳秒)
                    
                    # 计算 tokens/second
                    tokens_per_second = (eval_count / eval_duration * 1e9) if eval_duration > 0 else 0

本地服务器上测试 DeepSeek-R1:1.5B 模型分别在 双卡负载和四卡负载下的吞吐量和并发量,测试结果如下:

并发测试结果

|-------------|---------|---------|-------------|----------------|---------------|----------------|---------------|----------------------|
| 测试类型 | 并发数 | 成功率 | 总token数 | 平均生成时间 (秒) | 平均总时间 (秒) | 平均每秒token数 | 实际总耗时 (秒) | 系统吞吐量 (tokens/s) |
| 两张卡 | 2 | 100% | 2742 | 3 | 4.03 | 91.96 | 23.84 | 115.01 |
| 两张卡 | 3 | 100% | 2686 | 3.61 | 3.71 | 73.56 | 14.78 | 181.75 |
| 两张卡 | 4 | 100% | 2665 | 4.48 | 4.59 | 59.75 | 14.16 | 188.19 |
| 两张卡 | 5 | 100% | 2556 | 4.8 | 4.91 | 53.34 | 12.39 | 206.27 |
| 单请求性能测试 | - | - | 300 | 2.44 | 2.52 | 123 | - | - |
| 四张卡 | 2 | 100% | 2526 | 2.64 | 4.16 | 96.77 | 25.38 | 99.51 |
| 四张卡 | 3 | 100% | 2456 | 3.34 | 3.44 | 72.44 | 14.51 | 169.28 |
| 四张卡 | 4 | 100% | 2781 | 4.54 | 4.65 | 62.27 | 14.59 | 190.57 |
| 四张卡 | 5 | 100% | 3000 | 6.53 | 6.65 | 45.93 | 14.42 | 208.07 |
| 单请求性能测试 | - | - | 300 | 2.52 | 2.59 | 119.31 | - | - |

从测试结果能够得出的一些关键结论是:

  1. 并发会导致单个请求的处理时间变长;
  2. 并发因为是并行处理,虽然单个请求时间变长,但是系统整体吞吐量会得到提升;
  3. 不一定用更多的卡就可以获得更高的吞吐量,需要根据实际情况去调整。

因此,大家在实际测试的时候,要尝试在不同的硬件配置和并发级别下进行测试,以找到最佳的性能平衡点。

相关推荐
空空潍3 小时前
Spring AI与LangChain对比:组件对应关系、设计差异与选型指南
人工智能·spring·langchain
0 13 小时前
260401日志
人工智能·深度学习·nlp
是有头发的程序猿3 小时前
用Open Claw接口做1688选品、价格监控、货源对比
开发语言·c++·人工智能
chools3 小时前
Java后端拥抱AI开发之个人学习路线 - - Spring AI【第一期】
java·人工智能·学习·spring·ai
IT_陈寒3 小时前
Vite热更新坑了我三天,原来配置要这么写
前端·人工智能·后端
嘉恩督4 小时前
ACoT-VLA:让机器人用动作思考——CVPR2026
机器人·大模型·vim
子兮曰4 小时前
CLI正在吞掉GUI:不是替代,是统治,AI时代的入口争夺战
人工智能·github·命令行
星星也在雾里4 小时前
Dify Agent + FastAPI + PostgreSQL实现数据库查询
数据库·人工智能·fastapi
Maschera964 小时前
openclaw-lark 的 Bot@Bot 跨Bot提及功能 - 开发经验分享
人工智能·node.js