先给结论:RAGless 这个项目的核心卖点,是把 RAG 这条链路里的“运行时生成”去掉,让知识库问答在运行阶段不调用 LLM API,因此 token 费用趋近于零。它仍然要建索引、要做检索,也需要回答用户问题,但它不再像常规 RAG 那样,每次提问都把一堆资料塞进 Prompt 里让大模型重新组织语言。适合的场景很明确:FAQ、产品文档、操作手册、规范条目这类“答案本来就写在某段原文里”的知识库问答。不适合的场景也同样明确:需要跨文档归纳、改写、翻译、多轮综合的开放问答,让它硬做会非常勉强。
这篇文章会从 RAG 的成本构成讲起,拆清楚 RAGless 到底省掉的是哪部分钱,再把落地时需要处理的数据、索引、查询、评测和边界问题逐个说一遍。如果你手上正好有一个文档问答需求,但预算又很紧,这篇文章值得完整看完。
1. RAGless 不是去掉检索,而是去掉运行时的“生成”
1.1 RAG 的钱主要花在哪一步
要理解 RAGless,得先理解 RAG 的成本发生在哪个环节。常规 RAG 在处理一次用户问题时,通常是这样走的:
- 用户输入一个问题。
- 系统把问题转成向量,或者做关键词召回。
- 从知识库中检索出若干相关片段。
- 把“用户问题 + 检索到的片段 + 系统提示词”拼成一段长文本。
- 调 LLM API 生成最终回答。
第 2 步和第 5 步都可能产生 API 费用。第 2 步如果使用远端 embedding 模型,也需要按 token 计费。但大头其实在第 5 步,因为你要发给模型的 Prompt 不只是用户那一句话,还包括检索出来的文档片段和提示词模板。文档片段越长、top-k 数量越多,单次请求消耗的 token 就越高。如果用户再追问几轮,历史消息也要重新拼进去,费用会进一步上升。
所以很多团队在做 RAG 原型时发现,单个问题看起来没多少钱,但一天几千次查询后,账单增长很快。更隐蔽的是,部分回答质量不高,需要加长上下文、提高召回数量来缓解,这又会反过来推高成本。RAG 的 API 费用不是固定值,而是和你的文档长度、召回数量、并发规模强相关。
1.2 RAGless 的取舍:索引期做重活,运行期只做检索和展示
RAGless 的核心变化,是把成本尽量挪到索引构建阶段,而不是每次用户提问时都付一次推理费用。
具体来说,它在离线阶段同样要做文本清洗、分块、向量化或关键词索引,这些操作即使使用本地开源模型或传统分词算法,也只需要承担服务器资源成本,不需要按次调用云端 LLM API。到了运行阶段,用户提问之后,系统只做检索、排序、按阈值截断、返回原文片段,或者用前端模板把命中片段拼成一个可读的问答结果。整个过程没有大模型生成这一步,所以 LLM API 费用是零。
它和 RAG“相似”的地方在于,它依然沿用“问题 → 召回 → 排序 → 返回相关内容”的框架,甚至很多分块策略、向量索引方案、Top-K 参数设计都是通用的。区别在于最后一步:RAG 是让模型“看着资料说人话”,RAGless 是直接把资料里的原句返回,或者让前端把几段原句拼成答案卡。
这种取舍换来的是明确的工程优势:
- 每次查询成本可控,接近 0。
- 响应延迟低,因为没有大模型排队和推理耗时。
- 回答可追溯,用户能看到答案来自哪篇文档、哪一个段落。
- 不存在大模型幻觉,因为根本不让模型自由发挥。
代价是:回答的自然语言组织能力弱,同义改写能力弱,复杂问题只有先靠检索命中才能解决。
1.3 两种方案适合的内容形态不同
RAGless 适合的是“答案能从原文里直接截取”的知识库。例如:
- 企业内部 FAQ:报销流程是什么、假期怎么申请、密码多久改一次。
- 产品操作手册:某个按钮在哪里、某个报错怎么处理、某项配置如何开启。
- 政策规范类文档:某一条规定原文是怎么写的、截止时间是哪天。
- 标准答案类业务库:常见问题对应的标准回复模板。
传统 RAG 则更适合“需要综合后再表达”的场景,比如多份合同的关键风险归纳、多篇论文的研究方法对比、开放式的资料问答。这种需求即使把相关段落都检索出来,直接堆给用户也读不出完整结论,必须有一层生成能力做组织。
因此 RAG 和 RAGless 不是替代关系,而是成本、效果和场景不同的两条路。给一个直观对比:
| 对比项 | 传统 RAG | RAGless |
|---|---|---|
| 运行时是否调用 LLM API | 是 | 否 |
| 单次查询 token 费用 | 与上下文长度正相关 | 接近 0 |
| 回答形态 | 模型生成的连贯文本 | 原文片段、命中段落、模板拼接 |
| 响应速度 | 受 LLM 推理影响 | 主要取决于检索引擎 |
| 可解释性 | 可给引用,但回答本身是生成的 | 天然可追溯到原文 |
| 跨文档综合能力 | 强 | 弱 |
| 最佳内容类型 | 开放问答、总结、对比、分析 | FAQ、文档查询、标准条目 |
2. 运行前先做三件事:数据清洗、索引构建、成本重新定位
2.1 先接受“零 API 成本不等于零部署成本”
“$0 LLM API costs at runtime”这句话要正确理解。它说的是运行阶段不按次付 LLM 费用,不代表整个系统不需要花钱。索引构建需要计算资源,全文检索或向量检索服务需要内存和 CPU,如果使用本地 embedding 模型做向量化,还需要额外的机器资源。文档越多,索引越大,内存占用也会上升。
所以在选型前,先做一个简单判断:你的查询量到底有多大?如果每天只有几十次问答,传统 RAG 也许花费并不高,没必要为了省 API 费用引入一套自建索引和检索体系。如果每天有几千上万次查询,或者你希望知识库问答直接暴露给终端用户,那 RAGless 的价值就很明显,API 费用的边际成本可以压到接近零。
另外还要考虑团队维护成本。自建检索服务需要处理文档更新、索引重建、并发查询和监控告警,这些虽然是工程上常见的工作,但确实需要有人持续维护。
2.2 数据准备:分块和清洗决定效果的上限
RAGless 不生成内容,因此它的回答效果几乎完全依赖于“索引里有没有相关文本”以及“检索能不能把这段文本找出来”。
第一步是清洗。网页正文要抽取出来,PDF 里的表格要尽量转成可检索的结构化文本,Markdown 里的代码块和目录要按需求决定是否保留。如果原始文档里混杂大量页眉、页脚、导航文字,这些噪声会成为检索时的干扰项。
第二步是分块。分块是 RAG 类系统最重要的前置步骤之一。块太大,检索命中后返回内容很泛,用户看不出针对性;块太小,语义不完整,有时一句话被截断在中间,返回结果难以理解。常见做法是让分块尽量覆盖一个完整的语义单元,比如按“标题 + 若干段落”来切,而不是死板地按固定字符数切。
关于分块,下面的参数可以作为起点,但一定要结合自己的文档类型调整:
| 参数 | 常规起始值 | 判断依据 |
|---|---|---|
| chunk_size | 300 到 800 字 | 文档段落长短、问题答案通常落在多长的片段内 |
| overlap | 50 到 100 字 | 避免关键词分布在分块边界时被切断 |
| 分块单位 | 标题、段落、列表项 | 优先保持一个完整语义节点 |
| 是否保留元数据 | 是 | 文档名、章节路径、更新时间要随块保存 |
我看过很多检索效果差的项目,最后定位下来都不是模型问题,而是源文档本身没清洗干净,或者分块策略完全不匹配实际问答长度。比如一份报销制度文档,如果每个条目只有两行,你却按 800 字分块,一个块会混入多个不同主题,用户问“差旅住宿上限是多少”时,召回结果里混着一堆无关差旅内容,回答体验自然很差。
2.3 索引方案的选择:关键词召回还是向量召回
RAGless 的索引层可以有两种选择。
第一种是关键词检索,也就是传统的倒排索引。它对“专有名词、编号、操作步骤”这类强标识内容非常有效。比如用户问“SSH 连接超时怎么办”,倒排索引能直接根据“SSH”和“超时”精确定位到相关文档段。优点是部署简单、资源占用低、可解释性强,不用考虑 embedding 模型的选型问题。
第二种是向量检索,也就是把文本通过 embedding 模型转成向量,再通过余弦相似度或内积做召回。它的优势是能匹配同义表达,例如用户问“连不上服务器怎么排查”,即使原文写的是“远程连接失败”,向量检索也有机会命中。缺点是构建索引时需要额外的 embedding 计算,可能是调用远端 embedding API,也可能是本地跑开源模型;查询时也需要为每一条 query 做向量化。
更稳妥的做法是混合召回:先用关键词召回和向量召回各取一批候选,再做结果合并和重排。这样既保留了精确关键词的命中率,也增加了语义模糊查询的召回范围。RAGless 项目不一定每条查询都能命中,所以召回策略做得越稳,可用性越高。
如果你刚开始尝试,建议先用纯关键词方案跑通流程。用 30 到 100 条高频问题测试一遍,如果关键词召回的效果已经能满足大多数问题,就不必急着引入向量检索。只有当用户提问方式和文档原文差异很大、关键词命中率明显不足时,再补上向量召回。
3. 最小可用流程:从一份 FAQ 跑通 RAGless
3.1 第一步:定义问题和答案的原始格式
无论最终用什么框架实现,我都建议先从一个非常小的知识库开始,比如 20 到 30 条常见问题。这样能快速验证方案是否可行,也方便人工检查每一条检索结果。
先整理一个结构化的源文件,最简单的是 Markdown 或者 JSON。每条内容至少包含三部分:
- 问题或主题
- 答案或对应原文
- 来源标识
如果内容来自文档,则应该把文档路径、章节标题、段落内容一起存下来。这里给一个示意,不用照抄格式,但思路可以参考:
[ { "id": "faq-001", "title": "SSH 登录提示 Permission denied 怎么办", "content": "先检查用户名和密钥路径是否正确……", "source": "docs/remote-login.md" }, { "id": "faq-002", "title": "如何修改默认端口", "content": "修改配置文件中的 port 字段……", "source": "docs/server-config.md" } ]对 FAQ 类内容,标题本身往往就是最好的检索字段。对长文档类内容,需要先把文档清洗、分块、生成小块文本,再把每个块和它的来源章节一起写入索引。
3.2 第二步:建立索引
这里的索引策略取决于你选的是关键词检索还是向量检索。关键词检索可以用常见的全文检索引擎或纯内存倒排;向量检索则需要一个向量数据库,或者把向量写到本地文件中用近邻搜索库加载。具体用哪个工具取决于你的技术栈和部署环境,但流程是通用的:
# 示例流程,不是某个工具的固定命令 python build_index.py \ --input data/faq.json \ --index-dir output/index \ --chunk-size 500 \ --chunk-overlap 50 \ --embedding-model your-embedding-model如果走纯关键词路线,embedding 部分可以省略;如果走向量路线,需要预先确定 embedding 模型。这里有一个关键判断:embedding 模型的选择会影响召回效果,但不会影响运行时是否调用 LLM API,只要你在索引构建时用本地模型即可。
索引构建完成后,应该单独存一份文件或目录,供查询服务启动时加载。生产环境下还需要考虑增量更新问题,新文档加入后不能只重建全量索引,尤其是文档量达到几千份以上时,增量索引和定时重建要一起做。
3.3 第三步:写一个最简查询接口
查询接口的逻辑比 RAG 简单很多。用户输入问题后,经过一次检索,从索引中找出与问题最相关的前若干条文本,然后直接返回。不同的落地方案可以有不同的输出形态:
- 返回命中文档标题和片段。
- 返回 FAQ 的标准答案。
- 返回原文加高亮。
- 将命中的多条片段交给前端按模板渲染。
如果整个链路不用 LLM 生成,那这一步通常就写成类似下面的伪代码:
def answer(query: str, top_k: int = 5) -> list[dict]: hits = search(query, top_k=top_k) result = [] for hit in hits: if hit.score < threshold: continue result.append({ "title": hit.title, "snippet": hit.snippet, "source": hit.source, "score": hit.score }) return result如果知识库内容是 FAQ,且每条 FAQ 本身有标准答案,那返回结果应该直接带 answer 字段。如果知识库内容是文档,那返回内容可以是一段原文,前端负责把多段原文拼接成答案区域,并在底部展示来源链接。
这里不建议用一个固定 threshold 值直接过滤所有数据。不同文档、不同索引算法的分数分布差异很大,我一般先跑一批人工标注的问题,观察命中结果的分数区间,再来定阈值。更稳的办法是:如果 top-1 的相似度很低,直接返回“未找到相关内容”,不要硬凑答案。硬凑会让用户觉得系统在瞎答。
4. 验证质量:RAGless 的效果要从“检索命中”开始看
4.1 单条用例的检查清单
RAGless 没有生成阶段,因此人工验证一条回答是否合格,和传统 RAG 的验证方式不太一样。重点不是“回答是否通顺”,而是“检索结果是否准确覆盖了用户问题的答案范围”。
我建议每一条测试用例都按下面的清单检查:
- 用户问“如何找回邮箱密码”,返回的第一条是否真的包含找回密码的步骤?
- 返回内容里是否有来源和章节信息?
- 如果问题需要查看某一步的截图或配置,返回文本是否能说明位置?
- 如果用户使用同义词发问,比如把“怎么改端口”写成“更换监听地址”,索引还能不能找到对应文档?
- 完全没有相关内容时,系统是否给出兜底提示,而不是返回垃圾片段?
单条用例不是看一次就够的。同一个问题换几种表达方式再测,比如把“怎么办”换成“处理方式”,把缩写换成全称,才能看到检索的鲁棒性。
4.2 离线评测指标
要量化和持续改进 RAGless 的效果,手工测试几条远远不够,应该造一份离线评测集。评测集的结构可以非常简单:每一行包含一个用户问题、一个期望命中的知识库内容 ID,也可以额外标注一个期望命中的来源文档路径。
[ { "question": "SSH 登录出现 Permission denied 通常要检查什么", "expected_ids": ["faq-001"] }, { "question": "数据库连接池大小怎么配置", "expected_ids": ["faq-002", "manual-003"] } ]有了这样的评测集,就可以计算几个简单指标:
- Recall@k:最相关的那条内容是否出现在前 k 条召回结果里。
- MRR:正确结果的排序位置有多靠前,位置越靠前越好。
- 无答案率:样本里有大量查询无法命中任何合理内容,说明索引或分块有问题。
这些指标虽然传统,但在 RAGless 这种依赖检索的系统里非常有用。因为它们能直接告诉你“检索层的工作是否合格”,不掺入生成模型的干扰。如果 MRR 很低,说明不是回答模板的问题,而是召回阶段根本拉不出正确内容。
我自己一般会先人工标 50 条到 100 条高频问题,按季度或文档更新节奏做一轮回归。把指标记录下来,改完分块、调整完索引策略后重新跑,用数据判断是否真的变好了。
4.3 在线运行要看什么日志
系统上线后,还需要观察真实用户问题与预期是否一致。传统 RAG 可以通过日志记录用户问题和大模型回答;RAGless 同样可以记录,但记录的重点要换成“检索命中日志”。
需要记录的信息包括:
- 用户原始查询。
- 召回的前几名内容 ID。
- Top-1 相似度分数。
- 是否落入无答案兜底。
- 用户是否点击了来源文档。
如果“无答案”比例过高,常见原因是用户提问方式和文档用词差异太大,说明需要加强召回策略或者补充同义词。如果用户频繁点击某一条来源文档,说明这条内容高频有用,可以考虑把它抽成标准答案模板。如果某份文档长期被检索但从没被点击,则要考虑是不是文档位置靠前误导了用户。
这些日志不需要依赖任何 LLM API,也能形成一套完整的数据优化闭环。
5. 边界和常见问题:别把 RAGless 当万能问答
5.1 它是怎么丢分的:同义改写、指代和多文档综合
RAGless 在测试时最容易出现的问题,是问法一变,命中结果就飘了。比如文档里写的是“异地备份”,用户却问“容灾机房在哪儿”,如果索引层没有同义扩展或语义召回,靠纯关键词搜索很难命中。
另一个问题是指代。RAGless 通常只能处理单轮独立问题,因为多轮对话需要理解“它”“刚才那个问题”这些指代关系,而它没有大模型理解上下文的能力。如果把多轮语义理解任务硬交给 RAGless,你只能自己维护一个轻量的改写模块,比如把上一轮的命名实体、关键短语带入当前问题,再触发检索。
如果用户问题需要综合三份文档才能回答,每条被召回的片段都只说了一部分,那么 RAGless 的表现就很差。这已经不是“加一个模板”能解决的问题,综合归纳本身需要生成能力。
因此,RAGless 的适用范围,必须限定在“答案能在单条记录或单段文本中闭合”的问题上。
5.2 文档更新后索引不同步的问题
这是工程落地时最容易踩的坑。文档更新了,但索引还是旧的,于是用户查到的永远是最早那一版内容,时间一长就会丧失信任。
解决办法可以按数据量大小分档:
- 数据量小,定时全量重建,比如每晚重建一次。
- 数据量中等,依赖文档变更事件,单独更新变更内容的块。
- 数据量大,建立版本号机制,每个块带着源文档版本,查询时只返回最新版本。
如果源文件是数据库里的记录,那更新的触发点就更明确:记录变更时,同时更新索引里的对应条目。总之,索引和源数据之间必须有一条明确同步链路,否则检索系统上线三个月后,很多内容会变成过期答案。
5.3 排查顺序:先看分块,再看召回,最后看返回模板
RAGless 出了问题,不要第一反应是换模型或者调 threshold,按下面顺序排查更快:
- 看返回是否命中了不应命中的文本块。如果是,先检查这个文本块是不是包含了太杂的主题。
- 看正确内容是否压根没出现在召回结果里。如果没出现,要检查索引里是否真的存在这条内容,以及问题用词和原文用词差异有多大。
- 看正确内容出现在召回结果里,但排序不靠前。这时候再调 Threshold、调 Top-K,或者加一条重排规则。
- 看正确内容命中了,但用户反馈看不懂。问题多半出在返回模板,而不是检索。
也就是说,先判断“检索阶段是否拿到正确答案”,再判断“展示阶段是否把它讲清楚”。很多表面上像检索能力不够的问题,实际是分块粒度把正确文本切碎了,或者跟一堆无关内容混在了一个块里。
5.4 适用与不适用清单
RAGless 更合适:
- 高频重复的 FAQ。
- 标准操作流程。
- 版本固定的产品文档。
- 政策、制度、规范原文查询。
- 对成本和响应速度极其敏感的对外问答场景。
RAGless 不合适:
- 需要跨文档归纳总结的问题。
- 需要判断用户情绪或意图的客服对话。
- 没有固定标准答案,需要生成解析的问题。
- 资料本身没有结构化,每篇文档都是一大段故事性文本的场景。
如果你发现你的业务场景里,用户问题的答案几乎都不是原文档中的某一段落,而更多是综合理解后的结果,那 RAGless 很难做为主方案。
6. 什么时候该上 RAGless,什么时候还得走 RAG
6.1 一个更现实的成本模型
选型不应该只盯着“LLM API 费用是 0”这一个点,而要看整体成本结构。RAGless 省的是每次查询的 token 费用,但会新增索引维护成本、检索服务部署成本、数据清洗成本和评测集的长期维护成本。
一个更现实的判断方式是这样的:
- 查询量低、答案又需要较多生成能力,直接走传统 RAG,简单直接。
- 查询量高、文档答案相对固定,RAGless 可以显著降低边际成本。
- 查询量高、但一部分问题确实需要生成总结,就采用 RAGless 优先、LLM 兜底的混合方案。
混合方案的策略并不复杂:先用 RAGless 检索并判断命中分数。如果 Top-1 命中得分很高,答案原文已经足够直接回答,就直接返回文档片段,完全不调用 LLM。如果得分偏低,说明纯检索可能满足不了用户,这时候才进入降级路径,把检索片段交给 LLM,由模型组织生成回答。
这种“先检索,后判断,再按需生成”的做法,在日常真实业务里会比纯 RAG 或纯 RAGless 都稳。既保住了大部分查询的低成本,也给真正复杂的查询留了出口。
6.2 从运营角度看使用成本
RAGless 不只是节省 API 费用,还大大降低了排障时的外部依赖。传统 RAG 如果回答质量差,你很难判断是召回问题还是 Prompt 问题还是模型问题。RAGless 没有模型生成层,回答质量只取决于数据质量和检索质量,定位问题会更快。
同时,因为每一次回答都能直接给出原文来源,用户也能自行判断结果是否可信。企业内部的合规类知识库、客服标准答案库这类场景,会非常看重这一点。
如果准备长期采用这种架构,一定要在前期预留几个能力模块:
- 一个评测集维护机制,用来做索引更新后的回归验证。
- 一个查询日志服务,用来观察无答案率和高频问题变化。
- 一个人工兜底反馈入口,让用户找不到答案时可以提交工单。
- 一个可选的 LLM 降级调用开关,避免业务复杂后方案完全锁死。
6.3 落地建议和验收标准
如果你想把这套方案真正落到自己的项目里,我建议按阶段推进:
第一阶段先选一个范围很小的知识库,比如 30 条高频 FAQ,用关键词检索跑通端到端流程。验收标准是:高频问题都能在结果中命中正确内容,且无答案兜底提示能正常出现。
第二阶段把数据量扩到几百份文档,加入向量召回或混合召回。验收标准是:新增评测集里的 Recall@1 有明显提升,用户提问表达方式变化后结果依然稳定。
第三阶段再把 RAGless 作为主入口接入真实业务,保留日志和人工兜底。验收标准不是看“回答是否像真人”,而是看“有多少比例的问题能不经 LLM 直接解决”,这个数字越高,说明 RAGless 承担的有效查询量越多,省下的成本也越明显。
从我自己的经验看,真正决定 RAGless 方案成败的,往往不是“检索算法选得够不够新”,而是数据清洗、分块粒度、评测集质量和文档更新机制是否扎实。把这几件事做好,哪怕只用最基础的关键词检索,也能解决相当一部分高频问答需求。相反,如果源文档一团乱、分块没有逻辑、也没有评测方法,就算把向量模型换得再好,用户依然会觉得这个知识库“什么都搜不到”。