This repository contains a production-ready research-driven blog writing agent that turns a user topic into a publishable markdown blog with supporting images.
The system implements a conservative, token-aware pipeline that reliably produces a blog even when LLM rate limits are reached. High-level flow:
- Start: user supplies a topic and an
as_ofdate. - Router: quickly decides whether external research is needed and picks a mode (
closed_book,hybrid,open_book). - Research (optional): runs web searches (Tavily) and extracts a small, high-signal evidence pack.
- Orchestrator: creates a 4–6 task plan (title, audience, tone, tasks).
- Worker(s): sequentially generate section markdown for each task (serialized to avoid token bursts).
- Reducer (merge_content -> decide_images -> generate_and_place_images): merge sections, plan images, generate images, and write final markdown.
- End: final markdown file is written (in working directory) and images are saved to
images/.
- Conservative token budgets and caps to avoid per-minute and per-day quota spikes.
- Groq LLM integration via
langchain-groqwith graceful fallback when daily TPD limits are hit (the app will still produce a blog + fallback visuals). - Research via Tavily (optional; requires
TAVILY_API_KEY). - Free image generation using Pollinations (no API key required) with a local SVG fallback when image generation fails.
- Streamlit frontend for running the app interactively (
bwa_frontend.py). - Generates a
.mdfile per blog and writes images toimages/.
- Python 3.10+ (Conda environment:
bwa) - LangGraph (graph orchestration)
- LangChain + langchain-core
- Groq LLM via
langchain-groq(primary LLM) - Tavily search via
langchain-tavily(web search) - Pollinations image generation (https://image.pollinations.ai) for cost-free images
- Streamlit frontend (
bwa_frontend.py) - Utilities:
pydantic,python-dotenv,pandas
bwa_backend.py— main graph and node implementations (router, research, orchestrator, worker, reducer).bwa_frontend.py— Streamlit UI for running the agent and previewing output.requirements.txt— recommended Python packages (seelangchain-tavilyaddition).images/— generated images are written here at runtime.screenshots/— example visuals used in this README (embedded below).
- Create or activate the
bwaconda environment.
conda activate bwa
pip install -r requirements.txt- Add a
.envfile in the project root with the following (if you use Groq/Tavily):
GROQ_API_KEY=your_groq_api_key_here
TAVILY_API_KEY=your_tavily_api_key_here # optional, for open_book/hybrid modes
- Run the Streamlit frontend:
streamlit run bwa_frontend.py- A markdown file is written for each generated blog (filename derived from the title) in the repository root.
- Images are written into the
images/folder. If Pollinations returns non-image content or is unreachable, the agent writes an auto-generated SVG fallback visual for each planned image so the markdown remains complete.
- The router is intentionally lightweight and low-token to decide whether research is necessary.
- Workers are executed sequentially (
worker_loop_node) to avoid parallel LLM calls and reduce tokens-per-minute usage. - When the Groq daily token hard cap (TPD) is reached, the system falls back to an internal plan + section renderers so you still get a coherent, actionable blog and images.
Below are the screenshots included in the screenshots/ folder (displayed in serial order):
- Image 1
- Image 2
- Image 3
- Image 4
- Image 5
- Image 6
- Image 7
- If you see repeated Conda warnings about
C:\\Users\\<you>\\.condarc, fix or remove that file — it does not affect the app but produces noise in terminal output. - If Groq returns 429/day-limit errors, the agent will fall back and continue producing content; to avoid this, upgrade your Groq plan or reduce usage.
If you want to improve planning prompts, image prompts, or add alternative image providers, modify bwa_backend.py and adjust the prompt templates and the _pollinations_generate_image_bytes implementation.
This project is provided as-is. For questions or improvements, open an issue or contact the maintainer.






