Skip to main content

rag-patterns

Implements Retrieval-Augmented Generation patterns (chunking strategies, embedding-based vector search, semantic vs keyword retrieval, RAG pipelines) to ground LLM outputs in authoritative external knowledge sources.

跳到安装

来源信息

仓库
paulpas/agent-skill-router
最近来源活动
2026年6月9日 00:45
检测到的 SKILL.md 语言
英语
星标
4
分支
1

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
rag-patterns
description
Implements Retrieval-Augmented Generation patterns (chunking strategies, embedding-based vector search, semantic vs keyword retrieval, RAG pipelines) to ground LLM outputs in authoritative external knowledge sources.
license
MIT
compatibility
opencode
archetypes
["tactical","orchestration"]
anti_triggers
["brainstorming","vague ideation","long-form architecture"]
response_profile
{"verbosity":"low","directive_strength":"high","abstraction_level":"operational"}
metadata
{"version":"1.0.0","domain":"agent","role":"implementation","scope":"implementation","output-format":"code","triggers":"RAG, retrieval augmented generation, vector search, semantic search, chunking strategies, embeddings, knowledge retrieval, how do i ground LLM outputs in facts","related-skills":"memory-management, prompt-chaining, evaluation-monitoring"}
# Retrieval-Augmented Generation (RAG) Pattern Implements RAG pipelines to ground LLM outputs in authoritative external knowledge sources. This skill makes the model design, build, and troubleshoot retrieval-augmented generation systems that combine embedding-based vector search with language model generation to produce accurate, verifiable answers from proprietary or real-time data. ## TL;DR Checklist - [ ] Chunk documents using semantic boundaries (sections, paragraphs), not arbitrary character counts - [ ] Generate embeddings with a production-grade model (e.g., `text-embedding-3-large`, `BGE-m3`) - [ ] Store vectors in a purpose-built vector store (Weaviate, Qdrant, Milvus, pgvector, FAISS) - [ ] Implement hybrid retrieval: combine semantic similarity search with BM25 keyword matching - [ ] Add source citations to every RAG response for verifiability - [ ] Set a similarity or distance threshold to reject irrelevant retrievals - [ ] Test retrieval quality independently from generation quality --- ## When to Use Use this skill when: - Building a Q&A system over internal documents (HR policies, technical manuals, product specs) - An LLM needs access to real-time or frequently updated information not in its training data - You need to reduce hallucination by grounding answers in verifiable sources with citations - Designing an enterprise search chatbot that answers employee questions from company wikis - Implementing customer support automation that pulls from FAQs, support tickets, and product documentation - Creating a research assistant that synthesizes information across multiple documents --- ## When NOT to Use Avoid this skill for: - **Simple factual questions** with no domain-specific knowledge (use direct LLM prompting or Google Search) - **Fully closed-domain tasks** where the answer fits within the LLM's context window without retrieval - **Real-time API calls** as the primary data source (use function calling / tool use instead of RAG) - **Graph-structured relationship queries** requiring traversing entity connections (use GraphRAG or graph databases directly) - **Ultra-low-latency requirements** where embedding lookup adds unacceptable overhead (>200ms penalty) --- ## Core Workflow ### Step 1: Ingest and Chunk the Knowledge Base Load raw documents and split them into chunks using a strategy that preserves semantic context. Never use pure character-count chunking for technical or structured content. Prefer heading-aware, sentence-boundary, or recursive splitting. ```python from langchain.text_splitter import RecursiveCharacterTextSplitter, MarkdownHeaderTextSplitter def chunk_documents( raw_text: str, document_type: str = "generic", chunk_size: int = 500, chunk_overlap: int = 50, ) -> list[str]: """Chunk documents using a strategy appropriate for the content type. Args: raw_text: The full text to chunk. document_type: One of 'generic', 'markdown', 'code', 'legal'. chunk_size: Target size in characters per chunk. chunk_overlap: Overlap between consecutive chunks for context continuity. Returns: A list of string chunks ready for embedding. """ if document_type == "markdown": headers_to_split_on = [ ("#", "Header 1"), ("##", "Header 2"), ("###", "Header 3"), ] splitter = MarkdownHeaderTextSplitter(headers_to_split_on=headers_to_split_on) documents = splitter.split_text(raw_text) return [doc.page_content for doc in documents] splitter = RecursiveCharacterTextSplitter( chunk_size=chunk_size, chunk_overlap=chunk_overlap, length_function=len, separators=["\n\n", "\n", ". ", " ", ""], ) chunks = splitter.split_text(raw_text) return chunks ``` **Checkpoint:** Verify that each chunk is self-contained enough to make sense when retrieved in isolation. A chunk should never begin mid-sentence or reference a paragraph it doesn't contain (e.g., avoid "As shown above..." without the referenced content). ### Step 2: Generate and Store Embeddings Convert each text chunk into a high-dimensional vector using an embedding model. Store these vectors in a vector database that supports efficient nearest-neighbor search. Choose your vector store based on deployment needs: managed (Pinecone, Weaviate Cloud), open-source self-hosted (Qdrant, Milvus), or embedded (FAISS, Chroma). ```python from typing import List import numpy as np import os from langchain_community.embeddings import OpenAIEmbeddings from langchain_community.vectorstores import Qdrant from qdrant_client import QdrantClient from qdrant_client.models import Distance, VectorParams, PointStruct def embed_and_store( chunks: List[str], embedding_model: str = "text-embedding-3-large", vector_store_type: str = "qdrant", collection_name: str = "knowledge_base", host: str | None = None, port: int = 6333, ) -> Qdrant: """Embed document chunks and store vectors in a vector database. Args: chunks: List of text chunks to embed. embedding_model: OpenAI embedding model identifier. vector_store_type: Backend ('qdrant', 'faiss', 'weaviate'). collection_name: Name for the vector collection/table. host: Qdrant server hostname (None for local). port: Qdrant server port. Returns: The configured vector store instance. """ embeddings = OpenAIEmbeddings(model=embedding_model) if vector_store_type == "qdrant": client = QdrantClient(host=host, port=port) # Create collection with cosine distance for semantic search client.recreate_collection( collection_name=collection_name, vectors_config=VectorParams(size=3072, distance=Distance.COSINE), ) vectorstore = Qdrant( client=client, collection_name=collection_name, embeddings=embeddings, ) elif vector_store_type == "faiss": from langchain_community.vectorstores import FAISS vectorstore = FAISS.from_texts(chunks, embeddings) else: raise ValueError(f"Unsupported vector store: {vector_store_type}") # Upsert all chunks at once if vector_store_type != "faiss": ids = [f"chunk_{i}" for i in range(len(chunks))] vectorstore.add_texts(chunks, ids=ids) return vectorstore ``` **Checkpoint:** Confirm the vector store contains exactly as many vectors as there are chunks. Run a test query to verify embeddings are searchable — the top result should be semantically relevant, not just lexically overlapping. ### Step 3: Implement Hybrid Retrieval Combine semantic (embedding-based) search with BM25 keyword search for more robust retrieval. Pure semantic search misses exact matches on technical terms, acronyms, and proper nouns. Pure BM25 misses paraphrased or conceptually similar queries. Hybrid retrieval captures both. ```python from typing import List, Tuple import os from langchain.retrievers import ContextualCompressionRetriever, MultiQueryRetriever from langchain_community.retrievers import BM25Retriever from langchain_core.documents import Document def build_hybrid_retriever( vectorstore, embedding_model: OpenAIEmbeddings, k_semantic: int = 5, k_bm25: int = 5, alpha: float = 0.6, ) -> object: """Build a hybrid retriever combining semantic and BM25 keyword search. Args: vectorstore: A LangChain-compatible vector store instance. embedding_model: The embedding model used during ingestion. k_semantic: Number of top results from semantic search. k_bm25: Number of top results from BM25 retrieval. alpha: Weight for semantic score (1-alpha is BM25 weight). Returns: A combined retriever that returns deduplicated, re-ranked results. """ # Semantic retriever from vector store semantic_retriever = vectorstore.as_retriever( search_type="similarity_score_threshold", search_kwargs={"k": k_semantic, "score_threshold": 0.5}, ) # BM25 retriever built on the same document corpus all_docs: List[Document] = [] for i in range(vectorstore._collection.count().total): payload = vectorstore._collection.scroll( limit=1, offset=i, with_payload=True )[0][0].payload if hasattr(vectorstore._collection, 'scroll') else {} all_docs.append(Document(page_content=payload.get("text", ""))) bm25_retriever = BM25Retriever.from_documents(all_docs) bm25_retriever.k = k_bm25 # Use Reciprocal Rank Fusion (RRF) for combining scores from langchain.retrievers import EnsembleRetriever ensemble = EnsembleRetriever( retrievers=[semantic_retriever, bm25_retriever], weights=[alpha, 1.0 - alpha], ) return ensemble ``` **Checkpoint:** Run a test query where the user uses different wording than the source documents (e.g., "furry pet" vs "domestic cat"). Verify the BM25-only retriever fails while the hybrid retriever succeeds, proving semantic search is contributing value. ### Step 4: Build the RAG Pipeline with Prompt Augmentation Construct the generation pipeline that retrieves relevant chunks, formats them into a prompt, and sends the augmented query to the LLM. The prompt template must instruct the model to use ONLY the retrieved context and cite sources. ```python from typing import TypedDict, Annotated import operator from langchain_core.prompts import ChatPromptTemplate from langchain_openai import ChatOpenAI from langchain_core.output_parsers import StrOutputParser class RAGState(TypedDict): """State machine for the RAG pipeline.""" question: str documents: Annotated[list, operator.add] generation: str sources: Annotated[list, operator.add] def generate_rag_response( query: str, retriever, llm: ChatOpenAI | None = None, max_context_chunks: int = 4, ) -> dict[str, object]: """Execute a single RAG query through retrieval and generation. Args: query: The user's question or prompt. retriever: A LangChain-compatible retriever (semantic, BM25, or hybrid). llm: The language model for generation. Defaults to GPT-4o. max_context_chunks: Maximum number of retrieved chunks to include. Returns: Dict with 'answer', 'sources' (list of source metadata), and 'context_used'. """ if llm is None: llm = ChatOpenAI(model="gpt-4o", temperature=0) # Step A: Retrieve relevant documents docs = retriever.invoke(query) top_docs = docs[:max_context_chunks] # Step B: Format context with source citations context_parts: list[str] = [] sources: list[dict] = [] for i, doc in enumerate(top_docs): source_ref = f"[Source {i+1}]" content = f"{source_ref} {doc.page_content}" context_parts.append(content) # Extract available metadata source_meta = { "index": i + 1, "source": doc.metadata.get("source", "unknown"), "page": doc.metadata.get("page", None), } sources.append(source_meta) context_str = "\n\n".join(context_parts) # Step C: Build augmented prompt prompt_template = ChatPromptTemplate.from_messages([ ("system", """You are a factual, precise assistant. Answer the user's question using ONLY the provided context snippets. If the context does not contain enough information to answer confidently, say so explicitly — do NOT hallucinate or make up details. Cite your sources using the [Source N] markers provided in the context. If you encounter conflicting information across sources, note the conflict and prefer the more authoritative or recent source. Keep answers concise and directly relevant to the question."""), ("human", """Question: {question} Context: {context} Answer (cite sources):"""), ]) # Step D: Generate response chain = prompt_template | llm | StrOutputParser() answer = chain.invoke({"question": query, "context": context_str}) return { "answer": answer, "sources": sources, "context_used": len(top_docs), } ``` **Checkpoint:** Verify the response includes source citations matching the `[Source N]` markers in the output. Check that the model does NOT fabricate facts when context is insufficient — it should explicitly state uncertainty rather than guessing. ### Step 5: Implement Source Validation and Conflict Resolution (Agentic RAG) For production-grade RAG, add an agentic layer that validates source quality, reconciles contradictions, and detects knowledge gaps. This step transforms a passive retrieval pipeline into an active reasoning system. ```python from dataclasses import dataclass from datetime import datetime @dataclass class ValidationResult: """Result of source validation by the agentic layer.""" is_authoritative: bool confidence: float # 0.0 to 1.0 notes: str conflicts_detected: list[str] def validate_sources_and_reconcile( retrieved_docs: list[object], query: str, ) -> list[object]: """Validate retrieved sources for authority and reconcile conflicts. This function acts as a reasoning gatekeeper that filters, prioritizes, and reconciles retrieved documents before they reach the LLM generation step. Args: retrieved_docs: List of Document objects from retrieval. query: The original user query for context. Returns: A filtered and prioritized list of validated document chunks. """ if not retrieved_docs: return [] # Strategy 1: Filter by metadata age — prefer recent documents authoritative_docs: list[object] = [] outdated_docs: list[object] = [] for doc in retrieved_docs: source_date = doc.metadata.get("date", None) if source_date: try: doc_dt = datetime.fromisoformat(source_date) days_old = (datetime.now() - doc_dt).days if days_old > 365: outdated_docs.append(doc) else: authoritative_docs.append(doc) except (ValueError, TypeError): authoritative_docs.append(doc) else: # No date metadata — include but flag for review authoritative_docs.append(doc) # Strategy 2: Detect and resolve contradictions within similar chunks conflicts: list[str] = [] if len(authoritative_docs) > 1: # Check for overlapping content with differing claims # In practice, use an LLM call to detect factual conflicts conflicts.append( "Cross-reference overlapping documents for factual consistency" ) # Strategy 3: Identify knowledge gaps — if top-k scores are low, signal external tool use top_score = retrieved_docs[0].metadata.get("score", 0.0) if retrieved_docs else 0.0 gap_detected = top_score < 0.5 if gap_detected: conflicts.append( "Low retrieval confidence — consider activating a web search tool" ) # Return authoritative documents, filtering out outdated ones result = authoritative_docs[:4] # Limit to top 4 validated chunks return result ``` **Checkpoint:** Confirm the agentic layer correctly filters out outdated documents when date metadata is present. Verify that low-confidence retrievals trigger a knowledge gap signal rather than passing stale or irrelevant context to the LLM. --- ## Implementation Patterns / Reference Guide ### Pattern 1: Google ADK + Vertex AI RAG Corpus (Managed Cloud) Use Google's Agent Development Kit with a managed RAG corpus for production deployments on GCP. This pattern handles indexing, embedding generation, and retrieval automatically. ```python from google.adk.memory import VertexAiRagMemoryService from google.adk.agents import Agent # Configuration constants RAG_CORPUS_RESOURCE_NAME = ( "projects/your-gcp-project-id/" "locations/us-central1/" "ragCorpora/your-corpus-id" ) # Maximum similar results to retrieve per query SIMILARITY_TOP_K: int = 5 # Maximum allowed semantic distance (0.0 = identical, 1.0 = unrelated) VECTOR_DISTANCE_THRESHOLD: float = 0.7 def build_vertex_rag_agent( agent_name: str = "research_assistant", model_id: str = "gemini-2.0-flash-exp", instruction: str = "", ) -> Agent: """Build an ADK agent with Vertex AI RAG memory service attached. Args: agent_name: Identifier for the agent. model_id: Google model to use for generation. instruction: System prompt for the agent. Returns: Configured Agent instance with integrated RAG retrieval. """ memory_service = VertexAiRagMemoryService( rag_corpus=RAG_CORPUS_RESOURCE_NAME, similarity_top_k=SIMILARITY_TOP_K, vector_distance_threshold=VECTOR_DISTANCE_THRESHOLD, ) agent = Agent( name=agent_name, model=model_id, instruction=instruction or "Answer using factual information from the knowledge base.", memory_service=memory_service, ) return agent ``` ### Pattern 2: Google Search as RAG (Quick Start) The simplest form of RAG — use a search tool to ground LLM outputs in real-time web results. Ideal for public-domain information that changes frequently. ```python from google.adk.tools import google_search from google.adk.agents import Agent def build_search_grounding_agent( agent_name: str = "web_researcher", model_id: str = "gemini-2.0-flash-exp", ) -> Agent: """Build an agent that grounds answers in live Google Search results. Args: agent_name: Identifier for the agent. model_id: The Google model to use.
在 GitHub 查看
这个 SKILL.md 很大,SkillsMP 这里只预览前一段内容。 在 GitHub 查看