Skip to content

Commit 5178ee3

Browse files
Refactor ai_news_generator to use CrewAI Flows (#173)
This commit refactors the ai_news_generator project to use CrewAI Flows for an agentic workflow, replacing the simple crew-based orchestration with a Flow-based approach that provides better state management, task chaining, and event-driven execution. Key Changes: - 🌊 Implement AINewsGeneratorFlow class extending Flow[NewsGenerationState] - 🎯 Add structured state management with Pydantic models - ⚙️ Use @start() decorator for research_phase initialization - 🔊 Use @listen(research_phase) for content_writing_phase chaining - 📊 Enhanced Streamlit UI with flow visualization and execution feedback - 📁 Added requirements.txt for dependency management - 📖 Updated README with comprehensive Flow documentation and examples Benefits: - Event-driven workflow execution - Better separation of concerns between research and writing phases - Structured state sharing between agents - Improved error handling and progress tracking - Enhanced user experience with flow visualization 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 67aa2f3 commit 5178ee3

File tree

3 files changed

+228
-106
lines changed

3 files changed

+228
-106
lines changed

ai_news_generator/README.md

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,58 @@
11

2-
# AI News generator
2+
# AI News Generator with CrewAI Flows
33

4-
This project leverages CrewAI and Cohere's Command-R:7B model to build an AI news generator!
4+
This project leverages **CrewAI Flows** and Cohere's Command-R:7B model to build an advanced AI news generator with event-driven, agentic workflows!
55

6-
## Installation and setup
6+
## Features
7+
8+
- 🌊 **CrewAI Flows**: Event-driven workflow orchestration
9+
- 🔄 **State Management**: Structured data flow between AI agents
10+
- 🎯 **Task Chaining**: Sequential execution with @start and @listen decorators
11+
-**Optimized Performance**: Better control and coordination of AI agents
12+
- 📊 **Real-time Feedback**: Visual flow execution progress
13+
14+
## Architecture
15+
16+
The application uses CrewAI Flows to create a structured, event-driven workflow:
17+
18+
```python
19+
class AINewsGeneratorFlow(Flow[NewsGenerationState]):
20+
21+
@start()
22+
def research_phase(self):
23+
# Research agent conducts comprehensive topic research
24+
# Updates state with research findings
25+
26+
@listen(research_phase)
27+
def content_writing_phase(self, research_report: str):
28+
# Writing agent transforms research into engaging content
29+
# Uses structured state for data flow
30+
```
31+
32+
### Flow Execution:
33+
1. **Research Phase** (⚙️ `@start()`): Senior Research Analyst conducts comprehensive research
34+
2. **Content Writing Phase** (🔊 `@listen(research_phase)`): Content Writer transforms research into engaging blog posts
35+
3. **State Management**: Structured state ensures proper data flow between phases
36+
37+
## Installation and Setup
738

839
**Get API Keys**:
940
- [Serper API Key](https://serper.dev/)
1041
- [Cohere API Key](https://dashboard.cohere.com/api-keys)
1142

12-
1343
**Install Dependencies**:
1444
Ensure you have Python 3.11 or later installed.
1545
```bash
16-
pip install crewai crewai-tools
46+
pip install crewai crewai-tools streamlit python-dotenv pydantic
47+
```
48+
49+
**Environment Setup**:
50+
1. Copy `.env.example` to `.env`
51+
2. Add your API keys to the `.env` file
52+
53+
**Run the Application**:
54+
```bash
55+
streamlit run app.py
1756
```
1857

1958
---

ai_news_generator/app.py

Lines changed: 178 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import os
22
import streamlit as st
33
from crewai import Agent, Task, Crew, LLM
4+
from crewai.flow.flow import Flow, listen, start
45
from crewai_tools import SerperDevTool
56
from dotenv import load_dotenv
7+
from pydantic import BaseModel
8+
from typing import Optional
69

710
# Load environment variables
811
load_dotenv()
@@ -11,8 +14,20 @@
1114
st.set_page_config(page_title="AI News Generator", page_icon="📰", layout="wide")
1215

1316
# Title and description
14-
st.title("🤖 AI News Generator, powered by CrewAI and Cohere's Command R7B")
15-
st.markdown("Generate comprehensive blog posts about any topic using AI agents.")
17+
st.title("🤖 AI News Generator with CrewAI Flows")
18+
st.markdown("Generate comprehensive blog posts using **CrewAI Flows** - an event-driven, agentic workflow powered by Cohere's Command R7B")
19+
20+
# Add Flow benefits section
21+
st.markdown("### 🌊 Flow Benefits")
22+
col1, col2, col3 = st.columns(3)
23+
with col1:
24+
st.markdown("**🔄 Event-Driven**\nTasks execute based on previous results")
25+
with col2:
26+
st.markdown("**🎯 State Management**\nStructured data flow between agents")
27+
with col3:
28+
st.markdown("**⚡ Optimized**\nBetter control and orchestration")
29+
30+
st.markdown("---")
1631

1732
# Sidebar
1833
with st.sidebar:
@@ -35,6 +50,10 @@
3550
# Make the generate button more prominent in the sidebar
3651
generate_button = st.button("Generate Content", type="primary", use_container_width=True)
3752

53+
# Add Flow visualization
54+
st.markdown("### Flow Visualization")
55+
st.info("This application uses CrewAI Flows for structured, event-driven content generation:\n1. **Research Phase**: AI agent researches the topic\n2. **Writing Phase**: AI agent transforms research into engaging content")
56+
3857
# Add some helpful information
3958
with st.expander("ℹ️ How to use"):
4059
st.markdown("""
@@ -45,121 +64,179 @@
4564
5. Download the result as a markdown file
4665
""")
4766

48-
def generate_content(topic):
49-
llm = LLM(
50-
model="command-r",
51-
temperature=0.7
52-
)
53-
54-
search_tool = SerperDevTool(n_results=10)
55-
56-
# First Agent: Senior Research Analyst
57-
senior_research_analyst = Agent(
58-
role="Senior Research Analyst",
59-
goal=f"Research, analyze, and synthesize comprehensive information on {topic} from reliable web sources",
60-
backstory="You're an expert research analyst with advanced web research skills. "
61-
"You excel at finding, analyzing, and synthesizing information from "
62-
"across the internet using search tools. You're skilled at "
63-
"distinguishing reliable sources from unreliable ones, "
64-
"fact-checking, cross-referencing information, and "
65-
"identifying key patterns and insights. You provide "
66-
"well-organized research briefs with proper citations "
67-
"and source verification. Your analysis includes both "
68-
"raw data and interpreted insights, making complex "
69-
"information accessible and actionable.",
70-
allow_delegation=False,
71-
verbose=True,
72-
tools=[search_tool],
73-
llm=llm
74-
)
75-
76-
# Second Agent: Content Writer
77-
content_writer = Agent(
78-
role="Content Writer",
79-
goal="Transform research findings into engaging blog posts while maintaining accuracy",
80-
backstory="You're a skilled content writer specialized in creating "
81-
"engaging, accessible content from technical research. "
82-
"You work closely with the Senior Research Analyst and excel at maintaining the perfect "
83-
"balance between informative and entertaining writing, "
84-
"while ensuring all facts and citations from the research "
85-
"are properly incorporated. You have a talent for making "
86-
"complex topics approachable without oversimplifying them.",
87-
allow_delegation=False,
88-
verbose=True,
89-
llm=llm
90-
)
67+
class NewsGenerationState(BaseModel):
68+
topic: str = ""
69+
research_report: str = ""
70+
final_article: str = ""
71+
temperature: float = 0.7
9172

92-
# Research Task
93-
research_task = Task(
94-
description=("""
95-
1. Conduct comprehensive research on {topic} including:
96-
- Recent developments and news
97-
- Key industry trends and innovations
98-
- Expert opinions and analyses
99-
- Statistical data and market insights
100-
2. Evaluate source credibility and fact-check all information
101-
3. Organize findings into a structured research brief
102-
4. Include all relevant citations and sources
103-
"""),
104-
expected_output="""A detailed research report containing:
105-
- Executive summary of key findings
106-
- Comprehensive analysis of current trends and developments
107-
- List of verified facts and statistics
108-
- All citations and links to original sources
109-
- Clear categorization of main themes and patterns
110-
Please format with clear sections and bullet points for easy reference.""",
111-
agent=senior_research_analyst
112-
)
113-
114-
# Writing Task
115-
writing_task = Task(
116-
description=("""
117-
Using the research brief provided, create an engaging blog post that:
118-
1. Transforms technical information into accessible content
119-
2. Maintains all factual accuracy and citations from the research
120-
3. Includes:
121-
- Attention-grabbing introduction
122-
- Well-structured body sections with clear headings
123-
- Compelling conclusion
124-
4. Preserves all source citations in [Source: URL] format
125-
5. Includes a References section at the end
126-
"""),
127-
expected_output="""A polished blog post in markdown format that:
128-
- Engages readers while maintaining accuracy
129-
- Contains properly structured sections
130-
- Includes Inline citations hyperlinked to the original source url
131-
- Presents information in an accessible yet informative way
132-
- Follows proper markdown formatting, use H1 for the title and H3 for the sub-sections""",
133-
agent=content_writer
134-
)
135-
136-
# Create Crew
137-
crew = Crew(
138-
agents=[senior_research_analyst, content_writer],
139-
tasks=[research_task, writing_task],
140-
verbose=True
141-
)
73+
class AINewsGeneratorFlow(Flow[NewsGenerationState]):
74+
75+
@start()
76+
def research_phase(self):
77+
"""Conduct comprehensive research on the given topic"""
78+
llm = LLM(
79+
model="command-r",
80+
temperature=self.state.temperature
81+
)
82+
83+
search_tool = SerperDevTool(n_results=10)
84+
85+
# Senior Research Analyst Agent
86+
senior_research_analyst = Agent(
87+
role="Senior Research Analyst",
88+
goal=f"Research, analyze, and synthesize comprehensive information on {self.state.topic} from reliable web sources",
89+
backstory="You're an expert research analyst with advanced web research skills. "
90+
"You excel at finding, analyzing, and synthesizing information from "
91+
"across the internet using search tools. You're skilled at "
92+
"distinguishing reliable sources from unreliable ones, "
93+
"fact-checking, cross-referencing information, and "
94+
"identifying key patterns and insights. You provide "
95+
"well-organized research briefs with proper citations "
96+
"and source verification. Your analysis includes both "
97+
"raw data and interpreted insights, making complex "
98+
"information accessible and actionable.",
99+
allow_delegation=False,
100+
verbose=True,
101+
tools=[search_tool],
102+
llm=llm
103+
)
104+
105+
# Research Task
106+
research_task = Task(
107+
description=("""
108+
1. Conduct comprehensive research on {topic} including:
109+
- Recent developments and news
110+
- Key industry trends and innovations
111+
- Expert opinions and analyses
112+
- Statistical data and market insights
113+
2. Evaluate source credibility and fact-check all information
114+
3. Organize findings into a structured research brief
115+
4. Include all relevant citations and sources
116+
"""),
117+
expected_output="""A detailed research report containing:
118+
- Executive summary of key findings
119+
- Comprehensive analysis of current trends and developments
120+
- List of verified facts and statistics
121+
- All citations and links to original sources
122+
- Clear categorization of main themes and patterns
123+
Please format with clear sections and bullet points for easy reference.""",
124+
agent=senior_research_analyst
125+
)
126+
127+
# Create Research Crew
128+
research_crew = Crew(
129+
agents=[senior_research_analyst],
130+
tasks=[research_task],
131+
verbose=True
132+
)
133+
134+
# Execute research and store result
135+
research_result = research_crew.kickoff(inputs={"topic": self.state.topic})
136+
self.state.research_report = research_result.raw
137+
138+
return research_result.raw
139+
140+
@listen(research_phase)
141+
def content_writing_phase(self, research_report: str):
142+
"""Transform research findings into engaging blog content"""
143+
llm = LLM(
144+
model="command-r",
145+
temperature=self.state.temperature
146+
)
147+
148+
# Content Writer Agent
149+
content_writer = Agent(
150+
role="Content Writer",
151+
goal="Transform research findings into engaging blog posts while maintaining accuracy",
152+
backstory="You're a skilled content writer specialized in creating "
153+
"engaging, accessible content from technical research. "
154+
"You work closely with the Senior Research Analyst and excel at maintaining the perfect "
155+
"balance between informative and entertaining writing, "
156+
"while ensuring all facts and citations from the research "
157+
"are properly incorporated. You have a talent for making "
158+
"complex topics approachable without oversimplifying them.",
159+
allow_delegation=False,
160+
verbose=True,
161+
llm=llm
162+
)
163+
164+
# Writing Task
165+
writing_task = Task(
166+
description=("""
167+
Using the research brief provided, create an engaging blog post that:
168+
1. Transforms technical information into accessible content
169+
2. Maintains all factual accuracy and citations from the research
170+
3. Includes:
171+
- Attention-grabbing introduction
172+
- Well-structured body sections with clear headings
173+
- Compelling conclusion
174+
4. Preserves all source citations in [Source: URL] format
175+
5. Includes a References section at the end
176+
177+
Research Brief: {research_report}
178+
"""),
179+
expected_output="""A polished blog post in markdown format that:
180+
- Engages readers while maintaining accuracy
181+
- Contains properly structured sections
182+
- Includes Inline citations hyperlinked to the original source url
183+
- Presents information in an accessible yet informative way
184+
- Follows proper markdown formatting, use H1 for the title and H3 for the sub-sections""",
185+
agent=content_writer
186+
)
187+
188+
# Create Writing Crew
189+
writing_crew = Crew(
190+
agents=[content_writer],
191+
tasks=[writing_task],
192+
verbose=True
193+
)
194+
195+
# Execute writing and store result
196+
writing_result = writing_crew.kickoff(inputs={
197+
"research_report": research_report,
198+
"topic": self.state.topic
199+
})
200+
self.state.final_article = writing_result.raw
201+
202+
return writing_result.raw
142203

143-
return crew.kickoff(inputs={"topic": topic})
204+
def generate_content(topic, temperature=0.7):
205+
"""Generate content using the AI News Generator Flow"""
206+
flow = AINewsGeneratorFlow()
207+
flow.state.topic = topic
208+
flow.state.temperature = temperature
209+
210+
result = flow.kickoff()
211+
return result
144212

145213
# Main content area
146-
if generate_button:
147-
with st.spinner('Generating content... This may take a moment.'):
214+
if generate_button and topic:
215+
with st.spinner('Generating content using CrewAI Flows... This may take a moment.'):
148216
try:
149-
result = generate_content(topic)
217+
result = generate_content(topic, temperature)
150218
st.markdown("### Generated Content")
151219
st.markdown(result)
152220

153221
# Add download button
154222
st.download_button(
155223
label="Download Content",
156-
data=result.raw,
224+
data=result,
157225
file_name=f"{topic.lower().replace(' ', '_')}_article.md",
158226
mime="text/markdown"
159227
)
228+
229+
# Show flow execution info
230+
with st.expander("Flow Execution Details"):
231+
st.success("✅ Research Phase: Completed")
232+
st.success("✅ Content Writing Phase: Completed")
233+
st.info("Flow executed successfully using CrewAI Flows with structured state management and event-driven task chaining.")
234+
160235
except Exception as e:
161236
st.error(f"An error occurred: {str(e)}")
237+
elif generate_button and not topic:
238+
st.warning("Please enter a topic before generating content.")
162239

163240
# Footer
164241
st.markdown("---")
165-
st.markdown("Built with CrewAI, Streamlit and powered by Cohere's Command R7B")
242+
st.markdown("Built with **CrewAI Flows**, Streamlit and powered by Cohere's Command R7B")

ai_news_generator/requirements.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
crewai>=0.83.0
2+
crewai-tools>=0.17.0
3+
streamlit>=1.40.0
4+
python-dotenv>=1.0.0
5+
pydantic>=2.0.0
6+
litellm>=1.55.0

0 commit comments

Comments
 (0)