agentsclimarketplace

Vector databases

Skill furkangonel/cowrangler/bundled_skills/mlops/vector-databases

Autonomous terminal AI agent for workflows and feasible project procedures. Co-Worker Co-Wrangler πŸ™

Install
npx -y skills add furkangonel/cowrangler --skill vector-databases

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Vector database operations β€” embed, store, search, and build RAG pipelines.

SKILL.md

12.7 KB, as published. Nobody here has run it

Vector Databases SOP

Embed documents, store vectors, run similarity search, and build RAG retrieval pipelines using ChromaDB, pgvector, and popular embedding models.

When to Use

  • User wants to build semantic search over a document corpus
  • User wants to implement a RAG (Retrieval-Augmented Generation) pipeline
  • User wants to store and query embeddings efficiently
  • User wants to find similar items by meaning, not keyword
  • User wants to choose between embedding models and vector stores

Part 1 β€” Embedding Model Selection

ModelProviderDimsBest ForCost
text-embedding-3-smallOpenAI1536General purpose, fast~$0.02/1M tokens
text-embedding-3-largeOpenAI3072Higher accuracy~$0.13/1M tokens
all-MiniLM-L6-v2sentence-transformers384Local, fast, EnglishFree
all-mpnet-base-v2sentence-transformers768Local, balanced qualityFree
BAAI/bge-m3HuggingFace1024Multilingual, localFree
nomic-embed-textOllama768Local, good qualityFree
mxbai-embed-largeOllama1024Local, high qualityFree

OpenAI Embeddings

import os
from openai import OpenAI

client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])

def embed_openai(texts: list[str], model="text-embedding-3-small") -> list[list[float]]:
    """Embed a batch of texts. Max 2048 inputs per call."""
    # Replace newlines β€” they degrade embedding quality
    texts = [t.replace("\n", " ") for t in texts]
    response = client.embeddings.create(input=texts, model=model)
    return [item.embedding for item in response.data]

# Single text
vec = embed_openai(["Hello world"])[0]
print(f"Dimension: {len(vec)}")

Sentence-Transformers (local)

# pip install sentence-transformers
from sentence_transformers import SentenceTransformer

model = SentenceTransformer("all-MiniLM-L6-v2")

def embed_local(texts: list[str]) -> list[list[float]]:
    embeddings = model.encode(texts, batch_size=64, show_progress_bar=True, normalize_embeddings=True)
    return embeddings.tolist()

vecs = embed_local(["Semantic search is powerful", "Vector databases store embeddings"])
print(f"Shape: {len(vecs)} x {len(vecs[0])}")

Ollama Embeddings (local server)

import requests

def embed_ollama(texts: list[str], model="nomic-embed-text") -> list[list[float]]:
    """Requires: ollama serve && ollama pull nomic-embed-text"""
    vecs = []
    for text in texts:
        r = requests.post("http://localhost:11434/api/embeddings",
                          json={"model": model, "prompt": text})
        r.raise_for_status()
        vecs.append(r.json()["embedding"])
    return vecs

Part 2 β€” Chunking Strategies

Chunking quality directly impacts retrieval quality. Poor chunks = poor answers.

from typing import Generator

def chunk_fixed(text: str, chunk_size: int = 512, overlap: int = 64) -> list[str]:
    """Fixed-size character chunking with overlap."""
    chunks = []
    start  = 0
    while start < len(text):
        end = min(start + chunk_size, len(text))
        chunks.append(text[start:end])
        start += chunk_size - overlap
    return chunks

def chunk_by_paragraph(text: str, max_chars: int = 800) -> list[str]:
    """Split on double newlines, merge short paragraphs."""
    paragraphs = [p.strip() for p in text.split("\n\n") if p.strip()]
    chunks, current = [], ""
    for para in paragraphs:
        if len(current) + len(para) > max_chars and current:
            chunks.append(current.strip())
            current = para
        else:
            current = current + "\n\n" + para if current else para
    if current:
        chunks.append(current.strip())
    return chunks

def chunk_by_sentence(text: str, max_sentences: int = 5) -> list[str]:
    """Simple sentence-based chunking."""
    import re
    sentences = re.split(r'(?<=[.!?])\s+', text)
    chunks = []
    for i in range(0, len(sentences), max_sentences):
        chunks.append(" ".join(sentences[i:i + max_sentences]))
    return chunks

