<?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: Sonny Levine</title>
    <description>The latest articles on DEV Community by Sonny Levine (@sl_elaira).</description>
    <link>https://dev.to/sl_elaira</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3906626%2F3c70d971-6e24-47bf-8891-034f978d2870.jpg</url>
      <title>DEV Community: Sonny Levine</title>
      <link>https://dev.to/sl_elaira</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sl_elaira"/>
    <language>en</language>
    <item>
      <title>Inside ShopPilot’s AI Content Engine</title>
      <dc:creator>Sonny Levine</dc:creator>
      <pubDate>Thu, 30 Apr 2026 20:51:54 +0000</pubDate>
      <link>https://dev.to/sl_elaira/inside-shoppilots-ai-content-engine-n85</link>
      <guid>https://dev.to/sl_elaira/inside-shoppilots-ai-content-engine-n85</guid>
      <description>&lt;p&gt;Every time we showed a Shopify merchant a generic AI-generated post, they spotted it instantly. Not because AI can't write â€” it can. Because it doesn't know &lt;em&gt;them&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The post would be grammatically perfect, energetic, on-trend. And it would sound like everyone else. The merchant knew it. Their customers would know it. And the post would die quietly in a feed full of posts that sounded exactly the same.&lt;/p&gt;

&lt;p&gt;That's the problem we set out to fix when we built ShopPilot's content engine. This post explains how it actually works.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: Generic AI Content is Identifiable
&lt;/h2&gt;

&lt;p&gt;The first version of our content generator worked the way most AI content tools work: take a product description, feed it to the model, get a post back. It was fast. It was coherent. And it was useless.&lt;/p&gt;

&lt;p&gt;We ran a simple test: we generated 10 posts for 5 different brands â€” a candle shop, a fitness coach, a jewelry maker, a sustainable clothing brand, and a coffee roaster. Then we shuffled them. Three merchants out of five correctly matched a post to the wrong brand within 30 seconds. The posts were good. They were just interchangeable.&lt;/p&gt;

&lt;p&gt;The mistake was treating the generation problem as a writing problem. It's not. It's a &lt;em&gt;context&lt;/em&gt; problem. The model has to know who this brand sounds like, who they're talking to, and what they're actually trying to say â€” before it generates the first word.&lt;/p&gt;

&lt;h2&gt;
  
  
  Brand-First Prompting: Encoding Voice Before Product
&lt;/h2&gt;

&lt;p&gt;The fix was building a brand context layer that loads before every generation call. When a merchant sets up ShopPilot, they configure four things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Voice:&lt;/strong&gt; Playful, Premium, No-Nonsense, Bold, or Friendly â€” not just a tone label, but a style pattern the model is instructed to match&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audience:&lt;/strong&gt; Who they're writing for (age range, intent, familiarity with the brand)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Product context:&lt;/strong&gt; What they sell, what makes it different, what claims they can and can't make&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platform:&lt;/strong&gt; Twitter/X, Instagram, Facebook â€” each platform gets a different length constraint, hashtag density, and CTA pattern&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These aren't prompts bolted onto the front of a generation call. They're encoded into a brand profile that's embedded into every request, so the model is reasoning about voice before it's reasoning about content. The result is that when you generate a post for Luminary Gems on Instagram vs. FitLife Coach on Twitter, you get two posts that sound like they came from two completely different brands â€” because the context that precedes the generation is completely different.&lt;/p&gt;

&lt;p&gt;Here's what the system prompt structure looks like in simplified form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are writing social content for [brand_name].
Voice: [voice_type] â€” [voice_description]
Audience: [audience_description]
What they sell: [product_context]
Platform: [platform] â€” [platform_constraints]
Rules: [brand_rules]

Write a [platform]-native post for: [user_prompt]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The brand rules field is where things get interesting. A premium candle brand can't say "cheap." A fitness coach can't make specific weight-loss claims. A jewelry shop might have a house rule about never using the word "luxury" because it reads as try-hard to their audience. These rules are applied as hard constraints in the system prompt, not soft suggestions. The model is instructed to treat a rule violation as a generation failure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Confidence Scoring: What We Measure and Why We Show It
&lt;/h2&gt;

