Skip to content
Back to skills

2702 Rag Pipeline Gen De6f607a

ASecurity

Generate complete RAG pipeline with embeddings, vector DB, and retrieval

  • 9 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added October 11, 2026
devopspythongofastapidockerawstestingapidatabase

Works with

  • cli
  • api

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro shows the line behind each finding and how to fix it

Scanned October 11, 2026

npx -y skills add tools-only/X-Skills --skill 2702-rag-pipeline-gen_de6f607a --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of 2702 Rag Pipeline Gen De6f607a?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for 2702 Rag Pipeline Gen De6f607a
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tools-only-2702-rag-pipeline-gen-de6f607a/badge)](https://www.skillsdirectory.com/skills/tools-only-2702-rag-pipeline-gen-de6f607a)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

SKILL.md
---
name: rag-pipeline-gen
description: >
  Generate complete RAG pipeline with embeddings, vector DB, and retrieval
shortcut: rpg
category: other
type: command
version: 1.0.0
author: Jeremy Longshore
estimated_time: 5-10 minutes
---
# RAG Pipeline Generator

Generate a complete, production-ready RAG (Retrieval-Augmented Generation) pipeline with document ingestion, embedding, vector storage, retrieval, and LLM integration.

## What You'll Get

When you run this command, you'll receive:

1. **Document ingestion pipeline** with chunking strategies
2. **Embedding generation** with OpenAI or open-source models
3. **Vector database integration** (Pinecone, Qdrant, or ChromaDB)
4. **Retrieval system** with reranking and hybrid search
5. **LLM integration** for answer generation
6. **API server** (FastAPI) for production deployment
7. **Docker configuration** for containerized deployment
8. **Testing suite** with evaluation metrics

## Usage

```
/rag-pipeline-gen <vector_db> [options]
```

**Vector Databases:** `pinecone`, `qdrant`, `chromadb`, `weaviate`

**Examples:**
- `/rpg pinecone` - Generate RAG pipeline with Pinecone
- `/rpg qdrant` - Generate RAG pipeline with Qdrant Cloud
- `/rpg chromadb` - Generate RAG pipeline with ChromaDB (local development)

## Generated Output

### Example: Pinecone RAG Pipeline

**Input:**
```
/rpg pinecone
```

**Output:**

#### 1. Project Structure

```
rag-pipeline/
├── src/
│   ├── ingestion/
│   │   ├── __init__.py
│   │   ├── document_loader.py    # Load PDFs, text, web pages
│   │   ├── chunker.py             # Text chunking strategies
│   │   └── embedder.py            # Generate embeddings
│   ├── retrieval/
│   │   ├── __init__.py
│   │   ├── vector_store.py        # Vector DB operations
│   │   ├── retriever.py           # Query and retrieval
│   │   └── reranker.py            # Reranking results
│   ├── generation/
│   │   ├── __init__.py
│   │   ├── llm_client.py          # LLM integration
│   │   └── prompt_templates.py    # Prompt engineering
│   ├── api/
│   │   ├── __init__.py
│   │   ├── main.py                # FastAPI server
│   │   └── models.py              # Pydantic models
│   └── config/
│       ├── __init__.py
│       └── settings.py            # Configuration
├── tests/
│   ├── __init__.py
│   ├── test_ingestion.py
│   ├── test_retrieval.py
│   └── test_integration.py
├── notebooks/
│   └── evaluation.ipynb           # RAG evaluation
├── docker/
│   ├── Dockerfile
│   └── docker-compose.yml
├── requirements.txt
├── .env.example
└── README.md
```

#### 2. Document Loader (src/ingestion/document_loader.py)

```python
from pathlib import Path
from typing import List, Dict
import PyPDF2
from langchain.text_splitter import RecursiveCharacterTextSplitter

class DocumentLoader:
    """Load and process documents from various sources."""

    def __init__(self, chunk_size: int = 1000, chunk_overlap: int = 200):
        self.chunker = RecursiveCharacterTextSplitter(
            chunk_size=chunk_size,
            chunk_overlap=chunk_overlap,
            separators=["\n\n", "\n", ". ", " ", ""]
        )

    def load_pdf(self, file_path: Path) -> List[Dict]:
        """Load and chunk PDF file."""
        with open(file_path, 'rb') as file:
            pdf_reader = PyPDF2.PdfReader(file)
            text = ""
            for page_num, page in enumerate(pdf_reader.pages):
                text += page.extract_text()

        # Chunk text
        chunks = self.chunker.split_text(text)

        # Create document objects
        documents = [
            {
                "text": chunk,
                "metadata": {
                    "source": str(file_path),
                    "page": i // (len(chunks) // len(pdf_reader.pages) + 1),
                    "chunk_index": i
                }
            }
            for i, chunk in enumerate(chunks)
        ]

        return documents

    def load_text(self, file_path: Path) -> List[Dict]:
        """Load and chunk text file."""
        with open(file_path, 'r', encoding='utf-8') as file:
            text = file.read()

        chunks = self.chunker.split_text(text)

        return [
            {
                "text": chunk,
                "metadata": {
                    "source": str(file_path),
                    "chunk_index": i
                }
            }
            for i, chunk in enumerate(chunks)
        ]

    def load_directory(self, directory: Path) -> List[Dict]:
        """Load all documents from directory."""
        documents = []

        for file_path in directory.glob("**/*"):
            if file_path.suffix == ".pdf":
                documents.extend(self.load_pdf(file_path))
            elif file_path.suffix in [".txt", ".md"]:
                documents.extend(self.load_text(file_path))

        return documents
```

#### 3. Embedder (src/ingestion/embedder.py)

```python
from typing import List
import openai
from tenacity import retry, stop_after_attempt, wait_exponential

class Embedder:
    """Generate embeddings for text chunks."""

    def __init__(
        self,
        model: str = "text-embedding-3-small",
        api_key: str = None
    ):
        self.model = model
        self.client = openai.OpenAI(api_key=api_key)

    @retry(
        stop=stop_after_attempt(3),
        wait=wait_exponential(multiplier=1, min=2, max=10)
    )
    async def embed(self, text: str) -> List[float]:
        """Generate embedding for single text."""
        response = await self.client.embeddings.create(
            model=self.model,
            input=text
        )
        return response.data[0].embedding

    async def embed_batch(
        self,
        texts: List[str],
        batch_size: int = 100
    ) -> List[List[float]]:
        """Generate embeddings for batch of texts."""
        embeddings = []

        for i in range(0, len(texts), batch_size):
            batch = texts[i:i + batch_size]

            response = await self.client.embeddings.create(
                model=self.model,
                input=batch
            )

            batch_embeddings = [item.embedding for item in response.data]
            embeddings.extend(batch_embeddings)

        return embeddings
```

#### 4. Vector Store (src/retrieval/vector_store.py)

```python
from typing import List, Dict, Optional
from pinecone import Pinecone, ServerlessSpec
import hashlib

class PineconeVectorStore:
    """Pinecone vector database operations."""

    def __init__(
        self,
        api_key: str,
        index_name: str,
        dimension: int = 1536,
        metric: str = "cosine"
    ):
        self.pc = Pinecone(api_key=api_key)
        self.index_name = index_name

        # Create index if doesn't exist
        if index_name not in self.pc.list_indexes().names():
            self.pc.create_index(
                name=index_name,
                dimension=dimension,
                metric=metric,
                spec=ServerlessSpec(cloud="aws", region="us-east-1")
            )

        self.index = self.pc.Index(index_name)

    async def upsert_documents(
        self,
        documents: List[Dict],
        embeddings: List[List[float]],
        namespace: str = "default"
    ):
        """Insert documents with embeddings into vector store."""
        vectors = []

        for doc, embedding in zip(documents, embeddings):
            # Generate unique ID
            doc_id = self._generate_id(doc)

            vectors.append({
                "id": doc_id,
                "values": embedding,
                "metadata": {
                    "text": doc["text"],
                    **doc["metadata"]
                }
            })

        # Batch upsert
        batch_size = 100
        for i in range(0, len(vectors), batch_size):
            batch = vectors[i:i + batch_size]
            self.index.upsert(vectors=batch, namespace=namespace)

        return len(vectors)

    async def search(
        self,
        query_embedding: List[float],
        top_k: int = 5,
        namespace: str = "default",
        filter: Optional[Dict] = None
    ) -> List[Dict]:
        """Search for similar documents."""
        results = self.index.query(
            vector=query_embedding,
            top_k=top_k,
            namespace=namespace,
            filter=filter,
            include_metadata=True
        )

        return [
            {
                "id": match["id"],
                "score": match["score"],
                "text": match["metadata"]["text"],
                "metadata": {
                    k: v for k, v in match["metadata"].items()
                    if k != "text"
                }
            }
            for match in results["matches"]
        ]

    def _generate_id(self, document: Dict) -> str:
        """Generate unique ID from document content."""
        content = f"{document['text']}{document['metadata']}"
        return hashlib.md5(content.encode()).hexdigest()

    async def delete_namespace(self, namespace: str):
        """Delete all vectors in namespace."""
        self.index.delete(namespace=namespace, delete_all=True)
```

#### 5. Retriever with Reranking (src/retrieval/retriever.py)

```python
from typing import List, Dict
import cohere
from src.ingestion.embedder import Embedder
from src.retrieval.vector_store import PineconeVectorStore

class RAGRetriever:
    """Retrieve and rerank documents for RAG."""

    def __init__(
        self,
        embedder: Embedder,
        vector_store: PineconeVectorStore,
        cohere_api_key: Optional[str] = None
    ):
        self.embedder = embedder
        self.vector_store = vector_store
        self.cohere = cohere.Client(cohere_api_key) if cohere_api_key else None

    async def retrieve(
        self,
        query: str,
        top_k: int = 5,
        initial_k: int = 20,
        rerank: bool = True,
        namespace: str = "default",
        filter: Optional[Dict] = None
    ) -> List[Dict]:
        """Retrieve relevant documents with optional reranking."""

        # 1. Embed query
        query_embedding = await self.embedder.embed(query)

        # 2. Vector search
        k = initial_k if rerank else top_k
        results = await self.vector_store.search(
            query_embedding=query_embedding,
            top_k=k,
            namespace=namespace,
            filter=filter
        )

        # 3. Rerank if enabled
        if rerank and self.cohere and len(results) > top_k:
            documents = [r["text"] for r in results]

            reranked = self.cohere.rerank(
                query=query,
                documents=documents,
                top_n=top_k,
                model="rerank-english-v2.0"
            )

            # Reorder results based on reranking
            results = [results[r.index] for r in reranked.results]

        return results[:top_k]

    async def hybrid_retrieve(
        self,
        query: str,
        top_k: int = 5,
        namespace: str = "default"
    ) -> List[Dict]:
        """Retrieve using multiple strategies and combine."""

        # Strategy 1: Standard retrieval
        results_standard = await self.retrieve(
            query=query,
            top_k=top_k,
            rerank=False,
            namespace=namespace
        )

        # Strategy 2: Multi-query retrieval
        query_variants = await self._generate_query_variants(query)
        results_variants = []

        for variant in query_variants[:2]:  # Use 2 variants
            variant_results = await self.retrieve(
                query=variant,
                top_k=top_k,
                rerank=False,
                namespace=namespace
            )
            results_variants.extend(variant_results)

        # Combine and deduplicate
        seen_ids = set()
        combined_results = []

        for result in results_standard + results_variants:
            if result["id"] not in seen_ids:
                seen_ids.add(result["id"])
                combined_results.append(result)

        # Sort by score and return top-k
        combined_results.sort(key=lambda x: x["score"], reverse=True)
        return combined_results[:top_k]

    async def _generate_query_variants(self, query: str) -> List[str]:
        """Generate alternative phrasings of query."""
        # Use LLM to generate variants (simplified here)
        return [query]  # Placeholder
```

#### 6. LLM Client (src/generation/llm_client.py)

```python
from typing import List, Dict
from anthropic import AsyncAnthropic

class LLMClient:
    """Generate answers using LLM."""

    def __init__(self, api_key: str, model: str = "claude-3-haiku-20240307"):
        self.client = AsyncAnthropic(api_key=api_key)
        self.model = model

    async def generate_answer(
        self,
        question: str,
        context: List[Dict],
        max_tokens: int = 1024
    ) -> Dict:
        """Generate answer using retrieved context."""

        # Format context
        context_text = "\n\n".join([
            f"Source {i+1} ({ctx['metadata'].get('source', 'Unknown')}):\n{ctx['text']}"
            for i, ctx in enumerate(context)
        ])

        # Build prompt
        prompt = f"""Answer the question using ONLY the provided context. If the answer cannot be found in the context, say "I don't have enough information to answer this question."

Context:
{context_text}

Question: {question}

Answer:"""

        # Generate response
        message = await self.client.messages.create(
            model=self.model,
            max_tokens=max_tokens,
            messages=[{"role": "user", "content": prompt}]
        )

        return {
            "answer": message.content[0].text,
            "sources": [
                {
                    "source": ctx["metadata"].get("source", "Unknown"),
                    "score": ctx["score"]
                }
                for ctx in context
            ],
            "usage": {
                "input_tokens": message.usage.input_tokens,
                "output_tokens": message.usage.output_tokens
            }
        }
```

#### 7. FastAPI Server (src/api/main.py)

```python
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import List, Optional
import asyncio

from src.ingestion.document_loader import DocumentLoader
from src.ingestion.embedder import Embedder
from src.retrieval.vector_store import PineconeVectorStore
from src.retrieval.retriever import RAGRetriever
from src.generation.llm_client import LLMClient
from src.config.settings import Settings

app = FastAPI(title="RAG API", version="1.0.0")
settings = Settings()

# Initialize components
embedder = Embedder(api_key=settings.openai_api_key)
vector_store = PineconeVectorStore(
    api_key=settings.pinecone_api_key,
    index_name=settings.pinecone_index_name
)
retriever = RAGRetriever(
    embedder=embedder,
    vector_store=vector_store,
    cohere_api_key=settings.cohere_api_key
)
llm = LLMClient(api_key=settings.anthropic_api_key)

class QueryRequest(BaseModel):
    question: str
    top_k: int = 5
    rerank: bool = True
    namespace: str = "default"

class QueryResponse(BaseModel):
    answer: str
    sources: List[Dict]
    usage: Dict

@app.post("/query", response_model=QueryResponse)
async def query(request: QueryRequest):
    """Query RAG system."""
    try:
        # Retrieve relevant documents
        context = await retriever.retrieve(
            query=request.question,
            top_k=request.top_k,
            rerank=request.rerank,
            namespace=request.namespace
        )

        # Generate answer
        result = await llm.generate_answer(
            question=request.question,
            context=context
        )

        return QueryResponse(**result)

    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

class IngestRequest(BaseModel):
    directory: str
    namespace: str = "default"

@app.post("/ingest")
async def ingest(request: IngestRequest):
    """Ingest documents from directory."""
    try:
        # Load documents
        loader = DocumentLoader()
        documents = loader.load_directory(Path(request.directory))

        # Generate embeddings
        texts = [doc["text"] for doc in documents]
        embeddings = await embedder.embed_batch(texts)

        # Upload to vector store
        num_uploaded = await vector_store.upsert_documents(
            documents=documents,
            embeddings=embeddings,
            namespace=request.namespace
        )

        return {
            "status": "success",
            "documents_ingested": num_uploaded
        }

    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@app.get("/health")
async def health():
    """Health check endpoint."""
    return {"status": "healthy"}

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)
```

#### 8. Docker Configuration (docker/Dockerfile)

```dockerfile
FROM python:3.11-slim

WORKDIR /app

# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy application
COPY src/ ./src/

# Environment variables
ENV OPENAI_API_KEY=""
ENV PINECONE_API_KEY=""
ENV ANTHROPIC_API_KEY=""
ENV COHERE_API_KEY=""

# Expose port
EXPOSE 8000

# Run application
CMD ["uvicorn", "src.api.main:app", "--host", "0.0.0.0", "--port", "8000"]
```

#### 9. Docker Compose (docker/docker-compose.yml)

```yaml
version: '3.8'

services:
  rag-api:
    build:
      context: .
      dockerfile: docker/Dockerfile
    environment:
      - OPENAI_API_KEY=${OPENAI_API_KEY}
      - PINECONE_API_KEY=${PINECONE_API_KEY}
      - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
      - COHERE_API_KEY=${COHERE_API_KEY}
      - PINECONE_INDEX_NAME=rag-index
    ports:
      - "8000:8000"
    volumes:
      - ./data:/app/data  # Mount for document ingestion
```

#### 10. Requirements (requirements.txt)

```
fastapi==0.109.0
uvicorn[standard]==0.27.0
anthropic==0.18.1
openai==1.12.0
pinecone-client==3.0.0
cohere==4.47
PyPDF2==3.0.1
langchain==0.1.6
pydantic==2.6.0
pydantic-settings==2.1.0
tenacity==8.2.3
python-multipart==0.0.9
```

#### 11. Example Usage

```python
import requests

# Query RAG system
response = requests.post(
    "http://localhost:8000/query",
    json={
        "question": "What is quantum computing?",
        "top_k": 5,
        "rerank": True
    }
)

result = response.json()
print(f"Answer: {result['answer']}")
print(f"Sources: {result['sources']}")
print(f"Tokens: {result['usage']}")
```

**Response:**
```json
{
  "answer": "Quantum computing is a type of computing that uses quantum-mechanical phenomena...",
  "sources": [
    {"source": "quantum_physics.pdf", "score": 0.92},
    {"source": "computing_basics.pdf", "score": 0.87}
  ],
  "usage": {
    "input_tokens": 450,
    "output_tokens": 120
  }
}
```

## Features Included

**Production-Ready:**
-  Document ingestion (PDF, text, web)
-  Intelligent chunking strategies
-  Batch embedding generation
-  Vector database integration
-  Retrieval with reranking
-  LLM answer generation
-  FastAPI REST API
-  Docker deployment
-  Error handling and retries
-  Source citation

**Advanced Features:**
-  Hybrid search (multiple strategies)
-  Metadata filtering
-  Namespace support (multi-tenancy)
-  Reranking for better relevance
-  Token usage tracking
-  Health check endpoints

## Time Savings

**Manual implementation:** 16-24 hours
- Document loading and chunking
- Embedding generation
- Vector DB setup and integration
- Retrieval logic
- LLM integration
- API server
- Docker configuration
- Testing

**With this command:** 5-10 minutes
- Run command
- Add API keys
- Deploy to production

**ROI:** 96-144x time multiplier

---

**Next Steps:**
1. Run `/rpg pinecone` or `/rpg qdrant` or `/rpg chromadb`
2. Copy generated code to your project
3. Install dependencies: `pip install -r requirements.txt`
4. Set API keys in `.env` file
5. Ingest documents: `POST /ingest`
6. Query system: `POST /query`
7. Deploy: `docker-compose up -d`

**Production checklist:**
- [ ] Set up monitoring (Prometheus, Grafana)
- [ ] Implement rate limiting
- [ ] Add authentication (JWT)
- [ ] Configure logging (structured logs)
- [ ] Set up alerting (Sentry, PagerDuty)
- [ ] Run evaluation (RAGAS metrics)
- [ ] Load testing (Locust, k6)

**Estimated monthly cost:** $50-$200 depending on document volume and query rate.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.