Chapter 5 · Part 2

Build the search function

Everything is in place: a corpus, its embeddings, and a way to score any query against them. Now we package it into the one function that is the search engine — embed the query, score it against the corpus, and return the best few matches.

Add this to search.py (replace the loose query-scoring lines from the last chapter):

search.py (continued)
import numpy as np

def search(query, k=3):
  query_embedding = model.encode(query, normalize_embeddings=True)
  scores = corpus_embeddings @ query_embedding      # cosine similarity, all docs

  top_k = np.argsort(-scores)[:k]                   # indices of the k highest scores
  return [(corpus[i], float(scores[i])) for i in top_k]

The only new piece is np.argsort(-scores): argsort returns the indices that would sort the scores ascending, so sorting the negated scores puts the highest first. Slice off the first k and you have your top matches.

Try it

Add a few searches to the bottom of search.py and run it:

search.py (continued)
for query in [
  "I can't log into my account",
  "when will my package arrive",
  "how do I get my money back",
]:
  print(f"\nQuery: {query}")
  for doc, score in search(query):
      print(f"  {score:.3f}  {doc}")
terminal
python search.py
Query: I can't log into my account
0.531  How do I reset my password?
0.334  How can I contact customer support?
0.286  How do I cancel my subscription?

Query: when will my package arrive
0.418  How do I update my shipping address?
0.377  Do you ship internationally?
0.331  Where can I see my past orders?

Query: how do I get my money back
0.604  What is your refund and return policy?
0.310  How do I cancel my subscription?
0.287  What payment methods do you accept?

Read those top hits. Not one query reuses the vocabulary of the document it matched — "money back" finds refund policy, "package arrive" finds shipping address — and the right answer lands first every time. That's a semantic search engine, and you built it in about a dozen lines.

Try your own queries. Add documents to the corpus list — anything you like — and search will index them the moment you re-run. The last chapter makes it something you'd actually ship.