← Back to DevBytes

Building a Content Writing Agent with LangGraph: Complete Guide

Introduction to Content Writing Agents with LangGraph

What is LangGraph?

LangGraph is a framework from the LangChain ecosystem designed specifically for building stateful, multi-step agent workflows using language models. Unlike simple chains that follow a linear path, LangGraph allows you to define complex graphs where nodes represent actions or LLM calls, and edges represent the flow of data and control between those actions. It excels at orchestrating workflows that require iteration, branching logic, and persistent state management — precisely the kind of patterns needed for sophisticated content writing agents.

At its core, LangGraph uses a directed graph structure where you define nodes (Python functions that process state) and edges (connections between nodes). The framework handles state propagation, ensures type safety, and provides built-in support for streaming, checkpointing, and human-in-the-loop interruptions.

Why Use LangGraph for Content Writing?

Traditional approaches to AI-assisted content writing often rely on a single massive prompt or a brittle linear chain. These fall short when you need:

LangGraph excels at all of these. By modeling content writing as a state machine with discrete stages (research, outline, draft, review, revise), you get a transparent, controllable, and highly effective writing agent that produces consistently better output than monolithic approaches.

Setting Up Your Environment

Prerequisites and Installation

You'll need Python 3.9+ and an API key from a supported LLM provider. Install the required packages with pip:

pip install langgraph langchain langchain-openai python-dotenv

Create a .env file to store your API key securely:

# .env
OPENAI_API_KEY=sk-your-key-here

API Keys and Configuration

Load environment variables and configure the chat model. We'll use GPT-4 for high-quality writing tasks, but you can substitute any LangChain-compatible model:

import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI

load_dotenv()

# Initialize the language model
llm = ChatOpenAI(
    model="gpt-4o",
    temperature=0.7,
    max_tokens=4096,
    api_key=os.getenv("OPENAI_API_KEY")
)

# For research tasks, use a slightly lower temperature for factual accuracy
research_llm = ChatOpenAI(
    model="gpt-4o",
    temperature=0.3,
    max_tokens=2048,
    api_key=os.getenv("OPENAI_API_KEY")
)

Building the Core Components

Defining the State Schema

The state is the backbone of a LangGraph application. It carries all data through the workflow. For a content writing agent, we need a rich state that tracks the topic, research findings, outline, draft content, review feedback, and a revision counter to prevent infinite loops:

from typing import TypedDict, Annotated, List, Optional
from langgraph.graph.message import add_messages
import operator

class ContentWritingState(TypedDict):
    """State schema for the content writing agent workflow."""
    
    # Core input
    topic: str
    content_type: str  # e.g., "blog post", "technical tutorial", "product description"
    target_audience: str
    tone: str  # e.g., "professional", "conversational", "technical"
    word_count_target: int
    
    # Research phase
    research_notes: Optional[str]
    
    # Outline phase
    outline: Optional[str]
    
    # Writing phase
    draft_content: Optional[str]
    
    # Review phase
    review_feedback: Optional[str]
    review_score: Optional[float]  # 1-10 rating
    needs_revision: bool
    
    # Revision tracking
    revision_count: int
    
    # Final output
    final_content: Optional[str]
    
    # Messages (for conversation history with the LLM)
    messages: Annotated[List, add_messages]

Creating the Research Node

The research node gathers context, facts, and background information on the given topic. It uses a focused LLM call to produce structured research notes that inform later stages:

