mcpField
C
citations-retrieval

citations-retrieval

github.com/thelobbiAgent 工作流2163 次安装1 天前更新
代码仓库

关于 citations-retrieval

Citations & Retrieval Skill 提供基于文档的引用与检索增强生成(RAG)实现指导,面向需要让 AI 回答可追溯、可核验的应用开发者。内容涵盖引用类型、检索流程、格式化与完整性验证。

主要功能

  1. 01支持文本、PDF 与内容块引用
  2. 02介绍基础 RAG 流程与上下文检索
  3. 03提供多文档问答模式
  4. 04说明学术引用格式化
  5. 05包含引用完整性验证建议
  6. 06涵盖文档分块与缓存实践

适用场景

  1. 01为文档问答添加来源标注
  2. 02构建基于文档的 RAG 应用
  3. 03核验回答引用是否对应原文
  4. 04整理多文档研究答案

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/thelobbi/claude --skill citations-retrieval

安装后在 Claude Code 中输入/citations-retrieval确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

OUTLINE.md
QUICK-REFERENCE.md
README.md
RESEARCH-SUMMARY.md

详细介绍

引用与检索技能

实现基于文档的引用和 RAG 模式,生成有依据、可验证的 AI 响应。

何时使用此技能

  • 带来源归属的文档问答
  • RAG(检索增强生成)系统
  • 基于所提供文档生成有依据的响应
  • 构建值得信赖的 AI 应用
  • 带引用的研究与分析

核心概念

引用类型
类型用途格式
char_location文本文档字符范围
page_locationPDF页码
content_block_location自定义内容内容块索引

基本引用

启用引用
import anthropic

client = anthropic.Anthropic()

response = client.messages.create(
    model="claude-sonnet-5",
    max_tokens=1024,
    documents=[
        {
            "type": "document",
            "source": {
                "type": "text",
                "media_type": "text/plain",
                "data": "The company was founded in 2020. Revenue reached $10M in 2023."
            },
            "title": "Company Overview",
            "citations": {"enabled": True}  # Enable citations!
        }
    ],
    messages=[{"role": "user", "content": "When was the company founded and what was the revenue?"}]
)

# Extract citations from response
for block in response.content:
    if block.type == "text":
        for citation in block.citations:
            print(f"Cited: {citation.document_title}")
            print(f"Location: chars {citation.start_char_index}-{citation.end_char_index}")
自定义内容块
# Fine-grained control over citation granularity
documents = [{
    "type": "document",
    "source": {
        "type": "content",
        "content": [
            {"type": "text", "text": "Section 1: Introduction..."},
            {"type": "text", "text": "Section 2: Methods..."},
            {"type": "text", "text": "Section 3: Results..."}
        ]
    },
    "title": "Research Paper",
    "citations": {"enabled": True}
}]

RAG 实现

基本 RAG 流水线
from sentence_transformers import SentenceTransformer
import numpy as np

# 1. Embed documents
embedder = SentenceTransformer('all-MiniLM-L6-v2')

def embed_documents(documents):
    chunks = []
    embeddings = []
    for doc in documents:
        # Chunk the document
        doc_chunks = chunk_document(doc, chunk_size=512)
        chunks.extend(doc_chunks)
        embeddings.extend(embedder.encode(doc_chunks))
    return chunks, np.array(embeddings)

# 2. Retrieve relevant chunks
def retrieve(query, chunks, embeddings, top_k=5):
    query_embedding = embedder.encode([query])[0]
    similarities = np.dot(embeddings, query_embedding)
    top_indices = np.argsort(similarities)[-top_k:][::-1]
    return [chunks[i] for i in top_indices]

# 3. Generate with retrieved context
def rag_query(query, chunks, embeddings):
    relevant_chunks = retrieve(query, chunks, embeddings)

    response = client.messages.create(
        model="claude-sonnet-5",
        max_tokens=1024,
        documents=[{
            "type": "document",
            "source": {"type": "text", "media_type": "text/plain", "data": chunk},
            "title": f"Source {i+1}",
            "citations": {"enabled": True}
        } for i, chunk in enumerate(relevant_chunks)],
        messages=[{"role": "user", "content": query}]
    )
    return response
