<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: gobuy.ai</title>
    <description>The latest articles on DEV Community by gobuy.ai (@gobuy).</description>
    <link>https://dev.to/gobuy</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3830071%2F9d84ca49-b872-440b-937f-0fa589c54ae8.jpg</url>
      <title>DEV Community: gobuy.ai</title>
      <link>https://dev.to/gobuy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gobuy"/>
    <language>en</language>
    <item>
      <title>The Shape of a Review History: Trajectory Analysis for Trust Scoring</title>
      <dc:creator>gobuy.ai</dc:creator>
      <pubDate>Wed, 14 Oct 2026 08:31:21 +0000</pubDate>
      <link>https://dev.to/gobuy/the-shape-of-a-review-history-trajectory-analysis-for-trust-scoring-g3e</link>
      <guid>https://dev.to/gobuy/the-shape-of-a-review-history-trajectory-analysis-for-trust-scoring-g3e</guid>
      <description>&lt;p&gt;When we started scoring products at GoBuy, the obvious first move was treating the review history as a &lt;em&gt;pile&lt;/em&gt;: aggregate the ratings, compute a Wilson confidence interval, ship a score. That works fine for a book with 400 reviews accumulated over six years at a steady 4.2 stars. It fails catastrophically for the products fraudsters care about — the ones whose histories look fine in aggregate but wrong in &lt;em&gt;shape&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This post is about the second layer of our pipeline: trajectory analysis. We stopped asking "what is the average rating?" and started asking "how did this product get here?" Because in 2026, with agents writing synthetic reviews at scale, the &lt;em&gt;shape&lt;/em&gt; of a review history is more informative than its content.&lt;/p&gt;

&lt;h2&gt;
  
  
  The intuition
&lt;/h2&gt;

&lt;p&gt;A real product's rating history is noisy but boring. New products get a small burst of early reviews (friends, enthusiasts, people who buy on launch day), then a long tail. The average drifts slightly as the product finds its audience. Sometimes there's a dip after a bad batch, and a recovery. Human purchasing is lumpy and seasonal.&lt;/p&gt;

&lt;p&gt;A manipulated history has a different signature. The most common one looks like this: months of near-silence, then a short window of intense 5-star activity, then back to silence. The aggregate numbers can look great. The trajectory looks like a heart attack on an ECG.&lt;/p&gt;

&lt;p&gt;Here's a toy example we use internally for testing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;

&lt;span class="n"&gt;rng&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;default_rng&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Organic: steady, noisy
&lt;/span&gt;&lt;span class="n"&gt;organic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;concatenate&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="n"&gt;rng&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;normal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;4.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;   &lt;span class="c1"&gt;# launch bump
&lt;/span&gt;    &lt;span class="n"&gt;rng&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;normal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;4.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;   &lt;span class="c1"&gt;# long tail
&lt;/span&gt;    &lt;span class="n"&gt;rng&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;normal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;3.9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;   &lt;span class="c1"&gt;# slight decay
&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;# Manipulated: silence, burst, silence
&lt;/span&gt;&lt;span class="n"&gt;manipulated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;concatenate&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="n"&gt;rng&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;normal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;3.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;   &lt;span class="c1"&gt;# early genuine reviews, mediocre
&lt;/span&gt;    &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;full&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;5.0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;           &lt;span class="c1"&gt;# the burst
&lt;/span&gt;    &lt;span class="n"&gt;rng&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;normal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;3.6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;   &lt;span class="c1"&gt;# back to reality
&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same count of reviews. Both average around 4.0. One of them you'd buy. The other you'd report.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decomposing the signal
&lt;/h2&gt;

