在这里插入图片描述

核心指标:从 17.95% 到 56.42% 准确率跃升——7 大技术让你的 RAG 系统告别"检索幻觉"


📋 阅读指南

章节 核心内容 适合读者
一、为什么 Naive RAG 会"落地即失效" 问题诊断与量化分析 所有读者
二、HOPE 框架:切片质量的根基优化 分块策略原理与实践 后端/AI 工程师
三、混合检索:BM25 + 向量双引擎方案 混合检索架构与代码 AI 工程师
四、查询转换:HyDE 与子问题分解 查询重写与多路召回 AI 工程师
五、智能路由与 CRAG 自愈检索 检索纠错与自适应路由 高级工程师
六、重排序:Cross-Encoder 精准狙击 精排模型原理与实战 AI 工程师
七、语义缓存:50ms 响应的性能杀手锏 缓存架构与阈值策略 后端工程师
八、全链路生产级部署 Checklist 完整落地检验表 技术负责人

前置知识:本文假设读者已了解向量数据库基础和 LlamaIndex v0.10+ 基本用法。如果你还不熟悉,建议先阅读本系列第 1-3 篇。


一、为什么 Naive RAG 会"落地即失效"?

在将 RAG(检索增强生成)系统从实验室原型推向企业生产环境的过程中,开发者往往会遭遇"落地即失效"的窘境:Demo 效果惊艳,上线后一塌糊涂。

核心原因只有一个:向量相似度 ≠ 逻辑相关性。

Naive RAG 依赖"切片-嵌入-检索-生成"的简单流水线。这套管道在受控实验室环境中表现尚可,但在企业真实数据(专有名词多、文档质量参差不齐、查询意图多样)面前,会暴露出三个致命缺陷。

1.1 三大致命缺陷详解

缺陷一:语义相关,但事实无关

向量搜索擅长捕捉语义共性,但缺乏对精确事实的辨析能力。

真实案例:某电商平台产品问答系统

  • 用户查询:"A100 显卡的显存容量是多少?"
  • 检索到的文档 A:"A100 显卡配备 80GB HBM2e 显存,适合大规模 AI 训练..."(正确)
  • 检索到的文档 B:"RTX 4090 显卡的安装手册,本产品采用 NVIDIA 最新架构..."(无关,但排名第 2)

为什么会这样? 因为"显卡"、"NVIDIA"等关键词在向量空间中距离很近,Embedding 模型将"A100"和"RTX 4090"都识别为"高端 GPU 产品"类别。向量搜索无法区分"A100 的显存容量"和"RTX 4090 的安装说明"这两个完全不同的事实需求。

后果:LLM 收到错误上下文后,可能回答"RTX 4090 的显存为 24GB",完全偏离用户真实意图。

缺陷二:对干扰项极度敏感

研究显示,LLM 对召回结果中的噪声数据脆弱程度令人震惊。

当检索器引入干扰项(Distracting passages)时,即使是 Llama2 等强力模型,准确率也会:

正常召回 → 准确率 56.42%
混入干扰项 → 准确率 17.95%(断崖式下跌 68%)

工程意义:如果你召回了 5 个文档,其中只有 1 个是无关的,LLM 仍然有 30% 以上的概率被带偏。在客服、医疗、金融等高风险场景中,这种错误代价极高。

缺陷三:检索噪声的连锁反应

错误的上下文不只是"没帮助",它会主动损害生成质量。

LLM 在收到错误信息后,会产生"上下文优先于参数知识"的倾向——放弃其训练中获得的正确知识,强行拟合错误信息,引发严重幻觉。

用一句话概括:Naive RAG 不是"检索到什么就回答什么",而是"检索到什么错误信息,模型就会一本正经地胡说八道"。

1.2 Naive RAG vs 生产级 RAG 量化对比

维度 Naive RAG 生产级 RAG 差距分析
检索精度 纯语义,无法处理精确事实 混合检索 + 重排序 准确率提升 200-300%
幻觉率 极高,噪声直接导致幻觉 低,CRAG 纠错机制 错误率降低 60-80%
响应延迟 线性调用,通常 >3s 语义缓存 + 自适应路由 延迟降低 ≥95%
扩展性 单一策略,复杂查询失效 智能路由,动态策略选择 查询覆盖率提升 150%
维护成本 低(简单但脆弱) 中(模块化,可观测) 长期 ROI 更优

1.3 生产级 RAG 的整体架构

在深入各个技术模块之前,先建立全局视图:

命中 < 50ms

未命中

正确 置信度>0.8

模糊 0.3-0.8

错误 置信度<0.3

用户查询

语义缓存命中?

直接返回缓存结果

查询转换模块

HyDE 假设文档生成

子问题分解

混合检索引擎

BM25 关键词检索

向量语义检索

RRF 融合排序

Cross-Encoder 重排序

CRAG 质量评估

LLM 生成答案

知识补偿:内部+外部

Web 搜索兜底

写入语义缓存

这张架构图展示了本文所有技术模块的协作方式。接下来,我们逐层深入每个模块。


二、根基优化:HOPE 框架下的切片质量标准

一个反直觉的真相:90% 的 RAG 检索问题,根源不在检索算法,而在切片策略。

再好的检索引擎,也无法从"残缺的切片"中找到完整答案。基于 HOPE (Holistic Passage Evaluation) 框架,我们需要重新审视切片的三个原则。

2.1 HOPE 三大原则详解

原则一:语义独立性(Semantic Independence)——核心性能之王

定义:切片的含义不应依赖于未被检索到的上下文。

为什么是"核心性能之王"?

想象你在读一本书,如果只看某一页,你能否理解这一页在说什么?如果不能,这一页的"语义独立性"不足。在 RAG 系统中,LLM 只能看到检索到的几个切片——如果每个切片都需要其他切片才能被理解,LLM 就必然"断章取义"。

量化价值

  • 优化语义独立性 → 事实准确性提升 56.2%
  • 同时带来 → 回答正确性提升 21.1%

工程实践要点

  • 放弃固定字符切分,采用语义边界感知的切分策略
  • 每个切片需包含"自包含"信息(完整的人名、产品名、时间、地点等实体)
  • 避免将概念定义和对应示例拆分到不同切片

反例与正例

原文:"Python 的 match-case 语法从 3.10 版本引入,用于模式匹配。
      以下是一个示例:match command:
          case 'quit': ..."

