生成策略与引用溯源
问题
RAG 中如何设计 Prompt 模板?如何实现引用溯源?
答案
检索到相关文档后,如何将其与用户问题组织成 Prompt 送入 LLM,以及如何让回答包含可追溯的引用,是 RAG 生成阶段的核心。
一、Prompt 模板设计
基础模板
RAG_PROMPT = """基于以下参考资料回答用户问题。
如果参考资料中没有相关信息,请明确说明"根据现有资料无法回答",不要编造。
参考资料:
{context}
用户问题:{question}
请用中文回答:"""
带引用的模板
RAG_PROMPT_WITH_CITATION = """基于以下编号的参考资料回答用户问题。
回答中必须用 [1]、[2] 等标注引用了哪条资料。
如果参考资料不足以回答,明确说明。
参考资料:
[1] {doc_1}
[2] {doc_2}
[3] {doc_3}
用户问题:{question}
请用中文回答,并标注引用来源:"""
二、引用溯源实现
1. 文档级引用
def generate_with_citations(query: str, docs: list, llm):
# 为每个文档编号
context_parts = []
for i, doc in enumerate(docs, 1):
source = doc.metadata.get("source", "未知来源")
page = doc.metadata.get("page", "")
context_parts.append(f"[{i}] (来源: {source}, 第{page}页)\n{doc.page_content}")
context = "\n\n".join(context_parts)
response = llm.invoke(RAG_PROMPT_WITH_CITATION.format(
context=context,
question=query,
doc_1=context_parts[0] if len(context_parts) > 0 else "",
doc_2=context_parts[1] if len(context_parts) > 1 else "",
doc_3=context_parts[2] if len(context_parts) > 2 else "",
))
return {
"answer": response.content,
"sources": [
{"index": i+1, "source": doc.metadata.get("source"), "page": doc.metadata.get("page")}
for i, doc in enumerate(docs)
]
}
2. 句子级引用(精确到段落)
更精细的引用方式,让 LLM 标注每个论述的具体来源:
# 输出示例
MVCC 通过为每条记录维护多个版本来实现并发控制 [1]。
在 Read Committed 隔离级别下,每次查询都会创建新的 Read View [2]。
而在 Repeatable Read 级别下,整个事务共享一个 Read View [1][2]。
三、上下文管理策略
Stuff(填充)
将所有检索到的文档直接塞入 Prompt:
简单直接,适合文档量小、Context Window 足够的场景
Map-Reduce
- 先对每个文档独立总结,再合并
- 适合大量文档,但多次 LLM 调用成本高
Refine(逐步精炼)
- 逐个文档迭代优化答案
- 答案质量高,但串行执行慢
| 策略 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| Stuff | 文档少,窗口够 | 简单快速 | 文档多会超窗口 |
| Map-Reduce | 大量文档 | 可并行 | 多次 LLM 调用 |
| Refine | 需要高质量综合 | 答案质量高 | 串行慢 |
四、减少幻觉的技巧
| 技巧 | 实现方式 |
|---|---|
| 明确约束 | "仅基于参考资料回答,如无法回答请说明" |
| 要求引用 | "每个论述必须标注来源编号" |
| 低 Temperature | 设 temperature=0 减少创造性 |
| 分离检索和判断 | 先让 LLM 判断"是否能回答",再生成答案 |
| Faithful 验证 | 用第二个 LLM 验证答案是否忠于原文 |
常见面试问题
Q1: RAG 中 LLM 的 temperature 应该设多少?
答案:
- 知识库问答:
temperature=0,追求确定性和准确性 - 创意写作辅助:
temperature=0.7,允许一定创造性 - 代码生成:
temperature=0,代码必须精确
Q2: 如何处理检索到的文档相互矛盾?
答案:
- 时间优先:在 Prompt 中指示"优先参考最新的资料"
- 来源权重:官方文档 > 社区文档 > 用户评论
- 显式说明:让 LLM 在回答中指出"资料存在矛盾,分别说明"