简介:本地部署私有化智能问答助理示例项目,完整演示了如何用Gradio搭建交互界面,以FAISS完成大规模向量的相似检索,配合Sentence-Transformers生成文本嵌入,从而实现文件上传、离线检索与智能回答的完整链路。项目共18个文件,压缩包约336KB,其中包含5个Python源码(主程序与版本迭代)、5个txt说明与配置、2个yaml配置、1个sh脚本及依赖whl包,另有证书、docx/xlsx/pdf等补充材料,覆盖环境依赖、接口配置到前端界面的主要工程要素,目录结构清晰,便于快速定位。目前已有140人浏览学习,适合AI入门者参考其工程结构,或作为私有化部署的起步模板。除核心代码外,资源还附带测试文档与配置文件,便于对照理解函数调用与模型加载逻辑;也可基于示例快速改造出符合特定业务规则、具备中文知识库检索能力的问答助理,降低从零搭建的时间成本。
1. 本地部署私有化智能问答助理:先过显存和版本两道坎
本地部署私有化智能问答助理这件事,听上去是把模型文件下载下来、再启动一个服务,实际上一大半时间都耗在显存预算和版本对齐上。某公司要搭一个数据不出内网的问答助理,技术选型用了 ChatGLM 系开源模型,开发者在单张 16G 显卡上直接拉全量权重,加载到一半进程就被系统杀掉,终端只留下一个 Killed,黑匣子一样完全没提示。后来把权重换成量化版本、把 transformers 钉到指定版本号,十分钟就起来了。这套方案适合手里只有一两张消费级显卡、又想把问答能力收回内网的开发者,也适合正在为 PyTorch 和 CUDA 版本打架发愁的人。先把环境对齐,后面才谈得上跑通。
2. 环境准备与版本选型:Python、CUDA、PyTorch 一次对齐
部署私有化问答助理,最忌讳一上来就pip install。模型能不能跑起来,先看三个东西:显存、驱动、Python 环境。这三样不齐,后面每一步都在翻车边缘试探。
2.1 硬件要求与显存预算
先把显存账算清楚。ChatGLM-6B 这类 6B 级模型的 FP16 权重本身约 12G,推理时还要叠加注意力机制的 KV cache 和激活值,实际占用在 13~15G,单卡 16G 是起步价。量化之后权重可以缩到 6G 上下,12G 显卡也能跑,8G 就得靠 int4 加短序列凑合。显存不够硬上全量,轻则 OOM,重则整机卡死。
| 模型规模 | 权重精度 | 预计显存占用 | 建议单卡 |
|---|---|---|---|
| 6B | FP16 | 13~15G | 16G |
| 6B | INT8 | 9~11G | 12G |
| 6B | INT4 | 6~8G | 8G 以上 |
为什么实际占用比权重多?推理过程中要保存每一层的激活值用于反向传播,虽然推理模式不反传,但前向计算的中间张量仍然会留在显存里,序列越长占用越大。这里有个选择顺序:先按 FP16 规划,显存不够再降级量化,而不是一开始就上 int4,量化对回答质量有影响,尤其是长文本生成的场景。
2.2 创建 Python 虚拟环境
不要用系统 Python 全局安装,装坏了没有后悔药。我一般用 conda 创建一个独立环境,Python 版本锁在 3.10 比较稳。3.11 能用但部分依赖还没完全跟上,3.12 目前踩坑概率更高,不建议刚上手就挑战。
conda create -n qa-assistant python=3.10 -y conda activate qa-assistant nvidia-smi先建环境再激活,最后执行nvidia-smi看驱动信息。这个命令输出的右上角会显示驱动支持的 CUDA 版本,比如 12.4,这决定后面 PyTorch 装哪个 CUDA 编译变体。驱动版本是向下兼容的,驱动支持 12.x 时,装 cu118 或 cu121 的 PyTorch 都能跑,前提是 PyTorch 主版本别超出驱动支持范围太多。
2.3 安装 PyTorch 与 transformers
PyTorch 别盲目装最新。之前遇到有人直接pip install torch装到 cu121,结果和机器上的老驱动匹配不上,torch.cuda.is_available()返回 False,模型悄悄掉到 CPU 上跑,速度慢十倍。稳妥做法是锁版本,用 cu118 变体,兼容性最广。
pip install torch==2.1.2 torchvision==0.16.2 --index-url https://download.pytorch.org/whl/cu118 pip install transformers==4.30.2 accelerate sentencepiece protobuf装完后必须做一次检查,确认 CUDA 真的可用:
python -c "import torch; print(torch.__version__, torch.cuda.is_available())"这里把 transformers 钉在 4.30.2 而不是最新版,是有原因的。ChatGLM 系列加载时需要执行仓库里的自定义代码,新版 transformers 调整过内部结构,经常出现config.json里某个字段读取不到的问题,报错信息还特别抽象。4.30.x 是社区验证最充分的版本段,先跑通再升级不迟。sentencepiece是分词器依赖,protobuf是模型配置解析依赖,漏装会在加载 tokenizer 时给出莫名其妙的ModuleNotFoundError,一眼看不出原因。
第 2 章最后再啰嗦一句:装完 torch 后第一件事是看检查命令的输出,第二个值必须为 True。如果输出 False,不要继续往下走,回头重装 cu118 对应版本,否则后面所有报错都会指向一个假象:你以为模型代码有问题,其实是 PyTorch 在 CPU 上默默运行。
3. 模型权重下载:文件放错地方最容易白等三小时
环境齐了,下一步是拿权重。这一步看似简单,翻车率却极高。直接点浏览器下载或git clone都会遇到大文件中断的问题,很多开发者在这上面白等三个小时,最后发现 bin 文件只有一半大小。
3.1 从模型镜像站拉取权重
权重文件默认源在国外,拉取很容易中途断掉,断了还没有续传机制。我一般先把下载服务切到镜像站,再让下载工具带断点续传去拉。
pip install -U huggingface_hub export HF_ENDPOINT=https://hf-mirror.com huggingface-cli download chatglm-6b --local-dir ./models/chatglm-6b --resume-download这里说明一下命令里的参数。export HF_ENDPOINT是把下载服务的地址切到镜像站,不设这个环境变量,脚本会去连默认源,速度可能很慢甚至超时。chatglm-6b是模型在镜像站上的名字,不同镜像命名不完全一致,搜不到就换chatglm2-6b或chatglm3-6b试试。
--local-dir表示直接落到指定目录,不经过缓存目录。好处是文件结构一目了然,坏处是如果中途断了,目录里会有碎片文件,所以要配合--resume-download续传。如果你只是反复实验,不介意文件放哪,可以改用--cache-dir走缓存,由库自己管理文件生命周期,但排查问题时不如--local-dir直观。
为什么不用git clone?模型仓库里有几个 GB 的 LFS 大文件,git 的 LFS 拉取是分块步进式的,中断后很难续传,失败率比huggingface-cli高得多。这个坑我踩过一次就不再碰了。
3.2 目录结构与完整性检查
下载完成后,先看目录结构对不对。ChatGLM 仓库下载下来应该是这样:
models/chatglm-6b/ ├── config.json ├── quantization.py ├── tokenization_chatglm.py ├── modeling_chatglm.py ├── pytorch_model.bin ├── ice_text.model └── tokenizer_config.json这些文件各有各的用途:config.json记录模型结构参数,modeling_chatglm.py和quantization.py是 ChatGLM 自定义的模型代码和量化代码,pytorch_model.bin是核心权重文件,6B FP16 大约 13G,ice_text.model是分词器的 sentencepiece 模型。前面加载时要求trust_remote_code=True,就是为了允许执行目录里的 py 文件。
完整性检查别急着做 md5 全量校验,那个太慢,先看文件大小是否符合预期:
du -sh models/chatglm-6b find models/chatglm-6b -maxdepth 1 -name "*.bin" -exec ls -lh {} \;核心 bin 文件大小如果离预期差几个 G,大概率是上次下载中断留下的残废文件。此时不要想着修补,直接删掉重新拉更省时间。文件不全会出什么错?加载时可能报UnicodeDecodeError或KeyError,提示信息完全不指向存储问题,最容易让人误判成代码 bug。
注意:下载完成后先确认文件完整再进入下一步。否则后面所有报错你都会在模型代码里找原因,最后发现只是 bin 文件少了一半。
4. 启动服务与排查:离线问答的五个典型翻车点
权重就位后,进入加载和服务封装阶段。这一步翻车点集中,我把最常见的五个问题整理成排查清单,每一条都是实际部署中反复出现过的。
4.1 用 transformers 加载模型
加载模型时,关键参数就几个,但每个都值得抠一下:
import torch from transformers import AutoModel, AutoTokenizer model_path = "./models/chatglm-6b" tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True) model = AutoModel.from_pretrained( model_path, trust_remote_code=True, torch_dtype=torch.float16, device_map="auto", ).eval()trust_remote_code=True是必须的,模型目录里的modeling_chatglm.py是自定义代码,不授权执行就没法构建模型结构。torch_dtype=torch.float16把权重转成半精度加载,显存占用直接减半,这是 16G 单卡跑 6B 全量的前提。device_map="auto"让库自动把层分配到可用设备上,单卡机器上它可以省略,但写上更稳妥,多卡机器上它会自动做跨卡分配。
接下来封装一个最简 HTTP 服务,用 FastAPI 比较顺手:
from fastapi import FastAPI from pydantic import BaseModel app = FastAPI() class QueryIn(BaseModel): query: str @app.post("/chat") async def chat(query_in: QueryIn): response, _ = model.chat(tokenizer, query_in.query) return {"response": response}model.chat是 ChatGLM 自带的方法,传入 query 和可选的 history,返回(response, history)元组。这里不维护会话历史,先用单轮问答验证链路。启动命令要特别注意监听地址:
uvicorn chat_server:app --host 0.0.0.0 --port 8000--host 0.0.0.0表示监听所有网卡,内网其他机器才能访问到这个服务。如果只在部署机上本机访问,可以省略这个参数,但既然做私有化问答助理,通常是要给内网用户调的。
4.2 常见问题与排查
实际部署中,以下五个问题占了绝大部分故障场景。
现象一:加载模型时进程被直接杀掉,终端提示 Killed 或 OOM。
原因:全量 FP16 权重加上推理中间张量超出显存预算,加载过程还有临时缓冲区,峰值比理论值更高。
解决:换成 int4 量化加载。
from transformers import BitsAndBytesConfig quant_config = BitsAndBytesConfig( load_in_4bit=True, bnb_4bit_compute_dtype=torch.float16 ) model = AutoModel.from_pretrained( model_path, trust_remote_code=True, quantization_config=quant_config, device_map="auto", ).eval()这需要额外安装bitsandbytes库,pip install bitsandbytes即可。量化后显存占用降到 6G 左右,12G 卡也能跑。
现象二:AutoModel.from_pretrained报 KeyError,提示 config 里找不到某个字段。
原因:transformers 版本太新,内部把模型结构的读取逻辑改了,读不到旧模型的配置字段。
解决:把 transformers 降到 4.30.2,重装后重启 Python 进程再试。
现象三:服务能起来,但一次回答要几十秒,torch.cuda.is_available()返回 False。
原因:PyTorch 装成了 CPU 版,或者 CUDA 版本与驱动不匹配,模型在 CPU 上硬跑。
解决:确认nvidia-smi里驱动支持的 CUDA 版本,按第 2 章的 cu118 命令重装 PyTorch。查一下 torch 的版本号,如果输出是2.1.2+cpu,基本就是这个原因。
现象四:多卡机器上device_map="auto"报错或者推理反而变慢。
原因:跨卡 offload 时通信开销过大,某些层被分布到不同卡上,同步等待时间比计算时间还长。
解决:用环境变量限定单卡跑:
CUDA_VISIBLE_DEVICES=0 uvicorn chat_server:app --host 0.0.0.0 --port 8000多卡机器上更推荐的做法是一卡一个实例,而不是一实例用多卡。
现象五:服务启动成功,但内网其他机器访问不了。
原因:uvicorn 默认只监听127.0.0.1,没有暴露到局域网,或者防火墙策略挡住了端口。
解决:启动命令带上--host 0.0.0.0,然后在部署机上用curl -v http://127.0.0.1:8000/docs确认服务健康,再用内网其他机器访问。访问不通时,优先检查防火墙和 SELinux 策略。
提示:遇到报错先改环境再改代码。多数 ChatGLM 部署问题不是模型代码的锅,是版本和显存的问题。
5. 验证与压测:三步确认问答助理能交到业务手里
服务起来了不等于能交付。我每次部署完都会走一遍完整验证,三步走完才敢把服务交给业务方。
第一步,验证服务与模型的连通性。用 curl 发一条真实问题:
curl -X POST http://127.0.0.1:8000/chat \ -H "Content-Type: application/json" \ -d '{"query": "介绍一下机器学习"}'正常响应是一个 JSON,response字段里有一段完整的中文回答。如果这里就失败,说明服务封装有问题,回到第 4 章排查。
第二步,看显存占用是否符合预期:
nvidia-smi关注进程列表里 Python 进程的显存占用。FP16 全量 6B 模型应该在 13~15G,int4 量化应该在 6~8G。如果显存占用远低于预期,检查模型是否真的在 GPU 上跑;如果占用过高,检查是否有多个进程加载了模型。
第三步,简单压测。循环发 10 个请求,观察响应时间和稳定性:
for i in $(seq 1 10); do time curl -s -X POST http://127.0.0.1:8000/chat \ -H "Content-Type: application/json" \ -d "{\"query\": \"人工智能的主要应用领域有哪些\"}" done| 指标 | 参考值 |
|---|---|
| 单请求响应时间 | 6B FP16 单卡 16G:1~3 秒 |
| 显存占用波动 | 不超过预算值的 20% |
| 连续请求稳定性 | 10 次内无超时、无进程退出 |
注意第一次请求通常偏慢,因为模型有初始化开销,压测前先发一条预热请求。如果连续请求出现显存持续上涨,多半是历史记录未释放,检查服务端的model.chat是否把 history 越传越长,必要时用history=None或清空逻辑。
从那以后,我每次部署完问答助理,都会强制走一遍这三步验证:连通、看显存、跑压测。不达到预期不放行,宁可多花十分钟确认,也不在业务方反馈「用不了」之后再去翻日志。希望帮到你。
本文还有配套的精品资源,点击获取