Start learning free

LESSON 114 OF 129 · AI ENGINEERING: PRODUCTION

Project: Ship the Assistant in Python

Practice

The shop's assistant is one route, POST /chat: a question comes in, the model answers, the answer goes out. That works on a good day. An assistant for real customers must also save money on repeated questions, survive a busy model, keep personal data in, keep bad answers from going out, and tell you what it cost.

One piece at a time: every piece is a few lines placed at the right spot of the route. A cache, for example, is a lookup before the slow work and a store after it. Here it is on another route:

@app.get("/price/<name>")
def price(name):
    if name in saved:
        return jsonify({"price": saved[name], "saved": True})
    value = slow_lookup(name)
    saved[name] = value
    return jsonify({"price": value, "saved": False})

The pieces of this chapter are already written above the route: normalize and cache, mask, check_answer, cost_of and the retry loop in ask. The route uses almost none of them yet. The tests send five questions, then read what the model received and what the app logged:

POST /chat  {"question": "Do you ship to Canada?"}  →  200  {"answer": "Yes, we ship to Canada. …", "cached": false}
POST /chat  {"question": "do you ship to canada"}  →  200  {"answer": "Yes, we ship to Canada. …", "cached": true}
POST /chat  {"question": "What is your return policy?"}  →  200  {"answer": "You can return an item within 30 days …", "cached": false}
POST /chat  {"question": "Can you email my invoice to ana@example.com? I paid with card 4242 4242 4242 4242."}  →  200  {"answer": "I can't send emails from this chat. …", "cached": false}
POST /chat  {"question": "I ordered 12 lamps in the wrong color. Refund me!"}  →  200  {"answer": "I can't answer this one myself. A person from our team will write to you today.", "cached": false}
GET /check/model  →  200  {"model_received": [… "claude-haiku-5-5 <- Can you email my invoice to [email]? I paid with card [card].", …], "waits_between_tries": [0.1, 0.1, 0.2]}
GET /check/log  →  200  ["WARNING claude-haiku-5-5 is busy (status 429), try 1 of 3", "INFO claude-haiku-5-5 answered, example cost $0.000099", "INFO Cache hit: no model call, no cost", …]

Your task: put the pieces to work, one per step: the cache lookup and the line that fills the cache, the wait between tries inside ask, the fallback model, the mask, the guardrail with SAFE_ANSWER, and three log lines.


Assignment
Follow the Coding Tutorial to add the cache, the backoff, the fallback model, the mask, the guardrail and the cost logging to POST /chat.


Hint
The place of each line matters. The mask comes before the cache key and the model call, so both see only the masked text. The guardrail's return comes before the line that fills the cache, so a blocked answer is never stored.


STEP 1 OF 11
    Ctrl + Enter to check

    Ai Ship Project in Python

    Keep going with Python1,600+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free