&lt;p&gt;Every post that comes out of ShopPilot's engine gets a confidence score before it reaches the merchant. This is probably the decision we've gotten the most questions about â€” most AI tools hide their uncertainty. We surface it explicitly. Here's why.&lt;/p&gt;

&lt;p&gt;The score is a composite of three signals:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Off-brand risk:&lt;/strong&gt; Does the post use language, claims, or tone that conflicts with the brand profile? A post for a "No-Nonsense" brand that starts with three exclamation points is off-brand, even if it reads well in isolation. The model scores its own output against the brand constraints and flags deviations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Factual claim density:&lt;/strong&gt; Posts with specific numbers ("increase sales by 40%"), health-adjacent claims ("heals dry skin"), or superlatives ("the best coffee in the city") carry higher risk. We flag these because they're the claims most likely to get a merchant in trouble â€” either legally or just with their audience.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platform fit:&lt;/strong&gt; A 280-character post with three hashtags fits Twitter. A post with no emojis and a 400-word caption doesn't fit Instagram. Platform fit scores how well the structural format of the generated post matches the expected conventions of the target platform.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A post that hits 85%+ confidence on all three dimensions gets a green flag. Below 70%, it gets a yellow â€” it's not wrong, but there's something worth reviewing. Below 55%, we don't surface it at all and regenerate automatically.&lt;/p&gt;

&lt;p&gt;We show the score because merchants who can see &lt;em&gt;why&lt;/em&gt; a post was flagged edit it better than merchants who are just told "this needs work." A yellow flag with "off-brand risk: post uses casual language inconsistent with Premium voice profile" is actionable. A vague "review before posting" isn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Human-in-the-Loop: Why We Chose Approval Over Autopilot
&lt;/h2&gt;

&lt;p&gt;We could have built full autopilot â€” generate, schedule, post, done. Tools like &lt;a href="https://buffer.com" rel="noopener noreferrer"&gt;Buffer&lt;/a&gt; and &lt;a href="https://hootsuite.com" rel="noopener noreferrer"&gt;Hootsuite&lt;/a&gt; give you scheduling. Some newer tools now offer auto-posting. We deliberately didn't go that route for the first version, and the reason is data.&lt;/p&gt;

&lt;p&gt;Merchants who approve posts before they go live catch about 1 in 8 posts that they'd have wanted to edit. That's a 12.5% error rate on content that the model thought was high-confidence. In the early months of a brand building its social presence, a 12.5% error rate in public is noticeable. You can't un-post a product caption that accidentally made a claim your product doesn't deliver on.&lt;/p&gt;

&lt;p&gt;The approval workflow in ShopPilot is designed to be low-friction. Posts are queued in a content calendar. One click approves. One click regenerates with feedback. The merchant sees the confidence score, the platform preview, and the generated content side by side. Average review time for a high-confidence post is under 10 seconds.&lt;/p&gt;

&lt;p&gt;Full autopilot is on the roadmap â€” but gated behind 30 days of approved posts for a brand. If a merchant has approved 120 posts and the model has learned their correction patterns, the error rate drops below 3%. At that point, autopilot makes sense. Before that, we think requiring a human to stay in the loop is the right call â€” not because we don't trust the model, but because the model needs those 30 days of feedback to actually know the brand.&lt;/p&gt;

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

&lt;p&gt;Three things on the near-term roadmap:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multilingual generation:&lt;/strong&gt; A significant portion of our merchants sell across markets where English isn't the primary customer language. We're adding Spanish, French, and Portuguese as first-class voice targets â€” not translations of English posts, but brand-native generation in each language.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A/B content variants:&lt;/strong&gt; Instead of generating one post, generate three â€” same brief, different angles. The merchant picks the one that matches their read on the moment. Over time, we track which variants convert better and weight the model toward those patterns for that brand.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Image generation:&lt;/strong&gt; The words are working. The missing piece is pairing AI-generated copy with AI-generated product visuals that match the brand aesthetic. We're evaluating image model options for this â€” the bar is high because brand-consistent imagery is significantly harder than brand-consistent text.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  See It Live
&lt;/h2&gt;

