Automating CV Generation with AI-Powered Job Context Extraction
In the "landing" project, we continually seek to enhance user experience and streamline complex processes. A significant feature within this project is CV generation, which initially relied on manual user input for crucial job-related details.
The Problem
Our original CV generation form required users to manually provide the company name, specific role name, and preferred language for a job application. This manual entry process introduced several pain points:
- Increased User Effort: Users had to extract and type this information, even when it was readily available within the job description they provided.
- Data Inconsistencies: Manual entry led to potential typos and formatting variations, complicating downstream processing.
- Validation Overhead: The form required extensive validation for these fields, adding complexity to both front-end and back-end logic.
This created unnecessary friction and a less efficient user journey.
The Approach: AI-Driven Context Extraction
To address these issues, we implemented an innovative solution: leveraging AI to automatically extract the essential job context from the provided job description. This upgrade significantly simplifies the user experience by reducing manual input and ensuring data consistency.
AI-Powered Job Context Extraction Workflow
The core of this enhancement is the new CvGenerationService::extractJobContext method. This method acts as an intelligent parser:
- Job Description Input: The user now only submits the raw job description through the CV generation form.
- Model Resolution: The
CvGenerationServicedynamically resolves the appropriate AI provider and model (e.g., Gemini-2.5-Flash) via anAiModelresolver. - Prompting the LLM: A
PrismClientsends a structured, JSON-only prompt containing the job description to the Large Language Model (LLM). - AI Analysis: The LLM processes the job description and performs an analysis to identify the job's language, the hiring company's name, and the specific role.
- JSON Response & Parsing: The LLM returns its analysis as a JSON payload. The
CvGenerationServicethen parses this response, applyingjson_decodeand trimming any extraneous characters. - Robust Fallbacks: To ensure stability, if the AI response is malformed, not an array, or if specific fields are missing, the service defaults to sensible values (e.g.,
CvLanguage::Englishfor language, and "Unknown" for company and role). - Automated CV Generation: With the extracted and validated context, the system seamlessly proceeds with the existing CV generation and persistence flow, requiring no further manual intervention for these details.
Simplified Form and Validation
This automated extraction allowed us to remove the company_name, role_name, and language fields from the GenerateCvRequest validation rules and the CvGenerator form state. Consequently, the user interface is cleaner, and our validation logic is simplified, focusing solely on the job description.
Before: Users filled out 4 fields: Job Description, Company Name, Role Name, Language. After: Users fill out 1 field: Job Description.
This transition from explicit user input to intelligent extraction marks a significant improvement in both usability and internal process efficiency.
Illustrative Code Snippet (PHP)
Below is a conceptual PHP example demonstrating the extractJobContext method's interaction with an AI client and how the generateCv method utilizes the extracted context:
<?php
namespace App\Services\Cv;
use App\Services\Ai\PrismClient;
use App\Enums\CvLanguage;
class CvGenerationService
{
protected PrismClient $prismClient;
public function __construct(PrismClient $prismClient)
{
$this->prismClient = $prismClient;
}
public function extractJobContext(string $jobDescription): array
{
$prompt = "Analyze the following job description and extract the language, company name, and role name as a JSON object: {\"language\": \"[language]\", \"company_name\": \"[company]\", \"role_name\": \"[role]\"}\n\nJob Description: " . $jobDescription;
// Resolve model and provider (e.g., 'gemini-2.5-flash') dynamically
$modelResponse = $this->prismClient->sendPrompt('gemini-2.5-flash', $prompt);
$decodedResponse = json_decode(trim($modelResponse), true);
// Provide robust fallbacks
if (!is_array($decodedResponse)) {
return [
'language' => CvLanguage::English,
'company_name' => 'Unknown',
'role_name' => 'Unknown',
];
}
return [
'language' => CvLanguage::tryFrom($decodedResponse['language'] ?? 'English') ?: CvLanguage::English,
'company_name' => $decodedResponse['company_name'] ?? 'Unknown',
'role_name' => $decodedResponse['role_name'] ?? 'Unknown',
];
}
public function generateCv(string $jobDescription): string
{
$context = $this->extractJobContext($jobDescription);
$language = $context['language'];
$companyName = $context['company_name'];
$roleName = $context['role_name'];
// In a real application, this would trigger actual CV document generation
return "Generating CV for role: {$roleName} at {$companyName} in {$language->value}...";
}
}
This code illustrates the interaction with an AI service, the parsing of the JSON response, and the application of default values for resilience. The generateCv method then uses this extracted context to proceed.
Key Insight
By embracing AI for natural language understanding, we transformed a multi-step, error-prone user input process into a single, intuitive action. This not only significantly improved the efficiency and user experience of our CV generation feature in the "landing" project but also established a robust pattern for integrating intelligent automation into other parts of our application. The LLM acts as an intelligent parser, reducing form complexity and boosting data accuracy.