---
title: "rag - RAG 做法全景与选型指南"
---

本文是本系列的进阶补充。前几篇讲清了 solon-ai RAG 的积木块（嵌入模型、重排模型、文档加载分割、知识库、查询条件、Agent RAG）；这一篇换个视角——把业界主流的 RAG 做法（Naive / Advanced / Modular、混合检索、GraphRAG、Self-RAG / CRAG / Adaptive RAG、Agentic RAG……）一次性摊开，并逐一对应到 solon-ai 的落法。看完你应该能回答一个问题：**我的业务场景，该用哪种做法、配哪几个 solon-ai 组件。**


### 1、先给一张全景图

把检索什么时候发生、由谁决定，作为主线，RAG 的流派大致是这么演进的：

```
Naive RAG ──> Advanced RAG ──> Modular RAG ──> Agentic RAG
（检索一次）   （检索前后加工）   （模块自由编排）    （模型自己决定检索）
     │             │                                │
     └─────────────┴──────> 控制型分支：Self-RAG / CRAG / Adaptive RAG
                          （对检索结果自我评估与纠错）

横向叠加能力：混合检索、元数据过滤、重排、语义分割、多知识库、图/结构化检索……
```

用一张表先建立直觉（**怎么选**看第 8 节）：

| 做法 | 检索时机 | 由谁决定检索 | solon-ai 里的落法 |
|---|---|---|---|
| Naive RAG | 提问前固定一次 | 代码 | `repository.search()` + `ChatMessage.ofUserAugment()` |
| Advanced RAG | 提问前固定一次（前后加工） | 代码 | `SplitterPipeline` + `QueryCondition` + `RerankingModel` |
| 混合检索 | 提问前固定一次 | 代码 | `SearchType.HYBRID` + `HybridSearchParams` |
| 多知识库/结构化检索 | 提问前（可路由） | 代码或模型 | 多个 `Repository` 按条件选择；数据库查询结果直接增强 |
| Agentic RAG | 推理中，按需多次 | 模型 | `RepositoryTool` 挂到 `ChatModel` / `ReActAgent` |
| 控制型（Self/CRAG/Adaptive） | 提问前，但带评估与纠错 | 代码 + 模型信号 | 相似度阈值重过滤 + 联网搜索兜底 + 复杂度路由 |

下面逐个展开，每种做法都配可运行的 solon-ai 配置化示例。

### 2、Naive RAG：最朴素的"检索→增强→生成"

业界说的 Naive RAG，就是一条线性流水线：切块 → 向量化入库 → 查询时取 top-k → 拼进提示语 → 生成。这也是 solon-ai 官方系列文章一以贯之的基础用法（概念篇里的标准四步）。

**特点**

- 实现简单，几行代码就能跑通，适合原型验证
- 短板：切块粗糙导致上下文断裂、检索结果不准时没有纠偏手段，关键信息容易"迷失在中间"（lost in the middle）

**solon-ai 落法**（对应系列四、五、七篇）

先备好两个模型与知识库（后文所有示例都复用这套底座）：

```yaml
# app.yml
solon.ai.chat:
  demo:
    apiUrl: "https://ai.moark.com/v1/chat/completions"   # 使用完整地址时不需要填写 standard
    model: "deepseek-flash"

solon.ai.embed:
  demo:
    apiUrl: "https://ai.moark.com/v1/embeddings"
    model: "bge-m3"
```

```java
@Configuration
public class RagConfig {
    @Bean
    public ChatModel chatModel(@Inject("${solon.ai.chat.demo}") ChatConfig config) {
        return ChatModel.of(config).build();
    }

    @Bean
    public EmbeddingModel embeddingModel(@Inject("${solon.ai.embed.demo}") EmbeddingConfig config) {
        return EmbeddingModel.of(config).build();
    }

    @Bean
    public InMemoryRepository repository(EmbeddingModel embeddingModel) {
        return new InMemoryRepository(embeddingModel); //内存知识库；生产建议换 Milvus/ES/Qdrant 等
    }
}
```

入库 + 检索 + 增强，三步就是 Naive RAG 的全部：

