Spaces:
Sleeping
Sleeping
| # import relevant libraries and modules | |
| import warnings | |
| from crewai import LLM, Task, Agent, Crew | |
| import os | |
| from dotenv import load_dotenv | |
| from pathlib import Path | |
| import gradio as gr | |
| from tools.hybrid_retriever_tool import HybridRetrieverTool | |
| # control warnings | |
| warnings.filterwarnings("ignore") | |
| load_dotenv() | |
| OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") | |
| #Define LLMs | |
| llm_planner = LLM(model="gpt-4o-mini", temperature=0.5) | |
| llm_writer = LLM(model="gpt-5-mini", temperature=1.0) | |
| llm_editor = LLM(model="gpt-4-turbo", temperature=0.3) | |
| llm_fact=LLM(model="gpt-4o-mini", temperature=0.3) | |
| #Define tools | |
| hybrid_tool = HybridRetrieverTool(alpha=0.6) #including RAG in search | |
| #Creating Agents | |
| planner = Agent( | |
| role="Research Lead", | |
| goal="Plan engaging and factually accurate content on {topic}", | |
| backstory="You are working on planning a blog article about the topic {topic}." | |
| "Use the retriever tool to gather accurate, recent information before outlining." #RAG search | |
| "You collect relevant information that helps the audience learn something and make informed decisions. " | |
| "Your work is the basis for the Content Writer to write an article on this topic.", | |
| allow_delegation=False, | |
| verbose=True, | |
| tools = [hybrid_tool], #Rag search | |
| llm=llm_planner | |
| ) | |
| writer = Agent( | |
| role="Technical Writer", | |
| goal="Write an insightful and factually accurate opinion piece about the topic: {topic}", | |
| backstory="You are working on writing a new opnion piece about the topic: {topic}. " | |
| "You base your writing on the work of the research lead, who provides an outline and relevant context about the topic. " | |
| "You follow the main objectives and direction of the outline, as provided by the Content Planner. " | |
| "You also provide objective and impartial insights, supported by relevant information that is provided by the Content Planner. " | |
| "You acknowledge in your opnion piece when your statements are opinions as opposed to objective staements.", | |
| allow_delegation=False, | |
| verbose=True, | |
| llm=llm_writer | |
| ) | |
| fact_checker = Agent( | |
| role="Data Verifier", | |
| goal="Verify factual accuracy, detect unsupported claims and identify missing references or sources.", | |
| backstory="You are a meticulous research analyst who checks every claim against known facts and relaible sources" | |
| "Use the retriever tool to cross-check the Content Writer's statements against reliable, recent information.", | |
| allow_delegation=False, | |
| verbose=True, | |
| tools = [hybrid_tool], #Rag search | |
| llm=llm_fact | |
| ) | |
| editor = Agent( | |
| role="Editor-in-Chief", | |
| goal="Edit a given blog post to align with the context and narrative style of the {topic} (e.g. academic, informal, satirical).", | |
| backstory="You are an editor who receives a blog post from the Content Writer. " | |
| "Your goal is to review the blog post to ensure that it follows journalistic best practices, " | |
| "provides balanced viewpoints, when providing opinions or assertions, " | |
| "and also avoids major controversial topics or opinions when possible. " | |
| "The blog post should align with the context and narrative style of the topic being discussed.", | |
| allow_delegation=False, | |
| verbose=True, | |
| llm=llm_editor | |
| ) | |
| #Creating Tasks | |
| plan = Task( | |
| name="content_planner_task", | |
| description=( | |
| "1. Prioritize the latest trends, key players, and groundbreaking news on {topic}.\n" | |
| "2. Identify the target audience, considering their interests and pain points.\n" | |
| "3. Develop a detailed content outline including and introduction, key points, and a call to action.\n" | |
| "4. Include SEO keywords and relevant data or sources" | |
| ), | |
| expected_output="A comprehensive content plan document with an outline, audience analysis, SEO keywords, and resources.", | |
| agent = planner | |
| ) | |
| write = Task( | |
| name="blog_writer_task", | |
| description=( | |
| "1. Use the provided content plan to craft a compelling blog post on {topic}.\n" | |
| "2. Incorporate SEO keywords naturally.\n" | |
| "3. Sections/Subtitles are properly named in an engaging manner.\n" | |
| "4. Ensure the post structured with an engaging introduction, insightful body, and a summarizing conclusion.\n" | |
| "5. The narrative style and language used should be determined by the target audience and {tone}.\n" | |
| "6. Proofread for grammatical errors and alignment with the target audience.\n" | |
| ), | |
| expected_output="A well-written blog post in markdown format, ready for publication. " | |
| "Each section should have 2 or 3 paragraphs and the whole blog should be a maximum of 1000 words.", | |
| agent=writer, | |
| context=[plan] | |
| ) | |
| fact_check = Task( | |
| name="fact_checker_task", | |
| description=( | |
| "Carefully review the drafted blog post provided by the Content Writer. " | |
| "Check all factual statements for accuracy and ensure sources or references are mentioned. " | |
| "Identify any unsupported claims, logical inconsistencies, or outdated information. " | |
| "Provide corrections or highlight areas that require further verification." | |
| ), | |
| expected_output=( | |
| "A revised version of the blog post in markdown format, with annotations or corrections " | |
| "for any inaccurate or unsupported statements." | |
| ), | |
| agent=fact_checker, | |
| context=[write] | |
| ) | |
| edit = Task( | |
| name="editor_task", | |
| description=("Edit and refine the fact-checked blog post to ensure clarity, flow and consistent {tone}.\n" | |
| "Polish grammar, readability, and overall style." | |
| ), | |
| expected_output="A well-written blog post in markdown format, ready for publication. " | |
| "Each section should have 2 or 3 paragraphs and the whole blog should be a maximum of 1000 words", | |
| agent=editor, | |
| context=[fact_check] | |
| ) | |
| # Creating the Crew | |
| crew = Crew( | |
| agents=[planner, writer, fact_checker, editor], | |
| tasks=[plan, write, fact_check, edit], | |
| process="sequential", | |
| verbose=True | |
| ) | |
| # fetch context for RAG search | |
| def fetch_context(topic): | |
| passages = hybrid_tool._run(topic) | |
| if isinstance(passages, str): | |
| summary = passages | |
| else: | |
| summary = hybrid_tool.summarize_passages(topic, passages) | |
| return summary | |
| # Define Gradio Handler | |
| def generate_blog(topic, tone, progress=gr.Progress(track_tqdm=True)): | |
| progress(0, desc="Starting collaborative report generation...") | |
| yield "### π§ Starting Collaborative Report\n" | |
| # Run the entire multi-agent workflow once (non-streaming) | |
| result = crew.kickoff(inputs={"topic": topic, "tone": tone}) | |
| # Try to extract clean text output | |
| if hasattr(result, "output"): | |
| final_output = result.output | |
| elif hasattr(result, "final_output"): | |
| final_output = result.final_output | |
| else: | |
| final_output = str(result) | |
| progress(1.0, desc="Completed.") | |
| yield f"β **Collaborative Report Generated Successfully!**\n\n{final_output}" | |
| # Build Gradio Interface | |
| css = """ | |
| #output-box { | |
| background-color: #f8f9fa; | |
| border-radius: 12px; | |
| padding: 1.5rem; | |
| font-family: 'Inter', sans-serif; | |
| font-size: 1rem; | |
| line-height: 1.6; | |
| white-space: pre-wrap; | |
| overflow-y: auto; | |
| max-height: 70vh; | |
| } | |
| #context-box h1, #context-box h2, #context-box h3 { | |
| font-size: 1rem !important; | |
| font-weight: 600 !important; | |
| } | |
| #context-box { | |
| font-family: 'Inter', sans-serif; | |
| font-size: 1rem; | |
| line-height: 1.6; | |
| background-color: #ffffff; | |
| border: 1px solid #ddd; | |
| border-radius: 10px; | |
| padding: 1rem; | |
| margin-top: 0.5rem; | |
| max-height: 70vh; | |
| overflow-y: auto; | |
| } | |
| """ | |
| with gr.Blocks(css=css) as demo: | |
| gr.Markdown( | |
| """ | |
| ## βοΈ AI Blog Writer Multi-Agent (Multi-Agent Crew: Sequential research β writing β verification β editing) | |
| Create a professional, fact-checked, and polished article using CrewAI agents. | |
| """ | |
| ) | |
| with gr.Row(): | |
| topic = gr.Textbox( | |
| label="Blog Topic", | |
| placeholder="e.g. Sustainable Investing, Vegan cuisine, AI in Healthcare", | |
| value="Sustainable Investing" | |
| ) | |
| tone=gr.Dropdown( | |
| ["professional", "playful", "academic", "casual", "satirical", "humorous"], | |
| label="Select Writing Tone", | |
| value="academic" | |
| ) | |
| fetch_btn = gr.Button("π Fetch & Summarize Context", variant="secondary") # Rag Search | |
| context_output = gr.Markdown(label="π Retrieved Context Summary") # Rag Search | |
| run_button = gr.Button("π Generate Blog", variant="primary") | |
| final_output = gr.Textbox(label="π° Generated Blog Post", elem_id="output-box", lines=30, interactive=False, show_label=True) | |
| fetch_btn.click(fetch_context, inputs=[topic], outputs=[context_output]) # Rag Search | |
| run_button.click(generate_blog, inputs=[topic, tone], outputs=[final_output]) | |
| #Launch app | |
| if __name__=="__main__": | |
| demo.launch(server_name="0.0.0.0", server_port=7860, share=True) | |