上下文检索(效果提升 49-67%)
# Add context to each chunk before embedding
def add_chunk_context(chunk, full_document):
    """Prepend context to improve retrieval accuracy by 49-67%"""

    context_prompt = f"""<document>
{full_document}
</document>

Please provide a short, succinct context for this chunk that will help with retrieval:

<chunk>
{chunk}
</chunk>

Context:"""

    response = client.messages.create(
        model="claude-haiku-4-5",  # Fast, cheap
        max_tokens=100,
        messages=[{"role": "user", "content": context_prompt}]
    )

    context = response.content[0].text
    return f"{context}\n\n{chunk}"

# Apply to all chunks
contextual_chunks = [add_chunk_context(chunk, full_doc) for chunk in chunks]

引用格式

格式化为编号参考文献
def format_with_citations(response):
    """Format response with numbered inline citations"""
    text = ""
    citations = []
    citation_map = {}

    for block in response.content:
        if block.type == "text":
            current_text = block.text

            for citation in block.citations:
                key = (citation.document_title, citation.start_char_index)
                if key not in citation_map:
                    citation_map[key] = len(citations) + 1
                    citations.append(citation)

                # Insert citation number
                ref_num = citation_map[key]
                current_text += f" [{ref_num}]"

            text += current_text

    # Add references section
    text += "\n\n## References\n"
    for i, citation in enumerate(citations, 1):
        text += f"[{i}] {citation.document_title}\n"

    return text
学术引用格式
def format_apa(author, year, title, source):
    """APA format: Author (Year). Title. Source."""
    return f"{author} ({year}). {title}. {source}."

def format_mla(author, title, source, year):
    """MLA format: Author. "Title." Source, Year."""
    return f'{author}. "{title}." {source}, {year}.'

def format_chicago(author, title, source, year):
    """Chicago format: Author. Title. Source, Year."""
    return f"{author}. {title}. {source}, {year}."

多文档问答

def multi_doc_qa(question, documents):
    """Answer questions across multiple documents with citations"""

    doc_inputs = []
    for i, doc in enumerate(documents):
        doc_inputs.append({
            "type": "document",
            "source": {
                "type": "text",
                "media_type": "text/plain",
                "data": doc["content"]
            },
            "title": doc.get("title", f"Document {i+1}"),
            "citations": {"enabled": True}
        })

    response = client.messages.create(
        model="claude-sonnet-5",
        max_tokens=2048,
        documents=doc_inputs,
        messages=[{
            "role": "user",
            "content": f"Answer this question based on the provided documents. Cite your sources.\n\nQuestion: {question}"
        }]
    )

    return response

RAG 的提示词缓存

# Cache static documents for repeated queries
response = client.messages.create(
    model="claude-sonnet-5",
    max_tokens=1024,
    documents=[{
        "type": "document",
        "source": {"type": "text", "media_type": "text/plain", "data": large_document},
        "title": "Knowledge Base",
        "citations": {"enabled": True},
        "cache_control": {"type": "ephemeral"}  # Cache this document!
    }],
    messages=[{"role": "user", "content": query}]
)

错误处理与验证

验证引用完整性
def validate_citations(response, documents):
    """Ensure all citations reference provided documents"""

    cited_titles = set()
    for block in response.content:
        if block.type == "text":
            for citation in block.citations:
                cited_titles.add(citation.document_title)

    provided_titles = {doc.get("title") for doc in documents}

    # Check for invalid citations
    invalid = cited_titles - provided_titles
    if invalid:
        raise ValueError(f"Citations reference unknown documents: {invalid}")

    return True

def extract_citation_spans(response):
    """Extract text spans for each citation"""

    citation_data = []
    for block in response.content:
        if block.type == "text":
            text = block.text
            for citation in block.citations:
                span = text[citation.start_char_index:citation.end_char_index]
                citation_data.append({
                    "text": span,
                    "document": citation.document_title,
                    "start": citation.start_char_index,
                    "end": citation.end_char_index
                })

    return citation_data

最佳实践

