1615 字
约 5 分钟
18
RAG检索增强生成
无标签

RAG 检索增强生成

系列:ToyAgent 智能体知识库 07/26

一句话核心:模型不知道你的私有知识——先检索资料、再把资料塞进提示词、最后让生成带出处,三步动作把"瞎编"变成"有据可查"。

为什么需要

一个只靠参数记忆的模型有三块天然盲区:

  • 知识过期:训练截止日之后发生的任何事,模型一无所知。你问它"上个月的发布会有什么内容",它要么沉默要么现编。
  • 私有知识:公司内部文档、本项目代码细节、你个人整理的笔记——这些从未进入过任何模型的训练集。
  • 幻觉:最危险的是前两种盲区叠加下的表现。模型不会说"我不知道",它会用极其流利自信的语气编一个答案出来。

RAG(Retrieval-Augmented Generation)的思路不是去"修"模型,而是在模型外面加一圈流程:Retrieve 检索 → Augment 增强 → Generate 生成。强制模型"戴着资料说话"。

核心机制

三步骨架用最小代码就能表达清楚:

// 1. 检索:给每条知识片段与问题的相关性打分,取 Top-N
List<Hit> hits = KNOWLEDGE.stream()
        .map(text -> new Hit(text, score(text, input)))
        .sorted(Comparator.comparingDouble(Hit::score).reversed())
        .limit(2)
        .toList();

// 2. 增强:把命中片段注入提示词,每条带来源编号
StringBuilder ctx = new StringBuilder("【参考资料】\n");
for (int i = 0; i < hits.size(); i++) {
    ctx.append("[").append(i + 1).append("] ")
       .append(hits.get(i).text()).append("\n");
}

// 3. 生成:用系统提示词约束模型——优先依据资料,不足则拒答
String answer = model.chat(List.of(
        Message.system("优先依据【参考资料】回答并标注来源 [1];"
                + "资料不足时如实说不知道,禁止编造。"),
        Message.user(ctx.toString()),
        Message.user(input)));

三段里各自藏着一个容易做错的设计决策:

  1. 检索的核心是 score()。教学版可以用字符 bigram 重合率这种土办法,生产上换成 Embedding 余弦相似度,再往上还有重排序(rerank)、HyDE、GraphRAG。
  2. 增强要给片段编号来源,这是后面"回答带出处"的前提。
  3. 生成的约束提示词必须显式授权拒答。不写这条,模型在资料不足时依然会编。

三步骨架的完整流转:

graph LR
    Q["用户问题"] --> R["检索 Retrieve"]
    K["知识库/向量库"] --> R
    R -->|Top-N 命中片段| A["增强 Augment"]
    A -->|带来源编号的参考资料| G["生成 Generate"]
    M["模型"] --> G
    G -->|资料充足| ANS["带出处的回答"]
    G -->|资料不足| REF["拒答:如实说不知道"]

图示:检索从知识库取 Top-N 片段,增强环节编号注入提示词,生成环节在资料不足时走拒答分支而非编造。

举例

模拟对话轨迹——知识库存三条手工条目:

KB[0]: RAG 的三步是检索、增强、生成。
KB[1]: 拒答是允许模型说"不知道"的能力。
KB[2]: 向量库常用 Milvus、ES、pgvector。

用户输入:向量库有哪些常见选择?

[检索] score(KB[0])=0.05  score(KB[1])=0.02  score(KB[2])=0.71
       → Top2 = [KB[2], KB[0]]

[增强] 系统提示词追加:
       【参考资料】
       [1] 向量库常用 Milvus、ES、pgvector。
       [2] RAG 的三步是检索、增强、生成。

[生成] 模型输出:
       常见的向量库有 Milvus、Elasticsearch 和 pgvector [1]。
       其中 pgvector 适合已有 PostgreSQL 的团队……

反过来,用户输入:量子力学的泡利不相容原理是什么?

[检索] 三条 KB 全部低分,Top2 也不相关
[生成] 模型输出:
       当前参考资料中没有覆盖量子力学的内容,我无法给出可靠回答。

第二次对话就是拒答。允许模型说"不知道",是 RAG 工程里最容易被忽略、却最重要的设计——宁可信度下降一点,也不要一个编造出来的答案。

LangChain4j 视角的同构:生产里把 score() 换成 EmbeddingStore 的 search(EmbeddingSearchRequest),把内存 List 换成向量库,其余两步原样保留:

List<EmbeddingMatch<TextSegment>> hits = store.search(
        EmbeddingSearchRequest.builder()
            .queryEmbedding(embeddingModel.embed(input).content())
            .maxResults(2).build())
    .matches();

教学版 vs 生产版

维度 教学版 生产实现
相似度 字符 bigram 重合率 Embedding 余弦相似度
存储 内存 List 向量库(Milvus / ES / pgvector)
预处理 手工知识条目 文档切分 → 向量化 → 入库
进阶 — 重排序、HyDE、GraphRAG、Agentic RAG

骨架不变论:把 score() 换成 Embedding、把 List 换成向量库,教学版立刻升级成生产级 RAG——三步骨架(检索→增强→生成)永不过时。变的只是每一步的实现质量。

面试考点

  • RAG 三步分别解决什么? 检索解决"找到相关资料",增强解决"资料进入模型视野",生成约束解决"模型不得脱离资料编造"。少了第三步的拒答授权,前两步白做。
  • 为什么检索结果要带来源编号? 让输出可溯源,用户能核对;同时给模型的引用行为一个明确锚点,降低"资料与记忆混写"的概率。
  • RAG 一定比微调好吗? 各管一头:RAG 管知识新鲜度与私有事实、可随时换库;微调管风格与固定行为模式。事实性知识用 RAG,行为塑形用微调,二者不互斥。

生产化路径

  1. 切分策略:按标题/段落结构切而非定长硬切,保留标题路径作为 chunk 元数据(知识库类文章尤其重要,否则检索到的片段丢失上下文层级)。
  2. 混合检索:向量召回 + 关键词召回(BM25)双路并行,再 rerank 合并,能显著缓解专有名词检索失效。
  3. 拒答工程:除提示词约束外,加"检索分数阈值"硬门——Top1 分数低于阈值直接走拒答分支,不进模型。
  4. 引用校验:生产上可对生成结果做一次"来源核对"(模型自检或规则匹配 [n] 是否存在),防止模型挂了个不存在的引用编号。
  5. 缓存与增量:高频问题的检索结果与 embedding 做缓存;知识库更新走增量入库,避免全量重建。
RAG检索增强生成
https://clxhxhhr.top/posts/4707/
作者
clxstart
发布于
2026-10-13
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。