```java
@Component
public class NaiveRagService {
    @Inject InMemoryRepository repository;
    @Inject ChatModel chatModel;

    //入库：加载 -> 分割 -> 存储
    public void ingest(String file) throws IOException {
        List<Document> documents = new SplitterPipeline()
                .next(new TokenSizeTextSplitter(500))   //按 token 上限切块（默认 500）
                .split(new TextLoader(new File(file)).load());

        repository.save(documents);
    }

    //查询：检索 -> 增强 -> 生成
    public String ask(String message) throws IOException {
        List<Document> context = repository.search(message);                  //1.检索
        ChatMessage prompt = ChatMessage.ofUserAugment(message, context);     //2.消息增强（内部为字符串格式化）

        return chatModel.prompt(prompt).call().getMessage().getContent();     //3.生成
    }
}
```

### 3、Advanced RAG：在检索的"前、中、后"做优化

Naive 的效果不够，就在流水线的三个环节上加工，这是生产环境最常见的基线形态。

**检索前（切块与查询）**

- 语义分割：`SemanticSplitter`（v3.10.1+）借助嵌入模型在语义边界切分，块内主题更聚焦
- 分割管道：`SplitterPipeline` 把多个分割器串起来用（比如先按段落正则、再按 token 兜底）

```java
List<Document> documents = new SplitterPipeline()
        .next(new RegexTextSplitter("\n\n"))                 //先按段落粗切
        .next(new SemanticSplitter(embeddingModel))           //再按语义边界细切
        .next(new TokenSizeTextSplitter(500))                 //最后按 token 兜底，确保不超限
        .split(loader.load());
```

**检索中（条件控制）**

`QueryCondition` 是检索的"遥控器"（对应系列第六篇）：

```java
List<Document> context = repository.search(new QueryCondition(message)
        .limit(8)                       //多召回一些，给重排留余量
        .similarityThreshold(0.5)       //相似度阈值（默认 0.4）
        .filterExpression("category == 'manual' AND year >= 2025") //元数据过滤（SnEL 语法）
);
```

**检索后（重排）**

粗召回 8 条 → 重排模型精排 → 取头部几条，几乎是最划算的一步优化：

```java
solon.ai.rerank:
  demo:
    apiUrl: "https://api.moark.com/v1/rerank"
    apiKey: "......"
    standard: "giteeai"
    model: "bge-reranker-v2-m3"
```

```java
@Configuration
public class RerankConfig {
    @Bean
    public RerankingModel rerankingModel(@Inject("${solon.ai.rerank.demo}") RerankingConfig config) {
        return RerankingModel.of(config).build();
    }
}
```

```java
public String ask(String message) throws IOException {
    List<Document> context = repository.search(new QueryCondition(message).limit(8)); //1.粗召回
    context = rerankingModel.rerank(message, context);                                //2.精排
    context = SimilarityUtil.refilter(context.stream(), 4);                           //3.按评分截取头部（可选）

    ChatMessage prompt = ChatMessage.ofUserAugment(message, context);
    return chatModel.prompt(prompt).call().getMessage().getContent();
}
```

**特点**：召回质量显著提升，仍是固定流水线，成本与延迟可控。多数知识库问答做到这一档就够了。

### 4、Modular RAG：模块自由编排

把 RAG 拆成可插拔的模块（加载、分割、存储、检索、重排、增强），按业务自由编排成不同形态。solon-ai 的接口设计天然就是这个思路：

- `DocumentLoader` / `DocumentSplitter` / `Repository` / `RepositoryStorable` 全部是小接口，内置适配 + 自定义扩展；
- "常规检索"（Rdb、Es、MongoDb 的普通查询）和"矢量相关性检索"都能包成知识库，统一走 `search(QueryCondition)` 一个入口。

一个体现"编排自由"的例子——把联网搜索包成只读知识库，混入本地流水线：

```java
//把"联网搜索"当成一个动态知识库用（仍是被动检索的编排形态）
@Bean
public BochaWebSearchRepository webRepository(EmbeddingModel embeddingModel,
                                              @Inject("${solon.ai.repo.demo}") AiConfig config) {
    return new BochaWebSearchRepository(embeddingModel, config); //solon-ai-search-bocha
}

public String askWithWeb(String message) throws IOException {
    //联网搜 10 条 -> 相似度过滤取 4 条（太多，费 tokens）
    List<Document> context = webRepository.search(new QueryCondition(message).limit(10));
    context = SimilarityUtil.refilter(context.stream(), 4);

    return chatModel.prompt(ChatMessage.ofUserAugment(message, context))
            .call().getMessage().getContent();
}
```

> solon-ai 提供 Bocha、Tavily、Baidu 三家联网搜索知识库适配（`solon-ai-search-*` 插件），详见系列第十篇《示例：联网搜索知识库》。