def research_node(state: ContentWritingState) -> dict:
    """
    Performs research on the given topic and returns structured notes.
    This node uses a lower-temperature model for factual accuracy.
    """
    print("šŸ” [RESEARCH] Gathering information on topic...")
    
    research_prompt = f"""
You are an expert researcher. Conduct thorough research on the following topic.

Topic: {state['topic']}
Content Type: {state['content_type']}
Target Audience: {state['target_audience']}

Please provide:
1. Key facts and statistics related to the topic
2. Current trends and developments
3. Common questions or pain points the audience has
4. Notable examples or case studies
5. Relevant terminology and concepts to explain

Format your research as structured, detailed notes that a writer can use to craft compelling content.
"""
    
    response = research_llm.invoke([
        {"role": "system", "content": "You are a meticulous researcher. Provide comprehensive, accurate research notes."},
        {"role": "user", "content": research_prompt}
    ])
    
    research_notes = response.content
    
    return {
        "research_notes": research_notes,
        "messages": [{"role": "assistant", "content": f"Research completed. Notes: {research_notes[:200]}..."}]
    }

Creating the Outline Node

The outline node takes the research notes and constructs a logical content structure. A good outline dramatically improves the coherence of the final draft:

def outline_node(state: ContentWritingState) -> dict:
    """
    Creates a detailed content outline based on research notes.
    The outline serves as the structural backbone for the writing phase.
    """
    print("šŸ“‹ [OUTLINE] Structuring content outline...")
    
    outline_prompt = f"""
You are a master content strategist. Create a detailed outline for the following content.

Topic: {state['topic']}
Content Type: {state['content_type']}
Target Audience: {state['target_audience']}
Tone: {state['tone']}
Target Word Count: {state['word_count_target']}

Research Notes (use these as source material):
{state['research_notes']}

Please create an outline that includes:
1. A compelling title (provide 3 options)
2. Introduction (hook, context, and thesis)
3. Main sections with descriptive headings (4-8 sections)
4. For each section, list 2-3 key points to cover
5. Conclusion structure (summary, call to action, next steps)
6. Estimated word count distribution across sections

Make the outline detailed enough that a writer can produce excellent content from it alone.
"""
    
    response = llm.invoke([
        {"role": "system", "content": "You are an expert content strategist. Create thorough, well-structured outlines."},
        {"role": "user", "content": outline_prompt}
    ])
    
    outline = response.content
    
    return {
        "outline": outline,
        "messages": [{"role": "assistant", "content": "Outline created successfully."}]
    }

Creating the Writing Node

The writing node is the heart of the agent. It consumes the research and outline to produce a complete first draft. We instruct the model to follow the outline closely while maintaining engaging prose:

def writing_node(state: ContentWritingState) -> dict:
    """
    Writes the full content draft based on the research notes and outline.
    This is the core content generation step.
    """
    print("āœļø [WRITING] Crafting content draft...")
    
    writing_prompt = f"""
You are an exceptional content writer. Write a complete, polished draft based on the following materials.

Topic: {state['topic']}
Content Type: {state['content_type']}
Target Audience: {state['target_audience']}
Tone: {state['tone']}
Target Word Count: approximately {state['word_count_target']} words

DETAILED OUTLINE (follow this structure carefully):
{state['outline']}

RESEARCH NOTES (incorporate these facts and insights naturally):
{state['research_notes']}

WRITING GUIDELINES:
- Follow the outline precisely — use the headings and cover all listed key points
- Write in the specified tone for the target audience
- Incorporate research facts and examples seamlessly
- Use clear transitions between sections
- Include engaging hooks and a compelling conclusion
- Aim for the target word count
- Write complete, publishable prose — not placeholder text
"""
    
    response = llm.invoke([
        {"role": "system", "content": "You are a world-class content writer. Produce complete, engaging, publish-ready content."},
        {"role": "user", "content": writing_prompt}
    ])
    
    draft_content = response.content
    
    return {
        "draft_content": draft_content,
        "messages": [{"role": "assistant", "content": f"Draft written. Word count: approximately {len(draft_content.split())} words."}]
    }

Creating the Review Node

The review node acts as a quality gate. It evaluates the draft against multiple criteria and assigns a score. If the score is below a threshold, the draft is sent back for revision:

