你手上的文档不方便传到外部平台,又想让同事用自然语言问它。直接把文件丢给在线对话窗口,一是数据出去了,二是它答不出你公司内部的细节。这篇按「先算资源、再定模型、后接检索、最后调效果」的顺序,把本地 DeepSeek 加知识库这条路拆成能照着做的动作,每一步都告诉你什么情况下该换做法。
先算清你手里的机器能撑到哪一档
搭知识库最容易踩的坑,是先去挑模型、看参数多大,再回头问机器跑不跑得动。正确顺序反过来:先看你那张卡的显存有多少,再倒推它能扛哪个档。显存怎么分、能留多少余量,别凭感觉,去站内的部署资料页和配置测算页按你手里的卡实际跑一遍,那里给的是可交互的换算,不是拍脑袋的结论。
- 先把权重占用切出来显存第一刀给模型权重,参数越多、精度越高,权重吃掉的显存越大。在配置测算页选中你的卡和想跑的档位,看权重那栏占了多少,这就是你的天花板起点。
- 再留上下文和并发的余量上下文越长、同时进来的人越多,KV 缓存和临时缓冲就越大。把预期的最长文档长度和并发数填进测算页,看总占用有没有逼近显存上限。逼近就是没余量,别自己骗自己。
- 最后看检索和重排的占用单卡机器上,向量检索和重排模型往往和生成模型抢同一块显存。把它们单独在测算页里估一遍,两块加起来才是真实占用,不是只看生成模型那一个数。
| 你担心的点 | 实际更该看的 | 去哪核对 |
|---|---|---|
| 参数是不是够大 | 这张卡在目标上下文下能稳住哪个档 | 站内部署资料页按卡实跑 |
| 精度是不是越高越好 | 量化档位对应的质量与显存取舍 | 站内术语库把量化名词对齐 |
| 生成慢不慢 | 检索和重排是否先把显存吃满 | 配置测算页单独估这两步 |
| 还能不能再塞并发 | 留没留下缓冲余量 | 测算页并发那栏的占用 |
量化档位决定同一张卡的上限。很多人一上来就选最低精度,觉得省显存就行,结果知识库答得含糊、老跑偏。合理的做法是先到站内术语分类页把量化相关的名词对齐,搞清楚每一档牺牲的是什么、换来的是多少显存,再决定你愿意拿多少质量换运行空间。术语没对齐就调档,等于闭着眼睛砍模型。
- 先按最高可用精度试一遍,跑不通再往下降一档,逐档试,不要一步到位
- 每降一档都记下:显存省了多少、回答质量掉了没有、检索结果还准不准
- 如果只是偶尔用,别为满负载留显存,按需调用比常驻省得多
- 如果团队多人天天用,余量宁多勿少,卡着上限跑迟早被一次长文档打崩
- 算完留不下余量就别硬跑,退到更小参数档或改成按需调用,取舍标准见 self-host 那篇
模型与运行方式怎么定,别只看名字
很多人在本地搭知识库时,第一步就栽在选模型上:看到名字里带 chat、带 instruct 就拿来当嵌入用,或者反过来。结果就是问一句话,它给你拼一段看着像那么回事、其实跟资料毫无关系的回答。先想清楚你这一步要的是「答问题」还是「找段落」,这两个环节用的是两套完全不同的权重。
| 环节 | 要的模型 | 它负责什么 | 拿错会怎样 |
|---|---|---|---|
| 找段落(检索) | 嵌入模型 | 把问题和资料都压成向量,靠距离找最相关的片段 | 检索结果全是无关段落,答得离谱还查不出原因 |
| 答问题(生成) | 对话/指令模型 | 读检索回来的片段,组织成人话回答 | 能答但只会编,不引用你的资料,等于白搭知识库 |
| 重排(可选) | 重排模型 | 对检索回来的候选再排一次序 | 不加也能跑,加了召回质量更稳,但多一份显存 |
嵌入模型这块,判断标准是「它输出的是向量,不是文字」。判断方法很简单:你调它的时候,返回结果里如果是一串浮点数而不是自然语言句子,那才是嵌入模型。关于嵌入接口的输入输出长什么样,可以到站内嵌入相关页面核对,把自己的调用参数和返回结构对一遍,比背模型名有用。
嵌入模型到底怎么用这里有嵌入接口的调用形态和字段说明,照着对一遍就知道自己有没有拿错模型→运行方式按场景挑,不存在通用最优解
单人试玩和多人共用完全是两码事。一个人用,首字慢一两秒无所谓,显存能省就省;五个人同时问,你关心的是并发上来之后会不会排队、首字延迟会不会飙、显存会不会直接爆。这三个指标必须放一起看,单看任何一个都会选错。
- 先定义你的真实场景写清楚几个人的团队、同时几个人在问、能接受等多久出第一个字。这个基线不写出来,后面选什么都靠感觉。
- 量自己的基线用站内测速工具,按你的真实提问长度和并发数跑一遍,记下首字延迟和显存占用的实际数字,别用别人博客里的数字。
- 换配置再量一次换个量化格式或换个并发上限,同一套问题再跑,把两组数字并排。哪组在你场景下更划算,一眼就能看出来。
- 定下来就别频繁动场景没变就别因为出了新版本就重跑一遍基线,稳定比新鲜值钱。
权重从哪来、能不能这么用,要能核对
「能下载」不等于「能随便用」。至少确认三件事:权重是从哪个仓库拉的、是什么量化格式、许可证允不允许你这种用法。商业闭源的分发、对外提供接口、内部员工用,这几种在许可证里待遇不一样。拿不准的时候别自己猜,去站内对照分类说明把条款过一遍。
- 来源可追溯:记下拉取权重的仓库地址和具体文件,别只留一个「网上下的」
- 量化格式写明:同一份权重,不同量化格式对显存和效果影响不小,选了哪个要记下来
- 许可证逐条看:你的用法是内部用、对外服务还是再分发,对应的限制不同
- 存档留证:把当时的许可证文本和仓库版本一起存下来,换环境时能复现
别追新追到无法复现
版本一换,行为可能全变,昨天调好的检索参数今天就不灵了。稳妥做法是:先固定一套能跑通的组合(模型权重加量化格式加参数),把它当成基线,之后每次想换,都按同一套检查项走一遍,确认没问题再切。
换版本该检查哪些项迁移不是替换文件那么简单,照着检查清单逐项过,避免换完才发现某一步断了→检索这一段才是知识库真正的内容
很多人把本地部署的力气全花在模型加载和显存上,结果上线后发现答得乱七八糟,回头一看是文档根本没处理干净。知识库的效果上限,在检索这一段就被定死了:模型再强,也只能看到你塞进向量库的那几段文字。所以别急着调生成参数,先把「入库前」和「检索后」两件事做扎实。
- 扫描件先过 OCR,识别完人工抽查几页,重点看数字和表格线有没有串行;
- 表格单独处理成结构化的行列文本,别让它被 OCR 拍成一堆空格;
- 页眉页脚、页码、水印、重复的公司抬头统一删掉,这类文字会在每个分段里重复出现,把相似度算歪;
- 同一份文档里的目录页、修订记录页可以整页丢弃,它们不承载答案;
- 清洗后的纯文本单独留一份存档,出问题时能对比是原文错还是切分错。
分段策略要跟着文档类型走,别一套参数切所有文件。制度、流程类文档适合按条款或章节号切,一条一意,切完每段开头补上「文档名 + 章条号」;操作手册、产品说明适合按小节切,一个小节讲一件事,段落长一点没关系。不管哪种,每段都要带上标题和来源路径,模型回答时你才能把出处指出来,用户也才敢信。段太长会稀释语义,太短会丢上下文,切完拿十几个典型问题试检索,看命中的段落是不是刚好够回答问题。
| 文档类型 | 切分依据 | 每段要不要带标题 | 常见坑 |
|---|---|---|---|
| 规章制度 | 条款号、章节号 | 要,带条款号 | 一条被切成两半,答案残缺 |
| 操作手册 | 小节标题 | 要,带小节名 | 跨小节合并,语义混杂 |
| FAQ 问答 | 一问一答 | 要,带原问题 | 多问合并成一段,检索漂移 |
| 合同协议 | 条款 + 甲乙方 | 要,带条款与主体 | 主体丢失,答错对象 |
向量库和嵌入模型怎么搭,别凭感觉选,去站内的 /stacks/vector-dbs 按你的规模、是否需要本地离线、运维成本对照着挑,那里有当时可选项的横向说明。重排要不要加,取决于你的问题是不是长尾:如果用户问法五花八门、口语化严重,初筛召回容易把对的排在后面,加一层重排往往立竿见影;如果问题基本是固定句式,初筛就已经够准,加了只是白烧算力。加之前先按 /ollama-rerank-model-pick 的思路拿你自己的真实问题集比一遍,用同一批问题对比加与不加的命中位置,别拿别人的结论直接套。
- 分开记录两类失败建一张表,一列是「检索不到」,一列是「检索到了但答错」。每次坏例子先归类,别混在一起。
- 检索不到怎么修先看库里到底有没有正确答案那段,如果没有,是清洗漏了或分段切碎了;如果有但没被召回,去调分段长度、嵌入模型或加混合检索。
- 检索到了答错怎么修看召回的段落是不是被别的无关段落挤下去了,或者模型把上下文里的噪声当成了答案。方向是加重排、收紧 top-k、在提示里要求只依据给定片段作答。
- 回归验证两类问题各留一批固定测试问题,每次改完配置重跑一遍,看有没有把原来对的改坏。
一套能跑起来的最小搭建顺序
这套顺序的核心思路是:每一步只验证一个环节,出了问题能马上定位是哪一层。很多人一上来就把模型、向量库、前端全接上,跑不通时根本不知道是谁的锅。按下面的顺序走,前一步不通就不要进下一步。
- 第一步:模型服务起在本机,先确认接口通按 /ollama-getting-started 里的方式把模型服务跑起来,先别碰知识库。起好后用命令行直接问一句最简单的话,比如「你好,请回一句话」,看有没有正常返回。返回了说明服务和接口都活着;报连接错误就去看服务是否还在前台跑、端口有没有被占用。这一步只要求「能问出话」,不要求答得好。
- 第二步:单独跑通向量化和检索先不接对话模型。把几份文档切块、向量化、存进本地索引,然后拿一个你确定答案就在文档里的问题去检索。关键是人工看召回的前几段:内容对不对、有没有把不相关的段落也塞进来。召回不对,后面接什么模型都是白搭。
- 第三步:接上问答环节,把引用格式写死把检索到的片段拼进提示词,让模型基于片段回答。提示词里明确两件事:答案要标出引用了哪一段;片段里找不到答案时直接说「不知道」,不许自己编。返回结果里如果出现了文档里没有的信息,说明约束没生效,回去改提示词而不是改模型。
- 第四步:固化一组自己的问题清单准备一组覆盖不同情况的问题:能直接命中的、需要跨段拼的、文档里根本没有的。以后每次改切块大小、改提示词、换模型,都跑同一组问题,对比这次和上次哪个变好哪个变坏。没有固定清单,你会凭感觉判断,越调越乱。
| 步骤 | 要验证的东西 | 通不过时先查什么 |
|---|---|---|
| 起模型服务 | 接口能返回一句话 | 服务是否在跑、端口是否被占 |
| 只跑检索 | 召回的前几段是不是你要的 | 切块方式、文档格式、索引是否写进去 |
| 接问答 | 回答只基于片段、找不到说不知道 | 提示词约束、拼进去的片段是否为空 |
| 跑问题清单 | 这轮改动是变好还是变坏 | 对比上次结果,定位是哪一步改坏了 |
- 每一步只测一个环节,不通过就不往下走,省得最后分不清是谁的错。
- 检索环节一定要人工看召回内容,光看「有返回」没意义。
- 提示词里写死「不知道」的出口,比事后删幻觉便宜得多。
- 问题清单要自己写,别人的清单覆盖不到你的文档。
- 改动一次只动一个变量,不然对比结果说明不了问题。
出问题时的排查顺序
本地知识库答非所问,八成不是模型不行,而是某一段链路断了。排查别乱翻,按「服务层 → 检索层 → 提示层」这个顺序走,一层确认没问题再进下一层,能少走很多弯路。
- 先看服务层确认推理服务端口能通、进程没崩;看显存是不是被别的任务占满、模型是否真的加载完成。服务层没起来,后面全是白搭;具体看哪些指标、怎么调吞吐和显存占用,到 /local-inference-speed-tuning 对一遍。
- 再看检索层把这次召回的原文整段打印出来,逐条看:本来该出现的那段话,到底有没有被取回来。取不回来就是切片、切分粒度或相似度阈值的问题,跟模型无关。
- 最后看提示层数一下塞进上下文的有几段、总长度有没有触发截断、文档内容是不是压过了你的指令。上下文该留多长、怎么权衡,参考 /context-window-selection 的取值思路。
- 三层都正常还漂如果服务在跑、召回正确、提示也没问题,答案依旧飘,那要怀疑嵌入模型对中文的适配。换一个更贴合中文语义的嵌入模型试试,对照 /chinese-embedding-model-choice 挑。
| 层次 | 先看什么 | 常见坑 |
|---|---|---|
| 服务层 | 端口、显存、模型加载状态 | 显存被占满导致加载不全 |
| 检索层 | 召回原文里有没有目标段 | 切得太碎或阈值太高取不到 |
| 提示层 | 段数、是否截断、指令位置 | 文档盖过指令、超长被截 |
| 嵌入层 | 中文语义匹配效果 | 向量模型对中文不敏感 |
- 服务层没过,别动检索参数,先把服务跑稳。
- 检索层取不到原文,调阈值和切片,不要急着换模型。
- 提示层被截断,先缩召回段数,再考虑换更大的上下文。
- 三层都正常还漂,才轮到换嵌入模型。
- 每次只改一个变量,改完立刻复测同一问题,方便定位。
常见问题
本地搭建知识库,是不是模型参数越大效果越好?
不一定。知识库的答案质量主要取决于检索到的那几段对不对,模型只负责把它们组织成话。参数大但显存不够导致上下文被压缩,反而更差。先保证检索准,再考虑升级模型档位。
必须用 DeepSeek 吗,换成别的本地模型行不行?
可以换,接口规范对齐之后检索那部分不用改。差别在于中文表达、指令遵循和上下文长度,选型思路参考 deepseek-model-comparison 和 open-model-comparison-guide,换模型前先跑一遍你的问题清单。
搭好之后怎么判断它到底能不能用?
准备二十到五十个只有你内部文档才答得出的问题,分「能查到答案」和「文档里根本没有」两类,看它答对率和对不知道的识别率。两组都达标才算能用,单看会答的那组容易被误导。