**特点**：灵活性最高，可按场景定制流水线；代价是设计与维护复杂度。小团队建议先停留在 Advanced 档，别为了编排而编排。

### 5、混合检索与多路召回（Hybrid Search）

纯向量检索语义泛化好，但专有名词、型号、接口名这类精确匹配弱；BM25/全文检索正好相反。混合检索就是两路互补，融合打分。

solon-ai 在 Elasticsearch 适配（v3.3+）里内置了三种搜索类型：`SearchType.FULL_TEXT`、`SearchType.VECTOR`、`SearchType.HYBRID`：

```java
//构建带元数据字段的 ES 知识库
repository = ElasticsearchRepository.builder(embeddingModel, client)
        .addMetadataField(MetadataField.keyword("category"))
        .addMetadataField(MetadataField.numeric("year"))
        .build();

//混合检索：全文 + 向量，权重各占一半
List<Document> results = repository.search(new QueryCondition("solon framework")
        .searchType(SearchType.HYBRID)
        .hybridSearchParams(HybridSearchParams.of(0.7)) //向量权重 0.7，全文权重自动补到 0.3
        .limit(8));
```

如果向量库不是 ES（Milvus / PgVector / Redis…），`SearchType.HYBRID` 用不上——但思路可以照搬：**向量库走语义这一路，另建一个纯全文索引的 `ElasticsearchRepository` 走关键词这一路，手动双路召回后用重排模型融合**。因为 `FULL_TEXT` 只对 `content` 做 `match` 查询，全程不碰 embedding 字段，所以这个全文索引可以建得很轻（不占向量库内存），ES 索引里还能同时做存量的业务全文检索：

```java
//① 语义这一路：向量库任选（PgVector / Milvus / Redis…）
Repository vectorRepo = PgVectorRepository.builder(embeddingModel, dataSource).tableName("kb_chunks").build();

//② 关键词这一路：ES 索引只做全文（FULL_TEXT 不碰 embedding，索引可以建得很轻）
Repository keywordRepo = ElasticsearchRepository.builder(embeddingModel, client)
        .indexName("kb_fulltext")
        .build();

List<Document> vectorHits = vectorRepo.search(new QueryCondition(query).limit(8));
List<Document> keywordHits = keywordRepo.search(new QueryCondition(query)
        .searchType(SearchType.FULL_TEXT)   //纯 BM25/match 检索，不做向量
        .limit(8));

//③ 两路合并去重（按 ID），交给重排模型统一精排——重排模型天然就是融合器
List<Document> merged = Stream.concat(vectorHits.stream(), keywordHits.stream())
        .collect(Collectors.toMap(Document::getId, d -> d, (a, b) -> a)) //按 ID 去重，重复命中优先保留向量路结果
        .values().stream().collect(Collectors.toList());
List<Document> reranked = rerankingModel.rerank(query, merged);
```

入库时给两个库各写一份（`vectorRepo.save(docs); keywordRepo.save(docs);`）即可，ID 一致方便去重与追溯。

若连重排模型都不想接，退化方案是对两路结果按排名做 **RRF（Reciprocal Rank Fusion）** 融合——rank 固定权重 `1 / (k + rank)`，`k` 取 60：`score = Σ 1/(60 + rank_i)`，同名文档跨两路累加。实现只要十来行，不再展开。

连 ES 全文索引都不想维护的场景，"多路召回 + 重排"就是最后的平替：`InMemoryRepository` / 向量库一路、`WebSearchRepository` / 关键词库另一路，结果合并后交给 `RerankingModel.rerank()` 统一精排即可。

**特点**：互补两路短板，工业界标配；代价是要维护两套索引或一次多路调用。

### 6、结构化 RAG：Text2SQL 的"solon-ai 打法"

数据在数据库里、要的是精确数字（"上季度华东区销售额"），向量检索是错误工具——正确做法是让模型把问题转成 SQL，查询结果作为上下文回填。

solon-ai 的打法（对应系列第一篇"常规检索"）：**查询结果只要能转成字符串，就能用同一个入口增强消息**：

```java
//用普通数据库查询结果做"常规检索"增强
List<Map<String, Object>> rows = db.sql("select region, amount from sales where quarter = ?", q).getMapList();

ChatMessage prompt = ChatMessage.ofUserAugment(message, rows); //上下文只要能转为字符串就行
ChatResponse resp = chatModel.prompt(prompt).call();
```

