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.

June 9, 2026
content-writeragent-blueprintmulti-stepseowriting

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

content-writeradd
agent.pyadd
tools.pyadd
config.jsonadd

Setup

1

Install Dependencies

pip install openai language-tool-python
2

Create config.json

{
  "openai_api_key": "sk-...",
  "model": "gpt-4o",
  "max_iterations": 15,
  "output_dir": "./output",
  "default_tone": "professional",
  "default_audience": "developers"
}
3

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

web_search
Search the web for information on a topic. Returns title, URL, snippet for top results.

Values: query: string, count?: int (default 5)

extract_content
Fetch and extract main text from a URL. Returns clean text content.

Values: url: string

grammar_check
Check text for grammar, spelling, and style issues. Returns list of errors with suggestions.

Values: text: string

tone_adjust
Rewrite text to match a specified tone (professional, casual, academic, technical).

Values: text: string, tone: string

seo_analyze
Analyze text for SEO: keyword density, readability score, missing H2s, meta description length.

Values: text: string, keyword?: string

save_draft
Save a draft to the output directory for later review.

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.

1

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.
2

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.
3

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.
4

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.
5

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

tone
Writing tone. Professional for docs, casual for blogs, academic for papers, technical for API documentation.

Values: professional, casual, academic, technical

audience
Target reader expertise level. Affects vocabulary, explanation depth, and example complexity.

Values: developers, general, executives, beginners

temperature
Higher values (0.7-0.9) produce more creative/varied writing. Lower values (0.3-0.5) are more predictable.

Values: 0.0 - 1.0 (default 0.7)

max_iterations
Pipeline stages consume iterations. 15 allows the agent to search, outline, draft, and edit comfortably.

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.