&lt;p&gt;We compute four features over the full timestamped history. All of them are cheap — no models, no GPU, just statistics you can run in a few milliseconds per product.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Burst density.&lt;/strong&gt; For each sliding window of width &lt;em&gt;w&lt;/em&gt; (we use 48 hours for marketplace data), compute the share of lifetime reviews that fall inside it. For an organic product, the maximum window share is bounded — you'd need a viral moment to concentrate 40% of a year's reviews into two days, and viral moments leave traces elsewhere (sales rank, cart-adds, external mentions). For an inflated product, the max window share is often the single most predictive feature we track.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Rating entropy.&lt;/strong&gt; Real reviewers disagree. Even beloved products collect 3s and the occasional 1. A history where 95% of reviews are exactly 5 stars has near-zero entropy, and zero-entropy histories with n &amp;gt; 30 reviews are, in our data, almost never organic. We use Shannon entropy over the discrete rating distribution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;scipy.stats&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;entropy&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;rating_entropy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ratings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ndarray&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;counts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bincount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ratings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;minlength&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;counts&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;entropy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A fully concentrated history scores 0.0 bits; a uniform spread over the five star levels scores ~2.32 bits. Organic products cluster between 1.1 and 1.8 bits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Regime change points.&lt;/strong&gt; We run a simple change-point detection (CUSUM on the mean rating) and look for sharp upward jumps that coincide with volume jumps. A product going from 3.4 → 4.9 average while its review velocity triples is not "improving." Real quality improvements show up in the &lt;em&gt;text&lt;/em&gt; and in return-rate signals long before the star average moves a full point.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Reviewer overlap.&lt;/strong&gt; This one crosses products. If 30% of a product's reviewers also reviewed three other products from the same seller — all in the same week — that's a ring, not a fandom. (Cross-product analysis deserves its own post; the short version is that we build a bipartite graph of reviewers × sellers and look for dense subgraphs.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Why not just use an LLM?
&lt;/h2&gt;

&lt;p&gt;We get this question constantly, including from our own team. Why bother with CUSUM and entropy when a language model can read the reviews and tell you which ones feel fake?&lt;/p&gt;

&lt;p&gt;Three reasons. First, &lt;strong&gt;cost&lt;/strong&gt;: we score hundreds of thousands of products; LLM passes per product per refresh would be orders of magnitude more expensive than computing four statistics over a vector. Trajectory features are computed in Postgres with window functions, roughly for free.&lt;/p&gt;

&lt;p&gt;Second, &lt;strong&gt;determinism&lt;/strong&gt;: the score a user sees must be reproducible. If someone disputes a trust score, we can show exactly which features fired, what the burst density was, when the change point occurred. "The model said so" is not an audit trail.&lt;/p&gt;

&lt;p&gt;Third, trajectory features catch what text analysis &lt;em&gt;can't&lt;/em&gt;. The fake reviews of 2026 are fluent, specific, and stylistically indistinguishable from real ones — they're written by models that have read a million genuine reviews. But the &lt;em&gt;timing&lt;/em&gt; of a paid campaign is set by a contract and a deadline, not by human enthusiasm. Language can be synthesized. Logistics can't.&lt;/p&gt;

&lt;p&gt;The two layers compose nicely: trajectory analysis flags &lt;em&gt;which&lt;/em&gt; products deserve scrutiny, and language features (which we do run, selectively) determine whether the flagged text itself is synthetic. Cheap first, expensive second.&lt;/p&gt;

&lt;h2&gt;
  
  
  The composite signal
&lt;/h2&gt;

&lt;p&gt;Each feature is normalized against a per-category baseline — because a niche hobby product with 60 reviews behaves differently from a phone case with 8,000, and "burst" means different things at those scales. The normalized features feed a logistic scorer whose weights we re-fit quarterly against a labeled set (products removed by marketplaces for review manipulation — the best ground truth available, even if it's conservative).&lt;/p&gt;

&lt;p&gt;The output isn't a verdict. It's one input into the composite Smart Score, weighted so that a single suspicious feature can't tank an honest product, but a converging pattern — burst + low entropy + change point + reviewer overlap — pushes the score down hard and fast.&lt;/p&gt;

&lt;p&gt;That design decision matters. False positives on &lt;em&gt;legitimate&lt;/em&gt; small sellers are the worst failure mode a trust system can have: the seller loses their livelihood, and we lose the credibility that makes the score worth computing. Trajectory analysis earns its keep precisely because it constrains the accusations to products whose histories are, statistically, almost impossible.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;The current frontier for us is reviewer-side trajectories instead of product-side: modeling each reviewer's &lt;em&gt;own&lt;/em&gt; history — their entropy, their timing, their category spread — so that a suspicious product's score can be decomposed into "bad product" versus "bad reviewers." Early results are promising and the math is pretty. That'll be the next post in this series.&lt;/p&gt;

&lt;p&gt;If you want to see the scoring standard we anchor all of this to, it's public at &lt;a href="https://docs.gobuy.ai/standard" rel="noopener noreferrer"&gt;docs.gobuy.ai/standard&lt;/a&gt; — critique welcome, especially the parts about category baselines, which are the hardest thing we've published.&lt;/p&gt;

</description>
      <category>python</category>
      <category>datascience</category>
      <category>statistics</category>
      <category>ecommerce</category>
    </item>
    <item>
      <title>A 48-Hour Burst of 5-Star Reviews Is Never Organic: How We Detect Review Inflation</title>
      <dc:creator>gobuy.ai</dc:creator>
      <pubDate>Tue, 13 Oct 2026 08:32:20 +0000</pubDate>
      <link>https://dev.to/gobuy/a-48-hour-burst-of-5-star-reviews-is-never-organic-how-we-detect-review-inflation-225p</link>
      <guid>https://dev.to/gobuy/a-48-hour-burst-of-5-star-reviews-is-never-organic-how-we-detect-review-inflation-225p</guid>
      <description>&lt;p&gt;&lt;em&gt;Part of the &lt;a href="https://dev.to/gobuy"&gt;Trust Engineering&lt;/a&gt; series, where we open-source the math behind &lt;a href="https://gobuy.ai" rel="noopener noreferrer"&gt;GoBuy&lt;/a&gt;'s product trust scores. Code is Python; the ideas are deliberately simple enough to reimplement in an afternoon.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;A few weeks ago someone asked us a question that sounds trivial and isn't: &lt;strong&gt;"How do you tell the difference between a product going viral and a product being review-bombed with fakes?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Both look identical at first glance. A product that sat at 40 reviews for eight months suddenly gets 300 five-star reviews in two days. Maybe a TikTok went viral. Maybe a seller bought 300 reviews. The aggregate rating — the only thing most tools look at — moves the same direction in both cases.&lt;/p&gt;

&lt;p&gt;The difference is in the &lt;strong&gt;shape of arrival times&lt;/strong&gt;. This post walks through how we detect review bursts, why we score inflation and suppression bursts differently, and the two statistical traps we fell into before landing on the current design.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why aggregate ratings can't see this
&lt;/h2&gt;

&lt;p&gt;If you average 2,000 reviews, adding 300 more barely moves the mean. That's the whole point of a mean — it's robust to dilution. Coordinated manipulators know this: they don't need to flip your average, they only need to nudge it a few points and keep it there. Aggregates are also memoryless about &lt;em&gt;ordering&lt;/em&gt;: a burst at the start of a product's life and a burst last Tuesday produce the same star average.&lt;/p&gt;

&lt;p&gt;So the first design decision: &lt;strong&gt;treat review arrival as a time series, not a bag of stars.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Model arrivals as a Poisson process
&lt;/h2&gt;

&lt;p&gt;Organic review traffic for a mature product is roughly stationary: reviews trickle in at a rate λ proportional to current sales volume. That makes arrival counts in fixed windows approximately Poisson.&lt;/p&gt;

&lt;p&gt;For each product we maintain an EWMA of the arrival rate over the trailing 90 days, then test each day's observed count &lt;code&gt;k&lt;/code&gt; against &lt;code&gt;Poisson(λ̂)&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;scipy&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;stats&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;burst_score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;daily_counts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ewma_lambda&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Probability that the last `window` days are NOT explained
    by the product&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s own organic arrival rate.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;expected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ewma_lambda&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;window&lt;/span&gt;
    &lt;span class="n"&gt;observed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;daily_counts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;:])&lt;/span&gt;
    &lt;span class="c1"&gt;# upper tail: how surprising is this burst, given normal traffic?
