通义千问1.5-1.8B-Chat-GPTQ-Int4集成实战:构建内网知识问答Agent

张开发
2026/6/6 6:23:50 15 分钟阅读
通义千问1.5-1.8B-Chat-GPTQ-Int4集成实战:构建内网知识问答Agent
通义千问1.5-1.8B-Chat-GPTQ-Int4集成实战构建内网知识问答Agent最近和几个在企业做IT的朋友聊天他们都在头疼同一个问题公司内部有海量的技术文档、产品手册、规章制度新员工来了找不到老员工也记不全。想用现在流行的AI问答工具吧又担心数据安全问题毕竟把内部文档传到外部服务器上谁心里都没底。这不正好看到通义千问开源了1.5-1.8B这个尺寸的模型还提供了量化版本我就琢磨着能不能用它来搭一个完全跑在自己服务器上的问答助手。折腾了几天还真跑通了效果比预想的好。今天就把这个从零开始的搭建过程还有踩过的坑跟大家详细聊聊。1. 为什么要在内网搭一个自己的问答助手你可能要问现在在线AI工具这么多为什么非要自己折腾一个本地部署的这还真不是技术人员的“洁癖”而是实实在在的业务需求。首先最核心的就是数据安全。企业的技术方案、客户信息、内部流程这些都是商业机密。把这些文档喂给在线的AI服务就相当于把自家保险箱的钥匙交给了别人保管风险太高。本地部署意味着所有数据都在自己的防火墙内从文档处理到模型推理全流程可控。其次是网络环境的限制。很多研发、生产环境是物理隔离的压根连不上外网。这时候再强大的在线AI也是“巧妇难为无米之炊”。本地化部署就解决了这个根本问题有没有外网都能用。再者是回答的精准性和可控性。通用大模型虽然知识面广但对企业内部那些特有的缩写、产品代号、历史流程它可能一无所知容易“胡说八道”。而我们构建的系统它的知识完全来源于我们喂给它的内部文档回答有据可查不会天马行空。最后从成本角度看通义千问1.5-1.8B这个尺寸的模型经过GPTQ-Int4量化后对硬件的要求已经非常亲民了。一台普通的带GPU的服务器甚至配置高一些的台式机就能跑起来长期使用的边际成本几乎为零。所以构建一个内网知识问答Agent不是一个“炫技”项目而是一个能直接提升信息检索效率、保障数据安全、降低沟通成本的实用工具。2. 技术方案选型为什么是通义千问RAG要解决“从海量内部文档中找答案”的问题我们得把任务拆解一下。第一步得让机器能“读懂”我们的文档第二步得能从读过的内容里“找到”相关部分第三步得能组织语言“回答”问题。对应的技术方案现在主流就是RAG检索增强生成。简单理解它就像一个配备了超级秘书的专家。秘书检索系统负责从档案室向量知识库里快速找到可能与问题相关的文件递给专家大语言模型专家基于这些文件结合自己的理解给出一个精准、可靠的回答。在这个架构里我们为“专家”这个角色选择了通义千问1.5-1.8B-Chat-GPTQ-Int4模型主要基于以下几点考虑尺寸与性能的平衡1.8B参数对于企业内部问答场景来说是一个甜点尺寸。它足够理解复杂的句子结构和业务逻辑同时推理速度够快资源消耗也在可接受范围内。对话能力优化-Chat后缀意味着这个版本专门针对多轮对话进行了优化。我们的问答助手很可能需要和用户连续交流比如追问细节、澄清问题这个特性至关重要。量化带来的部署便利-GPTQ-Int4表示模型权重被量化到了4位整数。这带来的最大好处就是显存占用大幅降低。原本可能需要8GB以上显存的模型现在4GB甚至更少就能跑起来部署门槛大大降低响应速度也更快。优秀的指令遵循能力在实际测试中这个尺寸的通义千问模型在理解“请根据给定文档回答”这类指令时表现不错能较好地约束自己不在文档之外胡编乱造。而对于“秘书”的角色我们选择用文本向量化模型把文档切成片段并转换成向量存入向量数据库比如Chroma、Milvus。当用户提问时先把问题也转换成向量去数据库里找出最相似的几个文档片段把这些片段和问题一起交给通义千问模型让它生成最终答案。整个流程可以概括为文档处理 - 向量化存储 - 问題检索 - 增强生成。3. 实战第一步环境准备与模型部署好了理论说再多不如动手做一遍。我们从头开始假设你有一台安装了Ubuntu 20.04/22.04的服务器并且有一张至少4GB显存的NVIDIA显卡。3.1 基础环境搭建首先通过SSH连接到你的服务器。我们使用Python 3.8和pip来管理环境。# 更新系统包 sudo apt-get update sudo apt-get upgrade -y # 安装Python3和pip如果尚未安装 sudo apt-get install python3-pip -y # 安装CUDA Toolkit以11.7为例请根据你的显卡驱动选择对应版本 # 具体安装步骤请参考NVIDIA官方文档这里假设已安装好CUDA环境。 # 创建一个独立的Python虚拟环境避免包冲突 python3 -m venv qwen_agent_env source qwen_agent_env/bin/activate3.2 安装核心依赖库接下来安装我们需要的Python库。这里会用到transformers来加载模型torch作为深度学习框架sentence-transformers来生成文本向量chromadb作为向量数据库还有langchain来帮我们简化流程编排。# 安装PyTorch请根据你的CUDA版本到PyTorch官网获取对应安装命令 # 例如对于CUDA 11.7 pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu117 # 安装其他核心依赖 pip install transformers accelerate sentence-transformers chromadb langchainaccelerate库可以帮助我们更高效地加载大模型。sentence-transformers提供了现成的、好用的文本向量化模型。3.3 下载与加载通义千问量化模型模型我们可以从Hugging Face社区获取。因为模型已经量化好了我们直接用transformers库加载就行。# 这是一个示例代码片段用于测试模型是否能正常加载和进行简单推理 from transformers import AutoModelForCausalLM, AutoTokenizer import torch # 指定模型路径可以是Hugging Face模型ID也可以是本地路径 model_name Qwen/Qwen1.5-1.8B-Chat-GPTQ-Int4 # 加载tokenizer和模型 tokenizer AutoTokenizer.from_pretrained(model_name) model AutoModelForCausalLM.from_pretrained( model_name, torch_dtypetorch.float16, # 使用半精度以节省显存 device_mapauto, # 自动分配模型层到可用的设备GPU/CPU trust_remote_codeTrue # 信任来自源的代码 ) # 将模型设置为评估模式 model.eval() # 准备一个简单的对话测试 messages [ {role: system, content: 你是一个乐于助人的助手。}, {role: user, content: 你好请介绍一下你自己。} ] # 应用聊天模板并生成 text tokenizer.apply_chat_template(messages, tokenizeFalse, add_generation_promptTrue) inputs tokenizer(text, return_tensorspt).to(model.device) with torch.no_grad(): outputs model.generate(**inputs, max_new_tokens100) response tokenizer.decode(outputs[0], skip_special_tokensTrue) print(模型回复, response.split(assistant\n)[-1].strip()) # 提取助手回复部分运行这段代码如果能看到模型生成的自我介绍恭喜你最核心的推理引擎已经就位了。第一次运行会下载模型文件需要一些时间。4. 构建企业知识库从文档到向量模型准备好了接下来要给它“喂”知识。我们假设企业内部文档都是PDF、Word或TXT格式存放在某个目录下。4.1 文档读取与预处理我们使用langchain的文档加载器来读取多种格式的文件并进行必要的清洗和分割。from langchain.document_loaders import DirectoryLoader, TextLoader from langchain.text_splitter import RecursiveCharacterTextSplitter # 1. 加载文档 # 假设你的文档都在 ./internal_docs 目录下 documents [] loader DirectoryLoader(./internal_docs, glob**/*.txt, loader_clsTextLoader) # 可以添加更多loader来处理PDF、Word等例如使用 UnstructuredFileLoader # from langchain.document_loaders import UnstructuredFileLoader # loader_pdf DirectoryLoader(./internal_docs, glob**/*.pdf, loader_clsUnstructuredFileLoader) loaded_docs loader.load() documents.extend(loaded_docs) print(f共加载了 {len(documents)} 个文档) # 2. 分割文本 # 将长文档切分成适合检索的小片段chunks text_splitter RecursiveCharacterTextSplitter( chunk_size500, # 每个片段的字符数 chunk_overlap50, # 片段之间的重叠字符保持上下文连贯 separators[\n\n, \n, 。, , , , , 、, , ] ) chunks text_splitter.split_documents(documents) print(f文档被分割成 {len(chunks)} 个文本片段)4.2 向量化与存储现在我们把文本片段转换成向量并存入向量数据库。这里选用sentence-transformers中的paraphrase-multilingual-MiniLM-L12-v2模型它体积小、速度快且支持中文非常适合这个场景。from langchain.embeddings import HuggingFaceEmbeddings from langchain.vectorstores import Chroma # 1. 初始化嵌入模型 embedding_model HuggingFaceEmbeddings( model_namesentence-transformers/paraphrase-multilingual-MiniLM-L12-v2, model_kwargs{device: cuda}, # 如果GPU可用可以加速编码 encode_kwargs{normalize_embeddings: True} # 归一化向量便于相似度计算 ) # 2. 创建向量数据库 # 将文本片段转换为向量并持久化存储 vector_db Chroma.from_documents( documentschunks, embeddingembedding_model, persist_directory./chroma_db # 指定本地存储目录 ) vector_db.persist() # 保存到磁盘 print(向量知识库已构建并保存至 ./chroma_db)这样你的企业内部知识就以向量的形式存储好了。./chroma_db目录下包含了所有数据下次启动可以直接加载无需重新处理文档。5. 组装问答Agent检索与生成的协同知识库有了模型也有了现在要把它们连接起来形成一个完整的问答流水线。5.1 构建检索链当用户提出一个问题时我们首先从向量库中检索出最相关的几个文档片段。# 加载已存在的向量数据库 vector_db Chroma( persist_directory./chroma_db, embedding_functionembedding_model ) # 定义一个检索函数 def retrieve_docs(question, k3): 检索与问题最相关的k个文档片段 relevant_docs vector_db.similarity_search(question, kk) # 将检索到的文档内容合并成一个上下文字符串 context \n\n.join([doc.page_content for doc in relevant_docs]) return context # 测试检索功能 test_question 公司的年假制度是怎样的 context retrieve_docs(test_question, k2) print(检索到的上下文) print(context[:500]) # 打印前500个字符5.2 设计提示词模板为了让通义千问模型更好地利用检索到的上下文我们需要设计一个清晰的提示词模板。这是RAG效果好坏的关键。def build_prompt(question, context): prompt_template f你是一个专业的企业知识问答助手。请严格根据以下提供的背景信息来回答问题。如果背景信息中没有明确答案请直接说“根据现有资料我无法回答这个问题”不要编造信息。 相关背景信息 {context} 问题{question} 请根据以上信息回答 return prompt_template # 组装完整的提示词 full_prompt build_prompt(test_question, context) print(完整的模型输入提示词示例) print(full_prompt[:800])5.3 集成生成与对话最后我们将检索、提示词构建和模型生成整合到一个函数中并封装一个简单的对话循环。def ask_agent(question, chat_history[]): 问答Agent的核心函数 # 1. 检索 context retrieve_docs(question, k3) # 2. 构建包含历史对话和当前问题的消息列表 messages [] # 可以加入系统提示 messages.append({role: system, content: 你是一个严谨的企业知识助手必须依据给定资料回答。}) # 加入历史对话简化处理仅将历史作为上下文 for hist in chat_history[-3:]: # 只保留最近3轮历史 messages.append(hist) # 加入当前问题和检索到的上下文 user_message build_prompt(question, context) messages.append({role: user, content: user_message}) # 3. 应用聊天模板并生成 text tokenizer.apply_chat_template(messages, tokenizeFalse, add_generation_promptTrue) inputs tokenizer(text, return_tensorspt).to(model.device) with torch.no_grad(): outputs model.generate(**inputs, max_new_tokens300, do_sampleTrue, temperature0.7) full_response tokenizer.decode(outputs[0], skip_special_tokensTrue) # 4. 提取本轮助手的回复 # 这里需要根据具体的对话模板格式进行解析以下为示例逻辑 try: # 假设回复部分在最后一个“assistant”标签后 response_sections full_response.split(assistant\n) if len(response_sections) 1: answer response_sections[-1].strip() else: answer full_response.split(根据以上信息回答)[-1].strip() except: answer 抱歉回答解析出现错误。 # 5. 更新对话历史 chat_history.append({role: user, content: question}) chat_history.append({role: assistant, content: answer}) return answer, chat_history # 模拟一个简单的对话 history [] q1 新员工入职需要准备哪些材料 ans1, history ask_agent(q1, history) print(fQ: {q1}) print(fA: {ans1}\n) q2 其中体检报告有什么要求 ans2, history ask_agent(q2, history) print(fQ: {q2}) print(fA: {ans2})6. 封装与部署提供API服务为了让其他内部系统如OA、内部IM也能调用这个问答助手我们需要将它封装成一个HTTP API服务。这里使用轻量级的FastAPI。# 文件api_server.py from fastapi import FastAPI, HTTPException from pydantic import BaseModel import uvicorn # 复用之前定义好的模型、向量库和ask_agent函数 # 注意这里需要将模型、tokenizer、vector_db等作为全局变量或应用状态加载 app FastAPI(title企业内部知识问答API) class QuestionRequest(BaseModel): question: str user_id: str default # 可用于区分不同用户的对话历史 # 用一个简单的字典在内存中维护不同用户的对话历史生产环境建议用Redis等 user_histories {} app.post(/ask) async def ask_question(request: QuestionRequest): try: # 获取或初始化该用户的对话历史 history user_histories.get(request.user_id, []) # 调用Agent获取答案 answer, updated_history ask_agent(request.question, history) # 更新存储的历史 user_histories[request.user_id] updated_history[-6:] # 只保留最近3轮对话 return {answer: answer, success: True} except Exception as e: raise HTTPException(status_code500, detailf服务器内部错误: {str(e)}) app.get(/health) async def health_check(): return {status: healthy} if __name__ __main__: # 在启动应用前确保模型和向量库已加载完毕 print(正在加载模型和知识库...) # 这里应该放置模型和向量库的初始化代码 print(服务启动中...) uvicorn.run(app, host0.0.0.0, port8000)运行这个API服务后其他系统就可以通过向http://你的服务器IP:8000/ask发送POST请求JSON格式{question: 你的问题, user_id: zhangsan}来获取答案了。7. 效果评估与优化建议搭建完成后我用自己的技术文档做了测试。对于事实清晰、文档中有明确描述的问题比如“项目部署的端口号是多少”Agent基本能准确回答并指出来源片段的大致位置虽然目前代码没显式返回出处但可以通过修改检索函数实现。对于需要总结归纳的问题比如“简述项目上线流程”如果文档中有流程章节它也能较好地概括出来。当然它也有局限性如果问题非常模糊或者答案分散在多个不连续的文档中效果会打折扣。根据实际体验这里有几个优化方向供你参考检索优化可以尝试不同的文本分割策略按章节、按段落或者使用更先进的检索器比如同时使用关键词检索BM25和向量检索进行混合搜索提高召回率。提示词工程进一步优化提示词模板明确要求模型在答案中引用来源或者对不确定的内容进行标注。模型微调如果企业有大量高质量的问答对可以考虑用这些数据对通义千问模型进行轻量级的微调LoRA让它更适应你所在行业的语言风格和知识结构。缓存机制对于常见问题可以将问答结果缓存起来下次直接返回极大提升响应速度。前端界面可以基于这个API开发一个简单的Web界面让非技术人员也能方便地使用。整个项目从环境准备到API上线如果文档量适中大概一两天就能搭出雏形。最难的部分可能不是编码而是前期的文档整理和清洗。不过一旦跑通这个系统就能7x24小时地为你的团队提供随叫随到的知识查询服务长期来看是非常划算的。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。

更多文章