更进一步，把 SQL 生成交给模型自己：注册一个"销售查询"工具（`@ToolMapping`），LLM 按需调用——这其实就是下一节 Agentic RAG 的思路，说明流派之间边界本来是模糊的。

**特点**：结果精确、天然支持聚合统计；依赖 schema 质量与生成查询的校验（只读账号、白名单表是底线）。

### 7、控制型 RAG：Self-RAG / CRAG / Adaptive RAG

这一族的做法不改变"被动检索"的骨架，而是给流水线加上**评估与纠错**：

- **Self-RAG（自反思）**：模型自己决定要不要检索，并自评回答有没有依据。学术版需要微调模型；工程实践里常用"相似度阈值"近似——检索结果都不达标，就明确告知模型"无可靠上下文，请说明或追问"。
- **CRAG（纠错）**：检索质量差就触发纠错——改写关键词重搜，或转向联网搜索兜底。
- **Adaptive RAG（自适应）**：先判断问题复杂度——简单题直接答、中等题单轮检索、复杂题多轮检索，按需分配算力。

**solon-ai 落法**（三者可以组合在同一段业务代码里）：

```java
public String askGuarded(String message) throws IOException {
    List<Document> context = repository.search(new QueryCondition(message).limit(8));

    //① Self-RAG 的工程近似：用相似度阈值评估检索质量
    if (context.isEmpty()
            || context.stream().noneMatch(d -> d.getScore() >= 0.55)) {
        //② CRAG 兜底：本地知识不够，转向联网搜索知识库重查
        context = webRepository.search(new QueryCondition(message).limit(5));
    }

    ChatMessage prompt = ChatMessage.ofUserAugment(message, context);

    //③ Adaptive RAG 的分级思路：问句太长（复杂），走流式慢慢答；简单问句一次 call
    if (message.length() > 100) {
        chatModel.prompt(prompt).stream()               //流式：Flux<ChatEvent>，订阅后开始执行
                .subscribe(e -> { /* 处理增量事件 */ });
        return "(已进入流式应答)";
    }

    return chatModel.prompt(prompt).call().getMessage().getContent();
}
```

> 上例里的复杂度判断（`length > 100`）只是示意。工程上更稳的做法是用一次轻量模型调用做意图分级，再路由到不同档位；或干脆交给下一节的 ReActAgent 让模型自决。

**特点**：对"检索到垃圾"的鲁棒性最好，成本可控；分级策略需要根据业务调参。

### 8、Agentic RAG：从被动到主动

前面所有做法有个共同点：**检索发生在提问之前，由代码决定**。Agentic RAG 把检索行为从"预处理阶段"移入"模型推理阶段"——模型自己决定要不要查、查什么、查几轮、结果不好要不要换个关键词再查。solon-ai 提供了现成的三级跳：

**阶段 A：把知识库变成工具**

`RepositoryTool`（v3.10.1+）把任意 `Repository` 封装成 LLM 可调用的工具：

```java
RepositoryTool repositoryTool = new RepositoryTool(repository, rerankingModel);

ChatModel agent = ChatModel.of(apiUrl)
        .apiKey(apiKey).model(model)
        .defaultToolAdd(repositoryTool)   //挂载知识库工具
        .build();

agent.prompt("刘德华今年有几场演唱会？").call();  //LLM 自行决定是否调用、怎么调用
```

工具内部已做多关键词批量检索（最多 5 个词，每个词独立检索后汇总）、结果格式化（标题/相关度/内容/引用地址）与长度截断保护（单条内容超 2000 字符截断）。

**阶段 B：升级 ReActAgent，支持多轮推理校准**

复杂任务需要"思考-行动-观察"循环时，用 `solon-ai-agent` 的 ReActAgent：

```java
ReActAgent agent = ReActAgent.of(chatModel)
        .name("知识助理").role("检索增强问答")
        .defaultToolAdd(repositoryTool)
        .build();

agent.prompt("对比一下 A、B 两个产品在 2025 年的价格策略").call();
```

模型会自己拆问题、多轮检索、交叉比对——这正是 Adaptive/Iterative RAG 在 Agent 形态下的自然实现。

**阶段 C：混合工具链**

知识库工具 + 联网搜索 + 网页抓取 + 代码搜索组合挂载，Agent 获得跨数据源的"感官"：