&lt;/span&gt;    &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;poisson&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;observed&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;surprise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;surprise&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;code&gt;surprise&lt;/code&gt; above 0.999 on a mature product means the last three days saw traffic that the product's own history essentially cannot explain. By itself, that's a viral product OR a manipulation campaign. It's a tripwire, not a verdict.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Condition the burst on &lt;em&gt;what kind&lt;/em&gt; of reviews arrived
&lt;/h2&gt;

&lt;p&gt;This is the step that separates virality from manipulation, and it comes straight from the data: &lt;strong&gt;organic bursts are rating-diverse, manufactured bursts are not.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When real humans discover a product, they arrive with real experiences — you get a spread of 4s and 5s, some 3s, the occasional angry 1-star. When a seller buys reviews, they buy one SKU: 5-star, unverified, short text. So we split the burst by rating value and verification status and compute the surprise score &lt;em&gt;per segment&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;manipulation_evidence&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;burst_reviews&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;product_baseline&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;five_star&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;burst_reviews&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rating&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;unverified&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;five_star&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;verified_purchase&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# share of burst that is 5-star vs product's historical 5-star share
&lt;/span&gt;    &lt;span class="n"&gt;five_share&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;five_star&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;burst_reviews&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;share_uplift&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;five_share&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;product_baseline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;five_star_share&lt;/span&gt;

    &lt;span class="c1"&gt;# unverified ratio among the 5-star burst
