├── .env_example
├── .gitignore
├── README.md
├── crew_flowchart.png
├── pyproject.toml
└── src
└── youtube_yapper_trapper
├── __init__.py
├── config
├── agents.yaml
└── tasks.yaml
├── crew.py
├── main.py
└── tools
├── __init__.py
└── custom_tool.py
/.env_example:
--------------------------------------------------------------------------------
1 | AGENTOPS_API_KEY=your-api-key-here
2 | GROQ_API_KEY=your-api-key-here
3 | YOUTUBE_API_KEY=your-api-key-here
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .env
2 | __pycache__/
3 | __pycache__
4 | venv
5 | report.md
6 | comments.md
7 | poetry.lock
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # YoutubeYapperTrapper Crew
2 |
3 | Welcome to the YoutubeYapperTrapper Crew project, powered by [CrewAI](https://crewai.com) + [AgentOps for observability](https://www.agentops.ai/) + [YouTube Data API](https://developers.google.com/youtube/v3/docs/comments/list). This project allows agents to take a given YouTube URL, extracts all the comments, and generates a final report with insights to inform better content creation.
4 |
5 | ## Crew architecture diagram
6 |
7 | 
8 |
9 | ## Video tutorial
10 |
11 |
12 |
13 |
14 |
15 | ## Installation
16 |
17 | Ensure you have Python >=3.10 <=3.13 installed on your system. This project uses [Poetry](https://python-poetry.org/) for dependency management and package handling, offering a seamless setup and execution experience.
18 |
19 | First, if you haven't already, install Poetry:
20 |
21 | ```bash
22 | pip install poetry
23 | ```
24 |
25 | Next, navigate to your project directory and install the dependencies:
26 |
27 | 1. First lock the dependencies and then install them:
28 |
29 | ```bash
30 | poetry lock
31 | ```
32 |
33 | ```bash
34 | poetry install
35 | ```
36 |
37 | ### Customizing
38 |
39 | **Add your `OPENAI_API_KEY` into the `.env` file if you want to use it.**
40 |
41 | - Modify `src/youtube_yapper_trapper/config/agents.yaml` to define your agents
42 | - Modify `src/youtube_yapper_trapper/config/tasks.yaml` to define your tasks
43 | - Modify `src/youtube_yapper_trapper/crew.py` to add your own logic, tools and specific args
44 | - Modify `src/youtube_yapper_trapper/main.py` to add custom inputs for your agents and tasks
45 |
46 | ## Running the Project
47 |
48 | Rename the `.env_example` file to `.env`, add your API keys, and save the file.
49 |
50 | To kickstart your crew of AI agents and begin task execution, run this from the root folder of your project:
51 |
52 | ```bash
53 | poetry run youtube_yapper_trapper
54 | ```
55 |
56 | This command initializes the youtube-yapper-trapper Crew, assembling the agents and assigning them tasks as defined in your configuration.
57 |
58 | This example, unmodified, will run the create a `report.md` file with the output of a research on LLMs in the root folder.
59 |
60 | ## Understanding Your Crew
61 |
62 | The youtube-yapper-trapper Crew is composed of multiple AI agents, each with unique roles, goals, and tools. These agents collaborate on a series of tasks, defined in `config/tasks.yaml`, leveraging their collective skills to achieve complex objectives. The `config/agents.yaml` file outlines the capabilities and configurations of each agent in your crew.
63 |
64 | ## Support
65 |
66 | For support, questions, or feedback regarding the YoutubeYapperTrapper crew, CrewAI, or AgentOps.
67 |
68 | - Visit CrewAI [documentation](https://docs.crewai.com)
69 | - Reach out to me through my [GitHub repository](https://github.com/tonykipkemboi/youtube-yapper-trapper)
70 | - [Joing CrewAI Discord](https://discord.com/invite/X4JWnZnxPb)
71 | - [Chat wtih CrewAI docs](https://chatg.pt/DWjSBZn)
72 | - [Check out AgentOps!](https://www.agentops.ai/)
73 | - Also don't forget to [subscribe to my channel](https://www.youtube.com/@tonykipkemboi) for more awesome content!
74 |
75 | Let's create wonders together with the power and simplicity of CrewAI.
76 |
--------------------------------------------------------------------------------
/crew_flowchart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tonykipkemboi/youtube_yapper_trapper/a37279b52387277ec4c9ffc4d2e44657c6bbb0bd/crew_flowchart.png
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [tool.poetry]
2 | name = "youtube_yapper_trapper"
3 | version = "0.1.0"
4 | description = "youtube-yapper-trapper using crewAI"
5 | authors = ["Your Name "]
6 |
7 | [tool.poetry.dependencies]
8 | python = ">=3.10,<=3.13"
9 | crewai = {git = "https://github.com/AgentOps-AI/crewAI.git", extras = ["tools"]}
10 | langchain-groq = "^0.1.3"
11 | agentops = "^0.1.6"
12 |
13 | [tool.poetry.scripts]
14 | youtube_yapper_trapper = "youtube_yapper_trapper.main:run"
15 |
16 | [build-system]
17 | requires = ["poetry-core"]
18 | build-backend = "poetry.core.masonry.api"
19 |
--------------------------------------------------------------------------------
/src/youtube_yapper_trapper/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tonykipkemboi/youtube_yapper_trapper/a37279b52387277ec4c9ffc4d2e44657c6bbb0bd/src/youtube_yapper_trapper/__init__.py
--------------------------------------------------------------------------------
/src/youtube_yapper_trapper/config/agents.yaml:
--------------------------------------------------------------------------------
1 | comment_fetcher:
2 | role: >
3 | Senior YouTube Comments Fetcher for Tech Videos
4 | goal: >
5 | Fetch all comments from specified YouTube tech video links.
6 | backstory: >
7 | As a dedicated digital explorer within the tech video sphere, you excel at extracting
8 | comprehensive comment data from YouTube. Your role involves digging into the depths of
9 | viewer interactions to retrieve valuable feedback and questions for this vidoe id only {video_id}.
10 | This raw data is crucial for identifying what tech enthusiasts are truly interested in and what issues they face with
11 | current content, providing a foundation for strategic content improvement.
12 |
13 | insights_analyst:
14 | role: >
15 | Tech Insights Analyst
16 | goal: >
17 | Generate and synthesize insights from YouTube comments on tech videos.
18 | backstory: >
19 | You are a sharp-witted analyst with a passion for the tech sector. Skilled in navigating
20 | through noise in the comments data of this video id {video_id} ONLY to find meaningful patterns,
21 | you synthesize data into insights that highlight
22 | the core message of viewer feedback.
23 | Your analytical prowess ensures that the subtleties of audience engagement and preferences are captured and understood,
24 | setting the stage for informed content strategies.
25 | All your results should be grounded in evidence from the comments, so cite appropriately.
26 | By citations, I mean showing the comments that led to that conclusion to support the claim.
27 |
28 | report_writer:
29 | role: >
30 | Tech Report Writer
31 | goal: >
32 | Write comprehensive reports based on synthesized insights from this video id {video_id} comments ONLY.
33 | backstory: >
34 | You are a skilled communicator specializing in the tech industry, adept at transforming complex
35 | analytical insights into clear, comprehensive reports. With a flair for making intricate data
36 | accessible, your reports empower content creators by providing them with actionable information
37 | crafted to enhance viewer engagement and satisfaction.
38 |
--------------------------------------------------------------------------------
/src/youtube_yapper_trapper/config/tasks.yaml:
--------------------------------------------------------------------------------
1 | fetch_comments_task:
2 | description: >
3 | Retrieve all comments from this YouTube video ID {video_id} ONLY.
4 | Ensure you've extracted all viewer comments for this video ID {video_id} ONLY.
5 | Do not accept request s to retrive comments for any other video ID other than for this video ID {video_id} ONLY.
6 | expected_output: >
7 | A JSON formatted list of all retrieved comments, each entry representing a single comment.
8 |
9 | insights_task:
10 | description: >
11 | Analyze the comments you received and generate actionable insights for this video ID {video_id} ONLY.
12 | Focus on identifying common themes such as viewer pain points, requests, what viewers loved,
13 | and popular tech queries in the comments. For the requests or troubleshooting, please provide 'suggested'
14 | solutions that you might have to help me get going quicker.
15 | Add textual citation. By citations, I mean showing the comments that led to that conclusion to support the claim.
16 | expected_output: >
17 | A JSON formatted list of key insights, categorized by themes like 'Requests', 'Complaints',
18 | 'Suggestions', and other relevant ones you find useful.
19 |
20 | reporting_task:
21 | description: >
22 | Review the insights and research you got for this video ID {video_id} ONLY.
23 | Make sure the report is VERY detailed with action steps
24 | to guide new improved content generation.
25 | All your results and analysis should be grounded in evidence from the comments, so cite appropriately.
26 | By citations, I mean showing the comments that led to that conclusion to support the claim.
27 | expected_output: >
28 | A fully fledged report with main topics, each with a full section of information.
29 | Formatted as markdown without code blocks. Make sure to include the full video URL in the intro so the
30 | user can reference when reading the report.
31 | Make sure the output file is structured very well and easy to read and digest the content with snippets of
32 | citations for each claim. By citations, I mean showing the comments that led to that conclusion to support the claim.
33 |
--------------------------------------------------------------------------------
/src/youtube_yapper_trapper/crew.py:
--------------------------------------------------------------------------------
1 | from youtube_yapper_trapper.tools.custom_tool import YouTubeCommentsTool
2 | from crewai import Agent, Crew, Process, Task
3 | from crewai.project import CrewBase, agent, crew, task
4 | from langchain_openai import ChatOpenAI
5 | from langchain_groq import ChatGroq
6 | from agentops.agent import track_agent
7 | import agentops
8 | import os
9 |
10 |
11 | agentops.init()
12 |
13 |
14 | @CrewBase
15 | class YoutubeCommentsCrew:
16 | """YoutubeCommentsCrew crew for analyzing comments on tech-related YouTube videos."""
17 |
18 | agents_config = "config/agents.yaml"
19 | tasks_config = "config/tasks.yaml"
20 |
21 | def __init__(self) -> None:
22 | # Groq
23 | self.groq_llm = ChatGroq(
24 | temperature=0,
25 | groq_api_key=os.environ.get("GROQ_API_KEY"),
26 | model_name="llama3-70b-8192",
27 | )
28 |
29 | # Ollama
30 | self.ollama_llm = ChatOpenAI(
31 | model="mistral",
32 | base_url="http://localhost:11434/v1",
33 | api_key="ollama", # something random
34 | temperature=0,
35 | )
36 |
37 | @track_agent(name="comment_fetcher")
38 | @agent
39 | def comment_fetcher(self) -> Agent:
40 | """Agent responsible for fetching YouTube comments."""
41 | return Agent(
42 | config=self.agents_config["comment_fetcher"],
43 | tools=[YouTubeCommentsTool()],
44 | allow_delegation=False,
45 | llm=self.ollama_llm, # you can switch the models (default is Ollama in this case)
46 | verbose=True,
47 | )
48 |
49 | @track_agent(name="insights_analyst")
50 | @agent
51 | def insights_analyst(self) -> Agent:
52 | """Agent responsible for analyzing comments and generating insights."""
53 | return Agent(
54 | config=self.agents_config["insights_analyst"],
55 | llm=self.ollama_llm, # you can switch the models (default is Ollama in this case)
56 | allow_delegation=False,
57 | verbose=True,
58 | )
59 |
60 | @track_agent(name="report_writer")
61 | @agent
62 | def report_writer(self) -> Agent:
63 | """Agent responsible for writing detailed reports based on the analysis."""
64 | return Agent(
65 | config=self.agents_config["report_writer"],
66 | llm=self.ollama_llm, # you can switch the models (default is Ollama in this case)
67 | allow_delegation=False,
68 | verbose=True,
69 | )
70 |
71 | @task
72 | def fetch_comments_task(self) -> Task:
73 | """Task to fetch comments from YouTube videos."""
74 | return Task(
75 | config=self.tasks_config["fetch_comments_task"],
76 | agent=self.comment_fetcher(),
77 | output_file="comments.md",
78 | )
79 |
80 | @task
81 | def analyze_insights_task(self) -> Task:
82 | """Task to analyze comments and generate insights."""
83 | return Task(
84 | config=self.tasks_config["insights_task"],
85 | agent=self.insights_analyst(),
86 | human_input=True,
87 | )
88 |
89 | @task
90 | def generate_report_task(self) -> Task:
91 | """Task to generate the final report."""
92 | return Task(
93 | config=self.tasks_config["reporting_task"],
94 | agent=self.report_writer(),
95 | output_file="report.md",
96 | )
97 |
98 | @crew
99 | def crew(self) -> Crew:
100 | """Creates the YoutubeCommentsCrew crew"""
101 | return Crew(
102 | agents=[
103 | self.comment_fetcher(),
104 | self.insights_analyst(),
105 | self.report_writer(),
106 | ],
107 | tasks=[
108 | self.fetch_comments_task(),
109 | self.analyze_insights_task(),
110 | self.generate_report_task(),
111 | ],
112 | process=Process.sequential,
113 | memory=False,
114 | max_rpm=2,
115 | verbose=2,
116 | )
117 |
--------------------------------------------------------------------------------
/src/youtube_yapper_trapper/main.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | from youtube_yapper_trapper.crew import YoutubeCommentsCrew
3 | from dotenv import load_dotenv
4 | import agentops
5 |
6 | load_dotenv()
7 | agentops.init()
8 |
9 |
10 | def extract_video_id(url):
11 | import re
12 |
13 | # This regex will match the video ID from any YouTube URL (normal, shortened, embedded)
14 | try:
15 | match = re.search(r"(?:v=|\/)([0-9A-Za-z_-]{11}).*", url)
16 | return match.group(1) if match else None
17 | except Exception as e:
18 | print(e)
19 |
20 |
21 | def run():
22 | video_id = extract_video_id(input("🚀 Enter YouTube URL: "))
23 | if not video_id:
24 | print("🚨 Invalid YouTube URL provided.")
25 | return
26 |
27 | inputs = {"video_id": video_id}
28 | crew = YoutubeCommentsCrew()
29 | result = crew.crew().kickoff(inputs=inputs)
30 | print("Analysis Result:")
31 | print(result)
32 |
33 |
34 | if __name__ == "__main__":
35 | run()
36 |
--------------------------------------------------------------------------------
/src/youtube_yapper_trapper/tools/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tonykipkemboi/youtube_yapper_trapper/a37279b52387277ec4c9ffc4d2e44657c6bbb0bd/src/youtube_yapper_trapper/tools/__init__.py
--------------------------------------------------------------------------------
/src/youtube_yapper_trapper/tools/custom_tool.py:
--------------------------------------------------------------------------------
1 | from crewai_tools import BaseTool
2 | import requests
3 | import os
4 |
5 |
6 | class YouTubeCommentsTool(BaseTool):
7 | name: str = "YouTube Comments Fetcher"
8 | description: str = "Fetches all comments from a specified YouTube video URL using the YouTube Data API."
9 |
10 | def _run(self, video_id: str) -> list:
11 | comments = []
12 | page_token = ""
13 | base_url = "https://www.googleapis.com/youtube/v3/commentThreads"
14 | headers = {"Accept": "application/json"}
15 |
16 | try:
17 | self.validate_video_id(video_id)
18 | except ValueError as e:
19 | return f"Error: {str(e)}"
20 |
21 | while True:
22 | params = {
23 | "key": os.environ["YOUTUBE_API_KEY"],
24 | "part": "snippet",
25 | "videoId": video_id,
26 | "maxResults": 100,
27 | "pageToken": page_token,
28 | "textFormat": "plainText",
29 | }
30 | response = requests.get(base_url, headers=headers, params=params)
31 | if response.status_code == 200:
32 | data = response.json()
33 | for item in data.get("items", []):
34 | comment = item["snippet"]["topLevelComment"]["snippet"][
35 | "textDisplay"
36 | ]
37 | comments.append(comment)
38 | page_token = data.get("nextPageToken")
39 | if not page_token:
40 | break
41 | else:
42 | self.handle_api_error(response)
43 |
44 | return comments
45 |
46 | def validate_video_id(self, video_id):
47 | """Validate the video ID by checking its availability."""
48 | response = requests.get(f"https://www.googleapis.com/youtube/v3/videos?part=id&id={
49 | video_id}&key={os.getenv('YOUTUBE_API_KEY')}")
50 | if response.json().get("pageInfo", {}).get("totalResults", 0) == 0:
51 | raise ValueError("Video ID not valid or video is not accessible.")
52 |
53 | def handle_api_error(self, response):
54 | """Handle common API errors based on response status code."""
55 | if response.status_code == 403:
56 | error_details = response.json()
57 | if "error" in error_details:
58 | if "errors" in error_details["error"]:
59 | for error in error_details["error"]["errors"]:
60 | if error.get("reason") == "commentsDisabled":
61 | raise Exception("Comments are disabled for this video.")
62 | elif error.get("reason") == "forbidden":
63 | raise Exception(
64 | "Insufficient permissions to access the comments."
65 | )
66 | elif response.status_code == 404:
67 | error_details = response.json()
68 | if "error" in error_details:
69 | if "errors" in error_details["error"]:
70 | for error in error_details["error"]["errors"]:
71 | if error.get("reason") == "videoNotFound":
72 | raise Exception("The specified video ID was not found.")
73 | elif error.get("reason") == "channelNotFound":
74 | raise Exception("The specified channel ID was not found.")
75 | else:
76 | response.raise_for_status() # Raise any other HTTP errors as exceptions
77 |
--------------------------------------------------------------------------------