Chunking guidance:

  • Fixed-size with overlap: Safe default; use for mixed content.
  • Paragraph-based: Best for structured documents (articles, reports, documentation).
  • Sentence-based: Best for FAQ-style or Q&A retrieval.
  • For code: chunk by function/class, not arbitrary size.
  • Ideal chunk size: 256–512 tokens for retrieval; 512–1024 for dense context.
  • Always include document metadata (source, title, page) in chunk metadata.

Part 3 β€” ChromaDB

ChromaDB is a lightweight, embedded vector store β€” no external server required for local use.

Install and Setup

pip install chromadb

Create a Collection and Add Documents

import chromadb
from chromadb.utils import embedding_functions

# Embedded (local file) β€” persists to disk
client = chromadb.PersistentClient(path="./chroma_db")

# Use OpenAI embeddings (or swap for sentence-transformers)
ef = embedding_functions.OpenAIEmbeddingFunction(
    api_key=os.environ["OPENAI_API_KEY"],
    model_name="text-embedding-3-small",
)

collection = client.get_or_create_collection(
    name="documents",
    embedding_function=ef,
    metadata={"hnsw:space": "cosine"},   # cosine similarity
)

# Add documents
docs = [
    "ChromaDB is an open-source vector database.",
    "Semantic search finds results by meaning, not keywords.",
    "RAG combines retrieval with language model generation.",
]
metas = [
    {"source": "intro.md", "section": "overview"},
    {"source": "search.md", "section": "concepts"},
    {"source": "rag.md", "section": "architecture"},
]
ids = [f"doc_{i}" for i in range(len(docs))]

collection.add(documents=docs, metadatas=metas, ids=ids)
print(f"Collection count: {collection.count()}")

Query

results = collection.query(
    query_texts=["how does vector search work?"],
    n_results=3,
    include=["documents", "metadatas", "distances"],
)

for i, (doc, meta, dist) in enumerate(zip(
    results["documents"][0],
    results["metadatas"][0],
    results["distances"][0],
)):
    print(f"[{i+1}] distance={dist:.4f}  source={meta['source']}")
    print(f"     {doc[:120]}")

Update and Delete

# Update a document
collection.update(ids=["doc_0"], documents=["Updated content for doc_0."])

# Delete
collection.delete(ids=["doc_2"])

# Filter by metadata during query
results = collection.query(
    query_texts=["vector databases"],
    n_results=2,
    where={"source": "intro.md"},   # metadata filter
)

Part 4 β€” pgvector (PostgreSQL)

pgvector stores vectors as a PostgreSQL column type β€” ideal for production when you already use Postgres.

Setup

# Enable extension (run once)
psql -d mydb -c "CREATE EXTENSION IF NOT EXISTS vector;"

pip install pgvector psycopg2-binary sqlalchemy

Schema and Insert

import os
import psycopg2
from pgvector.psycopg2 import register_vector

conn = psycopg2.connect(os.environ["DATABASE_URL"])
register_vector(conn)
cur  = conn.cursor()

# Create table
cur.execute("""
    CREATE TABLE IF NOT EXISTS documents (
        id        BIGSERIAL PRIMARY KEY,
        content   TEXT NOT NULL,
        source    TEXT,
        embedding vector(1536)   -- match your embedding model dimension
    );
""")

# Create index for fast ANN search (IVFFlat)
cur.execute("""
    CREATE INDEX IF NOT EXISTS documents_embedding_idx
    ON documents USING ivfflat (embedding vector_cosine_ops)
    WITH (lists = 100);   -- lists β‰ˆ sqrt(n_rows)
""")
conn.commit()

# Insert with embedding
def insert_document(content: str, source: str, embedding: list[float]):
    cur.execute(
        "INSERT INTO documents (content, source, embedding) VALUES (%s, %s, %s)",
        (content, source, embedding)
    )
    conn.commit()

Similarity Search

def search_similar(query_embedding: list[float], limit: int = 5) -> list[dict]:
    cur.execute("""
        SELECT id, content, source,
               1 - (embedding <=> %s::vector) AS similarity
        FROM documents
        ORDER BY embedding <=> %s::vector
        LIMIT %s;
    """, (query_embedding, query_embedding, limit))
    rows = cur.fetchall()
    return [{"id": r[0], "content": r[1], "source": r[2], "similarity": r[3]} for r in rows]

