Ai-Mee Help Centre
Home
Features
How-To Guides
FAQ
Need Help?
Home
Features
How-To Guides
FAQ
Need Help?

Phase 1: Platform-Specific Prompt Intent Overhaul

Status: ✅ IMPLEMENTED
Prerequisites: None (self-contained prompt changes)
Objective: Give each platform group a distinct content intent so generated posts serve their actual purpose — social drives organic growth, email informs subscribers, blog targets search queries.


Background

Currently all platforms receive the same context-enriched prompt via synthesizeTopic(), regardless of whether the output will be a Tweet, a newsletter, or a blog post. The generators differ only in formatting instructions, not strategic intent. This means:

  • Social posts read like product announcements, not community content
  • Email content is generic feature promotion, not subscriber value
  • Blog posts lack SEO structure and are too short (400–600 words) for search ranking

7.1 — Social Generator Prompt Rewrites

Target files: api/src/agents/createPosts.ts
Platforms: facebook, instagram, twitter, linkedin, tiktok

What changes

Replace the current "call-to-action" framing (which defaults to "learn more") with organic growth framing. The goal shifts from driving traffic to driving engagement signals (comments, shares, saves, follows) that grow reach.

Per-platform changes

Facebook — add to prompt:

This post's goal is organic reach and community engagement, not direct promotion.
- Spark a conversation: ask a question the audience genuinely wants to answer
- Use storytelling or a relatable observation related to the topic
- CTA should be low-friction: "What do you think?", "Drop a comment below", "Share this with someone who…"
- Avoid "learn more" / "click here" / "buy now" CTAs

Instagram — add to prompt:

This post's goal is organic discovery and saves/shares, not direct promotion.
- Open with a hook that stops the scroll in the first line
- Make it save-worthy: a tip, insight, or relatable moment the audience will want to revisit
- CTA should be engagement-focused: "Save this for later", "Tag someone who needs this", "Share your experience in the comments"
- Avoid "link in bio for X" as the primary CTA

Twitter/X — add to prompt:

This post's goal is organic reach through replies and retweets, not direct promotion.
- Start with the most provocative or insightful statement — no warm-up
- Invite a reaction or debate: a bold take, a surprising stat, or a question
- CTA: "What's your take?", "Agree or disagree?", "Reply with your experience"
- Avoid "check out our…" CTAs

LinkedIn — add to prompt:

This post's goal is professional reach and industry credibility, not direct promotion.
- Lead with a lesson, insight, or industry observation — not a product feature
- Tell a short story or share a pattern noticed from experience
- CTA should invite peer discussion: "Has your team experienced this?", "What's worked for you?", "Curious what others think"
- Avoid "follow us for more content" as the primary CTA

TikTok — add to prompt:

This post's goal is algorithmic reach through watch-time and saves, not direct promotion.
- The first line must be a hook that creates curiosity or FOMO
- Structure as a "did you know" / "here's why" / "stop doing X" format where possible
- CTA should drive follows or saves: "Follow for more", "Save this if you…", "Part 2 if this gets 50 comments"
- Avoid self-promotional language in the hook

Tasks

  • [x] Update createFacebookPost() prompt in api/src/agents/createPosts.ts
  • [x] Update createInstagramPost() prompt
  • [x] Update createTwitterPost() prompt (preserve 280-char enforcement logic)
  • [x] Update createLinkedInPost() prompt
  • [x] Update createTikTokPost() prompt

7.2 — Blog Generator Prompt Rewrite

Target files: api/src/agents/createPosts.ts
Platforms: blog, ghost, wordpress, blogger

What changes

Restructure createBlogPost() around search intent rather than product promotion. Target word count increases from 400–600 to 800–1200 words to meet minimum SEO thresholds for competitive ranking.

Updated prompt structure

Write an SEO-optimised blog post for {company_name} ({website}).

Company: {company_description}
Topic/Brief: {feature_description}

{brand_voice}

SEO requirements:
- H1 title: must be a search query or closely match what a person would type into Google
- H2 headings: structure as questions (e.g. "What is…?", "How do you…?", "Why does…?")
- Length: 800–1,200 words
- Include a <meta name="description"> tag (150–160 characters) summarising the post for search results
- Naturally include semantically related terms throughout (do not keyword-stuff)
- Write for a reader who found this post via a search query — they want a clear answer, not a sales pitch

Structure:
1. H1 — the search query / primary topic (include primary keyword)
2. Introduction — answer the main question in 2–3 sentences, then expand
3. 3–4 H2 sections — each addressing a related sub-question a searcher would have
4. Optional: numbered list or comparison table if the topic suits it
5. Conclusion with a soft, helpful CTA (not a hard sell)
6. 2–3 [IMAGE: ...] placeholders at relevant points

Return the full post in valid HTML (h1, h2, h3, p, ul/ol, table if used, meta description tag).
Wrap the HTML in triple backticks.
Write 1 post only. No explanations outside the HTML.

Tasks

  • [x] Rewrite createBlogPost() prompt in api/src/agents/createPosts.ts
  • [x] Update word-count validation in extractBlogHtml() (if it enforces a max — adjust to 1,200; no hard limit existed, prompt now targets 800–1,200 words)
  • [x] Update critiqueContent() scoring rubric for blog: add "SEO structure" criterion, penalise H2s that are not question-format when no keywords are provided

