diff --git a/cookbook/examples/workflows/social_media_content_planner/planner_agents.yaml b/cookbook/examples/workflows/social_media_content_planner/planner_agents.yaml deleted file mode 100644 index a875bbd80..000000000 --- a/cookbook/examples/workflows/social_media_content_planner/planner_agents.yaml +++ /dev/null @@ -1,35 +0,0 @@ -draft_analyzer: - role: > - Draft Analyzer - goal: > - Analyze draft and identify key ideas, sections and technical concepts - backstory: > - You are a technical writer with years of experience writing, editing and - reviewing technical blogs. You have a talent for understanding and - documenting technical concepts. - verbose: false - -twitter_thread_planner: - role: > - Twitter Thread Planner - goal: > - Create a Twitter thread plan based on the provided draft analysis - backstory: > - You are a technical writer with years of experience in converting long - technical blogs into Twitter threads. You have a talent for breaking longform - content into bite-sized tweets that are engaging and informative. And identify - relevant urls to media that can be associated with a tweet. - verbose: false - -linkedin_post_planner: - role: > - LinkedIn Post Planner - goal: > - Create an engaging LinkedIn post based on the provided draft analysis - backstory: > - You are a technical writer with extensive experience crafting technical LinkedIn - content. You excel at distilling technical concepts into clear, authoritative posts - that resonate with a professional audience while maintaining technical accuracy. - You know how to balance technical depth with accessibility and incorporate relevant - hashtags and mentions to maximize engagement. - verbose: false \ No newline at end of file diff --git a/cookbook/examples/workflows/social_media_content_planner/planner_tasks.yaml b/cookbook/examples/workflows/social_media_content_planner/planner_tasks.yaml deleted file mode 100644 index 1e7cfb850..000000000 --- a/cookbook/examples/workflows/social_media_content_planner/planner_tasks.yaml +++ /dev/null @@ -1,80 +0,0 @@ -analyze_draft: - description: | - Analyze the markdown file at {draft_path} to create a developer-focused - technical overview - - 1. Map out the core idea that the blog discusses - 2. Identify key sections and what each section is about - 3. For each section, extract all URLs that appear inside image markdown syntax ![](image_url) - 4. You must associate these identified image urls to their correspoinding sections, so that we can use them with the tweets as media pieces - - Focus on details that are important for a comprehensive understanding of - the blog. - expected_output: | - A technical analysis containing: - - Blog title and core concept/idea - - Key technical sections identified with their main points - - Important code examples or technical concepts covered - - Key takeaways for developers - - Relevant urls to media that are associated with the key sections and can be associated with a tweet, this must be done - -create_twitter_thread_plan: - description: | - Develop an engaging Twitter thread based on the draft analysis provided and closely follow the writing style prvided in the {path_to_example_threads} - - The thread should break down complex technical concepts into digestible, tweet-sized chunks - that maintain technical accuracy while being accessible. - - Plan should include: - - A strong hook tweet that captures attention, it should be under 10 words, it must be same as the title of the blog - - Logical flow from basic to advanced concepts - - Code snippets or key technical highlights that fit Twitter's format - - Relevant urls to media that are associated with the key sections and must be associated with their corresponding tweets - - Clear takeaways for engineering audience - - Make sure to cover: - - close follow the writing style provided in the {path_to_example_threads} - - The core problem being solved - - Key technical innovations or approaches - - Interesting implementation details - - Real-world applications or benefits - - Call to action for the conclusion - - Add relevant urls to each tweet that can be associated with a tweet - - Focus on creating a narrative that technical audiences will find valuable - while keeping each tweet concise, accessible and impactful. - expected_output: | - A Twitter thread with a list of tweets, where each tweet has the following: - - content - - urls to media that are associated with the tweet, whenever possible - - is_hook: true if the tweet is a hook tweet, false otherwise - - -create_linkedin_post_plan: - description: | - Develop a comprehensive LinkedIn post based on the draft analysis provided - - The post should present technical content in a professional, long-form format - while maintaining engagement and readability. - - Plan should include: - - An attention-grabbing opening statement, it should be same as the title of the blog - - Well-structured body that breaks down the technical content - - Professional tone suitable for LinkedIn's business audience - - One main blog URL placed strategically at the end of the post - - Strategic use of line breaks and formatting - - Relevant hashtags (3-5 maximum) - - Make sure to cover: - - The core technical problem and its business impact - - Key solutions and technical approaches - - Real-world applications and benefits - - Professional insights or lessons learned - - Clear call to action - - Focus on creating content that resonates with both technical professionals - and business leaders while maintaining technical accuracy. - expected_output: | - A LinkedIn post plan containing: - - content - - a main blog url that is associated with the post \ No newline at end of file diff --git a/cookbook/examples/workflows/social_media_content_planner/prompts.py b/cookbook/examples/workflows/social_media_content_planner/prompts.py new file mode 100644 index 000000000..3dc59a64d --- /dev/null +++ b/cookbook/examples/workflows/social_media_content_planner/prompts.py @@ -0,0 +1,110 @@ +# Planner Agents Configuration +agents_config = { + "blog_analyzer": { + "role": "Blog Analyzer", + "goal": "Analyze blog and identify key ideas, sections, and technical concepts", + "backstory": ( + "You are a technical writer with years of experience writing, editing, and reviewing technical blogs. " + "You have a talent for understanding and documenting technical concepts.\n\n" + ), + "verbose": False, + }, + "twitter_thread_planner": { + "role": "Twitter Thread Planner", + "goal": "Create a Twitter thread plan based on the provided blog analysis", + "backstory": ( + "You are a technical writer with years of experience in converting long technical blogs into Twitter threads. " + "You have a talent for breaking longform content into bite-sized tweets that are engaging and informative. " + "And identify relevant URLs to media that can be associated with a tweet.\n\n" + ), + "verbose": False, + }, + "linkedin_post_planner": { + "role": "LinkedIn Post Planner", + "goal": "Create an engaging LinkedIn post based on the provided blog analysis", + "backstory": ( + "You are a technical writer with extensive experience crafting technical LinkedIn content. " + "You excel at distilling technical concepts into clear, authoritative posts that resonate with a professional audience " + "while maintaining technical accuracy. You know how to balance technical depth with accessibility and incorporate " + "relevant hashtags and mentions to maximize engagement.\n\n" + ), + "verbose": False, + }, +} + +# Planner Tasks Configuration +tasks_config = { + "analyze_blog": { + "description": ( + "Analyze the markdown file at {blog_path} to create a developer-focused technical overview\n\n" + "1. Map out the core idea that the blog discusses\n" + "2. Identify key sections and what each section is about\n" + "3. For each section, extract all URLs that appear inside image markdown syntax ![](image_url)\n" + "4. You must associate these identified image URLs to their corresponding sections, so that we can use them with the tweets as media pieces\n\n" + "Focus on details that are important for a comprehensive understanding of the blog.\n\n" + ), + "expected_output": ( + "A technical analysis containing:\n" + "- Blog title and core concept/idea\n" + "- Key technical sections identified with their main points\n" + "- Important code examples or technical concepts covered\n" + "- Key takeaways for developers\n" + "- Relevant URLs to media that are associated with the key sections and can be associated with a tweet, this must be done.\n\n" + ), + }, + "create_twitter_thread_plan": { + "description": ( + "Develop an engaging Twitter thread based on the blog analysis provided and closely follow the writing style provided in the {path_to_example_threads}\n\n" + "The thread should break down complex technical concepts into digestible, tweet-sized chunks " + "that maintain technical accuracy while being accessible.\n\n" + "Plan should include:\n" + "- A strong hook tweet that captures attention, it should be under 10 words, it must be the same as the title of the blog\n" + "- Logical flow from basic to advanced concepts\n" + "- Code snippets or key technical highlights that fit Twitter's format\n" + "- Relevant URLs to media that are associated with the key sections and must be associated with their corresponding tweets\n" + "- Clear takeaways for engineering audience\n\n" + "Make sure to cover:\n" + "- The core problem being solved\n" + "- Key technical innovations or approaches\n" + "- Interesting implementation details\n" + "- Real-world applications or benefits\n" + "- Call to action for the conclusion\n" + "- Add relevant URLs to each tweet that can be associated with a tweet\n\n" + "Focus on creating a narrative that technical audiences will find valuable " + "while keeping each tweet concise, accessible, and impactful.\n\n" + ), + "expected_output": ( + "A Twitter thread with a list of tweets, where each tweet has the following:\n" + "- content\n" + "- URLs to media that are associated with the tweet, whenever possible\n" + "- is_hook: true if the tweet is a hook tweet, false otherwise\n\n" + ), + }, + "create_linkedin_post_plan": { + "description": ( + "Develop a comprehensive LinkedIn post based on the blog analysis provided\n\n" + "The post should present technical content in a professional, long-form format " + "while maintaining engagement and readability.\n\n" + "Plan should include:\n" + "- An attention-grabbing opening statement, it should be the same as the title of the blog\n" + "- Well-structured body that breaks down the technical content\n" + "- Professional tone suitable for LinkedIn's business audience\n" + "- One main blog URL placed strategically at the end of the post\n" + "- Strategic use of line breaks and formatting\n" + "- Relevant hashtags (3-5 maximum)\n\n" + "Make sure to cover:\n" + "- The core technical problem and its business impact\n" + "- Key solutions and technical approaches\n" + "- Real-world applications and benefits\n" + "- Professional insights or lessons learned\n" + "- Clear call to action\n\n" + "Focus on creating content that resonates with both technical professionals " + "and business leaders while maintaining technical accuracy.\n\n" + ), + "expected_output": ( + "A LinkedIn post plan containing:\n" + "- content\n" + "- a main blog URL that is associated with the post\n\n" + ), + }, +} diff --git a/cookbook/examples/workflows/social_media_content_planner/social_media_content_planner.py b/cookbook/examples/workflows/social_media_content_planner/social_media_content_planner.py index aa26665d7..3122edbf8 100644 --- a/cookbook/examples/workflows/social_media_content_planner/social_media_content_planner.py +++ b/cookbook/examples/workflows/social_media_content_planner/social_media_content_planner.py @@ -4,26 +4,19 @@ from phi.tools.firecrawl import FirecrawlTools from pydantic import BaseModel, Field from typing import List, Optional -import yaml import json from dotenv import load_dotenv import scheduler +from prompts import agents_config, tasks_config # Load environment variables load_dotenv() -# Load agent and task configurations -with open('planner_agents.yaml', 'r') as f: - agents_config = yaml.safe_load(f) - -with open('planner_tasks.yaml', 'r') as f: - tasks_config = yaml.safe_load(f) - # Define Pydantic models to structure responses class BlogAnalyzer(BaseModel): """ - Represents the response from the Draft Analyzer agent. + Represents the response from the Blog Analyzer agent. Includes the blog title and content in Markdown format. """ title: str @@ -61,13 +54,13 @@ class LinkedInPost(BaseModel): blog_analyzer = Agent( model=OpenAIChat(id="gpt-4o"), tools=[FirecrawlTools(scrape=True, crawl=False)], # Enables blog scraping capabilities - description=f"{agents_config['draft_analyzer']['role']} - {agents_config['draft_analyzer']['goal']}", + description=f"{agents_config['blog_analyzer']['role']} - {agents_config['blog_analyzer']['goal']}", instructions=[ - f"{agents_config['draft_analyzer']['backstory']}", - tasks_config['analyze_draft']['description'], # Task-specific instructions for draft analysis + f"{agents_config['blog_analyzer']['backstory']}", + tasks_config['analyze_blog']['description'], # Task-specific instructions for blog analysis ], response_model=BlogAnalyzer, # Expects response to follow the BlogAnalyzer Pydantic model - verbose=agents_config['draft_analyzer']['verbose'], + verbose=agents_config['blog_analyzer']['verbose'], ) # Twitter Thread Planner: @@ -116,7 +109,7 @@ def scrape_blog_post(self, blog_post_url: str): print(f"Blog title: {result.title}") return result.blog_content_markdown else: - raise ValueError("Unexpected content type received from draft analyzer.") + raise ValueError("Unexpected content type received from blog analyzer.") def generate_plan(self, blog_content: str, post_type: str) -> dict: if post_type == "twitter": @@ -158,7 +151,7 @@ def run(self): # Scrape the blog post blog_content = self.scrape_blog_post(self.blog_post_url) - # Generate the plan based on the draft and post type + # Generate the plan based on the blog and post type plan = self.generate_plan(blog_content, self.post_type) # Schedule and publish the content @@ -172,3 +165,4 @@ def run(self): # Initialize and run the workflow workflow = ContentPlanningWorkflow(blog_post_url=blog_post_url, post_type=post_type) workflow.run() +