diff --git a/ai_news_generator/README.md b/ai_news_generator/README.md index 6bc3e25b6..2a845f947 100644 --- a/ai_news_generator/README.md +++ b/ai_news_generator/README.md @@ -1,7 +1,41 @@ -# AI News generator +# AI News Generator -This project leverages CrewAI and Cohere's Command-R:7B model to build an AI news generator! +This project leverages **CrewAI Flows** and Cohere's Command-R:7B model to build a modular, agentic AI news generator! + +The application has been refactored to use CrewAI's new Flow-based architecture, providing better modularity, state management, and workflow orchestration. + +## ✨ Features + +- **Flow-Based Architecture**: Built using CrewAI Flows with `@start` and `@listen` decorators +- **Two-Phase Workflow**: Research phase followed by content writing phase +- **Modular Design**: Separate agents for research and content writing +- **State Management**: Proper state handling between workflow phases +- **Multiple Interfaces**: Both CLI and Streamlit web interface +- **Structured Output**: Well-formatted markdown blog posts with citations + +## πŸ—οΈ Architecture + +The application uses a **Flow-based architecture** with two main phases: + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” @start β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Research Phase │──────────────▢│ Writing Phase β”‚ +β”‚ β”‚ @listen β”‚ β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ β€’ Web search β”‚ β”‚ β€’ Content β”‚ +β”‚ β€’ Fact checking β”‚ β”‚ generation β”‚ +β”‚ β€’ Source β”‚ β”‚ β€’ Formatting β”‚ +β”‚ validation β”‚ β”‚ β€’ Citations β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +### Core Components + +- **`NewsGeneratorFlow`**: Main Flow class orchestrating the workflow +- **Research Agent**: Senior Research Analyst for comprehensive research +- **Writing Agent**: Content Writer for transforming research into engaging content +- **State Management**: Pydantic models for structured data flow ## Installation and setup @@ -9,13 +43,122 @@ This project leverages CrewAI and Cohere's Command-R:7B model to build an AI new - [Serper API Key](https://serper.dev/) - [Cohere API Key](https://dashboard.cohere.com/api-keys) - **Install Dependencies**: Ensure you have Python 3.11 or later installed. ```bash - pip install crewai crewai-tools + pip install crewai crewai-tools streamlit python-dotenv + ``` + +**Environment Setup**: + Create a `.env` file in the project directory: + ```env + SERPER_API_KEY=your_serper_api_key_here + COHERE_API_KEY=your_cohere_api_key_here ``` +## πŸš€ Usage + +### Command Line Interface + +Run the flow directly from the command line: + +```bash +# Basic usage +python main.py --topic "Latest developments in artificial intelligence" + +# Save to file +python main.py --topic "Climate change solutions" --output article.md + +# Verbose mode +python main.py --topic "Blockchain innovations" --verbose +``` + +### Streamlit Web Interface + +Launch the interactive web interface: + +```bash +streamlit run app.py +``` + +Then open your browser to the displayed URL (typically `http://localhost:8501`). + +### Programmatic Usage + +Use the flow in your own Python code: + +```python +from news_flow import NewsGeneratorFlow + +# Create and run the flow +flow = NewsGeneratorFlow() +result = flow.kickoff(inputs={"topic": "Your topic here"}) +print(result) + +# Or use the convenience function +from news_flow import generate_content_with_flow + +content = generate_content_with_flow("Your topic here") +print(content) +``` + +## πŸ”§ Flow Implementation Details + +### Flow Structure + +The `NewsGeneratorFlow` class implements the CrewAI Flow pattern: + +```python +class NewsGeneratorFlow(Flow[ResearchState]): + @start() + def research_phase(self) -> str: + # Initial research using Senior Research Analyst + + @listen(research_phase) + def writing_phase(self, research_results: str) -> str: + # Content writing using research results +``` + +### State Management + +The flow uses structured state management with Pydantic models: + +```python +class ResearchState(BaseModel): + topic: str + research_brief: str + sources: list[str] = [] + key_findings: list[str] = [] +``` + +### Agent Specialization + +- **Senior Research Analyst**: Handles web research, fact-checking, and source validation +- **Content Writer**: Transforms research into engaging, well-structured blog content + +## πŸ§ͺ Testing + +Test the basic functionality: + +```bash +# Test the flow with a simple topic +python main.py --topic "Python programming" --verbose + +# Test the web interface +streamlit run app.py +``` + +## πŸ“ Project Structure + +``` +ai_news_generator/ +β”œβ”€β”€ app.py # Streamlit web interface +β”œβ”€β”€ main.py # CLI entry point +β”œβ”€β”€ news_flow.py # Core Flow implementation +β”œβ”€β”€ README.md # This file +└── .env # Environment variables (create this) +``` + --- ## πŸ“¬ Stay Updated with Our Newsletter! diff --git a/ai_news_generator/__pycache__/app.cpython-311.pyc b/ai_news_generator/__pycache__/app.cpython-311.pyc new file mode 100644 index 000000000..2e06cd544 Binary files /dev/null and b/ai_news_generator/__pycache__/app.cpython-311.pyc differ diff --git a/ai_news_generator/__pycache__/main.cpython-311.pyc b/ai_news_generator/__pycache__/main.cpython-311.pyc new file mode 100644 index 000000000..71678eacb Binary files /dev/null and b/ai_news_generator/__pycache__/main.cpython-311.pyc differ diff --git a/ai_news_generator/__pycache__/news_flow.cpython-311.pyc b/ai_news_generator/__pycache__/news_flow.cpython-311.pyc new file mode 100644 index 000000000..baef6b8d2 Binary files /dev/null and b/ai_news_generator/__pycache__/news_flow.cpython-311.pyc differ diff --git a/ai_news_generator/app.py b/ai_news_generator/app.py index 7fc78e07d..c981fe462 100644 --- a/ai_news_generator/app.py +++ b/ai_news_generator/app.py @@ -1,7 +1,6 @@ import os import streamlit as st -from crewai import Agent, Task, Crew, LLM -from crewai_tools import SerperDevTool +from news_flow import generate_content_with_flow from dotenv import load_dotenv # Load environment variables @@ -46,101 +45,10 @@ """) def generate_content(topic): - llm = LLM( - model="command-r", - temperature=0.7 - ) - - search_tool = SerperDevTool(n_results=10) - - # First Agent: Senior Research Analyst - senior_research_analyst = Agent( - role="Senior Research Analyst", - goal=f"Research, analyze, and synthesize comprehensive information on {topic} from reliable web sources", - backstory="You're an expert research analyst with advanced web research skills. " - "You excel at finding, analyzing, and synthesizing information from " - "across the internet using search tools. You're skilled at " - "distinguishing reliable sources from unreliable ones, " - "fact-checking, cross-referencing information, and " - "identifying key patterns and insights. You provide " - "well-organized research briefs with proper citations " - "and source verification. Your analysis includes both " - "raw data and interpreted insights, making complex " - "information accessible and actionable.", - allow_delegation=False, - verbose=True, - tools=[search_tool], - llm=llm - ) - - # Second Agent: Content Writer - content_writer = Agent( - role="Content Writer", - goal="Transform research findings into engaging blog posts while maintaining accuracy", - backstory="You're a skilled content writer specialized in creating " - "engaging, accessible content from technical research. " - "You work closely with the Senior Research Analyst and excel at maintaining the perfect " - "balance between informative and entertaining writing, " - "while ensuring all facts and citations from the research " - "are properly incorporated. You have a talent for making " - "complex topics approachable without oversimplifying them.", - allow_delegation=False, - verbose=True, - llm=llm - ) - - # Research Task - research_task = Task( - description=(""" - 1. Conduct comprehensive research on {topic} including: - - Recent developments and news - - Key industry trends and innovations - - Expert opinions and analyses - - Statistical data and market insights - 2. Evaluate source credibility and fact-check all information - 3. Organize findings into a structured research brief - 4. Include all relevant citations and sources - """), - expected_output="""A detailed research report containing: - - Executive summary of key findings - - Comprehensive analysis of current trends and developments - - List of verified facts and statistics - - All citations and links to original sources - - Clear categorization of main themes and patterns - Please format with clear sections and bullet points for easy reference.""", - agent=senior_research_analyst - ) - - # Writing Task - writing_task = Task( - description=(""" - Using the research brief provided, create an engaging blog post that: - 1. Transforms technical information into accessible content - 2. Maintains all factual accuracy and citations from the research - 3. Includes: - - Attention-grabbing introduction - - Well-structured body sections with clear headings - - Compelling conclusion - 4. Preserves all source citations in [Source: URL] format - 5. Includes a References section at the end - """), - expected_output="""A polished blog post in markdown format that: - - Engages readers while maintaining accuracy - - Contains properly structured sections - - Includes Inline citations hyperlinked to the original source url - - Presents information in an accessible yet informative way - - Follows proper markdown formatting, use H1 for the title and H3 for the sub-sections""", - agent=content_writer - ) - - # Create Crew - crew = Crew( - agents=[senior_research_analyst, content_writer], - tasks=[research_task, writing_task], - verbose=True - ) - - return crew.kickoff(inputs={"topic": topic}) + """ + Generate content using the new CrewAI Flow-based approach + """ + return generate_content_with_flow(topic) # Main content area if generate_button: diff --git a/ai_news_generator/main.py b/ai_news_generator/main.py new file mode 100644 index 000000000..49af79ac7 --- /dev/null +++ b/ai_news_generator/main.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 +""" +Main entry point for the AI News Generator using CrewAI Flows. + +This script provides a command-line interface to generate news content +using the NewsGeneratorFlow implementation. +""" + +import argparse +import sys +from typing import Optional +from news_flow import NewsGeneratorFlow + + +def main(): + """ + Main function to run the AI News Generator Flow from command line. + """ + parser = argparse.ArgumentParser( + description="Generate AI news content using CrewAI Flows", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +Examples: + python main.py --topic "Latest developments in artificial intelligence" + python main.py --topic "Climate change solutions" --output article.md + """ + ) + + parser.add_argument( + "--topic", + type=str, + required=True, + help="Topic to research and write about" + ) + + parser.add_argument( + "--output", + type=str, + help="Output file to save the generated content (optional)" + ) + + parser.add_argument( + "--verbose", + action="store_true", + help="Enable verbose logging" + ) + + args = parser.parse_args() + + if not args.topic.strip(): + print("Error: Topic cannot be empty", file=sys.stderr) + sys.exit(1) + + try: + print(f"πŸš€ Starting AI News Generator Flow for topic: '{args.topic}'") + print("πŸ“Š Initializing research phase...") + + # Create and run the flow + flow = NewsGeneratorFlow() + result = flow.kickoff(inputs={"topic": args.topic}) + + # Convert result to string if it's not already + content = str(result) + + print("βœ… Content generation completed!") + + # Save to file if specified + if args.output: + try: + with open(args.output, 'w', encoding='utf-8') as f: + f.write(content) + print(f"πŸ’Ύ Content saved to: {args.output}") + except IOError as e: + print(f"Error saving file: {e}", file=sys.stderr) + sys.exit(1) + else: + # Print to stdout + print("\n" + "="*80) + print("GENERATED CONTENT:") + print("="*80) + print(content) + print("="*80) + + return 0 + + except KeyboardInterrupt: + print("\n⚠️ Operation cancelled by user", file=sys.stderr) + return 1 + except Exception as e: + print(f"❌ Error: {e}", file=sys.stderr) + if args.verbose: + import traceback + traceback.print_exc() + return 1 + + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/ai_news_generator/news_flow.py b/ai_news_generator/news_flow.py new file mode 100644 index 000000000..49cbb2335 --- /dev/null +++ b/ai_news_generator/news_flow.py @@ -0,0 +1,193 @@ +import os +from typing import Any, Dict +from pydantic import BaseModel +from crewai import Agent, Task, Crew, LLM +from crewai.flow.flow import Flow, listen, start +from crewai_tools import SerperDevTool +from dotenv import load_dotenv + +load_dotenv() + + +class ResearchState(BaseModel): + """State model for research findings""" + topic: str + research_brief: str + sources: list[str] = [] + key_findings: list[str] = [] + + +class NewsGeneratorFlow(Flow[ResearchState]): + """ + CrewAI Flow for generating AI news content. + + This flow orchestrates a two-phase process: + 1. Research Phase: Comprehensive research on the given topic + 2. Writing Phase: Transform research into engaging blog content + """ + + def __init__(self): + super().__init__() + self.llm = LLM( + model="command-r", + temperature=0.7 + ) + self.search_tool = SerperDevTool(n_results=10) + + def _create_research_agent(self) -> Agent: + """Create the Senior Research Analyst agent""" + return Agent( + role="Senior Research Analyst", + goal="Research, analyze, and synthesize comprehensive information from reliable web sources", + backstory=( + "You're an expert research analyst with advanced web research skills. " + "You excel at finding, analyzing, and synthesizing information from " + "across the internet using search tools. You're skilled at " + "distinguishing reliable sources from unreliable ones, " + "fact-checking, cross-referencing information, and " + "identifying key patterns and insights. You provide " + "well-organized research briefs with proper citations " + "and source verification. Your analysis includes both " + "raw data and interpreted insights, making complex " + "information accessible and actionable." + ), + allow_delegation=False, + verbose=True, + tools=[self.search_tool], + llm=self.llm + ) + + def _create_content_writer_agent(self) -> Agent: + """Create the Content Writer agent""" + return Agent( + role="Content Writer", + goal="Transform research findings into engaging blog posts while maintaining accuracy", + backstory=( + "You're a skilled content writer specialized in creating " + "engaging, accessible content from technical research. " + "You work closely with the Senior Research Analyst and excel at maintaining the perfect " + "balance between informative and entertaining writing, " + "while ensuring all facts and citations from the research " + "are properly incorporated. You have a talent for making " + "complex topics approachable without oversimplifying them." + ), + allow_delegation=False, + verbose=True, + llm=self.llm + ) + + @start() + def research_phase(self) -> str: + """ + Initial phase: Conduct comprehensive research on the topic + """ + research_agent = self._create_research_agent() + + research_task = Task( + description=f""" + 1. Conduct comprehensive research on {self.state.topic} including: + - Recent developments and news + - Key industry trends and innovations + - Expert opinions and analyses + - Statistical data and market insights + 2. Evaluate source credibility and fact-check all information + 3. Organize findings into a structured research brief + 4. Include all relevant citations and sources + """, + expected_output="""A detailed research report containing: + - Executive summary of key findings + - Comprehensive analysis of current trends and developments + - List of verified facts and statistics + - All citations and links to original sources + - Clear categorization of main themes and patterns + Please format with clear sections and bullet points for easy reference.""", + agent=research_agent + ) + + # Create a single-agent crew for research + research_crew = Crew( + agents=[research_agent], + tasks=[research_task], + verbose=True + ) + + # Execute research and return results + research_result = research_crew.kickoff(inputs={"topic": self.state.topic}) + + # Update state with research findings + self.state.research_brief = str(research_result) + + return str(research_result) + + @listen(research_phase) + def writing_phase(self, research_results: str) -> str: + """ + Second phase: Transform research into engaging blog content + """ + content_writer = self._create_content_writer_agent() + + writing_task = Task( + description=f""" + Using the research brief provided: {research_results} + + Create an engaging blog post that: + 1. Transforms technical information into accessible content + 2. Maintains all factual accuracy and citations from the research + 3. Includes: + - Attention-grabbing introduction + - Well-structured body sections with clear headings + - Compelling conclusion + 4. Preserves all source citations in [Source: URL] format + 5. Includes a References section at the end + """, + expected_output="""A polished blog post in markdown format that: + - Engages readers while maintaining accuracy + - Contains properly structured sections + - Includes Inline citations hyperlinked to the original source url + - Presents information in an accessible yet informative way + - Follows proper markdown formatting, use H1 for the title and H3 for the sub-sections""", + agent=content_writer + ) + + # Create a single-agent crew for writing + writing_crew = Crew( + agents=[content_writer], + tasks=[writing_task], + verbose=True + ) + + # Execute writing phase + writing_result = writing_crew.kickoff(inputs={ + "topic": self.state.topic, + "research_results": research_results + }) + + return str(writing_result) + + def kickoff(self, inputs: Dict[str, Any]) -> Any: + """ + Initialize and run the flow with the given topic + """ + # Initialize state with the topic + if not hasattr(self, 'state') or self.state is None: + self.state = ResearchState(topic=inputs.get("topic", "")) + else: + self.state.topic = inputs.get("topic", "") + + # Start the flow execution + return super().kickoff(inputs=inputs) + + +def generate_content_with_flow(topic: str) -> str: + """ + Convenience function to generate content using the NewsGeneratorFlow + + Args: + topic (str): The topic to research and write about + + Returns: + str: The generated blog post content + """ + flow = NewsGeneratorFlow() + result = flow.kickoff(inputs={"topic": topic}) + return str(result) \ No newline at end of file