How to Build an AI Blog Post Generator
Generate SEO-optimized blog posts using AI that match your brand voice.
Jay Banlasan
The AI Systems Guy
Most AI blog post generators produce generic garbage that sounds like every other site in your niche. The problem is not the AI. It's the lack of constraints. Without a brand voice file, a target keyword strategy, and a structure that reflects how your audience actually reads, you get mediocre content that ranks for nothing and converts nobody. This ai blog post generator business system produces posts that sound like you, target real search intent, and are actually useful enough to share.
I use this system to produce first drafts for SEO content at about 80% quality. A human takes it from 80% to 100% in 15-20 minutes instead of 90+ minutes of writing from scratch. That's the right use of AI in a content workflow.
What You Need Before Starting
- Python 3.10+
anthropicSDK- A voice guide for your brand (even a few paragraphs describing your writing style)
- Target keywords for the post
- Basic understanding of your audience's questions and pain points
Step 1: Build Your Brand Voice Configuration
The voice config is what separates generic AI content from content that sounds like you.
# brand_voice.py
BRAND_VOICE = {
"author_name": "Jay Banlasan",
"persona": "AI systems operator and paid ads specialist with 7+ years running campaigns for agencies and direct clients",
"tone": [
"Direct and practical — no fluff, lead with the answer",
"First person — 'I built this', 'I noticed', 'I use'",
"Conversational but technical — Grade 5-6 reading level",
"Confident without being arrogant — state what works without overpromising",
"Honest timelines and results — specific numbers over vague claims"
],
"avoid": [
"Hedge words: might, could potentially, it's possible",
"Filler openers: Great question, That's a good point",
"Corporate speak: leverage, synergy, robust, seamless",
"Em dashes and en dashes",
"Passive voice where active is possible",
"Lists of options when the answer is obvious"
],
"structure": {
"intro": "Open with a specific real scenario or result. No preamble.",
"headings": "Action-oriented H2s using How/What/Why verbs",
"sections": "One main idea per section. Short paragraphs.",
"closing": "Practical next step. No motivational sign-off."
},
"audience": "Business owners and marketing managers who run or oversee paid ads and want to use AI to work more efficiently"
}
Step 2: Build the Research and Outline Generator
The outline is the foundation. Bad outline = bad post no matter how good the writing is.
import anthropic
import json
_client = anthropic.Anthropic()
def generate_outline(
target_keyword: str,
audience_pain_point: str,
word_count_target: int = 800,
voice: dict = BRAND_VOICE
) -> dict:
prompt = f"""Create a detailed blog post outline. Return JSON only.
Author persona: {voice['persona']}
Target keyword: {target_keyword}
Reader's pain point: {audience_pain_point}
Target length: {word_count_target} words
Tone: {', '.join(voice['tone'][:3])}
Requirements:
- Title must include the target keyword naturally
- H2 headings should be specific and action-oriented
- Each section note should describe what the section proves or demonstrates
- The post should be a tutorial or case study, not a listicle
Return:
{{
"title": "exact post title",
"meta_description": "155-char SEO description",
"target_keyword": "{target_keyword}",
"secondary_keywords": ["related term 1", "related term 2"],
"intro_hook": "first sentence that grabs attention",
"sections": [
{{
"heading": "H2 heading text",
"subheadings": ["optional H3 1", "optional H3 2"],
"content_note": "what this section covers and proves",
"word_count": 120
}}
],
"conclusion_approach": "how to close the post"
}}"""
response = _client.messages.create(
model="claude-sonnet-4-5", max_tokens=1000,
messages=[{"role": "user", "content": prompt}]
)
return json.loads(response.content[0].text.strip())
Step 3: Build the Section Writer
Write each section individually. This produces better quality than asking the AI to write 800 words in one shot.
def write_section(
section: dict,
post_context: str,
voice: dict = BRAND_VOICE,
include_code: bool = False
) -> str:
avoid_list = "\n".join(f"- {item}" for item in voice["avoid"][:4])
code_instruction = (
"Include a practical code example if relevant to the concept. "
"Code should be Python and actually runnable."
) if include_code else "No code blocks needed."
prompt = f"""Write this section of a blog post. First person as {voice['author_name']}.
Post topic: {post_context}
Section heading: {section['heading']}
What to cover: {section['content_note']}
Target word count: {section.get('word_count', 150)}
Voice requirements:
- {chr(10).join(f'- {t}' for t in voice['tone'][:3])}
Avoid:
{avoid_list}
{code_instruction}
Write only the section body — no heading, no intro to the section.
Be specific. Use concrete examples over abstract descriptions."""
response = _client.messages.create(
model="claude-sonnet-4-5", max_tokens=600,
messages=[{"role": "user", "content": prompt}]
)
return response.content[0].text.strip()
Step 4: Build the Introduction Writer
The intro is the most important part. It determines whether someone reads or bounces.
def write_intro(outline: dict, voice: dict = BRAND_VOICE) -> str:
prompt = f"""Write the opening two paragraphs of a blog post. First person as {voice['author_name']}.
Title: {outline['title']}
Keyword: {outline['target_keyword']}
Hook: {outline['intro_hook']}
Persona: {voice['persona']}
Audience: {voice['audience']}
Rules:
- First sentence matches the hook above or improves it
- Include the target keyword naturally in the first 100 words
- Second paragraph explains why this matters for the reader's business
- No preamble, no "In this post I will..."
- Grade 5-6 reading level
- Under 120 words total
Write only the two paragraphs. No heading."""
response = _client.messages.create(
model="claude-sonnet-4-5", max_tokens=200,
messages=[{"role": "user", "content": prompt}]
)
return response.content[0].text.strip()
Step 5: Assemble the Full Post
Combine all pieces into a formatted Markdown document.
def generate_full_post(
target_keyword: str,
audience_pain_point: str,
word_count: int = 800,
include_code_sections: bool = True
) -> str:
print("Generating outline...")
outline = generate_outline(target_keyword, audience_pain_point, word_count)
print("Writing intro...")
intro = write_intro(outline)
sections = []
for i, section in enumerate(outline["sections"]):
print(f"Writing section {i+1}/{len(outline['sections'])}: {section['heading']}")
body = write_section(
section,
post_context=outline["title"],
include_code=include_code_sections and i < 3 # code in first 3 sections
)
sections.append(f"## {section['heading']}\n\n{body}")
# Assemble
post = f"""# {outline['title']}
{intro}
{"".join(chr(10)*2 + s for s in sections)}
## What to Do Next
{outline['conclusion_approach']}
---
*{BRAND_VOICE['author_name']} is an {BRAND_VOICE['persona']}.*
"""
return post, outline
# Usage
post_text, outline = generate_full_post(
target_keyword="ai request priority queue management",
audience_pain_point="AI batch jobs blocking live customer requests",
word_count=800
)
# Save
with open("draft_post.md", "w") as f:
f.write(post_text)
print(f"\nPost generated. {len(post_text.split())} words.")
print(f"Title: {outline['title']}")
print(f"Meta: {outline['meta_description']}")
Step 6: Run a Self-Check Before Human Review
Have the AI check its own output against your standards before it reaches a human editor.
def self_check_post(post_content: str) -> dict:
prompt = f"""Review this blog post draft against these standards:
1. Does it lead with a specific scenario or result? (no vague openers)
2. Does it avoid hedge words (might, could potentially)?
3. Does it avoid em dashes and en dashes?
4. Is it Grade 5-6 reading level throughout?
5. Are all claims specific (numbers, timeframes) not vague?
6. Does each section cover exactly what the heading promises?
Post:
{post_content[:3000]}
Return JSON: {{"passes": [list of passing checks], "fails": [list of failing checks], "overall": "pass|needs_revision"}}"""
response = _client.messages.create(
model="claude-haiku-3", max_tokens=400,
messages=[{"role": "user", "content": prompt}]
)
try:
return json.loads(response.content[0].text.strip())
except Exception:
return {"overall": "review manually"}
What to Build Next
- Build a keyword gap analyzer that identifies which topics your competitors rank for that you don't, then feeds them into this generator as a content queue
- Add an internal linking step that scans your existing posts and suggests where to add links to new content
- Build a WordPress or Ghost CMS publisher that takes the generated Markdown and publishes it as a draft automatically
Related Reading
- How to Automate Content Calendar Planning with AI - plan the full month's content before generating individual posts
- How to Build an AI Product Description Generator - apply the same voice config approach to e-commerce copy
- How to Create an AI FAQ Generator - FAQ content from the same posts drives additional search traffic
Want this system built for your business?
Get a free assessment. We will map every system your business needs and show you the ROI.
Get Your Free Assessment