query_vec = embed_openai(["What is semantic search?"])[0]
results   = search_similar(query_vec, limit=3)
for r in results:
    print(f"similarity={r['similarity']:.4f}  {r['content'][:100]}")

Part 5 β€” RAG Retrieval Pipeline

import os
from openai import OpenAI

openai_client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])

def rag_answer(question: str, collection, n_docs: int = 4, model: str = "gpt-4o-mini") -> str:
    """
    1. Embed the question
    2. Retrieve top-n chunks from vector store
    3. Build prompt with retrieved context
    4. Generate answer with LLM
    """

    # Step 1: Retrieve
    results = collection.query(
        query_texts=[question],
        n_results=n_docs,
        include=["documents", "metadatas", "distances"],
    )
    chunks = results["documents"][0]
    metas  = results["metadatas"][0]
    dists  = results["distances"][0]

    # Filter low-relevance chunks (cosine distance > 0.4 = less than 60% similarity)
    relevant = [(c, m) for c, m, d in zip(chunks, metas, dists) if d < 0.4]
    if not relevant:
        return "I could not find relevant information to answer this question."

    context_parts = []
    for i, (chunk, meta) in enumerate(relevant):
        src = meta.get("source", "unknown")
        context_parts.append(f"[{i+1}] (source: {src})\n{chunk}")
    context = "\n\n".join(context_parts)

    # Step 2: Generate
    messages = [
        {
            "role": "system",
            "content": (
                "You are a helpful assistant. Answer questions using ONLY the provided context. "
                "If the answer is not in the context, say so. "
                "Cite the source number (e.g. [1]) for each claim."
            ),
        },
        {
            "role": "user",
            "content": f"Context:\n{context}\n\nQuestion: {question}",
        },
    ]

    response = openai_client.chat.completions.create(
        model=model,
        messages=messages,
        temperature=0.2,
    )
    return response.choices[0].message.content

# Usage
answer = rag_answer("What is ChromaDB used for?", collection)
print(answer)

Part 6 β€” Indexing Pipeline Template

#!/usr/bin/env python3
"""index_documents.py β€” read files β†’ chunk β†’ embed β†’ store in ChromaDB"""
import os, glob, chromadb
from chromadb.utils import embedding_functions

SOURCE_DIR = "./docs"
CHROMA_DIR = "./chroma_db"
CHUNK_SIZE = 512
CHUNK_OVERLAP = 64

ef = embedding_functions.OpenAIEmbeddingFunction(
    api_key=os.environ["OPENAI_API_KEY"],
    model_name="text-embedding-3-small",
)
client     = chromadb.PersistentClient(path=CHROMA_DIR)
collection = client.get_or_create_collection("documents", embedding_function=ef,
                                             metadata={"hnsw:space": "cosine"})

# Avoid re-indexing existing docs
existing_ids = set(collection.get()["ids"])
docs, metas, ids = [], [], []

for filepath in glob.glob(f"{SOURCE_DIR}/**/*.md", recursive=True):
    with open(filepath, encoding="utf-8") as f:
        text = f.read()
    chunks = chunk_fixed(text, CHUNK_SIZE, CHUNK_OVERLAP)
    for i, chunk in enumerate(chunks):
        doc_id = f"{filepath}::{i}"
        if doc_id in existing_ids:
            continue
        docs.append(chunk)
        metas.append({"source": filepath, "chunk": i})
        ids.append(doc_id)

# Batch insert (100 at a time to respect API limits)
BATCH = 100
for i in range(0, len(docs), BATCH):
    collection.add(documents=docs[i:i+BATCH], metadatas=metas[i:i+BATCH], ids=ids[i:i+BATCH])
    print(f"Indexed {min(i+BATCH, len(docs))} / {len(docs)}")

print(f"Done. Collection size: {collection.count()}")

Checklist

  • Embedding model chosen based on accuracy/cost/local tradeoff
  • Embedding dimension matches vector store collection configuration
  • Chunking strategy matches document structure (paragraph/sentence/fixed)
  • Chunk metadata includes source file path and chunk index for traceability
  • Already-indexed documents skipped on re-run (idempotent indexing)
  • Retrieval filters out low-similarity results before passing to LLM
  • pgvector: IVFFlat or HNSW index created after initial data load (not before)
  • RAG prompt instructs LLM to cite sources and acknowledge missing info

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.