教程|用 HKVM-RAG 思路自建证据组织层:多跳问答 F1 提升 5-10 个点

效果

在 HotpotQA、2WikiMultiHopQA 上:

  • 纯 dense retriever:F1 55-60%
  • 加图记忆:F1 65-70%
  • 加 HKVM 风格超图:F1 73-78%

局限

  • 建图需要 LLM 调用,成本不低
  • 图质量决定上限,bad tuple 拖累整体
  • 动态文档(每天新增)需要增量更新图

2. 检索时走超图

def hypergraph_rag(query, k=5):
    # 提取 query 实体
    query_entities = extract_entities(query)

    # 在图上做 PPR(personalized PageRank)
    seed = {e: 1.0 for e in query_entities}
    ppr = nx.pagerank(graph, personalization=seed, alpha=0.85)

    # 拉 top 文档
    doc_scores = {}
    for node, score in ppr.items():
        for neighbor in graph.neighbors(node):
            doc_id = graph[node][neighbor].get("doc_id")
            if doc_id is not None:
                doc_scores[doc_id] = doc_scores.get(doc_id, 0) + score

    top_docs = sorted(doc_scores.items(), key=lambda x: -x[1])[:k]
    context = "
".join([documents[i] for i, _ in top_docs])

    # 生成
    return llm_call(f"基于:
{context}

回答:{query}")

效果

在 HotpotQA、2WikiMultiHopQA 上:

  • 纯 dense retriever:F1 55-60%
  • 加图记忆:F1 65-70%
  • 加 HKVM 风格超图:F1 73-78%

局限

  • 建图需要 LLM 调用,成本不低
  • 图质量决定上限,bad tuple 拖累整体
  • 动态文档(每天新增)需要增量更新图

简化版实现

from openai import OpenAI
import networkx as nx

client = OpenAI()
graph = nx.Graph()

# 1. 离线建图:对每个 passage 让 LLM 提取 (entity, relation, entity) 三元组
def extract_tuples(text):
    resp = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": f"提取文本中的实体关系三元组:
{text}

输出 JSON: [[e1, r, e2], ...]"}]
    )
    return json.loads(resp.choices[0].message.content)

for i, doc in enumerate(documents):
    for e1, r, e2 in extract_tuples(doc):
        graph.add_edge(e1, e2, relation=r, doc_id=i)

2. 检索时走超图

def hypergraph_rag(query, k=5):
    # 提取 query 实体
    query_entities = extract_entities(query)

    # 在图上做 PPR(personalized PageRank)
    seed = {e: 1.0 for e in query_entities}
    ppr = nx.pagerank(graph, personalization=seed, alpha=0.85)

    # 拉 top 文档
    doc_scores = {}
    for node, score in ppr.items():
        for neighbor in graph.neighbors(node):
            doc_id = graph[node][neighbor].get("doc_id")
            if doc_id is not None:
                doc_scores[doc_id] = doc_scores.get(doc_id, 0) + score

    top_docs = sorted(doc_scores.items(), key=lambda x: -x[1])[:k]
    context = "
".join([documents[i] for i, _ in top_docs])

    # 生成
    return llm_call(f"基于:
{context}

回答:{query}")

效果

在 HotpotQA、2WikiMultiHopQA 上:

  • 纯 dense retriever:F1 55-60%
  • 加图记忆:F1 65-70%
  • 加 HKVM 风格超图:F1 73-78%

局限

  • 建图需要 LLM 调用,成本不低
  • 图质量决定上限,bad tuple 拖累整体
  • 动态文档(每天新增)需要增量更新图

HKVM-RAG 的设计

  • Key:passage-level LLM evidence tuples → 超边(答案路径)
  • Value:原始 passage 文本
  • 检索:用超图打分,召回答案链

简化版实现

from openai import OpenAI
import networkx as nx

client = OpenAI()
graph = nx.Graph()

# 1. 离线建图:对每个 passage 让 LLM 提取 (entity, relation, entity) 三元组
def extract_tuples(text):
    resp = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": f"提取文本中的实体关系三元组:
{text}

输出 JSON: [[e1, r, e2], ...]"}]
    )
    return json.loads(resp.choices[0].message.content)

for i, doc in enumerate(documents):
    for e1, r, e2 in extract_tuples(doc):
        graph.add_edge(e1, e2, relation=r, doc_id=i)

2. 检索时走超图

def hypergraph_rag(query, k=5):
    # 提取 query 实体
    query_entities = extract_entities(query)

    # 在图上做 PPR(personalized PageRank)
    seed = {e: 1.0 for e in query_entities}
    ppr = nx.pagerank(graph, personalization=seed, alpha=0.85)

    # 拉 top 文档
    doc_scores = {}
    for node, score in ppr.items():
        for neighbor in graph.neighbors(node):
            doc_id = graph[node][neighbor].get("doc_id")
            if doc_id is not None:
                doc_scores[doc_id] = doc_scores.get(doc_id, 0) + score

    top_docs = sorted(doc_scores.items(), key=lambda x: -x[1])[:k]
    context = "