❌ 错误切片:
  切片1:"Python 的 match-case 语法从 3.10 版本引入,用于模式匹配。"
  切片2:"以下是一个示例:match command: case 'quit': ..."
  → 切片2 脱离上下文后,无法理解这是什么语言的什么功能

✅ 正确切片:
  切片1(完整):"Python 的 match-case 语法从 3.10 版本引入,用于模式匹配。
                 示例:match command: case 'quit': ..."
  → 保持语义独立,单独检索到也能理解完整含义
原则二:信息完备性(Collective Information Preservation)

定义:确保文档中的原子事实在切片过程中不产生"信息损耗"。

什么是原子事实损耗?

原文:"Python 3.10 及以上版本支持 match-case 语法。"

❌ 错误切片:
  片段A:"Python 3.10 及以上版本支持"
  片段B:"match-case 语法。"
  
  → 片段B 丢失了"3.10 及以上版本"这个关键版本限制。
    用户问"Python 支持 match-case 吗",LLM 可能回答"支持",
    但实际上 Python 3.9 不支持——这是一个典型的幻觉场景。

✅ 正确处理:保持完整句子不被切断,保护原子事实的完整性

工程原则:宁可让切片稍微超过目标长度,也不要为了强行对齐 chunk_size 而截断一个完整的事实陈述。

原则三:单核概念(Concept Unity)——颠覆性洞察

传统观点:切片应只含单一概念,避免"主题漂移"。

HOPE 研究颠覆结论:概念统一性与系统性能呈负相关或微弱相关

为什么过分追求单核概念会适得其反?

现代 Embedding 模型在预训练时接触了大量"多概念混合"的文本(技术博客的"概念+代码+配置"三段式结构就是典型)。如果你强行将这些内容拆成多个"纯概念"切片,反而破坏了模型对这些混合语义结构的理解能力。

工程建议:在生产中,宁可让一个切片包含 2-3 个相关概念,也不要为了"纯粹性"牺牲上下文完整性。

✅ 推荐做法:
"概念解释 + 代码示例"作为一个切片
  → 检索效果优于将两者拆分

❌ 反直觉的错误做法:
为追求"单概念",把概念和代码强行分开
  → 实际降低了检索和生成质量

2.2 传统切片 vs HOPE 优化切片对比

✅ HOPE 框架优化切片

原始文档

AST 语义边界解析

切片1: 完整概念+示例

切片2: 独立可理解段落

切片3: 完整代码块

❌ 传统固定长度切片

原始文档

按 512 字符强制截断

切片1: 概念不完整

切片2: 缺失上下文

切片3: 代码被截断

2.3 Higress 生产级 AST 切片实践

Higress-RAG 放弃"字符长度切分",转而采用结构感知切分:

  1. Markdown AST-based 分隔符:解析抽象语法树,在标题和段落边界处切分
  2. 代码块保护(Code Block Protection):确保配置项、代码逻辑等核心单元在物理上不被切断
  3. 实体保护:检测并保护命名实体(产品型号、版本号、人名等)不被截断

数据流转图

原始文档

Higress-Native Splitter

Step1: Markdown AST 解析

Step2: 代码块边界识别

Step3: 实体完整性检测

生成候选切片集 P

HOPE 评估流程

语义独立性评估
单独是否可理解?

信息完备性评估
原子事实是否完整?

概念相关性评估
是否保持上下文?

加权 HOPE 总分

分数 ≥ 阈值?

写入向量索引

触发切片重分配

2.4 LlamaIndex 中实现语义感知切片

from llama_index.core.node_parser import (
    MarkdownNodeParser,
    CodeSplitter,
    SentenceSplitter
)
from llama_index.core.node_parser import HierarchicalNodeParser

# 方案一:Markdown 结构感知切片(推荐用于技术文档)
markdown_parser = MarkdownNodeParser(
    # 在 Markdown 标题边界处切分,天然保证语义独立性
    include_metadata=True,
    include_prev_next_rel=True  # 保留前后切片关系,用于上下文补偿
)

# 方案二:代码感知切片(推荐用于代码库文档)
code_splitter = CodeSplitter(
    language="python",
    chunk_lines=40,           # 按行数切,而非字符数
    chunk_lines_overlap=5,    # 保留 5 行重叠,避免函数定义断层
    max_chars=1500
)

# 方案三:层次化切片(推荐用于长文档 RAG)
# 同时生成粗粒度和细粒度切片,构建层次化索引
hierarchical_parser = HierarchicalNodeParser.from_defaults(
    chunk_sizes=[2048, 512, 128]
    # 2048: 大段落,用于理解全局上下文
    # 512:  段落级别,用于精确检索
    # 128:  句子级别,用于精细定位
)

# 实际使用示例
from llama_index.core import SimpleDirectoryReader, VectorStoreIndex

documents = SimpleDirectoryReader("./docs").load_data()

# 解析为节点(切片)
nodes = markdown_parser.get_nodes_from_documents(documents)

# 验证切片质量
for node in nodes[:3]:
    print(f"切片长度: {len(node.text)} 字符")
    print(f"切片内容预览: {node.text[:100]}...")
    print(f"来源文档: {node.metadata.get('file_name', 'unknown')}")
    print("---")

切片参数调优指南

文档类型 推荐解析器 chunk_size overlap 原则
Markdown 技术文档 MarkdownNodeParser 按标题边界 - 标题即语义边界
代码库文档 CodeSplitter 40 行/切片 5 行 保护函数完整性
长篇 PDF 报告 HierarchicalNodeParser [2048, 512] 50 字符 层次化粒度
通用文本 SentenceSplitter 512 token 50 token 句子边界优先

三、混合检索:BM25 + 向量的"双引擎"方案

在技术文档场景中,精确匹配(关键词)与模糊语义(向量)缺一不可。这不是一个"选哪个更好"的问题,而是必须同时使用两者。

3.1 为什么单一检索不够?

混合检索 双引擎覆盖

两者优势叠加

互补短板

向量密集检索 擅长领域

同义词: 显存 ≈ GPU Memory

语义改写: 内存 ≈ Memory

意图理解: 多快 ≈ 延迟多低

模糊查询: 大概什么配置

BM25 稀疏检索 擅长领域

专有名词: A100

代码 ID: HTTP-404

产品型号: bge-v2-m3

精确数字: 80GB

