在 RAG(检索增强生成)应用中,回答内容与检索片段(Chunk)的对应关系追踪是实现回答可解释性、准确性验证和错误溯源的关键环节。以下从追踪目的、技术实现等维度展开详细说明:
先看下效果,如下:


一、为什么需要追踪回答与检索片段的对应关系?
-
事实性验证:确保回答中的信息来自可信的检索片段,避免大模型 “幻觉”(编造错误信息)。
-
可解释性增强:向用户展示回答的依据来源,提升系统可信度(如金融、医疗等合规场景)。
-
系统调试优化:定位检索或生成环节的问题(如片段相关性不足、信息遗漏)。
-
合规与审计:满足数据溯源需求(如法律场景中需记录信息来源)。

二、代码实现
我们常使用llamaindex开发RAG应用。llamaindex有专门的引文查询引擎–CitationQueryEngine 。
官网教程:
https://docs.llamaindex.ai/en/stable/examples/query_engine/citation_query_engine/
使用如下:
pip install llama-index
pip install llama-index-embeddings-openai
pip install llama-index-llms-openai
创建具有默认参数的 CitationQueryEngine :
query_engine = CitationQueryEngine.from_args(
index,
similarity_top_k=3,
# here we can control how granular citation sources are, the default is 512
citation_chunk_size=512,
)
res = query_engine.query("宋集薪是谁?")
print(res.response) # LLM 输出回答
print("------来源---------------")
for node in res.source_nodes:
print("相关片段:", node.text)
print("片段分数:", node.score)
print("片段元数据:", node.metadata)
print("="*40)
结果如下:

核心代码讲解:
-
可以看到大模型最终合成答案里有[1]、[2]、[3]这样的标签,这是标签对应chunk块顺序。
-
标签从 1 开始计数,但python 数组从 0 开始计数!
-
参数citation_chunk_size默认值为512,表示引用的chunk的token大小。
(1)该值与文本切割node节点的chunk_size不一样。
(2)若citation_chunk_size>chunk_size,则最终以chunk_size为准;
否则,该CitationQueryEngine将按照citation_chunk_size重新切割产生新的chunk块。
(3)citation_chunk_size也不宜过大,若超过源文档的大小,则该查询失去效果。
-
可以查看引用的内容:response.source_nodes 。
完整的示例代码如下。
from llama_index.core import VectorStoreIndex, Settings, SimpleDirectoryReader
from llama_index.llms.openai_like import OpenAILike
from llama_index.core.node_parser import SentenceSplitter
from llama_index.embeddings.openai_like import OpenAILikeEmbedding
# ================== 初始化模型 ==================
def init_models():
"""初始化模型并验证"""
# Embedding模型
embed_model = OpenAILikeEmbedding(
model_name="BAAI/bge-m3",
api_base="https://api.siliconflow.cn/v1",
api_key="sk-xxxxx",
embed_batch_size=10,
)
llm = OpenAILike(
model="DeepSeek-ai/DeepSeek-V3",
api_base="https://api.siliconflow.cn/v1",
api_key="sk-xxxx",
context_window=128000,
is_chat_model=True,
is_function_calling_model=False,
)
Settings.embed_model = embed_model
Settings.llm = llm
# 验证模型
test_embedding = embed_model.get_text_embedding("测试文本")
print(f"Embedding维度验证:{len(test_embedding)}")
return embed_model, llm
init_models()
# load documents, split into chunks
documents = SimpleDirectoryReader(r"D:Testaiyoyo