Tokens And Embeddings

One Search System: Stacking the Chapter's Choices and Measuring Each

0 of 11 complete

0%

Contents

Back|Tokens And EmbeddingsOne Search System: Stacking the Chapter's Choices and Measuring Each
1/11
32 min left
Prerequisites
Keyword Plus Meaning: When Combining Two Searches Helps, and When It Hurtsrequired
Related Topics
Chunking: The First Lever on Retrieval QualityRetrieval and RAG in ProductionIs There Really a Best Chunk Size? Measured on This Course's Own LessonsRetrieval and RAG in ProductionThe RAG Scale Cliff: What Breaks Between 100 and 5 Million DocumentsRetrieval and RAG in ProductionHybrid Retrieval: When Keyword Search Beats Your EmbeddingsRetrieval and RAG in ProductionThe RAG Retrieval Cliff: Engineering Recall Back at ScaleRetrieval and RAG in Production
1 of 11

Putting the Pieces Together

Each lesson in this chapter made one choice and measured it on its own. A real search system makes all of them at once. Do they still help when they are stacked? Does one undo another?

A flat illustration of two engineers at a whiteboard covered with a diagram of boxes, arrows and a database cylinder, one pointing with a marker while the other holds a laptop. A line says each lesson in this chapter made one choice, and this lesson puts them together and measures each one.

This lesson builds one search system, one choice at a time, and measures what each step adds and what it costs.

A hand-drawn list, one line per part of the chapter. Lessons 1 and 2, tokens: text is split into tokens before a model reads it. Lessons 3 and 4, embed: an embedding is a list of numbers for meaning. Lesson 5, the cut: check how much of a long text is really read. Lesson 6, pieces: split documents, and a document scores by its best piece. Lessons 7 to 9, store: length 1, fewer numbers, 1 byte per number. Lessons 10 and 11, search: clusters to search less, and keywords as a second vote. A note says lesson 12 stacks the choices from lessons 6, 9, 10 and 11 and measures each.

Try It: The Steps on One Small Document

This file runs the chapter's steps on one short document: split it into pieces, embed them, store each number in 1 byte, search by meaning, then add keywords and combine the two rankings. With only four pieces there is nothing to gain from clusters, so that step is left out here; the real test below includes it. Here is the file, open in my VS Code.

A real screenshot of whole_pipeline.py open in VS Code, 36 lines: embed, dot, words and place helpers, a short document about an API, rate limits and invoices, a question about requests per minute, and five commented steps: split into pieces of 12 words, embed each piece, store each number in 1 byte, search by meaning, and add keywords then combine the two rankings.

And this is what it printed:

A real screenshot of VS Code's terminal after running python whole_pipeline.py. Piece 1: meaning 0.466, 0 shared words, combined place 4, "Our API returns JSON". Piece 2: meaning 0.704, 3 shared words, combined place 1, "send 100 requests per". Piece 3: meaning 0.526, 0 shared words, place 3, "sent on the first day". Piece 4: meaning 0.527, 0 shared words, place 2, "by card."

The meaning score is the cosine from earlier lessons: higher means closer in meaning. The combined place uses lesson 11's rule, adding 1 divided by (60 plus the place) from each ranking. Pieces 1, 3 and 4 share no words with the question, so they tie in the word ranking and all get the worse place, 4.

Piece 2, about requests per minute, came first by meaning and by shared words, so it came first combined too.

A hand-drawn pair of boxes from the run: piece 2, "send 100 requests per...", with meaning 0.704 and 3 shared words, place 1; and piece 4, "by card.", a scrap, yet meaning 0.527. A note says cutting every 12 words left a two-word piece at the end.

Notice piece 4: just "by card.", two words. Cutting every 12 words left a scrap at the end of the document, and it still scored 0.527, just ahead of piece 3. Lesson 6 showed that a fixed word count also splits sentences in the middle. Cutting at sentence breaks is a common fix, but neither lesson measured it.

