RAG落地测评:回答与检索片段对应追踪精选
在 RAG(检索增强生成)系统中,将模型输出与检索到的片段(Chunk)建立精确映射,是实现可解释推理、事实核查与错误归因的核心能力。尤其在金融、医疗等强监管领域,这种溯源机制几乎是基础设施级别的刚需。
先看最终呈现效果——大模型生成的每条回答都附带像 [1]、[2] 这样的来源标记。今天我们就手把手拆解,如何借助 LlamaIndex 框架落地这一能力。
为什么必须追踪回答与检索片段的对应关系?
- 事实校验:确保答案内容源于可信的检索块,杜绝大模型“幻觉”(捏造虚假信息)。
- 可解释性:向用户亮出回答的证据链,增强系统公信力(如金融投资建议、临床诊断辅助等合规场景)。
- 调试与优化:精准定位检索或生成环节的短板(比如片段相关性不足、关键信息被遗漏)。
- 合规审计:满足数据来源追溯要求(例如法律文书中需记录每一条引用的原始段落)。
这四点看似抽象,实际落地时你会发现,每一项都直接决定 RAG 系统在生产环境中的可用性和用户信任度。
代码实现
我们通常基于 LlamaIndex 构建 RAG 应用,它内置了专门的引文查询引擎——CitationQueryEngine。官方文档提供了完整教程与示例。
首先安装必需的依赖包:
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 上限。这里有几个要点:
- 该值与文本切割 Node 的
chunk_size无关,两者相互独立。 - 若
citation_chunk_size>chunk_size,则以chunk_size为准;反之,CitationQueryEngine 会按citation_chunk_size重新切分,生成新的 Chunk 块。 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\\data2").load_data()
# 2. 文档切分为 chunk,添加 chunk_id
sentence_splitter = SentenceSplitter(
chunk_size=400,
chunk_overlap=100,
separator="。!?!?.n¡¿", # 适配中文,英语,西班牙语三种语言的分隔符
)
# 3. 分割文档
nodes = sentence_splitter.get_nodes_from_documents(documents)
index = VectorStoreIndex(nodes)
from llama_index.core.query_engine import CitationQueryEngine
query_engine = CitationQueryEngine.from_args(
index,
similarity_top_k=3,
citation_chunk_size=512,
)
res = query_engine.query("宋集薪是谁?")
print(res.response) # LLM 输出回答
print("------来源---------------")
for node in res.source_nodes:
print(node.get_text())
print("="*40)
前后端对接
后端逻辑完成后,可通过 FastAPI 将其封装为 Web API,把回答与 source_nodes 一并返回给前端。接口返回的 JSON 示例结构如下:
{
"code": 0,
"message": "",
"data": {
"answer": "宋集薪是一个少年,居住在毗邻陈平安的院落中,并拥有一名婢女(被称为“稚圭”)[1][3]。他性格有些顽劣,喜欢戏弄他人,比如在与锦衣少年的对话中先是提出高价卖婢女,后又改口抬价[1][2]。此外,他与婢女关系似乎较为亲近,能心有灵犀地配合[3]。他还计划下个月离开当地[3]。",
"reference": {
"total": 3,
"chunks": [
{"id": "d5f7457f79be3e2d", "content": "略"},
{"id": "476769d43a003fb9", "content": "略"},
{"id": "a8194d208880c1a0", "content": "略。"}
],
"doc_aggs": [
{"doc_name": "jianlai.txt", "doc_id": "5864b70c475f11f08a6042010a8a0006", "count": 81}
]
},
"prompt": "略",
"created_at": 1750215867.10315,
"id": "47d55175-bede-4d83-b42a-e1296c09ff16",
"session_id": "8e9345563cee49af95ad6d75072b200a"
}
}
前端依据返回的 answer 文本中的标签与 reference.chunks 字段,即可渲染出带超链接或悬浮提示的溯源效果。
总结:追踪机制的价值
通过回答与 Chunk 的对应关系追踪,RAG 系统完成了从“黑盒生成”到“白盒可解释”的质变。这不仅显著提升用户信任,还为系统优化提供了精确的数据反馈。实际落地时,需根据业务场景选择追踪粒度——段落级、句子级甚至 token 级,并结合工具链集成降低开发成本。
一句话:装上这套机制,你的 RAG 应用就不再只是“看起来聪明”,而是“每一步推理都有据可查”。
