Pranav Srivastava
Labs
45 minIntermediate#OKF#RAG#Vector Search

Turn a folder into an OKF bundle, then embed it

Build a tiny Open Knowledge Format bundle (markdown + YAML), then embed it so an AI can find the right file by meaning and answer grounded in its exact contents — the folder-plus-vector architecture, hands-on.

What you build

An OKF folder you built, embedded into a vector index, answering questions by finding the right file and grounding its answer in that file — with a citation.

Stack

Python 3.10+ · sentence-transformers · Anthropic API


By the end of this lab you will have built a small Open Knowledge Format (OKF) bundle — knowledge as plain markdown files — and then embedded it, so an AI can find the right file by meaning and answer grounded in its exact contents. That combination — vectors to find, the folder to ground — is the architecture the Folders vs Vectors course builds toward. Here you build it.

Before you start

Python 3.10+, an Anthropic API key (export ANTHROPIC_API_KEY=...), about 45 minutes.

terminal
uv init okf-lab && cd okf-lab
uv add anthropic sentence-transformers numpy pyyaml
# or: pip install anthropic sentence-transformers numpy pyyaml

Build the OKF bundle

An OKF bundle is just a folder of markdown files, each with a little YAML frontmatter (the only required field is type). This script writes a tiny one. Create and run build_bundle.py:

build_bundle.py
import os

FILES = {
    "knowledge/tables/orders.md": """---
type: BigQuery Table
title: Orders
description: One row per completed customer order.
tags: [sales, revenue]
---
# Schema
- order_id (STRING): globally unique order id
- customer_id (STRING): foreign key to [customers](customers.md)
- amount_eur (FLOAT): order total, in euros

# Joins
Joined with [customers](customers.md) on customer_id.
""",
    "knowledge/tables/customers.md": """---
type: BigQuery Table
title: Customers
description: One row per customer.
tags: [sales]
---
# Schema
- customer_id (STRING): globally unique id
- country (STRING): ISO country code
- created_at (DATE): sign-up date
""",
    "knowledge/metrics/weekly_active_users.md": """---
type: Metric
title: Weekly Active Users
description: Distinct customers with an order in the last 7 days.
tags: [metric, engagement]
---
# Definition
COUNT(DISTINCT customer_id) from orders where the order date is
within the last 7 days. This is order-based activity, not app opens.
""",
}

for path, content in FILES.items():
    os.makedirs(os.path.dirname(path), exist_ok=True)
    with open(path, "w") as f:
        f.write(content)

print("Wrote", len(FILES), "files into knowledge/")
terminal
uv run build_bundle.py

That folder is your knowledge base — human-readable, git-versionable, no database required.

Load and parse the bundle

Reading an OKF bundle is just walking the folder and splitting each file into its frontmatter (structured fields) and its body (the markdown). Create ask.py:

ask.py
import glob
import yaml


def load_bundle(root: str = "knowledge") -> list[dict]:
    docs = []
    for path in sorted(glob.glob(f"{root}/**/*.md", recursive=True)):
        raw = open(path).read()
        meta, body = {}, raw
        if raw.startswith("---"):
            _, front, body = raw.split("---", 2)  # frontmatter, then the body
            meta = yaml.safe_load(front) or {}
        docs.append({"path": path, "meta": meta, "body": body.strip()})
    return docs


docs = load_bundle()
print("Loaded:", [d["path"] for d in docs])

Embed the files — the vector half

Now turn each file into a vector so we can find it by meaning. We embed the title, description, and body together.

ask.py (add)
import numpy as np
from sentence_transformers import SentenceTransformer

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

texts = [
    f"{d['meta'].get('title', '')}{d['meta'].get('description', '')}\n{d['body']}"
    for d in docs
]
vectors = embedder.encode(texts, normalize_embeddings=True)

Find with vectors, ground in the folder

This is the whole architecture in one function: embed the question, find the nearest file (vector find), then hand the model that file's exact contents and make it answer only from there, citing the path (folder ground).

ask.py (add)
from anthropic import Anthropic

client = Anthropic()


def ask(question: str) -> None:
    q = embedder.encode([question], normalize_embeddings=True)[0]
    i = int(np.argmax(vectors @ q))          # VECTOR: nearest file by meaning
    doc = docs[i]                            # FOLDER: open that exact file

    system = (
        "Answer using ONLY the knowledge file below, and cite it by its path. "
        "If the file does not contain the answer, say you don't know.\n\n"
        f"File: {doc['path']}\n"
        f"Type: {doc['meta'].get('type', '')}\n\n"
        f"{doc['body']}"
    )
    resp = client.messages.create(
        model="claude-opus-4-8",
        max_tokens=512,
        system=system,
        messages=[{"role": "user", "content": question}],
    )
    print(f"\nQ: {question}")
    print(f"[vector matched → {doc['path']}]")
    print(resp.content[0].text)


ask("How is weekly active users calculated?")
ask("What does an order row contain, and how does it link to customers?")

Run it

terminal
uv run ask.py

The first question never says "metric" or the file name — yet the vector index finds weekly_active_users.md by meaning, and the answer is grounded in that file's exact definition, with a citation. The second lands on orders.md and answers from its real schema and join. Vectors found the door; the folder gave the exact contents behind it.

Where to go next

  • Follow the links — after matching a file, parse its markdown links and pull in the neighbours it references (e.g. orders → customers) before answering.
  • Let an agent navigate — give the folder to a ReAct agent as tools (search, open file, follow link) and let it explore.
  • Go deeperFolders vs Vectors covers the whole picture; RAG in Production covers scaling the vector half.