To run it: install Ollama from ollama.com, open it and leave it running. Run ollama pull bge-m3, save the file below as whole_pipeline.py, and run python3 whole_pipeline.py (on Windows: python whole_pipeline.py). If you see "Connection refused", open the Ollama app first.

# Every step of this chapter in one small search: split, embed, store in 1 byte, search, add keywords.
# Needs Ollama (ollama.com) running, and:  ollama pull bge-m3
import json, re, urllib.request

def embed(texts):
    body = {"model": "bge-m3", "input": texts}
    req = urllib.request.Request("http://localhost:11434/api/embed", data=json.dumps(body).encode(),
                                 headers={"Content-Type": "application/json"})
    return json.loads(urllib.request.urlopen(req).read())["embeddings"]

def dot(a, b): return sum(x * y for x, y in zip(a, b))
def words(t): return set(re.findall(r"\w+", t.lower()))
def place(scores): return [sum(s >= x for s in scores) for x in scores]

document = ("Our API returns JSON. Errors use standard HTTP codes. "
            "Each key may send 100 requests per minute; going over returns HTTP 429. "
            "Invoices are sent on the first day of each month and can be paid by card.")
question = "How many requests per minute am I allowed?"

# 1. split into pieces of 12 words
pieces = [" ".join(document.split()[i:i + 12]) for i in range(0, len(document.split()), 12)]
# 2. embed each piece
vectors = embed(pieces)
# 3. store each number in 1 byte
scale = 127 / max(abs(x) for v in vectors for x in v)
stored = [[round(x * scale) for x in v] for v in vectors]
# 4. search by meaning
q = embed([question])[0]
meaning = [dot(q, v) / scale for v in stored]
# 5. add keywords, then combine the two rankings
shared = [len(words(question) & words(p)) for p in pieces]
m, k = place(meaning), place(shared)
both = [1 / (60 + m[i]) + 1 / (60 + k[i]) for i in range(len(pieces))]
for i, p in enumerate(pieces):
    print(f"piece {i + 1}: meaning {meaning[i]:.3f}  shared words {shared[i]}  combined place {place(both)[i]}  {p[:22]}...")

Five Steps, Each Adding One Choice

Five numbered cards in a row: 1 whole, one embedding per lesson; 2 pieces, 200-word pieces; 3 one byte, per stored number; 4 clusters, search 16 of 32; 5 plus keywords, fuse with BM25. A note says every step keeps the choices before it, with the same 470 questions and bge-m3 all the way through.

  1. Whole: one per lesson, exact search, which means comparing the question with every stored embedding. Each such comparison counts as one in the tables below. This is where lesson 6 started.
  2. Pieces: cut every lesson into 200-word pieces; a lesson scores by its best piece. From lesson 6.
  3. One byte: store each piece's numbers in 1 byte instead of 4. From lesson 9.
  4. Clusters: group the pieces into 32 clusters, each with a centre, and search only the 16 clusters whose centres are nearest the question. From lesson 10.
  5. Plus keywords: combine the meaning ranking with a keyword ranking made by BM25, the word-counting recipe from lesson 11, which keeps a word index: a list of which words appear in which pieces. From lesson 11.

Each step keeps everything before it. Step 5 is all five choices together.

A sequence diagram of one question through the finished system, with three columns: question, meaning search over 1-byte pieces, and keywords with BM25. Step 1: find the nearest 16 of 32 clusters. Step 2: take the best piece per lesson. Step 3: rank by shared words. Step 4: fuse the two rankings. Step 5: return the top 5 lessons. A note says the meaning side searches about half the pieces, and the keyword side reads every piece's words.

A hand-drawn sketch of the finished system: a question goes to two indexes, 1-byte pieces in 32 clusters and a word index with BM25, and both feed a fused top 5. The Ollama logo sits below. A note says the embeddings and the word index are built once, and each question reads both.

The Test

