Practical Examples

Three runnable tracks: Chroma over local Markdown, pgvector on an existing Postgres, and Qdrant filters with a hybrid teaser. Reuse embed_openai or embed_ollama from Embeddings (CPU is enough). Python 3.10+.

Use window_split from Chunking in real FAQs. Below, each short file is one chunk to keep the page focused.


Example 1: Chroma FAQ over Markdown

Goal: Q&A over faq/*.md. Answers must use made-up clauses in the files.

faq/refund.md:

# Refunds
Order KH-8842 must request a refund within 7 days of delivery. Late requests are denied.

faq/hours.md:

# Support hours
Weekdays 09:00–18:00 (Beijing time). Closed on weekends and public holidays.
from pathlib import Path
import chromadb

# from embeddings_util import embed_openai as embed
def embed(texts: list[str]) -> list[list[float]]:
    raise SystemExit("Plug in OpenAI or Ollama from the Embeddings chapter")

client = chromadb.PersistentClient(path="./chroma")
col = client.get_or_create_collection("markdown_faq")

ids, docs, metas = [], [], []
for path in Path("faq").glob("*.md"):
    text = path.read_text(encoding="utf-8")
    ids.append(path.stem + "-0")
    docs.append(text)
    metas.append({"source": path.name, "lang": "en"})
col.add(ids=ids, documents=docs, metadatas=metas, embeddings=embed(docs))

q = "When is the last day to refund KH-8842?"
hit = col.query(query_embeddings=embed([q]), n_results=2)
context = "\n\n".join(hit["documents"][0])
print(hit["ids"][0])
prompt = (
    "Answer only from context. If unknown, say you do not know.\n\n"
    f"{context}\n\nQuestion: {q}"
)

Check: Ask about KH-8842 / 7 days; then ask for a stock price (should refuse). Use hit_at_k from Evaluation on refund-0.


Example 2: pgvector on existing Postgres

Assume articles(id, title, body, org_id) already holds rows. Postgres basics: PostgreSQL.

CREATE EXTENSION IF NOT EXISTS vector;
ALTER TABLE articles ADD COLUMN IF NOT EXISTS embedding vector(1536);
CREATE INDEX IF NOT EXISTS articles_embedding_hnsw
  ON articles USING hnsw (embedding vector_cosine_ops);
import psycopg
from pgvector.psycopg import register_vector

conn = psycopg.connect("postgresql://postgres:secret@localhost:5432/app")
register_vector(conn)

rows = conn.execute(
    "SELECT id, body FROM articles WHERE embedding IS NULL LIMIT 64"
).fetchall()
for article_id, body in rows:
    vec = embed([body])[0]
    conn.execute(
        "UPDATE articles SET embedding = %s WHERE id = %s",
        (vec, article_id),
    )
conn.commit()

question = "How many days do I have to request a refund?"
qvec = embed([question])[0]
found = conn.execute(
    """
    SELECT id, body
    FROM articles
    WHERE org_id = 42 AND embedding IS NOT NULL
    ORDER BY embedding <=> %s
    LIMIT 4
    """,
    (qvec,),
).fetchall()

Bind org_id = 42 in the application to the current tenant—do not let the model choose it. Backfill overnight. After body changes, set embedding to NULL and re-embed.


Example 3: Qdrant filter + hybrid teaser

Start the server (Qdrant), then filter by department. Leave the sparse channel as a teaser until filter hit@k is solid.

from qdrant_client import QdrantClient
from qdrant_client.models import (
    Distance,
    FieldCondition,
    Filter,
    MatchValue,
    PointStruct,
    VectorParams,
)

client = QdrantClient(url="http://localhost:6333")
if client.collection_exists("faq"):
    client.delete_collection("faq")
client.create_collection(
    "faq",
    vectors_config=VectorParams(size=1536, distance=Distance.COSINE),
)

texts = [
    ("HR leave is 10 days per year (policy HR-10).", "hr"),
    ("Engineering on-call lives in oncall.md; it is not leave policy.", "eng"),
]
points = []
for i, (text, dept) in enumerate(texts, start=1):
    points.append(
        PointStruct(
            id=i,
            vector=embed([text])[0],
            payload={"dept": dept, "text": text},
        )
    )
client.upsert("faq", points=points, wait=True)

q = "How many leave days does HR get per year?"
hits = client.query_points(
    collection_name="faq",
    query=embed([q])[0],
    query_filter=Filter(must=[FieldCondition(key="dept", match=MatchValue(value="hr"))]),
    limit=3,
    with_payload=True,
).points
print([h.payload["text"] for h in hits])

Hybrid teaser: declare dense + sparse named vectors, then Prefetch + FusionQuery(RRF) (skeleton in the Qdrant chapter). Clause HR-10 often needs keywords; “annual leave” paraphrases need dense vectors. Compare “dense + dept filter” hit@k before adding sparse.


Acceptance

ExamplePasses when
1 ChromaMade-up order ids retrieve; off-topic questions refuse
2 pgvectorWHERE org_id does not leak tenants; backfill leaves embedding IS NULL at 0
3 QdrantFiltering hr hides the engineering on-call sentence

You can later hang the same chunks on LangChain RAG or Dify Knowledge Base. The principles do not change.


Next steps

评论