&lt;/span&gt;    &lt;span class="n"&gt;unverified_ratio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unverified&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;five_star&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;share_uplift&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unverified_ratio&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our operating heuristic after tuning on labeled data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Burst + 5-star share ≥ 0.9 + unverified ratio ≥ 0.6&lt;/strong&gt; → inflation campaign. Strong evidence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Burst + ratings spread matches historical&lt;/strong&gt; → probably genuine virality. No penalty, just flag for re-scoring.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Burst of 1-stars&lt;/strong&gt; → review bombing (a competitor, a brigade). Also penalized, but scored separately, because the &lt;em&gt;product&lt;/em&gt; may be fine — the review section is what's poisoned.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last case matters more than people realize. Roughly a third of the bursts we see are negative, and naive detectors that only hunt 5-star spikes misclassify them as "engagement."&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: The trajectory view — bursts in context
&lt;/h2&gt;

&lt;p&gt;A burst in isolation is ambiguous; a burst in a &lt;em&gt;trajectory&lt;/em&gt; usually isn't. We bucket every product's rating history into three regimes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Steady state&lt;/strong&gt; — arrival rate flat, rating distribution stable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step change&lt;/strong&gt; — arrival rate shifts to a new plateau (price cut, ranking jump, viral moment).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spike and decay&lt;/strong&gt; — a burst that fades back to baseline.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The dangerous signature is &lt;strong&gt;spike-and-decay with a rating gap&lt;/strong&gt;: the burst reviews average 4.9 while the pre-burst baseline averages 3.8 and the post-burst reviews snap back to 3.9. Real virality brings in customers whose experiences match the baseline population. Manufactured bursts insert a population whose experiences were never real, so the ratings before and after agree with each other and disagree with the burst. When before/after agree and the burst is the outlier, the burst is the anomaly — regardless of content.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;rating_gap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;baseline_before&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;burst&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;baseline_after&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;burst&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mean&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;baseline_before&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mean&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;baseline_after&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A gap above ~0.8 stars with burst volume &amp;gt; 10% of lifetime reviews is, in our labeled set, almost always manipulation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two traps
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Trap one: young products.&lt;/strong&gt; Everything above assumes an EWMA baseline exists. A product three days old with 200 reviews has no baseline; the burst detector is undefined. For young products we fall back to category priors (how fast do products in this category &lt;em&gt;normally&lt;/em&gt; accumulate reviews) — that's a whole separate post.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trap two: legitimate sales events.&lt;/strong&gt; Prime Day produces genuine bursts that look structurally like campaigns: volume spike, 5-star heavy, verified though — which is the tell. Verification status is the single highest-signal feature we have, which is also exactly why it's the one the paid-review market has been hardest to fake at scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the agent sees
&lt;/h2&gt;