An isometric row of four blocks joined by arrows: 112 lessons, 1,823 pieces, 5 steps with one choice each, and 470 questions asked. A note says bge-m3, 1,024 numbers, reading up to 8,192 tokens per text.

  • Questions: the 470 quiz questions inside the 112 AI lessons, as in lessons 6 to 11. The right answer is the lesson a question came from.
  • Model: bge-m3, reading up to 8,192 tokens so whole lessons are read in full, as lesson 5 showed is needed.
  • Measured at every step: whether the right lesson came first and whether it was in the top 5; how much storage the take; and how many comparisons each question needs, counted, not timed.

Here is the lab's report, from the terminal.

A real terminal recording of pipeline.py's report: 470 quiz questions, 112 lessons, bge-m3 with 1,024 numbers, each step adding one choice. Whole: 286 first, 401 in the top 5, 0.46 MB stored, 112 compared. Pieces: 356, 439, 7.47 MB, 1,823. One byte: 356, 439, 1.87 MB, 1,823. Clusters: 357, 437, 2.00 MB, 1,018. Plus keywords: 376, 451, 2.00 MB, 1,018. A note says stored means embeddings only, 16 of 32 clusters are searched, and compared includes the centres.

As a check, the first three steps repeat their own lessons exactly:

Three boxes, top 5 of 470: whole 401, where lesson 6 said 401; pieces 439, where lesson 6 said 439; 1 byte 439, where lesson 9 said 439. A note says with the same questions and model the numbers match, so the steps can be trusted to stack.

What Each Step Did for the Answers

A bar chart of the right lesson in the top 5, of 470, at each step: whole 401, pieces 439, one byte 439, clusters 437, plus keywords 451. Titled pieces and keywords did the lifting. A note says only step 4 lost any, and only 2.

  • Pieces: 401 to 439. The biggest gain in this lesson.
  • One byte: 439 to 439. No change.
  • Clusters: 439 to 437. Two lost.
  • Plus keywords: 437 to 451. The second biggest gain in this lesson.

A bar chart of the right lesson ranked first, of 470, at each step: 286, 356, 356, 357, 376. Titled the strict test agrees.

Ranked first tells the same story: 286, 356, 356, 357, 376. Clusters even gained one here. We search only some clusters, so a lesson with no piece in them drops out, and with fewer lessons left the right one can move up. One question more or less does not matter.

What Each Step Cost

A bar chart of stored embeddings in megabytes at each step: whole 0.46, pieces 7.47, one byte 1.87, clusters 2.00, plus keywords 2.00. Titled pieces cost space and 1 byte won it back. A note says the last two include the cluster centres.

Storage. Pieces multiplied it: 0.46 MB to 7.47 MB, because there are 1,823 pieces instead of 112 lessons. One byte per number brought it back down to 1.87 MB. The 32 cluster centres add a little, to 2.00 MB. Keyword search stores no . It needs its word index instead, and this lesson did not measure the size of that index.

A bar chart of comparisons per question at each step: whole 112, pieces 1,823, one byte 1,823, clusters 1,018, plus keywords 1,018. Titled clusters halved the meaning search. A note says keyword search is extra work not counted here.

Work. Pieces also multiplied the comparisons, 112 to 1,823. Searching 16 of 32 clusters brought it down to about 1,018. Keyword search adds its own work, reading the word index, which is not counted here.

An editorial ledger of each step's change in top-5 answers from the step before, and its cost. Pieces: plus 38, storage up from 0.46 to 7.47 MB. One byte: 0, storage down to 1.87 MB. Clusters: minus 2, comparisons down from 1,823 to about 1,018. Plus keywords: plus 14, no extra embedding storage, a word index instead. A note says two steps bought answers, and two steps bought efficiency at almost no cost.

That is the pattern of the whole chapter. Two choices bought right answers: pieces and keywords. Two choices saved space or work and lost almost no right answers: 1 byte and clusters.

