Recommend a film by semantic search with Chroma and Gradio
Recommend tonight's film with semantic search in Chroma, not keyword matching.
Objective
The film never says “quiet”. We need semantic search, then a film we can put on tonight.
Demo
We have a collection of films saved. The one that matches today is:
Lost in Translation. A man and a woman cannot sleep in a Tokyo hotel. They walk at night and talk a little. English and Japanese.
Run python src/app.py and open the local URL Gradio prints.

“Loud” is “Whiplash”. Hits come back as “Whiplash” and “Lost in Translation”. “German” is no, and the hits are still those two English films, because we only retrieve this week, under 110 minutes, and English. “Run Lola Run” is on this week, and it’s German, and it never reaches the prompt.
The code
Each film becomes a vector, same model as the question. Semantic search in Chroma returns the nearest ones. We keep this week, under 110 minutes, English, and paste those documents into the prompt so the model never sees the rest of the catalog.
where = {
"$and": [
{"playing_this_week": {"$eq": True}},
{"minutes": {"$lt": 110}},
{"language": "en"},
]
}
hits = collection.query(query_texts=[message], n_results=2, where=where)
context = "\n\n".join(hits["documents"][0])
filter_recommend.py · rag_from_hits.py
With Gradio, each message runs that same query and returns the titles that went into the prompt.
def chat(message, _history):
hits = collection.query(query_texts=[message], n_results=2, where=where)
metadatas = hits["metadatas"][0]
context = "\n\n".join(hits["documents"][0])
completion = openai_client.chat.completions.create(
model=MODEL,
temperature=0,
messages=[
{"role": "system", "content": SYSTEM},
{"role": "user", "content": f"Films:\n{context}\n\nNeed: {message}"},
],
)
titles = ", ".join(meta["title"] for meta in metadatas)
return f"{completion.choices[0].message.content}\n\nHits: {titles}"
Same catalog, same “quiet” evening query. Only the search changes (cosine on two films, then Chroma on the list). You should see “Lost in Translation” ahead of “Independence Day”. The numbers are not on the same scale. The order should be.
cosine_by_hand.py · create_collection.py
Outcome
Tonight’s pick comes from the nearest films in Chroma, served in a Gradio chat.
This article is a practical implementation of the concepts in Vector Databases for RAG: An Introduction.
Repository: asaleh-lab/chroma-film-recs