Besly
Recipes Calculators Blog Help centre Science & sources Get the app →
Engineering

How we test an AI food logger

November 2, 2026 · 7 min read

We test Besly by making the language model unable to invent calories for foods that exist in our catalog, then running hundreds of scripted conversations against the real database before every change to the prompt or the tools. This post covers how that works, which numbers we trust, and where the logger still gets things wrong.

Snap your plate, get calories and macros — free on the App Store →

Open on your iPhone
Scan to get Besly

1. The problem: a quiet failure

Calorie apps are judged on one number, but the failure is silent. A model can say 420 kcal for a plate that is closer to 700, sound sure about it, and nobody notices until the scale disagrees. A language model is not a food database; it remembers roughly what foods contain and rounds toward the average. So the model in Besly chooses, and the database answers.

2. What the agent can and can't do

Besly uses one tool-calling agent. There is no separate text parser in front of it. The model gets 13 tools, among them search the catalog, add a catalog food with a chosen serving, add an estimate, edit, replace, remove, and ask a clarifying question. The system prompt is short, about 1,200 tokens.

The catalog is the source of truth. More than 80,000 foods live on the phone as a local database (36 US restaurant chains among them, copied from each chain's published nutrition), and a remote catalog covers the rest. When a food is found, the model picks it and a serving; code does the multiplication. When nothing matches, the model may add an estimate, and the card is marked as an estimate with a lower confidence label instead of looking like a database hit.

3. Why our first tests lied

Our first suite was sentences we wrote ourselves. On 33 of them the agent got 32 right. Then we built a second suite from 60 conversations taken from the debug logs of our own test phones, with the original typos and dictation errors (the logs held 874 inputs, 487 unique, 88 of them dictated). Same model, same prompt: 42 of 60. The model was fine on tidy sentences and mediocre on how people actually talk.

That gap drove most of the work. Simplifying the prompt and fixing the code around it lifted the real-sentence set from 42 to 55 of 60, and later to 57. We don't store the text of users' messages on our servers, so the English suite (80 scenarios) was written by hand, modeled on the same difficulty classes and on food names that people actually log. That is weaker evidence than real transcripts, and we say so.

Three real failures from those suites, each caught in testing before it reached users:

4. The eval suite

Today the food-logging agent has 577 scenarios in 27 suites: single foods, multi-food messages, quantity corrections, replace and remove, "my usual breakfast," restaurant orders, packaged-food labels, portion language, photos, and adversarial messages. A scenario is a short conversation plus expectations: which tool calls should happen, which catalog ids are acceptable or forbidden, how many items should end up in the draft, and a calorie tolerance band. Scoring is rules, not another model grading the first one.

The important part is that the harder suites run against a Python mirror of the app's search and portion logic reading the real catalog, not a mock. A mock database hides exactly the bugs above. When we moved our hard set to the real catalog it exposed portion bugs in the resolver; after we fixed them it went from 67 to 83 of 101 scenarios.

A "smart" runner reads the diff, maps changed files and symbols to the suites they can affect, runs a fast subset first, caches passing results for seven days, and reports a regression only when a scenario that last passed now fails. A Python check also compares the tool definitions in the Swift app with the ones the eval uses, because a drifted mirror makes every result meaningless. Evals use a separate API key from production. The suites cost roughly $0.002 to $0.009 per scenario run, depending on the suite.

5. Held-out testing

If you fix a failure by putting its example into the prompt, that scenario will pass forever and prove nothing. Our rule since October: test a fix on similar cases that are not in the prompt, and on opposite cases where the right behavior is to leave things alone.

One prompt sentence we tried for the potato problem broke six unrelated scenarios in the quick set, every time. We moved the rule into a tool description instead and re-measured. Even that left lasting drift in unrelated scenarios, so the shipped app keeps those fields off and uses only the code-side change (next section). Not every older suite follows the held-out rule yet.

6. Fix the code, not the prompt

Search is where most bugs lived, so most fixes are deterministic wrappers around search: protect a brand the user named, put generic foods ahead of barcoded products when no brand was mentioned, and retry without size words like "footlong" and use the size on the serving menu. Code can be unit-tested and can't be talked out of its behavior by a long conversation.

Portions got the same treatment. Early on the model wrote free text like "1.5 servings (255 g)" and code sometimes multiplied twice. Now search returns each candidate's servings as a numbered menu, the model picks an id and a count, and one formula applies it. On 544 scenarios the pass count stayed at 500, but gross errors (more than double or under half the expected calories) fell from 7 to 5, the median miss on out-of-band turns from 41% to 27%, and a "12 count" order that previously logged 0 kcal came out at 380.

7. Photos

Photo logging is Besly's lead feature, and the hard part is portion size, not recognizing the food. The agent receives the photo as input to the same loop and uses typical servings, says when something is a guess, and makes correcting the amount quick.

One guard sits outside the agent. A photo of a laptop screen showing a food list was once logged as a meal, so a small separate classifier now runs in parallel and blocks logging for menus, screens, recipe text and non-food photos. On 21 photo scenarios the suite went from 15 to 21 passing with the gate. If the classifier fails or answers late, the gate stays open.

8. Restaurant data

Chain items are copied from each chain's own published nutrition, and each page shows when it was pulled. Chipotle, for example, publishes values per ingredient, not per bowl, so its page lists ingredients and you add them up for your order.

9. What still fails

If a number looks wrong, the help center shows how to correct it in a couple of taps.

10. Try it, or break it

Besly is an iPhone app. Get it on the App Store, photograph a plate, and tell us when it's wrong at support@besly.co. Besly is not medical advice.