def review_node(state: ContentWritingState) -> dict:
    """
    Reviews the draft content against quality criteria.
    Returns a score and structured feedback. If the score is below the threshold,
    the draft needs revision.
    """
    print("šŸ”Ž [REVIEW] Evaluating draft quality...")
    
    review_prompt = f"""
You are a meticulous content editor. Review the following draft and provide a detailed evaluation.

ORIGINAL REQUIREMENTS:
- Topic: {state['topic']}
- Content Type: {state['content_type']}
- Target Audience: {state['target_audience']}
- Tone: {state['tone']}
- Target Word Count: {state['word_count_target']}

OUTLINE (the draft should follow this):
{state['outline']}

DRAFT TO REVIEW:
{state['draft_content']}

Evaluate the draft on these criteria, scoring each from 1-10:
1. Structure & Organization — Does it follow the outline? Are sections logical?
2. Content Quality — Is the information accurate, substantive, and well-researched?
3. Tone & Voice — Does it match the specified tone and audience?
4. Engagement — Is the writing compelling and interesting?
5. Completeness — Are all key points covered? Is the word count appropriate?
6. Grammar & Polish — Is the writing clean and error-free?

Provide:
- Individual scores for each criterion
- An OVERALL SCORE (average, 1-10)
- Specific, actionable feedback for improvement
- A list of concrete revision suggestions (if any)
- A final verdict: "APPROVED" (score >= 7.5) or "NEEDS_REVISION" (score < 7.5)

Be honest and thorough. Do not approve subpar content.
"""
    
    response = llm.invoke([
        {"role": "system", "content": "You are a rigorous content editor. Evaluate drafts honestly and provide actionable feedback."},
        {"role": "user", "content": review_prompt}
    ])
    
    feedback = response.content
    
    # Parse the overall score from the feedback
    # Look for patterns like "OVERALL SCORE: 8.5" or "Overall Score: 7"
    import re
    score_match = re.search(r'OVERALL SCORE[:\s]*([\d.]+)', feedback, re.IGNORECASE)
    if score_match:
        review_score = float(score_match.group(1))
    else:
        # Fallback: try to find any score-like pattern
        score_match = re.search(r'score[:\s]*([\d.]+)', feedback, re.IGNORECASE)
        review_score = float(score_match.group(1)) if score_match else 5.0
    
    needs_revision = review_score < 7.5
    
    return {
        "review_feedback": feedback,
        "review_score": review_score,
        "needs_revision": needs_revision,
        "messages": [{"role": "assistant", "content": f"Review complete. Score: {review_score}/10. {'Needs revision' if needs_revision else 'Approved!'}"}]
    }

Creating the Revision Node

The revision node takes the review feedback and rewrites the draft to address all issues. It tracks how many revisions have been made to prevent infinite revision loops:

def revision_node(state: ContentWritingState) -> dict:
    """
    Revises the draft based on review feedback.
    Increments the revision count and produces an improved version.
    """
    revision_num = state.get('revision_count', 0) + 1
    print(f"šŸ”„ [REVISION] Applying revision #{revision_num}...")
    
    revision_prompt = f"""
You are a skilled content reviser. Improve the following draft based on detailed editorial feedback.

ORIGINAL REQUIREMENTS:
- Topic: {state['topic']}
- Content Type: {state['content_type']}
- Target Audience: {state['target_audience']}
- Tone: {state['tone']}
- Target Word Count: {state['word_count_target']}

OUTLINE (ensure the revised draft follows this):
{state['outline']}

CURRENT DRAFT:
{state['draft_content']}

EDITORIAL FEEDBACK (address EVERY point):
{state['review_feedback']}

REVISION INSTRUCTIONS:
- Address every specific criticism and suggestion in the feedback
- Fix structural issues, tone problems, and content gaps
- Improve engagement and readability
- Ensure the draft follows the outline precisely
- This is revision #{revision_num} — be thorough
- Return the COMPLETE revised draft, not just the changed sections
"""
    
    response = llm.invoke([
        {"role": "system", "content": "You are an expert content reviser. Produce complete, improved drafts that address all feedback."},
        {"role": "user", "content": revision_prompt}
    ])
    
    revised_draft = response.content
    
    return {
        "draft_content": revised_draft,
        "revision_count": revision_num,
        "review_feedback": None,  # Clear old feedback for fresh review
        "review_score": None,
        "needs_revision": False,
        "messages": [{"role": "assistant", "content": f"Revision #{revision_num} complete. New draft ready for review."}]
    }

