{"id":306,"date":"2026-11-09T13:20:03","date_gmt":"2026-11-09T13:20:03","guid":{"rendered":"https:\/\/devdojo.co.in\/?p=306"},"modified":"2026-11-09T13:20:04","modified_gmt":"2026-11-09T13:20:04","slug":"genai-series-part-9-embeddings-vectors","status":"publish","type":"post","link":"https:\/\/devdojo.co.in\/index.php\/2026\/11\/09\/genai-series-part-9-embeddings-vectors\/","title":{"rendered":"Generative AI Part 9: Embeddings Explained: Turning Text into Vectors"},"content":{"rendered":"\n<div class=\"dd-post\">\n<style>\n.single article:has(.dd-hero) .entry-media{display:none}\n.dd-post{--navy:#0b1020;--blue:#3178c6;--violet:#7c5cff;--ink:#1f2433;--muted:#5b6275;--line:#e3e6ef;font-size:17px;line-height:1.75;color:var(--ink)}\n.dd-post h2{margin:2.2em 0 .6em;font-size:1.6em;line-height:1.3;scroll-margin-top:90px}\n.dd-post h3{margin:1.6em 0 .5em;font-size:1.22em;scroll-margin-top:90px}\n.dd-post a{color:var(--blue);text-decoration:underline;text-underline-offset:3px}\n.dd-post pre{background:#0f1430;color:#e6e9f5;padding:18px 20px;border-radius:12px;overflow-x:auto;line-height:1.6;border:1px solid #232a52;margin:1.2em 0;font-size:14.5px !important}\n.dd-post pre code{font-size:1em !important;background:none;color:inherit;padding:0;font-family:\"JetBrains Mono\",\"Fira Code\",Consolas,Menlo,monospace;white-space:pre}\n.dd-post :not(pre)>code{background:#eef1fb;color:#2b3a8f;padding:2px 6px;border-radius:5px;font-size:.9em}\n.dd-post .dd-file{display:inline-block;background:#151b3b;color:#c9d1ff;font:600 13px\/1 \"JetBrains Mono\",Consolas,monospace;padding:7px 12px;border-radius:8px 8px 0 0;margin:1em 0 -1.2em;position:relative}\n.dd-post .dd-hero{margin:0 0 1.6em;border-radius:16px;overflow:hidden;box-shadow:0 10px 30px rgba(11,16,32,.25)}\n.dd-post .dd-hero svg{display:block;width:100%;height:auto}\n.dd-post .dd-hero .node{transition:transform .25s,filter .25s;transform-box:fill-box;transform-origin:center;cursor:pointer}\n.dd-post .dd-hero .node:hover{transform:scale(1.08);filter:drop-shadow(0 0 10px #7c5cff)}\n.dd-post .dd-series{display:inline-flex;align-items:center;gap:8px;background:#151b3b;color:#dfe3ff;border-radius:999px;padding:6px 14px;font-size:.88em;font-weight:600;margin:0 0 .8em}\n.dd-post .dd-series span{background:var(--violet);color:#fff;border-radius:999px;padding:2px 10px}\n.dd-post .dd-prereq{font-size:.96em;color:var(--muted);border-left:3px solid var(--line);padding-left:12px;margin:.6em 0 1.2em}\n.dd-post .dd-summary{background:linear-gradient(135deg,#eef3ff,#f4efff);border:1px solid #d6dcf5;border-left:5px solid var(--violet);border-radius:12px;padding:18px 22px;margin:1.4em 0}\n.dd-post .dd-summary strong.lbl{display:block;font-size:1.05em;margin-bottom:6px;color:#3a2fa0}\n.dd-post .dd-summary ul{margin:.3em 0 0 1.1em}\n.dd-post .dd-toc{background:#f8f9fc;border:1px solid var(--line);border-radius:12px;padding:16px 22px;margin:1.4em 0}\n.dd-post .dd-toc ol{margin:.4em 0 0 1.2em;columns:2;column-gap:28px}\n.dd-post .dd-toc li{break-inside:avoid;margin:.15em 0}\n@media(max-width:640px){.dd-post .dd-toc ol{columns:1}}\n.dd-post .dd-tip,.dd-post .dd-note,.dd-post .dd-warn{border-radius:10px;padding:14px 18px;margin:1.3em 0}\n.dd-post .dd-tip{background:#ecfbf3;border-left:4px solid #1f9d61}\n.dd-post .dd-note{background:#eef4ff;border-left:4px solid var(--blue)}\n.dd-post .dd-warn{background:#fff6e8;border-left:4px solid #e08a00}\n.dd-post .dd-cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:14px;margin:1.3em 0}\n.dd-post .dd-card{border:1px solid var(--line);border-radius:12px;padding:14px 16px;background:#fff;box-shadow:0 2px 8px rgba(20,30,70,.05)}\n.dd-post .dd-card b{display:block;color:#3a2fa0;margin-bottom:4px}\n.dd-post table{width:100%;border-collapse:collapse;margin:1.3em 0;font-size:.95em}\n.dd-post th,.dd-post td{border:1px solid var(--line);padding:10px 12px;text-align:left;vertical-align:top}\n.dd-post th{background:#151b3b;color:#fff}\n.dd-post tr:nth-child(even) td{background:#f8f9fc}\n.dd-post .dd-tablewrap{overflow-x:auto}\n.dd-post figure{margin:1.6em 0;text-align:center}\n.dd-post figure svg{width:100%;height:auto;border-radius:12px}\n.dd-post figcaption{font-size:.9em;color:var(--muted);margin-top:8px}\n.dd-post details{border:1px solid var(--line);border-radius:10px;padding:12px 16px;margin:.7em 0;background:#fff}\n.dd-post details summary{cursor:pointer;font-weight:600}\n.dd-post details[open] summary{margin-bottom:8px;color:#3a2fa0}\n.dd-post .dd-roadmap{border:2px solid #d6dcf5;background:#fbfbff}\n.dd-post .dd-roadmap h4{margin:1em 0 .3em;color:#151b3b;font-size:1em;text-transform:uppercase;letter-spacing:.04em}\n.dd-post .dd-roadmap ol{margin:.2em 0 .6em 1.4em;padding:0}\n.dd-post .dd-roadmap li{margin:.2em 0}\n.dd-post .dd-roadmap .here{background:var(--violet);color:#fff;border-radius:999px;padding:1px 9px;font-size:.8em;font-weight:700;margin-left:6px}\n.dd-post .dd-roadmap .soon{color:#8a90a6;font-size:.82em;margin-left:6px}\n.dd-post .dd-tabs{border:1px solid var(--line);border-radius:12px;padding:6px 16px 16px;margin:1.4em 0;background:#fbfbfe}\n.dd-post .dd-tabs>input{position:absolute;opacity:0;pointer-events:none}\n.dd-post .dd-tabs .labels label{display:inline-block;padding:8px 16px;margin:10px 6px 0 0;border-radius:999px;border:1px solid #cfd5ea;cursor:pointer;font-weight:600;font-size:.95em;background:#fff;transition:background .2s}\n.dd-post .dd-tabs .labels label:hover{border-color:var(--violet)}\n.dd-post .dd-tabs .panel{display:none;margin-top:12px}\n.dd-post .dd-tabs #ti1:checked~.labels label[for=ti1],.dd-post .dd-tabs #ti2:checked~.labels label[for=ti2],.dd-post .dd-tabs #ti3:checked~.labels label[for=ti3],.dd-post .dd-tabs #ti4:checked~.labels label[for=ti4]{background:var(--violet);color:#fff;border-color:var(--violet)}\n.dd-post .dd-tabs #ti1:checked~.p1,.dd-post .dd-tabs #ti2:checked~.p2,.dd-post .dd-tabs #ti3:checked~.p3,.dd-post .dd-tabs #ti4:checked~.p4{display:block}\n.dd-post .dd-tabs #ti1:focus-visible~.labels label[for=ti1],.dd-post .dd-tabs #ti2:focus-visible~.labels label[for=ti2],.dd-post .dd-tabs #ti3:focus-visible~.labels label[for=ti3],.dd-post .dd-tabs #ti4:focus-visible~.labels label[for=ti4]{outline:2px solid var(--blue);outline-offset:2px}\n.dd-post .dd-cta{background:linear-gradient(135deg,#0b1020,#151b3b);color:#e8ebff;border-radius:16px;padding:24px 26px;margin:2em 0;text-align:center}\n.dd-post .dd-cta h3{color:#fff;margin-top:0}\n.dd-post .dd-cta a{color:#a99bff}\n.dd-post .dd-vsc{position:relative;margin:1.5em 0;border-radius:12px;overflow:hidden;background:#1e1e1e;border:1px solid #2b2b2b;box-shadow:0 14px 34px rgba(0,0,0,.28);font-family:\"Segoe UI\",system-ui,sans-serif}\n.dd-post .dd-vsc>input{position:absolute;opacity:0;pointer-events:none}\n.dd-post .dd-vsc-bar{display:flex;align-items:center;gap:12px;background:#323233;color:#cccccc;font-size:12.5px;padding:8px 12px}\n.dd-post .dd-dots{display:inline-flex;gap:7px}.dd-post .dd-dots i{width:12px;height:12px;border-radius:50%;background:#ff5f57;display:block}.dd-post .dd-dots i:nth-child(2){background:#febc2e}.dd-post .dd-dots i:nth-child(3){background:#28c840}\n.dd-post .dd-vsc-title{flex:1;text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding-right:50px;opacity:.85}\n.dd-post .dd-vsc-tabs{display:flex;align-items:stretch;background:#252526;border-bottom:1px solid #1e1e1e;overflow-x:auto}\n.dd-post .dd-vsc-tabs label{display:flex;align-items:center;gap:7px;padding:9px 16px;color:#969696;font-size:13px;cursor:pointer;background:#2d2d2d;border-right:1px solid #252526;border-top:1px solid transparent;white-space:nowrap;margin:0;transition:color .15s}\n.dd-post .dd-vsc-tabs label:hover{color:#e0e0e0}\n.dd-post .dd-vsc-tabs .ic{font:700 9.5px\/1 Consolas,monospace;padding:3px 4px;border-radius:3px;color:#1e1e1e}\n.dd-post .ic.py{background:#ffd43b}.dd-post .ic.js{background:#f1e05a}.dd-post .ic.sh{background:#89e051}\n.dd-post .dd-vsc-tabs label.run{margin-left:auto;background:transparent;border:0;color:#89d185;font-weight:600;display:none}\n.dd-post .dd-vsc-tabs label.run:hover{color:#b5f0b0;background:#2a2d2e}\n.dd-post .dd-vsc-pane{display:none}\n.dd-post .dd-vsc>.f1:checked~.dd-vsc-tabs .t1,.dd-post .dd-vsc>.f2:checked~.dd-vsc-tabs .t2,.dd-post .dd-vsc>.f3:checked~.dd-vsc-tabs .t3{background:#1e1e1e;color:#ffffff;border-top-color:#3178c6}\n.dd-post .dd-vsc>.f1:checked~.p1,.dd-post .dd-vsc>.f2:checked~.p2,.dd-post .dd-vsc>.f3:checked~.p3{display:block}\n.dd-post .dd-vsc>.f1:checked~.dd-vsc-tabs .run1,.dd-post .dd-vsc>.f2:checked~.dd-vsc-tabs .run2,.dd-post .dd-vsc>.f3:checked~.dd-vsc-tabs .run3{display:flex}\n.dd-post .dd-vsc>.f1:focus-visible~.dd-vsc-tabs .t1,.dd-post .dd-vsc>.f2:focus-visible~.dd-vsc-tabs .t2,.dd-post .dd-vsc>.f3:focus-visible~.dd-vsc-tabs .t3,.dd-post .dd-vsc>input[type=checkbox]:focus-visible~.dd-vsc-tabs .run{outline:2px solid #3178c6;outline-offset:-2px}\n.dd-post .dd-vsc-crumbs{background:#1e1e1e;color:#a9a9a9;font-size:12px;padding:5px 16px 2px}\n.dd-post .dd-vsc pre.dd-vsc-code{background:#1e1e1e;color:#d4d4d4;border:0;border-radius:0;margin:0;padding:8px 0 14px;counter-reset:ln;line-height:1.62;overflow-x:auto}\n.dd-post .dd-vsc pre code{font-family:Consolas,\"JetBrains Mono\",\"Fira Code\",Menlo,monospace;color:inherit;background:none;white-space:pre}\n.dd-post .dd-vsc .l{display:inline-block;min-width:100%;padding-right:18px;box-sizing:border-box}\n.dd-post .dd-vsc .l::before{counter-increment:ln;content:counter(ln);display:inline-block;width:3.2em;padding-right:1.4em;text-align:right;color:#6e7681;user-select:none;-webkit-user-select:none}\n.dd-post .dd-vsc .l:hover{background:#2a2d2e}.dd-post .dd-vsc .l:hover::before{color:#c6c6c6}\n.dd-post .dd-vsc .k,.dd-post .dd-vsc .kn{color:#c586c0}.dd-post .dd-vsc .kc{color:#569cd6}.dd-post .dd-vsc .s{color:#ce9178}.dd-post .dd-vsc .c{color:#6a9955}.dd-post .dd-vsc .m{color:#b5cea8}.dd-post .dd-vsc .nf{color:#dcdcaa}.dd-post .dd-vsc .nc{color:#4ec9b0}.dd-post .dd-vsc .nb{color:#4ec9b0}.dd-post .dd-vsc .nd{color:#dcdcaa}.dd-post .dd-vsc .n{color:#9cdcfe}.dd-post .dd-vsc .o{color:#d4d4d4}.dd-post .dd-vsc .gp{color:#89d185;user-select:none;-webkit-user-select:none}\n.dd-post .dd-vsc-term{display:none;border-top:1px solid #3c3c3c;background:#181818}\n.dd-post .dd-vsc>.r1:checked~.p1 .dd-vsc-term,.dd-post .dd-vsc>.r2:checked~.p2 .dd-vsc-term,.dd-post .dd-vsc>.r3:checked~.p3 .dd-vsc-term{display:block;animation:ddterm .35s ease-out}\n@keyframes ddterm{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}\n.dd-post .dd-term-head{display:flex;gap:18px;align-items:center;font-size:11px;letter-spacing:.06em;color:#8b8b8b;padding:7px 16px 0}\n.dd-post .dd-term-head span:first-child{color:#e7e7e7;border-bottom:1px solid #e7e7e7;padding-bottom:4px}\n.dd-post .dd-term-head .dd-term-note{margin-left:auto;letter-spacing:0;text-transform:none;color:#d7ba7d;border:0;font-size:11.5px}\n.dd-post .dd-vsc-term pre{background:#181818;color:#cccccc;border:0;margin:0;padding:10px 16px 14px;border-radius:0}\n.dd-post .dd-vsc-status{display:flex;flex-wrap:wrap;gap:4px 14px;background:#007acc;color:#fff;font-size:11.5px;padding:4px 12px}\n.dd-post .dd-vsc-status .sp{flex:1}\n.dd-post .dd-vsc-termonly pre.dd-vsc-code{background:#181818;padding:12px 16px}\n@media(max-width:640px){.dd-post .dd-vsc-title{padding-right:0;text-align:left}.dd-post .dd-vsc-status span:nth-child(n+4){display:none}.dd-post .dd-vsc .l::before{width:2.2em;padding-right:1em}}\n.dd-post .dd-vsc-hint{font-size:.93em;color:var(--muted);margin-top:-.6em}\n.dd-post .dd-next{border:1px dashed var(--violet);border-radius:12px;padding:16px 20px;margin:1.6em 0;background:#f7f4ff}\n<\/style>\n<style>\n.dd-post .dd-dims{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:10px;margin:.8em 0}\n.dd-post .dd-dims div{background:#151b3b;color:#e8ebff;border-radius:10px;padding:10px 12px;font-size:.92em}\n.dd-post .dd-dims b{display:block;color:#a99bff;font-size:.82em;text-transform:uppercase;letter-spacing:.05em}\n.dd-post .dd-bar{height:10px;border-radius:999px;background:#2a3266;margin-top:8px;overflow:hidden}\n.dd-post .dd-bar i{display:block;height:100%;border-radius:999px;background:linear-gradient(90deg,#3178c6,#7c5cff)}\n<\/style>\n\n<div class=\"dd-hero\">\n<svg viewBox=\"0 0 1200 520\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" role=\"img\" aria-label=\"Generative AI series Part 9 banner: three sentences go into an embedding model and come out as lists of numbers, which become points on a meaning map where similar sentences sit close together\">\n<defs>\n<linearGradient id=\"ddbg\" x1=\"0\" y1=\"0\" x2=\"1\" y2=\"1\"><stop offset=\"0\" stop-color=\"#0b1020\"\/><stop offset=\"1\" stop-color=\"#151b3b\"\/><\/linearGradient>\n<linearGradient id=\"ddac\" x1=\"0\" y1=\"0\" x2=\"1\" y2=\"0\"><stop offset=\"0\" stop-color=\"#3178c6\"\/><stop offset=\"1\" stop-color=\"#7c5cff\"\/><\/linearGradient>\n<radialGradient id=\"ddglow\" cx=\".5\" cy=\".5\" r=\".5\"><stop offset=\"0\" stop-color=\"#7c5cff\" stop-opacity=\".45\"\/><stop offset=\"1\" stop-color=\"#7c5cff\" stop-opacity=\"0\"\/><\/radialGradient>\n<\/defs>\n<rect x=\"0\" y=\"-60\" width=\"1200\" height=\"640\" fill=\"url(#ddbg)\"\/>\n<circle cx=\"1000\" cy=\"110\" r=\"250\" fill=\"url(#ddglow)\"\/>\n<g opacity=\".12\" stroke=\"#9fb2ff\"><path d=\"M0 100H1200M0 200H1200M0 300H1200M0 400H1200M200 0V520M400 0V520M600 0V520M800 0V520M1000 0V520\"\/><\/g>\n<rect x=\"60\" y=\"46\" width=\"128\" height=\"38\" rx=\"19\" fill=\"url(#ddac)\"\/>\n<text x=\"124\" y=\"71\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"18\" font-weight=\"700\" fill=\"#fff\">PART 9<\/text>\n<text x=\"204\" y=\"71\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"18\" fill=\"#aab4e8\">Generative AI Series \u00b7 DevDojo<\/text>\n<text x=\"60\" y=\"148\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"54\" font-weight=\"800\" fill=\"#ffffff\">Embeddings Explained<\/text>\n<text x=\"60\" y=\"206\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"44\" font-weight=\"800\" fill=\"url(#ddac)\">Turning Text into Vectors<\/text>\n<text x=\"60\" y=\"248\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"23\" fill=\"#c9d1ff\">Cosine similarity \u00b7 semantic search \u00b7 OpenAI + Ollama<\/text>\n<g font-family=\"Segoe UI,Arial,sans-serif\">\n<g class=\"node\"><rect x=\"60\" y=\"290\" width=\"300\" height=\"190\" rx=\"16\" fill=\"#1b2250\" stroke=\"#3178c6\" stroke-width=\"2\"\/><text x=\"210\" y=\"320\" text-anchor=\"middle\" font-size=\"15\" fill=\"#aac8f0\">Text<\/text><text x=\"80\" y=\"358\" font-size=\"17\" fill=\"#e8ebff\">&#8220;reset my password&#8221;<\/text><text x=\"80\" y=\"398\" font-size=\"17\" fill=\"#e8ebff\">&#8220;forgot my login&#8221;<\/text><text x=\"80\" y=\"438\" font-size=\"17\" fill=\"#e8ebff\">&#8220;delivery to Pune&#8221;<\/text><\/g>\n<g class=\"node\"><rect x=\"480\" y=\"320\" width=\"240\" height=\"130\" rx=\"20\" fill=\"url(#ddac)\"\/><text x=\"600\" y=\"368\" text-anchor=\"middle\" font-size=\"25\" font-weight=\"800\" fill=\"#fff\">Embedding<\/text><text x=\"600\" y=\"398\" text-anchor=\"middle\" font-size=\"25\" font-weight=\"800\" fill=\"#fff\">model<\/text><text x=\"600\" y=\"428\" text-anchor=\"middle\" font-size=\"14\" fill=\"#dfe3ff\">text \u2192 numbers<\/text><\/g>\n<g class=\"node\"><rect x=\"840\" y=\"290\" width=\"300\" height=\"190\" rx=\"16\" fill=\"#0b1020\" stroke=\"#61dafb\" stroke-width=\"2\"\/><text x=\"990\" y=\"318\" text-anchor=\"middle\" font-size=\"15\" fill=\"#a8ecfb\">Meaning map<\/text>\n<circle cx=\"900\" cy=\"370\" r=\"9\" fill=\"#7c5cff\"\/><circle cx=\"930\" cy=\"352\" r=\"9\" fill=\"#7c5cff\"\/><text x=\"868\" y=\"408\" font-size=\"13\" fill=\"#c9bfff\">password \u00b7 login<\/text>\n<circle cx=\"1080\" cy=\"440\" r=\"9\" fill=\"#89e051\"\/><text x=\"1010\" y=\"468\" font-size=\"13\" fill=\"#b8f0a0\">delivery<\/text>\n<path d=\"M915 361 L1075 436\" stroke=\"#7f89b5\" stroke-width=\"1.5\" stroke-dasharray=\"4 4\"\/><text x=\"1010\" y=\"380\" font-size=\"12\" fill=\"#7f89b5\">far apart<\/text><\/g>\n<\/g>\n<path id=\"ddf1\" d=\"M360 385 C 420 385, 420 385, 480 385\" fill=\"none\" stroke=\"url(#ddac)\" stroke-width=\"3\" stroke-dasharray=\"8 8\" opacity=\".8\"\/>\n<path id=\"ddf2\" d=\"M720 385 C 780 385, 780 385, 840 385\" fill=\"none\" stroke=\"url(#ddac)\" stroke-width=\"3\" stroke-dasharray=\"8 8\" opacity=\".8\"\/>\n<circle r=\"6\" fill=\"#61dafb\"><animateMotion dur=\"1.4s\" repeatCount=\"indefinite\"><mpath href=\"#ddf1\"\/><\/animateMotion><\/circle>\n<circle r=\"5\" fill=\"#ffffff\"><animateMotion dur=\"1.4s\" begin=\".7s\" repeatCount=\"indefinite\"><mpath href=\"#ddf1\"\/><\/animateMotion><\/circle>\n<circle r=\"6\" fill=\"#7c5cff\"><animateMotion dur=\"1.4s\" begin=\".35s\" repeatCount=\"indefinite\"><mpath href=\"#ddf2\"\/><\/animateMotion><\/circle>\n<circle r=\"5\" fill=\"#89e051\"><animateMotion dur=\"1.4s\" begin=\"1.05s\" repeatCount=\"indefinite\"><mpath href=\"#ddf2\"\/><\/animateMotion><\/circle>\n<text x=\"60\" y=\"510\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"16\" fill=\"#7f89b5\">devdojo.co.in \u00b7 Beginner friendly \u00b7 Python + Node.js examples<\/text>\n<\/svg>\n<\/div>\n\n<div class=\"dd-series\">Series: Generative AI <span>Part 9 of 30<\/span><\/div>\n<p class=\"dd-prereq\"><strong>What you should know first:<\/strong> read <a href=\"https:\/\/devdojo.co.in\/?p=303\">Part 8: Structured Outputs for Reliable JSON<\/a> and <a href=\"https:\/\/devdojo.co.in\/?p=223\">Part 3: Your First LLM API Calls<\/a>. You need Python 3.10+ or Node.js 20+, and either an OpenAI API key or a free local model in <a href=\"https:\/\/devdojo.co.in\/?p=281\">Ollama (Part 6)<\/a>.<\/p>\n\n<p>So far in this series, we have used LLMs to <em>write<\/em> text. Today we use AI to <em>understand<\/em> text. <strong>Embeddings<\/strong> turn any sentence into a list of numbers (a <strong>vector<\/strong>) that captures its meaning. Sentences that mean the same thing get similar numbers, even when they share no words. In this part you will learn what embeddings are, how cosine similarity measures &#8220;closeness&#8221;, how to create embeddings with OpenAI and with a free local Ollama model, and how to build a small <strong>semantic search<\/strong> in Python and Node.js. This is the foundation for vector databases (Part 10) and RAG (Part 11).<\/p>\n\n<div class=\"dd-summary\">\n<strong class=\"lbl\">Quick Summary<\/strong>\n<ul>\n<li>An <strong>embedding<\/strong> is a fixed-length list of numbers that represents the meaning of a piece of text.<\/li>\n<li>Texts with similar meaning have vectors that point in a similar direction. &#8220;Forgot my login&#8221; lands near &#8220;reset my password&#8221;.<\/li>\n<li>We measure closeness with <strong>cosine similarity<\/strong>: near 1 means very similar, near 0 means unrelated.<\/li>\n<li>Create embeddings with <code>client.embeddings.create(model=\"text-embedding-3-small\", input=[...])<\/code> or for free with <code>ollama.embed(model=\"nomic-embed-text\", input=[...])<\/code>.<\/li>\n<li><strong>Semantic search<\/strong> = embed your documents once, embed the question, then return the documents with the highest similarity.<\/li>\n<li>Always use the <strong>same model<\/strong> for documents and questions, and cache vectors so you never pay twice.<\/li>\n<\/ul>\n<\/div>\n\n<nav class=\"dd-toc\" aria-label=\"Table of contents\">\n<strong>In this guide<\/strong>\n<ol>\n<li><a href=\"#what-are-embeddings\">What are embeddings?<\/a><\/li>\n<li><a href=\"#why-embeddings\">Why keyword search is not enough<\/a><\/li>\n<li><a href=\"#cosine-similarity\">Cosine similarity explained<\/a><\/li>\n<li><a href=\"#setup\">Setup<\/a><\/li>\n<li><a href=\"#openai-embeddings\">Create embeddings with OpenAI<\/a><\/li>\n<li><a href=\"#choose-model\">Choosing an embedding model<\/a><\/li>\n<li><a href=\"#semantic-search\">Build a semantic search<\/a><\/li>\n<li><a href=\"#cache\">Save money: cache and shorten vectors<\/a><\/li>\n<li><a href=\"#errors\">Common errors and fixes<\/a><\/li>\n<li><a href=\"#best-practices\">Best practices<\/a><\/li>\n<li><a href=\"#practice\">Practice task<\/a><\/li>\n<li><a href=\"#faq\">FAQ<\/a><\/li>\n<\/ol>\n<\/nav>\n\n<details class=\"dd-roadmap\">\n<summary>Full Generative AI roadmap (30 parts)<\/summary>\n<h4>Beginner<\/h4>\n<ol start=\"1\">\n<li><a href=\"https:\/\/devdojo.co.in\/?p=215\">What Is Generative AI and How Do LLMs Work?<\/a><\/li>\n<li><a href=\"https:\/\/devdojo.co.in\/?p=219\">Tokens, Context Windows and Model Parameters<\/a><\/li>\n<li><a href=\"https:\/\/devdojo.co.in\/?p=223\">Your First LLM API Calls in Python and Node.js (OpenAI, Anthropic, Gemini)<\/a><\/li>\n<li><a href=\"https:\/\/devdojo.co.in\/?p=240\">Prompt Engineering Basics: Roles, Instructions and Examples<\/a><\/li>\n<li><a href=\"https:\/\/devdojo.co.in\/?p=244\">Temperature, Top-p and Controlling Model Output<\/a><\/li>\n<li><a href=\"https:\/\/devdojo.co.in\/?p=281\">Run LLMs Locally with Ollama<\/a><\/li>\n<li><a href=\"https:\/\/devdojo.co.in\/?p=284\">Streaming Responses and Building a Simple Chat UI<\/a><\/li>\n<li><a href=\"https:\/\/devdojo.co.in\/?p=303\">Structured Outputs: Getting Reliable JSON from LLMs<\/a><\/li>\n<\/ol>\n<h4>Intermediate<\/h4>\n<ol start=\"9\">\n<li>Embeddings Explained: Turning Text into Vectors <span class=\"here\">You are here<\/span><\/li>\n<li>Vector Databases: pgvector, Chroma and Pinecone <span class=\"soon\">Coming soon<\/span><\/li>\n<li>RAG Fundamentals: Retrieval-Augmented Generation <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Document Loading and Chunking Strategies <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Better RAG: Hybrid Search, Re-ranking and Query Rewriting <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Function Calling and Tool Use <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Conversation Memory and Long Context Management <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Multimodal AI: Vision, Images and Audio Inputs <span class=\"soon\">Coming soon<\/span><\/li>\n<li>How Image Generation Works: Diffusion Models Basics <span class=\"soon\">Coming soon<\/span><\/li>\n<li>LangChain vs LlamaIndex: When to Use a Framework <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Testing LLM Apps: Evals, Metrics and Test Sets <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Cutting Cost and Latency: Caching, Batching and Model Choice <span class=\"soon\">Coming soon<\/span><\/li>\n<\/ol>\n<h4>Advanced<\/h4>\n<ol start=\"21\">\n<li>Fine-Tuning vs Prompting vs RAG: Choosing the Right Tool <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Fine-Tuning an Open Model with LoRA <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Transformers Deep Dive: Attention Explained Simply <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Reasoning Models and Thinking Time <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Guardrails, Safety and Prompt Injection Defence <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Monitoring and Tracing LLM Apps in Production <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Serving Open Models: vLLM, Quantization and GPUs <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Building a Production GenAI App End to End <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Responsible AI: Privacy, Licensing and Bias <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Final Project: Build Your Own AI Knowledge Assistant <span class=\"soon\">Coming soon<\/span><\/li>\n<\/ol>\n<\/details>\n<\/div>\n\n\n\n<div class=\"dd-post\">\n<h2 id=\"what-are-embeddings\">What Are Embeddings? Turning Text into Vectors<\/h2>\n<p>Computers are great with numbers but have no idea what words mean. An <strong>embedding model<\/strong> fixes that. You give it text, and it gives you back a list of numbers, for example 1,536 numbers for OpenAI&#8217;s <code>text-embedding-3-small<\/code>. This list is called a <strong>vector<\/strong> or an <strong>embedding<\/strong>.<\/p>\n<p>Each number is like one &#8220;direction of meaning&#8221; the model has learned while reading huge amounts of text. No single number means &#8220;animal&#8221; or &#8220;food&#8221; on its own, but together they place your text at a point in a very large space. The important rule is simple:<\/p>\n<div class=\"dd-note\"><strong>Similar meaning \u2192 nearby vectors.<\/strong> &#8220;I forgot my login details&#8221; and &#8220;How do I reset my password?&#8221; share almost no words, yet their embeddings are close because they mean nearly the same thing.<\/div>\n<figure>\n<svg viewBox=\"0 0 900 300\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" role=\"img\" aria-label=\"Diagram: a sentence goes into an embedding model and comes out as a fixed-length list of numbers called a vector\">\n<rect width=\"900\" height=\"300\" fill=\"#0b1020\"\/>\n<g font-family=\"Segoe UI,Arial,sans-serif\">\n<rect x=\"30\" y=\"100\" width=\"230\" height=\"100\" rx=\"14\" fill=\"#1b2250\" stroke=\"#3178c6\" stroke-width=\"2\"\/>\n<text x=\"145\" y=\"135\" text-anchor=\"middle\" font-size=\"14\" fill=\"#aac8f0\">Your text<\/text>\n<text x=\"145\" y=\"168\" text-anchor=\"middle\" font-size=\"17\" fill=\"#fff\">&#8220;I love chai&#8221;<\/text>\n<rect x=\"330\" y=\"90\" width=\"230\" height=\"120\" rx=\"18\" fill=\"#3178c6\"\/><rect x=\"330\" y=\"90\" width=\"230\" height=\"120\" rx=\"18\" fill=\"#7c5cff\" opacity=\".45\"\/>\n<text x=\"445\" y=\"140\" text-anchor=\"middle\" font-size=\"21\" font-weight=\"700\" fill=\"#fff\">Embedding model<\/text>\n<text x=\"445\" y=\"170\" text-anchor=\"middle\" font-size=\"13\" fill=\"#e3e6ff\">same model for every text<\/text>\n<rect x=\"630\" y=\"70\" width=\"240\" height=\"160\" rx=\"14\" fill=\"#151b3b\" stroke=\"#61dafb\" stroke-width=\"2\"\/>\n<text x=\"750\" y=\"100\" text-anchor=\"middle\" font-size=\"14\" fill=\"#a8ecfb\">Vector (embedding)<\/text>\n<text x=\"750\" y=\"135\" text-anchor=\"middle\" font-family=\"Consolas,monospace\" font-size=\"16\" fill=\"#b5cea8\">[0.021, -0.113,<\/text>\n<text x=\"750\" y=\"160\" text-anchor=\"middle\" font-family=\"Consolas,monospace\" font-size=\"16\" fill=\"#b5cea8\"> 0.458, &#8230; 0.007]<\/text>\n<text x=\"750\" y=\"205\" text-anchor=\"middle\" font-size=\"13\" fill=\"#c9d1ff\">always the same length<\/text>\n<path d=\"M262 150H326M562 150H626\" stroke=\"#7c5cff\" stroke-width=\"3\" marker-end=\"url(#ar)\"\/>\n<defs><marker id=\"ar\" viewBox=\"0 0 10 10\" refX=\"8\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto\"><path d=\"M0 0L10 5L0 10z\" fill=\"#7c5cff\"\/><\/marker><\/defs>\n<text x=\"450\" y=\"270\" text-anchor=\"middle\" font-size=\"14\" fill=\"#7f89b5\">e.g. 384, 768, 1536 or 3072 numbers depending on the model<\/text>\n<\/g><\/svg>\n<figcaption>Figure 1: An embedding model turns any text into a fixed-length list of numbers (a vector).<\/figcaption>\n<\/figure>\n<p>Three facts to remember:<\/p>\n<div class=\"dd-cards\">\n<div class=\"dd-card\"><b>Fixed length<\/b>A one-word text and a full page both give the same number of values for a given model.<\/div>\n<div class=\"dd-card\"><b>Model-specific<\/b>Vectors from two different models cannot be compared. Pick one model and stick with it.<\/div>\n<div class=\"dd-card\"><b>Not generation<\/b>An embedding model does not write text. It only turns text into numbers, so it is much cheaper and faster than a chat model.<\/div>\n<\/div>\n\n<h2 id=\"why-embeddings\">Why Keyword Search Is Not Enough<\/h2>\n<p>Classic search (like SQL <code>LIKE '%password%'<\/code>) only finds documents that contain the exact words. Real users type things like &#8220;can&#8217;t log in&#8221;, &#8220;locked out&#8221; or &#8220;login nahi ho raha&#8221;. Keyword search misses all of these. Embeddings search by <em>meaning<\/em>, which unlocks:<\/p>\n<ul>\n<li><strong>Semantic search<\/strong> in docs, products or support articles.<\/li>\n<li><strong>RAG<\/strong> (Retrieval-Augmented Generation): find the right paragraphs and give them to an LLM so it answers from your data.<\/li>\n<li><strong>Recommendations<\/strong>: &#8220;people who read this also liked\u2026&#8221; by finding nearby items.<\/li>\n<li><strong>Clustering and de-duplication<\/strong>: group similar tickets or detect near-duplicate questions.<\/li>\n<li><strong>Classification<\/strong>: compare a text to example vectors for each label.<\/li>\n<\/ul>\n<figure>\n<svg viewBox=\"0 0 900 420\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" role=\"img\" aria-label=\"Diagram: a two-dimensional meaning map where animal words cluster together, vehicle words cluster together and food words cluster together; a query vector points toward the animal cluster with a small angle\">\n<rect width=\"900\" height=\"420\" fill=\"#0b1020\"\/>\n<g opacity=\".14\" stroke=\"#9fb2ff\"><path d=\"M0 70H900M0 140H900M0 210H900M0 280H900M0 350H900M150 0V420M300 0V420M450 0V420M600 0V420M750 0V420\"\/><\/g>\n<g font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"15\">\n<ellipse cx=\"230\" cy=\"110\" rx=\"140\" ry=\"70\" fill=\"#7c5cff\" opacity=\".16\"\/>\n<circle cx=\"180\" cy=\"95\" r=\"8\" fill=\"#a99bff\"\/><text x=\"194\" y=\"100\" fill=\"#e3deff\">cat<\/text>\n<circle cx=\"250\" cy=\"80\" r=\"8\" fill=\"#a99bff\"\/><text x=\"264\" y=\"85\" fill=\"#e3deff\">kitten<\/text>\n<circle cx=\"230\" cy=\"140\" r=\"8\" fill=\"#a99bff\"\/><text x=\"244\" y=\"145\" fill=\"#e3deff\">puppy<\/text>\n<ellipse cx=\"700\" cy=\"320\" rx=\"150\" ry=\"70\" fill=\"#3178c6\" opacity=\".2\"\/>\n<circle cx=\"640\" cy=\"310\" r=\"8\" fill=\"#61dafb\"\/><text x=\"654\" y=\"315\" fill=\"#d2f3fd\">car<\/text>\n<circle cx=\"730\" cy=\"290\" r=\"8\" fill=\"#61dafb\"\/><text x=\"744\" y=\"295\" fill=\"#d2f3fd\">bus<\/text>\n<circle cx=\"720\" cy=\"345\" r=\"8\" fill=\"#61dafb\"\/><text x=\"734\" y=\"350\" fill=\"#d2f3fd\">scooter<\/text>\n<ellipse cx=\"690\" cy=\"100\" rx=\"130\" ry=\"60\" fill=\"#89e051\" opacity=\".14\"\/>\n<circle cx=\"650\" cy=\"95\" r=\"8\" fill=\"#89e051\"\/><text x=\"664\" y=\"100\" fill=\"#dcf7cf\">samosa<\/text>\n<circle cx=\"730\" cy=\"115\" r=\"8\" fill=\"#89e051\"\/><text x=\"744\" y=\"120\" fill=\"#dcf7cf\">chai<\/text>\n<circle cx=\"90\" cy=\"380\" r=\"6\" fill=\"#fff\"\/>\n<path d=\"M90 380L205 125\" stroke=\"#ffd43b\" stroke-width=\"3\" marker-end=\"url(#ar2)\"\/>\n<path d=\"M90 380L630 312\" stroke=\"#ff7b72\" stroke-width=\"2\" stroke-dasharray=\"6 5\" marker-end=\"url(#ar3)\"\/>\n<defs><marker id=\"ar2\" viewBox=\"0 0 10 10\" refX=\"8\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto\"><path d=\"M0 0L10 5L0 10z\" fill=\"#ffd43b\"\/><\/marker><marker id=\"ar3\" viewBox=\"0 0 10 10\" refX=\"8\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto\"><path d=\"M0 0L10 5L0 10z\" fill=\"#ff7b72\"\/><\/marker><\/defs>\n<text x=\"100\" y=\"250\" fill=\"#ffd43b\">query: &#8220;baby cat&#8221;<\/text>\n<text x=\"100\" y=\"272\" fill=\"#ffd43b\" font-size=\"13\">small angle \u2192 high similarity<\/text>\n<text x=\"330\" y=\"372\" fill=\"#ff7b72\" font-size=\"13\">big angle \u2192 low similarity<\/text>\n<\/g><\/svg>\n<figcaption>Figure 2: Real vectors have hundreds of dimensions, but you can picture them on a 2D &#8220;meaning map&#8221;. Similar texts point in a similar direction.<\/figcaption>\n<\/figure>\n\n<h2 id=\"cosine-similarity\">Cosine Similarity Explained (with Code You Can Run)<\/h2>\n<p>To know if two vectors are &#8220;close&#8221;, we look at the <strong>angle<\/strong> between them. <strong>Cosine similarity<\/strong> gives a score:<\/p>\n<ul>\n<li><strong>Close to 1<\/strong> \u2192 same direction \u2192 very similar meaning.<\/li>\n<li><strong>Around 0<\/strong> \u2192 unrelated.<\/li>\n<li><strong>Negative<\/strong> \u2192 opposite direction (rare with modern text models).<\/li>\n<\/ul>\n<p>The formula is: <code>dot(a, b) \/ (length(a) \u00d7 length(b))<\/code>. The dot product multiplies matching numbers and adds them up. Let us try it with tiny 3-number vectors so you can see the math clearly:<\/p>\n<div class=\"dd-vsc\" role=\"group\" aria-label=\"Code editor: similarity.py, similarity.mjs\">\n<input type=\"radio\" name=\"v1\" id=\"v1f1\" class=\"f1\" checked>\n<input type=\"radio\" name=\"v1\" id=\"v1f2\" class=\"f2\">\n<input type=\"checkbox\" id=\"v1r1\" class=\"r1\">\n<input type=\"checkbox\" id=\"v1r2\" class=\"r2\">\n<div class=\"dd-vsc-bar\"><span class=\"dd-dots\"><i><\/i><i><\/i><i><\/i><\/span><span class=\"dd-vsc-title\">similarity.py \u2014 devdojo-embeddings \u2014 Visual Studio Code<\/span><\/div>\n<div class=\"dd-vsc-tabs\">\n<label for=\"v1f1\" class=\"t1\"><b class=\"ic py\">PY<\/b>similarity.py<\/label>\n<label for=\"v1f2\" class=\"t2\"><b class=\"ic js\">JS<\/b>similarity.mjs<\/label>\n<label for=\"v1r1\" class=\"run run1\" title=\"Show the output in the terminal\">\u25b6 Run<\/label>\n<label for=\"v1r2\" class=\"run run2\" title=\"Show the output in the terminal\">\u25b6 Run<\/label>\n<\/div>\n<div class=\"dd-vsc-pane p1\">\n<div class=\"dd-vsc-crumbs\">devdojo-embeddings \u203a similarity.py<\/div>\n<pre class=\"dd-vsc-code\"><code><span class=\"l\"><span class=\"kn\">import<\/span> <span class=\"n\">math<\/span><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"c\"># Tiny 3-number \"embeddings\" so you can see the math.<\/span><\/span>\n<span class=\"l\"><span class=\"c\"># Real models give 384 to 3072 numbers per text.<\/span><\/span>\n<span class=\"l\"><span class=\"n\">words<\/span> = {<\/span>\n<span class=\"l\">    <span class=\"s\">\"cat\"<\/span>:    [<span class=\"m\">0.90<\/span>, <span class=\"m\">0.80<\/span>, <span class=\"m\">0.10<\/span>],<\/span>\n<span class=\"l\">    <span class=\"s\">\"kitten\"<\/span>: [<span class=\"m\">0.85<\/span>, <span class=\"m\">0.75<\/span>, <span class=\"m\">0.20<\/span>],<\/span>\n<span class=\"l\">    <span class=\"s\">\"car\"<\/span>:    [<span class=\"m\">0.10<\/span>, <span class=\"m\">0.20<\/span>, <span class=\"m\">0.95<\/span>],<\/span>\n<span class=\"l\">}<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"k\">def<\/span> <span class=\"nf\">cosine_similarity<\/span>(<span class=\"n\">a<\/span>, <span class=\"n\">b<\/span>):<\/span>\n<span class=\"l\">    <span class=\"n\">dot<\/span> = <span class=\"nb\">sum<\/span>(<span class=\"n\">x<\/span> * <span class=\"n\">y<\/span> <span class=\"k\">for<\/span> <span class=\"n\">x<\/span>, <span class=\"n\">y<\/span> in <span class=\"nb\">zip<\/span>(<span class=\"n\">a<\/span>, <span class=\"n\">b<\/span>))<\/span>\n<span class=\"l\">    <span class=\"n\">len_a<\/span> = <span class=\"n\">math<\/span>.<span class=\"n\">sqrt<\/span>(<span class=\"nb\">sum<\/span>(<span class=\"n\">x<\/span> * <span class=\"n\">x<\/span> <span class=\"k\">for<\/span> <span class=\"n\">x<\/span> in <span class=\"n\">a<\/span>))<\/span>\n<span class=\"l\">    <span class=\"n\">len_b<\/span> = <span class=\"n\">math<\/span>.<span class=\"n\">sqrt<\/span>(<span class=\"nb\">sum<\/span>(<span class=\"n\">y<\/span> * <span class=\"n\">y<\/span> <span class=\"k\">for<\/span> <span class=\"n\">y<\/span> in <span class=\"n\">b<\/span>))<\/span>\n<span class=\"l\">    <span class=\"k\">return<\/span> <span class=\"n\">dot<\/span> \/ (<span class=\"n\">len_a<\/span> * <span class=\"n\">len_b<\/span>)<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"nb\">print<\/span>(<span class=\"s\">\"cat vs kitten:\"<\/span>, <span class=\"nb\">round<\/span>(<span class=\"n\">cosine_similarity<\/span>(<span class=\"n\">words<\/span>[<span class=\"s\">\"cat\"<\/span>], <span class=\"n\">words<\/span>[<span class=\"s\">\"kitten\"<\/span>]), <span class=\"m\">3<\/span>))<\/span>\n<span class=\"l\"><span class=\"nb\">print<\/span>(<span class=\"s\">\"cat vs car:   \"<\/span>, <span class=\"nb\">round<\/span>(<span class=\"n\">cosine_similarity<\/span>(<span class=\"n\">words<\/span>[<span class=\"s\">\"cat\"<\/span>], <span class=\"n\">words<\/span>[<span class=\"s\">\"car\"<\/span>]), <span class=\"m\">3<\/span>))<\/span><\/code><\/pre>\n<div class=\"dd-vsc-term\"><div class=\"dd-term-head\"><span>TERMINAL<\/span><span>OUTPUT<\/span><span>PROBLEMS<\/span><\/div><pre><code><span class=\"gp\">$<\/span> python similarity.py\ncat vs kitten: 0.996\ncat vs car:    0.293<\/code><\/pre><\/div>\n<div class=\"dd-vsc-status\"><span>\u2387 main<\/span><span>Ln 18, Col 1<\/span><span class=\"sp\"><\/span><span>Spaces: 4<\/span><span>UTF-8<\/span><span>LF<\/span><span>Python<\/span><\/div>\n<\/div>\n<div class=\"dd-vsc-pane p2\">\n<div class=\"dd-vsc-crumbs\">devdojo-embeddings \u203a similarity.mjs<\/div>\n<pre class=\"dd-vsc-code\"><code><span class=\"l\"><span class=\"c\">\/\/ Tiny 3-number \"embeddings\" so you can see the math.<\/span><\/span>\n<span class=\"l\"><span class=\"c\">\/\/ Real models give 384 to 3072 numbers per text.<\/span><\/span>\n<span class=\"l\"><span class=\"kc\">const<\/span> <span class=\"n\">words<\/span> = {<\/span>\n<span class=\"l\">  <span class=\"n\">cat<\/span>: [<span class=\"m\">0.9<\/span>, <span class=\"m\">0.8<\/span>, <span class=\"m\">0.1<\/span>],<\/span>\n<span class=\"l\">  <span class=\"n\">kitten<\/span>: [<span class=\"m\">0.85<\/span>, <span class=\"m\">0.75<\/span>, <span class=\"m\">0.2<\/span>],<\/span>\n<span class=\"l\">  <span class=\"n\">car<\/span>: [<span class=\"m\">0.1<\/span>, <span class=\"m\">0.2<\/span>, <span class=\"m\">0.95<\/span>],<\/span>\n<span class=\"l\">};<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"kc\">function<\/span> <span class=\"n\">cosineSimilarity<\/span>(<span class=\"n\">a<\/span>, <span class=\"n\">b<\/span>) {<\/span>\n<span class=\"l\">  <span class=\"kc\">const<\/span> <span class=\"n\">dot<\/span> = <span class=\"n\">a<\/span>.<span class=\"n\">reduce<\/span>((<span class=\"n\">sum<\/span>, <span class=\"n\">x<\/span>, <span class=\"n\">i<\/span>) =&gt; <span class=\"n\">sum<\/span> + <span class=\"n\">x<\/span> * <span class=\"n\">b<\/span>[<span class=\"n\">i<\/span>], <span class=\"m\">0<\/span>);<\/span>\n<span class=\"l\">  <span class=\"kc\">const<\/span> <span class=\"n\">lenA<\/span> = <span class=\"nb\">Math<\/span>.<span class=\"n\">hypot<\/span>(...<span class=\"n\">a<\/span>);<\/span>\n<span class=\"l\">  <span class=\"kc\">const<\/span> <span class=\"n\">lenB<\/span> = <span class=\"nb\">Math<\/span>.<span class=\"n\">hypot<\/span>(...<span class=\"n\">b<\/span>);<\/span>\n<span class=\"l\">  <span class=\"k\">return<\/span> <span class=\"n\">dot<\/span> \/ (<span class=\"n\">lenA<\/span> * <span class=\"n\">lenB<\/span>);<\/span>\n<span class=\"l\">}<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"n\">console<\/span>.<span class=\"n\">log<\/span>(<span class=\"s\">\"cat vs kitten:\"<\/span>, <span class=\"n\">cosineSimilarity<\/span>(<span class=\"n\">words<\/span>.<span class=\"n\">cat<\/span>, <span class=\"n\">words<\/span>.<span class=\"n\">kitten<\/span>).<span class=\"n\">toFixed<\/span>(<span class=\"m\">3<\/span>));<\/span>\n<span class=\"l\"><span class=\"n\">console<\/span>.<span class=\"n\">log<\/span>(<span class=\"s\">\"cat vs car:   \"<\/span>, <span class=\"n\">cosineSimilarity<\/span>(<span class=\"n\">words<\/span>.<span class=\"n\">car<\/span>, <span class=\"n\">words<\/span>.<span class=\"n\">cat<\/span>).<span class=\"n\">toFixed<\/span>(<span class=\"m\">3<\/span>));<\/span><\/code><\/pre>\n<div class=\"dd-vsc-term\"><div class=\"dd-term-head\"><span>TERMINAL<\/span><span>OUTPUT<\/span><span>PROBLEMS<\/span><\/div><pre><code><span class=\"gp\">$<\/span> node similarity.mjs\ncat vs kitten: 0.996\ncat vs car:    0.293<\/code><\/pre><\/div>\n<div class=\"dd-vsc-status\"><span>\u2387 main<\/span><span>Ln 17, Col 1<\/span><span class=\"sp\"><\/span><span>Spaces: 2<\/span><span>UTF-8<\/span><span>LF<\/span><span>JavaScript<\/span><\/div>\n<\/div>\n<\/div>\n<p class=\"dd-vsc-hint\">Click the <strong>similarity.py<\/strong> \/ <strong>similarity.mjs<\/strong> tabs to switch language, then press <strong>\u25b6 Run<\/strong> to see the real output.<\/p>\n<p>&#8220;cat&#8221; and &#8220;kitten&#8221; score <strong>0.996<\/strong> (almost the same direction), while &#8220;cat&#8221; and &#8220;car&#8221; score only <strong>0.293<\/strong>, even though the words look alike. Real embeddings work exactly the same way, just with hundreds or thousands of numbers.<\/p>\n<div class=\"dd-tip\"><strong>Shortcut:<\/strong> OpenAI embeddings are normalized to length 1. For normalized vectors, cosine similarity equals the plain dot product, so you can skip the division.<\/div>\n<\/div>\n\n\n\n<div class=\"dd-post\">\n<h2 id=\"setup\">Setup: Install the SDKs<\/h2>\n<p>We will use two options side by side: the <strong>OpenAI API<\/strong> (cloud, paid per token, very cheap for embeddings) and <strong>Ollama<\/strong> (free, runs on your laptop). Pick one or try both.<\/p>\n<div class=\"dd-vsc dd-vsc-termonly\" role=\"group\" aria-label=\"Terminal: Terminal \u2014 devdojo-embeddings\">\n<div class=\"dd-vsc-bar\"><span class=\"dd-dots\"><i><\/i><i><\/i><i><\/i><\/span><span class=\"dd-vsc-title\">Terminal \u2014 devdojo-embeddings<\/span><\/div>\n<pre class=\"dd-vsc-code\"><code><span class=\"c\"># Python<\/span>\n<span class=\"gp\">$<\/span> pip install openai ollama\n<span class=\"c\"># Node.js (in a folder with &quot;type&quot;: &quot;module&quot; or use .mjs files)<\/span>\n<span class=\"gp\">$<\/span> npm install openai ollama\n<span class=\"c\"># Cloud option: set your OpenAI key<\/span>\n<span class=\"gp\">$<\/span> export OPENAI_API_KEY=&quot;sk-...&quot;\n<span class=\"c\"># Free local option: pull an embedding model in Ollama<\/span>\n<span class=\"gp\">$<\/span> ollama pull nomic-embed-text<\/code><\/pre>\n<\/div>\n\n<h2 id=\"openai-embeddings\">Create Embeddings with the OpenAI API<\/h2>\n<p>The embeddings endpoint takes a <code>model<\/code> and an <code>input<\/code>. The input can be one string or a list of strings. Sending a list is faster and cheaper than one request per text.<\/p>\n<div class=\"dd-vsc\" role=\"group\" aria-label=\"Code editor: embed_openai.py, embed-openai.mjs\">\n<input type=\"radio\" name=\"v2\" id=\"v2f1\" class=\"f1\" checked>\n<input type=\"radio\" name=\"v2\" id=\"v2f2\" class=\"f2\">\n<input type=\"checkbox\" id=\"v2r1\" class=\"r1\">\n<input type=\"checkbox\" id=\"v2r2\" class=\"r2\">\n<div class=\"dd-vsc-bar\"><span class=\"dd-dots\"><i><\/i><i><\/i><i><\/i><\/span><span class=\"dd-vsc-title\">embed_openai.py \u2014 devdojo-embeddings \u2014 Visual Studio Code<\/span><\/div>\n<div class=\"dd-vsc-tabs\">\n<label for=\"v2f1\" class=\"t1\"><b class=\"ic py\">PY<\/b>embed_openai.py<\/label>\n<label for=\"v2f2\" class=\"t2\"><b class=\"ic js\">JS<\/b>embed-openai.mjs<\/label>\n<label for=\"v2r1\" class=\"run run1\" title=\"Show the output in the terminal\">\u25b6 Run<\/label>\n<label for=\"v2r2\" class=\"run run2\" title=\"Show the output in the terminal\">\u25b6 Run<\/label>\n<\/div>\n<div class=\"dd-vsc-pane p1\">\n<div class=\"dd-vsc-crumbs\">devdojo-embeddings \u203a embed_openai.py<\/div>\n<pre class=\"dd-vsc-code\"><code><span class=\"l\"><span class=\"kn\">from<\/span> <span class=\"n\">openai<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">OpenAI<\/span><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"n\">client<\/span> = <span class=\"n\">OpenAI<\/span>()  <span class=\"c\"># reads OPENAI_API_KEY from your environment<\/span><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"n\">texts<\/span> = [<\/span>\n<span class=\"l\">    <span class=\"s\">\"How do I reset my password?\"<\/span>,<\/span>\n<span class=\"l\">    <span class=\"s\">\"I forgot my login details\"<\/span>,<\/span>\n<span class=\"l\">    <span class=\"s\">\"What is the delivery time to Pune?\"<\/span>,<\/span>\n<span class=\"l\">]<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"n\">resp<\/span> = <span class=\"n\">client<\/span>.<span class=\"n\">embeddings<\/span>.<span class=\"n\">create<\/span>(<\/span>\n<span class=\"l\">    <span class=\"n\">model<\/span>=<span class=\"s\">\"text-embedding-3-small\"<\/span>,<\/span>\n<span class=\"l\">    <span class=\"nb\">input<\/span>=<span class=\"n\">texts<\/span>,          <span class=\"c\"># send many texts in one call<\/span><\/span>\n<span class=\"l\">)<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"k\">for<\/span> <span class=\"n\">item<\/span>, <span class=\"n\">text<\/span> in <span class=\"nb\">zip<\/span>(<span class=\"n\">resp<\/span>.<span class=\"n\">data<\/span>, <span class=\"n\">texts<\/span>):<\/span>\n<span class=\"l\">    <span class=\"n\">vec<\/span> = <span class=\"n\">item<\/span>.<span class=\"n\">embedding<\/span><\/span>\n<span class=\"l\">    <span class=\"nb\">print<\/span>(<span class=\"s\">f\"{<\/span><span class=\"n\">text<\/span><span class=\"s\">!r} -&gt; {<\/span><span class=\"nb\">len<\/span>(<span class=\"n\">vec<\/span>)<span class=\"s\">} numbers, first 3: {<\/span>[<span class=\"nb\">round<\/span>(<span class=\"n\">x<\/span>, <span class=\"m\">3<\/span>) <span class=\"k\">for<\/span> <span class=\"n\">x<\/span> in <span class=\"n\">vec<\/span>[:<span class=\"m\">3<\/span>]]<span class=\"s\">}\"<\/span>)<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"nb\">print<\/span>(<span class=\"s\">\"tokens used:\"<\/span>, <span class=\"n\">resp<\/span>.<span class=\"n\">usage<\/span>.<span class=\"n\">total_tokens<\/span>)<\/span><\/code><\/pre>\n<div class=\"dd-vsc-term\"><div class=\"dd-term-head\"><span>TERMINAL<\/span><span>OUTPUT<\/span><span>PROBLEMS<\/span><span class=\"dd-term-note\">Example output \u2013 your numbers will differ<\/span><\/div><pre><code><span class=\"gp\">$<\/span> python embed_openai.py\n&#x27;How do I reset my password?&#x27; -&gt; 1536 numbers, first 3: [0.012, -0.034, 0.051]\n&#x27;I forgot my login details&#x27; -&gt; 1536 numbers, first 3: [0.008, -0.029, 0.047]\n&#x27;What is the delivery time to Pune?&#x27; -&gt; 1536 numbers, first 3: [-0.041, 0.017, 0.006]\ntokens used: 24<\/code><\/pre><\/div>\n<div class=\"dd-vsc-status\"><span>\u2387 main<\/span><span>Ln 20, Col 1<\/span><span class=\"sp\"><\/span><span>Spaces: 4<\/span><span>UTF-8<\/span><span>LF<\/span><span>Python<\/span><\/div>\n<\/div>\n<div class=\"dd-vsc-pane p2\">\n<div class=\"dd-vsc-crumbs\">devdojo-embeddings \u203a embed-openai.mjs<\/div>\n<pre class=\"dd-vsc-code\"><code><span class=\"l\"><span class=\"k\">import<\/span> <span class=\"n\">OpenAI<\/span> <span class=\"k\">from<\/span> <span class=\"s\">\"openai\"<\/span>;<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"kc\">const<\/span> <span class=\"n\">client<\/span> = new <span class=\"n\">OpenAI<\/span>(); <span class=\"c\">\/\/ reads OPENAI_API_KEY from your environment<\/span><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"kc\">const<\/span> <span class=\"n\">texts<\/span> = [<\/span>\n<span class=\"l\">  <span class=\"s\">\"How do I reset my password?\"<\/span>,<\/span>\n<span class=\"l\">  <span class=\"s\">\"I forgot my login details\"<\/span>,<\/span>\n<span class=\"l\">  <span class=\"s\">\"What is the delivery time to Pune?\"<\/span>,<\/span>\n<span class=\"l\">];<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"kc\">const<\/span> <span class=\"n\">resp<\/span> = <span class=\"k\">await<\/span> <span class=\"n\">client<\/span>.<span class=\"n\">embeddings<\/span>.<span class=\"n\">create<\/span>({<\/span>\n<span class=\"l\">  <span class=\"n\">model<\/span>: <span class=\"s\">\"text-embedding-3-small\"<\/span>,<\/span>\n<span class=\"l\">  <span class=\"n\">input<\/span>: <span class=\"n\">texts<\/span>, <span class=\"c\">\/\/ send many texts in one call<\/span><\/span>\n<span class=\"l\">});<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"n\">resp<\/span>.<span class=\"n\">data<\/span>.<span class=\"n\">forEach<\/span>((<span class=\"n\">item<\/span>, <span class=\"n\">i<\/span>) =&gt; {<\/span>\n<span class=\"l\">  <span class=\"kc\">const<\/span> <span class=\"n\">vec<\/span> = <span class=\"n\">item<\/span>.<span class=\"n\">embedding<\/span>;<\/span>\n<span class=\"l\">  <span class=\"kc\">const<\/span> <span class=\"n\">first3<\/span> = <span class=\"n\">vec<\/span>.<span class=\"n\">slice<\/span>(<span class=\"m\">0<\/span>, <span class=\"m\">3<\/span>).<span class=\"n\">map<\/span>((<span class=\"n\">x<\/span>) =&gt; <span class=\"n\">x<\/span>.<span class=\"n\">toFixed<\/span>(<span class=\"m\">3<\/span>));<\/span>\n<span class=\"l\">  <span class=\"n\">console<\/span>.<span class=\"n\">log<\/span>(<span class=\"s\">`\"${<\/span><span class=\"n\">texts<\/span>[<span class=\"n\">i<\/span>]<span class=\"s\">}\" -&gt; ${<\/span><span class=\"n\">vec<\/span>.<span class=\"n\">length<\/span><span class=\"s\">} numbers, first 3: [${<\/span><span class=\"n\">first3<\/span><span class=\"s\">}]`<\/span>);<\/span>\n<span class=\"l\">});<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"n\">console<\/span>.<span class=\"n\">log<\/span>(<span class=\"s\">\"tokens used:\"<\/span>, <span class=\"n\">resp<\/span>.<span class=\"n\">usage<\/span>.<span class=\"n\">total_tokens<\/span>);<\/span><\/code><\/pre>\n<div class=\"dd-vsc-term\"><div class=\"dd-term-head\"><span>TERMINAL<\/span><span>OUTPUT<\/span><span>PROBLEMS<\/span><span class=\"dd-term-note\">Example output \u2013 your numbers will differ<\/span><\/div><pre><code><span class=\"gp\">$<\/span> node embed-openai.mjs\n&quot;How do I reset my password?&quot; -&gt; 1536 numbers, first 3: [0.012, -0.034, 0.051]\n&quot;I forgot my login details&quot; -&gt; 1536 numbers, first 3: [0.008, -0.029, 0.047]\n&quot;What is the delivery time to Pune?&quot; -&gt; 1536 numbers, first 3: [-0.041, 0.017, 0.006]\ntokens used: 24<\/code><\/pre><\/div>\n<div class=\"dd-vsc-status\"><span>\u2387 main<\/span><span>Ln 22, Col 1<\/span><span class=\"sp\"><\/span><span>Spaces: 2<\/span><span>UTF-8<\/span><span>LF<\/span><span>JavaScript<\/span><\/div>\n<\/div>\n<\/div>\n<p class=\"dd-vsc-hint\">Switch between the Python and Node.js tabs and press <strong>\u25b6 Run<\/strong>. The output shown is an example: real values depend on the model.<\/p>\n<p>Notice that every text gives <strong>1,536 numbers<\/strong>, no matter how long it is. The first two sentences (password and login) will also have similar values overall, while the delivery question will look different. You never read these numbers yourself; you only compare them. See the official <a href=\"https:\/\/platform.openai.com\/docs\/guides\/embeddings\" target=\"_blank\" rel=\"noopener\">OpenAI embeddings guide<\/a> for the full list of options.<\/p>\n<div class=\"dd-warn\"><strong>Tip:<\/strong> embedding models have an input limit (about 8,000 tokens for OpenAI&#8217;s models). For long documents, split them into smaller chunks first. We cover chunking in Part 12.<\/div>\n\n<h2 id=\"choose-model\">Choosing an Embedding Model<\/h2>\n<p>There is no single &#8220;best&#8221; model. Click the tabs to compare four popular choices:<\/p>\n<div class=\"dd-tabs\" role=\"group\" aria-label=\"Compare popular embedding models\">\n<input type=\"radio\" name=\"emtab\" id=\"ti1\" checked><input type=\"radio\" name=\"emtab\" id=\"ti2\"><input type=\"radio\" name=\"emtab\" id=\"ti3\"><input type=\"radio\" name=\"emtab\" id=\"ti4\">\n<div class=\"labels\"><label for=\"ti1\">OpenAI small<\/label><label for=\"ti2\">OpenAI large<\/label><label for=\"ti3\">Ollama (local)<\/label><label for=\"ti4\">Sentence Transformers<\/label><\/div>\n<div class=\"panel p1\"><p><strong><code>text-embedding-3-small<\/code><\/strong> \u2014 the best default for most apps. Cheap, fast and good quality.<\/p>\n<div class=\"dd-dims\"><div><b>Numbers per text<\/b>1536 (can shorten)<div class=\"dd-bar\"><i style=\"width:50%\"><\/i><\/div><\/div><div><b>Runs<\/b>OpenAI cloud API<\/div><div><b>Good for<\/b>Search, RAG, FAQ bots<\/div><\/div><\/div>\n<div class=\"panel p2\"><p><strong><code>text-embedding-3-large<\/code><\/strong> \u2014 higher accuracy for hard or multilingual search, at a higher price and twice the storage.<\/p>\n<div class=\"dd-dims\"><div><b>Numbers per text<\/b>3072 (can shorten)<div class=\"dd-bar\"><i style=\"width:100%\"><\/i><\/div><\/div><div><b>Runs<\/b>OpenAI cloud API<\/div><div><b>Good for<\/b>When small is not accurate enough<\/div><\/div><\/div>\n<div class=\"panel p3\"><p><strong><code>nomic-embed-text<\/code><\/strong> in Ollama \u2014 free, private, runs on your laptop. Great for learning and for data that must not leave your machine.<\/p>\n<div class=\"dd-dims\"><div><b>Numbers per text<\/b>768<div class=\"dd-bar\"><i style=\"width:25%\"><\/i><\/div><\/div><div><b>Runs<\/b>Your computer (Ollama)<\/div><div><b>Good for<\/b>Offline apps, privacy, zero cost<\/div><\/div><\/div>\n<div class=\"panel p4\"><p><strong><code>all-MiniLM-L6-v2<\/code><\/strong> via the Sentence Transformers Python library \u2014 a tiny, popular open model. Fast even on CPU.<\/p>\n<div class=\"dd-dims\"><div><b>Numbers per text<\/b>384<div class=\"dd-bar\"><i style=\"width:12.5%\"><\/i><\/div><\/div><div><b>Runs<\/b>Your Python process<\/div><div><b>Good for<\/b>Prototypes, English text, small servers<\/div><\/div><\/div>\n<\/div>\n<p>For most beginners: start with <code>text-embedding-3-small<\/code> if you already use OpenAI, or <code>nomic-embed-text<\/code> in Ollama if you want everything free and local. Leaderboards like the <a href=\"https:\/\/huggingface.co\/spaces\/mteb\/leaderboard\" target=\"_blank\" rel=\"noopener\">MTEB leaderboard<\/a> rank many more models if you need to go deeper.<\/p>\n\n<h2 id=\"semantic-search\">Build a Semantic Search in Python and Node.js<\/h2>\n<p>Now let us put it all together. We will search a tiny &#8220;help centre&#8221; by meaning. The flow has four steps:<\/p>\n<figure>\n<svg viewBox=\"0 0 900 250\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" role=\"img\" aria-label=\"Diagram of semantic search: step 1 embed all documents once and store them, step 2 embed the user question, step 3 compare with cosine similarity, step 4 return the top matches\">\n<rect width=\"900\" height=\"250\" fill=\"#0b1020\"\/>\n<g font-family=\"Segoe UI,Arial,sans-serif\" text-anchor=\"middle\">\n<g><rect x=\"20\" y=\"70\" width=\"190\" height=\"110\" rx=\"14\" fill=\"#1b2250\" stroke=\"#3178c6\" stroke-width=\"2\"\/><text x=\"115\" y=\"108\" font-size=\"15\" fill=\"#a99bff\" font-weight=\"700\">1. Embed docs<\/text><text x=\"115\" y=\"135\" font-size=\"13\" fill=\"#e8ebff\">once, then store<\/text><text x=\"115\" y=\"156\" font-size=\"13\" fill=\"#e8ebff\">the vectors<\/text><\/g>\n<g><rect x=\"240\" y=\"70\" width=\"190\" height=\"110\" rx=\"14\" fill=\"#1b2250\" stroke=\"#3178c6\" stroke-width=\"2\"\/><text x=\"335\" y=\"108\" font-size=\"15\" fill=\"#a99bff\" font-weight=\"700\">2. Embed question<\/text><text x=\"335\" y=\"135\" font-size=\"13\" fill=\"#e8ebff\">same model,<\/text><text x=\"335\" y=\"156\" font-size=\"13\" fill=\"#e8ebff\">every request<\/text><\/g>\n<g><rect x=\"460\" y=\"70\" width=\"190\" height=\"110\" rx=\"14\" fill=\"#1b2250\" stroke=\"#3178c6\" stroke-width=\"2\"\/><text x=\"555\" y=\"108\" font-size=\"15\" fill=\"#a99bff\" font-weight=\"700\">3. Compare<\/text><text x=\"555\" y=\"135\" font-size=\"13\" fill=\"#e8ebff\">cosine similarity<\/text><text x=\"555\" y=\"156\" font-size=\"13\" fill=\"#e8ebff\">with each doc<\/text><\/g>\n<g><rect x=\"680\" y=\"70\" width=\"200\" height=\"110\" rx=\"14\" fill=\"#3178c6\"\/><text x=\"780\" y=\"108\" font-size=\"15\" fill=\"#fff\" font-weight=\"700\">4. Top matches<\/text><text x=\"780\" y=\"135\" font-size=\"13\" fill=\"#eef\">highest scores win<\/text><text x=\"780\" y=\"156\" font-size=\"13\" fill=\"#eef\">(\u2192 RAG in Part 11)<\/text><\/g>\n<path d=\"M212 125H236M432 125H456M652 125H676\" stroke=\"#7c5cff\" stroke-width=\"3\"\/>\n<text x=\"450\" y=\"225\" font-size=\"14\" fill=\"#7f89b5\">Meaning-based search: &#8220;can&#8217;t log in&#8221; finds &#8220;reset your password&#8221; with zero shared words<\/text>\n<\/g><\/svg>\n<figcaption>Figure 3: The four steps of semantic search with embeddings.<\/figcaption>\n<\/figure>\n<p>This example uses <strong>Ollama<\/strong> so it is completely free. First run <code>ollama pull nomic-embed-text<\/code>, then:<\/p>\n<div class=\"dd-vsc\" role=\"group\" aria-label=\"Code editor: semantic_search.py, semantic-search.mjs\">\n<input type=\"radio\" name=\"v3\" id=\"v3f1\" class=\"f1\" checked>\n<input type=\"radio\" name=\"v3\" id=\"v3f2\" class=\"f2\">\n<input type=\"checkbox\" id=\"v3r1\" class=\"r1\">\n<input type=\"checkbox\" id=\"v3r2\" class=\"r2\">\n<div class=\"dd-vsc-bar\"><span class=\"dd-dots\"><i><\/i><i><\/i><i><\/i><\/span><span class=\"dd-vsc-title\">semantic_search.py \u2014 devdojo-embeddings \u2014 Visual Studio Code<\/span><\/div>\n<div class=\"dd-vsc-tabs\">\n<label for=\"v3f1\" class=\"t1\"><b class=\"ic py\">PY<\/b>semantic_search.py<\/label>\n<label for=\"v3f2\" class=\"t2\"><b class=\"ic js\">JS<\/b>semantic-search.mjs<\/label>\n<label for=\"v3r1\" class=\"run run1\" title=\"Show the output in the terminal\">\u25b6 Run<\/label>\n<label for=\"v3r2\" class=\"run run2\" title=\"Show the output in the terminal\">\u25b6 Run<\/label>\n<\/div>\n<div class=\"dd-vsc-pane p1\">\n<div class=\"dd-vsc-crumbs\">devdojo-embeddings \u203a semantic_search.py<\/div>\n<pre class=\"dd-vsc-code\"><code><span class=\"l\"><span class=\"kn\">import<\/span> <span class=\"n\">ollama<\/span>  <span class=\"c\"># pip install ollama  (and run: ollama pull nomic-embed-text)<\/span><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"n\">MODEL<\/span> = <span class=\"s\">\"nomic-embed-text\"<\/span><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"n\">docs<\/span> = [<\/span>\n<span class=\"l\">    <span class=\"s\">\"To reset your password, click 'Forgot password' on the login page.\"<\/span>,<\/span>\n<span class=\"l\">    <span class=\"s\">\"We deliver to Pune, Mumbai and Delhi within 3 to 5 working days.\"<\/span>,<\/span>\n<span class=\"l\">    <span class=\"s\">\"You can cancel an order from the My Orders page before it ships.\"<\/span>,<\/span>\n<span class=\"l\">    <span class=\"s\">\"Our support team is available on chat from 9 AM to 9 PM IST.\"<\/span>,<\/span>\n<span class=\"l\">]<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"k\">def<\/span> <span class=\"nf\">embed<\/span>(<span class=\"n\">texts<\/span>):<\/span>\n<span class=\"l\">    <span class=\"k\">return<\/span> <span class=\"n\">ollama<\/span>.<span class=\"n\">embed<\/span>(<span class=\"n\">model<\/span>=<span class=\"n\">MODEL<\/span>, <span class=\"nb\">input<\/span>=<span class=\"n\">texts<\/span>)[<span class=\"s\">\"embeddings\"<\/span>]<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"k\">def<\/span> <span class=\"nf\">cosine<\/span>(<span class=\"n\">a<\/span>, <span class=\"n\">b<\/span>):<\/span>\n<span class=\"l\">    <span class=\"n\">dot<\/span> = <span class=\"nb\">sum<\/span>(<span class=\"n\">x<\/span> * <span class=\"n\">y<\/span> <span class=\"k\">for<\/span> <span class=\"n\">x<\/span>, <span class=\"n\">y<\/span> in <span class=\"nb\">zip<\/span>(<span class=\"n\">a<\/span>, <span class=\"n\">b<\/span>))<\/span>\n<span class=\"l\">    <span class=\"k\">return<\/span> <span class=\"n\">dot<\/span> \/ ((<span class=\"nb\">sum<\/span>(<span class=\"n\">x<\/span> * <span class=\"n\">x<\/span> <span class=\"k\">for<\/span> <span class=\"n\">x<\/span> in <span class=\"n\">a<\/span>) ** <span class=\"m\">0.5<\/span>) * (<span class=\"nb\">sum<\/span>(<span class=\"n\">y<\/span> * <span class=\"n\">y<\/span> <span class=\"k\">for<\/span> <span class=\"n\">y<\/span> in <span class=\"n\">b<\/span>) ** <span class=\"m\">0.5<\/span>))<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"n\">doc_vectors<\/span> = <span class=\"n\">embed<\/span>(<span class=\"n\">docs<\/span>)                 <span class=\"c\"># 1. embed every document once<\/span><\/span>\n<span class=\"l\"><span class=\"n\">question<\/span> = <span class=\"s\">\"I can't log in to my account\"<\/span><\/span>\n<span class=\"l\"><span class=\"n\">query_vector<\/span> = <span class=\"n\">embed<\/span>([<span class=\"n\">question<\/span>])[<span class=\"m\">0<\/span>]       <span class=\"c\"># 2. embed the question<\/span><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"n\">scored<\/span> = [(<span class=\"n\">cosine<\/span>(<span class=\"n\">query_vector<\/span>, <span class=\"n\">v<\/span>), <span class=\"n\">d<\/span>) <span class=\"k\">for<\/span> <span class=\"n\">v<\/span>, <span class=\"n\">d<\/span> in <span class=\"nb\">zip<\/span>(<span class=\"n\">doc_vectors<\/span>, <span class=\"n\">docs<\/span>)]<\/span>\n<span class=\"l\"><span class=\"n\">scored<\/span>.<span class=\"n\">sort<\/span>(<span class=\"n\">reverse<\/span>=<span class=\"kc\">True<\/span>)                 <span class=\"c\"># 3. highest similarity first<\/span><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"nb\">print<\/span>(<span class=\"s\">\"Question:\"<\/span>, <span class=\"n\">question<\/span>)<\/span>\n<span class=\"l\"><span class=\"k\">for<\/span> <span class=\"n\">score<\/span>, <span class=\"n\">doc<\/span> in <span class=\"n\">scored<\/span>[:<span class=\"m\">2<\/span>]:<\/span>\n<span class=\"l\">    <span class=\"nb\">print<\/span>(<span class=\"s\">f\"{<\/span><span class=\"n\">score<\/span><span class=\"s\">:.2f}  {<\/span><span class=\"n\">doc<\/span><span class=\"s\">}\"<\/span>)<\/span><\/code><\/pre>\n<div class=\"dd-vsc-term\"><div class=\"dd-term-head\"><span>TERMINAL<\/span><span>OUTPUT<\/span><span>PROBLEMS<\/span><span class=\"dd-term-note\">Example output \u2013 scores vary by model<\/span><\/div><pre><code><span class=\"gp\">$<\/span> python semantic_search.py\nQuestion: I can&#x27;t log in to my account\n0.71  To reset your password, click &#x27;Forgot password&#x27; on the login page.\n0.52  Our support team is available on chat from 9 AM to 9 PM IST.<\/code><\/pre><\/div>\n<div class=\"dd-vsc-status\"><span>\u2387 main<\/span><span>Ln 28, Col 1<\/span><span class=\"sp\"><\/span><span>Spaces: 4<\/span><span>UTF-8<\/span><span>LF<\/span><span>Python<\/span><\/div>\n<\/div>\n<div class=\"dd-vsc-pane p2\">\n<div class=\"dd-vsc-crumbs\">devdojo-embeddings \u203a semantic-search.mjs<\/div>\n<pre class=\"dd-vsc-code\"><code><span class=\"l\"><span class=\"k\">import<\/span> <span class=\"n\">ollama<\/span> <span class=\"k\">from<\/span> <span class=\"s\">\"ollama\"<\/span>; <span class=\"c\">\/\/ npm i ollama  (and run: ollama pull nomic-embed-text)<\/span><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"kc\">const<\/span> <span class=\"n\">MODEL<\/span> = <span class=\"s\">\"nomic-embed-text\"<\/span>;<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"kc\">const<\/span> <span class=\"n\">docs<\/span> = [<\/span>\n<span class=\"l\">  <span class=\"s\">\"To reset your password, click 'Forgot password' on the login page.\"<\/span>,<\/span>\n<span class=\"l\">  <span class=\"s\">\"We deliver to Pune, Mumbai and Delhi within 3 to 5 working days.\"<\/span>,<\/span>\n<span class=\"l\">  <span class=\"s\">\"You can cancel an order from the My Orders page before it ships.\"<\/span>,<\/span>\n<span class=\"l\">  <span class=\"s\">\"Our support team is available on chat from 9 AM to 9 PM IST.\"<\/span>,<\/span>\n<span class=\"l\">];<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"kc\">const<\/span> <span class=\"n\">embed<\/span> = <span class=\"k\">async<\/span> (<span class=\"n\">texts<\/span>) =&gt;<\/span>\n<span class=\"l\">  (<span class=\"k\">await<\/span> <span class=\"n\">ollama<\/span>.<span class=\"n\">embed<\/span>({ <span class=\"n\">model<\/span>: <span class=\"n\">MODEL<\/span>, <span class=\"n\">input<\/span>: <span class=\"n\">texts<\/span> })).<span class=\"n\">embeddings<\/span>;<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"kc\">const<\/span> <span class=\"n\">cosine<\/span> = (<span class=\"n\">a<\/span>, <span class=\"n\">b<\/span>) =&gt; {<\/span>\n<span class=\"l\">  <span class=\"kc\">const<\/span> <span class=\"n\">dot<\/span> = <span class=\"n\">a<\/span>.<span class=\"n\">reduce<\/span>((<span class=\"n\">s<\/span>, <span class=\"n\">x<\/span>, <span class=\"n\">i<\/span>) =&gt; <span class=\"n\">s<\/span> + <span class=\"n\">x<\/span> * <span class=\"n\">b<\/span>[<span class=\"n\">i<\/span>], <span class=\"m\">0<\/span>);<\/span>\n<span class=\"l\">  <span class=\"k\">return<\/span> <span class=\"n\">dot<\/span> \/ (<span class=\"nb\">Math<\/span>.<span class=\"n\">hypot<\/span>(...<span class=\"n\">a<\/span>) * <span class=\"nb\">Math<\/span>.<span class=\"n\">hypot<\/span>(...<span class=\"n\">b<\/span>));<\/span>\n<span class=\"l\">};<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"kc\">const<\/span> <span class=\"n\">docVectors<\/span> = <span class=\"k\">await<\/span> <span class=\"n\">embed<\/span>(<span class=\"n\">docs<\/span>);          <span class=\"c\">\/\/ 1. embed every document once<\/span><\/span>\n<span class=\"l\"><span class=\"kc\">const<\/span> <span class=\"n\">question<\/span> = <span class=\"s\">\"I can't log in to my account\"<\/span>;<\/span>\n<span class=\"l\"><span class=\"kc\">const<\/span> [<span class=\"n\">queryVector<\/span>] = <span class=\"k\">await<\/span> <span class=\"n\">embed<\/span>([<span class=\"n\">question<\/span>]); <span class=\"c\">\/\/ 2. embed the question<\/span><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"kc\">const<\/span> <span class=\"n\">scored<\/span> = <span class=\"n\">docVectors<\/span><\/span>\n<span class=\"l\">  .<span class=\"n\">map<\/span>((<span class=\"n\">v<\/span>, <span class=\"n\">i<\/span>) =&gt; ({ <span class=\"n\">score<\/span>: <span class=\"n\">cosine<\/span>(<span class=\"n\">queryVector<\/span>, <span class=\"n\">v<\/span>), <span class=\"n\">doc<\/span>: <span class=\"n\">docs<\/span>[<span class=\"n\">i<\/span>] }))<\/span>\n<span class=\"l\">  .<span class=\"n\">sort<\/span>((<span class=\"n\">a<\/span>, <span class=\"n\">b<\/span>) =&gt; <span class=\"n\">b<\/span>.<span class=\"n\">score<\/span> - <span class=\"n\">a<\/span>.<span class=\"n\">score<\/span>);         <span class=\"c\">\/\/ 3. highest similarity first<\/span><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"n\">console<\/span>.<span class=\"n\">log<\/span>(<span class=\"s\">\"Question:\"<\/span>, <span class=\"n\">question<\/span>);<\/span>\n<span class=\"l\"><span class=\"k\">for<\/span> (<span class=\"kc\">const<\/span> { <span class=\"n\">score<\/span>, <span class=\"n\">doc<\/span> } <span class=\"k\">of<\/span> <span class=\"n\">scored<\/span>.<span class=\"n\">slice<\/span>(<span class=\"m\">0<\/span>, <span class=\"m\">2<\/span>)) {<\/span>\n<span class=\"l\">  <span class=\"n\">console<\/span>.<span class=\"n\">log<\/span>(<span class=\"s\">`${<\/span><span class=\"n\">score<\/span>.<span class=\"n\">toFixed<\/span>(<span class=\"m\">2<\/span>)<span class=\"s\">}  ${<\/span><span class=\"n\">doc<\/span><span class=\"s\">}`<\/span>);<\/span>\n<span class=\"l\">}<\/span><\/code><\/pre>\n<div class=\"dd-vsc-term\"><div class=\"dd-term-head\"><span>TERMINAL<\/span><span>OUTPUT<\/span><span>PROBLEMS<\/span><span class=\"dd-term-note\">Example output \u2013 scores vary by model<\/span><\/div><pre><code><span class=\"gp\">$<\/span> node semantic-search.mjs\nQuestion: I can&#x27;t log in to my account\n0.71  To reset your password, click &#x27;Forgot password&#x27; on the login page.\n0.52  Our support team is available on chat from 9 AM to 9 PM IST.<\/code><\/pre><\/div>\n<div class=\"dd-vsc-status\"><span>\u2387 main<\/span><span>Ln 31, Col 1<\/span><span class=\"sp\"><\/span><span>Spaces: 2<\/span><span>UTF-8<\/span><span>LF<\/span><span>JavaScript<\/span><\/div>\n<\/div>\n<\/div>\n<p class=\"dd-vsc-hint\">Click the tabs to compare Python and Node.js. Press <strong>\u25b6 Run<\/strong> to see example output.<\/p>\n<p>The question &#8220;I can&#8217;t log in to my account&#8221; does not contain the words &#8220;reset&#8221; or &#8220;password&#8221;, yet the password article comes first. That is semantic search. In a real app you would store <code>doc_vectors<\/code> in a <strong>vector database<\/strong> instead of a Python list, which is exactly what Part 10 covers. If you want to see a full working version with PostgreSQL, our <a href=\"https:\/\/devdojo.co.in\/?p=198\">RAG tutorial with Python and pgvector<\/a> shows where this leads.<\/p>\n<div class=\"dd-tip\"><strong>Want to switch to OpenAI?<\/strong> Replace the <code>embed()<\/code> function body with <code>client.embeddings.create(model=\"text-embedding-3-small\", input=texts)<\/code> and return <code>[d.embedding for d in resp.data]<\/code>. The rest of the code stays the same.<\/div>\n<\/div>\n\n\n\n<div class=\"dd-post\">\n<h2 id=\"cache\">Save Money: Cache and Shorten Your Vectors<\/h2>\n<p>Two easy wins for real projects:<\/p>\n<ol>\n<li><strong>Cache embeddings.<\/strong> The same text with the same model always gives the same vector, so store it and never pay twice.<\/li>\n<li><strong>Shorten vectors.<\/strong> OpenAI&#8217;s <code>text-embedding-3<\/code> models accept a <code>dimensions<\/code> parameter. A 512-number vector uses one third of the storage of 1,536 numbers and is faster to compare, with only a small loss in quality.<\/li>\n<\/ol>\n<div class=\"dd-vsc\" role=\"group\" aria-label=\"Code editor: cache_embeddings.py\">\n<input type=\"radio\" name=\"v4\" id=\"v4f1\" class=\"f1\" checked>\n<input type=\"checkbox\" id=\"v4r1\" class=\"r1\">\n<div class=\"dd-vsc-bar\"><span class=\"dd-dots\"><i><\/i><i><\/i><i><\/i><\/span><span class=\"dd-vsc-title\">cache_embeddings.py \u2014 devdojo-embeddings \u2014 Visual Studio Code<\/span><\/div>\n<div class=\"dd-vsc-tabs\">\n<label for=\"v4f1\" class=\"t1\"><b class=\"ic py\">PY<\/b>cache_embeddings.py<\/label>\n<label for=\"v4r1\" class=\"run run1\" title=\"Show the output in the terminal\">\u25b6 Run<\/label>\n<\/div>\n<div class=\"dd-vsc-pane p1\">\n<div class=\"dd-vsc-crumbs\">devdojo-embeddings \u203a cache_embeddings.py<\/div>\n<pre class=\"dd-vsc-code\"><code><span class=\"l\"><span class=\"kn\">import<\/span> <span class=\"n\">json<\/span>, <span class=\"n\">hashlib<\/span><\/span>\n<span class=\"l\"><span class=\"kn\">from<\/span> <span class=\"n\">pathlib<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">Path<\/span><\/span>\n<span class=\"l\"><span class=\"kn\">from<\/span> <span class=\"n\">openai<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">OpenAI<\/span><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"n\">client<\/span> = <span class=\"n\">OpenAI<\/span>()<\/span>\n<span class=\"l\"><span class=\"n\">CACHE<\/span> = <span class=\"n\">Path<\/span>(<span class=\"s\">\"embeddings_cache.json\"<\/span>)<\/span>\n<span class=\"l\"><span class=\"n\">cache<\/span> = <span class=\"n\">json<\/span>.<span class=\"n\">loads<\/span>(<span class=\"n\">CACHE<\/span>.<span class=\"n\">read_text<\/span>()) <span class=\"k\">if<\/span> <span class=\"n\">CACHE<\/span>.<span class=\"n\">exists<\/span>() <span class=\"k\">else<\/span> {}<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"k\">def<\/span> <span class=\"nf\">embed_cached<\/span>(<span class=\"n\">text<\/span>, <span class=\"n\">model<\/span>=<span class=\"s\">\"text-embedding-3-small\"<\/span>, <span class=\"n\">dims<\/span>=<span class=\"m\">512<\/span>):<\/span>\n<span class=\"l\">    <span class=\"n\">key<\/span> = <span class=\"n\">hashlib<\/span>.<span class=\"n\">sha256<\/span>(<span class=\"s\">f\"{<\/span><span class=\"n\">model<\/span><span class=\"s\">}:{<\/span><span class=\"n\">dims<\/span><span class=\"s\">}:{<\/span><span class=\"n\">text<\/span><span class=\"s\">}\"<\/span>.<span class=\"n\">encode<\/span>()).<span class=\"n\">hexdigest<\/span>()<\/span>\n<span class=\"l\">    <span class=\"k\">if<\/span> <span class=\"n\">key<\/span> not in <span class=\"n\">cache<\/span>:                       <span class=\"c\"># only pay for new text<\/span><\/span>\n<span class=\"l\">        <span class=\"n\">resp<\/span> = <span class=\"n\">client<\/span>.<span class=\"n\">embeddings<\/span>.<span class=\"n\">create<\/span>(<span class=\"n\">model<\/span>=<span class=\"n\">model<\/span>, <span class=\"nb\">input<\/span>=<span class=\"n\">text<\/span>, <span class=\"n\">dimensions<\/span>=<span class=\"n\">dims<\/span>)<\/span>\n<span class=\"l\">        <span class=\"n\">cache<\/span>[<span class=\"n\">key<\/span>] = <span class=\"n\">resp<\/span>.<span class=\"n\">data<\/span>[<span class=\"m\">0<\/span>].<span class=\"n\">embedding<\/span><\/span>\n<span class=\"l\">        <span class=\"n\">CACHE<\/span>.<span class=\"n\">write_text<\/span>(<span class=\"n\">json<\/span>.<span class=\"n\">dumps<\/span>(<span class=\"n\">cache<\/span>))<\/span>\n<span class=\"l\">    <span class=\"k\">return<\/span> <span class=\"n\">cache<\/span>[<span class=\"n\">key<\/span>]<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"n\">vec<\/span> = <span class=\"n\">embed_cached<\/span>(<span class=\"s\">\"Embeddings turn text into numbers\"<\/span>)<\/span>\n<span class=\"l\"><span class=\"nb\">print<\/span>(<span class=\"nb\">len<\/span>(<span class=\"n\">vec<\/span>), <span class=\"s\">\"numbers (shortened with dimensions=512)\"<\/span>)<\/span><\/code><\/pre>\n<div class=\"dd-vsc-term\"><div class=\"dd-term-head\"><span>TERMINAL<\/span><span>OUTPUT<\/span><span>PROBLEMS<\/span><span class=\"dd-term-note\">Example output<\/span><\/div><pre><code><span class=\"gp\">$<\/span> python cache_embeddings.py\n512 numbers (shortened with dimensions=512)<\/code><\/pre><\/div>\n<div class=\"dd-vsc-status\"><span>\u2387 main<\/span><span>Ln 18, Col 1<\/span><span class=\"sp\"><\/span><span>Spaces: 4<\/span><span>UTF-8<\/span><span>LF<\/span><span>Python<\/span><\/div>\n<\/div>\n<\/div>\n<p class=\"dd-vsc-hint\">Press <strong>\u25b6 Run<\/strong> to see example output. Run it twice: the second run reads from <code>embeddings_cache.json<\/code> and makes no API call.<\/p>\n<div class=\"dd-note\">The cache key includes the model name and dimensions. If you change either one, you must re-embed everything, because vectors from different settings cannot be compared.<\/div>\n\n<h2 id=\"errors\">Common Errors and Fixes<\/h2>\n<div class=\"dd-tablewrap\"><table>\n<thead><tr><th>Problem<\/th><th>Why it happens<\/th><th>Fix<\/th><\/tr><\/thead>\n<tbody>\n<tr><td>Search results look random<\/td><td>Documents and question were embedded with different models<\/td><td>Use exactly the same model (and <code>dimensions<\/code>) for both<\/td><\/tr>\n<tr><td><code>ValueError<\/code> \/ shapes do not match<\/td><td>Comparing a 768-number vector with a 1,536-number vector<\/td><td>Check <code>len(vec)<\/code>; re-embed old data after a model change<\/td><\/tr>\n<tr><td>&#8220;maximum context length&#8221; error<\/td><td>Text is longer than the model&#8217;s input limit<\/td><td>Split long documents into chunks before embedding<\/td><\/tr>\n<tr><td><code>model \"nomic-embed-text\" not found<\/code><\/td><td>Model not downloaded in Ollama<\/td><td>Run <code>ollama pull nomic-embed-text<\/code><\/td><\/tr>\n<tr><td>429 rate limit errors<\/td><td>One API call per text in a loop<\/td><td>Send a list in <code>input<\/code> (batching) and add retries<\/td><\/tr>\n<tr><td>All scores are very close (e.g. 0.70\u20130.80)<\/td><td>Normal for many models; scores are relative, not absolute<\/td><td>Rank by score and test a threshold on your own data<\/td><\/tr>\n<\/tbody><\/table><\/div>\n\n<h2 id=\"best-practices\">Best Practices for Embeddings<\/h2>\n<ul>\n<li><strong>One model per index.<\/strong> Store the model name next to each vector so you know what you are comparing.<\/li>\n<li><strong>Clean your text first.<\/strong> Remove HTML tags, menus and repeated footers before embedding.<\/li>\n<li><strong>Batch requests.<\/strong> Embed hundreds of texts per call instead of one at a time.<\/li>\n<li><strong>Embed once, search many times.<\/strong> Documents are embedded when they change, not on every search.<\/li>\n<li><strong>Keep the original text.<\/strong> A vector cannot be turned back into text, so always store the text (or an ID) with it.<\/li>\n<li><strong>Evaluate on real questions.<\/strong> Write 20 real user questions with the expected answer and check how often the right document is in the top 3.<\/li>\n<li><strong>Mind privacy.<\/strong> Embeddings are derived from your data. Use a local model if the data must not leave your servers.<\/li>\n<\/ul>\n\n<h2 id=\"practice\">Practice Task<\/h2>\n<div class=\"dd-tip\">\n<p><strong>Build a &#8220;similar questions&#8221; finder.<\/strong><\/p>\n<ol>\n<li>Write 10 short FAQ questions about any topic (your college, a shop, a product).<\/li>\n<li>Embed them all with one call (OpenAI or Ollama).<\/li>\n<li>For a new question typed by the user, print the 3 most similar FAQ questions with their scores.<\/li>\n<li>Bonus: if the best score is below a threshold you choose (say 0.5), print &#8220;No good match found&#8221;.<\/li>\n<li>Bonus 2: save the vectors to a JSON file so the second run is instant.<\/li>\n<\/ol>\n<\/div>\n\n<h2 id=\"faq\">FAQ<\/h2>\n<details><summary>What is the difference between an embedding and a token?<\/summary><p>A token is a small piece of text (part of a word) that a model reads, as we saw in <a href=\"https:\/\/devdojo.co.in\/?p=219\">Part 2<\/a>. An embedding is a list of numbers that represents the meaning of a whole text, made by an embedding model.<\/p><\/details>\n<details><summary>Can I use ChatGPT models like GPT to make embeddings?<\/summary><p>No. Chat models generate text. You need a dedicated embedding model such as <code>text-embedding-3-small<\/code>, <code>nomic-embed-text<\/code> or a Sentence Transformers model.<\/p><\/details>\n<details><summary>How many dimensions should I use?<\/summary><p>Start with the model default. If storage or speed matters, try a smaller <code>dimensions<\/code> value (like 512) and check that search quality is still good on your own test questions.<\/p><\/details>\n<details><summary>Are embeddings expensive?<\/summary><p>They are much cheaper than chat completions, because the model only reads text and does not generate any. With Ollama or Sentence Transformers they are completely free, you only pay for your own hardware.<\/p><\/details>\n<details><summary>Do embeddings work for Hindi and other Indian languages?<\/summary><p>Many modern models are multilingual and work reasonably well for Hindi and other languages, but quality varies. Test with your own sentences, and consider a larger or specifically multilingual model if results are weak.<\/p><\/details>\n<details><summary>Do I need a vector database?<\/summary><p>For a few thousand texts, a simple list in memory (like our example) is fine. For more data, fast filtering and persistence, use a vector database. That is the topic of Part 10.<\/p><\/details>\n\n<h2>Conclusion<\/h2>\n<p>Embeddings turn text into vectors so your code can compare <em>meaning<\/em> instead of matching words. You learned how cosine similarity works, how to create embeddings with OpenAI and Ollama in Python and Node.js, how to build a small semantic search, and how to cache and shorten vectors to save money. Every RAG system, smart search box and recommendation feature you build from now on starts with these few lines of code.<\/p>\n\n<div class=\"dd-next\"><strong>Next part \u2014 Part 10: Vector Databases: pgvector, Chroma and Pinecone.<\/strong> We will store our embeddings in a real database, search millions of vectors in milliseconds and add filters like category and date.<\/div>\n\n<div class=\"dd-cta\">\n<h3>New posts every day on DevDojo<\/h3>\n<p>Follow the full <strong>Generative AI series<\/strong> from basics to production. Bookmark <a href=\"https:\/\/devdojo.co.in\/\">devdojo.co.in<\/a> and come back for Part 10.<\/p>\n<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Learn what embeddings are, how cosine similarity works and how to build a semantic search with OpenAI and Ollama in Python and Node.js. Beginner friendly.<\/p>\n","protected":false},"author":1,"featured_media":305,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[38],"tags":[168,89,115,90,167,112,88],"class_list":["post-306","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ai","tag-cosine-similarity","tag-embeddings","tag-ollama","tag-openai-api","tag-semantic-search","tag-series-generative-ai","tag-vector-search"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.4 - aioseo.com -->\n\t<meta name=\"description\" content=\"Learn what embeddings are, how cosine similarity works and how to build a semantic search with OpenAI and Ollama in Python and Node.js. Beginner friendly.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Lokendra\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/devdojo.co.in\/index.php\/2026\/11\/09\/genai-series-part-9-embeddings-vectors\/\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.4\" \/>\n\t\t<meta property=\"og:locale\" content=\"en_US\" \/>\n\t\t<meta property=\"og:site_name\" content=\"DevDojo - Empowering Developers\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"Embeddings Explained: Turning Text into Vectors\" \/>\n\t\t<meta property=\"og:description\" content=\"Learn what embeddings are, how cosine similarity works and how to build a semantic search with OpenAI and Ollama in Python and Node.js. Beginner friendly.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/devdojo.co.in\/index.php\/2026\/11\/09\/genai-series-part-9-embeddings-vectors\/\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2026-11-09T13:20:03+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2026-11-09T13:20:04+00:00\" \/>\n\t\t<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n\t\t<meta name=\"twitter:title\" content=\"Embeddings Explained: Turning Text into Vectors\" \/>\n\t\t<meta name=\"twitter:description\" content=\"Learn what embeddings are, how cosine similarity works and how to build a semantic search with OpenAI and Ollama in Python and Node.js. Beginner friendly.\" \/>\n\t\t<script type=\"application\/ld+json\" class=\"aioseo-schema\">\n\t\t\t{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"BlogPosting\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/11\\\/09\\\/genai-series-part-9-embeddings-vectors\\\/#blogposting\",\"name\":\"Embeddings Explained: Turning Text into Vectors | DevDojo\",\"headline\":\"Generative AI Part 9: Embeddings Explained: Turning Text into Vectors\",\"author\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/author\\\/lokendra\\\/#author\"},\"publisher\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/#organization\"},\"image\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/devdojo.co.in\\\/wp-content\\\/uploads\\\/2026\\\/11\\\/genai-series-part-9-embeddings-vectors.jpg\",\"width\":1200,\"height\":630,\"caption\":\"Generative AI Part 9 banner: text goes into an embedding model and becomes vectors on a meaning map\"},\"datePublished\":\"2026-11-09T13:20:03+00:00\",\"dateModified\":\"2026-11-09T13:20:04+00:00\",\"inLanguage\":\"en-US\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/11\\\/09\\\/genai-series-part-9-embeddings-vectors\\\/#webpage\"},\"isPartOf\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/11\\\/09\\\/genai-series-part-9-embeddings-vectors\\\/#webpage\"},\"articleSection\":\"AI, Cosine Similarity, Embeddings, Ollama, OpenAI API, Semantic Search, Series: Generative AI, Vector Search\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/11\\\/09\\\/genai-series-part-9-embeddings-vectors\\\/#breadcrumblist\",\"itemListElement\":[{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/#listItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/devdojo.co.in\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/category\\\/ai\\\/#listItem\",\"name\":\"AI\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/category\\\/ai\\\/#listItem\",\"position\":2,\"name\":\"AI\",\"item\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/category\\\/ai\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/11\\\/09\\\/genai-series-part-9-embeddings-vectors\\\/#listItem\",\"name\":\"Generative AI Part 9: Embeddings Explained: Turning Text into Vectors\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/#listItem\",\"name\":\"Home\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/11\\\/09\\\/genai-series-part-9-embeddings-vectors\\\/#listItem\",\"position\":3,\"name\":\"Generative AI Part 9: Embeddings Explained: Turning Text into Vectors\",\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/category\\\/ai\\\/#listItem\",\"name\":\"AI\"}}]},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/#organization\",\"name\":\"DevDojo\",\"description\":\"Empowering Developers\",\"url\":\"https:\\\/\\\/devdojo.co.in\\\/\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/author\\\/lokendra\\\/#author\",\"url\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/author\\\/lokendra\\\/\",\"name\":\"Lokendra\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/11\\\/09\\\/genai-series-part-9-embeddings-vectors\\\/#authorImage\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/99f102f0cc9080237ce54b91304fd1b3b6fa29925cbaeba93cd796872e58b45c?s=96&d=mm&r=g\",\"width\":96,\"height\":96,\"caption\":\"Lokendra\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/11\\\/09\\\/genai-series-part-9-embeddings-vectors\\\/#webpage\",\"url\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/11\\\/09\\\/genai-series-part-9-embeddings-vectors\\\/\",\"name\":\"Embeddings Explained: Turning Text into Vectors | DevDojo\",\"description\":\"Learn what embeddings are, how cosine similarity works and how to build a semantic search with OpenAI and Ollama in Python and Node.js. Beginner friendly.\",\"inLanguage\":\"en-US\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/#website\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/11\\\/09\\\/genai-series-part-9-embeddings-vectors\\\/#breadcrumblist\"},\"author\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/author\\\/lokendra\\\/#author\"},\"creator\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/author\\\/lokendra\\\/#author\"},\"image\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/devdojo.co.in\\\/wp-content\\\/uploads\\\/2026\\\/11\\\/genai-series-part-9-embeddings-vectors.jpg\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/11\\\/09\\\/genai-series-part-9-embeddings-vectors\\\/#mainImage\",\"width\":1200,\"height\":630,\"caption\":\"Generative AI Part 9 banner: text goes into an embedding model and becomes vectors on a meaning map\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/11\\\/09\\\/genai-series-part-9-embeddings-vectors\\\/#mainImage\"},\"datePublished\":\"2026-11-09T13:20:03+00:00\",\"dateModified\":\"2026-11-09T13:20:04+00:00\"},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/#website\",\"url\":\"https:\\\/\\\/devdojo.co.in\\\/\",\"name\":\"DevDojo\",\"description\":\"Empowering Developers\",\"inLanguage\":\"en-US\",\"publisher\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/#organization\"}}]}\n\t\t<\/script>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"Embeddings Explained: Turning Text into Vectors | DevDojo","description":"Learn what embeddings are, how cosine similarity works and how to build a semantic search with OpenAI and Ollama in Python and Node.js. Beginner friendly.","canonical_url":"https:\/\/devdojo.co.in\/index.php\/2026\/11\/09\/genai-series-part-9-embeddings-vectors\/","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"BlogPosting","@id":"https:\/\/devdojo.co.in\/index.php\/2026\/11\/09\/genai-series-part-9-embeddings-vectors\/#blogposting","name":"Embeddings Explained: Turning Text into Vectors | DevDojo","headline":"Generative AI Part 9: Embeddings Explained: Turning Text into Vectors","author":{"@id":"https:\/\/devdojo.co.in\/index.php\/author\/lokendra\/#author"},"publisher":{"@id":"https:\/\/devdojo.co.in\/#organization"},"image":{"@type":"ImageObject","url":"https:\/\/devdojo.co.in\/wp-content\/uploads\/2026\/11\/genai-series-part-9-embeddings-vectors.jpg","width":1200,"height":630,"caption":"Generative AI Part 9 banner: text goes into an embedding model and becomes vectors on a meaning map"},"datePublished":"2026-11-09T13:20:03+00:00","dateModified":"2026-11-09T13:20:04+00:00","inLanguage":"en-US","mainEntityOfPage":{"@id":"https:\/\/devdojo.co.in\/index.php\/2026\/11\/09\/genai-series-part-9-embeddings-vectors\/#webpage"},"isPartOf":{"@id":"https:\/\/devdojo.co.in\/index.php\/2026\/11\/09\/genai-series-part-9-embeddings-vectors\/#webpage"},"articleSection":"AI, Cosine Similarity, Embeddings, Ollama, OpenAI API, Semantic Search, Series: Generative AI, Vector Search"},{"@type":"BreadcrumbList","@id":"https:\/\/devdojo.co.in\/index.php\/2026\/11\/09\/genai-series-part-9-embeddings-vectors\/#breadcrumblist","itemListElement":[{"@type":"ListItem","@id":"https:\/\/devdojo.co.in\/#listItem","position":1,"name":"Home","item":"https:\/\/devdojo.co.in\/","nextItem":{"@type":"ListItem","@id":"https:\/\/devdojo.co.in\/index.php\/category\/ai\/#listItem","name":"AI"}},{"@type":"ListItem","@id":"https:\/\/devdojo.co.in\/index.php\/category\/ai\/#listItem","position":2,"name":"AI","item":"https:\/\/devdojo.co.in\/index.php\/category\/ai\/","nextItem":{"@type":"ListItem","@id":"https:\/\/devdojo.co.in\/index.php\/2026\/11\/09\/genai-series-part-9-embeddings-vectors\/#listItem","name":"Generative AI Part 9: Embeddings Explained: Turning Text into Vectors"},"previousItem":{"@type":"ListItem","@id":"https:\/\/devdojo.co.in\/#listItem","name":"Home"}},{"@type":"ListItem","@id":"https:\/\/devdojo.co.in\/index.php\/2026\/11\/09\/genai-series-part-9-embeddings-vectors\/#listItem","position":3,"name":"Generative AI Part 9: Embeddings Explained: Turning Text into Vectors","previousItem":{"@type":"ListItem","@id":"https:\/\/devdojo.co.in\/index.php\/category\/ai\/#listItem","name":"AI"}}]},{"@type":"Organization","@id":"https:\/\/devdojo.co.in\/#organization","name":"DevDojo","description":"Empowering Developers","url":"https:\/\/devdojo.co.in\/"},{"@type":"Person","@id":"https:\/\/devdojo.co.in\/index.php\/author\/lokendra\/#author","url":"https:\/\/devdojo.co.in\/index.php\/author\/lokendra\/","name":"Lokendra","image":{"@type":"ImageObject","@id":"https:\/\/devdojo.co.in\/index.php\/2026\/11\/09\/genai-series-part-9-embeddings-vectors\/#authorImage","url":"https:\/\/secure.gravatar.com\/avatar\/99f102f0cc9080237ce54b91304fd1b3b6fa29925cbaeba93cd796872e58b45c?s=96&d=mm&r=g","width":96,"height":96,"caption":"Lokendra"}},{"@type":"WebPage","@id":"https:\/\/devdojo.co.in\/index.php\/2026\/11\/09\/genai-series-part-9-embeddings-vectors\/#webpage","url":"https:\/\/devdojo.co.in\/index.php\/2026\/11\/09\/genai-series-part-9-embeddings-vectors\/","name":"Embeddings Explained: Turning Text into Vectors | DevDojo","description":"Learn what embeddings are, how cosine similarity works and how to build a semantic search with OpenAI and Ollama in Python and Node.js. Beginner friendly.","inLanguage":"en-US","isPartOf":{"@id":"https:\/\/devdojo.co.in\/#website"},"breadcrumb":{"@id":"https:\/\/devdojo.co.in\/index.php\/2026\/11\/09\/genai-series-part-9-embeddings-vectors\/#breadcrumblist"},"author":{"@id":"https:\/\/devdojo.co.in\/index.php\/author\/lokendra\/#author"},"creator":{"@id":"https:\/\/devdojo.co.in\/index.php\/author\/lokendra\/#author"},"image":{"@type":"ImageObject","url":"https:\/\/devdojo.co.in\/wp-content\/uploads\/2026\/11\/genai-series-part-9-embeddings-vectors.jpg","@id":"https:\/\/devdojo.co.in\/index.php\/2026\/11\/09\/genai-series-part-9-embeddings-vectors\/#mainImage","width":1200,"height":630,"caption":"Generative AI Part 9 banner: text goes into an embedding model and becomes vectors on a meaning map"},"primaryImageOfPage":{"@id":"https:\/\/devdojo.co.in\/index.php\/2026\/11\/09\/genai-series-part-9-embeddings-vectors\/#mainImage"},"datePublished":"2026-11-09T13:20:03+00:00","dateModified":"2026-11-09T13:20:04+00:00"},{"@type":"WebSite","@id":"https:\/\/devdojo.co.in\/#website","url":"https:\/\/devdojo.co.in\/","name":"DevDojo","description":"Empowering Developers","inLanguage":"en-US","publisher":{"@id":"https:\/\/devdojo.co.in\/#organization"}}]},"og:locale":"en_US","og:site_name":"DevDojo - Empowering Developers","og:type":"article","og:title":"Embeddings Explained: Turning Text into Vectors","og:description":"Learn what embeddings are, how cosine similarity works and how to build a semantic search with OpenAI and Ollama in Python and Node.js. Beginner friendly.","og:url":"https:\/\/devdojo.co.in\/index.php\/2026\/11\/09\/genai-series-part-9-embeddings-vectors\/","article:published_time":"2026-11-09T13:20:03+00:00","article:modified_time":"2026-11-09T13:20:04+00:00","twitter:card":"summary_large_image","twitter:title":"Embeddings Explained: Turning Text into Vectors","twitter:description":"Learn what embeddings are, how cosine similarity works and how to build a semantic search with OpenAI and Ollama in Python and Node.js. Beginner friendly."},"aioseo_meta_data":{"post_id":"306","title":"Embeddings Explained: Turning Text into Vectors | DevDojo","description":"Learn what embeddings are, how cosine similarity works and how to build a semantic search with OpenAI and Ollama in Python and Node.js. Beginner friendly.","keywords":null,"keyphrases":{"focus":{"keyphrase":"embeddings"},"additional":[]},"primary_term":null,"canonical_url":null,"og_title":"Embeddings Explained: Turning Text into Vectors","og_description":"Learn what embeddings are, how cosine similarity works and how to build a semantic search with OpenAI and Ollama in Python and Node.js. Beginner friendly.","og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":"","og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"BlogPosting","isEnabled":true},"graphs":[]},"schema_type":"default","schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":"-1","robots_max_videopreview":"-1","robots_max_imagepreview":"large","priority":0,"frequency":"default","local_seo":null,"breadcrumb_settings":null,"limit_modified_date":false,"ai":{"faqs":[],"keyPoints":[],"schemas":[],"titles":[],"descriptions":[],"socialPosts":{"email":{"subject":"","preview":"","content":""},"linkedin":[],"twitter":[],"facebook":[],"instagram":[]}},"created":"2026-11-09 13:13:07","updated":"2026-11-09 13:20:04","focus_keyword":"embeddings","additional_keywords":null,"truseo_locale":null,"seo_analyzer_scan_date":null},"aioseo_breadcrumb":"<div class=\"aioseo-breadcrumbs\"><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/devdojo.co.in\/\" title=\"Home\">Home<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">\u00bb<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/devdojo.co.in\/index.php\/category\/ai\/\" title=\"AI\">AI<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">\u00bb<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\tGenerative AI Part 9: Embeddings Explained: Turning Text into Vectors\n\t\t<\/span><\/div>","aioseo_breadcrumb_json":[{"label":"Home","link":"https:\/\/devdojo.co.in\/"},{"label":"AI","link":"https:\/\/devdojo.co.in\/index.php\/category\/ai\/"},{"label":"Generative AI Part 9: Embeddings Explained: Turning Text into Vectors","link":"https:\/\/devdojo.co.in\/index.php\/2026\/11\/09\/genai-series-part-9-embeddings-vectors\/"}],"_links":{"self":[{"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/posts\/306","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/comments?post=306"}],"version-history":[{"count":1,"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/posts\/306\/revisions"}],"predecessor-version":[{"id":307,"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/posts\/306\/revisions\/307"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/media\/305"}],"wp:attachment":[{"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/media?parent=306"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/categories?post=306"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/tags?post=306"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}