Systems Library / Operations & Admin / How to Build an AI Job Description Generator
Operations & Admin hr people

How to Build an AI Job Description Generator

Generate inclusive, compelling job descriptions using AI in minutes.

Jay Banlasan

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

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

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

Related Systems