ChatGLM3-6B+LangChain+Faiss搭建企业知识库问答系统实战

发布时间:2026/10/19 6:41:34
ChatGLM3-6B+LangChain+Faiss搭建企业知识库问答系统实战 如果你正在企业内部文档的泥潭里挣扎应该能体会这种痛苦关键手册翻了半天找不到新同事入职培训全凭口口相传知识库建了好几年最后变成一座无人问津的资料坟场。解决这个问题其实不用等到什么大型平台落地一台普通显卡机器用ChatGLM3-6B加LangChain和Faiss就能搭出一套实用的企业内部知识库问答系统让沉淀的资料真正能被问出来。这篇文章记录的就是我实际搭建这套系统的完整过程从选型思路、原理分析到代码实现、踩坑实录都会讲到。适合有一定Python基础、想在本地或公司内网部署私有大模型问答系统的开发者参考。整套方案的硬件成本可以压到一张16G显存的显卡数据完全不出内网对知识敏感的企业场景尤其合适。1. 整体方案与选型思路三个组件如何分工1.1 为什么不做微调而选RAG企业内部知识库这个需求第一反应往往面临两条路微调大模型或者做检索增强RAG。很多人一开始都会纠结是不是把公司文档拿去微调一下模型就学会了我在评估时发现微调这条路的坑比想象中深得多。微调的本质是改变模型的权重让它记住训练时见过的内容。但企业内部文档有几个特点数量持续增长、内容频繁更新、不同部门权限不同。每次文档更新都要重新训练成本极高而且微调后的模型仍然可能把多个来源的信息混在一起回答时你很难定位它根据哪份文档说的这对企业内部需要严谨溯源的使用场景是个大问题。所以我最终选了RAG方案它的核心思路是不为模型注入知识而是让模型在回答时查资料。RAG的优势非常直观文档更新只需重建向量索引不用动模型回答可以注明依据来自哪份文档不同部门可以挂载不同的知识库。三个优势直接命中企业知识管理的痛点。补充一个现实成本对比。全量微调一个7B参数级别的模型哪怕用LoRA这样的高效微调方法也需要可靠的训练环境和大量调参时间。而RAG方案把主要工作集中在文档清洗、切分策略和提示词调优上这些工作在普通开发机上就能完成。对于把现有文档变成可问答的知识库这个目标RAG的性价比明显更高。1.2 三个核心组件各做什么方案定了之后就是选型。技术栈定的是标题里的三件套每个组件承担的职责完全不同ChatGLM3-6B负责读和写也就是理解用户问题、阅读检索到的资料、组织自然语言回答。它是整个系统的输出大脑。选择它不是因为参数最大而是因为中文理解和生成质量在6B级别里表现扎实对硬件要求也现实——FP16精度约需14G显存4bit量化后6G就能跑。LangChain负责串联提供标准化的文档加载、文本分割、向量化、检索、提示词拼装、模型调用等组件把一条完整的问答链路串起来。没有它你需要自己写一堆胶水代码去管理prompt模板和调用顺序。Faiss负责检索。文档向量化之后需要一个高效检索库来存向量、算相似度。Faiss是Meta开源的向量检索库轻量、成熟、本地就能跑不像Elasticsearch或Milvus那样需要单独部署一套服务对企业内部小规模知识库非常友好。选择LangChain而不自己从头写还有一个原因LangChain生态里的文档加载器覆盖了PDF、Word、Markdown、TXT等企业最常见的格式同时它对模型调用做了统一抽象今天你想换Qwen或其他模型只需要替换封装层问答链的代码可以几乎不动。1.3 一次完整问答的链路再把这套系统的运行链路拉出来看会更加直观用户提问 → 把问题进行向量化 → 在Faiss索引里检索出最相关的K个文档片段 → 将用户问题 文档片段组装成带约束的prompt → 交给ChatGLM3-6B生成回答 → 返回结果并同时给出参考来源这里面没有花哨的步骤但每一步都值得推敲。比如把问题进行向量化这里用到的向量化组件必须和建立索引时用的是同一个模型否则检索结果会完全跑偏。这种细节在实操部分还会反复出现。整条链路里检索质量直接决定回答质量而生成模型决定回答的自然度和逻辑性——两头都重要缺一不可。2. 核心原理与参数选择决定检索质量的关键项2.1 RAG把闭卷考试变成开卷考试理解RAG最简单的方式是把它想象成一场开卷考试。普通大模型问答是闭卷考试模型靠训练时记住的知识来作答遇到训练数据里没有的内容只能硬编。RAG则不同它先帮模型把相关参考资料翻出来再让模型照着资料作答。那怎么翻资料呢核心是向量化加相似度计算。把一大段文字压缩成一组数字也就是embedding向量这组向量可以理解为文字在高维空间里的坐标。语义相近的两句话坐标的距离就近语义无关的内容坐标就离得远。用户提问时把问题也变成向量在知识库里找距离最近的若干文档片段就是翻资料的过程。这个过程对用户是完全透明的。你问公司的年假制度是什么系统先检索到《人事管理制度.docx》中关于年假的那一段然后把这段文字和问题一起喂给ChatGLM3-6B模型基于这段资料来回答。因为参考内容放在prompt里模型不需要背制度内容所以回答的依据是实时检索出来的最新版本。这里有一个容易忽略的要点最终回答的效果很大程度上取决于你给模型看的资料质量。如果你的文档切片切得词不达意或者检索出来的片段根本牛头不对马嘴再强的生成模型也不可能凭空答好。这就像开卷考试时如果连资料页都没翻对就算让学霸来考他也只能交白卷。2.2 嵌入模型选型与显存安排嵌入模型就是把文本转成向量的模型它在系统里虽然不起眼却是检索质量的地基。我实测对比过几个中文嵌入模型最终锁定了BAAI北京智源开源的bge系列中文模型理由是中文语义理解在开源模型里属于第一梯队且对硬件非常友好。代码里的指定方式非常简单from langchain_community.embeddings import HuggingFaceEmbeddings embedding_model HuggingFaceEmbeddings( model_nameBAAI/bge-large-zh-v1.5, devicecpu, encode_kwargs{normalize_embeddings: True}, )这里有两个我踩过坑的细节。第一device参数的安排。经验不足时容易把嵌入模型和ChatGLM都塞进GPU结果显存直接爆掉后来我把嵌入模型放到CPU上推理效果基本不受影响显存压力却小了很多。bge-large模型在CPU上向量化一段文档也只要几百毫秒做知识库索引完全可接受只有大量文档初次建库时耗时较长顺手把索引存到本地就好了。第二normalize_embeddings默认是关闭的。打开之后向量会被归一化这样用余弦相似度检索时结果更稳定建议保持开启。如果机器资源更紧张也可以选BAAI/bge-small-zh-v1.5向量维度从1024降到512检索速度更快精度损失在可接受范围内。反过来如果追求更高精度并且显存充足可以考虑bge-m3等更大模型。我的建议是先跑通流程再追求精度用small版本起步确认效果后再往大了换只需要重新建一次索引就能看到差异。2.3 文档切分参数chunk_size和overlap怎么定如果说嵌入模型决定了检索能力的上限那么文档切分就决定了这个上限能不能兑现。切分就是把长文档拆成合适大小的片段这个环节的设定直接影响检索精准度。先看两个极端。切得太小比如每个片段只有一两句话检索结果虽然精准但往往缺乏上下文模型看到一截没头没尾的中间段落理解不了完整含义回答就会片面。切得太大比如把整本手册当一个片段检索出来的内容又臭又长里面大部分信息和问题无关模型容易被噪声干扰回答也变得啰嗦。这两种情况我都实测过。推荐的参数是chunk_size500到800字overlap50到100字。chunk_size是每个片段的目标长度中文场景下相当于一两个段落overlap是相邻片段之间重叠的部分作用是保住段落边界的上下文连续性——如果一个关键信息恰好被切断在前后两个片段之间重叠部分可以让它至少完整地出现在某一个片段里。具体实现使用的是LangChain里的递归字符分割器from langchain.text_splitter import RecursiveCharacterTextSplitter text_splitter RecursiveCharacterTextSplitter( chunk_size600, chunk_overlap80, separators[\n\n, \n, 。, , , , , , ], )这个separators列表特别值得说。默认的分隔符是按英文习惯排的对中文文档并不友好。我加上了中文的句号、感叹号、问号、分号和逗号这样切分时会优先在完整句子边界切断而不是在词语中间硬切。实测下来回答的连贯性有明显提升。2.4 检索TopK与相似度阈值文档切好、向量建好之后还需要决定每次问答检索多少个片段喂给模型这个参数是k值。k值太小比如1或2漏召回的概率大增问题问到文档里一个不显眼的位置就可能找不到答案k值太大比如8或10模型会面临大量低相关内容的干扰回答反而容易被带偏。我实测下来k4左右是平衡性最好的选择。企业文档问答场景里大部分问题的答案来源相对集中4个片段足够覆盖答案所在的核心段落同时有少量空间容纳相关背景信息。还要提一个Faiss检索的隐藏问题k值只管返回数量不管返回质量。也就是说即使知识库里没有任何相关内容系统照样会返回4个最接近但实际不相关的片段ChatGLM拿到这些垃圾上下文后依然会硬着头皮编答案。这是RAG系统最常见的幻觉来源之一。解决办法是引入相似度阈值过滤Faiss检索时可以要求只接受相似度大于某个阈值的片段如果所有片段都低于阈值就告诉用户未找到相关信息。这个阈值需要反复试通常在0.5到0.7之间具体取决于嵌入模型和文档类型。3. 实操全流程一步一步跑通企业内部知识库3.1 环境准备机器配置与依赖安装先说明硬件参考。我自己用的是一张16G显存的显卡ChatGLM3-6B以4bit量化加载约需7G显存给计算图留出余量后跑得很稳。如果你的显卡有24G显存可以直接上FP16精度回答质量还会再高一点要是只有8G显存用4bit量化也能跑但要关闭嵌入模型的GPU占用。软件环境方面Python用3.10或3.11都可以。建议创建独立虚拟环境避免相互污染。下面是依赖清单直接保存为requirements.txttransformers4.44.0 accelerate bitsandbytes torch2.1.0 langchain0.2.0 langchain-community langchain-huggingface faiss-cpu sentence-transformers pypdf unstructured安装命令pip install -r requirements.txt需要注意一个版本坑langchain在0.1之后经历了较大的模块拆分部分组件被移动到了langchain-community和langchain-huggingface包里。如果你在导入HuggingFaceEmbeddings或FAISS时报错找不到模块基本都是因为缺了这两个依赖包补装即可。3.2 加载ChatGLM3-6B并封装成LangChain LLM模型加载是整个流程的核心环节。ChatGLM3-6B官方模型路径是THUDM/chatglm3-6b通过HuggingFace Transformers加载需要设置trust_remote_codeTrue因为模型使用了自定义代码。下面是我实际使用的加载方式import torch from transformers import AutoModel, AutoTokenizer, BitsAndBytesConfig, pipeline from langchain_huggingface import HuggingFacePipeline quantization_config BitsAndBytesConfig( load_in_4bitTrue, bnb_4bit_compute_dtypetorch.float16, bnb_4bit_quant_typenf4, ) tokenizer AutoTokenizer.from_pretrained( THUDM/chatglm3-6b, trust_remote_codeTrue ) model AutoModel.from_pretrained( THUDM/chatglm3-6b, quantization_configquantization_config, trust_remote_codeTrue, device_mapauto, ) model model.eval()然后通过transformers的pipeline接口封装成LangChain能直接调用的LLM对象pipe pipeline( text-generation, modelmodel, tokenizertokenizer, max_new_tokens512, temperature0.3, top_p0.9, repetition_penalty1.05, ) llm HuggingFacePipeline(pipelinepipe)这里几个参数值得单独说明。temperature0.3是我多次测试后觉得最合适的温度回答严谨、不容易跑偏又保留了一定的自然表达。如果做创意类内容生成可以调高到0.7以上但知识库问答必须偏保守。max_new_tokens512控制回答长度企业制度类问题一般200字内就能答完512够用。repetition_penalty1.05用来抑制模型开始绕圈子重复这是ChatGLM系列在长回答时的一个常见毛病。3.3 文档载入、切分与向量化文档加载是企业知识库最琐碎也最不能跳过的一步。LangChain的DirectoryLoader配合UnstructuredFileLoader可以自动识别目录下的PDF、Word、Markdown等格式import os from langchain_community.document_loaders import DirectoryLoader, UnstructuredFileLoader base_dir ./docs # 分别加载不同格式 pdf_loader DirectoryLoader( base_dir, glob**/*.pdf, loader_clsUnstructuredFileLoader ) word_loader DirectoryLoader( base_dir, glob**/*.docx, loader_clsUnstructuredFileLoader ) md_loader DirectoryLoader( base_dir, glob**/*.md, loader_clsUnstructuredFileLoader ) documents pdf_loader.load() word_loader.load() md_loader.load()加载进来之后我强烈建议先做一轮文档体检。实际操作里企业内部文档的格式远比想象中混乱扫描版PDF提取出来是空文本、Word里嵌了图片表格导致内容缺失、文档头部页眉页脚混入正文。这些脏数据如果不处理后面建出来的知识库就是垃圾进垃圾出。我一般先用documents[0].page_content[:200]打印前几份文档的内容肉眼看一眼提取质量再决定要不要换更专业的解析工具。切分和向量化就直接使用之前定好参数的text_splitter和embedding_modeltexts text_splitter.split_documents(documents) print(f切分后片段数: {len(texts)})3.4 构建Faiss向量库并落地保存加载切分完成后就可以把片段转成向量、写入Faiss索引了。这一步最简单代码只有几行from langchain_community.vectorstores import FAISS vectorstore FAISS.from_documents(texts, embedding_model) vectorstore.save_local(./faiss_index)建库过程如果资料多会明显耗时尤其是嵌入模型跑在CPU上时几百份文档可能要花几十分钟。所以保存到本地非常关键以后新增文档时只需要把新文档加载、切分后追加到已有索引即可不用每次都从零重建# 加载已有索引并追加新文档 vectorstore FAISS.load_local( ./faiss_index, embedding_model, allow_dangerous_deserializationTrue ) new_texts text_splitter.split_documents(new_documents) vectorstore.add_documents(new_texts) vectorstore.save_local(./faiss_index)这里有一个安全提示FAISS.load_local在LangChain 0.2版本里打开本地文件需要显式设置allow_dangerous_deserializationTrue。这个参数本身没有危险但如果你的faiss索引文件来源不可信加载前要谨慎。企业内部使用时索引都是自己生成的问题不大。3.5 组装问答链并做一轮真实测试向量库就绪后最后一步是组装问答链。这里我建议直接使用RetrievalQA它把检索 拼装prompt 调用模型生成整套流程封装好了from langchain.chains import RetrievalQA from langchain.prompts import PromptTemplate prompt_template 你是一位企业内部知识助手请严格基于下面的资料内容回答用户问题。 资料内容 {context} 用户问题 {question} 回答要求 1. 优先使用资料内容作答答案尽量引用资料中的原文依据 2. 如果资料中没有相关内容请直接回答“未在现有资料中找到相关信息” 3. 不要编造资料之外的任何信息 4. 使用中文输出回答简洁准确 答案 prompt PromptTemplate( templateprompt_template, input_variables[context, question], ) qa_chain RetrievalQA.from_chain_type( llmllm, chain_typestuff, retrievervectorstore.as_retriever( search_kwargs{k: 4} ), chain_type_kwargs{prompt: prompt}, return_source_documentsTrue, )chain_typestuff表示把检索到的全部片段一次性拼进prompt里交给模型。这种方式在片段不多时效果最好因为模型能看到完整的上下文如果检索出的片段很多甚至超过了模型输入上限才需要换成map_reduce或refine这类复杂策略。return_source_documentsTrue可以拿到检索到的原始文档方便输出引用来源这个对企业场景很重要。跑一个真实测试question 新员工入职第一天的流程是什么 result qa_chain({query: question}) print(回答:, result[result]) print(参考资料:, [doc.metadata.get(source) for doc in result[source_documents]])我在实测中用一份包含人事制度和入职手册的文档语料提问系统能准确返回入职流程的步骤并且明确告诉用户依据来自哪份文档。当知识库里完全没有相关内容时模型也能遵守prompt里的约束回答未在现有资料中找到相关信息而不是强行编造。这一点是通过prompt约束解决RAG幻觉的关键效果。4. 常见问题与排查技巧实际跑起来的血泪经验4.1 显存不足一族的自救办法如果你在加载模型时遇到CUDA out of memory不要慌按照下面的顺序逐个排查。第一步确认你已经启用了4bit量化代码里BitsAndBytesConfig是否生效有时bitsandbytes库与CUDA版本不匹配会静默降级导致量化没有真正启用用import bitsandbytes试一下能正常加载就行。第二步关掉嵌入模型的GPU占用把HuggingFaceEmbeddings的device参数改为cpu。第三步缩短max_new_tokens生成阶段会在显存里缓冲输出序列512降到256能省出一块空间。第四步如果还不行清理一下机器上的其他进程看看有没有别的工作占用显存。还有一个被很多人忽略的做法device_mapauto配合accelerate库可以让模型的某些层自动放入CPU或GPU实现层级别的显存负载均衡。代价是推理速度会下降一点但至少能跑起来。如果连这种模式都跑不动那就只有换更大的显卡或者换更小的模型版本了。4.2 回答胡说八道或幻觉过高怎么办模型回答内容完全不在知识库里是RAG系统遇到最多的问题。排查时先从prompt入手确认你有没有在提示词里强制约束根据资料回答和资料中没有就直说。很多人以为模型自然会这么做实测下来如果不约束大模型在资料支持不足时依然倾向于编造一个看似合理的答案。其次是调整检索参数。把k值从默认的4提到6或8可能让正确答案进入候选项同时提高检索阈值过滤那些相似度很低但被硬塞进来的无关片段。搜索结果太弱和太杂都可能诱发幻觉。更进阶的方案是给检索结果加一道重排序流程。Faiss拿到的是向量层面的相似度这个排序有时会跟用户的真实意图不一致。我试过接入bge-reranker这种专门的重排小模型把Faiss检索出的20个候选再精排一次取前4个作为最终上下文回答准确率提升非常明显。代价是检索链路会慢几百毫秒但知识库问答对延迟没那么敏感值得尝试。最后如果你对某个领域的回答频繁不准优先检查文档本身。比如制度类文档如果版本混乱、新旧混存系统就很容易检索到过期信息。企业知识库上线前做一次文档版本清理是性价比最高的优化。4.3 检索结果不相关怎么调检索效果差往往不是模型的问题而是切分或向量化环节的问题。排查顺序从里到外先用vectorstore.similarity_search(question, k5)直接打印检索到的片段肉眼判断这些片段和问题是否相关。如果不相关问题出在检索源头。第一检查separators是否包含中文字符如果没加句号等中文分隔符一个600字的片段可能是从句子中间硬切的语义自然混乱。第二检查chunk_size如果设置得太大比如1500一个片段里塞了太多话题相似度会被稀释。第三检查嵌入模型是否选对bge系列比一些通用的多语言模型在中文场景下要敏感得多。第四检查文档内容本身是不是可搜索的文本扫描版PDF提取出来的可能是一堆乱码或空行这类文档需要先做OCR再入知识库。另有一个实战技巧把检索阈值从严格逐步放宽。先把k设为5不设阈值根据返回片段的相似度分数分布来定合理阈值这样比凭感觉设0.6可靠得多。4.4 生成速度慢到不能忍怎么办ChatGLM3-6B在消费级显卡上4bit量化后生成速度大约是每秒8到12个token一个100字的回答需要等待约两秒。如果嫌慢几个优化方向。第一确认你没有把模型跑在CPU上。如果没接device_mapauto模型可能默认就在CPU推理速度会慢到不可接受。第二使用flash attention加速AutoModel.from_pretrained时传torch_dtypetorch.float16并在配置里打开attn_implementationflash_attention_2即可对A卡和部分N卡速度提升明显。第三如果在线问答响应速度仍然低于预期可以把服务拆分文档索引离线批量更新在线推理只做检索和生成。我也试过用vLLM这类推理加速框架来部署模型吞吐量确实大幅提升但配置复杂度明显增加单机并发请求不高时性价比一般。如果后续要做成多用户在线服务再考虑引入不迟。4.5 上线部署前要做的事如果能跑通本地demo下一步自然是给团队用。这个阶段有几个现实问题必须处理。第一API服务化。直接用脚本交互肯定不行我用FastAPI把问答逻辑包了一层HTTP接口调用方传一个问题返回回答和参考来源列表。第二权限控制。企业内部文档往往分保密级别Faiss索引本身不区分权限需要在上层逻辑里对检索范围做隔离比如不同部门挂不同的知识库子索引。第三操作审计。知识库问答应该记录谁在什么时间问了什么问题返回了什么内容出现争议时能追溯。这些工作没有体现在demo里但属于企业知识库能用和合格地使用的分界线。在给内部推广之前建议至少把权限隔离和日志记录做了。5. 写在最后一点实际体会整套系统跑通之后我的感受是技术链路本身并不复杂LangChain和Faiss把工程细节封装得很好真正花时间的地方反而在文档清洗、切分参数调优和prompt打磨这些看起来不重要的环节上。这几个环节没有一个标准答案只能拿着自己企业的真实文档反复测试、迭代。这块内容后续还可以扩展的方向一个是给检索链路加入重排序模型提升精准度另一个是把文档更新流程做成自动化管线再有就是针对不同部门做知识库的权限隔离。如果你也在搭企业知识库建议从一个小范围的场景切入——比如只做人事制度或者技术手册——先把检索质量和回答体验打磨顺再逐步扩大知识库范围。千万不要一上来就想把全公司的文档一次性灌进去那会让索引质量失控排查起来也极其痛苦。个人经验是一次跑出正确结果比先跑通再回头调试要难得多但先跑通再回头调试是必经之路。希望这套方案的细节能帮你少踩几个坑。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询