ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

DeepSeek本地化部署实战:用Ollama与RAG搭建内网知识库

DeepSeek本地化部署实战:用Ollama与RAG搭建内网知识库 简介一份系统讲解DeepSeek本地化部署与RAG实战的PDF教程面向希望将开源大模型落地到本地、构建私域知识库的技术人员。教程从DeepSeek-R1的获取安装讲起覆盖LM Studio、HuggingFace和魔搭社区三种下载途径并给出从1.5B到671B不同参数模型的推荐与最低硬件配置方便按算力选型。随后重点对比微调、岗前培训与RAG三条路线说明各自适用场景与成本差异再详细拆解基于RAG搭建知识库的完整流程包括创建知识库、导入语料、创建助手并关联大模型配合AnythingLLM、RAGFlow、QAnything等工具落地。资源为单个PDF文件约4.91MB含大量配置表格、目录结构示意与实操步骤适合入门到进阶用户按需查阅。目前已有344人学习可作为本地部署RAG应用的速查手册并快速复现案例。1. 把 DeepSeek 装进内网本地知识库到底解决什么问题做 DeepSeek 本地化部署的团队绝大多数不是冲着“省钱”去的而是有一条硬约束数据不能出内网。要么是公司制度要求要么是项目交付时客户明确讲了“文档不许上传到任何外部接口”。这时候云端 API 再便宜也没用唯一的路就是自己在内网把模型跑起来再用 RAG 把私有文档喂给模型。很多人以为难点在“装模型”实际上模型下载下来就能用真正决定项目能不能交付的是后面接的本地知识库——文档怎么切、向量怎么存、检索怎么召回这些才是全项目里最耗时间的地方。这篇笔记沿着“部署 → 建知识库 → 调检索 → 踩坑”的顺序写用的是 Ollama 加 LangChain 加 ChromaDB 这套最常见的组合不需要 GPU 集群一台 16G 内存的普通工作站就能起步。适合三种人要做内部知识库问答的运维和开发、刚接手大模型本地化项目的交付工程师、以及想在公司内网搭一套私有 AI 助手的同学。看完你能从零跑通一个能回答自己文档问题的完整链路也能避开我当初翻车翻出来的几个大坑。2. DeepSeek 本地化部署先想清楚推理工具再动手2.1 三条部署路线选错了后面全难受本地把 DeepSeek 跑起来常见做法有 Ollama、vLLM、llama.cpp 三条路线。很多人第一步就在这纠结其实这三条路的分工很清楚Ollama 主打零配置上手装完就能命令行聊天vLLM 主打高吞吐和连续运行适合多人在线、频繁调用的场景llama.cpp 则是为 CPU 和贫寒硬件准备的量化支持最细。我的建议是做 RAG 知识库的初期验证直接选 Ollama。理由是知识库项目里大部分算力开销在向量化和检索模型服务本身只要稳定、调用简单就够了。Ollama 一条命令就能拉模型、起服务还自带兼容 OpenAI 的 HTTP 接口LangChain 里直接配base_url就能用省掉一堆折腾。vLLM 也值得一提等你的知识库用户超过几十个人、并发请求上来之后再迁过去不迟。vLLM 对显存的管理比 Ollama 激进支持 continuous batching同样一张 4090 上能承载的并发量明显更高。但 vLLM 的安装和依赖环境坑不少新手上来就直接上 vLLM 容易在环境阶段就失去耐心。llama.cpp 适合 CPU 机器。如果你的机器没有像样的 N 卡或者公司只给你一台 32G 内存的老服务器llama.cpp 配合 GGUF 量化模型就是唯一能跑的路线。代价是推理速度慢单次问答可能要等十几秒但对问答型知识库来说这个延迟勉强能接受。2.2 用 Ollama 跑起 DeepSeek 的最小命令安装 Ollama 本身不复杂Linux 上一条命令Windows 和 macOS 有安装包。装完先确认服务状态然后拉模型。# 安装 OllamaLinux curl -fsSL https://ollama.com/install.sh | sh # 启动服务默认监听 11434 端口 systemctl start ollama systemctl status ollama # 拉取 DeepSeek-R1 的 7B 量化版适合 16G 内存的机器 ollama pull deepseek-r1:7b # 测试推理 ollama run deepseek-r1:7b 介绍一下检索增强生成 RAG 的核心流程这里有几个参数需要解释。deepseek-r1:7b是模型标签7b表示 70 亿参数版本是在效果和资源占用之间比较平衡的选择如果你只有 8G 内存建议用deepseek-r1:1.5b虽然回答质量会明显下降但至少能跑如果有 24G 以上显存的 GPU可以直接上deepseek-r1:14b效果会有一个肉眼可见的提升。拉完模型后Ollama 会常驻一个 HTTP 服务在 11434 端口。可以用下面这条命令验证接口通不通curl http://localhost:11434/v1/chat/completions \ -H Content-Type: application/json \ -d { model: deepseek-r1:7b, messages: [{role: user, content: 你好请做个自我介绍}] }看到正常的 JSON 返回说明模型服务这块就绪了。注意这里用的是/v1/chat/completions路径这是 Ollama 为了兼容 OpenAI SDK 专门提供的LangChain 和很多现成工具可以直接填这个地址不用额外写适配层。提示Ollama 模型默认存放在~/.ollama/models目录如果系统盘不够大可以在启动前设置OLLAMA_MODELS/data/ollama环境变量把模型目录指到数据盘。这个一定要提前想等模型拉下来再搬目录会很痛苦。2.3 本地推理的硬件底线与量化选择DeepSeek 本地化部署里最容易被低估的是内存带宽。很多人以为模型能不能跑只看显存和内存大小其实推理速度还受内存带宽限制。实测中同样跑 7B 模型在 DDR4 和 DDR5 机器上的生成速度能差两倍以上。如果你的机器内存带宽捉襟见肘尽量选低量化等级比如 Q4_K_M 而不是 Q8能明显减少每次生成时读取的数据量。量化等级的选择也有规律。Q4_K_M 是最常用的一档效果损失小体积只有原始模型的一半左右Q8 效果几乎无损但体积和内存占用高出不少Q2 和 Q3 不推荐回答质量下降得厉害容易出现逻辑颠三倒四的情况。Ollama 拉取默认就是 Q4_K_M这点对新手很友好。还有一个容易被忽略的点Ollama 默认会尽量把模型加载到 GPU 显存里VRAM 不够的部分用 CPU 内存补。这个自动混合模式对用户体验好但要注意观察日志。如果发现模型全跑在 CPU 上导致速度极慢多半是显存不够且没触发混合模式可以检查一下ollama ps的输出看看模型加载在哪个设备上。3. RAG 架构拆解为什么直接问 DeepSeek 不靠谱非要搭一层知识库3.1 模型记不住你的私有文档这不是参数问题本地把 DeepSeek 跑通之后很多人第一反应是直接开个网页对话框然后把公司制度文档一字不落贴进去问。这个做法在短文档上还凑合但文档一长模型就开始“断片”——不是它笨是上下文窗口就这么大塞不进去。7B 模型的上下文窗口一般是 8K 到 32K token折算成中文大概几千到两万多字。你贴一篇 50 页的制度文件进去模型根本读不完更别提从中检索准确答案了。这就引出 RAG 的核心逻辑不把整篇文档塞给模型而是先把文档拆成小块提前做索引等用户提问时只把和问题相关的几个小块拿出来连同问题一起发给模型。这样每个请求只有几百到几千 token模型能专注地基于给定材料作答既绕开了上下文长度限制又保证了答案来自你的私域文档而不是模型自带的“通识记忆”。RAG 处理不了的问题也顺带说清楚如果要模型学会一种全新的写作风格或者要对某个领域有深度推理能力那是微调的事RAG 做不到。RAG 解决的是“从一堆文档里找到依据并回答”这件事它是知识库问答的正确姿势但不是万能药。3.2 RAG 的四个阶段每个阶段都是一道坎一个完整的本地知识库 RAG 链路拆开来是四段加载解析、文本切分、向量化入库、检索生成。第一段是读文档PDF、Word、Markdown 各有各的解析库PDF 里还有扫描件和文字版的区分第二段是把长文本切成小块切大了检索粒度太粗切小了上下文不连贯第三段是给每块文本算一个向量表示存进向量数据库第四段是用户提问时先算问题的向量再到库里搜最相近的几条文本最后拼成 prompt 发给 DeepSeek。很多人理解 RAG 只关注了第四段——检索和生成但实战中前三段反而更影响效果。我搭过好几个知识库最深的感受是解析和切分决定了检索质量的上限向量模型决定了检索质量的下限。如果文档解析出来是乱码后续全白搭如果切分把一段完整的技术说明拦腰截断后面再怎么调检索也找不回那段断掉的逻辑。3.3 用一段伪代码理解 RAG 全流程这里给出一个简化版的 RAG 主流程方便把上面的概念落到实处。这不是完整可跑的代码是让你看清每个环节的输入输出。# 阶段一加载文档把 PDF/Word 变成纯文本 from langchain_community.document_loaders import PyPDFLoader loader PyPDFLoader(employee_handbook.pdf) documents loader.load() # 每页是一个 Document # 阶段二文本切分按语义块切成小块 from langchain.text_splitter import RecursiveCharacterTextSplitter splitter RecursiveCharacterTextSplitter( chunk_size500, # 每块最大字符数 chunk_overlap100, # 相邻块保留 100 字重叠避免切断语义 separators[\n\n, \n, 。, , ] ) chunks splitter.split_documents(documents) # 阶段三向量化入库每个 chunk 转成一个向量 from langchain_community.embeddings import OllamaEmbeddings from langchain_community.vectorstores import Chroma embeddings OllamaEmbeddings(modelbge-m3) # 中文场景推荐 vectorstore Chroma.from_documents( documentschunks, embeddingembeddings, persist_directory./employee_handbook_db ) # 阶段四检索 生成找到关联块组装 prompt 发给 LLM question 新员工的试用期是多久 retrieved_docs vectorstore.similarity_search(question, k4) context \n.join([doc.page_content for doc in retrieved_docs]) prompt f请根据以下资料回答问题\n{context}\n\n问题{question}这段代码里要注意两个参数。chunk_size500是按字符数算的中文场景下 500 字大约能覆盖 95% 的独立知识点这个值不是越大越好越大检索越粗越小每块信息越单薄。chunk_overlap100则是在相邻块之间保留 100 字的重叠区防止一个完整句子在切分处被截断。向量化用的是bge-m3这是 BAAI 开源的中文嵌入模型在中文文本上的检索效果比 OpenAI 的text-embedding-ada-002还要好而且能直接在 Ollama 里拉取使用。如果你处理的是纯英文文档可以考虑nomic-embed-text这类英文专用嵌入模型但我做过的项目里中文场景基本都锁 bge 系列。4. 搭建本地知识库从零到能答问题的完整实操4.1 准备嵌入模型bge-m3 与 Ollama 的结合方式向量化需要的是嵌入模型这和对话用的 DeepSeek 是两套模型。很多人一开始会误以为用同一个模型既能对话又能做嵌入实际上 LLM 和嵌入模型是分开部署的各司其职。# 拉取中文嵌入模型 bge-m3约 1.2GB ollama pull bge-m3 # 验证嵌入模型是否可用 ollama run bge-m3 测试这句话的向量嵌入模型拉取后不会显示在对话界面里它是通过 API 调用的。在 LangChain 侧OllamaEmbeddings(modelbge-m3)就是调用本地 11434 端口的嵌入接口整个过程不涉及外部网络请求数据完全在内网流转。如果你所在的环境没法直接访问公共模型仓库还有一条离线兜底路径找一台能联网的机器把模型用ollama pull拉下来后在~/.ollama/models/blobs目录里找到对应的权重文件用优盘或内网传输工具拷到目标机器放到相同路径并修改 manifest 文件即可。这条路麻烦但在涉密环境下是唯一选择。4.2 写一个知识库管理脚本入库、检索、问答三合一下面这个脚本是一个最简但完整的知识库管理工具实现了文档入库、检索测试、问答三个功能。建一个knowledge_base.py文件把这段代码粘进去就能跑。import argparse from pathlib import Path from langchain_community.document_loaders import DirectoryLoader, PyPDFLoader, TextLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_community.embeddings import OllamaEmbeddings from langchain_community.vectorstores import Chroma from langchain_community.chat_models import ChatOllama EMBED_MODEL bge-m3 LLM_MODEL deepseek-r1:7b PERSIST_DIR ./kb_chroma_db DOCS_DIR ./docs def build_kb(): 入库扫描 docs 目录下的所有文档切分后写入向量库 loader DirectoryLoader( DOCS_DIR, glob**/*.pdf, loader_clsPyPDFLoader, show_progressTrue ) documents loader.load() splitter RecursiveCharacterTextSplitter( chunk_size500, chunk_overlap80, separators[\n\n, \n, 。, , ] ) chunks splitter.split_documents(documents) embeddings OllamaEmbeddings(modelEMBED_MODEL) vectorstore Chroma.from_documents( chunks, embeddings, persist_directoryPERSIST_DIR ) vectorstore.persist() print(f已入库 {len(chunks)} 个文本块) def query_kb(question: str, k: int 4): 检索并回答搜索相关块交给 DeepSeek 生成答案 embeddings OllamaEmbeddings(modelEMBED_MODEL) vectorstore Chroma( persist_directoryPERSIST_DIR, embedding_functionembeddings ) docs vectorstore.similarity_search(question, kk) context \n\n.join([d.page_content for d in docs]) llm ChatOllama(modelLLM_MODEL, temperature0.3) prompt f你是企业内部知识库助手请严格根据提供的资料回答问题。 如果资料中没有相关信息请直接说明“资料中未找到相关内容”。 资料内容 {context} 用户问题{question} response llm.invoke(prompt) print(f答案{response.content}\n) print(参考依据) for i, doc in enumerate(docs, 1): print(f[{i}] {doc.metadata.get(source, )}) if __name__ __main__: parser argparse.ArgumentParser(description本地知识库管理工具) parser.add_argument(--build, actionstore_true, help构建知识库索引) parser.add_argument(--question, typestr, help提问内容) args parser.parse_args() if args.build: build_kb() if args.question: query_kb(args.question)使用方式# 第 1 步把公司文档放进 docs 目录然后构建索引 python knowledge_base.py --build # 第 2 步问一个问题 python knowledge_base.py --question 试用期考核标准是什么代码背后的逻辑DirectoryLoader负责批量扫描目录下的文档这里只匹配了 PDF你也可以在glob参数里加上**/*.txt或**/*.md来收录文本文件split_documents把长文档切块Chroma.from_documents自动完成向量化并落盘到kb_chroma_db目录。查询时直接加载已存在的向量库不再重复处理文档。这里要特别提醒temperature0.3知识库问答属于事实型任务温度设太高会让模型自由发挥出现“编造依据”的问题0.2~0.3 是比较稳的区间。如果是创意写作或头脑风暴场景才需要把温度调到 0.7 以上。similarity_search(question, k4)里的k4表示召回 4 个最相关的文本块。太少可能漏掉关键信息太多会把无关内容混进 prompt 干扰生成。4 到 6 之间是大多数内部知识库的最佳区间。4.3 文档目录设计一个清晰的文件组织方式知识库的文档管理要提前规划目录结构不然跑两个月后索引会乱成一团。我一般这样组织. ├── docs/ # 原始文档目录 │ ├── 人事制度/ │ ├── 技术规范/ │ └── 项目总结/ ├── kb_chroma_db/ # 向量库持久化目录自动生成 ├── logs/ # 入库和查询日志 ├── knowledge_base.py # 主脚本 └── config.yaml # 参数配置每次新增或更新文档不需要重新跑全量入库可以单独对某个子目录重建索引。文档较多时按业务线建子目录是最省事的办法因为DirectoryLoader会保留相对路径作为 metadata查出来能定位到是哪篇文档的哪个部分。4.4 验证知识库效果的三个必测问题建完知识库后别急着上生产先用三类问题验证效果。第一类单点事实题。选一个你确定答案在文档里的问题比如“员工年度体检在哪家医院”答对了说明链路基本通了。第二类跨章节综合题。比如“试用期被辞退有补偿吗”这种问题需要在不同文档里找依据拼起来能测出分块和召回的配合度。第三类文档里没有答案的问题。比如公司制度里根本没提“股票期权”好的知识库应该回答“资料中未找到相关内容”而不是把网上通识知识拿出来说。这三种问题各问三遍基本能暴露 80% 的搭建问题。如果第一种就答不对问题多半出在解析或分块如果第二种答不对多半是k值太小或者向量模型不合适如果第三种答得头头是道说明 prompt 里没有加限制。这些问题后面会专门讲怎么排查。5. 避坑指南本地知识库最容易翻车的五个位置5.1 PDF 解析出来是乱码或空文本我第一次搭知识库就死在这。导入了一份扫描版的制度文件入库时显示成功但问什么都是“资料中未找到相关内容”。排查半天才发现 PDF 解析出的文本全是空字符——那是一个纯图片扫描件PyPDFLoader根本没有 OCR 能力。原因很简单PDF 分两种一种是带文本层的电子版能直接提取文字一种是纯扫描件内容全是图片必须走 OCR 流程。解决方法是给扫描件先过一遍 OCR 工具。常见做法是用paddleocr它能输出带坐标的文本配合fitz按页重新拼装成可读取的 PDF。如果公司文档里扫描件占比不高也可以单独用一把小工具处理# 用 PaddleOCR 把扫描 PDF 转成带文本层的 PDF核心步骤 import fitz from paddleocr import PaddleOCR ocr PaddleOCR(use_angle_clsTrue, langch, show_logFalse) def ocr_pdf(src_path, dst_path): pdf fitz.open(src_path) for page_num in range(len(pdf)): page pdf[page_num] pix page.get_pixmap(dpi220) img_bytes pix.tobytes(png) result ocr.ocr(img_bytes, clsTrue) if result: for line in result: for word in line: if word and len(word) 2: page.insert_text((72, 72), word[1][0]) pdf.save(dst_path)OCR 这步只能保证文本能提取出来识别质量另说。如果文档有大量表格和公式OCR 的文字位置会很乱建议先跑一页试试效果再批量处理。识别不出来的文档调 DPI 到 300 通常能改善但会出现速度成倍下降的问题。5.2 检索召回了一堆不相关内容相关度排序像玄学知识库答非所问的另一个高发区是召回环节。我用默认参数跑过几次用户问“加班调休怎么算”返回的依据里有“年度休假制度”“缺勤处理办法”就是没有“加班管理”那篇。检查后发现是Chroma的默认距离函数在这套嵌入下对中文语义的分辨力不够。解决思路有两个方向。一个是调k值从 4 提到 8看看答案有没有改善另一个更根本——换距离函数。ChromaDB 支持l2、cosine、ip三种相似度度量默认是l2欧氏距离但对 bge 系列的向量用cosine余弦相似度通常更稳定。初始化向量库时显式指定vectorstore Chroma.from_documents( chunks, embeddings, persist_directoryPERSIST_DIR, collection_metadata{hnsw:space: cosine} )改完距离函数后重新跑一次检索测试绝大多数情况下相关度排序都会有明显改善。还有一个小技巧在入库前把文档里的噪声内容清理掉。页眉页脚的页码、公司地址、网页导航栏这些高频无意义片段会污染切分结果让向量被无关文本占住相似度。用正则把这些内容预先过滤掉比调任何参数都管用。5.3 中文标点把文本块切得稀碎RecursiveCharacterTextSplitter的separators参数顺序是有讲究的。它按列表顺序依次尝试切分先按\n\n切再按\n切再按。切。很多人图省事不设separators用默认的英文标点列表结果中文文档切出来的块不是整句结束而是断在逗号或空格处语义被拦腰截断。还有个更隐蔽的坑中文引号“”和英文引号在向量空间里几乎不干扰但在切分器里行为不同。如果你文档里有“他说‘明天来办理’”这种句子默认切分器遇到“不会切遇到会切导致半个句子被切到下一块。正确做法是把中英文标点都放进separators并把中文句号放在英文句号前面避免英文句号把带小数点的序号错误切开splitter RecursiveCharacterTextSplitter( chunk_size500, chunk_overlap80, separators[\n\n, \n, 。, , , , ., , ;, , ,] )如果切分后发现某块文本只有一两个词那基本是切分器在错误位置下刀了。可以在入库前加一行调试代码按chunk.page_content[:50]打印每个块的开头快速看出切分是否合理。5.4 知识库更新后旧内容“阴魂不散”知识库交付后文档是会持续更新的。最典型的翻车场景是你更新了某个制度文件重新入库用户提问时返回的还是旧版本内容。这个问题的根源是Chroma.from_documents每次调用都会往集合里追加写入不会自动覆盖同源文档。解决思路更新文档时先按source元数据把旧块删掉再插入新块。常见做法是# 删除某个来源文档的所有旧块 all_docs vectorstore.get() to_delete [ id_ for id_, source in zip(all_docs[ids], all_docs[metadatas]) if 制度文件_v2.pdf in source[source] ] if to_delete: vectorstore.delete(idsto_delete) # 重新解析并写入新版本 new_chunks load_and_split(制度文件_v2.pdf) vectorstore.add_documents(new_chunks)如果知识库里文档数量多了手动管理会很累我后来直接用文档的 MD5 值做 metadata入库前比对有变化的才处理。另外persist()调用在最新版 Chroma 里被标记了弃用新版本在写入时会自动持久化显式调用反而可能报错如果遇到这个报错把persist()去掉即可。5.5 DeepSeek 生成的内容和提供资料互相矛盾这个问题表面看起来像模型“不听话”其实是 prompt 约束不够强。DeepSeek-R1 的底座是一个通用对话模型默认不会主动限制自己“只用资料回答”。一个很常见的现象是资料里只写了“试用期一个月”但模型回答时补了一句“依据《劳动合同法》可约定不超过六个月”这明显是模型自己联想出来的。解决方法是加强 prompt 中的边界指令并且给模型一个“不知道”的出口。我在前面脚本里用的“如果资料中没有相关信息请直接说明‘资料中未找到相关内容’”就是这个目的。还有更强的做法要求模型在回答末尾列出用到的资料编号这样一旦答错还能顺着线索排查是检索问题还是生成问题。prompt f请严格根据以下资料回答问题禁止使用资料之外的知识。 引用资料时用 [1] [2] 标注并在末尾列出对应资料内容。 资料 [1] {docs[0].page_content} [2] {docs[1].page_content} [3] {docs[2].page_content} [4] {docs[3].page_content} 问题{question} 6. 把知识库调到能上线的状态召回效果评估与检索策略进阶知识库能跑通只是第一步距离“能上线交付”还差一次系统的效果调优。我自己重构知识库项目时做过一个最简单的评估方法准备 20 个用户常见问题每个问题标注好应该命中哪篇文档然后统计检索命中率。这不需要任何 GPU半小时就能跑完但它是后端所有调优动作的基准线。命中率低于 60% 时优先怀疑切分和嵌入模型命中率在 60% 到 80% 之间调整相似度度量和k值命中率超过 80% 但最终回答质量不行问题出在 prompt 或生成参数上。这条排查路径能节省大量“玄学调参”的时间。进一步的检索策略有几种做法如果文档里存在大量“关键词很独特但语义相近词很少”的内容比如产品编号、故障码、零件号可以尝试混合检索把关键词匹配的结果和向量检索的结果做加权融合。常见做法是用BM25跑一遍关键词检索用向量库跑一遍语义检索然后按分数加权合并。# 混合检索示意BM25 关键词召回 向量语义召回 先各取 top N bm25_results get_bm25_hits(question, docs_index, top_k10) vector_results vectorstore.similarity_search(question, k10) # 按 rrf 公式做简单融合取 top 4 作为最终上下文 rrf_scores {} for rank, doc in enumerate(bm25_results): rrf_scores[doc.id] rrf_scores.get(doc.id, 0) 1 / (60 rank) for rank, doc in enumerate(vector_results): rrf_scores[doc.id] rrf_scores.get(doc.id, 0) 1 / (60 rank) final_docs sorted(rrf_scores.items(), keylambda x: -x[1])[:4]RRF 是 Reciprocal Rank Fusion 的缩写60是常用的平滑常数让排序靠前的结果得分更高同时避免某个单一检索器完全主导结果。这个方案不需要引入额外依赖也是我目前在知识库项目里用得最多的检索方式。最后想说一个我自己的教训刚做本地化部署时我花了很多时间在测试不同模型、调整对话温度上结果发现真正影响交付效果的是基础链路——文档清不干净、分块合不合理、检索召不召回得到。有一次我把整套工具推倒重来只做了文档清洗和分块优化检索质量就从“经常答非所问”提升到了“基本可靠”。从那以后我每次都先把评估跑起来再动手调什么参数。这个习惯希望对你也有一点参考价值。希望这篇 DeepSeek 本地化部署和 RAG 搭建本地知识库的实操笔记能帮你在内网里少走几个弯路。本文还有配套的精品资源点击获取
返回列表