用 Sciverse 做科学问答的 Citation Grounding
RAGAgent高级
用 Sciverse 做科学问答的 Citation Grounding
为 LLM 回答的每一句话找到可验证的文献来源,消除幻觉
用户场景
开发者构建高可信度科学问答系统,需要对 LLM 生成的每个论点进行事实核查,通过检索文献并验证原文来确认或标记为不可验证。
预估调用量
~10–25 次 API 调用
适用工具
agentic-searchcontent调用链路
LLM 生成草稿→ 拆句→ agentic-search(逐句)→ content(验证原文)→ 标注引用
输入示例
LLM 生成的草稿回答: "mRNA 疫苗使用可电离脂质纳米颗粒(iLNP)包裹 mRNA。其中 MC3 是最广泛使用的可电离脂质。LNP 的粒径通常在 80-100nm。"
输出示例
{
"grounded_answer": "mRNA 疫苗使用可电离脂质纳米颗粒(iLNP)包裹 mRNA [1]。其中 MC3 是最广泛使用的可电离脂质 [2]。LNP 的粒径通常在 80-100nm [1]。",
"citations": [
{"id": 1, "doc_id": "lnp_review_2021", "offset": 4200, "quote": "iLNP encapsulates mRNA...", "verified": true},
{"id": 2, "doc_id": "mc3_study_2018", "offset": 1800, "quote": "MC3 (DLin-MC3-DMA) remains the most widely...", "verified": true}
],
"unverified_claims": []
}Agent Prompt 示例
你是一个 Citation Grounding Agent。工作流程:
1. 接收 LLM 生成的草稿回答
2. 将草稿拆分为独立论点/句子
3. 对每个论点调用 agentic-search 查找支持证据
4. 对高分结果调用 content 读取原文,确认证据是否真正支持该论点
5. 输出每句话的来源(doc_id + offset + 原文引用),无法验证的标记为 [unverified]分步实现
Step 1: 环境准备
安装依赖并配置环境变量
!pip install httpx
import os
os.environ["SCIVERSE_API_TOKEN"] = "sv-your-token-here" # 替换为你的真实值Step 2: 拆分草稿并逐句检索
将 LLM 回答拆分为独立论点,对每个论点调用 agentic-search
import os
import httpx
BASE = "https://api.sciverse.space"
TOKEN = os.environ["SCIVERSE_API_TOKEN"]
HEADERS = {"Authorization": f"Bearer {TOKEN}"}
def split_claims(draft: str) -> list:
"""将草稿拆分为独立论点句子"""
sentences = [s.strip() for s in draft.split("。") if s.strip()]
return [s for s in sentences if len(s) > 10]
async def search_evidence(claim: str):
"""对单个论点检索支持证据"""
async with httpx.AsyncClient(timeout=30) as client:
resp = await client.post(
f"{BASE}/agentic-search",
headers=HEADERS,
json={"query": claim, "top_k": 5}
)
resp.raise_for_status()
return (resp.json().get("hits") or [])
draft = "mRNA 疫苗使用可电离脂质纳米颗粒(iLNP)包裹 mRNA。其中 MC3 是最广泛使用的可电离脂质。LNP 的粒径通常在 80-100nm。"
claims = split_claims(draft)
print(f"Split into {len(claims)} claims")
Step 3: 调用 content 验证原文
对高分 hit 调用 content 读取原文,确认是否真正支持论点
async def verify_with_content(hit: dict, claim: str) -> dict:
"""读取原文验证证据是否真正支持论点"""
async with httpx.AsyncClient(timeout=30) as client:
resp = await client.get(
f"{BASE}/content",
headers=HEADERS,
params={"doc_id": hit["doc_id"], "offset": hit.get("offset", 0), "limit": 1000}
)
resp.raise_for_status()
data = resp.json()
# 检查原文中是否包含与论点相关的关键词
text = data["text"].lower()
claim_keywords = [w for w in claim.lower().split() if len(w) > 3]
match_count = sum(1 for kw in claim_keywords if kw in text)
match_ratio = match_count / max(len(claim_keywords), 1)
return {
"doc_id": hit["doc_id"],
"offset": hit.get("offset", 0),
"quote": data["text"][:150],
"match_ratio": match_ratio,
"verified": match_ratio >= 0.3 and hit["score"] >= 0.7
}
async def ground_claims(claims: list):
results = []
for claim in claims:
hits = await search_evidence(claim)
if hits and hits[0]["score"] >= 0.6:
verification = await verify_with_content(hits[0], claim)
results.append({"claim": claim, **verification})
else:
results.append({"claim": claim, "verified": False, "doc_id": None})
status = "\\u2713" if results[-1]["verified"] else "\\u2717"
print(f" {status} {claim[:50]}...")
return results
results = await ground_claims(claims)
Step 4: 生成带引用的最终回答
将验证结果组装为带 citation 的最终输出
def build_grounded_answer(results: list) -> dict:
citations = []
grounded_parts = []
unverified = []
for r in results:
if r["verified"]:
cite_id = len(citations) + 1
citations.append({
"id": cite_id,
"doc_id": r["doc_id"],
"offset": r.get("offset", 0),
"quote": r.get("quote", ""),
"verified": True
})
grounded_parts.append(f"{r['claim']} [{cite_id}]")
else:
grounded_parts.append(f"{r['claim']} [unverified]")
unverified.append(r["claim"])
return {
"grounded_answer": "\\u3002".join(grounded_parts) + "\\u3002",
"citations": citations,
"unverified_claims": unverified
}
final = build_grounded_answer(results)
print(f"\
Grounded answer:\
{final['grounded_answer']}")
print(f"\
Citations: {len(final['citations'])}")
print(f"Unverified: {len(final['unverified_claims'])}")
for c in final["citations"]:
print(f" [{c['id']}] {c['doc_id']} (offset: {c['offset']})")
注意事项
- 仅靠 score 判定 verified 不够严谨;本示例增加了 content 原文验证步骤
- match_ratio 关键词匹配仅为简化示例,生产环境建议使用 LLM/NLI 模型判断原文是否真正支持论点
- 验证逻辑可根据需求增强:如使用 LLM 判断原文是否支持论点(NLI 任务)
- score 阈值 0.7 是建议值,医学领域建议 0.8+
- 生产环境建议并发验证多个 claims(asyncio.gather)以提升速度
- 对于 unverified 的论点,建议在最终输出中明确标注或要求用户确认
FAQ
这个案例解决什么问题?
为 AI 生成的科学回答补充论文证据,降低无来源结论。
如何判断证据是否支持回答?
需要读取 content 上下文,并保留原文 quote 供人工或模型复核。
是否可以自动给每句话加引用?
可以辅助生成,但引用是否充分仍需根据原文上下文判断。
不支持的 claim 怎么处理?
应标记为未找到充分证据,而不是强行附会引用。
下一步
还没有 API Key?
登录控制台「密钥」即可创建。同一套 API Key 可用于已开通的 Sciverse、点石与 Skills 能力,提供基础试用额度,具体以账号权限为准。