The Columns Hidden Inside Your Customer Reviews

The Columns Hidden Inside Your Customer Reviews
A restaurant has ten thousand reviews. Its dashboard has an average rating, a monthly trend, and a search box.
Somewhere inside those reviews is a much more useful answer: people like the food, but delivery packaging keeps ruining it.
There is no column for that.
That is what interests me about TypeSafe's Jev. I want to look at it as a way to turn human text into data: reviews, comments, forum discussions, the messy sentences that contain signals our databases cannot currently query.
I recently wrote about a restaurant procurement agent. That project started with operational inputs: menus, portions, inventory, supplier availability. Here I want to look at another side of the same business. Customers are already describing problems. How do those descriptions become something an engineer can aggregate and a restaurant owner can investigate?
This is an architecture I would explore, not a report from a deployed Jev integration. The examples below are hypothetical. I haven't run a benchmark on restaurant reviews.
A prep station for language
Think about a restaurant kitchen before service. Ingredients arrive in different shapes. Someone washes, separates, portions, and puts them into containers the rest of the kitchen can work with.
I see a similar job between raw text and analytics.
A review arrives as a paragraph. The next stage produces a few useful attributes. Conventional software groups and counts them. A person, or a more capable language model with access to the evidence, can then work on the interpretation.
The distinction matters because Jev gives up free-form string generation. TypeSafe presents it as a model for fast, structured decisions. It doesn't browse Reddit for you or write the research report. You supply the material and the questions.
Its three question types are Noul, a yes/no probability; Choice, a selection from supplied alternatives with probabilities; and Score, a position along defined levels. That is enough to add many useful attributes to an existing record. It doesn't make it an arbitrary extractor of new names, quotations, or product identifiers.
If I need the exact restaurant name, I would preferably get it from source metadata. If I need an exact quotation, I would preserve the original text. Asking a classifier to choose between known categories is a different operation from asking a model to discover and return an unrestricted string.
The interesting AI feature is the column you couldn't populate before.
One review can contain several different problems
Consider this invented delivery review:
“The noodles were great, but the sauce leaked through the bag. This is the second time. I'd order again if they changed the containers.”
A single positive/negative label throws away most of what makes that useful.
I would ask whether the author praises the food, reports a packaging failure, describes a repeated problem, and makes a future order conditional on a change. Those signals can coexist. Forcing them into one winning category would lose information before the analysis even starts.
At the application boundary, a normalized record might look like this. The values are illustrative, and this is my storage shape, not a Jev API response:
type ReviewSignals = { reviewId: string; restaurantId: string; source: 'delivery' | 'direct' | 'forum'; publishedAt: string; foodPraiseProbability: number; packagingFailureProbability: number; repeatProblemProbability: number; conditionalReturnProbability: number; modelVersion: string; questionSetVersion: string; };
The first four fields come from ingestion. The next four are model judgments. The last two let me explain how those judgments were produced.
I would keep the raw review linked to this row. An owner clicking “packaging complaints increased” should be able to read the evidence, including the examples the classifier got wrong.
Question wording deserves as much care as the schema. “Is packaging bad?” invites a broad interpretation. “Does the author report that the container or bag leaked, broke, or failed to contain the food?” defines an observable claim.
Even then, a complaint describes the author's account. It doesn't establish which party caused the failure. A crushed meal might involve the container, the courier, or both. I would resist naming the field restaurant_packaging_defect unless the evidence actually supports that attribution.
Parallel questions, then parallel records
There are two kinds of parallel work here, and I would keep them separate in the design.
For one review, ask all the independent questions together. TypeSafe documents that questions in a request share the same input state and are evaluated independently. One answer does not become context for the next. If a later question requires information fetched using an earlier answer, that needs another step. Question semantics.
Across the dataset, workers process different reviews concurrently. That is our ingestion and scheduling problem: queues, bounded concurrency, retries, rate limits, and resumable jobs. Parallel question evaluation doesn't promise unlimited corpus throughput.
The pipeline I have in mind is quite ordinary:
Source exports / permitted APIs ↓ Normalize records, retain context, remove duplicates ↓ Queue → workers → Jev questions for each record ↓ Versioned semantic attributes + original record links ↓ SQL aggregates → evidence review → optional written report
I would give each result a key built from the source record, content hash, model version, and question-set version. Retrying a request should not create a second observation in the dashboard. Changing a question should not silently overwrite the previous measurement.
The kitchen analogy has a useful limit here. You can prepare ingredients in parallel, but putting twice as many cooks in one doorway won't double dinner service. The slowest part might be collecting the data, resolving duplicates, or reviewing ambiguous examples.
Let SQL do the counting
Once those columns exist, familiar tools become useful again.
Here is an illustrative PostgreSQL query over enriched delivery reviews. The 0.8 cutoff is a placeholder to validate against labeled examples, not a recommended universal threshold:
SELECT restaurant_id, date_trunc('month', published_at) AS month, count(*) AS analyzed_reviews, count(*) FILTER ( WHERE packaging_failure_probability >= 0.8 ) AS flagged_packaging_reviews, round( 100.0 * count(*) FILTER ( WHERE packaging_failure_probability >= 0.8 ) / count(*), 1 ) AS flagged_share_pct FROM review_signals WHERE source = 'delivery' AND model_version = :model_version AND question_set_version = :question_set_version GROUP BY restaurant_id, date_trunc('month', published_at);
That last number is the share of analyzed reviews flagged by this rule. It is not the percentage of orders with failed packaging. We don't have all orders in this table. We have people who wrote reviews, within whatever collection process we used.
I'd put that distinction in the dashboard label. Otherwise a technically correct query becomes a misleading product feature.
The next useful question might be whether flagged reviews concentrate around one location, a delivery channel, or a period after a container change. Those joins require reliable operational metadata. Jev cannot supply missing order history by interpreting a paragraph more confidently.
The same idea works for products
Now replace the restaurant with a coffee grinder.
“Loud” can mean several things. One owner mentions the noise and still recommends it. Another says it wakes their child and explains why they returned it. A third repeats something they heard without owning the product.
I would want separate signals for claimed ownership, noise complaints, reported returns, and explicit reasons for returning. These are much more useful than a single sentiment score when deciding what to investigate about a product.
The model still only judges what the text supports. “The author claims to own it” is a defensible label. “Verified purchaser” requires purchase evidence outside the text.
There is also a discovery problem. A fixed question set finds the issues I thought to ask about. If a new failure mode appears and none of my questions cover it, a cheap classifier can miss it at enormous scale.
I would regularly inspect a diverse sample of unflagged and uncertain records, and use open-ended analysis to propose new categories. Then I would label examples, evaluate the revised questions, and backfill comparable periods. Otherwise the dashboard can look stable simply because its vocabulary stopped evolving.
Reddit is a research corpus, with a boundary
The Reddit version interests me even more: a deliberately scoped collection of posts and comments about a product or buying decision.
Suppose I want to investigate why people discussing home espresso equipment regret a purchase. I would define the communities, time window, collection method, and inclusion criteria first. Then I could classify first-hand reports, price objections, maintenance complaints, and explicit alternatives people are considering.
A comment like “same here” needs its parent. A sarcastic reply can reverse the apparent meaning. I would pass the relevant context with the comment and make clear which text is the target of the judgment. I would also retain thread relationships so that one lively discussion doesn't masquerade as fifty independent purchasing experiences.
Collection needs its own implementation, using access appropriate to the source. Jev begins after we have the records; it doesn't solve that access problem.
What could the output honestly say? Something like: “Within this collected set of discussions, maintenance complaints were more frequent among posts describing regret.”
It could not establish the percentage of all espresso-machine owners who regret buying one. A million comments still reflect the people, communities, search terms, and periods that brought those comments into the dataset.
Scale makes a sample bigger. It doesn't automatically make it representative.
Honestly, this is where I think the engineering gets interesting. Processing the text cheaply is only one part. Defining what the resulting number means is the part the product has to get right.
The price changes what is worth trying
As checked on September 17, 2026, TypeSafe's parallel-questions cookbook uses a Jev input price of $0.042 per million tokens and zero output-token cost. That is a published pricing assumption, not my measured bill or a guarantee of future pricing.
At that rate, a hypothetical million records averaging 1,000 total billed input tokens each would cost $42 for model input. “Total” matters: the budget must include the text, context, questions, and other billed request content. Collection, storage, retries, evaluation, and human review are separate costs.
That makes a broad enrichment pass worth considering. I would still measure tokens and throughput on a realistic pilot before extrapolating.
The same cookbook reports roughly 12.2× lower cost and 10× lower latency for batching thirteen questions over one document instead of asking them separately. That is a vendor example of batching, not evidence that my million-review pipeline will finish ten times faster.
For me, the appealing consequence is practical: I can consider retaining several narrowly defined signals per record instead of asking one overloaded question and hoping its answer will support every future analysis.
Correct types still need correct judgments
TypeSafe's launch discussion distinguishes schema correctness from decision correctness. A model constrained to valid choices can still choose the wrong one. Its “zero hallucinations” language should be read in that narrower sense. Launch explanation.
I would start with a labeled evaluation set spanning clear examples, mixed opinions, sarcasm, short replies, and the languages actually present in the corpus. For each signal, I would check false positives and missed cases. A packaging-complaint filter and a claim about purchase intent may need different acceptance rules.
A returned probability also deserves validation on that specific task. I wouldn't interpret every 0.9 as a demonstrated 90% correctness rate in my data. TypeSafe's confidence documentation explains the supplied measures; whether they support my operating threshold still needs evidence from the intended workload.
Ambiguous cases can remain uncertain, go to human review, or receive a second analysis. If I exclude them from a chart, I want their count visible. Quietly dropping the hard cases can make a trend look cleaner than the underlying evidence.
Finally, a writing model should receive computed aggregates, definitions, coverage limitations, and selected source excerpts. It can help explain the results. It should not invent the counts, turn a correlation into a cause, or claim that a few selected quotes prove a population-wide trend.
The useful thing is what becomes queryable
I keep coming back to that restaurant with ten thousand reviews.
The review text already contains more detail than the star rating can express. What I want is a way to preserve some of that detail in a form ordinary software can work with, while keeping the original words close enough to challenge the interpretation.
That is the role I would give Jev: a focused processing stage between human language and the database. The report comes later. First I want to know which columns are worth creating, how reliably we can populate them, and what decisions they actually help someone make.
If you have a pile of reviews or community discussions that your current dashboard can't explain, I'd be interested to hear which missing column you wish you could query.
More than a blog post
I share frontend news and the reasoning behind it throughout the day. Pick the language that feels natural to you.