单一策略的短板

  • 只用 BM25:无法处理同义词和语义改写。用户搜索"显卡显存",文档写"GPU memory",BM25 无法匹配
  • 只用向量:对专有名词和精确数字容易出错。“A100"和"RTX 4090"都是"高端 GPU”,向量距离很近

3.2 RRF:倒数排名融合算法(为什么不用简单加权?)

由于 BM25 得分(通常 >20)与向量相似度(0~1 之间)量纲完全不同,直接加权求和会产生严重的"分数失真"——BM25 的得分会在数值上"淹没"向量分数。

RRF(Reciprocal Rank Fusion)的解决方案

s c o r e ( d ) = ∑ r ∈ R 1 k + r a n k r ( d ) score(d) = \sum_{r \in R} \frac{1}{k + rank_r(d)} score(d)=rRk+rankr(d)1

其中:

  • d d d:文档
  • R R R:所有检索器的结果集合
  • r a n k r ( d ) rank_r(d) rankr(d):文档 d d d 在检索器 r r r 中的排名(从 1 开始)
  • k k k:平滑常数(通常取 60,防止排名第 1 的文档得分过高)

RRF 核心洞察:只关心排名位置,不关心原始分数。两个引擎都认可的文档,RRF 分数自然更高。

实例计算

文档 BM25 排名 向量排名 RRF 得分 最终排名 解读
文档 A 1 5 1/(60+1) + 1/(60+5) = 0.0316 1 BM25 强势,向量中等
文档 B 3 2 1/(60+3) + 1/(60+2) = 0.0320 2 两个引擎都认可
文档 C 2 8 1/(60+2) + 1/(60+8) = 0.0309 3 BM25 较好,向量一般
文档 D 10 1 1/(60+10) + 1/(60+1) = 0.0307 4 向量强势但 BM25 弱

→ 文档 B 虽然在两个检索器中都不是第一,但因为两者都认可,综合排名反而最高。这正是 RRF 的精髓。

3.3 LlamaIndex 混合检索实战代码

from llama_index.core.retrievers import QueryFusionRetriever
from llama_index.core.indices.vector_store import VectorIndexRetriever
from llama_index.retrievers.bm25 import BM25Retriever
from llama_index.core import VectorStoreIndex, StorageContext
import jieba  # 中文分词,BM25 中文支持必需

# ===== 前置准备:构建两种索引 =====
# 向量索引(存储 Embedding)
vector_index = VectorStoreIndex.from_documents(documents)

# BM25 索引(存储分词后的关键词信息)
# 注意:BM25 需要从已有的节点构建,不能直接从文档
from llama_index.retrievers.bm25 import BM25Retriever
bm25_retriever = BM25Retriever.from_defaults(
    nodes=vector_index.docstore.docs.values(),  # 复用已有节点
    similarity_top_k=5,
    # 中文场景:自定义分词函数(默认按空格分词,中文效果差)
    # tokenizer=lambda text: list(jieba.cut(text))
)

# ===== 步骤 1:配置向量检索器 =====
vector_retriever = VectorIndexRetriever(
    index=vector_index, 
    similarity_top_k=5  # 召回 Top 5 候选文档
)

# ===== 步骤 2:配置 BM25 关键词检索器 =====
# 需要安装:pip install llama-index-retrievers-bm25
bm25_retriever = BM25Retriever.from_defaults(
    index=vector_index,
    similarity_top_k=5
)

# ===== 步骤 3:使用 RRF 融合排名信号 =====
hybrid_retriever = QueryFusionRetriever(
    [vector_retriever, bm25_retriever],
    similarity_top_k=5,         # 融合后最终保留的文档数
    num_queries=1,              # 设为 1 禁用查询扩展(技术文档场景推荐)
    mode="reciprocal_rank_fusion",  # 使用 RRF 算法
    use_async=True              # 异步并行执行,显著降低延迟
)

# ===== 步骤 4:执行混合检索 =====
results = hybrid_retriever.retrieve("A100 显卡显存容量")

# 打印检索结果
for i, node in enumerate(results):
    print(f"排名 {i+1}: 得分={node.score:.4f}")
    print(f"内容预览: {node.text[:100]}...")
    print()

关键参数说明

参数 说明 调优建议
similarity_top_k(各检索器) 每个检索器召回的候选数 越大召回率越高,但延迟增加;建议 5-10
similarity_top_k(融合后) 最终保留的文档数 通常小于各检索器的 top_k;建议 3-5
num_queries 查询扩展数量 技术文档设 1(精确匹配为主);开放域设 2-4
mode 融合算法 生产环境强烈推荐 reciprocal_rank_fusion
use_async 是否异步并行 生产环境务必设为 True

💡 RRF vs alpha 参数的区别

RRF 是基于排名的融合算法,不受 BM25 和向量检索原始分数量纲差异的影响,因此不需要 alpha 参数。 alpha 更多用于向量数据库(如 Pinecone、Milvus)自带的基于得分加权的混合检索接口。 生产环境中,RRF 的鲁棒性通常优于简单的得分加权,推荐优先使用 RRF。

场景参数调优参考表

场景 向量 top_k BM25 top_k 融合 top_k num_queries
技术文档/代码库 5 5 3 1
企业知识库 10 10 5 1
通用问答 10 5 5 2
开放域搜索 15 10 10 3-4

四、查询转换:HyDE 与子问题分解

用户提问往往极其简略,与文档的表述方式存在"语义鸿沟"。查询转换的目标是理解用户的真实意图,将查询重写成更容易匹配到正确答案的形式。

4.1 什么是"语义鸿沟"?

用户查询风格:  "A100 显存多大?"       → 口语化、简短、问句
文档内容风格:  "NVIDIA A100 Tensor Core GPU 配备 80GB HBM2e 高带宽显存..." 
                                        → 正式、完整、陈述句

这两段文本的语义是高度相关的,但在向量空间中的距离可能并不近——因为风格、长度、结构都差异巨大

4.2 HyDE:假设性文档嵌入

HyDE(Hypothetical Document Embeddings)的核心思想是:

与其用"问题"去匹配"答案文档",不如先让 LLM 生成一个"假设答案",再用"假设答案"去匹配"真实答案文档"——Answer-to-Answer 匹配效果远好于 Question-to-Answer。

工作流程

