Creating and Registering CrewAI based Job descriptions creator
This documentation explains how to build CrewAI agents that generate tailored job descriptions, register them on Fetch.ai’s Agentverse, and enable collaboration between agents for dynamic data sharing. Below, you'll see the main.py script containing the workflow logic run_job_posting_workflow, as well as sample scripts for a Job Description Agent and a User Agent.
Overview
Purpose
This project showcases:
- Creating CrewAI agents for complex tasks (e.g., analyzing company requirements and generating job descriptions).
- Integrating with Fetch.ai’s Agentverse, enabling seamless communication and data exchange.
- Using autonomous agents to streamline job description creation while aligning with company culture and specific role requirements.
Prerequisites
Environment Setup
To begin creating and registering CrewAI agents, ensure you have the following:
- Python 3.10+ or higher
- A virtual environment (recommended)
- Fetch.ai SDK for agent creation and registration
- CrewAI Framework for defining tasks and workflows
Key Components
- Job Description Agent: A specialized CrewAI agent that processes input (company domain, hiring needs, etc.) to generate a job description.
- User Agent: Acts as a client to discover and interact with the Job Description Agent.
- Agent Collaboration: Multiple agents communicate via Agentverse, orchestrating tasks through CrewAI workflows.
Architecture Diagram

Environment Variables
Create a .env file with your API keys:
SERPER_API_KEY=<your_serper_api_key>
OPENAI_API_KEY=<your_openai_api_key>
AGENTVERSE_API_KEY=<your_agentverse_api_key>
Replace the placeholders with your actual keys.
Project Directory & Configuration Files
For a clean, maintainable setup, we recommend the following structure:
crewai_job_descriptions/
├── .env
├── config/
│ ├── agents.yaml
│ └── tasks.yaml
├── crew.py
├── jd_agent.py
├── main.py
├── requirements.txt
├── user_agent.py
├── job_description_example.md
└── README.md
1. requirements.txt
Purpose: Declares project dependencies so you (or anyone) can install them quickly with pip install -r requirements.txt.
Create a file called requirements.txt at the root of your project (alongside jd_agent.py and main.py). Example contents:
flask==2.2.5
flask-cors==3.0.10
fetchai==0.16.3
crewai==0.100.1
crewai_tools==0.33.0
python-dotenv==1.0.0
Install everything at once via:
pip install -r requirements.txt
2. Crew.py
Purpose: Defines your JobPostingCrew class, specifying the Agents and Tasks.
Place crew.py at the root directory. It contains the code:
from typing import List
from crewai import Agent, Crew, Process, Task
from crewai.project import CrewBase, agent, crew, task
# Check our tools documentations for more information on how to use them
from crewai_tools import SerperDevTool, ScrapeWebsiteTool, WebsiteSearchTool, FileReadTool
from pydantic import BaseModel, Field
web_search_tool = WebsiteSearchTool()
seper_dev_tool = SerperDevTool()
file_read_tool = FileReadTool(
file_path='./job_description_example.md',
description='A tool to read the job description example file.'
)
class ResearchRoleRequirements(BaseModel):
"""Research role requirements model"""
skills: List[str] = Field(..., description="List of recommended skills ...")
experience: List[str] = Field(..., description="List of recommended experience ...")
qualities: List[str] = Field(..., description="List of recommended qualities ...")
@CrewBase
class JobPostingCrew:
"""JobPosting crew"""
agents_config = 'config/agents.yaml'
tasks_config = 'config/tasks.yaml'
@agent
def research_agent(self) -> Agent:
return Agent(
config=self.agents_config['research_agent'],
tools=[web_search_tool, seper_dev_tool],
verbose=True
)
@agent
def writer_agent(self) -> Agent:
return Agent(
config=self.agents_config['writer_agent'],
tools=[web_search_tool, seper_dev_tool, file_read_tool],
verbose=True
)
@agent
def review_agent(self) -> Agent:
return Agent(
config=self.agents_config['review_agent'],
tools=[web_search_tool, seper_dev_tool, file_read_tool],
verbose=True
)
@task
def research_company_culture_task(self) -> Task:
return Task(
config=self.tasks_config['research_company_culture_task'],
agent=self.research_agent()
)
@task
def research_role_requirements_task(self) -> Task:
return Task(
config=self.tasks_config['research_role_requirements_task'],
agent=self.research_agent(),
output_json=ResearchRoleRequirements
)
@task
def draft_job_posting_task(self) -> Task:
return Task(
config=self.tasks_config['draft_job_posting_task'],
agent=self.writer_agent()
)
@task
def review_and_edit_job_posting_task(self) -> Task:
return Task(
config=self.tasks_config['review_and_edit_job_posting_task'],
agent=self.review_agent()
)
@task
def industry_analysis_task(self) -> Task:
return Task(
config=self.tasks_config['industry_analysis_task'],
agent=self.research_agent()
)
@crew
def crew(self) -> Crew:
"""Creates the JobPostingCrew"""
return Crew(
agents=self.agents, # Automatically created by the @agent decorator
tasks=self.tasks, # Automatically created by the @task decorator
process=Process.sequential,
verbose=True,
)
3. config/agents.yaml
Purpose: Specifies each agent’s role, goal, and backstory.
Create a folder named config/ at your project root. Inside it, create agents.yaml:
research_agent:
role: >
Research Analyst
goal: >
Analyze the company website and provided description...
backstory: >
Expert in analyzing company cultures and identifying key values...
writer_agent:
role: >
Job Description Writer
goal: >
Use insights from the Research Analyst to create...
backstory: >
Skilled in crafting compelling job descriptions...
review_agent:
role: >
Review and Editing Specialist
goal: >
Review the job posting for clarity, engagement...
backstory: >
A meticulous editor ensuring every piece of content is polished...
4. config/tasks.yaml
Purpose: Defines the text prompts (description) and expected output for each task.
In the same config/ folder, make a second file called tasks.yaml:
research_company_culture_task:
description: >
Analyze {company_domain} and {company_description}...
expected_output: >
A comprehensive report detailing the company's culture...
research_role_requirements_task:
description: >
Based on {hiring_needs}, list recommended skills, experience...
expected_output: >
A list of recommended skills, experiences, and qualities...
draft_job_posting_task:
description: >
Draft a job posting for {hiring_needs}, using {specific_benefits}...
expected_output: >
A detailed, engaging job posting that includes an introduction...
review_and_edit_job_posting_task:
description: >
Review the job posting for {hiring_needs} for clarity...
expected_output: >
A polished, error-free job posting with final approval in markdown.
industry_analysis_task:
description: >
Conduct an in-depth analysis of {company_domain}'s industry...
expected_output: >
A detailed analysis highlighting major industry trends, opportunities...
Main.py (Workflow Definition)
Below is an example main.py script containing the run_job_posting_workflow function that the Job Description Agent calls to generate job descriptions. It uses Crew to structure tasks and (optionally) a local SQLite database for storage or logging.
import sys
from crew import JobPostingCrew
import sqlite3
async def run_job_posting_workflow(company_domain, company_description, hiring_needs, specific_benefits):
"""
Executes the job posting workflow with proper connection handling and debugging output.
Args:
company_domain (str): The company's domain.
company_description (str): A description of the company.
hiring_needs (str): Description of the role being hired for.
specific_benefits (str): Specific benefits offered for the role.
Returns:
str: The raw output of the 'industry_analysis_task' if found, else None.
"""
try:
# Initialize the JobPostingCrew
job_posting_crew = JobPostingCrew().crew()
# Define the inputs
inputs = {
'company_domain': company_domain,
'company_description': company_description,
'hiring_needs': hiring_needs,
'specific_benefits': specific_benefits,
}
# Execute the job posting process
result = job_posting_crew.kickoff(inputs=inputs)
# Debugging output
print("Result type:", type(result))
print("Attributes:", dir(result))
# Attempt to retrieve and print tasks_output
if hasattr(result, 'tasks_output') and result.tasks_output:
print("Tasks Output:")
for task_output in result.tasks_output:
print(f"Task Name: {task_output.name}")
if task_output.name == "industry_analysis_task":
return task_output.raw
print("Task 'review_and_edit_job_posting_task' not found in tasks_output.")
else:
print("No 'tasks_output' attribute found or it's empty.")
except Exception as e:
print(f"Error occurred: {e}")
return None
The run_job_posting_workflow function is invoked by the Job Description Agent’s webhook to handle incoming requests and generate the job description.
Job Description Agent Script
A Job Description Agent is a specialized CrewAI agent that creates detailed job descriptions based on:
- Company Domain
- Company Description
- Hiring Needs
- Specific Benefits
It registers itself with Agentverse, processes incoming requests (via a Flask webhook), and sends the generated output back to the requester.