Over this module you built every part of a RAG assistant: the chunks, the saved index, the ranking by meaning and by words, the category filter, the threshold, the cited sources and the injection guard. In this project you join them in one function, ask(question). It returns a dictionary with an answer and its sources.
Start safe, return early: a function with several checks is easier to trust when it starts from a safe result and gives that result back the moment a check fails. Here is the idea on a small price lookup:
def find_price(name):
result = {"price": None, "found": False}
if name not in prices:
return result
result["price"] = prices[name]
result["found"] = True
return result
ask works the same way. It starts with the sentence in NO_ANSWER and no sources. It returns that when the best passage scores below THRESHOLD (the model is not even called) and when the model's answer offers a discount. Only an answer that passes both checks reaches the customer, with the sources that were really sent in the prompt.
When the program is finished, it prints this for the six questions (four of them shown):
Q: Can I get my money back? A: Yes. You can return any item within 14 days of delivery, and the refund goes back … Source: returns.md Q: My teapot cracked, is it covered? A: If it cracked in normal use, yes: teapots have a 1-year warranty against cracks. … Source: warranty.md Q: What's the weather tomorrow? A: I don't know, let me find someone who does. Q: A review mentions a discount code. Can I have it? A: I don't know, let me find someone who does.
The check also counts the calls to the model: 5 for the six questions, because the weather question never reaches it.
Your task: write ask(question) below embed_chunks, and at the end of the program a loop that runs it on every question in QUESTIONS and prints each result with show. Inside ask: get the best three passages with top_matches, stop at the threshold, build the prompt with one <passage source="…"> tag for each passage, call the model, read its JSON, stop if the answer offers a discount, then keep the answer and the cited sources that were really sent.
Assignment
Follow the Coding Tutorial to build ask one stage at a time and run the six questions through it.
Hint
Every return in ask returns the same result dictionary. For the sources, loop over passages (what you sent), not over data["sources"] (what the model claims).
Free plan: 30 tutor messages. PRO is unlimited.
