An intelligent resume tailoring system powered by CrewAI that analyzes job descriptions and automatically customizes your resume to match specific job requirements while maintaining authenticity and readability.
Job applications require tailored resumes that highlight relevant skills and experiences for each position. Manually customizing resumes for every application is time-consuming and inconsistent. Gary automates this process using AI agents that:
- Analyze job descriptions to extract key requirements, skills, and company culture indicators
- Tailor your master resume to emphasize relevant experience and integrate important keywords naturally
- Validate the tailored resume for ATS compatibility and human readability
- Generate professional Word documents ready for submission
- Track all applications in Google Sheets for easy management
-
Multi-Agent AI System: Three specialized agents working sequentially
- Job Analyst: Extracts skills, responsibilities, qualifications, tone, and cultural values from job descriptions
- Resume Tailor: Customizes resume content to align with job requirements while maintaining authenticity
- Resume Validator: Scores keyword integration, ATS compatibility, and readability before final generation
-
Intelligent Resume Customization: Naturally integrates job-specific keywords and phrases without sounding robotic
-
ATS Optimization: Ensures resumes pass Applicant Tracking Systems while remaining human-readable
-
Automated Document Generation: Creates formatted Word documents using customizable templates
-
Application Tracking: Logs all job applications to Google Sheets with job details and application dates
-
Comprehensive Validation: Provides detailed feedback on resume quality with actionable suggestions
Here's what a generated resume looks like:
Professional, ATS-optimized resume document generated by Gary
gary/
├── assets/
│ └── example_resume.jpg # Example resume output image
├── data/
│ └── resume.json # Your master resume data
├── templates/
│ └── resume_word_template.docx # Word document template
├── resumes/ # Generated resume documents (output)
├── src/
│ └── gary/
│ ├── config/
│ │ ├── agents.yaml # Agent configurations
│ │ └── tasks.yaml # Task definitions
│ ├── tools/
│ │ └── resume_word_doc_tool.py
│ ├── utils/
│ │ ├── clean_job_description.py
│ │ ├── google_sheets.py
│ │ ├── read_json.py
│ │ ├── result_parser.py
│ │ └── resume_word_doc_generator.py
│ ├── config.py # Path configurations
│ ├── crew.py # CrewAI agent & task definitions
│ ├── exceptions.py # Custom exceptions
│ ├── main.py # Entry point
│ └── models.py # Pydantic data models
├── .env # Environment variables
├── googleSheetsCredentials.json # Google service account credentials
├── pyproject.toml # Project dependencies
└── README.md
- Python >= 3.10, < 3.14
- uv package manager (recommended) or pip
Install uv if you haven't already:
pip install uvClone the repository and install dependencies:
cd gary
crewai installOr using uv directly:
uv syncCreate a .env file in the project root with the following structure:
# OpenRouter API Configuration (or use OpenAI directly)
OPENROUTER_API_KEY=your_openrouter_api_key_here
OPENROUTER_BASE_URL=https://openrouter.ai/api/v1
# Google Sheets Configuration
GOOGLE_SHEETS_ID=your_google_sheets_id_hereHow to get these values:
- OPENROUTER_API_KEY: Sign up at OpenRouter and generate an API key
- Or use OpenAI directly by modifying
crew.pyto use OpenAI's API
- Or use OpenAI directly by modifying
- GOOGLE_SHEETS_ID: Found in your Google Sheets URL:
https://docs.google.com/spreadsheets/d/{SHEET_ID}/edit
- Go to Google Cloud Console
- Create a new project or select an existing one
- Enable the Google Sheets API:
- Navigate to "APIs & Services" > "Library"
- Search for "Google Sheets API"
- Click "Enable"
- Create a service account:
- Go to "APIs & Services" > "Credentials"
- Click "Create Credentials" > "Service Account"
- Fill in service account details and click "Create"
- Skip optional permissions and click "Done"
- Generate credentials JSON:
- Click on the created service account
- Go to the "Keys" tab
- Click "Add Key" > "Create New Key"
- Select "JSON" and click "Create"
- Download the JSON file
- Rename the downloaded JSON file to
googleSheetsCredentials.json - Place it in the project root directory
- The file should have this structure:
{
"type": "service_account",
"project_id": "your-project-id",
"private_key_id": "key-id",
"private_key": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n",
"client_email": "[email protected]",
"client_id": "client-id",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "cert-url"
}- Create a new Google Sheet or use an existing one
- Share the sheet with the service account email (found in
client_emailin the JSON file)- Give it "Editor" permissions
- Copy the Sheet ID from the URL and add it to your
.envfile
After generating each resume, the system automatically logs the job application to Google Sheets. The following fields from the JobDetails model are written:
| Field | Source | Description | Example |
|---|---|---|---|
| Date Applied | job_details.date_applied |
Auto-generated current date in MM-DD-YYYY format | 10-02-2025 |
| Company Name | job_details.company_name |
Company hiring for the position | TechCorp |
| Job Title | job_details.job_title |
Position title | Senior Software Engineer |
| Location | job_details.location |
Job location | San Francisco, CA |
| Job ID | job_details.job_id |
Optional job identifier (empty if not provided) | ENG-2024-123 |
| Job Description | job_details.job_description |
Full cleaned job description text | We are seeking a Senior... |
| Status | Hardcoded | Application status | Done |
Example Google Sheet:
| Date Applied | Company Name | Job Title | Location | Job ID | Job Description | Status |
|---|---|---|---|---|---|---|
| 10-02-2025 | TechCorp | Senior Software Engineer | San Francisco, CA | ENG-2024-123 | We are seeking a Senior Software Engineer with expertise in Python and cloud technologies... | Done |
| 09-28-2025 | StartupXYZ | Full Stack Developer | Remote | Looking for a Full Stack Developer proficient in React and Node.js... | Done | |
| 09-25-2025 | BigCorp | DevOps Engineer | New York, NY | DEV-456 | Join our DevOps team to build scalable infrastructure... | Done |
The sheet will be populated automatically - you don't need to create the columns manually
Create your master resume in data/resume.json following this structure:
{
"header": {
"name": "Your Full Name",
"phone": "+1 (XXX) XXX-XXXX",
"email": "[email protected]",
"location": "City, State, Country",
"links": [
{
"platform": "GitHub",
"url": "https://github.com/yourusername"
},
{
"platform": "LinkedIn",
"url": "https://linkedin.com/in/yourusername"
},
{
"platform": "Portfolio",
"url": "https://yourportfolio.com"
}
]
},
"professional_summary": {
"summary": "Your professional summary highlighting your experience, skills, and expertise. This will be customized per job application."
},
"work_experience": [
{
"title": "Job Title",
"company": "Company Name",
"startDate": "Month Year",
"endDate": "Present",
"responsibilities": [
"Detailed accomplishment or responsibility with metrics",
"Another achievement with quantifiable results",
"Technical contribution with specific technologies and impact"
]
}
],
"skills": [
{
"category": "Programming Languages",
"items": ["Python", "JavaScript", "Go"]
},
{
"category": "Frameworks & Libraries",
"items": ["React", "FastAPI", "Node.js"]
},
{
"category": "Databases",
"items": ["PostgreSQL", "MongoDB", "Redis"]
},
{
"category": "Cloud Technologies",
"items": ["AWS", "GCP"]
},
{
"category": "Developer Tools",
"items": ["Docker", "Kubernetes", "Git"]
}
],
"education": [
{
"degree": "Degree Name, Major",
"institution": "University Name",
"startDate": "Month Year",
"endDate": "Month Year",
"coursework": ["Relevant Course 1", "Relevant Course 2"]
}
],
"projects": [
{
"name": "Project Name",
"description": "Detailed project description highlighting technologies used, problem solved, and impact/results."
}
]
}Important Notes:
- Include as much detail as possible in your master resume
- Use specific metrics and achievements
- The AI will select and emphasize the most relevant parts for each job
- The
locationin the header will be automatically updated to match the job location
The Word document template is located at templates/resume_word_template.docx. This template uses Jinja2 syntax for variable substitution.
Here's the Jinja2 template used to generate the resume. You can customize this template according to your needs:
{{ header.name }}
{{ header.location }} | {{ header.phone }} | {{ header.email }}
{% for link in header.links %}{{ link.url | replace("http://", "") }}{% if not loop.last %} | {% endif %}{% endfor %}
PROFESSIONAL SUMMARY
{{ resume_content.professional_summary.summary }}
WORK EXPERIENCE
{% for exp in resume_content.work_experience %}
{{ exp.title}}, {{ exp.company }} {{ exp.startDate }} — {{ exp.endDate }}
{%- for resp in exp.responsibilities %}
{{ resp }}{% endfor %}
{% endfor %}
EDUCATION
{% for edu in resume_content.education %}
{{ edu.degree }}, {{ edu.institution }} {{ edu.startDate }} — {{ edu.endDate }}
{% if edu.coursework %}
Coursework: {%- for course in edu.coursework %}{{ course }}{% if not loop.last %}, {% endif %}{% endfor %}
{% endif %}
{%- endfor -%}
SKILLS
{% for skill in resume_content.skills %}
{{ skill.category }}: {% for item in skill['items'] %}{{ item }}{% if not loop.last %}, {% endif %}{% endfor %}
{%- endfor %}
PROJECTS
{% for proj in resume_content.projects %}
{{ proj.name }}: {{ proj.description }}
{%- endfor %}- Open
templates/resume_word_template.docxin Microsoft Word - Modify the template:
- Change fonts, colors, and formatting
- Adjust spacing and layout
- Rearrange sections (e.g., put Skills before Work Experience)
- Add or remove sections
- Preserve the Jinja2 placeholders (text within
{{ }}and{% %}) - Save the file as
.docxformat
Customization Tips:
- Keep Jinja2 syntax intact for dynamic content generation
- Use Word's styles for consistent formatting
- Test with sample data to ensure proper rendering
- Adjust margins and spacing for optimal page usage
-
Input Collection: The system prompts you for job details via CLI:
- Company name
- Job title
- Location
- Job ID (optional)
- Job description (paste multi-line, type 'END' to finish)
-
Job Analysis: The Job Analyst agent:
- Extracts technical, soft, and management skills
- Identifies key responsibilities and qualifications
- Analyzes company tone and cultural values
- Returns structured job analysis
-
Resume Tailoring: The Resume Tailor agent:
- Customizes professional summary for the specific role
- Emphasizes relevant work experiences
- Integrates keywords naturally into responsibilities
- Selects most applicable skills and projects
- Returns tailored resume content
-
Validation: The Resume Validator agent:
- Analyzes keyword integration rate
- Scores ATS compatibility (0-100)
- Scores human readability (0-100)
- Identifies strengths, weaknesses, and provides suggestions
- Returns validation report with overall score
-
Document Generation:
- Combines tailored content with your header info
- Updates location to match job location
- Renders Word document from template
- Saves to
resumes/directory with naming:{name}_{company}_{job_title}_{job_id}.docx
-
Application Tracking:
- Logs job details to Google Sheets
- Records date applied, company, title, location, and status
The system uses three specialized CrewAI agents:
| Agent | Model | Role | Temperature |
|---|---|---|---|
| Job Analyst | Gemini 2.5 Flash | Extract structured insights from job descriptions | 0.2 (precise) |
| Resume Tailor | Claude Sonnet 4 | Customize resume content naturally and strategically | 0.4 (balanced) |
| Resume Validator | Gemini 2.5 Flash | Validate quality, ATS compatibility, readability | 0.2 (precise) |
From the project root directory:
crewai runOr using the installed script:
garyOr using Python directly:
python -m gary.main$ crewai run
================================================================================
JOB DETAILS INPUT
================================================================================
Company Name: TechCorp
Job Title: Senior Software Engineer
Location: San Francisco, CA
Job ID (optional, press Enter to skip): ENG-2024-123
Job Description (paste below, then type 'END' on a new line and press Enter):
We are seeking a Senior Software Engineer with expertise in Python and cloud technologies...
[paste full job description]
END
✓ Job details collected for TechCorp - Senior Software Engineer (Applied: 10-02-2025)
[Agent execution logs...]
================================================================================
RESUME VALIDATION REPORT
================================================================================
Passed Validation: True
Overall Score: 92/100
Ready for Generation: True
Keyword Integration Rate: 87.5%
Keywords Integrated: 35/40
ATS Score: 94/100
Human Readability Score: 90/100
Strengths:
✓ Natural integration of technical keywords
✓ Strong quantifiable achievements
✓ Well-aligned with job requirements
Weaknesses:
✗ Missing mention of specific AWS services mentioned in job description
Suggestions:
→ Consider adding specific AWS service experience if applicable
================================================================================
GENERATING WORD DOCUMENT
================================================================================
✓ Resume generated successfully: C:\projects\gary\resumes\john_doe_TechCorp_Senior_Software_Engineer_ENG-2024-123.docx
✓ Job details logged to Google Sheets
- Word Document: Professional resume saved in
resumes/directory - Google Sheets: Job application logged automatically
- Validation Report: Detailed feedback displayed in terminal
- Logs: Execution logs saved in
logs.txt
Edit agent configurations in src/gary/config/agents.yaml:
- Adjust agent roles, goals, and backstories
- Modify agent instructions and constraints
Edit task configurations in src/gary/config/tasks.yaml:
- Customize task descriptions and expected outputs
- Adjust validation criteria and scoring
Edit src/gary/crew.py to change models:
llm=llm_config("openrouter/google/gemini-2.5-flash", 0.2)Available models (via OpenRouter):
openrouter/anthropic/claude-sonnet-4openrouter/google/gemini-2.5-flashopenrouter/openai/gpt-4- Many more at OpenRouter Models
- Verify
googleSheetsCredentials.jsonis in the project root - Ensure the service account email has Editor access to the sheet
- Check that
GOOGLE_SHEETS_IDin.envmatches your sheet URL
- Verify
templates/resume_word_template.docxexists - Ensure Jinja2 placeholders in template match model fields
- Test template with sample data
- OpenRouter has rate limits; consider upgrading plan for heavy usage
- Adjust
max_iterin agent configurations to reduce API calls
- Check that
data/resume.jsonfollows the exact schema - Validate JSON syntax using a JSON validator
- Ensure all required fields are present
This project is open source and available for personal and commercial use.
Contributions are welcome! Please feel free to submit pull requests or open issues for bugs and feature requests.
Built with:
- CrewAI - Multi-agent AI framework
- OpenRouter - Unified LLM API
- python-docx-template - Word document templating
- gspread - Google Sheets integration
