开源大模型部署实战:从Ollama到vLLM搭建生产级推理服务 资本市场的叙事往往比工程现实提前半步。当外界把目光放在月之暗面 IPO、大模型“第二股”这一类概念上时真正需要被回答的问题反而被忽略了模型榜单上的分数只能换来一次发布会能不能在真实流量冲击下保持可用的延迟、吞吐、成本和稳定性才决定一家模型公司、一个模型产品能不能走得更远。旧叙事的主语是参数规模、测试集分数和演示 Demo新叙事的主语应当是服务、评测、安全、成本与可维护性。这篇内容会把注意力从资本市场拉回工程现场用一个最小可运行的路线把一个开源大模型从本地启动开始逐步搭成可以被业务调用的推理服务并补上评测、安全、可观测和排错能力。1. 所谓“第二股叙事”其实是可运营的系统能力1.1 旧叙事是模型能力新叙事是运行能力过去两年里行业评价大模型的方式非常直观看参数量、看榜单分数、看发布会演示。这个阶段的工作重心是“让模型更像一个聪明的助手”。但当模型进入真实业务系统后评价方式会迅速切换成另一套问题。业务高峰期单卡能扛多少并发用户感知到的首字延迟和总耗时分别是多少提示词被注入后模型会不会输出风险内容上游模型服务崩溃时业务系统会不会整体不可用一次模型升级如何证明新模型在历史用例上不退步这些问题没有一项属于算法本身却决定了一个大模型产品能不能被长期运营。所谓“第二股需要新叙事”本质上是投资者的预期变了模型能不能讲故事不再是唯一竞争力模型服务能不能稳定运行、可以被验证和维护才是新的技术底盘。1.2 技术叙事要从离线指标走向在线指标模型开发阶段重视的是离线评估指标而运行阶段需要同时看在线服务质量。两者之间存在很大差异。指标类型常用指标主要关注点谁在看模型能力准确率、F1、ROUGE、人工评测模型回答是否正确算法团队服务质量首 token 延迟、端到端延迟、吞吐用户等待时间和并发承载SRE、后端团队稳定性错误率、超时率、可用性服务是否持续可用SRE、运维团队成本每千 token 成本、GPU 利用率、缓存命中率单位成本是否可接受技术负责人、财务业务反馈Bad case 率、投诉量、业务转化率模型对业务目标是否正向产品、运营、算法很多团队只在第 1 类指标上做决策结果就是模型离线评测“看起来更好”上线后却因为延迟高、格式不稳定、成本超标而回滚。把后面 4 类指标纳入模型发布评审是从 Demo 走向系统的第一步。1.3 本文的实践主线接下来的内容会落到一条明确的技术主线上把一个开源大模型部署成 OpenAI 兼容的推理服务然后让业务系统通过标准接口调用它并围绕调用做超时、缓存、评测、安全与监控。具体会经过 4 个阶段用 Ollama 在本地跑通模型理解模型的下载、加载和调用关系。用 vLLM 部署一套适合更高并发流量的 OpenAI 兼容 API理解关键参数。用标准 OpenAI SDK 把服务接入业务代码补上超时、重试、缓存与成本控制。用评测集、安全策略和监控手段证明模型服务“可用且可信”。这套路线既适合独立开发者搭建个人模型服务也适合企业内部从零建设大模型应用平台。2. 部署路线与模型选型先决定模型在哪里运行2.1 三条部署路线不能混为一谈大模型落地时第一个要决定的问题不是在哪个框架上跑而是模型以什么形态提供给业务。部署路线典型形态优势代价闭源模型 API调用云端供应商开放的接口接入快、不关心推理资源、模型能力强数据出域、单位成本不可控、厂商依赖开源模型私有化在自己的 GPU 集群上跑开源权重数据可控、可定制、长期成本阶梯下降运维复杂、硬件成本高、需要算法团队端侧/边缘部署在 PC、手机或嵌入式设备上跑小参数模型时延低、离线可用、隐私最好模型能力有限硬件适配工作量大很多项目的失败不是因为模型选错了而是因为三种形态混用。企业数据敏感度高的场景比如金融单据分析、医疗文本处理通常不适合直接把数据送入外部 API个人开发者做工具类应用则没必要自建 GPU 服务。建议先按数据合规要求、调用量、模型能力要求三个维度做决策不要看到新模型就立刻下载。2.2 显存和内存预算的粗算方法部署开源大模型之前需要先估算硬件资源。模型权重占用的显存可以用一个粗略公式计算参数数量 × 每个参数所需字节数 权重显存占用FP32 精度4 字节FP16 / BF16 精度2 字节INT8 量化1 字节INT4 / NF4 量化约 0.5 字节以 7B 参数模型为例FP16 精度下权重约 14GBINT8 约 7GBINT4 约 3.5GB。实际部署时还需要额外留出 KV Cache、激活值、CUDA context 和并发请求的显存占用所以不能只按权重算。这里要特别提醒量化能降低显存占用但会带来一定质量损失不要为了在低配环境跑大模型无限制使用低比特量化尤其对依赖指令遵循能力的业务场景质量下降会非常明显。2.3 学习环境与生产环境怎么分配不同阶段对硬件的要求完全不同。环境参考配置能做什么不能做什么本地学习16GB 内存的 MacBook Air M3、普通 Windows 游戏本跑 7B 左右量化模型、学习 API 调用、做 Prompt 实验高并发推理、大规模微调开发联调单张 24GB 显存 GPU如常见云主机跑 7B/14B FP16、验证 vLLM、跑评测集生产流量、多副本调度生产测试多张 GPU配套对象存储、Redis、监控高并发 API、灰度发布、故障演练不做充分验证就直接上新模型生产稳定GPU 集群 网关 Prometheus 日志系统7 × 24 运行把模型加载当成无状态服务即可忽略显存和热迁移无 GPU 条件时可以用 CPU 跑小模型学习原理。它足够理解接口和流程但不要把 CPU 推理的吞吐数据当作生产依据。2.4 模型从哪里下载开源模型一般通过模型仓库获取Hugging Face、ModelScope 或各推理工具自带的模型库。以 Ollama 为例模型通过仓库拉取后保存在本地下载和启动用一条命令即可完成。下载模型前先看磁盘剩余空间。一个 7B 量化模型通常在 4GB 到 6GBFP16 版本在 14GB 到 15GB下载到系统盘容易把磁盘占满建议先指定模型存储目录具体方法会在第 3 部分展开。3. 用 Ollama 快速跑通本地大模型3.1 安装并配置模型存储路径Ollama 对个人开发者来说是最低成本的“大模型下载 本地部署”入口。它把模型格式、显存调度和 HTTP API 都封装好适合用来理解整套交互逻辑。安装完成后先在终端确认版本。ollama --version如果还没有安装可以到 Ollama 官网获取对应操作系统的安装包。Windows 安装完成后默认模型会存放在用户目录下占用 C 盘空间。很多用户会遇到“模型下载到 D 盘”的需求做法是在启动前设置环境变量OLLAMA_MODELS。以 Windows 为例在 PowerShell 中设置系统级环境变量setx OLLAMA_MODELS D:\ollama\models设置完成后需要重启终端并确保 Ollama 服务已经重新读取环境变量。Linux 环境下采用如下方式export OLLAMA_MODELS/data/models ollama serve检查配置是否生效可以下载一个模型后观察模型文件是否落在指定目录。注意先确认目标磁盘空间是否充足再执行模型下载。模型下载中断后重新拉取是常见问题不要反复删除缓存文件优先保证存储目录的稳定性。3.2 下载并运行一个 7B 级别模型本文以 Qwen 系 7B 指令模型为例。不同时间点的具体 tag 会有变化实际使用时要先查询模型仓库中的可用标识。ollama pull qwen2.5:7b下载完成后可以用ollama list查看本地模型列表。ollama list需要进入交互式对话时直接执行ollama run qwen2.5:7b交互模式下可以输入问题验证模型是否真的被加载成功。退出交互模式通常使用/bye或Ctrl D。ollama run命令有一个容易被忽视的行为首次调用时才会真正加载模型到内存或显存所以第一次对话可能比后续慢很多。这是冷启动延迟不是模型卡死。3.3 用本地 HTTP API 验证请求链路Ollama 启动后默认监听本机 11434 端口。它同时提供原生 API 和 OpenAI 兼容 API。先用原生接口做一个最小请求验证curl http://localhost:11434/api/chat \ -d { model: qwen2.5:7b, messages: [ {role: user, content: 用一句话解释什么是大模型推理} ], stream: false }返回结果中会包含message.content以及prompt_eval_count、eval_count等 token 统计字段。这些字段是成本核算和性能排障的重要依据。如果希望业务代码兼容 OpenAI SDKOllama 也提供了 OpenAI 兼容端点curl http://localhost:11434/v1/chat/completions \ -H Content-Type: application/json \ -d { model: qwen2.5:7b, messages: [ {role: user, content: 你好} ] }注意这里的 URL 路径里必须有/v1。很多本地调用失败都是因为把base_url配成了根地址漏掉了/v1。3.4 理解推理参数对输出质量的影响Ollama API 支持传入标准的生成参数。比较常用的是下面三个。参数作用设置过小的表现设置过大的表现temperature控制随机性回答固定、缺少多样重复、发散、跑题top_p控制候选 token 范围可能过于保守可能输出无关内容max_tokens / num_predict控制最大生成长度回答被截断资源浪费、等待时间变长实际项目中不要所有场景都用同一套参数。代码生成、信息抽取、医疗建议、营销文案这些任务的最佳参数都不同。先固定一小批 Prompt人工观察不同参数下的输出差异比直接套用博客里的“推荐值”更可靠。4. 用 vLLM 搭一个生产级 OpenAI 兼容服务4.1 为什么从 Ollama 换到 vLLMOllama 适合个人学习和快速验证但在高并发生产场景下还需要更强的推理调度能力。vLLM 是当前常见的开源推理服务框架它通过 PagedAttention、Continuous Batching 等机制提高显存利用率和吞吐。PagedAttention 解决的问题是传统推理中显存碎片化。请求产生的 KV Cache 不必占一整块连续显存而是按页分配能放入更多并发请求。Continuous Batching 则让推理服务不需要等一个 batch 完成后再收集下一个请求而是动态插入新请求。这两个机制叠加后单位 GPU 能服务的请求数量会明显提升。因此生产服务建议使用 vLLM而不是把 Ollama 直接暴露给大量业务方。4.2 安装 vLLM 并准备 Python 环境vLLM 以 Python 包形式安装。建议使用独立虚拟环境避免污染系统 Python。mkdir -p ~/llm-server cd ~/llm-server python3 -m venv .venv source .venv/bin/activate pip install -U pip pip install vllm具体安装前先确认本机 CUDA 版本、Python 版本和 vLLM 的兼容矩阵。不要拿到安装命令就直接执行。依赖版本不匹配时报错往往出现在import vllm阶段表现为 CUDA 扩展加载失败。4.3 下载模型到本地目录生产环境不建议让服务每次启动时都去模型仓库现拉权重。应先把模型下载到本地固定目录让启动过程更稳定、更可控。使用 ModelScope 下载模型的示例pip install modelscope modelscope download --model Qwen/Qwen2.5-7B-Instruct --local_dir /data/models/qwen2.5-7b-instruct如果没有特殊要求也可以通过 Hugging Face CLI 下载。下载完成后记录本地模型路径接下来 vLLM 启动时直接使用该路径。注意模型权重要和推理代码分开备份。团队协作时模型目录应使用统一的命名规范例如模型名-版本-精度方便后面做模型灰度与回滚。4.4 启动 vLLM OpenAI 兼容服务vLLM 提供了一个vllm serve子命令可以直接启动兼容 OpenAI 的 HTTP 服务。下面是一个最小示例vllm serve /data/models/qwen2.5-7b-instruct \ --served-model-name demo-model \ --host 0.0.0.0 \ --port 8000 \ --gpu-memory-utilization 0.90 \ --max-model-len 32768启动时可以通过--served-model-name指定对外暴露的模型名。这个参数容易被忽略如果业务侧传入的model字段与这里不一致服务端可能返回 404。gpu-memory-utilization表示 vLLM 最多可以使用多少 GPU 显存。不要为了多放一些 KV Cache 就设置为 1.0预留一部分显存给 CUDA context、驱动和偶发的显存波峰会更稳妥。max-model-len控制模型支持的上下文最大长度。长度越大单请求 KV Cache 占用越高。如果设置过大的max-model-len会出现启动阶段就 OOM 的情况。建议根据业务实际需要设置不要盲目调到模型支持上限。启动成功后先检查模型列表curl http://localhost:8000/v1/models能返回模型信息说明服务已经暴露正常。4.5 用真实请求做一次基础验证调用 OpenAI 兼容接口curl http://localhost:8000/v1/chat/completions \ -H Content-Type: application/json \ -d { model: demo-model, messages: [ {role: user, content: 请用一句话解释为什么大模型需要 GPU} ], max_tokens: 256, temperature: 0.3 }验证关注点有三个返回值里是否包含choices[0].message.content。usage.total_tokens是否符合预期。首次请求耗时是否正常第二次请求是否变快。首 token 延迟、总延迟和吞吐后续需要从监控面板看不能只靠人工请求感受。但从现在开始至少要为每次输出记录usage这部分是成本和效果分析的基础数据。5. 接入业务系统超时、重试、缓存和成本控制5.1 用 OpenAI SDK 调用本地兼容服务vLLM 和 Ollama 都提供 OpenAI 兼容接口业务代码可以使用标准 OpenAI SDK 接入方便在本地推理服务和远程 API 之间切换。from openai import OpenAI client OpenAI( base_urlhttp://127.0.0.1:8000/v1, api_keyEMPTY, timeout60.0, max_retries2, ) response client.chat.completions.create( modeldemo-model, messages[ {role: system, content: 你是一个简洁的技术助手。}, {role: user, content: 什么是 RAG}, ], temperature0.3, max_tokens512, ) print(response.choices[0].message.content) print(response.usage)在本地私有化部署中api_key不是安全凭证可以传空字符串。但在生产网关里仍然需要为每个调用方分配独立 Key便于配额管理、审计和成本拆分。5.2 超时、重试与降级必须显式设计真实业务中最常出问题的不是模型答得不好而是同步等待模型返回时把整个业务线程拖垮。超时设置要区分两类连接超时TCP 建连时间通常设置 3 秒到 5 秒。读取超时等待模型返回时间需要根据上下文长度动态调整。短文本 30 秒到 60 秒合理长文档分析可能要 120 秒以上。重试不是越多越好。如果服务端已经过载盲目重试只会加重故障。建议只在网络错误和明确的 5xx 错误下重试并且最多重试 1 到 2 次。降级策略在模型不可用时尤其重要。例如搜索推荐场景可以在模型服务不可用时直接返回缓存结果或兜底规则文案而不是让用户看到“内部错误”。class LLMService: def __init__(self): self.client OpenAI( base_urlhttp://127.0.0.1:8000/v1, api_keyEMPTY, timeout60.0, max_retries2, ) def chat(self, messages): try: response self.client.chat.completions.create( modeldemo-model, messagesmessages, temperature0.3, max_tokens512, ) return response.choices[0].message.content, response.usage except Exception as exc: return 模型服务暂时不可用请稍后重试。, None上面这段代码保留了异常时的兜底文案但实际项目中要记录异常类型和请求标识不能只吞异常。5.3 缓存不是只在业务层做大模型服务存在多层缓存体系。vLLM 支持 Prefix Caching。开启以后如果多个请求共享相同前缀例如系统提示词、Few-shot 示例、工具定义服务端可以复用已经计算过的 KV Cache降低首个 token 生成时间和成本。在 vLLM 启动参数中加入--enable-prefix-caching业务层缓存则适合精确重复的问题、热门问题和文档检索结果。需要设计合理的缓存 Key把模型版本、Prompt 模板版本、参数版本都纳入 key 维度。否则模型升级后业务可能还在读旧缓存。常见设计cache_key f{model_version}:{prompt_template_version}:{hash(user_input)}对于语义相似但文本不完全一致的问题可以做语义缓存即先用 embedding 检索相似历史问题。但这个方案本身需要维护向量索引收益没有想象中高不要在低并发场景过早设计。5.4 成本控制必须从第一天开始大模型的成本曲线不是“按用户数线性增长”而是“按 token 指数增长”。如果业务不记录每个请求的 token 用量成本问题会在月底账单爆发时才暴露。建议在每个请求的日志中至少保留字段示例作用request_id8f2c9d1e关联日志user_idu_1024用户维度用量拆分modeldemo-model模型维度成本prompt_tokens512输入成本completion_tokens128输出成本latency_ms3500服务质量cached_tokens256缓存命中情况当多个业务方接入同一个模型服务时还需要通过网关做配额限制。没有配额系统任何一个调用方的死循环都可能把 GPU 资源打满导致全平台不可用。5.5 流式输出还是非流式输出对话类应用通常建议使用流式输出用户可以在完整内容生成前就看到文字显著降低体感延迟。但对后台处理任务例如批量摘要、信息抽取非流式更简单便于做超时控制和重试。OpenAI SDK 中开启流式的写法是streamTrue返回结果是迭代器stream client.chat.completions.create( modeldemo-model, messagesmessages, streamTrue, ) for chunk in stream: delta chunk.choices[0].delta.content if delta: print(delta, end, flushTrue)流式模式下错误处理更复杂。网络中断往往发生在流中间此时前端已经输出了一部分内容重试成本和副作用都需要单独设计。生产环境要区分“未开始生成”和“已经输出一半”两种情况。6. 效果怎么证明建立可回归的大模型评测集6.1 不要用单次对话判断模型好还是坏很多团队评估新模型时只是随机抽几十个问题人工看回答质量。这种方式作为日常观察可以但不能作为上线依据因为你无法在两周后精准复现同一组测试。正确做法是构建一份结构化的回归评测集让每个候选模型都跑同一批请求然后对比输出结果。评测集建议包含以下类型知识类问题确认模型能否稳定回答业务知识。指令遵循类测试固定输出格式例如 JSON 输出。抽取类从长文本抽取关键字段。安全类确认模型拒绝有害请求。边界类空输入、超长输入、多语言混杂。将评测样本保存为 JSONL 文件后续每次模型升级都复用。{id: case-001, category: extract, prompt: 提取下面句子中的日期合同签署日为2025年6月1日。, expected: 2025年6月1日} {id: case-002, category: format, prompt: 用 JSON 格式输出北京、上海、广州三个城市, expected: [\北京\, \上海\, \广州\]}6.2 评测指标怎么选评测方式不能一刀切。分类和抽取类任务可以用规则判断输出是否正确开放性问答则需要人工打分或 LLM-as-Judge。任务类型推荐评测方式说明分类准确率直接比对标签信息抽取精确匹配 / 宽松匹配日期、实体等字段比较格式控制解析成功率看能否被 JSON、XML 解析器正确解析代码生成编译 / 测试用例运行结果是否正确开放问答人工打分 / 裁判模型质量、相关性和安全性综合判断不要只使用单一分数。例如模型 A 准确率比模型 B 高 2%但在格式解析成功率上低了 20%这种情况下是否上线要由业务场景决定。6.3 把评测做成可以反复执行的脚本评测脚本不需要复杂核心是保证幂等同样的模型、同样的评测集、同样的参数应该得到一致结果。import json import openai client openai.OpenAI( base_urlhttp://127.0.0.1:8000/v1, api_keyEMPTY, timeout60.0, ) results [] with open(eval_set.jsonl, r, encodingutf-8) as f: for line in f: case json.loads(line) response client.chat.completions.create( modeldemo-model, messages[ {role: user, content: case[prompt]} ], temperature0, max_tokens512, ) output response.choices[0].message.content.strip() results.append({ id: case[id], category: case[category], expected: case[expected], output: output, match: case[expected] in output, }) match_count sum(1 for r in results if r[match]) print(fmatch_rate: {match_count / len(results):.2%})注意生成时的temperature要设为 0 或接近 0否则同一模型多次评测结果可能不稳定无法判断是模型变化还是随机性导致。6.4 Bad Case 驱动改进而不是只看分数评测报告生成后重点不是看整体分数而是逐个看 Bad Case。常见的 Bad Case 分类和改进方向如下问题现象可能原因优先处理方式格式不符合要求Prompt 未明确格式修改系统提示词知识陈旧模型训完不知道新知识引入 RAG输出存在幻觉对话式模型倾向编造要求注明不确定接入检索总是重复固定措辞temperature 不合适或微调数据偏差调整参数或检查数据拒绝正常问题安全策略过强修正安全规则和评测集把 Bad Case 记录到独立数据库中每次模型迭代后重新跑一遍看历史问题是否解决、是否出现新的问题。这一步是技术团队最有价值的资产积累。7. 能力不足时怎么办RAG、微调与多模态7.1 先尝试 RAG再考虑微调业务中发现模型回答不准确很多人第一反应是“微调”。但多数场景更适合先接入 RAG。RAG 适合以下情况知识更新频繁例如新闻、政策、商品信息。回答必须能追溯到原文例如