Two honest footnotes, from comparing with lesson 11:

  • Does one step undo another? A little. Lesson 11 combined keywords with exact meaning search and found 455. Here, with clusters in front, it was 451. So in the finished system clusters cost 4 right lessons, not 2: a lesson dropped by the clusters can only be rescued part of the way by the keyword vote.
  • Keyword search alone scored 453 on these questions (lesson 11). The whole stack, at 451, is just below it. These quiz questions reuse the lessons' own words, which is what keyword search is built for. With questions in other words, lesson 11 showed the opposite. Measure on your own questions before you decide.

Count It Yourself

This box holds the real ranks from every step, for all 470 questions. A rank of 112, the last place, means the right lesson was not found at all, for example because none of its pieces was in the searched clusters. This counting is also how to measure your own system: for each question with a known answer, record the place of the right document, then count how many places are 5 or better. Press Run, then change TOP to 1.

Building Your Own Search System

A flowchart: split into pieces; is memory tight? If yes, 1 byte, then ask whether search is slow; if no, go straight to that question; if search is slow, clusters. A note says then add keywords if your users share your documents' words, and measure after every step.

  1. Write a question set first: 50 or more questions where you already know the right document. Without it, nothing below can be measured.
  2. Start simple: whole documents, exact search. Measure.
  3. Add one choice at a time. The order used here was pieces, then 1 byte, then clusters, then keywords. This lesson did not test other orders, and the footnote above shows steps can interact, so re-measure after each one. Add 1 byte if memory matters, clusters if search is slow, and keywords if your users share your documents' words.
  4. Keep a ledger: right answers, storage and work after every step. A step that costs more than it gives can be removed.
  5. Always include keyword search alone as a baseline. Here it was nearly as good as the whole stack.

Two cards with logos: Ollama, which ran the chapter's embedding models (bge-m3 in this lesson), and Python, with NumPy, a library for fast number arrays, used in the lab, all on a laptop. A note says free and local, with no paid API and no GPU.

What This Lesson Measured, and What It Did Not

Two columns. Measured: 470 quiz questions, one model with one order of steps, storage and work counted. Not measured: other orders of steps, time on a real database, questions from real users.

Measured: one model, bge-m3; one order of steps; 470 quiz questions; storage of the and comparisons per question, counted.

Not measured: other orders of the steps, other settings for each step, time on a real database, the size of the keyword index, and questions from real users. Our quiz questions use the same words as their lessons, so keyword search does well here. Lesson 11 showed it can do worse when users choose different words.

What to Do Next

A hand-drawn list of four things to do: run whole_pipeline.py, all five steps in one file; write 50 questions you know the answers to; add one choice at a time, never two at once; and keep a ledger of answers, storage and work after every step. A note says every choice is a trade, so measure what you get for it.

  1. Run whole_pipeline.py and change the piece size from 12 words.
  2. Write your own question set: 50 questions with known answers.
  3. Add one choice at a time, never two at once.
  4. Keep a ledger of answers, storage and work after every step.

The number to keep: 401 to 451, right lesson in the top 5 of 470, whole lessons to the full system. A note says pieces and keywords bought answers, and 1 byte and clusters bought efficiency.

This chapter started with a single token. It ends with a search system you can build on a laptop, and a way to measure every choice in it.

Knowledge Check

Knowledge Check

5 questions - Score 80% to pass

Q1

Which two steps raised the number of right lessons in the top 5 the most?

Q2

Storing each number in 1 byte kept 439 right lessons in the top 5. What did it buy?

Q3

Searching 16 of 32 clusters cut comparisons from 1,823 to about 1,018. What did it cost in the top 5?

Q4

Why did the first three steps give exactly the same numbers as lessons 6 and 9?

Q5

You are building your own search system. What should you do before adding any of these steps?

Two panels, start and finish, right lesson in the top 5 of 470. Step 1, whole: 401, 0.46 MB. Step 5, all: 451, 2.00 MB. A note says 50 more right lessons in the top 5, for about 4.4 times the storage.