文档库 向量检索引擎 LLM(生成假设答案) HyDE 转换器 用户 文档库 向量检索引擎 LLM(生成假设答案) HyDE 转换器 用户 "A100 显存多大?" 根据查询生成假设答案文档 "NVIDIA A100 GPU 配备了 80GB HBM2e 显存,\n这是目前数据中心级 GPU 中最大的显存容量之一,\n特别适合大模型训练场景..." 用假设答案做向量检索 相似度检索 召回真实文档(风格与假设答案相近) Top-K 真实文档 基于真实文档生成的精准答案

HyDE 为什么有效?

假设答案和真实文档在以下维度上高度相似:

  • 写作风格(都是陈述句)
  • 内容密度(都包含具体数值和专业术语)
  • 语言模式(都是技术性描述)

因此,用假设答案检索真实文档,语义距离远小于用原始问题检索。

适用场景与风险

场景 HyDE 效果 注意事项
技术文档查询 ⭐⭐⭐⭐⭐ 极佳 理想场景
产品知识库 ⭐⭐⭐⭐ 良好 需确保 LLM 有足够背景知识
时效性强的查询 ⭐⭐ 一般 LLM 可能生成过时的假设答案
极度专业/冷门领域 ⭐⭐ 一般 LLM 假设答案质量可能偏低

4.3 子问题查询引擎

对于跨领域或跨文档的复杂查询,直接检索往往效果不佳。更好的方式是将复杂查询分解为多个子查询,分别检索后再综合。

案例演示

用户查询(复杂):"对比 A 产品与 B 产品的安全架构"

自动分解为子查询:
├── 子查询1:"A 产品的身份认证与加密机制是什么?"
│   → 专门检索 A 产品文档
├── 子查询2:"B 产品的安全边界与隔离方案是什么?"
│   → 专门检索 B 产品文档
└── 子查询3:"A 与 B 在安全合规性方面的核心差异是什么?"
    → 检索对比分析类文档

最后由 LLM 综合三个子查询结果,生成完整对比答案

为什么拆分有效?

向量数据库中的文档通常是独立存储的。一个复合查询"A 和 B 的对比"很难同时召回两个产品的核心文档——因为没有单一文档会同时深入介绍两个竞争产品。拆分后,每个子查询专注于一个明确目标,召回率显著提升。

4.4 LlamaIndex 查询转换完整代码

# ==================== HyDE 集成 ====================
from llama_index.core.indices.query.query_transform import HyDEQueryTransform
from llama_index.core.query_engine import TransformQueryEngine

# 创建基础查询引擎
base_query_engine = index.as_query_engine(similarity_top_k=5)

# 创建 HyDE 查询转换器
# include_original=True:同时保留原始查询,防止假设答案质量过低时完全偏离
hyde = HyDEQueryTransform(
    include_original=True,
    llm=llm  # 可选:指定生成假设答案的 LLM(默认使用全局 LLM)
)

# 包装为 HyDE 查询引擎
hyde_query_engine = TransformQueryEngine(
    query_engine=base_query_engine,
    query_transform=hyde
)

# 执行 HyDE 查询
# 内部流程:用户查询 → LLM 生成假设答案 → 假设答案做向量检索 → 生成最终答案
response = hyde_query_engine.query("A100 显存容量")
print(response)

# ==================== 子问题查询引擎 ====================
from llama_index.core.query_engine import SubQuestionQueryEngine
from llama_index.core.tools import QueryEngineTool, ToolMetadata

# 为不同数据源创建查询引擎工具
tool_product_a = QueryEngineTool(
    query_engine=query_engine_a,  # A 产品文档的查询引擎
    metadata=ToolMetadata(
        name="product_a_docs",
        description="包含 A 产品的技术文档、安全架构说明、API 参考"
        # ⚠️ 描述越准确,Router LLM 的路由决策越精准
    )
)

tool_product_b = QueryEngineTool(
    query_engine=query_engine_b,  # B 产品文档的查询引擎
    metadata=ToolMetadata(
        name="product_b_docs",
        description="包含 B 产品的技术文档、安全架构说明、部署指南"
    )
)

# 创建子问题查询引擎
# 它会:1) 分解复杂查询 2) 并行执行子查询 3) 综合结果生成答案
sub_question_engine = SubQuestionQueryEngine.from_defaults(
    query_engine_tools=[tool_product_a, tool_product_b],
    verbose=True  # 开启日志,可看到子问题分解过程
)

# 执行复杂对比查询
response = sub_question_engine.query("对比 A 与 B 的安全架构差异,哪个更适合金融场景?")
print(response)

查询转换策略选型指南

收到用户查询

是否包含对比/多目标?

使用子问题引擎
SubQuestionQueryEngine

查询是否口语化/简短?

使用 HyDE
HyDEQueryTransform

是否含明确专业术语?

直接混合检索
QueryFusionRetriever


五、智能路由与 CRAG 自愈检索

5.1 自适应路由(Adaptive Routing)

不同的查询需要不同的检索策略。让每一个查询都经过完整的多步骤处理流程,既浪费资源,也增加延迟。智能路由的目标是:让正确的查询走正确的路径

路由决策矩阵

用户查询示例 复杂度判断 路由策略 典型延迟
“公司地址是什么?” 语义缓存直接返回 <50ms
“A100 显存多大?” 向量检索 + Top 3 重排序 200-500ms
“比较我们系统与竞品的安全架构” 子问题分解 + 多索引检索 1-3s

RouterQueryEngine 工作原理

低复杂度
简单事实

中复杂度
技术细节

高复杂度
跨文档推理

用户查询

Router LLM 评估

复杂度判断

语义缓存
+ 轻量向量搜索

混合检索
+ Cross-Encoder 重排序

子问题分解
+ 多索引并行检索

LLM 生成答案

5.2 CRAG:纠错型检索(让 RAG 具备"自我怀疑"能力)

核心思想:检索不是终点,而是决策点。

CRAG(Corrective Retrieval Augmented Generation)在传统 RAG 基础上增加了一个"评估器",评估检索结果的质量,并根据结果质量选择不同的处理路径。

CRAG 三种处理路径详解

路径一:正确(Correct)

判定标准:检索结果与用户查询高度相关,置信度 > 0.8。

处理方式:直接执行 LLM 生成,无需额外操作。

查询:   "A100 显存容量"
检索结果:"NVIDIA A100 配备 80GB HBM2e 显存"
评估结论:置信度 0.95 → ✅ 正确,直接生成

