网易首页 > 网易号 > 正文 申请入驻

CRAG 架构解析:如何在生成器前修正错误检索结果

0
分享至


绝大多数 RAG 系统把检索当作不会出错的环节,无论拿到的文档是否真正切题,都会径直送入生成器。

"CRAG 提出了标准 RAG 从未追问的问题:如果检索器出错了,该怎么办?"

"不加甄别地引入检索文档,无论其是否相关,都会主动误导生成器,让 RAG 的表现甚至不如不做检索。

CRAG 详解

CRAG 引入了一个轻量级检索评估器,对给定查询所检索到的文档集合给出三种置信度判定之一。

得分达到 UPPER_TH 及以上时判定为"正确":至少有一篇文档具有足够相关性,进入知识精炼流程,将文档拆解为句子级片段,过滤掉无关内容,再将保留下来的部分重组为干净的知识(k_in)。得分在 LOWER_TH 以下则判定为"错误":所有本地文档均不相关,全部丢弃,查询被改写后转入网络搜索,以搜索结果作为外部知识(k_ex)。两个阈值之间的区域称为"模糊",质量不确定,同时使用 k_in 与 k_ex 作为上下文,在两个来源之间对冲风险。

模糊路径是论文中细节最丰富的部分,也是工程实现中最容易被跳过的部分。当评估器拿不准时,同时采信两个来源是最稳妥的策略,但这并不等同于"无条件双源并用"——触发条件明确限定在置信度真正偏低的场景。

分解-再重组算法

即便一篇文档被判定为"正确",其中仍可能夹带大量无关内容。以保险政策文件为例:某一页直接描述理赔程序,其余许多页却充斥着行政样板文字与其他险种的条款定义。CRAG 针对这一问题设计了句子级精炼算法:

  1. 分解:将每篇检索文档拆分为独立的句子(片段)。
  2. 过滤:对每个片段评估其与具体查询的相关性,丢弃低于阈值的片段。
  3. 重组:将保留下来的片段按原始顺序拼接为连贯的上下文字符串。

经过这道处理,送入生成器的上下文不仅体积更小,信息密度也更高——即便文档整体上是相关的,其中的无关句子也不会对生成过程构成干扰。

在原论文中,片段级别的评分由经过微调的 T5 模型完成。本文的代码实现中,片段评分与其他所有评估任务共用同一个 LLM,以少量评分精度的损失换取更简洁的运维结构和更少的模型依赖。

架构图

下图直接取自 CRAG 论文,看图是了解整个系统设计最直接的方式。



推理阶段的 CRAG 框架。对检索到的文档 d₁ 和 d₂ 进行评估,所得置信度触发三种知识检索动作之一,随后上下文才会被送入生成器。

逐步解读架构图

  • 顶行(检索):输入问题 x,检索器返回文档 d₁ 和 d₂,这是未经过滤的原始结果。
  • 中间左侧(检索评估器):评估器判断"检索到的文档对于 x 是否正确",给出置信度得分,分流至正确(绿色)、模糊(橙色)或错误(红色)路径。
  • 中间右侧,知识精炼(绿色框):正确路径下,d₁ 和 d₂ 被分解为 strip₁、strip₂……strip_ₙ;过滤器剔除无关片段(图中以 ✗ 表示),保留部分重组为 k_in(内部知识)。
  • 中间右侧,知识搜索(红色框):错误路径下,x 被改写为搜索查询 q(例如"Death of a Batman; screenwriter; Wikipedia"),网络搜索返回 k₁……k_ₙ,筛选后得到 k_ex(外部知识)。
  • 底行(生成):生成器接收 x 与对应知识,正确路径为 x + k_in,模糊路径为 x + k_in + k_ex,错误路径为 x + k_ex。原始未过滤的 d₁/d₂ 绝不会直接到达生成器。

生成器位于质量关卡的下游。送达它的知识,必然经过评估、过滤或外部来源的验证。上下文污染在正确实现的 CRAG 系统中,从架构层面便无从发生。

实现

构建 CRAG:逐节点解析