7.3 — Email Generator Prompt Rewrite

Target files: api/src/agents/createPosts.ts, api/src/utils/smart-mapper.ts

What changes

Shift the framing in generateContentForRoles() from "promoting a feature" to "informing subscribers about something valuable". This is a prompt-level change — no new data sources yet (those come in Phase 8).

Updated role generation context

In generateContentForRoles(), update the base system framing passed to each role's LLM call:

You are writing content for a marketing email sent to an existing subscriber list.
The goal is to inform and provide value — NOT to sell or promote.
Subscribers have already opted in; they want industry news, product updates, useful tips,
and insights relevant to their work or interests.

Do NOT use:
- "Check out our latest…"
- "We're excited to announce…" (use sparingly, only for genuinely significant news)
- Generic calls-to-action like "Learn more" or "Click here"

DO use:
- Specific, concrete details about what has changed, improved, or is new
- Industry context that makes the update feel timely and relevant
- CTAs that deliver clear value: "Read the full guide", "Try it now", "See what's new"

Tasks

  • [x] Update the base framing in generateContentForRoles() in api/src/utils/smart-mapper.ts
  • [x] Update HERO_HEADLINE role prompt to request a subject-line-style headline (not a campaign tagline)
  • [x] Update CTA_BUTTON / CTA_LINK role prompt to require value-specific CTAs
  • [x] Update the legacy createEmailTemplate() step-1 prompt (used as fallback)

7.4 — Platform-Group Intent in synthesizeTopic()

Target file: api/src/modules/auto-generate-context.ts

What changes

synthesizeTopic() currently produces one prompt for all platforms in a generation run. Add platform-group detection and inject a strategic framing block that aligns the synthesised topic with the platform group's intent.

Platform group detection

function detectPlatformGroup(platforms: string[]): 'social' | 'email' | 'blog' | 'mixed' {
  const socialPlatforms = new Set(['facebook', 'instagram', 'twitter', 'linkedin', 'tiktok'])
  const blogPlatforms = new Set(['blog', 'ghost', 'wordpress', 'blogger'])
  const emailPlatforms = new Set(['email'])

  const hasSocial = platforms.some((p) => socialPlatforms.has(p))
  const hasBlog = platforms.some((p) => blogPlatforms.has(p))
  const hasEmail = platforms.some((p) => emailPlatforms.has(p))

  const groupCount = [hasSocial, hasBlog, hasEmail].filter(Boolean).length
  if (groupCount > 1) return 'mixed'
  if (hasSocial) return 'social'
  if (hasBlog) return 'blog'
  if (hasEmail) return 'email'
  return 'mixed'
}

Framing blocks to inject into synthesizeTopic() prompt

const platformGroupFraming: Record<string, string> = {
  social: `
CONTENT GOAL — ORGANIC GROWTH:
The content you suggest must be designed to earn engagement (comments, shares, saves, follows),
not to drive direct traffic or sales. Suggest topics that spark conversation or genuine curiosity.
Avoid product-announcement angles.`,

  email: `
CONTENT GOAL — SUBSCRIBER VALUE:
The content you suggest must inform and provide genuine value to existing subscribers.
Suggest topics that feel timely, specific, and useful — industry news, product updates, 
practical tips, or notable changes relevant to the company's niche.
Avoid generic promotional angles.`,

  blog: `
CONTENT GOAL — ORGANIC SEARCH:
The content you suggest must target a question or search query a potential customer would type
into Google or an AI assistant. Suggest a specific topic phrased as a search intent 
(e.g. "how to X", "best Y for Z", "why does W happen"). Avoid promotional angles.`,

  mixed: `
CONTENT GOAL — MULTI-CHANNEL:
The content you suggest will be adapted across multiple channel types. Suggest a topic that has
genuine value for existing subscribers (email), is discoverable via search (blog), and can spark
engagement on social. Prioritise informational/educational angles over promotional ones.`,
}

Tasks

  • [x] Add detectPlatformGroup() helper function to auto-generate-context.ts
  • [x] Update synthesizeTopic() signature to accept platforms: string[]
  • [x] Inject the appropriate framing block into the LLM synthesis prompt before the topic-selection instruction
  • [x] Update runSingleGeneration() to pass platforms through to synthesizeTopic()
  • [x] Add a brief log line so server logs show which platform group was detected per run

Testing Checklist

  • [ ] Generate a Facebook post manually via the dashboard — verify CTA is engagement-focused (not "learn more")
  • [ ] Generate a LinkedIn post — verify it leads with an insight, not a product feature
  • [ ] Generate a blog post — verify it contains H2s phrased as questions and <meta name="description">
  • [ ] Generate a blog post — verify word count is 800–1,200 words
  • [ ] Generate an email via POST /generate_post — verify headline is subject-line style and CTA is value-specific
  • [ ] Trigger POST /plan_posts with a customer that has social + blog platforms — confirm server logs show distinct platform groups
  • [ ] Run unit tests: cd api && pnpm test:routes — no regressions

Rollback

All changes are prompt-only (no schema, no new services). To revert: restore original prompt strings in createPosts.ts, revert framing in smart-mapper.ts, and remove detectPlatformGroup call from synthesizeTopic.