&lt;p&gt;All of this condenses to one flag and one number in our MCP output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"smart_score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"flags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"temporal_burst"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rating_gap"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"burst"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"window_days"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"surprise"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.9998&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"direction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"inflation"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An AI shopping agent doesn't need to reimplement any of this — it needs a reason not to recommend the product, expressed in one line. The full scoring spec is documented at &lt;a href="https://docs.gobuy.ai/standard" rel="noopener noreferrer"&gt;docs.gobuy.ai/standard&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Takeaways if you're building anything similar:&lt;/strong&gt; model arrivals, not aggregates; condition bursts on rating composition and verification; and always check whether the burst is the outlier relative to its own before/after baselines. The math is 60 lines of Python. The labeled data is the hard part — and the reason we publish the method instead of the model.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Questions, edge cases, or products you think would break this? That's genuinely what the comments are for.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>statistics</category>
      <category>datascience</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>Nobody Reads Reviews Anymore. Not People, Not Agents — So We Built the Reader</title>
      <dc:creator>gobuy.ai</dc:creator>
      <pubDate>Mon, 12 Oct 2026 07:35:53 +0000</pubDate>
      <link>https://dev.to/gobuy/nobody-reads-reviews-anymore-not-people-not-agents-so-we-built-the-reader-5624</link>
      <guid>https://dev.to/gobuy/nobody-reads-reviews-anymore-not-people-not-agents-so-we-built-the-reader-5624</guid>
      <description>&lt;p&gt;Salesforce's 2026 holiday data has a line that should stop everyone building shopping agents: &lt;strong&gt;41% of shoppers skip reading reviews entirely when a brand's AI assistant answers their questions.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not "read fewer reviews." Skip entirely. The assistant's answer replaces the corpus.&lt;/p&gt;

&lt;p&gt;Combine that with what was already true — humans never read all 500 reviews anyway; we read the first ten and the star average — and you get the strange situation this post is about:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nobody reads reviews anymore. Not people. Not agents.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The agent side of the story
&lt;/h2&gt;

&lt;p&gt;It's tempting to think AI agents fix this. They don't — for a mechanical reason: &lt;strong&gt;context windows can't afford the corpus either.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A product with 500 reviews averages maybe 150,000 words. An agent doing a multi-product comparison can't ingest that per product and still function. So what do agents actually do? They read the star average, maybe the first few reviews, and reason from the listing copy — which is exactly the shallow read a tired human does at midnight, except the agent does it confidently, at scale, and calls it research.&lt;/p&gt;

&lt;p&gt;And the corpus it skims is contaminated. Estimates put fake, incentivized, or AI-generated reviews at 30–40% of volume on major marketplaces. Trustpilot removed 4.5M fake reviews from a single platform in a year. The star average agents inherit is a blend of real sentiment and industrial manipulation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trust transfer
&lt;/h2&gt;

&lt;p&gt;Here's the part that should worry the people building those assistants: when a shopper stops reading reviews because the assistant answered, trust doesn't disappear — it transfers. From an open, if polluted, corpus... to a closed one controlled by the seller.&lt;/p&gt;

&lt;p&gt;The assistant's knowledge of the product comes from the product page. The product page is written by the seller. The 41% who skip reviews have moved their trust from "what everyone said" to "what the brand told the AI." That's not an AI problem; it's a single-source problem wearing an AI hat.&lt;/p&gt;

&lt;h2&gt;
  
  
  So we built the reader
&lt;/h2&gt;