Assembling the LangGraph Workflow

Defining the Graph Structure

Now we connect all nodes into a directed graph. The workflow is: Research → Outline → Write → Review → (conditional: revise or finish). The review node conditionally routes back to revision if the score is too low, or ends the workflow if approved:

from langgraph.graph import StateGraph, END

def build_content_writing_graph():
    """
    Constructs the LangGraph workflow for content writing.
    
    Flow:
    research -> outline -> write -> review -> [conditional]
                                               |-> revise -> review (loop)
                                               |-> finalize -> END
    """
    
    # Initialize the graph with our state schema
    workflow = StateGraph(ContentWritingState)
    
    # Add all nodes
    workflow.add_node("research", research_node)
    workflow.add_node("outline", outline_node)
    workflow.add_node("write", writing_node)
    workflow.add_node("review", review_node)
    workflow.add_node("revise", revision_node)
    workflow.add_node("finalize", finalize_node)
    
    # Define the main flow edges
    workflow.add_edge("research", "outline")
    workflow.add_edge("outline", "write")
    workflow.add_edge("write", "review")
    
    # Conditional edge: after review, either revise or finalize
    workflow.add_conditional_edges(
        "review",
        should_continue_revising,
        {
            "revise": "revise",
            "finalize": "finalize"
        }
    )
    
    # After revision, go back to review
    workflow.add_edge("revise", "review")
    
    # Finalize ends the workflow
    workflow.add_edge("finalize", END)
    
    # Set the entry point
    workflow.set_entry_point("research")
    
    return workflow.compile()

Conditional Routing Logic

The conditional routing function determines whether the draft needs another revision round or is ready for finalization. It also enforces a maximum revision count to prevent infinite loops:

MAX_REVISIONS = 3

def should_continue_revising(state: ContentWritingState) -> str:
    """
    Determines whether to revise the draft again or finalize it.
    
    Returns 'revise' if:
    - The review score is below 7.5
    - The revision count hasn't exceeded MAX_REVISIONS
    
    Returns 'finalize' if:
    - The review score is 7.5 or higher
    - OR the max revision count has been reached
    """
    needs_revision = state.get('needs_revision', False)
    revision_count = state.get('revision_count', 0)
    
    if needs_revision and revision_count < MAX_REVISIONS:
        print(f"šŸ”„ Draft needs revision (score: {state.get('review_score')}). Revision {revision_count + 1} of {MAX_REVISIONS}.")
        return "revise"
    else:
        if revision_count >= MAX_REVISIONS and needs_revision:
            print(f"āš ļø Max revisions ({MAX_REVISIONS}) reached. Finalizing best available draft (score: {state.get('review_score')}).")
        else:
            print(f"āœ… Draft approved! Score: {state.get('review_score')}/10. Finalizing...")
        return "finalize"

The Finalize Node

The finalize node marks the content as ready and prepares it for delivery. This is where you might format the output, save to a file, or trigger downstream publishing workflows:

def finalize_node(state: ContentWritingState) -> dict:
    """
    Finalizes the approved content and prepares it for delivery.
    """
    print("šŸ [FINALIZE] Preparing final content...")
    
    final_content = state['draft_content']
    
    # You could add post-processing here: formatting, SEO optimization,
    # saving to database, triggering publishing pipeline, etc.
    
    return {
        "final_content": final_content,
        "messages": [{"role": "assistant", "content": "Content finalized and ready for delivery."}]
    }

