Skip to main content
Welcome — in this hands-on demo we’ll convert a Markdown policy document into vector embeddings using a SentenceTransformer, store them in LanceDB (an open-source vector database), and run semantic search over the document. All code runs in a Jupyter notebook; the examples below show the essential code, explanations, and sample outputs so you can reproduce the workflow. Table of contents
    1. Imports and prerequisites
    1. Load the policy document
    1. Chunk the document by headings
    1. Load the embedding model
    1. Encode chunks and store in LanceDB
    1. Define search utilities
    1. Example queries
    1. Notes on behavior and limitations
    1. Wrap-up and resources

1) Imports and prerequisites

Install dependencies (example):
Then import the Python modules required for file handling, text processing, embeddings, and the vector DB:
Helpful links: Below is a short table of the key libraries used in this tutorial.

2) Load the policy document

Load the Markdown file (here: kodekloud_airlines_policy.md) and print a short preview to confirm successful load.
This policy includes sections for baggage, ticket changes and cancellations, pets, check-in, boarding, and more. We’ll create semantic chunks from these sections and embed them.
The image shows a code or document editor with a file open displaying airline policies, including sections on baggage, ticket changes, and boarding information. The interface includes a file explorer on the left, showing other files and their modification times.

3) Chunk the document

Embedding an entire long document at once reduces retrieval granularity. The recommended approach is to split into semantic chunks (for example, by headings) and embed each chunk independently. The following chunk_by_headings function groups content by top-level headings (#, ##, ###) and discards very short buffers. It returns a list of dictionaries with section and text keys.
Run the chunker and inspect the first few chunks:

4) Load the embedding model

We use the all-MiniLM-L6-v2 SentenceTransformer (compact, high-quality for semantic search). If you pull from the Hugging Face Hub frequently, consider setting HF_TOKEN as an environment variable to avoid unauthenticated rate limits.
If you see warnings about unauthenticated Hugging Face Hub requests, set a HF_TOKEN environment variable to increase rate limits and speed up downloads.
Load the model and encode a test string:
Example console output (actual numbers may differ):

5) Encode chunks and store in LanceDB

Create (or recreate) a local LanceDB store, encode each chunk into a normalized embedding vector, and store rows with section, text, and vector fields.
You should see the row count equal to the number of chunks created (for example, 10).

6) Define search utilities

Create two helper functions:
  • search_policy(question, k) — performs a vector search in LanceDB and returns top-k hits as a pandas DataFrame
  • pretty_print_results(question, k, preview_chars) — prints results with section context and distance score
Notes:
  • Using normalize_embeddings=True ensures cosine similarity is computed as a simple dot product in many vector DBs.
  • The _distance field returned by LanceDB is typically the computed metric (lower is closer depending on configuration).

7) Example queries

Try a few representative questions to demonstrate semantic search behavior.
  • Example: “What is the cabin baggage weight limit?”
Sample output:
The top match is the baggage policy chunk. The second match references pet weight (pet + carrier), which can be returned because embeddings capture semantic relationships — sometimes leading to plausible but different interpretations.
The image shows a Jupyter Notebook interface displaying a Python script related to a text embedding project. It includes details on baggage policies and training demos for cabin pets, with sections for cabin baggage and checked baggage rules.
Try additional queries:
A sample cancellation-related query should return the ticket change and cancellation policy as the top match; ambiguous queries can return multiple sections that are semantically related.
The image shows a Jupyter Notebook interface with a code cell displaying results for a text embedding query related to airline ticket changes and cancellations. The sidebar lists various files in a project directory.

8) Notes on behavior and limitations

  • Embeddings provide semantic matching, not exact keyword/string matching: paraphrases and related concepts can match.
  • Retrieval quality depends on chunking strategy, model selection, and the currency of the embedded document.
  • Ambiguous queries may return plausible but incorrect sections. Combining retrieval with a downstream RAG (Retrieval-Augmented Generation) pipeline, intent classification, or rule-based filters can improve precision.
  • If the source document changes, re-embed the affected chunks and update the vector store to reflect the latest content.
Quick summary table: pros/cons

9) Wrap-up and resources

What we covered:
  • Loading a Markdown policy document
  • Chunking by headings for semantic units
  • Encoding chunks with a SentenceTransformer
  • Storing vectors in LanceDB
  • Performing semantic search and displaying top matches
Embedding documents into a vector database simplifies semantic retrieval and enhances search experiences across knowledge bases and documentation. Further reading and references: Thank you for following this demo — feel free to reuse and adapt the code for other policy or documentation search tasks.

Watch Video

Practice Lab