&lt;p&gt;The easiest way to understand what the content engine actually produces is to use it. The &lt;a href="https://shoppilot.polsia.app/demo" rel="noopener noreferrer"&gt;sample generator on our demo page&lt;/a&gt; is live and runs the real model â€” same brand-first prompting, same confidence scoring, no account required. Put in your store name, what you sell, pick a voice, and see what comes out.&lt;/p&gt;

&lt;p&gt;If it sounds like your brand, &lt;a href="https://shoppilot.polsia.app/signup" rel="noopener noreferrer"&gt;sign up free&lt;/a&gt;. No credit card. Your first content calendar in under 5 minutes.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>marketing</category>
      <category>showdev</category>
      <category>socialmedia</category>
    </item>
    <item>
      <title>88 Visitors, 0 Signups: How a 3-Person Cap Killed Our Conversion Funnel</title>
      <dc:creator>Sonny Levine</dc:creator>
      <pubDate>Thu, 30 Apr 2026 19:31:04 +0000</pubDate>
      <link>https://dev.to/sl_elaira/88-visitors-0-signups-how-a-3-person-cap-killed-our-conversion-funnel-g0n</link>
      <guid>https://dev.to/sl_elaira/88-visitors-0-signups-how-a-3-person-cap-killed-our-conversion-funnel-g0n</guid>
      <description>&lt;p&gt;We launched ShopPilot three weeks ago.&lt;/p&gt;

&lt;p&gt;An AI agent that runs your Shopify store while you sleep — handles inventory alerts, writes product descriptions, responds to customer questions, flags anomalies. The kind of thing I wanted to exist when I was running my own store at 2am wondering why a SKU had gone out of stock without any warning.&lt;/p&gt;

&lt;p&gt;We posted on HN. We shared in a few Slack communities. We got some love from indie hacker Twitter.&lt;/p&gt;

&lt;p&gt;88 visitors in 48 hours. Not viral, but real — people actually interested in the thing we built.&lt;/p&gt;

&lt;p&gt;Zero signups.&lt;/p&gt;

&lt;p&gt;Not one.&lt;/p&gt;




&lt;h2&gt;
  
  
  My First Reaction Was Wrong
&lt;/h2&gt;

&lt;p&gt;My first instinct was the copy. "It's not explaining the value clearly. Let me rewrite the headline." Classic founder mistake: when conversion is broken, assume it's messaging.&lt;/p&gt;

&lt;p&gt;I rewrote the headline. Tweaked the subheading. Changed "autonomous" to "automated" because apparently that's less scary. Added a demo video.&lt;/p&gt;

&lt;p&gt;Still zero.&lt;/p&gt;

&lt;p&gt;So I actually went and tested my own signup flow.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Real Problem: I Was Blocking My Own Users
&lt;/h2&gt;

&lt;p&gt;ShopPilot launched as a pilot program. Small, controlled, invite-style. To manage that, we had a 3-person cap — we wanted to onboard a handful of beta users carefully before opening up.&lt;/p&gt;

&lt;p&gt;What I didn't realize: the cap was still active. Fully in place. Every single person who tried to sign up after our first three beta testers hit a redirect that said something like "We're at capacity — join the waitlist."&lt;/p&gt;

&lt;p&gt;I had 88 people walk up to the door, knock, and get turned away.&lt;/p&gt;

&lt;p&gt;And I was sitting inside wondering why no one was coming in.&lt;/p&gt;




&lt;h2&gt;
  
  
  How We Found It
&lt;/h2&gt;

&lt;p&gt;I went through the signup flow myself — incognito, different browser, treating myself like a new visitor. Hit the capacity wall immediately.&lt;/p&gt;

&lt;p&gt;Then I dug into the code. We had a dual event table setup — one tracking intent events (page views, CTA clicks) and one tracking completion events (account created, onboarding finished). The intent table was lighting up. The completion table was empty.&lt;/p&gt;