应做:
  • 为所有基于文档的查询启用引用
  • 使用上下文检索提高准确性(+49-67%)
  • 使用 cache_control 缓存静态文档
  • 为文档提供清晰的标题以标明来源
  • 适当拆分文档(512-1024 个 token)
  • 使用响应前验证引用完整性
  • 统一引用格式(APA、MLA、Chicago)
  • 在生产系统中测试引用提取
不应做:
  • 未启用引用就依赖引用
  • 使用过小的分块(<100 个 token)
  • 忽略生产环境中的引用验证
  • 跳过文档预处理
  • 在同一文档中混用引用格式
  • 假设所有大模型响应默认都带引用
  • 未进行引用验证测试就部署

故障排除

未返回引用
# Ensure citations are enabled
documents = [{
    "type": "document",
    "source": {"type": "text", "media_type": "text/plain", "data": content},
    "citations": {"enabled": True}  # Must be explicit!
}]
引用指向了错误的文本
# Verify character indexes match actual text
text = block.text
cited_text = text[citation.start_char_index:citation.end_char_index]
print(f"Cited text: {cited_text}")
print(f"Expected: {expected_text}")
大型文档的性能
# Use chunking for large documents
def chunk_with_overlap(text, chunk_size=1024, overlap=256):
    chunks = []
    for i in range(0, len(text), chunk_size - overlap):
        chunks.append(text[i:i + chunk_size])
    return chunks

# Pass chunks individually for better retrieval
large_chunks = chunk_with_overlap(large_text)

集成示例

#!/usr/bin/env python3
"""Complete RAG + Citations example"""

import anthropic
from sentence_transformers import SentenceTransformer
import numpy as np

def create_rag_system():
    """Initialize RAG system with citations"""

    client = anthropic.Anthropic()
    embedder = SentenceTransformer('all-MiniLM-L6-v2')

    # Sample documents
    documents = [
        {
            "title": "Python Guide",
            "content": "Python 3.11 introduced exception groups..."
        },
        {
            "title": "Web Standards",
            "content": "HTTP/2 introduced multiplexing capabilities..."
        }
    ]

    # Embed documents
    chunks = []
    embeddings = []
    for doc in documents:
        # Add document title as context
        chunk = f"[{doc['title']}]\n{doc['content']}"
        chunks.append(chunk)
        embeddings.append(embedder.encode(chunk))

    embeddings = np.array(embeddings)

    # Query function
    def query(question):
        # Retrieve relevant chunks
        query_emb = embedder.encode(question)
        similarities = np.dot(embeddings, query_emb)
        top_idx = np.argmax(similarities)
        relevant_chunk = chunks[top_idx]

        # Get cited answer
        response = client.messages.create(
            model="claude-sonnet-5",
            max_tokens=1024,
            documents=[{
                "type": "document",
                "source": {
                    "type": "text",
                    "media_type": "text/plain",
                    "data": relevant_chunk
                },
                "title": documents[top_idx]["title"],
                "citations": {"enabled": True}
            }],
            messages=[{
                "role": "user",
                "content": question
            }]
        )

        return response

    return query

if __name__ == "__main__":
    query_fn = create_rag_system()
    response = query_fn("What is Python 3.11?")

    # Display with citations
    for block in response.content:
        if block.type == "text":
            print(f"Answer: {block.text}")
            for citation in block.citations:
                print(f"  - Cited from: {citation.document_title}")

性能提示

  • 批量处理查询以提高吞吐量(10-20 个并发请求)
  • 使用提示词缓存缓存常用文档
  • 使用 Haiku 生成上下文(更快、更省钱)
  • 策略性地拆分内容(按句子/段落边界)
  • 监控 token 用量以了解引用开销(约 5-10%)

限制

  • 引用仅来自所提供的文档
  • 字符索引引用要求文本完全匹配
  • PDF 支持需要结构化解析
  • 提取引用会消耗 token(约 5-10% 的开销)
  • 不支持对带引用的响应执行批处理操作

另请参阅

  • [[llm-integration]] - API 基础知识与身份验证
  • [[prompt-caching]] - 缓存文档以节省成本
  • [[vision-multimodal]] - PDF 和图像处理
  • [[complex-reasoning]] - 带引用的扩展思考

评论

支持 Markdown