This project is an interactive job-application assistant built with Google ADK. A user-facing supervisor coordinates specialist agents for job research, career evidence selection, resume writing, resume evaluation, and application support.
The design is intentionally conversational rather than a fixed one-pass pipeline. The user can tailor a resume, revise individual bullets, ask questions about fit, prepare for interviews, or add tentative career-bank updates without restarting the entire workflow.
USER
|
v
JobApplicationSupervisor
(user-facing manager)
|
+--------------------+--------------------+
| | |
v v v
JobResearchAgent CareerEvidenceSelector ApplicationSupportAgent
|
v
ResumeWriterAgent
|
v
ResumeAlignmentEvaluatorAgent
|
revise if useful
|
v
JobApplicationSupervisor
|
v
USER
The specialist agents are wrapped with ADK AgentTool. This keeps the supervisor
in control of the conversation: specialists perform focused work and return their
results to the supervisor rather than taking over future user messages.
The supervisor also has direct tools to:
- read the reviewed career evidence bank;
- read industry profiles;
- run deterministic resume-quality checks; and
- append tentative, user-reviewable career-bank updates.
Loads or reads a job posting, identifies required and preferred qualifications, assigns stable RQ/PQ IDs, extracts employer terminology and likely hiring priorities, and performs company research when configured.
Reads the career bank and all industry profiles, selects a profile unless the user overrides it, maps reviewed evidence to each RQ/PQ, and identifies which qualifications deserve the highest placement in the resume.
Creates or revises a LaTeX resume using reviewed career evidence, selected profile guidance, employer terminology, user constraints, and optional legacy resume context. It can be called independently for iterative revisions rather than requiring the full workflow every time.
Checks factual support, qualification coverage, top-down prioritization, employer-language alignment, concision, and LaTeX usability. It also runs the deterministic resume-quality checker.
Handles job-specific support outside resume writing, including interview answers, strengths/weaknesses, recruiter-screen talking points, why-this-role responses, recruiter messages, and cover-letter ideas.
Candidate information is handled in this order:
career_data/career_evidence.yamlcontains the reviewed authoritative baseline.- Candidate facts explicitly supplied by the user in the current conversation may supplement or correct the bank for the current application.
- The selected industry profile guides evidence ranking, terminology, emphasis, and default section order.
- A legacy resume is optional wording/context reference only.
- The LaTeX template controls presentation rather than candidate facts.
Top-level tentative_updates in the career bank are deliberately excluded from
reviewed evidence by read_career_evidence. They cannot support resume claims
until the user manually reviews and integrates them or explicitly confirms the
information during the current conversation.
Expected local file:
career_data/career_evidence.yaml
Evidence may be nested beneath roles. Individual experience items should retain stable IDs so qualifications can be mapped to specific evidence.
The current workflow expects the core wording for an evidence item under:
experience: ...Industry-specific approved language can later be nested within that experience item. When an appropriate approved-language variant exists, the selector and writer treat it as preferred wording while preserving the underlying evidence.
The supervisor can add a career-bank proposal only when the user explicitly asks it to save, add, record, or update something.
The write tool does not alter reviewed evidence. Instead, it appends a record to
the same career_evidence.yaml file under:
tentative_updates:
- id: TU-...
review_status: needs_user_review
update_type: approved_language
target: USDA-PIPELINE-001
proposed_text: Developed reproducible genomic workflows...
industry: software_engineeringThis keeps the update inside the career bank while clearly separating it from reviewed facts. The user manually decides whether and where to integrate the proposal and then removes the tentative record.
Store profiles under:
career_data/profiles/
For example:
plant_biotech.yaml
plant_genetics.yaml
general_biotech.yaml
software_engineering.yaml
academia.yaml
The filename stem is the profile identifier. Profiles are intentionally lightweight and may contain:
target_roles:
priority_tags:
priority_skills:
emphasize:
deemphasize:
terminology:
section_order:
Special rules:Special rules is appropriate for user-authored instructions that apply only to
one industry. Global factual, privacy, resume-style, and agent-behavior rules are
kept in prompts.py so they apply consistently.
Profile fields are passed through rather than normalized, so the exact
user-authored Special rules key is preserved.
The workflow is designed around a fast hiring-manager scan.
The strongest supported evidence for the most important job qualifications should appear early. Required qualifications normally outrank preferred ones, and repeated or strongly emphasized responsibilities may receive additional priority.
The job posting guides presentation language. When employer terminology is a truthful description of existing evidence, the writer should prefer that wording so the hiring manager can recognize the match quickly.
- A resume bullet may contain no more than one sentence.
- One bullet should communicate one primary accomplishment, responsibility, or qualification signal.
- Independent evidence items should not be compressed into one oversized bullet.
- The writer aims for roughly 15-30 words when practical.
- Bullets above 40 words are flagged for review, but length alone is a warning rather than an automatic failure.
- Supported metrics should remain when they materially strengthen the job match.
A summary is optional. When useful, it must remain a high-level overview rather than a miniature resume:
- no more than three short sentences;
- no more than 60 words;
- focus on professional identity and the strongest qualification areas for the target job; and
- avoid detailed accomplishments, chronology, and long tool lists.
resume_quality.py provides objective checks that do not depend on LLM judgment.
It currently checks:
- whether any bullet contains more than one sentence;
- whether a bullet exceeds the 40-word review threshold;
- whether a professional summary exceeds 60 words; and
- whether a professional summary exceeds three sentences.
The writer uses the checker before returning a resume, and the evaluator uses it again during review. Subjective questions such as whether a bullet is persuasive or whether the strongest evidence was selected remain agent-evaluation tasks.
The supervisor should call only the specialist needed for the current request. Examples:
- New job/resume: research -> evidence selection -> writer -> evaluator.
- Shorten one bullet: writer only, with evaluation if the change is substantive.
- New interview question: ApplicationSupportAgent only.
- New job posting: refresh research and evidence selection.
- Same job, different industry profile: refresh evidence selection and resume, without automatically repeating company research.
For a new resume, the supervisor may send evaluator feedback back to the writer. It is instructed not to exceed two writer/evaluator revision cycles in one user turn; unresolved uncertainty is returned to the user instead of looping.
job_app_workflow/
├── __init__.py
├── agent.py
├── config.py
├── llm_factory.py
├── search_provider.py
├── document_reader.py
├── career_reader.py
├── career_writer.py
├── resume_quality.py
├── template_reader.py
├── prompts.py
├── .env.example
├── requirements.txt
├── career_data/
│ └── README.md
├── tests/
│ ├── __init__.py
│ ├── _package.py
│ ├── test_career_data.py
│ └── test_resume_quality.py
├── templates/
│ ├── README.md
│ └── base_resume_template.tex
├── resumes/
│ └── README.md
└── job_postings/
└── README.md
The tests are developer safeguards, not an extra step in preparing a job application. A normal user does not need to interact with them.
They protect deterministic behavior that should not change unpredictably when the code is edited. Current tests verify that:
- industry-profile fields such as
Special rulesload unchanged; - tentative career updates append without altering reviewed career content;
- tentative updates are excluded from authoritative evidence reads;
- invalid update types are rejected;
- a two-sentence resume bullet is detected;
- a one-sentence bullet is accepted;
- unusually long bullets are flagged without being treated as automatic failures;
- overlong professional summaries are rejected; and
- common abbreviations such as
Ph.D.do not create false sentence-count failures.
These are different from future agent evals, which should assess probabilistic behavior such as whether the supervisor chose the right specialist, whether the best evidence was placed near the top, or whether a resume is meaningfully better aligned to the posting.
Run the deterministic tests from the workflow directory:
python -m unittest discover -s tests -vThey do not call an LLM or require an API key.
The project pins:
google-adk==2.6.3
Pinning the version makes multi-agent routing behavior more reproducible across installations. Upgrade intentionally and rerun the tests after changing the pin.
Create and activate a virtual environment, then install dependencies:
python -m venv .venv
source .venv/bin/activate
# Windows PowerShell: .venv\Scripts\Activate.ps1
pip install -r requirements.txtCopy and edit the environment template:
cp .env.example .envCareer-data defaults:
CAREER_DATA_DIR=career_data
CAREER_EVIDENCE_FILE=career_evidence.yaml
INDUSTRY_PROFILE_DIR=career_data/profiles
Place job postings in:
job_postings/
Place reviewed career data in:
career_data/
Place optional legacy resumes in:
resumes/
These user-data directories are ignored by Git except for their README files.
The tests/ directory is intentionally committed.
adk run .Or launch the development UI:
adk web --port 8000Use the parent-directory invocation instead if that is how your local ADK project is organized.
Job posting file: job_postings/research_scientist.docx
Resume template: templates/base_resume_template.tex
Keep the resume to two pages.
Select the most appropriate industry profile automatically.
Prioritize the qualifications a hiring manager is most likely to scan for first.
Keep bullets concise and to one sentence each.
Job posting file: job_postings/computational_biologist.docx
Industry profile: general_biotech
Resume template: templates/base_resume_template.tex
Keep the resume to two pages and emphasize transferable genomics and
computational-biology experience.
After a resume is created, the user can simply say:
The first USDA bullet is too long. Shorten it while keeping the qualifications
most relevant to this job.
The supervisor should reuse the existing application context rather than rerun company research.
Based on this job and my career evidence, help me answer:
"What is your greatest strength?"
Choose a strength that is especially relevant to this role, support it with a
specific example, and make the answer sound natural when spoken.
I like the revised wording for USDA-PIPELINE-001. Add it to the career bank as a
tentative software_engineering approved-language update for me to review manually.
The supervisor should append a needs_user_review record rather than altering
the reviewed evidence itself.
Career files and resumes may contain personally identifiable information. The workflow does not automatically redact content before sending it to the configured model provider. Keep local career data private and use sanitized copies or placeholders when appropriate.