Compiling and Running the Agent

With the graph built, you compile it and invoke it with an initial state. The agent runs autonomously through all stages:

# Build the compiled graph
content_agent = build_content_writing_graph()

# Prepare the initial state
initial_state = {
    "topic": "Building Resilient Microservices with Circuit Breaker Patterns",
    "content_type": "technical tutorial",
    "target_audience": "backend developers with 2+ years experience",
    "tone": "technical but accessible",
    "word_count_target": 2000,
    "research_notes": None,
    "outline": None,
    "draft_content": None,
    "review_feedback": None,
    "review_score": None,
    "needs_revision": False,
    "revision_count": 0,
    "final_content": None,
    "messages": []
}

# Run the agent
print("šŸš€ Starting Content Writing Agent...")
print(f"Topic: {initial_state['topic']}")
print(f"Target: {initial_state['word_count_target']} words | {initial_state['tone']} tone")
print("-" * 60)

final_state = content_agent.invoke(initial_state)

# Access the final content
print("\n" + "=" * 60)
print("šŸ“„ FINAL CONTENT:")
print("=" * 60)
print(final_state['final_content'][:500] + "...\n")
print(f"\nāœ… Agent completed. Final score: {final_state.get('review_score')}/10")
print(f"Total revisions: {final_state.get('revision_count', 0)}")
print(f"Final word count: approximately {len(final_state['final_content'].split())} words")

Complete Working Example

Full Code Implementation

Below is the complete, runnable implementation of the content writing agent. Save this as a single Python file and run it after installing dependencies and setting your API key:

"""
Complete Content Writing Agent with LangGraph
=============================================
A multi-stage agent that researches, outlines, writes,
reviews, and revises content until quality standards are met.
"""

import os
import re
from typing import TypedDict, Annotated, List, Optional
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, END
from langgraph.graph.message import add_messages

# ─── Configuration ───────────────────────────────────────────

load_dotenv()

# Primary writing model (higher temperature for creativity)
llm = ChatOpenAI(
    model="gpt-4o",
    temperature=0.7,
    max_tokens=4096,
    api_key=os.getenv("OPENAI_API_KEY")
)

# Research model (lower temperature for factual accuracy)
research_llm = ChatOpenAI(
    model="gpt-4o",
    temperature=0.3,
    max_tokens=2048,
    api_key=os.getenv("OPENAI_API_KEY")
)

MAX_REVISIONS = 3
QUALITY_THRESHOLD = 7.5

# ─── State Schema ────────────────────────────────────────────

class ContentWritingState(TypedDict):
    topic: str
    content_type: str
    target_audience: str
    tone: str
    word_count_target: int
    research_notes: Optional[str]
    outline: Optional[str]
    draft_content: Optional[str]
    review_feedback: Optional[str]
    review_score: Optional[float]
    needs_revision: bool
    revision_count: int
    final_content: Optional[str]
    messages: Annotated[List, add_messages]

# ─── Node Functions ─────────────────────────────────────────

def research_node(state: ContentWritingState) -> dict:
    print("šŸ” [RESEARCH] Gathering information...")
    
    prompt = f"""You are an expert researcher. Conduct thorough research.

Topic: {state['topic']}
Content Type: {state['content_type']}
Target Audience: {state['target_audience']}

Provide:
1. Key facts and statistics
2. Current trends and developments
3. Common questions or pain points
4. Notable examples or case studies
5. Relevant terminology and concepts

Format as structured, detailed research notes."""
    
    response = research_llm.invoke([
        {"role": "system", "content": "You are a meticulous researcher. Provide comprehensive research notes."},
        {"role": "user", "content": prompt}
    ])
    
    return {
        "research_notes": response.content,
        "messages": [{"role": "assistant", "content": "Research completed."}]
    }