路径二:错误(Incorrect)

判定标准:检索结果完全不相关,或包含明显错误信息,置信度 < 0.3。

处理方式:丢弃内部检索结果,触发外部 Web 搜索(如 Tavily API)。

查询:   "GPT-4o 最新版本的发布日期"
检索结果:文档库中最新记录是 2023 年的 GPT-4
评估结论:置信度 0.12 → ❌ 错误(知识已过时),触发 Web 搜索

路径三:模糊(Ambiguous)

判定标准:检索结果部分相关,信息不完整或存在歧义,置信度在 0.3~0.8 之间。

处理方式:同时利用内部文档和外部 Web 资源,进行知识补偿融合。

查询:   "LlamaIndex 的 CRAG 支持哪些评估模型?"
检索结果:文档提到了 CRAG 概念,但没有列出支持的模型
评估结论:置信度 0.55 → ⚠️ 模糊,同时检索内部文档 + GitHub 最新 Release

5.3 CRAG 完整工作流程

混合检索引擎

传入 Query + Retrieved Docs

置信度 > 0.8

置信度 < 0.3

0.3 ≤ 置信度 ≤ 0.8

提取关键信息

触发 Tavily / Bing API

内部文档 + Web 搜索并行

用户查询

初始检索

质量评估器

正确路径

错误路径

模糊路径

知识精炼

Web搜索

知识补偿

LLM生成

最终答案

5.4 LlamaIndex 智能路由 + CRAG 完整实现

# ==================== 智能路由引擎 ====================
from llama_index.core.query_engine import RouterQueryEngine
from llama_index.core.selectors import LLMSingleSelector, PydanticSingleSelector
from llama_index.core.tools import QueryEngineTool, ToolMetadata

# 创建三种不同复杂度的查询引擎
# 引擎1:简单查询 - 带缓存的轻量引擎
simple_engine = vector_index.as_query_engine(
    similarity_top_k=3,
    response_mode="compact"  # 紧凑模式,适合简单事实查询
)

# 引擎2:中等查询 - 带重排序的精确引擎
advanced_engine = vector_index.as_query_engine(
    similarity_top_k=10,
    node_postprocessors=[reranker],  # 引入 Cross-Encoder 重排序
    response_mode="refine"
)

# 引擎3:复杂查询 - 子问题分解引擎
complex_engine = SubQuestionQueryEngine.from_defaults(
    query_engine_tools=[tool_a, tool_b, tool_c],
    verbose=True
)

# 为每个引擎定义路由工具(描述越精准,路由越准确)
router_tools = [
    QueryEngineTool(
        query_engine=simple_engine,
        metadata=ToolMetadata(
            name="simple_fact_search",
            description=(
                "适合简单事实型查询,例如:产品参数、定义、版本号、配置项。"
                "查询通常是直接、明确的单一问题。"
            )
        )
    ),
    QueryEngineTool(
        query_engine=advanced_engine,
        metadata=ToolMetadata(
            name="technical_detail_search",
            description=(
                "适合技术细节查询,例如:原理分析、配置步骤、API 用法、错误排查。"
                "需要精确匹配和语义理解的场景。"
            )
        )
    ),
    QueryEngineTool(
        query_engine=complex_engine,
        metadata=ToolMetadata(
            name="complex_analysis",
            description=(
                "适合复杂的对比分析、多步骤推理、跨文档综合查询。"
                "例如:产品对比、方案评估、多维度分析。"
            )
        )
    )
]

# 创建路由引擎
router_engine = RouterQueryEngine(
    selector=LLMSingleSelector.from_defaults(),  # 用 LLM 做路由决策
    query_engine_tools=router_tools,
    verbose=True  # 开启日志,可看到路由决策过程
)

# 测试路由效果
queries = [
    "A100 的显存容量是多少?",           # 期望路由 → simple_fact_search
    "如何配置 LlamaIndex 的混合检索?",   # 期望路由 → technical_detail_search
    "对比 Pinecone 和 Milvus 的优劣势",   # 期望路由 → complex_analysis
]

for q in queries:
    print(f"\n查询:{q}")
    response = router_engine.query(q)
    print(f"答案:{response}")

六、重排序:Cross-Encoder 精准狙击

初次召回(Recall)追求覆盖率,但 LLM 处理的长上下文因无关信息而性能下降。引入 Cross-Encoder 进行精排是生产级 RAG 的必选动作

6.1 两阶段检索架构

第二阶段: 精排

第一阶段: 召回

Top-K 候选
(高召回,低精度)

Top-N 精选
(低召回,高精度)

用户查询

Bi-Encoder 双塔模型
向量检索

候选文档池
通常 10-50 个

Cross-Encoder 交叉编码器
逐对精确评分

精排结果
通常 3-5 个

LLM 生成最终答案

6.2 Bi-Encoder vs Cross-Encoder 深度对比

对比维度 Bi-Encoder(双塔) Cross-Encoder(交叉编码)
工作原理 独立编码 Query 和 Doc,计算向量余弦相似度 将 Query+Doc 拼接,联合编码,直接输出相关度分数
Query-Doc 交互 ❌ 无交互:两者独立编码 ✅ 深度交互:同时看到两者
计算速度 ⭐⭐⭐⭐⭐ 极快(可预计算 Doc 向量) ⭐⭐ 较慢(每次都要联合计算)
召回精度 ⭐⭐⭐ 中等 ⭐⭐⭐⭐⭐ 极高
适用规模 百万级文档 百级候选(精排阶段)
使用阶段 召回阶段 精排阶段

为什么 Cross-Encoder 更精准?一个直觉性例子

Query: "苹果公司的创始人"

Doc1: "苹果公司由史蒂夫·乔布斯、沃兹尼亚克和罗纳德·韦恩于 1976 年创立。"
Doc2: "苹果是一种常见水果,原产地在中亚,富含维生素 C。"

Bi-Encoder 的问题:
  两个文档都包含"苹果",向量距离可能都较近

Cross-Encoder 的优势:
  联合编码时,模型能在 "苹果" 在 Query 和 Doc1 中都指 "公司" 这个实体,
  而 Doc2 中的"苹果"指的是"水果"——这种 Token 级别的语义交互能力
  是 Cross-Encoder 精度远超 Bi-Encoder 的根本原因。

6.3 LlamaIndex 重排序实战