本文实现遵循论文中的架构:七个节点,一个条件分支,无循环。



已编译的 CRAG LangGraph 图直接由 app.get_graph().draw_mermaid_png() 生成。文档相关性检查节点是唯一的条件分支点,两条路径在上下文综合节点汇合,随后进入答案生成。

状态模式(State Schema)

LangGraph 需要一个 TypedDict 作为所有节点之间的共享内存,每个节点从中读取状态并返回部分更新。

class State(TypedDict):
question: str # 原始用户问题,始终不修改
docs: List[Document] # 原始检索的 top-k 块
good_docs: List[Document]# 通过评估器的块
verdict: str # CORRECT | INCORRECT
reason: str # 评估器的推理过程(用于日志记录)
strips: List[str] # 句子级别的分解片段
kept_strips: List[str] # 通过相关性过滤的片段
refined_context: str # 送入生成器的最终组合上下文
web_query: str # 改写后用于 Google 搜索的查询
web_docs: List[Document] # Google Custom Search 的返回结果
answer: str # 最终生成的答案

节点:知识检索

接收用户问题,查询 FAISS 向量存储,返回前 4 个块。

# 数据摄入(在启动时运行一次)
chunks = RecursiveCharacterTextSplitter(
chunk_size=900, chunk_overlap=150
).split_documents(docs)
retriever = FAISS.from_documents(chunks, embeddings)
.as_retriever(search_kwargs={'k': 4})
def retrieve_node(state: State):
docs = retriever.invoke(state['question'])
return {'docs': docs}

节点:文档相关性检查

这是整个架构的核心节点。每篇检索文档都会获得一个连续置信度得分(0.0–1.0),得分超过 UPPER_TH(0.7)的文档进入 good_docs。若 good_docs 非空,则 verdict = CORRECT;否则 verdict = INCORRECT,流程升级至网络搜索。

UPPER_TH = 0.7 # 论文推荐的"正确"阈值
LOWER_TH = 0.3 # 低于此值 = 错误(中间值 = 模糊)
class DocEvalScore(BaseModel):
score: float # 0.0–1.0 置信度
reason: str # 简短说明(记录用于调试)
# system prompt 中的评分标准:
# 1.0 → 用具体细节直接回答查询
# 0.7-0.9 → 中度相关,有用的相关信息
# 0.4-0.6 → 轻微相关
# 0.0-0.3 → 不相关,与查询无关联
def eval_each_doc_node(state: State):
good_docs = []
for i, doc in enumerate(state['docs'], start=1):
decision = doc_eval_llm.invoke(
doc_eval_prompt.format_messages(
question=state['question'], doc=doc.page_content
)
)
if decision.score >= UPPER_TH:
good_docs.append(doc)
verdict = 'CORRECT' if good_docs else 'INCORRECT'
return {'good_docs': good_docs, 'verdict': verdict}

为什么采用连续得分而非直接分类?阈值由此变为一个可调的配置项,调整判定标准无需动提示词。在保险这类高精度场景中,把 UPPER_TH 从 0.7 提到 0.85 只需改一个常量,策略与机制分离是这里更重要的设计原则。

节点:查询优化

仅在 INCORRECT 路径下激活,将用户的自然语言问题改写为关键词密集的网络搜索查询。改写逻辑具备明确的领域意识:展开缩写、补充专业术语、剥离口语化措辞。

class WebQuery(BaseModel):
query: str
# 提示词中内置的改写示例:
# 'How does CI claims work?'
# → 'critical illness insurance claim settlement process requirements'
# 'What are the benefits of life insurance?'
# → 'life insurance policy benefits coverage sum assured'
def rewrite_query_node(state: State):
decision = rewrite_llm.invoke(
rewrite_prompt.format_messages(question=state['question'])
)
return {'web_query': decision.query}

节点:外部知识搜索

调用 Google Custom Search API,将每条摘要片段封装为 LangChain Document。统一的 Document 接口让下游的上下文综合节点得以用相同逻辑处理本地文档与网络来源——两条路径在此完全对称。

