Use Sciverse for Citation Grounding in Scientific Q&A
RAGAgentAdvanced

Use Sciverse for Citation Grounding in Scientific Q&A

Find verifiable literature sources for each LLM statement and reduce hallucinations

Scenario
Developers building high-trust scientific Q&A systems need to fact-check each generated claim by retrieving and verifying supporting literature.
Estimated calls
~10-25 API calls
Tools
agentic-searchcontent
Pipeline
LLM 生成草稿→ 拆句→ agentic-search(逐句)→ content(验证原文)→ 标注引用

Input example

Draft answer:
"Protein language models can predict variant effects and support structure prediction."
Need: verify each claim with literature evidence.

Output example

Grounded answer:
Claim 1: supported by doc_id=..., quote=...
Claim 2: supported by doc_id=..., quote=...
Unsupported claims are marked as "not verified".

Agent Prompt example

You are a citation grounding assistant. Split draft answers into claims, retrieve evidence for each claim, verify against source text, and output a final answer with citations.

Implementation steps

Step 1: Set up the environment

Configure token and HTTP client

!pip install httpx
import os
os.environ["SCIVERSE_API_TOKEN"] = "sv-your-token-here"  # 替换为你的真实值

Step 2: Split the draft and search per sentence

Break the answer into atomic claims and search for each claim

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: Verify source text with content

Read original context to verify whether evidence supports the claim

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: Generate the final cited answer

Return only verified claims with citations and mark unsupported ones

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']})")

Notes

  • Use atomic claims; broad sentences are harder to verify.
  • Citation grounding is stricter than ordinary RAG.
  • A retrieved paper is not enough; verify the exact source text.
  • Mark unsupported or weakly supported claims explicitly.
  • Keep quotes short and traceable.
  • Avoid merging evidence from unrelated papers into one citation.

FAQ

这个案例解决什么问题?

为 AI 生成的科学回答补充论文证据,降低无来源结论。

如何判断证据是否支持回答?

需要读取 content 上下文,并保留原文 quote 供人工或模型复核。

是否可以自动给每句话加引用?

可以辅助生成,但引用是否充分仍需根据原文上下文判断。

不支持的 claim 怎么处理?

应标记为未找到充分证据,而不是强行附会引用。

Next steps

Need an API key?

Create one in Console > Tokens.The same API key works for enabled Sciverse, DianShi, and Skills capabilities, with starter quota available according to account permissions.

Open console