Aira Thalca
commited on
Commit
·
58af945
1
Parent(s):
a4c6732
add streamlit app
Browse files- .gitignore +3 -0
- README.md +57 -0
- app.py +91 -0
- pyproject.toml +20 -0
- requirements.txt +6 -0
- src/educational_books/__init__.py +0 -0
- src/educational_books/crews/book_content/book_content.py +48 -0
- src/educational_books/crews/book_content/config/agents.yaml +21 -0
- src/educational_books/crews/book_content/config/tasks.yaml +34 -0
- src/educational_books/crews/book_outline/book_outline.py +48 -0
- src/educational_books/crews/book_outline/config/agents.yaml +21 -0
- src/educational_books/crews/book_outline/config/tasks.yaml +29 -0
- src/educational_books/main.py +92 -0
- src/educational_books/tools/__init__.py +0 -0
- src/educational_books/tools/custom_tool.py +22 -0
- src/educational_books/types.py +15 -0
- uv.lock +0 -0
.gitignore
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.env
|
| 2 |
+
__pycache__/
|
| 3 |
+
lib/
|
README.md
CHANGED
|
@@ -10,3 +10,60 @@ pinned: false
|
|
| 10 |
---
|
| 11 |
|
| 12 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
---
|
| 11 |
|
| 12 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
| 13 |
+
|
| 14 |
+
# Education Book Generator Crew
|
| 15 |
+
|
| 16 |
+
Welcome to the Education Book Generator Crew project, powered by [crewAI](https://crewai.com). This template is designed to help you set up a multi-agent AI system with ease, leveraging the powerful and flexible framework provided by crewAI. Our goal is to enable your agents to collaborate effectively on complex tasks, maximizing their collective intelligence and capabilities.
|
| 17 |
+
|
| 18 |
+
## Installation
|
| 19 |
+
|
| 20 |
+
Ensure you have Python >=3.10 <3.13 installed on your system. This project uses [UV](https://docs.astral.sh/uv/) for dependency management and package handling, offering a seamless setup and execution experience.
|
| 21 |
+
|
| 22 |
+
First, if you haven't already, install uv:
|
| 23 |
+
|
| 24 |
+
```bash
|
| 25 |
+
pip install uv
|
| 26 |
+
```
|
| 27 |
+
|
| 28 |
+
Next, navigate to your project directory and install the dependencies:
|
| 29 |
+
|
| 30 |
+
(Optional) Lock the dependencies and install them by using the CLI command:
|
| 31 |
+
```bash
|
| 32 |
+
crewai install
|
| 33 |
+
```
|
| 34 |
+
|
| 35 |
+
### Customizing
|
| 36 |
+
|
| 37 |
+
**Add your `OPENAI_API_KEY` into the `.env` file**
|
| 38 |
+
|
| 39 |
+
- Modify `src/educational_books/config/agents.yaml` to define your agents
|
| 40 |
+
- Modify `src/educational_books/config/tasks.yaml` to define your tasks
|
| 41 |
+
- Modify `src/educational_books/crew.py` to add your own logic, tools and specific args
|
| 42 |
+
- Modify `src/educational_books/main.py` to add custom inputs for your agents and tasks
|
| 43 |
+
|
| 44 |
+
## Running the Project
|
| 45 |
+
|
| 46 |
+
To kickstart your crew of AI agents and begin task execution, run this from the root folder of your project:
|
| 47 |
+
|
| 48 |
+
```bash
|
| 49 |
+
crewai run
|
| 50 |
+
```
|
| 51 |
+
|
| 52 |
+
This command initializes the educational_books Crew, assembling the agents and assigning them tasks as defined in your configuration.
|
| 53 |
+
|
| 54 |
+
This example, unmodified, will run the create a `report.md` file with the output of a research on LLMs in the root folder.
|
| 55 |
+
|
| 56 |
+
## Understanding Your Crew
|
| 57 |
+
|
| 58 |
+
The educational_books 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.
|
| 59 |
+
|
| 60 |
+
## Support
|
| 61 |
+
|
| 62 |
+
For support, questions, or feedback regarding the {{crew_name}} Crew or crewAI.
|
| 63 |
+
|
| 64 |
+
- Visit our [documentation](https://docs.crewai.com)
|
| 65 |
+
- Reach out to us through our [GitHub repository](https://github.com/joaomdmoura/crewai)
|
| 66 |
+
- [Join our Discord](https://discord.com/invite/X4JWnZnxPb)
|
| 67 |
+
- [Chat with our docs](https://chatg.pt/DWjSBZn)
|
| 68 |
+
|
| 69 |
+
Let's create wonders together with the power and simplicity of crewAI.
|
app.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
from src.educational_books.main import BookFlow
|
| 3 |
+
import threading
|
| 4 |
+
import time
|
| 5 |
+
|
| 6 |
+
def reset_state():
|
| 7 |
+
st.session_state.book_outline = None
|
| 8 |
+
st.session_state.book = None
|
| 9 |
+
st.session_state.title = None
|
| 10 |
+
st.session_state.book_flow = None
|
| 11 |
+
st.session_state.section_finished = 0
|
| 12 |
+
st.session_state.total_sections = 0
|
| 13 |
+
|
| 14 |
+
def start_generation(topic):
|
| 15 |
+
book_flow = BookFlow()
|
| 16 |
+
st.session_state.book_flow = book_flow
|
| 17 |
+
def run_kickoff():
|
| 18 |
+
book_flow.kickoff(inputs={"topic": topic})
|
| 19 |
+
kickoff_thread = threading.Thread(target=run_kickoff)
|
| 20 |
+
kickoff_thread.start()
|
| 21 |
+
|
| 22 |
+
def update_state():
|
| 23 |
+
book_flow = st.session_state.get("book_flow", None)
|
| 24 |
+
if book_flow is not None:
|
| 25 |
+
if hasattr(book_flow.state, "book_outline") and book_flow.state.book_outline and st.session_state.book_outline is None:
|
| 26 |
+
st.session_state.book_outline = book_flow.state.book_outline_md
|
| 27 |
+
st.session_state.title = book_flow.state.title
|
| 28 |
+
st.session_state.total_sections = len(book_flow.state.book_outline)
|
| 29 |
+
if hasattr(book_flow.state, "section_finished") and book_flow.state.section_finished != st.session_state.section_finished:
|
| 30 |
+
st.session_state.section_finished = book_flow.state.section_finished
|
| 31 |
+
if hasattr(book_flow.state, "book") and book_flow.state.book and st.session_state.book is None:
|
| 32 |
+
st.session_state.book = book_flow.state.book_md
|
| 33 |
+
st.session_state.generation_started = False
|
| 34 |
+
|
| 35 |
+
st.title("Educational Book Generator")
|
| 36 |
+
topic = st.text_input("Enter the topic for the book:", "Bubble Sort")
|
| 37 |
+
|
| 38 |
+
if "generation_started" not in st.session_state:
|
| 39 |
+
st.session_state.generation_started = False
|
| 40 |
+
if "book_outline" not in st.session_state:
|
| 41 |
+
st.session_state.book_outline = None
|
| 42 |
+
if "book" not in st.session_state:
|
| 43 |
+
st.session_state.book = None
|
| 44 |
+
if "title" not in st.session_state:
|
| 45 |
+
st.session_state.title = None
|
| 46 |
+
if "book_flow" not in st.session_state:
|
| 47 |
+
st.session_state.book_flow = None
|
| 48 |
+
if "section_finished" not in st.session_state:
|
| 49 |
+
st.session_state.section_finished = 0
|
| 50 |
+
|
| 51 |
+
# Button to start generation
|
| 52 |
+
if st.button("Generate Book"):
|
| 53 |
+
st.session_state.generation_started = True
|
| 54 |
+
reset_state()
|
| 55 |
+
start_generation(topic)
|
| 56 |
+
|
| 57 |
+
# Show "Generating book..." message if the process has started
|
| 58 |
+
if st.session_state.generation_started:
|
| 59 |
+
if st.session_state.book_outline is None:
|
| 60 |
+
st.write("Generating book outline... Please wait.")
|
| 61 |
+
elif st.session_state.book is None:
|
| 62 |
+
st.write("Book Outline is ready")
|
| 63 |
+
outline_filename = f"{st.session_state.title.replace(' ', '_')}_outline.md"
|
| 64 |
+
st.download_button(
|
| 65 |
+
label="Download Book Outline",
|
| 66 |
+
data=st.session_state.book_outline,
|
| 67 |
+
file_name=outline_filename,
|
| 68 |
+
mime="text/markdown"
|
| 69 |
+
)
|
| 70 |
+
st.progress(st.session_state.section_finished / st.session_state.total_sections, text=f"Generating book content... {st.session_state.section_finished}/{st.session_state.total_sections}")
|
| 71 |
+
elif st.session_state.book_outline is not None and st.session_state.book is not None:
|
| 72 |
+
st.write("Book Outline is ready")
|
| 73 |
+
outline_filename = f"{st.session_state.title.replace(' ', '_')}_outline.md"
|
| 74 |
+
st.download_button(
|
| 75 |
+
label="Download Book Outline",
|
| 76 |
+
data=st.session_state.book_outline,
|
| 77 |
+
file_name=outline_filename,
|
| 78 |
+
mime="text/markdown"
|
| 79 |
+
)
|
| 80 |
+
st.write("Book content is ready")
|
| 81 |
+
content_filename = f"{st.session_state.title.replace(' ', '_')}.md"
|
| 82 |
+
st.download_button(
|
| 83 |
+
label="Download Generated Book",
|
| 84 |
+
data=st.session_state.book,
|
| 85 |
+
file_name=content_filename,
|
| 86 |
+
mime="text/markdown"
|
| 87 |
+
)
|
| 88 |
+
if st.session_state.generation_started:
|
| 89 |
+
time.sleep(5)
|
| 90 |
+
update_state()
|
| 91 |
+
st.rerun()
|
pyproject.toml
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[project]
|
| 2 |
+
name = "educational_books"
|
| 3 |
+
version = "0.1.0"
|
| 4 |
+
description = "educational_books using crewAI"
|
| 5 |
+
authors = [{ name = "Your Name", email = "you@example.com" }]
|
| 6 |
+
requires-python = ">=3.10,<3.13"
|
| 7 |
+
dependencies = [
|
| 8 |
+
"crewai[tools]>=0.95.0,<1.0.0"
|
| 9 |
+
]
|
| 10 |
+
|
| 11 |
+
[project.scripts]
|
| 12 |
+
kickoff = "educational_books.main:kickoff"
|
| 13 |
+
plot = "educational_books.main:plot"
|
| 14 |
+
|
| 15 |
+
[build-system]
|
| 16 |
+
requires = ["hatchling"]
|
| 17 |
+
build-backend = "hatchling.build"
|
| 18 |
+
|
| 19 |
+
[tool.crewai]
|
| 20 |
+
type = "flow"
|
requirements.txt
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
crewai
|
| 2 |
+
crewai-tools
|
| 3 |
+
pydantic
|
| 4 |
+
langchain-groq
|
| 5 |
+
langchain-community
|
| 6 |
+
streamlit
|
src/educational_books/__init__.py
ADDED
|
File without changes
|
src/educational_books/crews/book_content/book_content.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from crewai import Agent, Crew, Process, Task, LLM
|
| 2 |
+
from crewai.project import CrewBase, agent, crew, task
|
| 3 |
+
from crewai_tools import SerperDevTool
|
| 4 |
+
from dotenv import load_dotenv
|
| 5 |
+
import streamlit as st
|
| 6 |
+
import os
|
| 7 |
+
|
| 8 |
+
from educational_books.types import Section
|
| 9 |
+
|
| 10 |
+
load_dotenv()
|
| 11 |
+
if not os.getenv("GEMINI_API_KEY"):
|
| 12 |
+
os.environ["GEMINI_API_KEY"] = st.secrets["google_api_key"]
|
| 13 |
+
|
| 14 |
+
if not os.getenv("SERPER_API_KEY"):
|
| 15 |
+
os.environ["SERPER_API_KEY"] = st.secrets["serper_api_key"]
|
| 16 |
+
|
| 17 |
+
@CrewBase
|
| 18 |
+
class BookContentCrew():
|
| 19 |
+
"""BookContent crew"""
|
| 20 |
+
agents_config = 'config/agents.yaml'
|
| 21 |
+
tasks_config = 'config/tasks.yaml'
|
| 22 |
+
llm = LLM(model="gemini/gemini-2.0-flash-exp")
|
| 23 |
+
|
| 24 |
+
@agent
|
| 25 |
+
def researcher(self) -> Agent:
|
| 26 |
+
return Agent(config=self.agents_config['researcher'], llm=self.llm, tools=[SerperDevTool()])
|
| 27 |
+
|
| 28 |
+
@agent
|
| 29 |
+
def writer(self) -> Agent:
|
| 30 |
+
return Agent(config=self.agents_config['writer'], llm=self.llm, verbose=True)
|
| 31 |
+
|
| 32 |
+
@task
|
| 33 |
+
def research_section(self) -> Task:
|
| 34 |
+
return Task(config=self.tasks_config['research_section'], agent=self.researcher())
|
| 35 |
+
|
| 36 |
+
@task
|
| 37 |
+
def write_section(self) -> Task:
|
| 38 |
+
return Task(config=self.tasks_config['write_section'], agent=self.writer(), output_pydantic=Section)
|
| 39 |
+
|
| 40 |
+
@crew
|
| 41 |
+
def crew(self) -> Crew:
|
| 42 |
+
"""Creates the Bookoutline crew"""
|
| 43 |
+
return Crew(
|
| 44 |
+
agents=self.agents,
|
| 45 |
+
tasks=self.tasks,
|
| 46 |
+
process=Process.sequential,
|
| 47 |
+
max_rpm=10,
|
| 48 |
+
)
|
src/educational_books/crews/book_content/config/agents.yaml
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
researcher:
|
| 2 |
+
role: >
|
| 3 |
+
Research Agent
|
| 4 |
+
goal: >
|
| 5 |
+
Gather comprehensive information about {topic} and {section_title} that will be used to create a well-informed and engaging section for the book.
|
| 6 |
+
Here is the outline description for the section:\n\n {section_description}
|
| 7 |
+
Here is the skill or key points that will be covered in this section:\n\n {covered_skills}
|
| 8 |
+
backstory: >
|
| 9 |
+
You are an experienced researcher skilled in finding the most relevant and up-to-date information on any given topic.
|
| 10 |
+
Your job is to provide educational content that is backed by solid research and reliable sources.
|
| 11 |
+
|
| 12 |
+
writer:
|
| 13 |
+
role: >
|
| 14 |
+
Section Writer
|
| 15 |
+
goal: >
|
| 16 |
+
Write a well-structured section for the book based on the provided section title and outline.
|
| 17 |
+
The section should be written in markdown format and contain around 2,000 words.
|
| 18 |
+
If there are code snippets or examples, they need to be included in the section
|
| 19 |
+
backstory: >
|
| 20 |
+
You are an exceptional writer, known for producing engaging, well-researched, and informative content.
|
| 21 |
+
You excel at transforming complex ideas into readable and well-organized sections.
|
src/educational_books/crews/book_content/config/tasks.yaml
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
research_section:
|
| 2 |
+
description: >
|
| 3 |
+
Research the provided section topic, title, and outline to gather additional content that will be helpful in writing the section.
|
| 4 |
+
Ensure you focus on reliable, high-quality sources of information.
|
| 5 |
+
|
| 6 |
+
Here is the outline description for the section:\n\n {section_description}
|
| 7 |
+
Here is the skill or key points that will be covered in this section:\n\n {covered_skills}
|
| 8 |
+
|
| 9 |
+
When researching, consider the following key points:
|
| 10 |
+
- you need to gather enough information to write a 2,000-word section
|
| 11 |
+
- The section you are researching needs to fit in well with the rest of the sections in the book.
|
| 12 |
+
|
| 13 |
+
Here is the outline of the entire book:\n\n
|
| 14 |
+
{book_outline}
|
| 15 |
+
expected_output: >
|
| 16 |
+
A set of reliable and high-quality content that will be used to write the section. This content should be well-organized and relevant to the section topic.
|
| 17 |
+
|
| 18 |
+
write_section:
|
| 19 |
+
description: >
|
| 20 |
+
Write a well-structured section based on the section title, goal, and outline description.
|
| 21 |
+
Each section should be written in markdown and should contain around 2,000 words.
|
| 22 |
+
|
| 23 |
+
Here is the topic for the book: {topic}
|
| 24 |
+
Here is the title of the section: {section_title}
|
| 25 |
+
Here is the outline description for the section:\n\n {section_description}
|
| 26 |
+
Here is the skill or key points that will be covered in this section:\n\n {covered_skills}
|
| 27 |
+
|
| 28 |
+
Important notes:
|
| 29 |
+
- The section you are writing needs to fit in well with the rest of the sections in the book.
|
| 30 |
+
|
| 31 |
+
Here is the outline of the entire book:\n\n
|
| 32 |
+
{book_outline}
|
| 33 |
+
expected_output: >
|
| 34 |
+
A markdown-formatted section of around 2,000 words that covers the provided section title and outline description.
|
src/educational_books/crews/book_outline/book_outline.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from crewai import Agent, Crew, Process, Task, LLM
|
| 2 |
+
from crewai.project import CrewBase, agent, crew, task
|
| 3 |
+
from crewai_tools import SerperDevTool
|
| 4 |
+
from dotenv import load_dotenv
|
| 5 |
+
import streamlit as st
|
| 6 |
+
import os
|
| 7 |
+
|
| 8 |
+
from educational_books.types import BookOutline
|
| 9 |
+
|
| 10 |
+
load_dotenv()
|
| 11 |
+
if not os.getenv("GEMINI_API_KEY"):
|
| 12 |
+
os.environ["GEMINI_API_KEY"] = st.secrets["google_api_key"]
|
| 13 |
+
|
| 14 |
+
if not os.getenv("SERPER_API_KEY"):
|
| 15 |
+
os.environ["SERPER_API_KEY"] = st.secrets["serper_api_key"]
|
| 16 |
+
|
| 17 |
+
@CrewBase
|
| 18 |
+
class BookOutlineCrew():
|
| 19 |
+
"""Bookoutline crew"""
|
| 20 |
+
agents_config = 'config/agents.yaml'
|
| 21 |
+
tasks_config = 'config/tasks.yaml'
|
| 22 |
+
llm = LLM(model="gemini/gemini-2.0-flash-exp")
|
| 23 |
+
|
| 24 |
+
@agent
|
| 25 |
+
def researcher(self) -> Agent:
|
| 26 |
+
return Agent(config=self.agents_config['researcher'], llm=self.llm, tools=[SerperDevTool()])
|
| 27 |
+
|
| 28 |
+
@agent
|
| 29 |
+
def planner(self) -> Agent:
|
| 30 |
+
return Agent(config=self.agents_config['planner'], llm=self.llm, verbose=True)
|
| 31 |
+
|
| 32 |
+
@task
|
| 33 |
+
def research_topic(self) -> Task:
|
| 34 |
+
return Task(config=self.tasks_config['research_topic'], agent=self.researcher())
|
| 35 |
+
|
| 36 |
+
@task
|
| 37 |
+
def generate_outline(self) -> Task:
|
| 38 |
+
return Task(config=self.tasks_config['generate_outline'], agent=self.planner(), output_pydantic=BookOutline)
|
| 39 |
+
|
| 40 |
+
@crew
|
| 41 |
+
def crew(self) -> Crew:
|
| 42 |
+
"""Creates the Bookoutline crew"""
|
| 43 |
+
return Crew(
|
| 44 |
+
agents=self.agents,
|
| 45 |
+
tasks=self.tasks,
|
| 46 |
+
process=Process.sequential,
|
| 47 |
+
max_rpm=10,
|
| 48 |
+
)
|
src/educational_books/crews/book_outline/config/agents.yaml
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
researcher:
|
| 2 |
+
role: >
|
| 3 |
+
Research Agent
|
| 4 |
+
goal: >
|
| 5 |
+
Gather comprehensive information about {topic} that will be used to create an organized and well-structured educational book outline.
|
| 6 |
+
that can be easily understood by university students.
|
| 7 |
+
backstory: >
|
| 8 |
+
You're a seasoned researcher, known for gathering the best sources and understanding the key elements of any topic.
|
| 9 |
+
You aim to collect all relevant information so the book outline can be developed with the most accurate and up-to-date content.
|
| 10 |
+
|
| 11 |
+
planner:
|
| 12 |
+
role: >
|
| 13 |
+
Outlining Planner
|
| 14 |
+
goal: >
|
| 15 |
+
Develop engaging and effective outline based on {topic}. This includes breaking down {topic} into smaller,
|
| 16 |
+
more manageable sections that are easy for students to follow and understand. The outline should be structured
|
| 17 |
+
in a way that allows students to progress through the book with ease from beginner to advanced levels.
|
| 18 |
+
backstory: >
|
| 19 |
+
You're a creative and detail-oriented planner with a passion for designing engaging and effective educational book outlines.
|
| 20 |
+
You have a deep understanding of the key concepts and skills related to {topic}, and you're able to break down
|
| 21 |
+
complex topics into smaller, more manageable sections that are easy for students to follow and understand
|
src/educational_books/crews/book_outline/config/tasks.yaml
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
research_topic:
|
| 2 |
+
description: >
|
| 3 |
+
Research the provided topic of {topic} to gather the most important information that will
|
| 4 |
+
be useful in creating a educational book outline. Ensure you focus on high-quality, reliable sources.
|
| 5 |
+
The information should focus on educational content that is relevant.
|
| 6 |
+
expected_output: >
|
| 7 |
+
A set of key points and important information about {topic} that will be used to create the outline.
|
| 8 |
+
|
| 9 |
+
generate_outline:
|
| 10 |
+
description: >
|
| 11 |
+
Create an educational book outline with sections in sequential order so that students can easily follow and understand the content in order.
|
| 12 |
+
Ensure that each sections has a title and a brief description that highlights what will be covered in that section.
|
| 13 |
+
Each sections also contain a list of skills or key concepts that will be covered.
|
| 14 |
+
It's important to note that the order of the sections should make sense and flow logically for students to learn effectively.
|
| 15 |
+
Also, make sure that you do not duplicate any sections or topics in the outline.
|
| 16 |
+
expected_output: >
|
| 17 |
+
A book title, list of outline sections with a title, brief description, list of skills or key concepts, and list of objectives that will be covered in that section.
|
| 18 |
+
Example:
|
| 19 |
+
Book Title: ...
|
| 20 |
+
Sections: [
|
| 21 |
+
{
|
| 22 |
+
title: ...,
|
| 23 |
+
description: ...,
|
| 24 |
+
covered_skills: [...],
|
| 25 |
+
objectives: [...]
|
| 26 |
+
},
|
| 27 |
+
...
|
| 28 |
+
]
|
| 29 |
+
Minimum of sections should be 3 and maximum should be 15.
|
src/educational_books/main.py
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python
|
| 2 |
+
from pydantic import BaseModel
|
| 3 |
+
from typing import List
|
| 4 |
+
from crewai.flow.flow import Flow, listen, start
|
| 5 |
+
from educational_books.crews.book_outline.book_outline import BookOutlineCrew
|
| 6 |
+
from educational_books.crews.book_content.book_content import BookContentCrew
|
| 7 |
+
from educational_books.types import Section, SectionOutline
|
| 8 |
+
import asyncio
|
| 9 |
+
|
| 10 |
+
class BookState(BaseModel):
|
| 11 |
+
title: str = "Mastering Data Structures and Algorithms"
|
| 12 |
+
book: List[Section] = []
|
| 13 |
+
book_outline: List[SectionOutline] = []
|
| 14 |
+
topic: str = "Sorting Algorithms"
|
| 15 |
+
book_outline_md: str = ""
|
| 16 |
+
book_md: str = ""
|
| 17 |
+
section_finished: int = 0
|
| 18 |
+
|
| 19 |
+
class BookFlow(Flow[BookState]):
|
| 20 |
+
initial_state = BookState
|
| 21 |
+
|
| 22 |
+
def write_to_file(self, filename: str, content: str, mode: str = "a"):
|
| 23 |
+
"""Helper method to write content to the file"""
|
| 24 |
+
with open(filename, mode) as f:
|
| 25 |
+
f.write(content)
|
| 26 |
+
|
| 27 |
+
@start()
|
| 28 |
+
def initialize(self, topic: str = None):
|
| 29 |
+
if topic:
|
| 30 |
+
self.state.topic = topic
|
| 31 |
+
|
| 32 |
+
@listen(initialize)
|
| 33 |
+
def generate_book_outline(self):
|
| 34 |
+
print("Generating book outline")
|
| 35 |
+
crew = BookOutlineCrew()
|
| 36 |
+
outline = crew.crew().kickoff(inputs={"topic": self.state.topic})
|
| 37 |
+
self.state.title = outline["title"]
|
| 38 |
+
for section in outline["sections"]:
|
| 39 |
+
self.state.book_outline_md += f"# {section.title}\n"
|
| 40 |
+
self.state.book_outline_md += f"{section.description}\n\n"
|
| 41 |
+
self.state.book_outline_md += f"## Covered Skills\n"
|
| 42 |
+
for skill in section.covered_skills:
|
| 43 |
+
self.state.book_outline_md += f"- {skill}\n"
|
| 44 |
+
self.state.book_outline_md += "\n"
|
| 45 |
+
self.state.book_outline_md += f"## Learning Objectives\n"
|
| 46 |
+
for objective in section.objectives:
|
| 47 |
+
self.state.book_outline_md += f"- {objective}\n"
|
| 48 |
+
self.state.book_outline_md += "\n"
|
| 49 |
+
self.state.book_outline = outline["sections"]
|
| 50 |
+
|
| 51 |
+
@listen(generate_book_outline)
|
| 52 |
+
async def generate_book(self):
|
| 53 |
+
print("Generating book")
|
| 54 |
+
crew = BookContentCrew()
|
| 55 |
+
tasks = []
|
| 56 |
+
book_outline = [(i, section.title, section.description, section.covered_skills) for i, section in enumerate(self.state.book_outline)]
|
| 57 |
+
|
| 58 |
+
async def generate_section(section):
|
| 59 |
+
content = crew.crew().kickoff(inputs={
|
| 60 |
+
"topic": self.state.topic,
|
| 61 |
+
"section_title": section.title,
|
| 62 |
+
"section_description": section.description,
|
| 63 |
+
"covered_skills": section.covered_skills,
|
| 64 |
+
"book_outline": book_outline
|
| 65 |
+
})
|
| 66 |
+
title = content["title"]
|
| 67 |
+
content = content["content"]
|
| 68 |
+
section = Section(title=title, content=content)
|
| 69 |
+
section_content = f"# {title}\n\n{content}\n\n"
|
| 70 |
+
self.state.book_md += section_content
|
| 71 |
+
self.state.section_finished += 1
|
| 72 |
+
return section
|
| 73 |
+
for section in self.state.book_outline:
|
| 74 |
+
print(f"Generating content for {section.title}")
|
| 75 |
+
task = asyncio.create_task(generate_section(section))
|
| 76 |
+
tasks.append(task)
|
| 77 |
+
|
| 78 |
+
sections = await asyncio.gather(*tasks)
|
| 79 |
+
self.state.book = sections
|
| 80 |
+
print(f"Book generated with {len(self.state.book)} sections")
|
| 81 |
+
|
| 82 |
+
def kickoff():
|
| 83 |
+
book_name = BookFlow()
|
| 84 |
+
book_name.kickoff()
|
| 85 |
+
|
| 86 |
+
def plot():
|
| 87 |
+
book_name = BookFlow()
|
| 88 |
+
book_name.plot()
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
if __name__ == "__main__":
|
| 92 |
+
kickoff()
|
src/educational_books/tools/__init__.py
ADDED
|
File without changes
|
src/educational_books/tools/custom_tool.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Type
|
| 2 |
+
|
| 3 |
+
from crewai.tools import BaseTool
|
| 4 |
+
from pydantic import BaseModel, Field
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class MyCustomToolInput(BaseModel):
|
| 8 |
+
"""Input schema for MyCustomTool."""
|
| 9 |
+
|
| 10 |
+
argument: str = Field(..., description="Description of the argument.")
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
class MyCustomTool(BaseTool):
|
| 14 |
+
name: str = "Name of my tool"
|
| 15 |
+
description: str = (
|
| 16 |
+
"Clear description for what this tool is useful for, your agent will need this information to use it."
|
| 17 |
+
)
|
| 18 |
+
args_schema: Type[BaseModel] = MyCustomToolInput
|
| 19 |
+
|
| 20 |
+
def _run(self, argument: str) -> str:
|
| 21 |
+
# Implementation goes here
|
| 22 |
+
return "this is an example of a tool output, ignore it and move along."
|
src/educational_books/types.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import List
|
| 2 |
+
from pydantic import BaseModel
|
| 3 |
+
class SectionOutline(BaseModel):
|
| 4 |
+
title: str
|
| 5 |
+
description: str
|
| 6 |
+
covered_skills: List[str]
|
| 7 |
+
objectives: List[str]
|
| 8 |
+
|
| 9 |
+
class BookOutline(BaseModel):
|
| 10 |
+
title: str
|
| 11 |
+
sections: List[SectionOutline]
|
| 12 |
+
|
| 13 |
+
class Section(BaseModel):
|
| 14 |
+
title: str
|
| 15 |
+
content: str
|
uv.lock
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|