首页 / 资讯中心 / 文章详情

ChatGLM本地部署实战:从环境配置到私有化问答系统

ChatGLM本地部署实战:从环境配置到私有化问答系统 ★ FEATURED ARTICLE
简介本地部署私有化智能问答助理示例项目面向对ChatGLM等大模型和机器学习应用感兴趣、希望构建离线知识库问答系统的开发者。项目基于深度学习技术以Gradio搭建交互界面结合FAISS完成大规模向量的相似性检索配合Sentence-Transformers生成文本嵌入实现文件上传、文档解析与自动应答可快速落地客户服务、技术支持、内部知识管理等场景。资源包共18个文件、约336KB包含多个Python主程序脚本、TXT配置与说明文档、YAML部署配置、Shell执行与更新脚本、依赖清单、WHL离线安装包、PEM安全证书以及DOCX/XLSX/PDF示例文档能覆盖从环境准备到运行调优的完整流程。目前已有140人学习下载多版本代码、依赖清单与执行命令便于对照修改和排查问题尤其适合在学习中快速试错和二次开发能帮助读者掌握私有化问答系统的构建思路与完整部署方案。1. 私有化智能问答助理ChatGLM本地部署的第一步怎么迈我手头有一台只有一块显卡的普通工作站要做的是一个只在内网跑的问答助理。数据不能出网络云端API基本不用考虑剩下的路就是本地部署一个开源模型。这个示例项目的核心是把ChatGLM这一档模型完整拉起来从环境检查、模型权重、量化加载到后端接口和Web问答页一步步串成一条能用的链路。适合两类人一类是刚接触本地私有化部署想先跑通一个最小可用的智能问答助理另一类是已经在做机器学习相关开发想把内部文档问答收回到自己机器上控制的人。它解决的核心问题就一句话让AI问答只发生在内网里数据不过外网回答质量还够看。如果你是后者直接翻到第三章开始搭环境就行。2. 选型与部署架构为什么私有化用ChatGLM、模型跑在什么机器上做私有化智能问答第一步不是写代码而是想清楚选型。云端大模型API用起来确实省事但一旦落到“私有化”三个字很多常规选择就失效了。这一章先把选型逻辑讲透再给出一套最小架构和资源预估后面所有命令都跑在这套决策之上。2.1 云端API与本地模型的取舍三个硬条件云端API调用方便、回答质量高但一个内部工具把每条提问都送到外部服务大多数团队接受不了。我在动手前会先问三个问题数据能不能出内网外部服务出现波动时工具是不是直接瘫痪高频调用之下成本怎么算这三个问题里只要有一个答案是否定的落到本地部署基本就是唯一选择。用一张表格把云端API和本地模型的关键差异摊开看会比较直观对比维度云端API本地部署模型网络依赖必须能访问外部服务完全不依赖外部网络数据出网内容会传到外部服务不出内网单次成本按调用量计费高频成本高一次硬件投入后续无调用费离线可用服务挂了就不可用内网独立运行回答质量整体较高模型档位更高受本机模型档位限制硬件门槛没有额外硬件要求需要一台带GPU的机器这个对比做完结论基本清楚只要对数据边界敏感或者调用频次高到按量计费肉疼本地部署就是更划算的路。选型上ChatGLM这一档开源的模型权重比较适中transformers接口对新手友好量化方案也成熟是本地部署入门最顺手的起点之一。后面就算换更大参数量的模型整个部署骨架不会变只是资源估算要重新做。2.2 最小可用的架构模型服务、后端接口与前端页面这个示例项目采用的是一套三层的极简架构。模型推理层负责加载ChatGLM权重、接收文本并生成回复接口层用FastAPI把模型推理包成HTTP服务前端层是一个简单的Web页面负责收集用户输入和展示回答。各层职责和选型如下层职责组件模型推理层加载权重、执行对话生成ChatGLM transformers后端接口层暴露HTTP接口、管理并发请求FastAPI uvicorn前端页面层用户输入、展示回答HTML JavaScript三层跑在同一台机器上数据和请求都在内网走模型权重加载一次后常驻内存后端接口进程与模型进程放一起避免重复加载带来的资源浪费。这个设计谈不上复杂但足够把一个私有化问答助理从零跑通。先跑通链路再谈优化是这个项目最核心的推进方式。2.3 显存、内存与量化档位动手前的资源估算动手之前先算账。ChatGLM这一档模型的原始权重在FP16精度下约占12GB磁盘空间推理时显存占用大概在14GB左右普通显卡扛不住。于是量化档位的选择直接决定了你要准备什么机器。以下是经验参考值实际数值会随上下文长度浮动加载精度显存占用参考适用场景FP16约14GB显卡显存充足追求回答质量int8约8GB显存中等质量和资源较平衡int4约6GB显存吃紧先把链路跑通内存建议16GB起步硬盘预留20GB左右。没有独立显卡也能用CPU跑但回答速度会降到每秒几个字体验很打折扣。我一般不建议零GPU新手直接上FP16显存一溢出就容易一脸懵。最省心的路线是先按int4把整条链路跑通之后资源有余量再往上调精度。3. 动手前的环境准备把GPU、驱动、Python依赖一次配齐环境问题在这个项目里占了一半的翻车率。很多人在权重加载阶段报错回头查才发现是CUDA没配对、Python版本太新、tokenizer文件不完整。这一章把环境准备做成一次能通过的操作清单照着做能把后续排查成本压到最低。3.1 GPU与CUDA检查先跑一条命令确认能不能用拿到机器先别急着装依赖跑三条命令确认底子# 检查显卡是否被系统识别以及驱动和显存大小 nvidia-smi # 查看CUDA编译版本确认与后续安装的torch匹配 nvcc --version # 确认Python版本推荐3.10或3.11 python --version命令背后要看的不是它有没有输出而是输出里的关键值。nvidia-smi能看到驱动版本、显存总量和当前占用显存量直接决定你能用FP16还是int4。nvcc --version输出的是CUDA Toolkit版本后文安装torch时要按这个版本选对应的安装命令。如果nvcc找不到不代表不能用GPU驱动装好且torch能识别即可只是排查时少一个信息源。Python版本我建议直接锁定在3.10。torch对3.10的预编译wheel最全避免为Python 3.12等新版本去等兼容包或者自己编译白白浪费时间。3.2 创建conda环境与安装依赖版本锁定的必要性环境隔离是这类部署必备的习惯。模型项目里的依赖版本敏感直接装在系统Python里日后其他项目一升级依赖这个项目就会被拖垮。# 创建独立环境避免污染系统Python conda create -n rag_assistant python3.10 -y # 激活环境 conda activate rag_assistant # 安装深度学习与推理相关依赖 pip install torch --index-url https://download.pytorch.org/whl/cu118 pip install transformers accelerate sentencepiece protobuf pip install fastapi uvicorn其中torch的安装命令需要和你本机CUDA版本对应上面示例是CUDA 11.8的索引地址如果你的环境是其他CUDA版本去PyTorch官网生成对应的安装命令替换即可。transformers负责加载模型和tokenizeraccelerate负责显存自动分配sentencepiece和protobuf是ChatGLM这类中文模型依赖的分词组件不能省。装完依赖后我建议把核心版本记到一个requirements.txt里torch2.0 transformers4.30,4.45 accelerate0.20 sentencepiece0.1.99 protobuf3.20 fastapi0.100 uvicorn0.23transformers的版本范围特别说一下。ChatGLM加载依赖trust_remote_codeTrue来执行模型自带的远程代码而transformers新版本对旧自定义代码的兼容性有变更版本太新或太老都可能触发加载报错。把版本锁在这个区间内是本地部署这类模型比较稳妥的做法。3.3 模型权重目录结构下载后的校验与软链接权重文件准备好了组织方式也不能乱。这个示例项目按三层目录来划分模型权重、后端服务、前端页面分开管理# 规划目录结构 mkdir -p models/chatglm-6b mkdir -p server mkdir -p web # 模型权重存放在其他磁盘时用软链接避免重复占用空间 # ln -s /mnt/data/models/chatglm-6b models/chatglm-6b从公开模型仓库把权重拿下来后先检查目录下是否有这些关键文件config.json、tokenization_chatglm.py、tokenizer.model、pytorch_model.bin或分片权重文件。tokenizer.model是中文分词关键缺了它会出现回答乱码的怪问题这一点后面排查章节会单独讲。磁盘空间也要顺手确认一下。模型权重解压后通常超过10GB如果所在分区剩余不足加载到一半会报磁盘写入错误。我习惯在解压后马上跑一遍du -sh models/chatglm-6b确认实际大小和预期相符再进入下一步。4. 跑通第一条问答链路模型加载、量化启动与接口验证环境就绪后进入核心环节把模型加载起来封装成接口再通过页面访问到。这个过程是“黑匣子”最集中的一段每一步都要留一个验证手段确认服务真正可用了再往下一层走。4.1 启动模型推理服务从命令行到FastAPI接口把模型推理包成一个HTTP服务常见做法是用FastAPI写一个推理脚本启动后常驻内存。下面是最小可用的服务端代码# server/chat_server.py from fastapi import FastAPI from pydantic import BaseModel from transformers import AutoModel, AutoTokenizer # 加载tokenizertrust_remote_codeTrue是执行ChatGLM自定义代码的开关 tokenizer AutoTokenizer.from_pretrained( models/chatglm-6b, trust_remote_codeTrue ) # 加载模型device_mapauto让transformers自动分配显存 model AutoModel.from_pretrained( models/chatglm-6b, trust_remote_codeTrue, device_mapauto ).eval() app FastAPI() class Query(BaseModel): prompt: str history: list [] app.post(/chat) def chat(query: Query): # model.chat()内部完成了多轮历史拼接和模板处理 response, new_history model.chat( tokenizer, query.prompt, historyquery.history ) return {response: response, history: new_history}这段代码有几个参数要特别解释。trust_remote_codeTrue是ChatGLM特有要求模型目录里的自定义模型文件会被transformers执行没有这个开关会直接报加载错误。device_mapauto是accelerate提供的自动分配机制单卡或多卡环境下都能把模型的各层分到显存中省去手动指定设备的麻烦。.eval()把模型切到推理模式避免训练模式下额外的内存开销。启动服务# 在server目录下执行0.0.0.0允许内网其他机器访问 uvicorn chat_server:app --host 0.0.0.0 --port 8000--host 0.0.0.0表示监听所有网卡接口这样内网其他机器可以通过服务器IP访问如果只在本机测试改成127.0.0.1即可。--port 8000是服务端口被占用时换成其他端口。首次启动会加载权重耗时取决于磁盘读取速度几秒到几十秒不等。日志停在一行没有任何报错不代表死掉了可能还在读模型文件。这时候最忌讳反复CtrlC重启耐心等日志出现Application startup complete再操作。4.2 接入Web问答界面请求参数与返回结构服务端接口就位后需要一个前端页面把问答交互搭起来。这里用一个极简的静态页面演示请求和响应的结构!-- web/index.html -- !DOCTYPE html html head meta charsetutf-8 title本地问答/title /head body textarea idinput rows3 placeholder输入你的问题/textarea button onclickask()发送/button div idanswer/div script async function ask() { const prompt document.getElementById(input).value; const res await fetch(http://服务器IP:8000/chat, { method: POST, headers: {Content-Type: application/json}, body: JSON.stringify({prompt: prompt, history: []}) }); const data await res.json(); document.getElementById(answer).innerText data.response; } /script /body /html这段页面里fetch把用户输入以JSON格式POST到后端/chat接口后端处理完返回{response: ..., history: [...]}页面把回答渲染到answer容器。请求体里的history字段是对话历史数组传空数组表示首轮提问传上次接口返回的history则可以进行多轮对话。注意服务器IP要替换成启动服务那台机器的实际内网IP不能写localhost。页面是在浏览器里跑的浏览器所在的机器可能和服务端不是同一台写localhost会请求到浏览器本机必然失败。4.3 首轮验证用curl和一段测试脚本确认服务可用页面写好后先不急着打开用curl做一次最直接的接口验证# 在能访问到服务器的机器上执行 curl -X POST http://127.0.0.1:8000/chat \ -H Content-Type: application/json \ -d {prompt: 你好请介绍你自己, history: []}如果服务正常会返回一段包含response字段的JSON。这一步能同时验证服务是否起来、模型是否加载完成、接口参数是否匹配。curl通了再打开页面curl都不通页面调不通是必然的别把时间花在浪费前端排查上。接口通了以后我建议再跑一个批量测试脚本把几个固定问题一次性打进去确认回答内容不是复读机import requests questions [ 什么是机器学习, 用一句话解释人工智能, 写一句产品欢迎语, ] for q in questions: r requests.post( http://127.0.0.1:8000/chat, json{prompt: q, history: []}, timeout120, ) text r.json()[response] print(f问{q}\n答{text}\n)这个脚本的作用是冒烟测试确认模型在不同类型问题上都有正常输出。timeout120是因为首次推理时模型的预填充会比较慢尤其是量化模型短超时会把正常请求误判为失败。回答里如果出现明显跑题或英文混杂先记下来这类问题会在第五章给出排查方向。5. 常见问题排查显存溢出、中文乱码、接口超时的处理记录复现这类项目几乎不可能一次走通。这一章按我实际踩过的顺序记录四个高频问题每条都是“现象、原因、解决”三步走。你照着前面的步骤操作如果服务没起来或者回答不对大概率落在这四个坑里。5.1 现象一加载模型就报CUDA out of memory现象执行模型加载代码后终端抛出CUDA out of memory模型进程直接退出。原因显存不足以容纳当前精度下的模型权重。FP16权重在单卡显存不到14GB的机器上很容易触发尤其后台还有其他进程占着显存时情况更严重。解决先把后台无关的显存进程清掉再用nvidia-smi确认可用显存。如果可用显存仍然不够把加载精度降到int4这是最直接的后悔药。示例项目里建议的量化加载方式如下from transformers import AutoModel, AutoTokenizer from transformers import BitsAndBytesConfig import torch # int4量化加载bitsandbytes库需要提前安装 quant_config BitsAndBytesConfig( load_in_4bitTrue, bnb_4bit_compute_dtypetorch.float16, ) tokenizer AutoTokenizer.from_pretrained( models/chatglm-6b, trust_remote_codeTrue ) model AutoModel.from_pretrained( models/chatglm-6b, trust_remote_codeTrue, quantization_configquant_config, device_mapauto ).eval()这里bitsandbytes是量化加载的底层库安装缺失会直接报ModuleNotFoundError。量化后显存占用会明显下降但回答质量也会有肉眼可见的波动这是精度换资源的必然代价。注意显存溢出不只是加载阶段会发生回答很长的问题时也可能在生成中途爆显存因为生成长文本需要缓存更多中间状态。5.2 现象二回答全是英文或中文乱码现象模型推理正常但输出文本夹杂大量英文或者中文变成“口口”样式的乱码。原因tokenizer相关文件不完整最常见的是tokenizer.model缺失或损坏。ChatGLM这类中文模型依赖专用的词表文件完成中文分词文件不在模型就只能退回到通用token的切分方式中文表现自然崩。解决检查模型目录下的关键文件是否齐全# 查看模型目录文件重点确认tokenizer相关文件 ls -lh models/chatglm-6b/重点看tokenizer.model和tokenization_chatglm.py是否存在。缺失就从原模型仓库重新下载这两个文件补回去。另外不要手动编辑config.json里的auto_map字段这个字段指向模型自定义代码文件一旦改错加载时连错误信息都会变得很隐晦远不如改回原样省事。5.3 现象三前端请求一直转圈直到超时现象浏览器里点击发送按钮后页面一直pending几十秒后报网络错误。原因这个现象通常有三个来源。一是后端服务根本没起来curl进接口都失败二是前端fetch地址写成了localhost而页面和模型不在同一台机器三是FastAPI没有配置CORS浏览器在跨源请求时直接拦截了响应前端拿不到结果。解决按顺序排查不要跳步。先在服务器本机执行一次curl确认服务可用再检查前端代码里的地址是否替换成了实际服务器IP最后确认CORS配置。FastAPI最简单的跨域处理如下from fastapi.middleware.cors import CORSMiddleware # 放行所有来源适合纯内网Demo阶段 app.add_middleware( CORSMiddleware, allow_origins[*], allow_methods[*], allow_headers[*], )这个配置加到app FastAPI()之后即可。allow_origins[*]表示允许任何来源访问在隔离内网里问题不大但如果有安全要求应该改成具体的前端来源地址别图省事一直放开。5.4 现象四int4量化后回答质量明显下降现象换上int4量化后模型能跑通但回答明显变得逻辑断裂甚至出现自相矛盾的句子。原因量化精度损失在低比特下更明显特别是生成长文本时更容易跑偏。这不是代码bug而是资源与质量的固有矛盾。解决如果机器的显存有余量优先上int8或FP16。资源没法变的时候从生成参数上补救把temperature调低到0.2以下top_p控制在0.7左右减少随机性。ChatGLM推理时的采样参数默认偏发散降低这两个值能让输出更贴近训练时的分布中心。提示量化这个环节有一定的玄学成分。同一套int4配置换一个话题方向质量浮动可能很大。判断质量不要靠一两个问题的印象要用固定的测试集对比。6. 进阶与验证用提示词模板与检索增强把问答从能用变好用链路跑通后下一步是把回答质量从“demo水平”拉到“内部能用”。两个手段性价比最高提示词模板让模型知道自己的边界检索增强解决它瞎编资料的问题。6.1 提示词模板把问答限定在业务边界内私有化问答最常见的失控场景是模型回答超范围用户问内部资料里没有的东西它也能编出一套答案。改进方式是给每轮提问拼上一段固定的提示词system_prompt 你是一个内部问答助手。 回答时只基于给定资料不要编造。 如果资料里没有相关内容直接回答“资料中未找到”。 请使用中文回答。 def build_full_prompt(query: str) - str: return f{system_prompt}\n问题{query}把拼好的build_full_prompt(query)作为prompt传给模型接口而不是把用户原始输入直接丢进去。实际操作中temperature设置在0.1到0.3之间top_p保持0.7左右回答会更收敛减少发散式生成。6.2 检索增强与生成参数回答正确率和可控性的调整方向内部文档问答里直接让模型凭训练记忆回答很多内容它没学过。常见做法是把相关资料切片后放进prompt让模型基于上下文作答def build_prompt_with_docs(query: str, docs: list) - str: context \n.join(docs[:3]) return f{system_prompt}\n相关资料{context}\n问题{query}这里的docs可以从内部文档里按关键词切出来排序取最相关的前几段拼进去。加资料之后模型瞎编的概率会明显下降代价是输入变长、回答速度变慢需要在上线前实测延迟是否可接受。从那以后我每次换量化档位或者换模型权重都会先把同一组十条测试问题跑一遍记录耗时时长和回答是否自洽再决定要不要上线。这成了我部署私有化问答的习惯。希望帮到你。整套部署脚本和配置整理成了可直接复现的示例项目按这篇文章的步骤从环境到接口两小时能跑通基础版本。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站