Local AI

Local RAG — build a private RAG pipeline on your own hardware

Build a complete Retrieval-Augmented Generation pipeline that runs entirely on your machine. Local embedding models, local vector databases, local LLMs. All private, all offline, no data egress. When you need production scale, migrate to Plugsky's managed RAG with zero code changes.

Local RAG stack

A local RAG pipeline has four components, all running on your machine:

┌──────────────┐     ┌──────────────┐     ┌──────────────┐
│  Documents    │ ──▶ │  Embedding    │ ──▶ │  Vector DB    │
│  (PDF, DOCX,  │     │  Model        │     │  (ChromaDB,    │
│   TXT, HTML)  │     │  (nomic, bge) │     │   Qdrant)      │
└──────────────┘     └──────────────┘     └──────────────┘
                                                    │
                                                    ▼
┌──────────────┐     ┌──────────────┐     ┌──────────────┐
│  Response     │ ◀── │  Local LLM   │ ◀── │  Retrieved   │
│  to user      │     │  (Ollama/     │     │  chunks      │
│               │     │   vLLM)       │     │  + query     │
└──────────────┘     └──────────────┘     └──────────────┘

Every component runs locally. No cloud services, no API calls to external providers, no data leaving your network.

Local embedding models

Embedding models convert text into vector representations. These run efficiently on CPU and are small enough to fit alongside your LLM:

ModelDimensionsContextBest forRAM use
nomic-embed-text7688192General purpose, best all-around~500 MB
bge-m310248192Multilingual, dense retrieval~2 GB
bge-small-en-v1.5384512Fast, low-resource~100 MB
mxbai-embed-large-v11024512High quality, English-focused~700 MB
all-MiniLM-L6-v2384256Lightest, CPU-friendly~80 MB

Running embeddings with Ollama

bash
# Pull an embedding model
ollama pull nomic-embed-text

# Test embedding
curl http://localhost:11434/api/embed \
  -d '{"model": "nomic-embed-text", "input": "Your text here"}'
python
from openai import OpenAI

client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")

def embed(texts):
    resp = client.embeddings.create(
        model="nomic-embed-text",
        input=texts if isinstance(texts, list) else [texts],
    )
    return [e.embedding for e in resp.data]

Local vector databases

Vector databases store and retrieve embeddings. These run locally with zero cloud dependency:

DatabaseModePersistenceBest for
ChromaDBIn-process / client-serverDisk (SQLite)Quick prototyping, small to medium collections
QdrantClient-server (Docker/binary)DiskProduction-like local testing, high performance
LanceDBEmbedded (Arrow)Disk (Lance format)Large collections, ML-native workflows
FAISS (manual)In-memoryNo (export/load)Maximum performance, static datasets

ChromaDB setup

bash
pip install chromadb
python
import chromadb

db = chromadb.PersistentClient(path="./rag-data")
collection = db.get_or_create_collection(
    name="knowledge-base",
    metadata={"hnsw:space": "cosine"},
)

# Add documents in batches
collection.add(
    ids=["doc-001", "doc-002"],
    embeddings=[emb1, emb2],
    documents=["Content of doc 1", "Content of doc 2"],
    metadatas=[{"source": "report.pdf"}, {"source": "email.txt"}],
)

# Query
results = collection.query(
    query_texts=["What is our policy on data retention?"],
    n_results=5,
)

Local LLM for generation

The generation step combines the user query with retrieved context and sends both to a local LLM. Use any engine running an OpenAI-compatible endpoint:

EngineRecommended models for RAGMin RAM
Ollamallama3.2, qwen2.5, mistral8 GB
vLLMLlama 3.2, Qwen 2.5, Mistral16 GB (GPU)
llama.cppAny GGUF model8 GB
LM StudioAny GGUF model (GUI)8 GB

Complete pipeline example

python
import chromadb
from openai import OpenAI

# ── 1. Connect to local embedding + LLM ──
client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")

# ── 2. Load or create vector store ──
db = chromadb.PersistentClient(path="./rag-data")
collection = db.get_or_create_collection("docs")

def index_documents(file_paths):
    """Chunk, embed, and store documents."""
    for path in file_paths:
        text = open(path).read()
        chunks = [text[i:i+1000] for i in range(0, len(text), 1000)]
        for idx, chunk in enumerate(chunks):
            resp = client.embeddings.create(
                model="nomic-embed-text",
                input=[chunk],
            )
            embedding = resp.data[0].embedding
            collection.add(
                ids=[f"{path}-{idx}"],
                embeddings=[embedding],
                documents=[chunk],
                metadatas=[{"source": path, "chunk": idx}],
            )

def ask(query, top_k=5):
    """Query the RAG pipeline."""
    # Embed the query
    q_resp = client.embeddings.create(
        model="nomic-embed-text",
        input=[query],
    )
    q_emb = q_resp.data[0].embedding

    # Retrieve
    results = collection.query(
        query_embeddings=[q_emb],
        n_results=top_k,
    )

    # Build context
    context = "\n---\n".join(results["documents"][0])

    # Generate answer
    response = client.chat.completions.create(
        model="llama3.2",
        messages=[
            {"role": "system", "content": f"Answer based on this context:\n\n{context}"},
            {"role": "user", "content": query},
        ],
    )
    return response.choices[0].message.content

# Usage
index_documents(["policy.pdf", "handbook.docx"])
answer = ask("What is our vacation policy?")
print(answer)

Local vs cloud RAG

FactorLocal RAG (DIY)Plugsky (managed RAG)
PrivacyComplete — data never leavesConfigurable (VPC/on-prem on Enterprise)
Setup timeHours to daysMinutes (upload and query)
Embedding models1-2 models (limited by RAM)Multiple models, auto-optimized
Vector DB maintenanceManual (ChromaDB, Qdrant)Fully managed
Chunking strategyMust implement and tuneAuto-chunking with configurable strategy
Re-rankingMust implement separatelyBuilt-in cross-encoder re-ranking
Hybrid searchMust implement (BM25 + vector)Built-in hybrid search
ScalingSingle machineAuto-scaling to millions of documents
CostHardware + electricityFlat monthly ($7-$249)

When to stay local

Local RAG is the right choice when you need complete data sovereignty, are prototyping and iterating rapidly, have a small document set (under 10,000 pages), or are building a single-user application. It's also ideal for air-gapped environments where no cloud connectivity is permitted.

Local RAG becomes impractical when you need to share the knowledge base across a team of 10+ users, need 99.9% uptime, have document collections exceeding 100,000 pages, or need multi-language hybrid search with re-ranking.

Migration to Plugsky

Plugsky's managed RAG API handles chunking, embedding, indexing, retrieval, and re-ranking automatically. Migration is straightforward:

python
# Before (local)
# Manual pipeline with ChromaDB + Ollama embedding + local LLM

# After (Plugsky)
from openai import OpenAI

client = OpenAI(
    base_url="https://api.plugsky.com/v1",
    api_key="sk-live-...",
)

# Upload documents — Plugsky handles chunking + embedding
client.files.create(file=open("policy.pdf", "rb"), purpose="assistants")

# Query — built-in retrieval + re-ranking
response = client.chat.completions.create(
    model="plugsky-pro",
    messages=[
        {"role": "system", "content": "Answer from your knowledge base."},
        {"role": "user", "content": "What is our vacation policy?"},
    ],
)

# No vector DB to manage. No embedding pipeline. No chunking config.

Build local RAG, deploy with Plugsky

Prototype your RAG pipeline locally with complete privacy. Migrate to Plugsky's managed RAG when you need team access, scaling, and enterprise features — no code changes required.

Start Free → Back to Local AI →