Content Writer Agent Blueprint
Multi-step content creation agent with outline, research, draft, edit, and finalization stages. Includes grammar checking, tone adjustment, and SEO optimization tools.
Content Writer Agent
A multi-step content creation agent. Unlike single-prompt content generation, this agent runs a pipeline: research the topic, create an outline, draft each section, edit for clarity and tone, then optimize for SEO. Each stage uses tools to verify and improve output.
Note:
Content Writer works best for long-form articles (800-2000 words). For short copy (social posts, ads), a single well-crafted prompt is more efficient than the full agent pipeline.
Agent File Structure
Setup
Install Dependencies
pip install openai language-tool-python
Create config.json
{
"openai_api_key": "sk-...",
"model": "gpt-4o",
"max_iterations": 15,
"output_dir": "./output",
"default_tone": "professional",
"default_audience": "developers"
}
Verify
python agent.py --topic "Python async programming patterns" --length 1000 --output article.md
The agent runs through all stages and writes the final article to ./output/.
Pipeline Overview
The agent follows a fixed pipeline — this is prompt chaining, not free-form ReAct. Each stage has a dedicated prompt and specific tools. The pipeline ensures quality at every step rather than hoping the model gets it right in one shot.
Stage 1: RESEARCH → web_search, extract_content
Stage 2: OUTLINE → structure analysis
Stage 3: DRAFT → section-by-section writing
Stage 4: EDIT → grammar_check, tone_adjust
Stage 5: FINALIZE → seo_analyze, format_output
System Prompt
You are a professional content writer. You create well-researched, clearly
structured articles through a multi-step pipeline. Follow these stages:
STAGE 1 — RESEARCH: Search for authoritative sources on the topic. Extract
key facts, statistics, and quotes. Store findings in a research document.
STAGE 2 — OUTLINE: Create a logical outline with H2 sections and H3 subsections.
Each section should address a specific aspect or question about the topic.
STAGE 3 — DRAFT: Write each section, one at a time. Use research findings.
Maintain consistent tone and reading level. Include concrete examples.
STAGE 4 — EDIT: Check grammar, adjust tone for the target audience,
eliminate redundancy, strengthen transitions between sections.
STAGE 5 — FINALIZE: Run SEO analysis, add meta description, ensure readability
scores are in range, format for the output medium (Markdown, HTML, etc.).
After each stage, clearly indicate STAGE COMPLETE and proceed to the next.
When all stages are complete, output FINAL_ARTICLE with the full content.
Tool Definitions
Agent Tools
Values: query: string, count?: int (default 5)
Values: url: string
Values: text: string
Values: text: string, tone: string
Values: text: string, keyword?: string
Values: filename: string, content: string
Tool Implementation
# tools.py
import os
import json
import requests
OUTPUT_DIR = "./output"
def web_search(query, count=5):
resp = requests.get(
"https://api.brave.com/res/v1/web/search",
params={"q": query, "count": count},
headers={"X-Subscription-Token": os.environ.get("BRAVE_API_KEY", "")}
)
data = resp.json()
return json.dumps([{
"title": r.get("title"),
"url": r.get("url"),
"snippet": r.get("description")
} for r in data.get("web", {}).get("results", [])])
def extract_content(url):
resp = requests.get(url, headers={"User-Agent": "ContentWriter/1.0"})
from bs4 import BeautifulSoup
soup = BeautifulSoup(resp.text, "html.parser")
for tag in soup(["script", "style", "nav", "footer", "header"]):
tag.decompose()
return soup.get_text(separator="\n", strip=True)[:4000]
def grammar_check(text):
import language_tool_python
tool = language_tool_python.LanguageTool('en-US')
matches = tool.check(text)
if not matches:
return "No grammar issues found."
return json.dumps([{
"message": m.message,
"context": m.context,
"suggestions": m.replacements[:3],
"rule": m.ruleId
} for m in matches[:10]])
def tone_adjust(text, tone):
# In a production agent, this would use the LLM. Here we defer to the model
# by returning a marker that the agent should rewrite at the given tone.
return f"TONE_ADJUST_REQUESTED:{tone}\n{text[:2000]}"
def seo_analyze(text, keyword=None):
words = text.split()
lines = text.split("\n")
h2s = [l for l in lines if l.startswith("## ")]
word_count = len(words)
if keyword:
keyword_count = text.lower().count(keyword.lower())
density = round((keyword_count / word_count) * 100, 2) if word_count > 0 else 0
else:
keyword_count = 0
density = 0
return json.dumps({
"word_count": word_count,
"h2_count": len(h2s),
"h2s": h2s,
"keyword_density": f"{density}%" if keyword else "N/A",
"target_density": "1-2%",
"readability_note": "Ensure Flesch-Kincaid score is 60-70 for general audience."
})
def save_draft(filename, content):
os.makedirs(OUTPUT_DIR, exist_ok=True)
path = os.path.join(OUTPUT_DIR, filename)
with open(path, "w") as f:
f.write(content)
return f"Draft saved to {path} ({len(content)} chars)"
Agent Initialization
# agent.py
import json
import argparse
from openai import OpenAI
import tools as agent_tools
TOOL_SCHEMAS = [
{
"type": "function",
"function": {
"name": "web_search",
"description": "Search the web for information on a topic",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string"},
"count": {"type": "integer", "default": 5}
},
"required": ["query"]
}
}
},
{
"type": "function",
"function": {
"name": "extract_content",
"description": "Fetch and extract main text from a URL",
"parameters": {
"type": "object",
"properties": {"url": {"type": "string"}},
"required": ["url"]
}
}
},
{
"type": "function",
"function": {
"name": "grammar_check",
"description": "Check text for grammar and spelling issues",
"parameters": {
"type": "object",
"properties": {"text": {"type": "string"}},
"required": ["text"]
}
}
},
{
"type": "function",
"function": {
"name": "tone_adjust",
"description": "Rewrite text to match a specified tone",
"parameters": {
"type": "object",
"properties": {
"text": {"type": "string"},
"tone": {"type": "string", "enum": ["professional", "casual", "academic", "technical"]}
},
"required": ["text", "tone"]
}
}
},
{
"type": "function",
"function": {
"name": "seo_analyze",
"description": "Analyze text for SEO quality",
"parameters": {
"type": "object",
"properties": {
"text": {"type": "string"},
"keyword": {"type": "string"}
},
"required": ["text"]
}
}
},
{
"type": "function",
"function": {
"name": "save_draft",
"description": "Save a draft to the output directory",
"parameters": {
"type": "object",
"properties": {
"filename": {"type": "string"},
"content": {"type": "string"}
},
"required": ["filename", "content"]
}
}
}
]
SYSTEM_PROMPT = """You are a professional content writer. You create well-researched, clearly
structured articles through a multi-step pipeline. Follow these stages:
STAGE 1 — RESEARCH: Search for authoritative sources on the topic. Extract
key facts, statistics, and quotes. Store findings in a research document.
STAGE 2 — OUTLINE: Create a logical outline with H2 sections and H3 subsections.
Each section should address a specific aspect or question about the topic.
STAGE 3 — DRAFT: Write each section, one at a time. Use research findings.
Maintain consistent tone and reading level. Include concrete examples.
STAGE 4 — EDIT: Check grammar, adjust tone for the target audience,
eliminate redundancy, strengthen transitions between sections.
STAGE 5 — FINALIZE: Run SEO analysis, add meta description, ensure readability
scores are in range, format for the output medium (Markdown, HTML, etc.).
After each stage, clearly indicate STAGE COMPLETE and proceed to the next.
When all stages are complete, output FINAL_ARTICLE with the full content."""
def run_agent(topic, length, config):
client = OpenAI(api_key=config["openai_api_key"])
agent_tools.OUTPUT_DIR = config.get("output_dir", "./output")
tone = config.get("default_tone", "professional")
audience = config.get("default_audience", "developers")
query = f"""Write a {length}-word article about: {topic}
Target audience: {audience}
Tone: {tone}
Format: Markdown with H2 and H3 headings, code blocks where relevant, and a meta description.
Follow the 5-stage pipeline: RESEARCH → OUTLINE → DRAFT → EDIT → FINALIZE.
Use tools at each stage. Save the final article with save_draft."""
messages = [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": query}
]
for i in range(config.get("max_iterations", 15)):
response = client.chat.completions.create(
model=config.get("model", "gpt-4o"),
messages=messages,
tools=TOOL_SCHEMAS,
temperature=0.7
)
msg = response.choices[0].message
messages.append(msg)
if msg.content and "FINAL_ARTICLE" in msg.content:
return msg.content.split("FINAL_ARTICLE", 1)[1].strip()
if msg.content and "FINAL_ARTICLE" in msg.content:
return msg.content.split("FINAL_ARTICLE", 1)[1].strip()
if not msg.tool_calls:
if msg.content:
messages.append({
"role": "user",
"content": "Continue to the next stage of the pipeline."
})
else:
messages.append({
"role": "user",
"content": "Use tools to progress through the writing pipeline."
})
continue
for tool_call in msg.tool_calls:
func_name = tool_call.function.name
func_args = json.loads(tool_call.function.arguments)
func = getattr(agent_tools, func_name)
result = func(**func_args)
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": result
})
return "Agent reached max iterations without completing the article."
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--topic", required=True)
parser.add_argument("--length", type=int, default=1200)
parser.add_argument("--output", default="article.md")
parser.add_argument("--config", default="config.json")
args = parser.parse_args()
with open(args.config) as f:
config = json.load(f)
article = run_agent(args.topic, args.length, config)
agent_tools.save_draft(args.output, article)
print(f"Article saved to {config.get('output_dir', './output')}/{args.output}")
Pipeline Walkthrough
Topic: "Python async programming patterns" — 1000 words, professional tone, developer audience.
STAGE 1 — RESEARCH
Agent calls web_search("Python async programming patterns best practices 2024") and web_search("asyncio common patterns"). Extracts content from 3 authoritative sources (Real Python, official docs, PyCon talk notes). Compiles key points: async/await, asyncio.gather(), create_task(), asyncio.Queue, error handling in coroutines.
STAGE COMPLETE. Research document contains 6 key facts and 3 code examples.
STAGE 2 — OUTLINE
Agent produces a logical outline:
- H2: Why Async Python
- H2: Core Pattern: async/await
- H2: Concurrent Execution with gather and create_task
- H2: Producer-Consumer with asyncio.Queue
- H2: Error Handling in Coroutines
- H2: When Not to Use Async
STAGE COMPLETE. Outline has 6 H2 sections and 3 H3 subsections.
STAGE 3 — DRAFT
Agent writes each section sequentially, pulling facts from the research document. Each section includes a code example. The draft hits ~1100 words.
STAGE COMPLETE. Draft is 1,120 words across 6 sections with code blocks.
STAGE 4 — EDIT
Agent calls grammar_check(draft_text). Finds 3 issues: missing comma in compound sentence, passive voice overuse in section 2, inconsistent bullet formatting in section 4. Calls tone_adjust on section 5 ("Error Handling") to make it more practical and less academic.
STAGE COMPLETE. 3 grammar fixes applied, tone adjusted on 1 section.
STAGE 5 — FINALIZE
Agent calls seo_analyze(article, "python async"). Reports: 1,080 words, 6 H2s, keyword density 1.8% (good), readability score 65. Adds meta description: "Master Python async programming with practical patterns for concurrency, error handling, and producer-consumer workflows using asyncio."
Calls save_draft("article.md", final_content).
FINAL_ARTICLE output with all corrections and SEO optimization.
Customization
Writing Configuration
Values: professional, casual, academic, technical
Values: developers, general, executives, beginners
Values: 0.0 - 1.0 (default 0.7)
Values: 10 - 20 (default 15)
Note:
Verified use case: Content Writer works well for technical blog posts, documentation pages, how-to guides, and comparison articles. It produces stronger results on technical topics where research yields concrete facts and code examples. For opinion pieces or creative writing, skip the pipeline and use a direct prompt.
Note:
Don't fully automate publishing. Use the agent to produce a solid first draft and SEO-ready structure, but always do a human editorial pass. The agent may introduce factual errors during the DRAFT stage — the RESEARCH facts are only as reliable as the search results.
Key Takeaway
A content writing agent's strength is the pipeline, not the model. Separating research, drafting, editing, and SEO into distinct stages prevents the "everything bagel" problem where a single prompt tries to do too much and fails at all of it. Each stage verifies and improves the output of the previous one.
Related Articles
AI Agent Blueprints & Configurations
Ready-to-run AI agent blueprints, configurations, and local setup guides. Build research agents, code reviewers, and content writers with copy-paste implementations.
Agent Blueprints
Ready-to-run AI agent implementations. Complete system prompts, tool definitions, and initialization code for research, code review, and content writing agents.
Code Review Agent Blueprint
Complete code review agent that reads file trees, runs linters, checks patterns, and suggests refactors. Ready-to-run with file system access and Git integration.