ResumeAI
I've spent countless hours manually tweaking my resume for every job application, and I knew there had to be a better way. This post is about my journey building ResumeAI, a tool to automate that tedious process using AI. I'll take you through the challenges of parsing existing resumes, the magic of using Google's Gemini API, and how I tackled the tough problem of preserving formatting without starting from scratch.

Building ResumeAI: Challenges learnings and limitations
The Problem I Set Out to Solve
We've all been there. You find the perfect job description, but your existing resume doesn't quite match. You have to open your resume, read the job description, and painstakingly rewrite your bullet points to include keywords and highlight relevant experience. It's a manual, repetitive process that steals time and energy that could be better spent on other things. I wanted to build a tool that could automate this, making job applications faster and more effective.
A High-Level Look at How It Works
I envisioned a simple, step-by-step workflow for the user.
- Upload & Select: The user uploads their master resume in DOCX format. My application then extracts the content and presents it as a list of paragraphs. The user can then select the paragraphs they want the AI to enhance.
- Configure & Customize: The user pastes the job description and enters the company name. They can also select which Google Gemini AI model they'd like to use - either the faster and more cost-effective "Flash" or the more advanced "Pro" model.
- Generate & Download: The AI goes to work, generating an optimized resume and a personalized cover letter. The user can review the changes and download their new resume as a PDF. I also added a feature to save the application, so they can track their progress later.
This simple process, however, was built on top of some pretty interesting technical challenges.
The Formatting Dilemma: DOCX and the AI
One of the biggest hurdles was figuring out how to let the user keep their resume's existing formatting. Many tools I looked at, like `renderCV`, force you to build a resume from scratch or require a partial rebuild from a scanned document. I wanted to avoid this. My solution was to use the python-docx library to read the DOCX file and process it in a way that the AI could understand. The extract_text_from_docx method in my backend code was responsible for this. It breaks down the resume into individual paragraphs and sends them to the AI as plain text, along with a few bits of metadata like whether the text is a heading or bolded.
The real magic happens after the AI returns the enhanced content. I had to create a new method, update_docx_with_customizations, to go back to the original document and replace only the specific paragraphs the user selected. This approach ensures that the overall structure, fonts, and any un-edited sections of the resume are kept exactly as they were, which was a huge win for usability.
The AI Integration: Two API Calls for One Job
Another fascinating aspect of this project was the AI itself. Instead of a single, massive prompt, I decided to use two separate requests to the Google Gemini API. The first API call enhances the individual paragraphs, focusing on incorporating keywords and powerful action verbs. I even built in a "word count" check to help the AI keep the total length in check, so the resume doesn't grow uncontrollably. The second API call is specifically for generating the cover letter and analyzing the job description to provide a "match score". By separating these tasks, I was able to get more focused and higher-quality results from the AI for each specific request.
The code for this looks something like this:
def generate_ai_customization(self, api_key, model_name, resume_data, selected_paragraph_ids, job_description, company_name, custom_paragraph_prompt='', custom_cover_letter_prompt='', regenerate_type=None):
...
# REQUEST 1: Enhance individual paragraphs
if regenerate_type != 'cover_letter':
...
paragraph_response = model.generate_content(paragraph_prompt)
...
# REQUEST 2: Generate cover letter
if regenerate_type != 'paragraphs':
...
cover_letter_response = model.generate_content(cover_letter_prompt)
...
...
return { ... }
My Biggest Challenge: Server-Side PDF Generation
The final step, generating a PDF, was surprisingly the hardest part. I initially planned to use the docx2pdf library to convert the finalized DOCX file into a PDF. However, as I discovered, this library often requires specific software to be installed on the server, which can be a major pain to set up and maintain.
To get around this, I added a fallback. The application now also saves the final DOCX file and, in cases where PDF conversion fails, it returns the DOCX file directly for the user to convert manually. It's not a perfect solution, but it guarantees the user still gets their customized resume without a complete failure.
What's Next for ResumeAI
The plan is to continue improving the tool by adding support for other AI providers, like OpenAI and Claude. Although this is last on the list of priorities as it does not take much development time and I personally preferred and only use Google's Gemini. I also want to build a feature for batch processing, allowing users to customize their resume for multiple jobs at once. The long-term goal is to integrate with job boards and ATS systems to provide a truly seamless application experience.
This project was a blast to build, and it taught me a ton about the nuances of document processing, AI integration, and creating a truly helpful user experience.
Feel free to check it out on my GitHub!