WeKnow-RAG:结合网络搜索和知识图谱的自适应检索增强生成方法
最近有一篇名为"WeKnow-RAG: An Adaptive Approach for Retrieval-Augmented Generation Integrating Web Search and Knowledge Graphs"的论文,提出了一个挺有意思的思路——把网络搜索和知识图谱结合起来,做成一种新的检索增强生成(RAG)方法。
WeKnow-RAG:网络搜索与知识图谱融合的自适应检索增强生成方法

1. 引言
大语言模型(LLMs)这几年的进步有目共睹,甚至被视为通往通用人工智能(AGI)的一条重要路径。但话说回来,它们也有一个绕不开的麻烦:容易产生事实性错误,严重时还会"编造"内容,也就是所谓的幻觉。这个问题直接影响了LLMs在实际应用中的可信度,尤其是在那些对准确性要求极高的场景里。
为了解决这个痛点,研究人员想出了检索增强生成(RAG)这个办法——通过引入外部数据库和信息检索机制,给LLM装上"外设"。而本文要聊的WeKnow-RAG,则是在这个基础上更进一步:它把网络搜索和知识图谱都整合进了RAG系统,目标是讓LLM的响应更准确、更靠谱。
2. WeKnow-RAG方法概述
WeKnow-RAG的核心想法很简单:把知识图谱那种结构化的表示能力,跟密集向量检索的灵活性结合起来。具体来说,它由以下几个关键组件构成:
- 基于网络的RAG
- 基于知识图谱的RAG
- 集成方法
下面我们来逐个拆解,看看它们是怎么工作的。
2.1 基于网络的RAG
基于网络的RAG,是WeKnow-RAG这套体系里的重要一环。它主要包含四个步骤。
2.1.1 网页内容解析
第一步,当然是把网页内容先解析出来,不然没法处理。这里直接用BeautifulSoup就能搞定:
from bs4 import BeautifulSoup
def parse_html(html_content):
soup = BeautifulSoup(html_content, 'html.parser')
# 提取需要的内容
text = soup.get_text()
return text
2.1.2 分块
分块就是把一篇长文档切成若干小段。WeKnow-RAG采用的是基于token的分块方式。举个例子,一个朴素的分块函数可能长这样:
def chunk_text(text, chunk_size=500):
tokens = text.split()
chunks = []
for i in range(0, len(tokens), chunk_size):
chunk = ' '.join(tokens[i:i+chunk_size])
chunks.append(chunk)
return chunks
2.1.3 多阶段检索
WeKnow-RAG在检索环节用了多阶段策略,主要是稀疏检索和密集检索两轮:
- 第一阶段:先用BM25算法做一次稀疏检索,打个底。
- 第二阶段:把稀疏检索(BM25)和密集检索(基于embedding的相似度)结合起来,做二次筛选。
这里要提一下BM25算法的评分函数,它长这样:
Score(query, C_i) = ∑(q_j ∈ query) IDF(q_j) · (f(q_j, C_i) · (k_1 + 1)) / (f(q_j, C_i) + k_1 · (1 - b + b · |C_i| / a vg_dl))
其中:
- q_j 是查询中的一个词
- IDF(q_j) 是词 q_j 的逆文档频率
- f(q_j, C_i) 是词 q_j 在文档 C_i 中的词频
- k_1 和 b 是参数(通常取 k_1 = 1.5, b = 0.75)
- |C_i| 是文档 C_i 的长度
- a vg_dl 是语料库中的平均文档长度
2.1.4 答案生成与自评估
为了尽量减少幻觉,WeKnow-RAG引入了一个自评估机制——让LLM自己给自己的答案打个置信度分:
def generate_answer_with_confidence(query, context):
prompt = f"""
Question: {query}
Context: {context}
Answer the question and provide your confidence level (high, medium, low).
"""
response = llm.generate(prompt)
# 解析响应,提取答案和置信度
return answer, confidence
2.2 基于知识图谱的RAG
知识图谱(KG)在这套方法里也扮演着重要角色。主要分成三个环节:
2.2.1 领域分类
收到一个问题,系统首先要判断它属于哪个领域:
def classify_domain(query):
prompt = f"Classify the domain of this query: {query}"
domain = llm.generate(prompt)
return domain
2.2.2 查询生成
根据领域分类结果,系统会生成对应的知识图谱查询语句。比如对音乐类问题,它会调用一些预设的音乐领域知识函数:
def generate_kg_query(query, domain):
if domain == "music":
prompt = f"""
Generate a KG query for this music-related question: {query}
A vailable functions:
- get_artist_info(artist_name, info_type)
- get_song_info(song_name, info_type)
...
"""
# 其他领域的处理逻辑
kg_query = llm.generate(prompt)
return kg_query
2.2.3 答案检索与后处理
系统通过API调用知识图谱拿到原始结果,然后再做个后处理:
def retrieve_and_postprocess(kg_query):
raw_result = kg_api.call(kg_query)
processed_result = postprocess(raw_result)
return processed_result
2.3 集成方法
WeKnow-RAG的巧妙之处在于,它采用了一个自适应框架,根据问题的所属领域以及该领域信息变化的快慢,智能地决定到底用KG还是Web来做主要检索,或者两者结合:
def adaptive_rag(query):
domain = classify_domain(query)
if domain in ["encyclopedia", "open_domain"]:
return kg_based_rag(query)
elif domain in ["music", "movies"]:
kg_result = kg_based_rag(query)
web_result = web_based_rag(query)
return integrate_results(kg_result, web_result)
else:
return web_based_rag(query)
3. 实验结果
这套方法在CRAG(Comprehensive RAG Benchmark)数据集上做了评估。关键数据如下:
| 模型版本 | 准确率 | 幻觉率 | 缺失率 | 得分 |
|---|---|---|---|---|
| 版本1 | 0.393 | 0.319 | 0.288 | 0.0743 |
| 版本2 | 0.409 | 0.316 | 0.276 | 0.0929 |
从数据来看,版本2相比版本1有了明显的提升,尤其在准确率和综合得分方面,改善幅度是实打实的。
4. 结论与未来展望
WeKnow-RAG通过把网络搜索和知识图谱融合到一起,提供了一种新颖的检索增强生成方案。它在提升LLM响应的准确性和可靠性方面,成效显著。其核心贡献可以归纳为以下几点:
- 开发了一个能适配不同查询类型和领域的、带特定领域知识图谱的RAG系统。
- 引入了结合稀疏检索与密集检索的多阶段网页检索方法,打了一套组合拳。
- 实现了LLM的自评估机制,有效降低了幻觉的出现频率。
- 提出了一个自适应框架,能根据情况灵活地组合KG和Web检索。
至于未来的方向,有几个值得关注的点:
- 进一步优化知识图谱的构建和更新策略,让数据更鲜活。
- 探索更高效的多模态检索方法,毕竟现实世界的信息远不止文本。
- 研究如何把WeKnow-RAG扩展到更广泛的领域和任务,看看这套思路的通用性有多强。
-
- 关于宇宙的好的网名有哪些
- 角色扮演 | 1
- 网名