from llama_index.core.postprocessor import SentenceTransformerRerank
from llama_index.core.postprocessor import CohereRerank  # 商业重排序 API

# ==================== 方案一:开源重排序模型(推荐)====================
# bge-reranker-v2-m3:中英文双语效果最佳的开源重排序模型
reranker = SentenceTransformerRerank(
    model="BAAI/bge-reranker-v2-m3",
    top_n=3  # 从召回的 K 个文档中,精选出 Top 3 传给 LLM
)

# ==================== 方案二:Cohere 商业 API(更高精度)====================
# cohere_reranker = CohereRerank(
#     api_key="your-cohere-api-key",
#     top_n=3
# )

# ==================== 组装完整查询引擎 ====================
query_engine = vector_index.as_query_engine(
    similarity_top_k=10,              # 第一阶段:向量检索召回 Top 10 候选
    node_postprocessors=[reranker]    # 第二阶段:精排保留 Top 3 传给 LLM
)

# 执行查询
response = query_engine.query("A100 显存容量")

# ==================== 调试:查看重排序前后对比 ====================
# 第一阶段结果(重排序前)
raw_results = hybrid_retriever.retrieve("A100 显存容量")
print("=== 重排序前 Top 5 ===")
for i, node in enumerate(raw_results[:5]):
    print(f"{i+1}. 原始得分: {node.score:.4f} | {node.text[:80]}...")

# 第二阶段结果(重排序后)
reranked_results = reranker.postprocess_nodes(
    raw_results, query_str="A100 显存容量"
)
print("\n=== 重排序后 Top 3 ===")
for i, node in enumerate(reranked_results):
    print(f"{i+1}. 重排序得分: {node.score:.4f} | {node.text[:80]}...")

重排序参数配置建议

场景 similarity_top_k(召回) top_n(精排后) 说明
高精确要求(金融/医疗) 10 3 严格筛选,显著降低幻觉率
均衡场景(通用问答) 20 5 兼顾召回率和精度
探索性查询(研报分析) 50 10 保留更多上下文信息

重排序模型选型

模型 语言支持 精度 推理速度 部署成本 推荐场景
BAAI/bge-reranker-v2-m3 中文+英文 ⭐⭐⭐⭐⭐ ⭐⭐⭐ 低(开源) 生产首选
BAAI/bge-reranker-base 中文+英文 ⭐⭐⭐⭐ ⭐⭐⭐⭐ 低(开源) 延迟敏感场景
Cohere Rerank 多语言 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ 高(付费 API) 精度优先场景
ms-marco-MiniLM 英文 ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ 低(开源) 纯英文场景

七、性能杀手锏:语义缓存

在完整的多步骤 RAG 流程中,将响应时间压低至 50ms 的关键技术是语义缓存。

7.1 为什么传统缓存不够用?

用户A 查询:"A100 显存容量"   → LLM 生成答案,写入缓存
用户B 查询:"A100 显存多大?"  → 传统字符串缓存:未命中,重新检索!
用户C 查询:"NVIDIA A100 内存" → 传统字符串缓存:未命中,重新检索!

这三个查询的语义完全相同,但传统缓存无法识别它们是同一个问题。

语义缓存的解决方案:将查询转换为向量,通过向量相似度判断是否命中缓存——语义等价的查询,即使措辞不同,也能命中同一个缓存条目。

7.2 传统缓存 vs 语义缓存对比

对比项 传统字符串缓存 语义缓存
匹配方式 精确字符串匹配 向量余弦相似度
同义查询处理 ❌ 无法命中 ✅ 自动识别并复用
实现复杂度 中(需要 Embedding 服务)
误命中风险 有(需要阈值调优)
缓存命中率提升 基准 提升 30-60%

7.3 动态阈值策略:最容易被忽视的工程细节

语义缓存的核心挑战是相似度阈值的设定

  • 阈值太低(如 0.80):会命中错误缓存,把"A100 显存容量"的答案给"A10 显存容量"的查询
  • 阈值太高(如 0.999):缓存几乎永远无法命中,失去意义

Higress-RAG 的解决方案是动态阈值策略,根据查询意图的明确程度动态调整阈值:

包含模糊词
大概/也许/可能/比较

明确、具体的查询

相似度 ≥ 阈值

相似度 < 阈值

新用户查询

意图模糊性检测

模糊意图

明确意图

阈值提升至 0.98
防止错误缓存扩散

标准阈值 0.95

向量相似度检索缓存

命中缓存
直接返回

未命中
触发实时检索

为什么模糊意图需要更高阈值?

含"大概"、"也许"等词的查询,往往表示用户在寻求估算或不确定信息。如果直接返回精确缓存结果,可能让用户忽略最新变化,甚至产生误导。更高的阈值强制这类查询走实时检索路径,确保答案的准确性。

阈值调优案例

缓存中:  "A100 显存容量" → "80GB HBM2e"

明确查询:"NVIDIA A100 的显存有多大?"
  向量相似度:0.96 ≥ 0.95 → ✅ 命中缓存(正确)

模糊查询:"A100 大概有多少显存?"
  向量相似度:0.96,但阈值提升到 0.98
  0.96 < 0.98 → ❌ 未命中,触发实时检索(保证信息时效性)

7.4 生产级语义缓存完整实现

import numpy as np
from sentence_transformers import SentenceTransformer
from typing import Optional, Any
import hashlib
import json
import time

