How to Build an AI Job Description Generator
Generate inclusive, compelling job descriptions using AI in minutes.
Jay Banlasan
The AI Systems Guy
Writing job descriptions takes longer than it should, and most of them sound the same. I use an ai job description generator to produce role-specific, clear postings in minutes instead of hours. Feed it the role requirements, and it writes copy that attracts the right candidates.
The trick is not just generating text. It is constraining the AI to produce descriptions that are specific, inclusive, and honest about what the role actually involves.
What You Need Before Starting
- Python 3.8+
- An AI API key (Claude or GPT)
- A role intake template (defined below)
- Your company tone guide (optional but improves output)
Step 1: Create the Role Intake Template
ROLE_INTAKE = {
"title": "Senior Backend Engineer",
"department": "Engineering",
"reports_to": "Engineering Manager",
"location": "Remote (US)",
"salary_range": "$140,000 - $180,000",
"must_have_skills": ["Python", "PostgreSQL", "REST APIs", "Docker"],
"nice_to_have_skills": ["Kubernetes", "Terraform", "GraphQL"],
"responsibilities": [
"Design and build backend services",
"Own database schema design and optimization",
"Participate in code reviews and architecture decisions"
],
"team_size": 8,
"company_description": "We build AI automation tools for small businesses.",
"benefits": ["Health insurance", "401k match", "Unlimited PTO", "Home office stipend"]
}
Step 2: Build the Generator
import anthropic
client = anthropic.Anthropic()
def generate_job_description(intake):
prompt = f"""Write a job description for this role. Be specific and direct.
No buzzwords. No "fast-paced environment" or "rockstar" language.
Include salary range. Use short paragraphs.
Role: {intake['title']}
Department: {intake['department']}
Reports To: {intake['reports_to']}
Location: {intake['location']}
Salary: {intake['salary_range']}
Must-Have Skills: {', '.join(intake['must_have_skills'])}
Nice-To-Have: {', '.join(intake['nice_to_have_skills'])}
Responsibilities: {chr(10).join('- ' + r for r in intake['responsibilities'])}
Team Size: {intake['team_size']}
Company: {intake['company_description']}
Benefits: {', '.join(intake['benefits'])}
Format: Title, About Us (2-3 sentences), What You Will Do (bullets),
What You Bring (required vs preferred split), Benefits, How to Apply."""
message = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=2048,
messages=[{"role": "user", "content": prompt}]
)
return message.content[0].text
Step 3: Add an Inclusivity Check
Run a second pass to flag biased or exclusionary language:
def check_inclusivity(job_description):
prompt = f"""Review this job description for biased or exclusionary language.
Flag any of these:
- Gendered terms (ninja, rockstar, he/him defaults)
- Unnecessary degree requirements
- Age-coded language (digital native, young team)
- Unreasonable experience requirements for the level
Job Description:
{job_description}
Return a JSON list of issues found with suggestions."""
message = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
messages=[{"role": "user", "content": prompt}]
)
return message.content[0].text
Step 4: Generate Multiple Variants
def generate_variants(intake, platforms):
variants = {}
for platform in platforms:
prompt_addon = {
"linkedin": "Optimize for LinkedIn. Professional but personable. Under 300 words.",
"indeed": "Standard job board format. Clear sections. Include salary prominently.",
"careers_page": "Match our brand voice. Longer format with team culture details."
}
message = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=2048,
messages=[{"role": "user", "content": f"Write a job posting. {prompt_addon.get(platform, '')} Role details: {intake}"}]
)
variants[platform] = message.content[0].text
return variants
Step 5: Save and Version
from pathlib import Path
from datetime import datetime
def save_job_description(title, content, version=1):
slug = title.lower().replace(" ", "-")
date = datetime.now().strftime("%Y-%m-%d")
filename = f"{date}-{slug}-v{version}.md"
Path("job-descriptions").mkdir(exist_ok=True)
Path(f"job-descriptions/{filename}").write_text(content)
return filename
What to Build Next
Connect this to your ATS so approved descriptions post automatically. Add a feedback loop where you track which descriptions get the most qualified applicants and feed that data back into your prompts.
Related Reading
- Building Your First Automation: A Complete Guide - how to structure any automation from scratch
- The Automation Decision Tree - deciding which processes to automate first
- AI-Powered Reporting That Actually Gets Read - making AI outputs useful for real people
Want this system built for your business?
Get a free assessment. We will map every system your business needs and show you the ROI.
Get Your Free Assessment