本地把 Ollama 跑起来不难,难的是检索接上生成之后:模型换一个答法就变、请求一多就排队、文档一更新索引就过期、出了问题不知道是检索错还是模型错。这篇不讲协议也不讲原理,只按工程顺序把「能跑通、能复现、能定位」这条线拉直,给你一套可照着做的步骤和排查顺序。
本地把 Ollama 拉到机器上跑起来,其实十分钟就能搞定。真正让人卡住的,是检索接上生成之后的那一段:同一个问题,换个模型、换个提示模板,回答就完全变样;请求一多就开始排队,你甚至分不清是算力不够还是代码写死锁了;文档更新一次索引就过期,手工重建又丢掉之前调好的参数;最麻烦的是答错了,你不知道该改切片、改召回条数,还是改提示。这篇不跟你讲 RAG 的原理和各类论文,只按工程顺序把「能跑通、能复现、能定位」这条线拉直。
下面按七个环节走:先把服务形态和端口定下来,再做进料切片,然后接检索、拼提示、设超时并发,最后落到日志排查和评测回归。每一步都给出可执行动作和判断标准,遇到没跑通的地方,照着顺序往下排就行。到站内 /tools/speed-test 可以顺手测一下你本地这一路的实际响应,对照排查时更有底。
先把 Ollama 服务形态定下来:谁调、走哪个地址、跑几路
很多人一上来就写检索代码,结果调了半天发现服务根本没被外部访问到。先把「谁调这个服务」这个问题回答清楚:是同一台机器上的脚本调,还是局域网里另一台机器调。前者只听本地就好,后者才需要放开监听地址,而且放开之后要确认防火墙和端口范围,别默认对外全开。
- 确定调用方位置,决定监听地址与端口暴露范围,局域网暴露时要额外确认谁在网内
- 记录实际使用的模型名与显存占用,加载失败先看日志区分「拉取失败」还是「显存不足」,处理动作完全不同
- 把生成模型和嵌入模型当成两个服务位来看:一个负责写答案,一个负责把文本转成向量,混在一起时换任一模型都会牵动整条链路
- 机器配置一般就先降到能跑通的最小规模,跑通后再加文档量,别一上来就在长上下文上排错
| 现象 | 优先怀疑 | 动作 |
|---|---|---|
| 服务连不上 | 监听地址或端口没放开 | 本地 curl 一次确认,再看监听配置 |
| 模型加载报错 | 拉取失败或显存不够 | 看日志区分两种原因,分别处理 |
| 首次响应特别慢 | 模型冷加载 | 先手动预热一次再压测 |
| 换模型后答法大变 | 生成与嵌入被一起换了 | 拆开记录两个模型名,逐个替换验证 |
进料与切片:把「文档差」和「检索差」分开对待
答不准这件事,有一大半责任在进料,不在检索。文档抽取出来本身就缺字、表格错位、页眉页脚重复,这时候你去调相似度阈值、去加重排,都是白费力气。所以第一件事不是调参,是把切片结果打印出来看几十条,用眼睛判断进料质量。
- 打印切片自检把切片后的内容原样输出几十条,看有没有明显截断、表格错位、重复页眉。有明显问题的属于进料问题,改抽取逻辑,不要改检索参数。
- 用三组参数做对比就同一条问题测三组切片长度与重叠量,看哪组能稳定把正确段落排进前几条,用这个口径定参数,别凭感觉。
- 给每块带来源和位置每块切片都带上来源标识和原始位置,答错时能立刻定位是切错还是召错。
- 更新流程可重复执行文档更新做成一条可重跑的流程,不要手工删库重建,否则每次更新都会丢掉之前调好的参数记录。
这里要提醒一点:切片长度没有通用最优值。文档偏问答型、句子短,切片就该碎一点;文档偏连续论述,切片就该长一点保留上下文。判断标准只有一个——同一条问题能不能稳定召回正确段落。
检索这一段:先保证「找得到」,再谈「排得准」
新手最容易犯的错,是在链路还没跑通的时候,就先上混合检索加重排。结果是哪一层出问题都看不出来。正确的顺序是先只用最便宜的检索方式,按向量相似度直接取前几条,确认端到端能出答案,再谈优化排序。
- 先用最简单的向量相似度取前几条,验证链路通不通,别没跑通就叠加重排
- 召回条数按切片大小和答案粒度调:切片碎就多召回几条,切片长就少几条
- 每次问答把命中的片段和相似度一起打日志,调参时看这份日志而不是靠感觉
- 正确内容在库里却不出现时,先怀疑向量模型和文本语言不匹配,再怀疑切片把关键句切断了
# 伪代码:最小检索验证,先不求准,只求通\nquery_vec = embed(question)\nhits = vector_store.search(query_vec, top_k=N)\nlog(question, [(h.id, h.score) for h in hits])\nanswer = generate(question, hits)\nlog(answer)什么情况下这套「先简后繁」不适用?当你的语料里同一问题有多种表述、且答案需要跨多篇文档拼起来时,纯向量召回确实容易漏。但即便如此,也应该先把纯向量这条基线跑出来并记录下来,再做混合检索,这样你才知道后面加的东西到底有没有用。
拼装与生成:把「资料」和「问题」的边界说清楚
检索回来的片段塞进提示里,这一步看似简单,其实决定了模型是老老实实看资料,还是自己编。核心就两条:边界要标清楚,资料量要克制。
- 上下文里明确标注哪些是资料、哪些是用户问题,并要求模型只用资料作答,资料不足时直说不知道
- 控制拼进去的资料总量,超长上下文会让模型忽略中间部分,宁可少放几条更相关的,也不要塞满
- 输出格式按下游需要定:只给人看就自由文本,要被程序解析就约束成固定结构
- 把每次请求实际用的提示模板版本记下来,答法变了才能分清是模型换了还是模板被改过
结构约束失败时,常见原因是资料里带了干扰符号——比如代码块、特殊标记被混进要求模型输出的结构里。这时先检查资料原文,而不是急着换模型。
超时、并发与排队:本地服务的容量要有数
本地推理的慢,绝大多数出在生成耗时上,而不是连接。所以超时要分两个来设:连接超时设短一点没关系,整体生成超时要给足,设太短会把本来正常的长回答直接掐断,你看到一个空响应还以为是模型坏了。
| 参数 | 建议方向 | 判断依据 |
|---|---|---|
| 连接超时 | 设短 | 网络层连接很快,超了就是地址或端口问题 |
| 整体生成超时 | 给足余量 | 按单条平均生成耗时乘以一个安全系数 |
| 并发上限 | 先测后定 | 实测单条平均耗时,再决定同时跑几路 |
| 批量任务重试 | 幂等 | 失败重投不能把同一份结果写两遍 |
并发别照搬云端经验。本地一张卡一条请求就可能吃满算力,并发提上去只会让每条都变慢,排队反而更长。先测出单条平均耗时,再定并发上限,这个顺序不能反。
- 记录基础指标压测前先记下显存、CPU、内存的基线,出现变慢时第一时间对照是资源打满还是请求堆积。
- 单条测速用一条典型问题测平均生成耗时,作为后续并发和超时设置的依据。
- 逐档加压并发从低往高试探,观察每条耗时变化,找到耗时明显抬升的那个拐点。
- 加幂等重试批量跑文档问答时给失败重投加幂等控制,避免重复写入。
出问题时怎么查:一份固定排查顺序
答非所问不稀奇,稀奇的是每次都靠猜。固定一套排查顺序,能省掉大量来回试错。顺序就是:先看进料,再看模型输入,最后看输出。
- 第一步看进料:把用户问题和命中的片段原样打出来,多数答非所问在这一步就能看出是没召回到
- 第二步看模型输入:确认提示里确实带上了要用的资料,变量没渲染、上下文被截断都属于这一层
- 第三步看输出:区分是模型忽略资料自由发挥,还是资料本身有误,两者修改位置完全不同
- 日志里至少留下请求标识、耗时、召回片段标识和模型名,缺一项都会让复盘变成猜
排查的价值不在找出一个原因,而在排除掉一批不可能的原因。
如果第一步就发现没召回到正确片段,那后面模型怎么优化都没意义,直接回到切片或向量模型那一层。反过来,如果片段明明召回了、提示里也带上了,但输出还是错的,问题就在提示措辞或模型本身,跟检索无关。
评测与回归:怎么知道改完到底变好没有
调参最怕的就是「感觉好了一点」。本地开发没有线上流量,就得自己攒一批问题当基线。这批问题要覆盖三类场景,缺一类都不完整。
| 问题类型 | 考察点 | 期望表现 |
|---|---|---|
| 库里有明确答案 | 检索能不能命中 | 答对且引用到正确片段 |
| 库里根本没有答案 | 会不会硬编 | 老实说不知道,不编造 |
| 答案分散在多篇 | 多片段能不能拼起来 | 整合多篇后给出完整回答 |
每次动切片、召回条数、提示模板,就把这批问题过一遍,只看两个指标:答对的比例,以及不该答时是否老实说不知道。第二个指标常被忽略,但它直接决定你的系统可不可信。
换生成模型或嵌入模型时,一定要整批重跑,别只看一两个样本。局部变好、整体退步是本地 RAG 里非常常见的现象,只挑顺眼的样本看,等于自己骗自己。评测结果连同参数一起存下来,下次要改之前,先翻上次为什么退回。
下一步把上面这套流程跑通之后,接着可以对照站内收录的本地部署与检索相关工具,看看别人是怎么解决同一类问题的,也能顺便核对当前各类部署方案的实际表现。→常见问题
Ollama 本地搭 RAG,模型有没有硬性要求?
没有硬性门槛,但要把生成和嵌入分成两个角色考虑。生成模型负责读资料写答案,嵌入模型负责把文档和问题变成向量。参数小的模型跑得快但读长资料容易漏点,文档多、答案细的时候要优先换更强的一档。具体挑哪个到站内嵌入模型目录和模型库对照当时的清单再定。
为什么明明文档里有答案,模型却说不知道?
多数情况是检索没把那段捞出来,而不是模型不会答。排查顺序:先把命中的片段打出来看内容对不对,再看切片是不是把关键句切断了,最后看语言与向量模型是否匹配。确定这几步都正常,再去调提示里对「资料不足」的表述。
本地跑起来很慢,是该换机器还是先调参数?
先区分慢在哪:是模型加载慢、检索慢,还是生成慢。检索慢通常靠减少召回条数和索引优化能缓解,生成慢才和算力直接相关。把召回条数、上下文长度先降下来跑一轮,再判断是否需要换配置,比直接加机器省事。
文档更新之后要不要重建整个索引?
看更新规模。整份文档改动大就重跑一次全量入库;只是新增几篇或改了几段,按来源标识做增量更新更省时间。无论哪种方式,都要让流程可重复执行,并保留参数记录,否则每次都等于重新调一遍。
怎么判断答得准不准,只能靠自己看吗?
自己看是基础,但要固定一批问题反复跑,才有对比意义。建议三类问题都留样本:库里有答案的、库里没有的、答案分散在多篇的。每次改动后重跑同一批,盯答对比例和「不知道」的触发是否正确,别只看单个样本的效果。