def outline_node(state: ContentWritingState) -> dict:
    print("šŸ“‹ [OUTLINE] Structuring content outline...")
    
    prompt = f"""You are a master content strategist. Create a detailed outline.

Topic: {state['topic']}
Content Type: {state['content_type']}
Target Audience: {state['target_audience']}
Tone: {state['tone']}
Target Word Count: {state['word_count_target']}

Research Notes:
{state['research_notes']}

Include:
1. Compelling title options (3)
2. Introduction structure (hook, context, thesis)
3. Main sections with headings (4-8 sections)
4. Key points for each section (2-3 each)
5. Conclusion structure
6. Word count distribution"""
    
    response = llm.invoke([
        {"role": "system", "content": "You are an expert content strategist."},
        {"role": "user", "content": prompt}
    ])
    
    return {
        "outline": response.content,
        "messages": [{"role": "assistant", "content": "Outline created."}]
    }

def writing_node(state: ContentWritingState) -> dict:
    print("āœļø [WRITING] Crafting content draft...")
    
    prompt = f"""You are an exceptional content writer. Write a complete draft.

Topic: {state['topic']}
Content Type: {state['content_type']}
Target Audience: {state['target_audience']}
Tone: {state['tone']}
Target: ~{state['word_count_target']} words

OUTLINE (follow precisely):
{state['outline']}

RESEARCH (incorporate naturally):
{state['research_notes']}

Guidelines:
- Follow the outline exactly
- Write in the specified tone
- Incorporate research seamlessly
- Write complete, publishable prose
- Use clear transitions
- Include engaging hook and compelling conclusion"""
    
    response = llm.invoke([
        {"role": "system", "content": "You are a world-class content writer. Produce complete, engaging content."},
        {"role": "user", "content": prompt}
    ])
    
    return {
        "draft_content": response.content,
        "messages": [{"role": "assistant", "content": f"Draft written. ~{len(response.content.split())} words."}]
    }

def review_node(state: ContentWritingState) -> dict:
    print("šŸ”Ž [REVIEW] Evaluating draft quality...")
    
    prompt = f"""You are a meticulous editor. Review this draft thoroughly.

REQUIREMENTS:
- Topic: {state['topic']}
- Type: {state['content_type']}
- Audience: {state['target_audience']}
- Tone: {state['tone']}
- Target: {state['word_count_target']} words

OUTLINE:
{state['outline']}

DRAFT:
{state['draft_content']}

Score each criterion 1-10:
1. Structure & Organization
2. Content Quality & Accuracy
3. Tone & Voice
4. Engagement & Readability
5. Completeness & Coverage
6. Grammar & Polish

Provide:
- Individual scores
- OVERALL SCORE (average)
- Specific, actionable feedback
- Revision suggestions
- Verdict: "APPROVED" (score >= 7.5) or "NEEDS_REVISION" (score < 7.5)

Be honest and rigorous."""
    
    response = llm.invoke([
        {"role": "system", "content": "You are a rigorous editor. Evaluate honestly."},
        {"role": "user", "content": prompt}
    ])
    
    feedback = response.content
    
    score_match = re.search(r'OVERALL SCORE[:\s]*([\d.]+)', feedback, re.IGNORECASE)
    if score_match:
        review_score = float(score_match.group(1))
    else:
        score_match = re.search(r'score[:\s]*([\d.]+)', feedback, re.IGNORECASE)
        review_score = float(score_match.group(1)) if score_match else 5.0
    
    needs_revision = review_score < QUALITY_THRESHOLD
    
    return {
        "review_feedback": feedback,
        "review_score": review_score,
        "needs_revision": needs_revision,
        "messages": [{"role": "assistant", "content": f"Score: {review_score}/10. {'Needs revision' if needs_revision else 'Approved!'}"}]
    }