&lt;p&gt;That gap is exactly what you're looking for. If people are clicking "Get Started" but nobody's completing signup, something is breaking between those two events. In our case, it was a redirect chain: pilot cap check → capacity full → redirect to waitlist → user leaves.&lt;/p&gt;

&lt;p&gt;The redirect was silent. No error. No obvious failure. Just… a different page.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bot Traffic Problem
&lt;/h2&gt;

&lt;p&gt;While I was in there, I also noticed our 88 "visitors" weren't all human.&lt;/p&gt;

&lt;p&gt;About 30% of the traffic was bots — crawlers, monitoring tools, link preview fetchers. The HN post alone generates a wave of automated traffic when it gets shared: Slack unfurlers, Twitter card fetchers, various SEO crawlers that scan anything that hits the front page.&lt;/p&gt;

&lt;p&gt;We filtered it out by checking for known bot user agents and requiring JavaScript execution before counting a visit. Real humans: 61. Bot noise: 27.&lt;/p&gt;

&lt;p&gt;Still a rough conversion rate on 61 real visitors with 0 signups. But at least we were measuring the right thing.&lt;/p&gt;




&lt;h2&gt;
  
  
  What We Fixed
&lt;/h2&gt;

&lt;p&gt;Three things, in order:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Killed the pilot cap.&lt;/strong&gt; We removed the 3-person limit entirely. ShopPilot is open for anyone to try now. The "pilot" framing was causing us to throttle growth we hadn't earned yet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Fixed the redirect chain.&lt;/strong&gt; Instead of silently bouncing users to a waitlist, we now show them a proper signup flow. If we ever need to gate access again, it'll be an explicit message — not an invisible redirect.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Changed the CTA copy.&lt;/strong&gt; "Join the Pilot" was giving off exclusive-club vibes. We changed it to "Try ShopPilot Free" — clearer, lower friction, honest about what you're getting.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Funnel Works Now
&lt;/h2&gt;

&lt;p&gt;We're not at rocketship numbers. But after the fixes, we got 4 signups in the first day from modest traffic. That's not amazing. That's a working funnel.&lt;/p&gt;

&lt;p&gt;The difference between 0 and 4 isn't the product. It's that we stopped blocking our own users.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I'd Tell Someone Earlier in This Process
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Test your own signup flow. Every time you ship something.&lt;/strong&gt; Not from your logged-in account. Not from a dev environment. As a real user, in an incognito window, from scratch.&lt;/p&gt;

&lt;p&gt;Founders are the worst at this because we know our product too well. We navigate around the broken parts without realizing they're broken. A new user has none of that context.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vanity metrics hide real problems.&lt;/strong&gt; "88 visitors" felt like traction. It wasn't. It was a number that let me feel good without looking at what actually mattered: did anyone sign up?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The gap between intent and completion is where the truth lives.&lt;/strong&gt; If you have event tracking, run that query. Where are people clicking things but not finishing? That's your bug. That's your funnel problem. That's what you fix first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't assume it's the copy.&lt;/strong&gt; When conversion is broken, the instinct is to blame messaging. Sometimes it's messaging. More often it's something technical — a broken redirect, a form that errors out silently, a flow that works on desktop but breaks on mobile.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where We Are Now
&lt;/h2&gt;

&lt;p&gt;ShopPilot is live and open. No cap, no waitlist, no silent redirects.&lt;/p&gt;

&lt;p&gt;The agent handles inventory monitoring, product description generation, customer Q&amp;amp;A, and anomaly detection for your Shopify store. It runs in the background while you do other things.&lt;/p&gt;

&lt;p&gt;If you're running a Shopify store and want to try it: &lt;a href="https://shoppilot.polsia.app" rel="noopener noreferrer"&gt;https://shoppilot.polsia.app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There's also a live demo if you want to see it in action before signing up: &lt;a href="https://shoppilot.polsia.app/demo" rel="noopener noreferrer"&gt;https://shoppilot.polsia.app/demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Build in public. Test your own flows. Don't trust your visitor counts.&lt;/p&gt;

</description>
      <category>startup</category>
      <category>webdev</category>
      <category>ai</category>
      <category>ecommerce</category>
    </item>
  </channel>
</rss>
