mcpField
K
knowledge-base-rag

knowledge-base-rag

github.com/itallstartedwithaidea开发44135 次安装28 周前更新
代码仓库

关于 knowledge-base-rag

knowledge-base-rag 是面向构建文档问答和语义搜索系统的 Agent Skill,涵盖从文档导入到基于检索内容生成回答的 RAG 流程,并强调按文档类型选择分块策略。

主要功能

  1. 01文档导入与元数据提取
  2. 02语义及递归分块
  3. 03生成嵌入并建立向量索引
  4. 04语义检索与结果重排
  5. 05基于来源生成回答

适用场景

  1. 01基于私有文档构建问答系统
  2. 02为文档和 PDF 建立可搜索知识库
  3. 03以检索事实辅助客户支持机器人

安装

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

$npx skills add https://github.com/itallstartedwithaidea/agent-skills --skill knowledge-base-rag

安装后在 Claude Code 中输入/knowledge-base-rag确认已加载。

详细介绍

知识库 RAG

Agent Skills™ 的一部分,由 googleadsagent.ai™ 提供

描述

知识库 RAG 实现了完整的检索增强生成流程:文档摄取、智能分块、嵌入生成、向量存储索引、语义检索和基于事实的响应生成。Agent 会构建 RAG 系统,从私有知识库中回答问题,并附上引用来源,减少幻觉。

RAG 解决了大模型的根本局限:它们无法获取训练截止日期之后创建的信息,也无法获取从未用于训练的专有信息。通过从向量存储中检索相关文档,并将其注入提示词上下文,RAG 可以使模型的响应基于准确、最新且与组织相关的知识。

RAG 系统的质量更多取决于分块策略,而非模型选择。这项技能融入了经过生产环境验证的分块方法:保留段落连贯性的语义分块、遵循文档结构(标题、代码块、表格)的递归拆分,以及在分块边界之间保持上下文的重叠窗口。每种策略都会根据文档类型进行匹配,以获得最佳检索质量。

适用场景

  • 基于私有文档构建问答系统
  • 从文档、Wiki 或 PDF 创建可搜索的知识库
  • 通过让大模型的响应基于检索到的事实来减少幻觉
  • 在大型文档集合中实现语义搜索
  • 构建具备产品专属知识的客户支持机器人
  • 用户询问 RAG、向量搜索或文档嵌入时

工作原理

graph TD
    A[Documents: PDF, MD, HTML] --> B[Ingestion Pipeline]
    B --> C[Extract Text + Metadata]
    C --> D[Intelligent Chunking]
    D --> E[Generate Embeddings]
    E --> F[Index in Vector Store]
    G[User Query] --> H[Embed Query]
    H --> I[Semantic Search: Top-K]
    I --> J[Re-rank Results]
    J --> K[Construct Prompt with Context]
    K --> L[LLM Generation]
    L --> M[Response with Citations]

该流程分为两个阶段:离线摄取(文档转向量)和在线检索(查询转答案)。重排序步骤会使用交叉编码器优化初始向量搜索结果,在生成步骤之前提高精确率。

实现

from dataclasses import dataclass
import hashlib

@dataclass
class Chunk:
    text: str
    metadata: dict
    embedding: list[float] | None = None

    @property
    def id(self) -> str:
        return hashlib.sha256(self.text.encode()).hexdigest()[:16]

class RecursiveChunker:
    def __init__(self, max_tokens: int = 512, overlap: int = 64):
        self.max_tokens = max_tokens
        self.overlap = overlap
        self.separators = ["\n## ", "\n### ", "\n\n", "\n", ". ", " "]

    def chunk(self, text: str, metadata: dict) -> list[Chunk]:
        chunks = self._split(text, self.separators)
        return [
            Chunk(text=c.strip(), metadata={**metadata, "chunk_index": i})
            for i, c in enumerate(chunks) if c.strip()
        ]

    def _split(self, text: str, separators: list[str]) -> list[str]:
        if not separators or self._token_count(text) <= self.max_tokens:
            return [text]

        sep = separators[0]
        parts = text.split(sep)
        chunks, current = [], ""

        for part in parts:
            candidate = current + sep + part if current else part
            if self._token_count(candidate) > self.max_tokens and current:
                chunks.append(current)
                overlap_text = current[-self.overlap * 4:]
                current = overlap_text + sep + part
            else:
                current = candidate

        if current:
            chunks.append(current)

        result = []
        for chunk in chunks:
            if self._token_count(chunk) > self.max_tokens:
                result.extend(self._split(chunk, separators[1:]))
            else:
                result.append(chunk)
        return result

    def _token_count(self, text: str) -> int:
        return len(text) // 4

class RAGPipeline:
    def __init__(self, embedder, vector_store, llm):
        self.embedder = embedder
        self.store = vector_store
        self.llm = llm
        self.chunker = RecursiveChunker()

    async def ingest(self, documents: list[dict]) -> int:
        all_chunks = []
        for doc in documents:
            chunks = self.chunker.chunk(doc["text"], doc["metadata"])
            for chunk in chunks:
                chunk.embedding = await self.embedder.embed(chunk.text)
            all_chunks.extend(chunks)

        await self.store.upsert(all_chunks)
        return len(all_chunks)

    async def query(self, question: str, top_k: int = 5) -> dict:
        query_embedding = await self.embedder.embed(question)
        results = await self.store.search(query_embedding, top_k=top_k)

        context = "\n\n".join(
            f"[Source: {r.metadata.get('source', 'unknown')}]\n{r.text}" for r in results
        )

        prompt = f"""Answer the question based on the provided context. Cite sources.
If the context does not contain the answer, say so explicitly.

Context:
{context}

Question: {question}"""

        response = await self.llm.generate(prompt)
        return {"answer": response, "sources": [r.metadata for r in results]}

最佳实践

  • 使用遵循文档结构(标题、段落、代码块)的递归分块
  • 对大多数使用场景,将分块大小设为 256-512 个 token,并设置 10-15% 的重叠
  • 在将向量搜索结果传递给大模型之前,使用交叉编码器进行重排序
  • 在每个分块中包含来源元数据,以便生成引用
  • 在建立索引之前,按内容哈希值去重分块,避免检索噪声
  • 指示大模型在上下文不包含答案时回答“我不知道”

平台兼容性

平台支持情况备注
Cursor完全支持流程代码生成
VS Code完全支持Python/TS RAG 实现
Windsurf完全支持支持 RAG 工作流
Claude Code完全支持端到端 RAG 构建
Cline完全支持向量存储集成
aider部分支持代码级支持

相关技能

关键词

rag retrieval-augmented-generation vector-search embeddings chunking knowledge-base semantic-search document-qa


© 2026 googleadsagent.ai™ | Agent Skills™ | MIT License

评论

支持 Markdown