def revision_node(state: ContentWritingState) -> dict:
    revision_num = state.get('revision_count', 0) + 1
    print(f"šŸ”„ [REVISION] Applying revision #{revision_num}...")
    
    prompt = f"""You are a skilled reviser. Improve this draft based on editorial feedback.

REQUIREMENTS:
- Topic: {state['topic']}
- Type: {state['content_type']}
- Audience: {state['target_audience']}
- Tone: {state['tone']}
- Target: {state['word_count_target']} words

OUTLINE:
{state['outline']}

CURRENT DRAFT:
{state['draft_content']}

FEEDBACK (address EVERY point):
{state['review_feedback']}

Instructions:
- Address all criticisms and suggestions
- Fix structure, tone, content gaps
- Improve engagement
- This is revision #{revision_num}
- Return the COMPLETE revised draft"""
    
    response = llm.invoke([
        {"role": "system", "content": "You are an expert reviser. Produce complete improved drafts."},
        {"role": "user", "content": prompt}
    ])
    
    return {
        "draft_content": response.content,
        "revision_count": revision_num,
        "review_feedback": None,
        "review_score": None,
        "needs_revision": False,
        "messages": [{"role": "assistant", "content": f"Revision #{revision_num} complete."}]
    }

def finalize_node(state: ContentWritingState) -> dict:
    print("šŸ [FINALIZE] Preparing final content...")
    return {
        "final_content": state['draft_content'],
        "messages": [{"role": "assistant", "content": "Content finalized."}]
    }

# ─── Routing Logic ──────────────────────────────────────────

def should_continue_revising(state: ContentWritingState) -> str:
    needs_revision = state.get('needs_revision', False)
    revision_count = state.get('revision_count', 0)
    
    if needs_revision and revision_count < MAX_REVISIONS:
        print(f"šŸ”„ Revision needed. Round {revision_count + 1} of {MAX_REVISIONS}.")
        return "revise"
    else:
        if revision_count >= MAX_REVISIONS and needs_revision:
            print(f"āš ļø Max revisions reached. Finalizing best draft (score: {state.get('review_score')}).")
        else:
            print(f"āœ… Approved! Score: {state.get('review_score')}/10")
        return "finalize"

# ─── Graph Construction ─────────────────────────────────────

def build_content_writing_graph():
    workflow = StateGraph(ContentWritingState)
    
    workflow.add_node("research", research_node)
    workflow.add_node("outline", outline_node)
    workflow.add_node("write", writing_node)
    workflow.add_node("review", review_node)
    workflow.add_node("revise", revision_node)
    workflow.add_node("finalize", finalize_node)
    
    workflow.add_edge("research", "outline")
    workflow.add_edge("outline", "write")
    workflow.add_edge("write", "review")
    
    workflow.add_conditional_edges(
        "review",
        should_continue_revising,
        {"revise": "revise", "finalize": "finalize"}
    )
    
    workflow.add_edge("revise", "review")
    workflow.add_edge("finalize", END)
    
    workflow.set_entry_point("research")
    
    return workflow.compile()

# ─── Main Execution ─────────────────────────────────────────

if __name__ == "__main__":
    content_agent = build_content_writing_graph()
    
    initial_state = {
        "topic": "Building Resilient Microservices with Circuit Breaker Patterns",
        "content_type": "technical tutorial",
        "target_audience": "backend developers with 2+ years experience",
        "tone": "technical but accessible",
        "word_count_target": 2000,
        "research_notes": None,
        "outline": None,
        "draft_content": None,
        "review_feedback": None,
        "review_score": None,
        "needs_revision": False,
        "revision_count": 0,
        "final_content": None,
        "messages": []
    }
    
    print("šŸš€ Starting Content Writing Agent...")
    print(f"Topic: {initial_state['topic']}")
    print(f"Target: {initial_state['word_count_target']} words | {initial_state['tone']} tone")
    print("-" * 60)
    
    final_state =

— Ad —

Google AdSense will appear here after approval

← Back to all articles