Local AI Stack Planner 不是一个只能跑通一次的安装脚本,而是你在本地搭建大模型应用之前,先把组件组合、硬件边界、数据流和验证路径想清楚的一套规划流程。很多人下载好了模型、装好了 Ollama,却卡在后面的 RAG 接入、向量库选型、并发预估和显存分配上;也有人一开始就搭了一套很重的 Kubernetes 平台,结果一台机器连 7B 模型都跑不流畅。这里会从本地 AI 技术栈分层讲起,给出硬件基线检查方法,再用一个最小 Planner 示例把需求翻译成候选技术栈,最后补充验证、排错和生产落地建议。
“Stack”在软件领域有多个含义:应用技术栈、处理协议栈、程序运行时的调用栈。Local AI Stack Planner 里的 stack 主要指的是技术栈,但实际排错时,你会经常遇到调用栈溢出、前端 Maximum call stack size exceeded、模型加载栈分配失败等问题。因此,这篇文章把技术栈规划、运行期排错和性能验证放在一起讲,才更贴近真实项目。
1. 本地 AI 技术栈为什么需要“先规划后实现”
1.1 Local AI Stack Planner 解决的是选择问题,不是安装问题
本地 AI 并不是一件单一产品。它至少包含模型文件、推理引擎、应用框架、向量数据库、部署方式和监控工具。每一层都有多个候选,而这些候选之间互相约束。例如,Ollama 使用方便,但对高并发推理 API 的支持不如 vLLM;LangChain 灵活性强,但小场景里直接调用 Ollama API 可能更简单;Chroma 适合单机向量检索,但数据量到几十万条后,Qdrant 或 Milvus 的优势会更明显。
Local AI Stack Planner 的核心作用,是把“我想在本地跑一个大模型”这种模糊想法,拆成“模型多大、用什么引擎、要不要 RAG、需要多少显存、怎么部署、怎么验证”这些具体问题。规划完成后再动手,安装过程的返工成本会低很多。
1.2 与云端 API 方案相比,本地 AI 栈的约束变化
云端 API 方案通常只需要关注接口协议、Token 成本和限流。本地 AI 方案多了一层硬件约束,规划方式完全不同。
| 维度 | 云端 API 方案 | 本地 AI 方案 |
|---|---|---|
| 算力来源 | 服务端集群负责 | 自己控制 GPU、CPU、内存 |
| 数据边界 | 数据需要通过网络发送到服务方 | 数据可以留在本机或内网 |
| 延迟构成 | 受网络和限流影响 | 受本地推理速度和队列影响 |
| 成本模型 | 按 Token 或调用量计费 | 硬件采购、电费和维护成本 |
| 扩容方式 | 提升账号配额即可 | 需要重新规划模型、显存和服务架构 |
这些差异决定了本地 AI 技术栈无法照搬云端部署方案。显存不足时,第一反应不应该是买新显卡,而是看量化等级、上下文长度和并发数能不能先降下来。判断顺序应该是“先规划,再调参,最后决定要不要升级硬件”。
1.3 本地 AI 技术栈的最小分层模型
本地 AI 技术栈可以按五层来拆。每层独立演进,替换某一层时不需要重写整条链路。
- 模型层:包括模型权重、量化格式、上下文长度和词表。
- 推理层:负责加载模型、分配显存、执行生成,例如 Ollama、llama.cpp、vLLM。
- 应用层:负责对话、文档问答、Agent 流程,例如 LangChain、LlamaIndex、Open WebUI。
- 数据层:负责文档解析、切片、向量化和检索,例如 Chroma、FAISS、Qdrant、Milvus。
- 部署与运维层:负责容器编排、配置管理、日志、监控和版本回滚。
规划时按这五层逐项填写,最终形成一份可评审的 stack 清单,而不是只下载一个大模型文件。
2. 先盘点硬件、系统和运行前置条件
2.1 硬件预算:显存、内存、磁盘和处理单元
本地 AI 最关键的硬件指标是显存。显存决定了能加载多大模型,以及上下文和并发能开到什么程度。下面给出的是常见量化模型在短上下文、低并发场景下的参考值,实际以模型文件大小和运行参数为准。
| 模型档位 | 量化文件大小约值 | 推荐显存 |
|---|---|---|
| 1B-3B Q4_K_M | 0.7GB-2GB | 4GB 以上 |
| 7B Q4_K_M | 4GB-5GB | 8GB 以上 |
| 14B Q5_K_M | 9GB-11GB | 16GB 以上 |
| 32B Q4_K_M | 19GB-21GB | 24GB 以上 |
| 70B Q4_K_M | 40GB-45GB | 48GB 以上 |
显存计算可以先用近似公式估算:模型权重显存大约等于“参数量乘以 0.6 字节”。例如 7B 参数按 Q4_K_M 量化后大约是 7 × 0.6 = 4.2GB,再叠加 KV cache、临时缓冲和推理框架开销。KV cache 与上下文长度、并发请求数量直接相关,长上下文或多并发时,至少再预留几 GB。
内存方面,CPU 推理时需要足够内存加载模型;即使 GPU 推理,也会因为模型加载、tokenizer、应用框架产生内存占用。磁盘方面要特别检查模型目录和向量库目录的空间,一个 7B 模型 5GB 左右,14B 模型 10GB 左右,多个版本叠加后占用很快增加。
2.2 操作系统与驱动确认
本地 AI 开发最顺畅的环境通常是 Linux。NVIDIA 显卡用户需要确认驱动和 CUDA 环境;Apple Silicon 用户可以优先选择支持 Metal 的推理框架;纯 CPU 环境则只能选择 3B 以下的小模型,或者接受 7B 模型的较低生成速度。
Windows 用户如果使用 NVIDIA 显卡,常见做法是使用 WSL2 配合 Docker Desktop,或者直接在 Windows 上运行 Ollama 等工具。这里要明确一个原则:生产环境优先使用 Linux 服务器,避免桌面系统自动更新导致驱动和容器 GPU 能力发生变化。
2.3 用命令完成环境基线检查
在下载模型之前,先用一组命令确认基线。这些命令可以在 Linux 服务器或 WSL2 环境中执行。
nvidia-smi lscpu free -h df -h /models python3 --version docker version docker compose version每一条命令都要有明确检查目标:
nvidia-smi确认显卡型号、驱动版本、当前显存占用。如果提示命令不存在,说明当前没有 NVIDIA 用户态驱动。lscpu确认 CPU 核数和架构,判断是否适合 CPU 推理。free -h确认可用内存。模型加载时会把权重加载到内存或显存,内存不足会导致进程被 kill。df -h /models确认模型存储目录的磁盘空间。python3 --version确认脚本运行环境。docker version和docker compose version确认容器化部署前置条件。
注意:不要只验证命令能执行。还要把输出记录到文档里,作为技术栈规划的输入,否则换一台机器又会遇到驱动、显存、磁盘不匹配的问题。
3. 用一个最小 Planner 把需求翻译成技术栈
3.1 Planner 的输入:场景、数据规模、并发和隐私要求
Local AI Stack Planner 可以做成一份需求文件加一个决策脚本。需求文件使用 YAML 描述,方便 Git 跟踪变更。下面是一个最小示例。
scenario: rag-chat # chat、rag-chat、code-assistant model: language: zh max_context_tokens: 8192 data: total_docs: 5000 doc_type: pdf,md weekly_growth: 200 performance: concurrent_requests: 2 max_first_token_latency_ms: 3000 budget_gpu_vram_gb: 16这些字段分别对应规划时需要回答的问题。scenario决定要不要引入 RAG;total_docs决定向量库规模;concurrent_requests决定推理引擎的选型;budget_gpu_vram_gb决定模型档位和量化等级。
| 字段 | 作用 | 常见值 |
|---|---|---|
| scenario | 业务类型 | chat、rag-chat、code-assistant |
| model.language | 生成语言 | zh、en、multilingual |
| model.max_context_tokens | 上下文长度 | 4096、8192、16384 |
| data.total_docs | 文档总量 | 0、5000、50000 |
| performance.concurrent_requests | 预期并发 | 1、2、8、30 |
| performance.budget_gpu_vram_gb | 显存预算 | 8、16、24、48 |
3.2 一个最小 Planner 示例:requirements.yaml + planner.py
Planner 脚本读取需求文件,根据规则输出候选技术栈。下面代码用于说明思路,实际项目需要结合自己的组件清单、版本和许可要求调整。
import yaml # key: (data_size, load, scenario) PROFILE_TABLE = { ("small", "low", "chat"): { "model": "qwen2.5:7b-instruct-q4_K_M", "engine": "ollama", "framework": "open-webui", "vector_db": "none", "deploy": "docker-compose", }, ("small", "low", "rag"): { "model": "qwen2.5:7b-instruct-q4_K_M", "engine": "ollama", "framework": "langchain", "vector_db": "chroma", "deploy": "docker-compose", }, ("medium", "medium", "rag"): { "model": "qwen2.5:14b-instruct-q5_K_M", "engine": "llama.cpp", "framework": "langchain", "vector_db": "qdrant", "deploy": "docker-compose", }, ("high", "high", "rag"): { "model": "qwen2.5:32b-instruct-q4_K_M", "engine": "vllm", "framework": "langchain", "vector_db": "milvus", "deploy": "kubernetes", }, } def pick_profile(requirements): docs = requirements["data"]["total_docs"] vram = requirements["performance"]["budget_gpu_vram_gb"] concurrent = requirements["performance"]["concurrent_requests"] scenario = requirements["scenario"] if docs <= 10000 and vram <= 16: data_size, load = "small", "low" elif docs <= 50000 and vram <= 32 and concurrent <= 8: data_size, load = "medium", "medium" else: data_size, load = "high", "high" if scenario == "rag-chat" or docs > 0: scenario = "rag" return PROFILE_TABLE.get( (data_size, load, scenario), { "model": "qwen2.5:7b-instruct-q4_K_M", "engine": "ollama", "framework": "langchain", "vector_db": "chroma", "deploy": "docker-compose", }, ) def main(): with open("requirements.yaml", "r", encoding="utf-8") as f: requirements = yaml.safe_load(f) profile = pick_profile(requirements) print(yaml.safe_dump( {"recommended_stack": profile}, allow_unicode=True, sort_keys=False, )) if __name__ == "__main__": main()这个脚本没有做复杂的权重计算,而是使用一张决策表把需求映射到候选组件。它解决的是“不同规模项目应该从哪条技术路线起步”的问题,而不是“最终必须用这些组件”。
3.3 运行 Planner 并理解输出
在项目目录下执行以下命令。
python3 -m venv .venv source .venv/bin/activate pip install pyyaml python planner.pyWindows PowerShell 下激活命令是.venv\Scripts\activate。正常输出如下。
recommended_stack: model: qwen2.5:7b-instruct-q4_K_M engine: ollama framework: open-webui vector_db: none deploy: docker-compose如果需求文件里scenario是rag-chat,或者total_docs大于 0,输出会改为带vector_db: chroma的方案。这说明 Planner 的决策链路是:文档量决定要不要向量库,显存决定模型大小,并发决定推理引擎。
注意:Planner 输出的是候选技术栈,不是最终架构。落地前还要确认模型许可、容器镜像版本、模型目录权限和数据备份方案,缺少这些检查不能直接进入生产环境。
3.4 用决策表解释选型逻辑
| 输入条件 | 模型档位 | 推理引擎 | 向量库 | 部署方式 |
|---|---|---|---|---|
| 单机 16GB 以下,纯聊天 | 7B Q4_K_M | Ollama | 无 | Docker Compose |
| 单机 16GB,文档问答 | 7B-14B Q4/Q5 | Ollama 或 llama.cpp | Chroma 或 Qdrant | Docker Compose |
| 单机 32GB,中等并发 | 32B Q4_K_M | vLLM 或 llama.cpp | Milvus 或 Qdrant | Kubernetes |
| 纯 CPU,低延迟要求不高 | 1B-3B Q4_K_M | llama.cpp | FAISS 或 Chroma | 单进程或 Docker |
4. 模型、推理引擎、应用框架与数据层怎么选
4.1 模型层先定“参数、量化、上下文”
模型层的选择顺序应该是:先看显存预算,再定参数量级,然后选择量化格式,最后确认上下文长度。
参数越大,生成质量通常越好,但显存和延迟成本也越高。量化是一种压缩权重的方法,常见格式如下。
| 量化级别 | 模型文件大小参考 | 优缺点 |
|---|---|---|
| Q4_K_M | 小 | 速度和质量比较均衡,日常开发建议首选 |
| Q5_K_M | 中 | 质量更稳,显存需求略高 |
| Q8_0 | 大 | 接近半精度效果,但显存占用明显 |
上下文长度影响 KV cache 显存。把上下文从 2048 提升到 8192 时,KV cache 占用会成倍增加。实际项目里不要盲目使用长上下文,如果文档问答场景较多,优先考虑 RAG 而不是把整份文档塞进上下文。
4.2 推理引擎按部署方式选
推理引擎是把模型权重变成可用 API 或命令行工具的关键层。选型依据主要是并发量、控制粒度和运维成本。
| 引擎 | 适合场景 | 注意点 |
|---|---|---|
| Ollama | 个人开发、快速验证、单机部署 | API 简单,便于和 Open WebUI 配合 |
| llama.cpp | 单机精细控制、CPU/GPU 混合推理 | 需要手动准备 GGUF 模型 |
| vLLM | 多并发 API 服务、生产环境 | 对 GPU 显存和 Linux 环境要求较高 |
| LM Studio | 桌面端试用、图形化操作 | 更适合开发调试,不擅长大规模服务 |
Ollama 的优势是模型管理和 API 简单,适合作为学习环境的第一步。vLLM 的优势是高并发吞吐,但配置更复杂,显存规划要更精细。生产环境不要仅凭“哪个下载量多”选型,而是用真实压测数据判断。
4.3 应用框架:不是所有场景都需要
如果只是做一个聊天界面,直接使用 Open WebUI 加 Ollama 即可,不一定要引入 LangChain。RAG 场景可以先用 LlamaIndex 或 LangChain 跑通检索链路,再逐步增加 Agent、多轮对话和工具调用。
| 框架 | 主要优势 | 适合场景 |
|---|---|---|
| LangChain | 组件丰富、生态大 | 需要编排多个工具和模型能力 |
| LlamaIndex | 文档索引和检索设计较好 | 文档问答、RAG 原型 |
| Open WebUI | 开箱即用的 Web 界面 | 对话演示、本地知识库入口 |
| Dify | 低代码编排 | 业务人员参与配置的场景 |
框架的选择会影响后续扩展路径。如果业务核心是文档问答,LlamaIndex 的检索抽象会更容易理解;如果业务核心是多 Agent 编排,LangChain 更合适。不要因为“大家都在用”就一次性引入全部生态。
4.4 向量数据库从轻到重推进
RAG 场景里,向量数据库负责存储文档切片后的向量,并在提问时检索相似片段。选型时先明确数据量和并发量。
| 向量库 | 部署成本 | 适合场景 |
|---|---|---|
| Chroma | 低 | 单机原型、几千到几万条向量 |
| FAISS | 低 | 离线检索、内嵌到应用 |
| Qdrant | 中 | 中大规模向量、过滤查询、生产服务 |
| Milvus | 高 | 大规模向量、多节点、运维团队支持 |
| pgvector | 中 | 已有 PostgreSQL,希望减少组件数量 |
如果文档量只有几百份,直接用 Chroma 或 FAISS 即可。当文档增长到十万条以上,或者需要复杂 metadata 过滤时,再迁移到 Qdrant 或 Milvus。迁移时要注意 embedding 模型是否一致,向量维度不一致会导致旧向量无法复用。
5. 把最小闭环跑通并记录性能数据
5.1 最小可运行闭环:Ollama + Open WebUI
学习环境里,先用 Docker Compose 启动 Ollama 和 Open WebUI 是成本最低的方案。下面的 compose 文件是示例,实际使用时要锁定镜像版本,避免latest或main在后续更新中引入不兼容变化。
services: ollama: image: ollama/ollama:latest restart: unless-stopped volumes: - ./models:/root/.ollama deploy: resources: reservations: devices: - driver: nvidia count: all capabilities: [gpu] open-webui: image: ghcr.io/open-webui/open-webui:main restart: unless-stopped ports: - "3000:8080" environment: OLLAMA_BASE_URL: http://ollama:11434 volumes: - ./webui-data:/app/backend/data启动后访问http://localhost:3000完成初始化,再进入模型管理界面拉取本地模型。注意,模型下载和 Docker 镜像拉取都需要网络,但模型下载完成后推理数据不会离开本机。
5.2 用脚本记录 tokens/s、显存占用和首 token 延迟
启动 Ollama 后,可以用 API 做一次最小推理验证。
curl http://localhost:11434/api/generate \ -d '{"model":"qwen2.5:7b-instruct-q4_K_M","prompt":"用一句话说明本地 AI 技术栈","stream":false}'同时观察nvidia-smi,确认 GPU 是否被进程占用。更规范的性能记录可以用 Python 脚本完成。
import time import requests payload = { "model": "qwen2.5:7b-instruct-q4_K_M", "prompt": "介绍一下 RAG", "stream": False, } start = time.time() resp = requests.post("http://localhost:11434/api/generate", json=payload) elapsed = time.time() - start data = resp.json() text = data.get("response", "") eval_count = data.get("eval_count", 0) eval_duration_ns = data.get("eval_duration", 0) or 1 tokens_per_second = eval_count / (eval_duration_ns / 1e9) print("generated_chars:", len(text)) print("elapsed_seconds:", round(elapsed, 3)) print("tokens_per_second:", round(tokens_per_second, 2))执行后记录三个指标:生成字符数、端到端耗时、每秒生成 Token 数。后续修改量化等级、上下文长度或并发参数时,再跑同一脚本,形成对比基线。
5.3 从结果反推硬件瓶颈
性能数据可以快速定位瓶颈。
| 现象 | 判断 | 处理建议 |
|---|---|---|
| 首次请求很慢,之后变快 | 模型冷启动,权重从内存加载到显存 | 预热模型,或调整常驻模型数量 |
| tokens/s 很低,但显存占用高 | 可能只用了部分 GPU 层或模型过大 | 检查nvidia-smi中的进程,调整 GPU layers |
| 请求一多就 OOM | KV cache 或并发开销超出显存 | 降低并发、缩短上下文、换小量化模型 |
| GPU 利用率很低,CPU 很高 | 推理没有充分使用 GPU | 确认引擎和容器配置正确传递 GPU |
注意:不要只看服务能启动,还要看显存占用、首 token 延迟和输出质量。一个能启动但回答质量差、响应极慢的本地 AI 服务,不具备生产价值。
6. 常见“栈”坑与排查路径
6.1Maximum call stack size exceeded不是模型问题
本地 AI 前端界面使用 Vue 等框架时,浏览器控制台可能出现类似[Vue warn]: Error in beforeCreate hook: "RangeError: Maximum call stack size exceeded"的报错。这个错误是 JavaScript 调用栈溢出,不是后端模型推理失败。
常见原因是递归组件没有终止条件,或者beforeCreate钩子里执行了会再次触发 hook 的代码。例如下面的写法就会无限递归。
beforeCreate() { const loop = () => { loop(); }; loop(); }排查顺序是:打开 DevTools 的 Console,查看报错堆栈;进入beforeCreate钩子,确认是否有递归调用;检查 Vue 组件树是否存在无终点的递归渲染。修复方式是增加递归终止条件,或者把无限层级树的数据改成扁平结构并用 path 字段控制渲染。
6.2 模型加载时显存不足
本地推理常见的错误是failed to allocate或加载后进程被杀。原因通常是模型文件太大,或者显存已经被其他进程占用。
先执行nvidia-smi查看显存占用,确认没有残留进程。然后看模型量化大小是多少,再检查推理参数里的ctx-size和 GPU layers。如果显存不足,优先降低上下文长度,再考虑减少 GPU layers 或换更小的量化模型。生产环境里还要给其他服务预留显存,不要把显卡全部占满。
6.3 RAG 检索结果质量差
RAG 问答效果差时,问题往往不在大模型,而在文档切片和 embedding。
如果切片过大,一个问题会检索到大段无关文本;如果切片过小,语义会被截断。实践中建议每片 512 到 1024 token,切片之间保留 10% 到 20% 的重叠。embedding 模型需要和文档语言匹配,中文场景可以优先测试支持中文的 embedding 模型,再通过检索命中率验证。
排查顺序是:先打印检索出的 top-k 片段,确认这些片段是否真的与问题相关;如果不相关,调整 chunk 大小、top_k 和 embedding 模型;如果相关但回答仍不对,再优化 prompt 和生成参数。
6.4 技术栈规划中的组件冲突
多个组件同时运行时,端口、模型目录和数据目录容易互相干扰。例如 Ollama 默认端口 11434,Open WebUI 默认端口 8080,如果其他服务占用了这些端口,启动会失败。
检查命令如下。
ss -lntp docker ps du -sh models/* webui-data/*生产环境建议为每个组件分配独立目录,固定端口,并通过.env文件统一管理配置。模型目录不要放在临时目录,避免机器重启后模型丢失。
7. 从开发验证到生产落地还需要补齐什么
7.1 学习环境与生产环境的差异
开发环境跑通只是第一步。生产环境要额外考虑配置外置、日志采集、权限控制、监控、备份和回滚。
| 项目 | 学习环境 | 生产环境 |
|---|---|---|
| 配置 | 写在脚本或当前目录 | 环境变量、配置文件模板 |
| 模型版本 | 手动下载,随时替换 | 固定模型 digest,记录版本 |
| 数据存储 | 本地目录、SQLite | PostgreSQL、内网对象存储 |
| 日志 | 终端输出 | 集中日志系统 |
| 监控 | 手动 nvidia-smi | Prometheus + Grafana |
| 权限 | 默认不鉴权 | API Key、SSO、最小权限 |
| 回滚 | 重装即可 | 模型目录备份、镜像版本回退 |
7.2 用环境变量管理关键参数
生产环境不要把显存、模型名、上下文长度写死在代码里。下面是一个.env.example示例,真实文件不要提交到 Git。
OLLAMA_MODEL=qwen2.5:7b-instruct-q4_K_M OLLAMA_NUM_CTX=8192 OLLAMA_MAX_LOADED_MODELS=1 DATA_DIR=/data/local-ai WEBUI_AUTH=trueDocker Compose 启动时读取这个文件,可以把配置与代码分离。修改OLLAMA_NUM_CTX或模型名时,不需要改源码,只需要更新环境变量并重启容器。
7.3 安全与回滚
本地 AI 并不意味着可以完全忽略安全。需要确认三点:模型文件来源是否可信、模型加载目录是否只允许指定用户写入、Web 服务是否暴露在公网。
模型下载后要记录文件大小和校验值。镜像不能一直使用latest,否则后续版本变化可能导致不可用。模型目录建议做软链接或符号链接管理,方便切换模型版本;向量库数据要定期备份,备份与模型文件分开存放。
8. 可复用清单与下一步路线
8.1 技术栈规划检查清单
每次搭建 Local AI Stack 之前,按下面清单逐项确认。
- 确认场景类型:聊天、RAG、Agent、批量处理。
- 确认硬件:显存、内存、磁盘、GPU 驱动。
- 确认模型:参数量、量化格式、上下文长度、许可要求。
- 确认推理引擎:Ollama、llama.cpp、vLLM,是否满足并发预期。
- 确认是否引入向量库,以及文档量和查询量级。
- 确认部署方式:单机 Docker Compose 还是 Kubernetes。
- 固定模型、镜像和依赖版本。
- 记录性能基线:tokens/s、首 token 延迟、显存占用。
- 配置日志、监控、备份和回滚。
- 验证隐私边界:哪些数据不会离开本地。
8.2 从最小案例到生产力应用
建议先跑通一个不超过三个组件的最小链路:Ollama 提供推理,Open WebUI 提供界面,本机目录保存模型。确认稳定运行后,再叠加 RAG,加入文档解析、切片和向量库。此时不要直接上多节点,先在一台机器上完成检索命中率和问答质量的验证。
当并发请求超过单机能力时,再把推理层替换为 vLLM,并增加负载均衡和监控。每一步都要用上一阶段的基线数据来判断,而不是凭感觉升级硬件。
8.3 建议学习路径
学习本地 AI 技术栈,可以从四个方向推进:先理解模型量化和显存关系,再掌握 Ollama 或 llama.cpp 的部署参数,然后实现一个 RAG 问答案例,最后补充监控和权限控制。
Local AI Stack Planner 真正要做的事,是让每次选型变更都能被记录、被验证、被回滚。建议从一台单机和一个 7B 模型开始,把性能指标和配置参数记在一个文件里,再逐步替换组件。环境变化时,这份记录就是你判断技术栈是否合理的依据。