&lt;p&gt;At GoBuy we took the obvious-but-awkward position: if no one in the transaction reads the reviews, the reading itself has to be a service.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What "reading" means when a machine does it seriously:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Per-review forensics.&lt;/strong&gt; Every review gets individual assessment: authenticity signals, Verified Purchase cross-checks, linguistic and temporal anomaly flags. Not corpus vibes — per-item inspection.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Statistical aggregation that respects sample size.&lt;/strong&gt; A Wilson-interval-style lower bound on the positive rate, so a 5.0 from 10 reviews loses to a 4.6 from 2,400. Sample-size honesty, built into the number. (A statistician publicly caught one of our example numbers being wrong recently; the correction is in our writeup. That exchange is part of how this should work.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Longitudinal signals.&lt;/strong&gt; Rating velocity, burst detection, trajectory shape — the distributional evidence that survives even when AI-generated text becomes stylistically undetectable. A brushed product has a &lt;em&gt;shape&lt;/em&gt; over time, regardless of how good the fake prose reads.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A disclaimer we actually mean.&lt;/strong&gt; The output is an Evidence Score: what the marketplace record supports. Not product quality, not safety, not a purchase recommendation. Evidence ≠ quality — and an evidence layer that forgets that becomes just another recommender with better branding.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The two doors
&lt;/h2&gt;

&lt;p&gt;The same reading serves both audiences who stopped reading:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Humans&lt;/strong&gt; get the score where they shop (browser extension, badges) — the 30-second answer the 500 reviews were supposed to provide.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agents&lt;/strong&gt; get it as an MCP tool (&lt;code&gt;check_product_trust&lt;/code&gt;, free, keyless) and a spec — &lt;a href="https://docs.gobuy.ai/standard" rel="noopener noreferrer"&gt;GPV-1&lt;/a&gt;, eight conditions that should hold before any autonomous checkout. Consent doesn't skip evidence.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Same reader. Same score. Two doors.&lt;/p&gt;

&lt;h2&gt;
  
  
  The uncomfortable summary
&lt;/h2&gt;

&lt;p&gt;The 2026 holiday season is the first with AI-mediated shopping at scale: Adobe projects $275B online, a fifth of traffic influenced by agents. Every one of those transactions leans on a corpus nobody reads — human or machine.&lt;/p&gt;

&lt;p&gt;Somebody has to do the reading. We decided it should be something you can audit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it:&lt;/strong&gt; the server is open source (MIT) at &lt;a href="https://github.com/gobuy-ai/gobuy-mcp" rel="noopener noreferrer"&gt;github.com/gobuy-ai/gobuy-mcp&lt;/a&gt;, the endpoint is &lt;a href="https://mcp.gobuy.ai/mcp" rel="noopener noreferrer"&gt;mcp.gobuy.ai/mcp&lt;/a&gt;, and the methodology series continues daily here.&lt;/p&gt;

&lt;p&gt;More: &lt;a href="https://gobuy.ai" rel="noopener noreferrer"&gt;https://gobuy.ai&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>ecommerce</category>
      <category>machinelearning</category>
      <category>agenticai</category>
    </item>
    <item>
      <title>How We Built a Fake-Review Detector Using Wilson Confidence Intervals and Bayesian Inference</title>
      <dc:creator>gobuy.ai</dc:creator>
      <pubDate>Sun, 11 Oct 2026 10:35:26 +0000</pubDate>
      <link>https://dev.to/gobuy/how-we-built-a-fake-review-detector-using-wilson-confidence-intervals-and-bayesian-inference-gij</link>
      <guid>https://dev.to/gobuy/how-we-built-a-fake-review-detector-using-wilson-confidence-intervals-and-bayesian-inference-gij</guid>
      <description>&lt;p&gt;Can math tell you whether those 4.9-star reviews are real?&lt;/p&gt;

&lt;p&gt;Not with certainty — but with confidence, and the difference between the two is where the interesting engineering lives. This post walks through the statistical core of GoBuy's scoring: two layers of classical inference applied to marketplace reviews, what each one is actually for, and the trap hiding in the most obvious approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Amazon lists over 300 million products, and conservative estimates put fake, AI-generated, or incentivized reviews at 30–40% of the total — a share that grows as LLMs make generation free. Existing tools (Fakespot, ReviewMeta) answered this with proprietary black boxes. We wanted the opposite: a system where every number is derivable from first principles, so anyone can audit why a product scores the way it does.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 1: the Wilson score lower bound — sample-size honesty for &lt;em&gt;ranking&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;The naive average ignores how many reviews it rests on. A product with 10 reviews at 100% positive and a product with 1,000 reviews at 95% positive both "average" near the top — but they are not equally believable.&lt;/p&gt;

&lt;p&gt;The Wilson score interval fixes this. For observed positive proportion &lt;code&gt;p&lt;/code&gt; over &lt;code&gt;n&lt;/code&gt; reviews at 95% confidence (z = 1.96):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lower = (p + z²/2n − z·√(p(1−p)/n + z²/4n²)) / (1 + z²/n)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Worked examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;480/500 positive (96%)&lt;/strong&gt; → lower bound &lt;strong&gt;0.939&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;950/1,000 positive (95%)&lt;/strong&gt; → lower bound &lt;strong&gt;0.935&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;10/10 positive (100%)&lt;/strong&gt; → lower bound &lt;strong&gt;0.72&lt;/strong&gt; — a perfect-but-tiny record still leaves real uncertainty&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The 10-review product drops below products with hundreds of slightly-less-perfect reviews. That is the point: &lt;strong&gt;the Wilson layer is a ranking tool that makes claims honest about their sample size.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One thing this layer is &lt;em&gt;not&lt;/em&gt;: a fraud detector. A lower bound on the positive rate measures how &lt;em&gt;liked&lt;/em&gt; a product appears to be, not whether the reviews are real — a coordinated burst raises the rate and shrinks the interval, which is exactly what an attacker produces. Fraud detection lives in different signals entirely (velocity, trajectory shape, verified-purchase mismatches — a future post).&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 2: the Beta-Binomial model — beliefs that update over time
&lt;/h2&gt;

&lt;p&gt;Wilson gives a snapshot. Reviews accumulate, and we want beliefs that update incrementally.&lt;/p&gt;

&lt;p&gt;Treat the true positive rate as a Beta-distributed random variable. Start with Beta(1,1) (uniform — no prior opinion), then each positive review nudges the parameters up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Beta(α₀ + positives, β₀ + negatives)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;480/500 → Beta(481, 21). The 5th percentile of that posterior is &lt;strong&gt;0.9425&lt;/strong&gt; — note how close it sits to Wilson's one-sided 0.943 bound. That is not a coincidence: &lt;strong&gt;with a flat prior, both layers are functions of the same two counts, and they agree to the third decimal.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So why keep the Bayesian layer at all? Because its value is not in the flat-prior case — it is in what the flat prior can't do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Real priors.&lt;/strong&gt; A category-level base rate (e.g., "electronics in this price band average 4.1 stars") gives the posterior something to learn &lt;em&gt;from&lt;/em&gt;, pulling thin histories toward category reality instead of letting 10 reviews dominate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sequential updates.&lt;/strong&gt; New evidence arrives daily; the posterior updates in O(1) without recomputing history.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regime changes.&lt;/strong&gt; A prior informed by recent behavior makes sudden distribution shifts visible faster.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The honest engineering summary: with Beta(1,1), the Bayesian layer is nearly the same signal as Wilson counted twice — it earns independent weight only when it carries real prior structure and the temporal dimension. That is how it runs in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the composite actually measures
&lt;/h2&gt;

&lt;p&gt;Neither layer measures review &lt;em&gt;authenticity&lt;/em&gt;. Together they measure how much the observed rating deserves its number — the evidence-quality backbone of a trust score. Authenticity (burst detection, trajectory anomalies, verified-purchase mismatches) is a separate family of longitudinal signals layered on top, and it deserves its own post.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The composite, briefly:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wilson lower bound — ranking honesty about sample size (~35%)&lt;/li&gt;
&lt;li&gt;Bayesian posterior with category priors — temporal + prior structure (~30%)&lt;/li&gt;
&lt;li&gt;Longitudinal authenticity signals — burst, trajectory, provenance (the rest; future post)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;The full corpus runs at &lt;a href="https://gobuy.ai" rel="noopener noreferrer"&gt;gobuy.ai&lt;/a&gt;; the gate that consumes it is &lt;a href="https://docs.gobuy.ai/standard" rel="noopener noreferrer"&gt;GPV-1&lt;/a&gt;, our open pre-purchase verification spec — one call, &lt;code&gt;check_product_trust&lt;/code&gt;, returns the evidence score an agent should see before checkout. The reference implementation is &lt;a href="https://github.com/digitalmentes7-maker/verify-then-checkout" rel="noopener noreferrer"&gt;open source&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Corrections: an earlier version stated 0.69 for the 10/10 Wilson example; the correct value is 0.72 (thanks to &lt;a class="mentioned-user" href="https://dev.to/arhancanli"&gt;@arhancanli&lt;/a&gt; for catching it). This version also clarifies the division of labor between the two statistical layers, following his equally fair observation that they nearly coincide under a flat prior.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>statistics</category>
      <category>datascience</category>
    </item>
  </channel>
</rss>