class ProductionSemanticCache:
    """
    生产级语义缓存实现
    - 支持动态阈值策略
    - 支持缓存 TTL(过期机制)
    - 支持缓存统计(命中率监控)
    """
    
    # 触发高阈值的模糊词列表(可根据业务场景扩展)
    FUZZY_KEYWORDS = ["大概", "也许", "可能", "比较", "差不多", "左右", 
                      "roughly", "maybe", "approximately", "about"]
    
    def __init__(
        self,
        model_name: str = "BAAI/bge-large-zh-v1.5",
        default_threshold: float = 0.95,
        fuzzy_threshold: float = 0.98,
        ttl_seconds: int = 3600  # 缓存有效期:1 小时
    ):
        self.model = SentenceTransformer(model_name)
        self.default_threshold = default_threshold
        self.fuzzy_threshold = fuzzy_threshold
        self.ttl_seconds = ttl_seconds
        
        # 缓存存储:{embedding_hash: (embedding, response, timestamp)}
        self.cache: dict = {}
        self.embeddings: list = []
        self.responses: list = []
        self.timestamps: list = []
        
        # 缓存统计
        self.stats = {"hits": 0, "misses": 0, "total": 0}
    
    def _cosine_similarity(self, a: np.ndarray, b: np.ndarray) -> float:
        """计算两个向量的余弦相似度"""
        norm_a, norm_b = np.linalg.norm(a), np.linalg.norm(b)
        if norm_a == 0 or norm_b == 0:
            return 0.0
        return float(np.dot(a, b) / (norm_a * norm_b))
    
    def _is_fuzzy_intent(self, query: str) -> bool:
        """检测查询是否包含模糊意图词"""
        return any(word in query for word in self.FUZZY_KEYWORDS)
    
    def _is_expired(self, timestamp: float) -> bool:
        """检查缓存条目是否已过期"""
        return (time.time() - timestamp) > self.ttl_seconds
    
    def get(self, query: str) -> Optional[Any]:
        """
        查询缓存
        返回:命中时返回缓存的响应,未命中时返回 None
        """
        self.stats["total"] += 1
        query_embedding = self.model.encode(query, normalize_embeddings=True)
        
        # 动态阈值
        threshold = self.fuzzy_threshold if self._is_fuzzy_intent(query) \
                    else self.default_threshold
        
        best_similarity = 0.0
        best_response = None
        
        for i, (cached_emb, cached_resp, cached_ts) in enumerate(
            zip(self.embeddings, self.responses, self.timestamps)
        ):
            # 跳过过期缓存
            if self._is_expired(cached_ts):
                continue
            
            similarity = self._cosine_similarity(query_embedding, cached_emb)
            if similarity > best_similarity:
                best_similarity = similarity
                best_response = cached_resp
        
        if best_similarity >= threshold:
            self.stats["hits"] += 1
            hit_rate = self.stats["hits"] / self.stats["total"] * 100
            print(f"✅ 缓存命中 | 相似度: {best_similarity:.3f} | "
                  f"阈值: {threshold} | 命中率: {hit_rate:.1f}%")
            return best_response
        
        self.stats["misses"] += 1
        print(f"❌ 缓存未命中 | 最高相似度: {best_similarity:.3f} | 阈值: {threshold}")
        return None
    
    def set(self, query: str, response: Any):
        """将查询结果写入缓存(预计算 embedding,加速 get 阶段)"""
        query_embedding = self.model.encode(query, normalize_embeddings=True)
        self.embeddings.append(query_embedding)
        self.responses.append(response)
        self.timestamps.append(time.time())
        print(f"💾 缓存写入 | 当前缓存条目数: {len(self.embeddings)}")
    
    def get_stats(self) -> dict:
        """返回缓存统计信息"""
        total = self.stats["total"]
        hits = self.stats["hits"]
        return {
            "total_queries": total,
            "cache_hits": hits,
            "cache_misses": self.stats["misses"],
            "hit_rate": f"{hits/total*100:.1f}%" if total > 0 else "0%",
            "cached_entries": len(self.embeddings)
        }


# ==================== 使用示例 ====================
cache = ProductionSemanticCache(
    default_threshold=0.95,
    fuzzy_threshold=0.98,
    ttl_seconds=3600
)

def rag_with_cache(query: str, query_engine) -> str:
    """带语义缓存的 RAG 查询函数"""
    # 1. 先查缓存
    cached_response = cache.get(query)
    if cached_response:
        return cached_response
    
    # 2. 缓存未命中,执行完整 RAG 流程
    response = query_engine.query(query)
    result = str(response)
    
    # 3. 将结果写入缓存
    cache.set(query, result)
    return result


# 测试语义缓存效果
queries = [
    "A100 显存容量",           # 第一次查询,写入缓存
    "A100 的显存有多大?",      # 语义等价,应命中缓存
    "NVIDIA A100 内存规格",    # 语义相似,应命中缓存
    "A100 大概有多少显存?",    # 模糊查询,阈值提升,可能不命中
]

for q in queries:
    print(f"\n查询: {q}")
    result = rag_with_cache(q, query_engine)
    print(f"结果: {result[:100]}...")

# 查看缓存统计
print("\n=== 缓存统计 ===")
print(json.dumps(cache.get_stats(), ensure_ascii=False, indent=2))

八、全链路生产级部署 Checklist

将 RAG 从"实验室玩具"转变为"企业级应用",需要逐一验证以下 5 个关键环节。

8.1 场景 → 技术映射决策表

应用场景 推荐技术组合 核心参数配置 预期效果
企业内部知识库 AST 切片 + 混合检索 + Rerank mode=rrf, top_n=3 准确率 85%+
电商产品问答 混合检索 + HyDE + Rerank top_n=5, include_original=True 准确率 90%+
技术文档搜索 AST 切片 + 混合检索 + CRAG threshold_correct=0.8 幻觉率 <5%
客服对话系统 语义缓存 + Router + Rerank cache_threshold=0.95, top_n=3 延迟 <100ms
研报/论文分析 层次化切片 + 子问题引擎 + CRAG chunk_sizes=[2048,512,128] 完整覆盖度高
实时问答(时效性) CRAG + Web 搜索兜底 threshold_incorrect=0.3 知识及时性高

8.2 五步落地 Checklist

✅ Step 1:切片深度调优
  • 评估当前切片的语义独立性:单独看每个切片,是否能独立理解?
  • 遵循 HOPE 原则,优先保证原子事实的完整性
  • 对技术文档采用 MarkdownNodeParser,保护标题层次结构
  • 对代码库文档采用 CodeSplitter,禁止切断函数定义
  • 测试 3 种以上 chunk_size 配置,记录 MRR@10 指标变化
  • 验证:100 个随机切片中,语义独立的比例 ≥ 90%
