Vision models: a hotel agent that reads room photos
Tuesday, 8:40. Yesterday StayHub's new AI room check marked 297 of Hotel Lumen's 300 room photos "ready", and 11 guests found stained sheets or used glasses. The hotel wants to know if they can still trust it.
An AI model can look at a room photo and tell you if the bed is made. Here's what it sees well, where it fails, and when a human decides.
- Problem
A 120-room hotel gets 300 housekeeping photos a day, guests send photos of damage or dirty rooms, and listings need accurate amenity tags. Staff can't look at everything carefully.
- Why the simple system fails
Asking a vision model 'is this room OK?' returns confident prose: it misses a hair on a pillow, counts three towels as four, claims a sea view from a picture of a car park, and nobody can tell which answers to trust. Charging a guest for 'damage' it imagined is a lawsuit.
- Engineering concept
Ask narrow visual questions, demand structured findings with evidence and an explicit 'unclear' option, validate them, derive confidence from checks rather than the model's tone, and send high-impact or uncertain cases to a person.
- Technology
Vision-language model via the AI gateway, image preprocessing (resize, crop, EXIF strip), JSON-schema structured output, rules-based validation, a review queue, and an eval set of labelled photos.
- Trade-offs
More automation saves staff time but raises the cost of each silent mistake. Higher resolution and multiple crops raise accuracy and token cost. Storing photos helps audits and evals but increases privacy exposure.
The problem
2 min- Maya · Product managerYesterday the AI marked 297 of 300 room photos ready. 11 guests complained: stained sheets, used glasses.
- You · Engineer at StayHubSo the model can't see stains?
- Lina · Senior engineer, your mentorOur prompt was "Is this room ready? Answer yes or no." What do you think went wrong?
The big idea
4 minTreat a vision model as a fast junior inspector: narrow questions, an allowed "unclear", and code, not the model's tone, decides what's trusted.
Change the question, not the model. Then let the cost of a mistake decide how much you automate.
How it works
5 minA cuts a photo into and answers in text. It's good at recognising and describing common things, unreliable at counting, measuring and tiny details, and sounds equally sure either way.
- 1Guided capture
The app tells the housekeeper where to stand: whole bed in frame, lights on.
- 2Clean the file
Strip hidden location data (), keep the original private, make a resized copy.
- 3Ask for structured findings
Structured findings: one answer per checklist item (ok, issue or unclear) with a line of evidence, instead of a paragraph.
- 4Code validates
Schema valid? Evidence present? Photo quality good? Any critical item (stains, rubbish) not ok?
- 5Route by doubt and impact
Confidence routing: code decides from checks it can verify. Clean results mark the room ready; doubt or critical items go to a supervisor; money always to a person.
Watch a decision being made
4 minLina and you redesign the room check
Make wrong "ready" answers rare and send doubtful rooms to a supervisor, without reviewing every photo.
- 1Split the question into a checklist
Bed made, towels, bedding stains, visible rubbish, floor clear. Mark stains and rubbish critical.
Why: A narrow question has a visible answer; "ready" blurs many small checks together.
- 2Allow "unclear", demand evidence
Each item returns
ok,issueorunclearplus evidence; photo quality is its own field.Why: If doubt has nowhere to go, the model guesses.
- 3Route in code
Rules a person can read:
tsif (check.photoQuality !== "good") return { route: "retake" }; if (critical.some((i) => i.status !== "ok")) return { route: "review" }; if (check.items.some((i) => i.status === "unclear")) return { route: "review" }; if (check.items.some((i) => i.status === "issue")) return { route: "housekeeping" }; return { route: "auto_ready" };Why: A model's own "0.95" is poorly calibrated; wrong answers sound just as sure.
- 4Measure on labelled photos
Run v2 on 300 photos supervisors already labelled; audit 200 auto-accepted rooms weekly.
Why: Without labels, "it seems better" is a feeling.
In real life
3 minAirbnb: automate photo tagging
- Has publicly described sorting listing photos by room type with computer vision
- A wrong tag is a small annoyance at huge volume
Fits when mistakes are cheap.
Be My Eyes: a human one tap away
- "Be My AI" describes photos for blind and low-vision users
- Publicly keeps the option to call a sighted volunteer
Fits when a silent mistake can hurt someone.
What goes wrong
3 minModels miscount beyond a few objects and guess sizes or sea views.
Crop and compare with expected counts; take facts from property data.
A wrong answer can say 0.95.
Build confidence from checks, calibrated on labelled photos.
Faces, passports and GPS coordinates; text in a photo can also be an injection.
Strip EXIF, limit retention and access, treat image text as untrusted.
Trade-offs
3 min| Option | Fits when | Costs |
|---|---|---|
| Full automation | Cheap mistakes: listing tags | Silent misses |
| Confidence-routed review | Costly but not catastrophic: housekeeping | A review team; drifting rules |
| Model assists, human decides | Money, safety, legal: damage fees | Least time saved |
- Karim · CTO18% review is 54 photos a day per hotel, times 40. Auto-accept everything except clear issues.
- Lina · Senior engineer, your mentorThen every
unclearstain becomes "ready" again. - Karim · CTOThen lower the rate with better photos, and review only critical items.
Your job as tech lead
4 minClaude Code can write the code for this. You still own the decisions, and you have to know what good looks like to accept or reject its work.
- The checklist and its answers (ok, issue, unclear, with evidence), not "Is this room ready?".
- The routing rules, in code: what auto-passes, what a supervisor sees, what goes back.
- What the model never decides alone (any charge) and never gets asked (sea view, room size).
Rebuild the photo check in `src/room-ready/` for Hotel Lumen; "ready? yes/no" passed 297 of 300 photos. Plan first. Checklist (bed made, bedding stains, rubbish, used glasses, towels), each ok, issue or unclear with evidence, validated by a JSON schema. Routing rules in code: critical not ok or anything unclear to the supervisor queue, other issues to housekeeping. Strip EXIF. Nothing may charge a guest. Run the 300-photo eval set; list every wrong auto-accept.
- Shares near 78 / 18 / 4%, and you open five wrong auto-accepts yourself.
- Routing rules are readable code, not prompt instructions.
- Nothing in the diff touches payments or fees.
- The model's own
confidence: 0.93used as the auto-accept threshold. - No
unclearoption in the schema. - The eval set replaced by a few new photos the prompt happens to pass.
Check yourself
2 minNext episode: finance is drowning in 9,000 supplier invoices a month, and the AI that reads them just booked a PO number that doesn't exist.
Mark it complete to update your skill map.
Go deeper (optional)You don't need this to understand the lesson. Open it when a project needs the details: Technical deep dive, Visual diagram, Analogy, Check your understanding, Practice ladder, Explain it yourself.
Technical deep dive
Lina and you redesign the room check
Room check v1 asks "Is this room ready? yes/no" and marked 297 of 300 photos ready, with 11 complaints. Redesign it so wrong "ready" answers become rare and the doubtful ones reach a supervisor, without reviewing every photo.
- 1Split the question into a checklist
Replace "is it ready?" with narrow, visible items: bed made, towels present, bedding stains, visible rubbish, floor clear. Mark which ones are critical (stains, rubbish).
Why: A narrow question has a visible answer. "Ready" is a judgement made of many small checks, and the model blurs them together.
- 2Allow "unclear" and demand evidence
Each item returns
ok,issueorunclear, plus a short evidence string. Photo quality is its own field.tsconst RoomCheck = z.object({ photoQuality: z.enum(["good", "too_dark", "blurry", "wrong_angle"]), items: z.array(z.object({ item: z.enum(["bed_made", "towels", "bedding_stains", "visible_rubbish", "floor_clear"]), status: z.enum(["ok", "issue", "unclear"]), evidence: z.string().max(200), })), });Why: If doubt has no place to go, the model guesses. Evidence makes each finding something a supervisor can check in seconds.
- 3Validate in code and build confidence
Parse with the schema (), then apply rules: bad photo → retake; critical item not
ok→ review;issuewithout evidence → review; anyunclear→ review; any otherissue(with evidence) → back to housekeeping.tsif (check.photoQuality !== "good") return { route: "retake" }; const critical = check.items.filter((i) => CRITICAL.includes(i.item)); if (critical.some((i) => i.status !== "ok")) return { route: "review" }; if (check.items.some((i) => i.status === "issue" && !i.evidence)) return { route: "review" }; if (check.items.some((i) => i.status === "unclear")) return { route: "review" }; if (check.items.some((i) => i.status === "issue")) return { route: "housekeeping" }; return { route: "auto_ready" };Why: Confidence comes from things we can verify. A model's own "0.95" is poorly calibrated: wrong answers often sound just as sure.
- 4Route by impact
Room ready → automatic when everything is clean. Stains, rubbish, doubt → supervisor. Anything involving money (damage fees) → always a person.
Why: The same accuracy is fine for one task and dangerous for another. The cost of a mistake sets how much we automate.
- 5Measure on labelled photos
Run v2 on 300 photos supervisors already labelled. Check: of the rooms auto-marked ready, how many were really ready? Then audit 200 auto-accepted rooms every week.
Why: Without labels, "it seems better" is a feeling. The eval set also tells us which item is weakest so we can fix that one.
| Visual task | Typical reliability | Design response |
|---|---|---|
| Is there a bed / bathtub / balcony? | High | Automate; spot-check |
| Is the bed made? Rubbish visible? | Good with clear photos | Checklist + unclear + capture guidance |
| Count pillows, towels, glasses | Unreliable above a few items | Crop the area; compare with the expected count; review mismatches |
| Small stains, hairs, cracks | Unreliable on resized images | Higher-resolution crops; human review for critical items |
| Room size in m², "sea view" | Often guessed | Don't ask the model; use property data |
| Is this photo genuine / from this room? | Cannot verify | Never base charges on it alone; human review |
Visual diagram
Housekeeping and damage photos flowing through a vision pipeline with validation and human review.
Analogy
- A remote supervisor seeing photos"Is the room fine?" gets "Looks fine!"A vision pipelineA vague prompt gives useless answers
- A remote supervisor seeing photosA checklist with "can't see"A vision pipelineStructured questions with an unclear option
- A remote supervisor seeing photosCan't see under the bedA vision pipelineCan't judge what isn't visible
- A remote supervisor seeing photosCharges go to the managerA vision pipelineHumans decide anything costly
Where the analogy breaks: a supervisor who's unsure will say so. A model often won't, so you must build "unclear" into the questions and check its answers in code.
Check your understanding
Select all that apply.
Practice ladder
Trace three room checks through the validator
Scenario Warm-upThe v2 validator rules, in order: 1. photoQuality not good → retake 2. A critical item (bedding_stains, visible_rubbish) not ok → review 3. Any issue without evidence → review 4. Any unclear → review 5. Any other issue (with evidence) → back to housekeeping 6. Otherwise → auto_ready
Trace these three results:
- Room 204: quality
good; bed_madeok; towelsunclear("rail partly hidden"); bedding_stainsok; visible_rubbishok. - Room 318: quality
too_dark; every itemok. - Room 411: quality
good; bed_madeissue(evidence: ""); every other itemok.
For each room, say which rule fires first and where the room goes.
Finish Lina's damage-report flow
Design PracticeGuests can report problems in the StayHub app with up to 3 photos and a sentence ("The shower door is broken"). Lina's flow has two gaps. Fill in steps 3 and 5.
- Clean the upload: strip EXIF, store the original privately with a 90-day retention.
- Ask narrow questions: category (plumbing, electrical, furniture, cleanliness, other), visible damage (yes / no / unclear), evidence, urgency suggestion.
- Validate and route: ___
- Maintenance ticket: created with the model's draft description and the photo.
- Money: ___
40 rooms with a "sea view" that faces a car park
Debugging PracticeThe listing-tagging job tagged 40 rooms at a beach hotel as "sea view". Guests complain they see a car park. The photos were taken through the window at an angle that shows a strip of blue at the edge.
Maya wants a fix this week. Explain what went wrong and design the new tagging rule for "sea view" and similar facts (room size, floor, quiet room).
Auto-approve small refunds from guest photos?
Decision StretchMaya: "Guests wait 2 days for a refund when the room isn't clean. Can the AI approve refunds under 30 € automatically when the photo clearly shows a dirty room?"
The facts: - About 60 cleanliness complaints a week across StayHub's partner hotels, average refund 22 €. - On 300 labelled complaint photos, the model's "clearly dirty" findings were right 94% of the time. - Photos can be old, from another stay, or from another hotel; the model can't tell. - Support handles each complaint in about 6 minutes today.
Write a short decision: "We chose X over Y because…, accepting the risk of…".
Explain it yourself
Explain how you'd make a vision model's room-photo checks trustworthy enough for daily hotel operations.