Job descriptions
How reveilio loads, parses, and structures a job description.
The JobDescription class
A JobDescription holds the raw text of a JD and a lazily-computed
structured parse. The LLM is only called the first time you access
.parsed. After that, the result is cached on the object.
Each instance is fully independent. You can create multiple
JobDescription objects for different open roles and reuse them
across any number of resumes.
Three ways to create one
1. From a file on disk
Supports .pdf, .docx, .doc, and .txt files.
jd = reveilio.JobDescription.from_file("roles/senior_python.pdf")
Reveilio extracts the plain text from the file, stores it in jd.text,
and sets jd.source to the file path.
2. From free text
Paste a JD directly from an email, database row, or API response.
jd = reveilio.JobDescription.from_text("""
Senior Python Engineer, 5+ years experience.
Must know FastAPI, PostgreSQL, and AWS.
Experience with CI/CD pipelines required.
""")
3. From in-memory bytes
Useful when integrating with web frameworks where uploaded files arrive as bytes.
jd = reveilio.JobDescription.from_bytes(
uploaded_file.read(),
filename="senior_python.pdf", # extension determines the parser
)
What gets extracted
When you access jd.parsed, the LLM extracts a structured dictionary
containing:
| Field | Description |
|---|---|
position_title | The role title (e.g. "Senior Full Stack Developer"). |
company | The hiring company name. |
location | Work location. |
work_mode | Remote, hybrid, or on-site. |
required_skills | List of must-have technical skills. |
preferred_skills | List of nice-to-have skills. |
soft_skills | Communication, leadership, etc. |
required_experience | Minimum years of experience. |
preferred_experience | Preferred years of experience. |
education_requirements | List of required degrees. |
certifications | Required or preferred certifications. |
tools_and_technologies | Specific tools mentioned. |
programming_languages | Languages required. |
frameworks | Frameworks required (React, Django, etc.). |
databases | Database systems mentioned. |
cloud_platforms | AWS, Azure, GCP, etc. |
methodologies | Agile, Scrum, etc. |
responsibilities | List of job responsibilities. |
Lazy parsing and caching
The structured parse is computed only on first access to
jd.parsed. This means creating a JobDescription object
is instant. The LLM call only happens when you actually need the parsed data,
which is typically when you call analyze_resume().
Once computed, the result is cached. If you score 500 resumes against the same JD, the JD parsing LLM call happens exactly once.
jd = reveilio.JobDescription.from_file("jd.pdf") # no LLM call yet
print(jd.parsed["position_title"]) # LLM call happens here
print(jd.parsed["required_skills"]) # cached, no second call
Managing multiple JDs
Each JobDescription is an independent object with its own cache.
Keep a dictionary of them for multi-role screening:
roles = {
"backend": reveilio.JobDescription.from_file("roles/backend.pdf"),
"frontend": reveilio.JobDescription.from_file("roles/frontend.txt"),
"devops": reveilio.JobDescription.from_text(devops_jd_text),
}
# Score one candidate against all open roles
for role_name, jd in roles.items():
result = reveilio.analyze_resume("resumes/alice.pdf", jd)
print(f"{role_name}: {result.overall_score}%")
Inspecting the raw text
The extracted text is always available via jd.text. This is useful for
debugging or for feeding into your own processing pipeline.
print(jd.text[:500]) # first 500 chars of extracted text
print(jd.source) # file path or "text"
jd.parsed returns a safe fallback dictionary with
"position_title": "N/A" and empty lists for skills and requirements.
Continue to Resume parsing.