DEV Community

Yurii Liub
Yurii Liub

Posted on • Originally published at hintder.ai

The hardest part of my AI dating app wasn't the AI — it was making it not sound like AI

I built hintder — you screenshot a dating profile (Tinder, Hinge, Bumble), and it writes 5 openers tuned to what's actually on her profile. Pick a tone, get a reply-worthy first message.

The interesting engineering wasn't "call an LLM and get text back." That's a weekend. The hard part was the thing every AI-writing product secretly fights: making the output not instantly readable as AI. Here's what actually moved the needle.

Problem 1: a screenshot is not a prompt

A dating profile is a messy image — photos, prompts, a bio, age, distance, sometimes Spotify. If you hand the whole thing to a model and say "write an opener," you get something generic, because the model averages over everything.

The fix was two stages, not one:

  1. Extract. A vision model reads the screenshot and returns structured hooks — the specific, openable details. Not "she likes the outdoors" but "her 3rd photo is bouldering, her prompt says she'll lose at Mario Kart."
  2. Generate. A second pass writes openers grounded in those extracted hooks, one hook per opener.

Splitting extraction from generation is what makes openers feel about her instead of about "a girl who likes hiking."

// stage 1 — structured extraction, not free text
const hooks = await vision.extract(screenshot, {
  schema: {
    prompts: "array of her written prompts, verbatim",
    standout_details: "specific, openable things (a pet, a trip, a joke)",
    vibe: "playful | dry | earnest | adventurous",
  },
});

// stage 2 — generate ONE opener per hook, never averaging
const openers = await llm.write({ hooks, tone, rules: ANTI_AI_RULES });
Enter fullscreen mode Exit fullscreen mode

Problem 2: the "obviously AI" tells

This was 80% of the work. Out of the box, the model writes openers that are grammatically perfect and completely dead. The tells, in order of how badly they out you as a bot:

  • The em-dash. Nobody texts an em-dash. It's the #1 giveaway.
  • "Ah, a fellow [hobby]" / "I see you're a person of culture." Reddit-comment cadence, not flirting.
  • Survey questions. "What do you enjoy most about hiking?" reads like an onboarding form, not a human who's into her.
  • Over-completeness. Real openers are a little lazy. A model writes a tidy paragraph; a person writes seven words and a typo's worth of casualness.

The fix wasn't a bigger model — it was constraints as first-class product logic:

ANTI_AI_RULES:
- never use an em-dash or semicolon
- max ~15 words; lowercase is fine
- reference exactly ONE detail, not a checklist
- no question she can answer in one word
- no "ah, a fellow…", no "I see you…", no "as someone who…"
- write like a confident text, not an essay
Enter fullscreen mode Exit fullscreen mode

Negative constraints ("never do X") turned out to matter more than positive ones. Telling a model what good looks like is vague; banning the specific tells is enforceable.

Problem 3: the second message is where it really dies

Everyone optimizes the opener. But the actual graveyard is message two: she replied "haha hey", and the guy freezes. So hintder takes the thread context and suggests the next line — keep the thread alive, don't restart it. Solving only the opener would've solved the easy half.

On privacy (because it's a screenshot of a real person)

Screenshots are processed for the reading and auto-deleted after 30 days. For a product built on images of real people, that constraint isn't a footnote — it's table stakes, and it shaped the storage design from day one.

What I'd tell past-me

  • Separate extraction from generation. Specificity is an architecture decision, not a prompt adjective.
  • Ban the tells explicitly. "Sound human" is unenforceable; "no em-dashes, no survey questions" is.
  • Solve the unglamorous half. The second message is where retention actually lives.

Try it on a real profile at hintder.ai — first 3 openers are free. If you've shipped anything that had to not sound like AI, I'd love to hear which tells you had to kill.

Top comments (0)