✅ Step 2:落地混合检索
  • 部署 BM25 + 向量双引擎(安装 llama-index-retrievers-bm25
  • 使用 RRF 替换简单得分加权(mode="reciprocal_rank_fusion"
  • 中文场景:集成 jieba 分词,避免 BM25 分词错误
  • 开启 use_async=True 并行检索,降低延迟
  • A/B 测试:混合检索 vs 纯向量检索,MRR 提升 ≥ 15%
✅ Step 3:实施查询转换
  • 分析查询日志,识别口语化/简短查询的比例(通常 >60%)
  • 高比例口语化查询场景:集成 HyDE(include_original=True
  • 含对比/多目标查询场景:部署子问题查询引擎
  • 监控查询转换耗时(HyDE 额外增加约 200-500ms LLM 调用)
  • 建立失败案例库,持续优化 ToolMetadata 描述精度
✅ Step 4:配置智能重排
  • 部署 Cross-Encoder(推荐 BAAI/bge-reranker-v2-m3
  • 确认两阶段配置:similarity_top_k ≥ 3 × top_n(保证精排有足够候选)
  • A/B 测试重排序前后准确率,目标提升 ≥ 20%
  • 测量重排序耗时(通常增加 100-300ms),评估是否可接受
✅ Step 5:启用语义缓存与监控
  • 部署语义缓存,初始阈值设 0.95
  • 配置模糊意图检测,区分明确查询和模糊查询
  • 设置缓存 TTL(知识库更新周期的一半,如每周更新则 TTL=3.5 天)
  • 建立缓存命中率监控,目标:高频查询命中率 ≥ 40%
  • P99 延迟 <200ms 为目标,根据监控数据持续优化
  • 配置 CRAG 质量评估器,记录"错误"路径触发频率,用于优化文档库

8.3 评估指标体系

建立完整的评估体系,才能量化每个优化步骤的真实价值:

指标 含义 评估工具 目标值
MRR@10 Mean Reciprocal Rank,检索排名质量 RAGAS ≥ 0.75
Answer Faithfulness 答案是否忠实于检索到的上下文 RAGAS ≥ 0.85
Answer Relevancy 答案是否回答了用户的问题 RAGAS ≥ 0.80
Context Precision 检索到的文档中相关文档的比例 RAGAS ≥ 0.70
P50/P99 延迟 中位/尾部延迟 Prometheus + Grafana <200ms/<500ms
缓存命中率 被缓存命中的查询比例 自定义监控 ≥ 40%
# 使用 RAGAS 评估 RAG 系统质量
from ragas import evaluate
from ragas.metrics import (
    faithfulness,
    answer_relevancy,
    context_precision,
    context_recall
)
from datasets import Dataset

# 准备评估数据集
eval_data = {
    "question": ["A100 显存容量", "如何配置混合检索?"],
    "answer": [str(response1), str(response2)],  # RAG 生成的答案
    "contexts": [
        [n.text for n in nodes1],  # 检索到的上下文
        [n.text for n in nodes2]
    ],
    "ground_truth": [
        "NVIDIA A100 GPU 配备 80GB HBM2e 显存",
        "使用 QueryFusionRetriever 配置 BM25 和向量检索器..."
    ]
}

dataset = Dataset.from_dict(eval_data)

# 执行评估
results = evaluate(
    dataset=dataset,
    metrics=[faithfulness, answer_relevancy, context_precision, context_recall]
)

print(results)

九、技术融合:完整生产级 RAG Pipeline

将所有模块串联,构建一个完整的生产级 RAG Pipeline:

命中 <50ms

未命中

简单

复杂

正确

模糊

错误

文档入库

HOPE 框架切片
Markdown AST Splitter

Embedding 生成
bge-large-zh-v1.5

向量索引
+ BM25 索引

用户查询

语义缓存检查
动态阈值 0.95/0.98

直接返回

Router 复杂度评估

直接混合检索

查询转换
HyDE / 子问题分解

混合检索
BM25 + 向量 + RRF

Cross-Encoder 重排序
bge-reranker-v2-m3

CRAG 质量评估

LLM 生成答案

知识补偿
内部+外部并行

Web 搜索兜底
Tavily API

写入语义缓存


十、总结与延伸阅读

核心要点回顾

本文系统介绍了 7 个提升 RAG 系统性能的关键技术,每一个都是独立可落地的优化模块:

  1. HOPE 切片框架:高质量检索的根基,语义独立性是"核心性能之王"
  2. 混合检索(BM25 + 向量 + RRF):单一检索必然有盲区,双引擎互补才是生产级标配
  3. HyDE 查询转换:用"假设答案"匹配"真实文档",从根本上缩小语义鸿沟
  4. 子问题分解:让复杂的跨文档查询"分而治之",显著提升多目标查询的质量
  5. 智能路由(Router):让正确的查询走正确的路径,兼顾效率与质量
  6. CRAG 自愈检索:让系统具备"判断检索结果好坏"的元认知能力
  7. Cross-Encoder 重排序:精排阶段的决定性武器,大幅降低传给 LLM 的噪音
  8. 语义缓存(动态阈值):将 P50 响应时间从秒级压低至 50ms 的性能杀手锏

不同阶段的优先级建议

🔬 第三优先级(场景强化)

CRAG 自愈检索

子问题分解引擎

RAGAS 评估体系

⭐ 第二优先级(显著提升)

HyDE 查询转换

语义缓存

智能路由

🚀 第一优先级(必做)

HOPE 切片优化

混合检索 BM25+向量

Cross-Encoder 重排序

架构设计的黄金法则

💡 永远没有"最好的"RAG 架构,只有"最适合当前场景"的架构。

关键决策维度:

  • 你的查询是简单事实还是复杂推理
  • 你的数据是结构化文档还是非结构化文本
  • 你的用户是技术人员(精确查询)还是普通消费者(口语化查询)?
  • 你的响应延迟要求是 <100ms 还是可以接受 1-3s

每一个问题的答案都会影响你的技术选型和参数配置。

延伸阅读

官方文档

论文推荐

  • 《CRAG: Corrective Retrieval Augmented Generation》- 自愈检索的理论基础
  • 《HyDE: Precise Zero-Shot Dense Retrieval without Relevance Labels》- HyDE 原始论文
  • 《RRF: Reciprocal Rank Fusion outperforms Condorcet and Individual Rank Learning Methods》- RRF 算法原始论文

开源项目


标签#LlamaIndex #RAG检索 #混合检索 #CRAG #重排序 #HyDE #语义缓存 #向量数据库 #生产级AI

分类:人工智能 → 自然语言处理 → RAG 系统工程


💬 互动时间:你在 RAG 检索优化中踩过哪些坑?是混合检索效果不理想,还是 CRAG 评估器阈值难以调优,亦或是语义缓存出现误命中?欢迎在评论区分享你的经验!

觉得有价值? 点赞收藏支持一下,你的认可是持续输出硬核内容的最大动力!

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