def web_search_node(state: State):
params = {
'key': os.getenv('GOOGLE_API_KEY'),
'cx': os.getenv('GOOGLE_CSE_ID'),
'q': state['web_query']
}
r = requests.get('https://www.googleapis.com/customsearch/v1',
params=params)
web_docs = [
Document(page_content=item['snippet'])
for item in r.json().get('items', [])
]
return {'web_docs': web_docs}

节点:上下文综合

为生成器组装最终上下文。逻辑简单,影响深远。

def refine(state: State):
# CORRECT 路径:使用本地评估通过的 good_docs
# INCORRECT 路径:使用 web_docs(此时 good_docs 为空)
# 完整 CRAG:模糊路径将同时使用两者
docs = state['good_docs'] or state['web_docs']
context = '\n\n'.join([d.page_content for d in docs])
return {'refined_context': context}

一个真实查询示例

测试查询:"SecureLife 重疾险的理赔结算流程是什么?"

[输入查询 → 知识检索] 通过 FAISS 从保险语料库中检索出 4 个块。

[文档相关性检查]

  • 文档 1 → 0.92(重疾险理赔程序——直接相关)
  • 文档 2 → 0.78(理赔材料要求——相关)
  • 文档 3 → 0.41(一般健康保险概述——轻微相关)
  • 文档 4 → 0.18(车险免责条款——不相关)

裁定:CORRECT。4 篇文档中有 2 篇通过,直接路由至上下文综合。

[上下文综合] 仅由文档 1 和文档 2 组装上下文,车险与通用健康险内容被丢弃,生成器收到的是 2 个聚焦的相关块。

[答案生成 → 最终回复] 生成结果列出所需材料(病历报告、诊断证明、主治医生证明)、结算时间线(完整材料提交后 90 天内)以及两阶段审批流程。

没有文档过滤时,生成器会收到车险免责条款块(文档 4,得分 0.18)作为上下文。行为稳定的模型可能将其忽略,但压力下的模型往往会尝试从中提取信息,最终输出一个将重疾险与车险理赔程序混为一谈的错误答案。CRAG 在架构层面堵死了这条路。

本文代码:
https://avoid.overfit.cn/post/1a4ba7fc989f4c7f9ca4c61179cf5656

by Bhavya Meghnani

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
曝张雨绮退租288㎡豪宅!季付23万,中介证实已搬家,房源正招租

曝张雨绮退租288㎡豪宅!季付23万,中介证实已搬家,房源正招租

乐天闲聊
2026-05-04 02:10:09
上市6年下跌6年,股价从未涨停过!有人被套后死扛亏33.5万

上市6年下跌6年,股价从未涨停过!有人被套后死扛亏33.5万

财经智多星
2026-05-04 11:15:27
阿娇在英皇25周年演唱会献唱,胖的惨不忍睹!大腿太粗了个子又矮

阿娇在英皇25周年演唱会献唱,胖的惨不忍睹!大腿太粗了个子又矮

小娱乐悠悠
2026-05-04 09:31:50
重磅!两架美军C17运输机接连抵达北京,这是要干嘛?

重磅!两架美军C17运输机接连抵达北京,这是要干嘛?

军武咖
2026-05-03 20:18:15
许家印终于交代:月薪3万60名女团员,养了恒大歌舞团,提供3服务

许家印终于交代:月薪3万60名女团员,养了恒大歌舞团,提供3服务

猫叔东山再起
2026-05-04 09:35:09
快讯!国民党主席郑丽文发表声明!

快讯!国民党主席郑丽文发表声明!

故事终将光明磊落
2026-05-04 12:51:31
德国总借款1965亿欧元,打造“欧洲最强常规军”,海陆空战力会如何?

德国总借款1965亿欧元,打造“欧洲最强常规军”,海陆空战力会如何?

澎湃新闻
2026-05-04 14:54:13
特朗普称将启动霍尔木兹海峡被困船只疏导行动,马克龙表示法国不参与;此前伊朗武装部队称将打击接近或进入霍尔木兹海峡的美军

