You type "dog food" into a shopping site's search box. A post titled "what should my puppy eat" does not show up. Then you ask an AI about your company's vacation policy. It either says it doesn't know or makes up something plausible.
A person gets both right away. Why can't a computer? The two techniques that solve these problems are embeddings and RAG. In-house chatbots that read your documents, product search and recommendations, and customer-support AI all use them heavily.
The video this post is based on is in Korean, so the example sentences and screenshots below are in Korean. We translate them in the text.
This post covers:
- Embeddings: turning text into a map of numbers
- Measuring it ourselves: similarity of six sentences, and where embeddings fail
- RAG: find, attach, answer
- Running RAG over 155 real documents
- The moment RAG gets it wrong, and how to fix it
How to read the numbers. [Official] means material published by the company itself. [Paper] means a public paper. [Measured] means numbers we got by running the code ourselves.
1. Embeddings: text as a map of numbers
Why keyword search fails
Ordinary search matches characters. If the words in the query appear in a document, it finds it. If not, it doesn't. In Korean, "강아지 사료" (dog food) and "반려견이 먹을 밥" (food for a pet dog to eat) share no characters at all. Same meaning, no match.
So compare meaning instead of characters. An embedding turns a sentence into numbers so a computer can compare meanings. The word means "placing" the sentence as a point inside a space of numbers.
One sentence = 1,024 numbers
We used bge-m3, which turns one sentence into 1,024 numbers [Official] (BAAI/bge-m3). A list of numbers like this is called a vector. Think of it as a coordinate on a map.
We can't draw 1,024 numbers, so we reduced them to two (PCA) and plotted them. These are real computed positions. The three pet sentences gathered on the left, the three interest-rate sentences on the right. [Measured] Similar meaning, close together. Different meaning, far apart. That is all an embedding does.
Closeness is measured as an angle
Closeness is measured by the angle between two arrows. This is cosine similarity. Same direction is 1, a right angle is 0, opposite is -1. The closer to 1, the more similar the sentences.
How does the model learn meaning?
It practices on a huge number of sentence pairs. Pairs that belong together, like a question and its answer, are pulled closer. Pairs that don't are pushed apart. Repeat this countless times and you get a map where sentences with the same meaning gather. This is contrastive learning.
2. Measuring it ourselves
Similarity of six sentences
a3/heatmap.py in our public practice repository has three parts:
- Read the sentence file into a list, one sentence per line.
- Turn each sentence into 1,024 numbers with the embedding model, with the option that sets each arrow's length to 1 (normalization).
- Multiply the lists together. Since each length is 1, the product is the cosine similarity. All pairs are multiplied at once to build a table.
Scores for "강아지 사료 추천해 줘" (recommend some dog food) against the others: [Measured]
| Compared sentence | Score |
|---|---|
| 반려견이 먹을 밥 뭐가 좋아? (what's good for my dog to eat?) | 0.73 |
| 고양이 간식은 뭘 사야 할까 (which cat treats should I buy?) | 0.69 |
| 3 interest-rate sentences | 0.40 – 0.46 |
The sentence that shares no characters came out closest. The post keyword search missed can now be found.
A picture that shows table values as color intensity is a heatmap. Darker cells mean more similar sentences. The pet group and the interest-rate group split into two blocks.
Where embeddings fail
Can you always trust embeddings? We measured five tricky pairs with a3/probe.py. [Measured]
| Pair (translated) | Relation | Score |
|---|---|---|
| "My stomach (배) hurt so I went to the hospital" ↔ "I took a boat (배) to the island" | Same word, different meaning | 0.71 |
| "My stomach hurt so I went to the hospital" ↔ "I had bad abdominal pain and saw a doctor" | Different words, same meaning | 0.66 |
| "This movie is really fun" ↔ "This movie is really not fun" | Opposites | 0.75 |
| "The base rate rose to 3%" ↔ "The base rate rose to 5%" | Only the number differs | 0.84 |
| Korean ↔ "I'm looking for food for my puppy" | Korean vs. English, same meaning | 0.87 |
In Korean, 배 means both "stomach" and "boat." The boat sentence, with a completely different meaning, scored higher than the abdominal-pain sentence that means the same thing. The shared word pulled it in. When sentences look alike, embeddings see them as close, so they are bad at telling opposites and numbers apart. On the other hand, sentences with the same meaning land together on one map even across languages.
That is why teams in production don't rely on embeddings alone. They mix in keyword matching for exact names and numbers. This mix is called hybrid search. Apple Music's search team added embedding search to their existing keyword search and reported that searches returning zero results dropped by 86% [Paper] (Multilingual Semantic Retrieval for Apple Music Search, arXiv 2607.10239).
3. RAG: find, attach, answer
The AI doesn't know your documents
We asked an AI how much Apple Music's search team reduced empty results in their 2026 paper (a1/ask.py).
- Question only: it said it could not confirm that paper's numbers.
- With one line of the paper's content attached: it answered 86%, exactly. [Measured]
The model didn't get smarter. We gave it the text that contained the answer.
AI like ChatGPT or Claude is called an LLM (Large Language Model). An LLM finished learning from text collected up to a certain date. It doesn't know what happened after that, or your documents that were never public. Ask about something it doesn't know and it says so, or invents a plausible answer. An invented answer is called a hallucination.
Why not attach every document?
A few pages are fine. Thousands of documents won't fit at once. There is a limit to how much you can send, and the more you send, the more it costs. So you pick only the parts that match the question. Find, attach, answer. That is RAG (Retrieval-Augmented Generation). The name comes from a 2020 paper [Paper] (Lewis et al. 2020).
Think of an open-book exam. In a closed-book exam, if a question comes up you don't know, you leave it blank or make something up. In an open-book exam, you can find the right page and write from it. RAG lets the AI bring the book into the exam room, and has it write which page it used next to each answer so a person can check.
You can also retrain the model on your documents: fine-tuning. But you'd have to retrain every time the documents change, so for documents that change often, teams usually try RAG first.
How RAG works
There are two parts:
- Preparation (once): load the documents, cut them into small pieces, make a vector for each piece with the embedding model, and store it. Each piece is a chunk, and cutting is chunking. A store that holds vectors and quickly finds the close ones is a vector database.
- Per question: turn the question into a vector with the same embedding model and take the few closest chunks (retrieval). Number those chunks, send them with the question to the LLM, and say "answer only from the material below and cite the numbers." The LLM writes an answer with source numbers (generation).
Why cut? It's like writing a thick book onto note cards instead of carrying the whole book. One vector for a whole document blurs many topics together. With cards, you can attach only the ones that match the question. In short, RAG is embedding search plus one final generation step.
4. Running it on real documents
The material is the concept section of the official Kubernetes docs in Korean. Kubernetes is a tool for running server programs.
- 155 documents, 1,103,975 characters (about 1.1 million)
- Cut every 500 characters into 3,033 chunks
- Embeddings with bge-m3, top 4 chunks attached per question [Measured]
b3/rag.py is under 40 lines, in three parts:
- Retrieve: turn the question into a vector, multiply it against all 3,033 prepared chunks at once, and take the top 4.
- Attach: label the 4 chunks with numbers starting at 1 and their document titles, and join them into one text.
- Generate: send it to the LLM with the instruction "answer only from the material below, put source numbers at the end of each sentence, and say you don't know if it isn't there."
The first question: "What do containers inside a Pod share?" A Pod is the unit Kubernetes uses to run programs together. The answer was "the network and shared namespaces, and file-system volumes," with bracketed numbers on every sentence. All four sources were Pod documents. [Measured]
5. The moment RAG gets it wrong
The second question: "How do I roll a Deployment back to a previous version?" A Deployment is the feature that moves a program to a new version.
RAG answered "The material does not include specific commands or steps." But the Deployment document has the rollback command written out. It followed the "say you don't know" instruction well, but the answer was in the documents, so for the user it's a wrong answer.
Looking at the chunks we passed
We used b3/peek.py to print where the chunk containing the right command ranked. [Measured]
- All four chunks we passed came from the Deployment document. Retrieval found the right document.
- But the chunk describing how to roll back wasn't among them.
- The right chunk ranked 12th, with a score of 0.642. The 4th scored 0.661. It was pushed out by less than 0.02.
The Deployment document is split into many chunks, and the question is similar to all of them. Scores are packed tightly from 0.672 at rank 1 to 0.642 at rank 12, so if the right chunk slips even a little, it doesn't make the top 4.
That is why half of RAG quality is retrieval. However good the LLM is, it doesn't know anything outside the chunks it receives. When an answer is wrong, look at the retrieved chunks before blaming the LLM.
Ways to fix it
- Hybrid search: mix meaning search with keyword search.
- Reranking: retrieve a generous set of candidates, then re-order them with a more careful model.
- Change the chunking: change chunk size or boundaries.
Big tech companies face the same problem. Anthropic added a line of document context to each chunk, plus keyword search (BM25) and reranking, and reported that the rate of the right answer missing from the top 20 chunks fell from 5.7% to 1.9% (a 67% reduction) [Official] (Introducing Contextual Retrieval).
Still, you only know which method works on your data by measuring. Teams in production keep a set of questions with known answers and fix things one at a time while tracking the score.
Summary
- An embedding turns a sentence into a point (a vector) on a map of meaning. Different words with the same meaning end up close. But it's bad at telling opposites and numbers apart.
- Cosine similarity is closeness measured as the angle between two arrows.
- RAG finds the chunks that match a question using embeddings, attaches them to the question for the LLM, and has it write an answer with source numbers.
- When RAG is wrong, check retrieval before the LLM. Start by finding out where the right chunk ranked.
Limits of this experiment
- We used one embedding model (bge-m3), one LLM for answers, and one document set (the Korean Kubernetes concept docs).
- The similarity scores are examples from a handful of sentences and two questions. Different models or sentences give different numbers.
- We only introduced hybrid search, reranking and chunking changes. We didn't apply them to this document set, so this post has no number for how far the 12th-place chunk would move up.
Watch the video
We covered the same material, with diagrams and the real runs on screen, in a Korean video.
Sources
- Apple Music search team, Multilingual Semantic Retrieval for Apple Music Search (arXiv 2607.10239, 2026)
- Anthropic, Introducing Contextual Retrieval (2024-09-19)
- Lewis et al., Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks (2020)
- BAAI, bge-m3 model card · Chen et al., BGE M3-Embedding (2024)
- Kubernetes official docs (Korean), Concepts (kubernetes/website, CC BY 4.0)
- Practice code: dante01yoon/embedding-rag-practice (
a1/ask.py,a3/heatmap.py,a3/probe.py,b3/rag.py,b3/peek.py)