".join([documents[i] for i, _ in top_docs])

    # 生成
    return llm_call(f"基于:
{context}

回答:{query}")

效果

在 HotpotQA、2WikiMultiHopQA 上:

  • 纯 dense retriever:F1 55-60%
  • 加图记忆:F1 65-70%
  • 加 HKVM 风格超图:F1 73-78%

局限

  • 建图需要 LLM 调用,成本不低
  • 图质量决定上限,bad tuple 拖累整体
  • 动态文档(每天新增)需要增量更新图

为什么需要证据组织层?

多跳问答 RAG 的常见瓶颈:

  • 每段独立打分,检索到好段但组织不出答案
  • 图记忆实体关系,多跳证据碎片化
  • 读者模型被过多无关段落干扰

HKVM-RAG 的设计

  • Key:passage-level LLM evidence tuples → 超边(答案路径)
  • Value:原始 passage 文本
  • 检索:用超图打分,召回答案链

简化版实现

from openai import OpenAI
import networkx as nx

client = OpenAI()
graph = nx.Graph()

# 1. 离线建图:对每个 passage 让 LLM 提取 (entity, relation, entity) 三元组
def extract_tuples(text):
    resp = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": f"提取文本中的实体关系三元组:
{text}

输出 JSON: [[e1, r, e2], ...]"}]
    )
    return json.loads(resp.choices[0].message.content)

for i, doc in enumerate(documents):
    for e1, r, e2 in extract_tuples(doc):
        graph.add_edge(e1, e2, relation=r, doc_id=i)

2. 检索时走超图

def hypergraph_rag(query, k=5):
    # 提取 query 实体
    query_entities = extract_entities(query)

    # 在图上做 PPR(personalized PageRank)
    seed = {e: 1.0 for e in query_entities}
    ppr = nx.pagerank(graph, personalization=seed, alpha=0.85)

    # 拉 top 文档
    doc_scores = {}
    for node, score in ppr.items():
        for neighbor in graph.neighbors(node):
            doc_id = graph[node][neighbor].get("doc_id")
            if doc_id is not None:
                doc_scores[doc_id] = doc_scores.get(doc_id, 0) + score

    top_docs = sorted(doc_scores.items(), key=lambda x: -x[1])[:k]
    context = "
".join([documents[i] for i, _ in top_docs])

    # 生成
    return llm_call(f"基于:
{context}

回答:{query}")

效果

在 HotpotQA、2WikiMultiHopQA 上:

  • 纯 dense retriever:F1 55-60%
  • 加图记忆:F1 65-70%
  • 加 HKVM 风格超图:F1 73-78%

局限

  • 建图需要 LLM 调用,成本不低
  • 图质量决定上限,bad tuple 拖累整体
  • 动态文档(每天新增)需要增量更新图

本周 arXiv 2606.07218 提出的 HKVM-RAG 给了我们一个思路:把”证据组织”从”段落匹配”中分离出来。本教程演示落地。

为什么需要证据组织层?

多跳问答 RAG 的常见瓶颈:

  • 每段独立打分,检索到好段但组织不出答案
  • 图记忆实体关系,多跳证据碎片化
  • 读者模型被过多无关段落干扰

HKVM-RAG 的设计

  • Key:passage-level LLM evidence tuples → 超边(答案路径)
  • Value:原始 passage 文本
  • 检索:用超图打分,召回答案链

简化版实现

from openai import OpenAI
import networkx as nx

client = OpenAI()
graph = nx.Graph()

# 1. 离线建图:对每个 passage 让 LLM 提取 (entity, relation, entity) 三元组
def extract_tuples(text):
    resp = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": f"提取文本中的实体关系三元组:
{text}

输出 JSON: [[e1, r, e2], ...]"}]
    )
    return json.loads(resp.choices[0].message.content)

for i, doc in enumerate(documents):
    for e1, r, e2 in extract_tuples(doc):
        graph.add_edge(e1, e2, relation=r, doc_id=i)

2. 检索时走超图

def hypergraph_rag(query, k=5):
    # 提取 query 实体
    query_entities = extract_entities(query)

    # 在图上做 PPR(personalized PageRank)
    seed = {e: 1.0 for e in query_entities}
    ppr = nx.pagerank(graph, personalization=seed, alpha=0.85)

    # 拉 top 文档
    doc_scores = {}
    for node, score in ppr.items():
        for neighbor in graph.neighbors(node):
            doc_id = graph[node][neighbor].get("doc_id")
            if doc_id is not None:
                doc_scores[doc_id] = doc_scores.get(doc_id, 0) + score

    top_docs = sorted(doc_scores.items(), key=lambda x: -x[1])[:k]
    context = "
".join([documents[i] for i, _ in top_docs])

    # 生成
    return llm_call(f"基于:
{context}

回答:{query}")

效果

在 HotpotQA、2WikiMultiHopQA 上:

  • 纯 dense retriever:F1 55-60%
  • 加图记忆:F1 65-70%
  • 加 HKVM 风格超图:F1 73-78%

局限

  • 建图需要 LLM 调用,成本不低
  • 图质量决定上限,bad tuple 拖累整体
  • 动态文档(每天新增)需要增量更新图
Leave a Comment