特朗普称将启动霍尔木兹海峡被困船只疏导行动,马克龙表示法国不参与;此前伊朗武装部队称将打击接近或进入霍尔木兹海峡的美军

大风新闻
2026-05-04 15:30:02
教育部正式设立太极拳本科专业

教育部正式设立太极拳本科专业

名人苟或
2026-05-04 07:38:20
为什么说章鱼根本不属于地球?它的DNA复杂程度让科学家头皮发麻

为什么说章鱼根本不属于地球?它的DNA复杂程度让科学家头皮发麻

半解智士
2026-05-03 20:47:57
悲催!1982年出生、2002年离世女子,立碑人仅姐妹4人,引发热议

悲催!1982年出生、2002年离世女子,立碑人仅姐妹4人,引发热议

火山詩话
2026-05-04 06:24:20
没时间了,中方通牒送进东京,断高市后路,日本人流泪向中国道歉

没时间了,中方通牒送进东京,断高市后路,日本人流泪向中国道歉

近史博览
2026-05-03 03:46:47
多个村镇银行获批解散,股份行、当地农商行等承接相关资产负债

多个村镇银行获批解散,股份行、当地农商行等承接相关资产负债

澎湃新闻
2026-05-03 08:32:26
南极探险邮轮爆发致命疫情,汉坦病毒主要攻击肾脏、鼠类为自然宿主

南极探险邮轮爆发致命疫情,汉坦病毒主要攻击肾脏、鼠类为自然宿主

界面新闻
2026-05-04 14:10:09
巴恩斯24+9+6难救主,阿伦打爆猛龙内线,哈登一战2神迹率队屠龙

巴恩斯24+9+6难救主,阿伦打爆猛龙内线,哈登一战2神迹率队屠龙

钉钉陌上花开
2026-05-04 10:27:24
赖清德回不来了!最想看他困在非洲的,不是大陆,是接班的自己人

赖清德回不来了!最想看他困在非洲的,不是大陆,是接班的自己人

蓝色海边
2026-05-04 10:49:31
符拉迪沃斯托克的正步,是对那片土地上魂灵的二次屠杀!

符拉迪沃斯托克的正步,是对那片土地上魂灵的二次屠杀!

胖胖说他不胖
2026-05-04 10:00:26
一夜之间大跳水!最低不到2折!网友直呼“2000多元凭空蒸发”

一夜之间大跳水!最低不到2折!网友直呼“2000多元凭空蒸发”

鲁中晨报
2026-05-03 08:34:29
硅谷华裔工程师回国两周后,反而更看好美国了?

硅谷华裔工程师回国两周后,反而更看好美国了?

回旋镖
2026-05-04 11:10:15
著名法学家、西南政法大学行政法学科创建人王连昌教授逝世,享年94岁

著名法学家、西南政法大学行政法学科创建人王连昌教授逝世,享年94岁

界面新闻
2026-05-04 13:36:55
2026-05-04 16:08:49
deephub incentive-icons
deephub
CV NLP和数据挖掘知识
1986文章数 1461关注度
往期回顾 全部

科技要闻

OpenAI“复活”了QQ宠物,网友直接玩疯

头条要闻

发布仅3天 日本陆自部队新徽章被骂下线

头条要闻

发布仅3天 日本陆自部队新徽章被骂下线

体育要闻

骑士破猛龙:加雷特·阿伦的活力

娱乐要闻

张敬轩还是站上了英皇25周年舞台

财经要闻

魔幻的韩国股市,父母给婴儿开户买股票

汽车要闻

同比大涨190% 方程豹4月销量29138台

态度原创

手机
家居
时尚
艺术
旅游

手机要闻

史无前例!iPhone 18缺席苹果9月科技春晚

家居要闻

灵动实用 生活艺术场

这几条裙子太适合度假了,减龄又时髦!

艺术要闻

300米!重庆解放碑区在建第一高楼,冲刺竣工!

旅游要闻

五一“堵”上5000米!四姑娘山大雪难挡“冲顶大军”,大峰打卡排队超1小时

无障碍浏览 进入关怀版