An AI-powered resume tailoring system that analyzes job descriptions and generates a customized, ATS-optimized LaTeX resume — ready to compile and submit.
Three specialized AI agents run sequentially:
| Agent | Model | Role |
|---|---|---|
| Job Analyst | Gemini 2.5 Flash (free tier) | Extracts skills, requirements, tone, and cultural signals from the job description |
| Resume Tailor | Qwen3:14b (local via Ollama) | Selects the most relevant projects from your pool, rewrites bullets with job-specific keywords, and enforces a one-page budget |
| Resume Validator | Gemini 2.5 Flash (free tier) | Scores ATS compatibility, keyword integration rate, and human readability before generating output |
Resume tailoring runs fully locally (Ollama) — your resume data never leaves your machine for the creative step. Gemini free tier handles the structured extraction tasks.
- LaTeX file saved to
resumestex/{Company}_{Role}.tex— compile withpdflatexor Overleaf - Validation report printed to terminal with ATS score, keyword integration rate, and suggestions
- Application log added to your Notion database with the full job description saved for interview prep
git clone https://github.com/rishikesh-20/resumeforge.git
cd resumeforge
uv syncCopy the example and fill in your keys:
cp .env.example .envThen edit .env:
GOOGLE_API_KEY=your_google_ai_studio_key
NOTION_API_KEY=your_notion_integration_key
NOTION_DATABASE_ID=your_notion_database_idGetting these keys:
GOOGLE_API_KEY— Google AI Studio → Get API key (free)NOTION_API_KEY— notion.so/my-integrations → New integration → copy secretNOTION_DATABASE_ID— The ID from your Notion database URL:notion.so/{workspace}/{DATABASE_ID}?v=...
Step 1 — Create an integration
Go to notion.so/my-integrations → New integration → give it a name → Submit. Copy the Internal Integration Secret — this is your NOTION_API_KEY.
Step 2 — Create a database
Create a new full-page database in Notion with exactly these properties:
| Property | Type |
|---|---|
| Name | Title |
| Company | Text |
| Role | Text |
| Location | Text |
| Date Applied | Date |
| Status | Select — add options: Applied, Interview, Offer, Rejected |
| Job ID | Text |
Step 3 — Get the database ID
Open the database in Notion. The URL looks like:
https://www.notion.so/{workspace}/{DATABASE_ID}?v=...
Copy the DATABASE_ID part (32-character string before ?v=) — this is your NOTION_DATABASE_ID.
Step 4 — Connect the integration to the database
Open the database → click ... (top right) → Connections → search for your integration → click Connect. Without this step, the API key alone won't work.
Copy the example and fill in your details:
cp data/resume.json.example data/resume.jsonEdit data/resume.json with your actual header, work experience, education, and skills.
Projects live in data/projects/ as individual JSON files — one file per project. Copy the example to get started:
cp data/projects/project.json.example data/projects/my-project.jsonAdd as many projects as you have. For each run, the AI scores all projects against the job description and selects the top 3 most relevant ones. The description field should be a detailed paragraph — the more context you give, the better the AI can tailor the bullet points.
Example project file:
{
"name": "My Project",
"tech_stack": "Python, FastAPI, PostgreSQL",
"date": "December 2024",
"description": "Detailed paragraph about what you built, why, how, and any results or metrics..."
}uv run streamlit run app.pyFill in the company, job title, location, optional job ID, and paste the job description. The app shows pipeline progress, the validation report, and a preview of the generated LaTeX, with a button to download the .tex file. The file is also saved to resumestex/ and the application is logged to Notion, same as the CLI.
uv run resumeforgeYou will be prompted for:
- Company name, job title, location, job ID (optional)
- Job description (paste and type
ENDon a new line to finish)
The three agents run sequentially (~3–6 minutes total, mostly the local Ollama step), then the .tex file is saved to resumestex/ and the application is logged to Notion with the full job description.
Once you have a .tex file:
pdflatex resumestex/Company_Role.texOr paste the contents into Overleaf for online compilation.
The template is at templates/resume_latex_template.tex. It uses Jinja2 with (( )) for variables and (% %) for blocks (to avoid conflicts with LaTeX syntax). Edit the static sections (certifications, formatting, margins) directly. Preserve the (( )) and (% %) placeholders.
resumeforge/
├── data/
│ ├── resume.json # Your master resume (gitignored — copy from resume.json.example)
│ ├── resume.json.example # Template to copy and fill in
│ └── projects/ # One JSON file per project (gitignored)
│ └── project.json.example # Template to copy for each project
├── resumestex/ # Generated .tex files (gitignored)
├── templates/
│ └── resume_latex_template.tex # Jinja2 LaTeX template
└── src/resumeforge/
├── config/
│ ├── agents.yaml # Agent roles and backstories
│ └── tasks.yaml # Task instructions
├── utils/
│ ├── resume_latex_generator.py
│ ├── notion_tracker.py
│ ├── read_projects.py # Loads all projects from data/projects/
│ └── ...
├── agents.py # Agent and task definitions
├── models.py # Pydantic data models
└── main.py # Entry point
- Qwen3:14b via Ollama — local LLM for resume tailoring
- Gemini 2.5 Flash — free-tier LLM for analysis and validation
- Jinja2 — LaTeX template rendering
- notion-client — application tracking