```java
ReActAgent agent = ReActAgent.of(chatModel)
        .defaultToolAdd(repositoryTool)      //私有知识库
        .defaultTalentAdd(new WebsearchTalent()) //全网实时信息（solon-ai-talent-web）
        .defaultTalentAdd(new WebfetchTalent())  //指定 URL 内容抓取
        .build();
```

> Talents 是 solon-ai 的"才能"组件体系（每个 Talent 可携带一组工具），`WebsearchTalent` / `WebfetchTalent` 来自 `solon-ai-talent-web` 插件（v3.9.6+）；若只想要单个工具，也可以从 Talent 里取出工具后用 `defaultToolAdd` 挂载。

**特点**：上限最高，能处理复杂、模糊、多跳的问题；代价是延迟高、token 消耗大、行为不易复现。适合深度研究型任务，不适合高并发的简单问答。

### 9、选型建议：一张决策路径

```
问题形态？
├─ 要精确数字/统计，数据在数据库 ──────────────> 结构化 RAG（查询结果直接增强）
├─ 全局性总结、多跳关系（"这批文档的共同主题"） ──> GraphRAG 思路（solon-ai 暂无内置适配，
│                                                 可用 ReActAgent 多轮检索 + LLM 归纳近似）
├─ 普通知识库问答
│    ├─ 第一档：Naive（search + ofUserAugment）——先跑通
│    ├─ 第二档：+ 语义分割 / QueryCondition / Rerank —— 上生产的主流基线
│    ├─ 第三档：+ 混合检索或多路召回 —— 专有名词、型号类查询多时
│    └─ + 相似度阈值守卫 / 联网兜底 / 分级路由 —— 对幻觉敏感、鲁棒性要求高时
└─ 复杂研究型、多数据源、开放式任务 ────────────> Agentic RAG（RepositoryTool + ReActAgent）
```

几条实战心法：

1. **先把检索质量做扎实，再上花活**。多数 RAG 效果差，问题出在检索而不是生成：切块（`SplitterPipeline`）→ 召回条件（`QueryCondition`）→ 重排（`RerankingModel`），这三步的收益远大于架构升级。
2. **元数据从入库那一刻就带上**。`new Document(content, metadata)`（或 `.metadata("file", ...)` 链式补写）+ 检索时的 `filterExpression(...)`，是成本最低的精度增强。
3. **被动流水线与主动 Agent 不冲突**。可以在系统里并存：高频简单问答走 Advanced 流水线（快、便宜），疑难工单/研究任务路由给 ReActAgent（准、贵）。
4. **别迷信流派**。选型的依据是问题形态与成本预算，不是论文的新旧。

### 附：各流派在 solon-ai 中的组件速查

| 流派 | 核心组件（所在模块） | 关键 API |
|---|---|---|
| Naive RAG | `solon-ai`（core） | `InMemoryRepository`、`repository.search()`、`ChatMessage.ofUserAugment()` |
| Advanced RAG | `solon-ai`（core） | `SplitterPipeline`、`SemanticSplitter`、`QueryCondition`、`RerankingModel.rerank()`、`SimilarityUtil.refilter()` |
| Modular RAG | `solon-ai`（core）+ loaders/repositorys 插件 | `DocumentLoader` / `DocumentSplitter` / `Repository` / `RepositoryStorable` 自由组合 |
| 混合检索 | `solon-ai-repo-elasticsearch` | `SearchType.HYBRID`、`HybridSearchParams.of(vectorWeight)`、`MetadataField` |
| 结构化 RAG | 任意数据访问组件 | 查询结果（可 toString 的任意集合）+ `ChatMessage.ofUserAugment()` |
| Self/CRAG/Adaptive | `solon-ai`（core）+ search 插件 | 相似度阈值 + `WebSearchRepository` 兜底 + 复杂度路由 |
| Agentic RAG | `solon-ai`（core）+ `solon-ai-agent`（+ `solon-ai-talent-web`） | `RepositoryTool`、`ChatModel.defaultToolAdd()`、`ReActAgent.of()`、`defaultTalentAdd()` |

> 版本说明：本文示例基于 solon-ai v3.10.1+（`RepositoryTool`、`SemanticSplitter` 均自此版本引入；混合检索自 v3.3 起）。联网搜索知识库适配在 `solon-ai-rag-searchs` 仓库下（bocha / tavily / baidu）；Agent 的联网工具（`WebsearchTalent` / `WebfetchTalent`）在 `solon-ai-talent-web` 插件。文中 API 均以当前源码为准核对。

