Skip to content

Repository files navigation

🤖 Blog Writing Agent — LangGraph Multi-Agent Pipeline

An autonomous, multi-agent blog generation architecture built with LangGraph, LangChain, and Google Gemini. The system dynamically routes requests based on research needs, generates structured section plans, executes parallel task drafting, stitches sections together, and automatically plans & generates technical diagrams using Gemini 2.5 Flash Image.

Python LangGraph Gemini Streamlit License


📖 Overview

This project implements a fully autonomous blog-writing pipeline as a stateful LangGraph agent graph. Rather than a single LLM call, the system:

  • Dynamically decides whether a topic needs external research
  • Plans a structured, multi-section blog outline
  • Drafts sections in parallel via worker agents
  • Merges and stitches sections into a cohesive draft
  • Plans and generates technical diagrams/images to accompany the content

The result is a modular, production-style agent pipeline rather than a single-shot prompt — built to demonstrate real orchestration patterns (routing, parallelization, reducer subgraphs) in a practical, end-to-end application.


🏗️ System Architecture

The graph features a dynamic router that directs execution based on whether a topic requires external web research (e.g., modern tech news vs. a timeless concept).

               ┌─────────────┐
               │  __start__  │
               └──────┬──────┘
                      │
                      ▼
               ┌─────────────┐
               │   router    │
               └──────┬──────┘
                      │
         ┌────────────┴────────────┐
         │ (needs_research=True)   │ (needs_research=False)
         ▼                         │
  ┌─────────────┐                  │
  │  research   │ (Tavily Search)  │
  └──────┬──────┘                  │
         └────────────┬────────────┘
                      │
                      ▼
               ┌─────────────┐
               │ orchestrator│ (Planner)
               └──────┬──────┘
                      │
                      ▼
               ┌─────────────┐
               │   worker    │ (Parallel Section Drafts)
               └──────┬──────┘
                      │
                      ▼
               ┌─────────────┐
               │   reducer   │ (Reducer Subgraph)
               └──────┬──────┘
                      │
                      ▼
               ┌─────────────┐
               │   __end__   │
               └─────────────┘

🔀 Reducer Subgraph Detail

The reducer node is itself a compiled stateful subgraph (reducer_subgraph) designed to handle text assembly, dynamic visual planning, and image byte generation.

 ┌─────────────────┐       ┌─────────────────┐       ┌────────────────────────────┐
 │  merge_content  │ ────> │  decide_images  │ ────> │ generate_and_place_images  │
 └─────────────────┘       └─────────────────┘       └────────────────────────────┘
 (Stitches worker            (Evaluates text &          (Calls Gemini Flash Image API
  markdown sections)          generates Global           & injects markdown images with
                              Image Plan)                graceful error fallback)
Stage Responsibility
merge_content Aggregates and orders individual section drafts produced by the workers
decide_images Uses structured outputs (GlobalImagePlan) to determine if up to 3 technical diagrams/tables are needed, outputting text placeholders ([[IMAGE_1]], etc.)
generate_and_place_images Intercepts placeholders, generates raw bytes via gemini-2.5-flash-image, handles fallback warnings on rate limits (429 RESOURCE_EXHAUSTED), and saves output files locally

📋 Data Schemas & Pydantic Models

The pipeline relies on strictly typed schemas for agent handoffs and structured LLM outputs:

from typing import List, Literal, Optional
from pydantic import BaseModel, Field

# Router Decision Schema
class RouterDecision(BaseModel):
    needs_research: bool
    mode: Literal['closed_book', 'hybrid', 'open_book']
    queries: List[str]

# Research Evidence Schemas
class EvidenceItem(BaseModel):
    title: str
    url: str
    published_at: Optional[str] = None
    snippet: Optional[str] = None
    source: Optional[str] = None

class EvidencePack(BaseModel):
    evidence: List[EvidenceItem]

# Task Schema (Section Spec)
class Task(BaseModel):
    id: int
    title: str
    goal: str = Field(..., description="One sentence describing section goal.")
    bullets: List[str] = Field(..., min_length=3, max_length=5)
    target_words: int = Field(..., description="Target word count (120-450).")
    tags: List[str]
    requires_research: bool
    requires_citations: bool
    requires_code: bool
    section_type: Literal[
        "intro", "core", "examples", "checklist", "common_mistakes", "conclusion"
    ]

# Global Plan Schema
class Plan(BaseModel):
    blog_title: str
    audience: str
    tone: str
    blog_kind: Literal['explainer', 'tutorial', 'news_roundup', 'comparison', 'system_design']
    constraints: List[str]
    tasks: List[Task]

✨ Key Features

  • 🧭 Dynamic routing — automatically decides whether a topic needs live web research
  • 🧩 Orchestrator–worker pattern — plans structured sections, then drafts them in parallel
  • 🔁 Reducer subgraph — merges parallel outputs into a single coherent document
  • 🖼️ Automated visual planning — decides when diagrams/images add value and generates them via Gemini 2.5 Flash Image
  • 🛡️ Multi-provider fallback chain — gracefully handles rate limits and API failures
  • 🎛️ Streamlit dashboard — interactive UI for running and viewing pipeline output
  • 📐 Strict structured outputs — Pydantic schemas enforce type-safe handoffs between agents

🛠️ Tech Stack

Category Tools
Orchestration LangGraph, LangChain
LLM Providers Google Gemini 2.5 (Flash / Flash Image), OpenRouter (fallback)
Research Tavily Search API
Validation Pydantic
UI Streamlit
Language Python 3.10+

📁 Repository Structure

.
├── blog_agent.py          # Main LangGraph graph definitions and worker logic
├── llm_manager.py         # Multi-provider fallback chain wrapper
├── main.py                # Pipeline CLI runner
├── streamlit_app.py       # Streamlit UI dashboard
├── test_models.py         # Diagnostic utility for API endpoint connectivity
├── requirements.txt       # Project dependencies
├── blog_basic.ipynb       # Prototype & testing notebook
└── README.md              # Project documentation

🚀 Quick Start

1. Installation & Environment Setup

git clone https://github.com/muhammadumarafzaal/BlogWriting-Agent-Langgraph.git
cd BlogWriting-Agent-Langgraph

python -m venv .venv
source .venv/bin/activate  # On Windows: .venv\Scripts\activate
pip install -r requirements.txt

2. Configure API Keys

Create a .env file in the root folder:

GOOGLE_API_KEY=your_gemini_api_key
TAVILY_API_KEY=your_tavily_search_api_key   # For research node
OPENROUTER_API_KEY=your_openrouter_api_key  # Optional fallback

3. Launch the UI

streamlit run streamlit_app.py

4. Or run via CLI

python main.py

🔮 Future Improvements

  • Add citation validation / fact-checking pass before final output
  • Support additional export formats (PDF, HTML)
  • Add caching layer for repeated research queries
  • Expand diagram generation to support more visual types (flowcharts, tables)
  • Add automated evaluation of generated blog quality

👤 Author

Muhammad Umar Afzaal Software Engineering Student | AI & Full-Stack Developer


📄 License

This project is licensed under the MIT License — see the LICENSE file for details.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages