Job hunting + tailored CV generation agent built with LangChain deepagents
Project description
๐ผ Job Hunt Agent
An end-to-end job search + tailored CV generation agent built with LangChain deepagents:
- builds your master profile (
profile/profile.md) from an uploaded CV, - searches postings on LinkedIn/Indeed and more (JobSpy scraper โ no API key needed; optional JSearch API),
- ranks them against your profile with honest fit scores,
- for every job you pick, a cv-tailor subagent writes a job-specific CV +
cover letter (XYZ-formula bullets, ATS-friendly) into
applications/, - starts an application only with your approval (human-in-the-loop interrupt).
What is deepagents and how is it used here?
Deepagents is an agent harness that ships with planning (write_todos), a
virtual file system, subagent delegation (task tool) and human
approval (interrupts). The mapping in this project:
| Deepagents feature | Here |
|---|---|
create_deep_agent(tools=...) |
search_jobs, get_job_details, read_profile, submit_application |
subagents=[...] |
cv-tailor: writes the CV/cover letter in an isolated context |
interrupt_on={...} + checkpointer |
submit_application never runs without human approval |
write_todos (built-in) |
The agent plans multi-job tasks itself |
| Model | Anthropic, OpenRouter or OpenAI โ whichever key you set |
Setup
git clone https://github.com/EceDalpolat/job-hunt-agent && cd job-hunt-agent
uv sync # or: pip install -e .
cp .env.example .env # fill in ONE of the API keys (see below)
- For the model, set one of
ANTHROPIC_API_KEY,OPENROUTER_API_KEYorOPENAI_API_KEY(priority in that order). Override the default model withJOB_AGENT_MODELโ e.g.openai/gpt-5.1on OpenRouter. - Job search needs no key: JobSpy
scrapes LinkedIn + Indeed directly. LinkedIn rate-limits aggressively; if it
blocks you, Indeed results keep coming. Setting
RAPIDAPI_KEYswitches to the JSearch API instead. If both fail, the agent falls back to 6 bundled sample postings.
Create your profile
uv run job-agent-profile path/to/your-cv.pdf --notes "linkedin.com/in/you, target: AI engineer roles"
This extracts the text from your CV (pdf/docx/md/txt), normalizes it with the
LLM into profile/profile.md, and that file becomes the single source of
truth โ the agent never puts anything on a CV that is not in it. Review it
before applying anywhere. See profile/profile.example.md
for the expected shape. (profile/profile.md is gitignored โ your personal data
stays local.)
Run
uv run job-agent # terminal CLI
uv run streamlit run job_agent/ui.py # web UI
In the Streamlit UI: upload your CV in the sidebar to build the profile, chat to search postings ("find AI/LLM engineer jobs in Istanbul, rank the top 5"), say "prepare CVs for 1 and 3", approve applications with โ /โ buttons, download the generated CV/cover letters from the sidebar, and watch live token/cost metrics.
Example CLI session:
๐ค > find python backend jobs that fit me
๐ค (reads your profile, searches postings, returns a fit-scored list)
๐ค > I want to apply to 1 and 3
๐ค (cv-tailor subagent writes a CV + cover letter per job into
applications/2026-07-06-healthtech-labs-senior-backend.../)
โ ๏ธ Pending approval:
Tool : submit_application
Args : {'job_id': 'mock-001', ...}
Approve? [y/n]: y
๐ค Application logged, apply page opened in your browser.
๐ฐ 7 LLM calls โข 41,203 in + 2,841 out tokens โข ~$0.16
Architecture
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Entry points โ main.py (CLI) ui.py (Streamlit) โ
โ setup_logging() + UsageTracker + thread_id โ
โโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ agent.invoke(message, callbacks=[tracker])
โโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Orchestration โ agent.py โ build_agent() โ
โ _resolve_model(): ANTHROPIC > OPENROUTER > โ
โ OPENAI (from config.py) โ
โ system prompt: prompts/system.md โ
โโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโ
โ tool calls โ task (subagent)
โโโโโโโโโโโโโผโโโโโโโโโโโ โโโโโโโโโโผโโโโโโโโโโโโโโโโ
Tools โ tools.py โ โ cv-tailor subagent โ
โ search_jobs: โ โ prompt: cv_tailor.md โ
โ JSearch>JobSpy>mock โ โ reads profile + the โ
โ get_job_details โ โ references/ guides, โ
โ read_profile โ โ writes XYZ-format CV, โ
โ submit_application โโโผโโโ save_application_pkg โ
โ (human approval!) โ โโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโฌโโโโโโโโโโโ
โโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Data & output โ schemas.py (Job, UsageRecord) config.py (paths)โ
โ applications/ logs/job_agent.log logs/usage.jsonlโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Flow of one "apply to 1 and 3" turn:
main.py/ui.pypasses the user message toagent.invoke(...); theUsageTrackercallback captures the tokens of every LLM call (main agent AND subagents).- The main agent (prompt:
prompts/system.md) callssearch_jobsโtools.pytries JSearch API โ JobSpy scraper โ sample data in order, normalizes results toschemas.Joband caches them in_JOB_CACHE. - For each selected job the main agent spawns the cv-tailor subagent via
the
tasktool (prompt:prompts/cv_tailor.md). The subagent reads the profile and the writing guides inreferences/, writes the CV/cover letter and saves them withsave_application_packageunderapplications/. submit_applicationpauses oninterrupt_on; y/n in the CLI or โ /โ in the UI resumes the flow withCommand(resume={"decisions": ...}).- Every step is logged to
logs/job_agent.log; every LLM call's tokens/cost go tologs/usage.jsonl(rates inconfig.PRICINGโ approximate, keep updated).
File map
job_agent/
โโโ config.py # paths, model defaults, pricing table (single place)
โโโ schemas.py # Pydantic: Job, UsageRecord, ApplicationLogEntry
โโโ prompts/ # system.md, cv_tailor.md, profile_builder.md (out of code)
โโโ logging_conf.py # logs/job_agent.log (rotating) + console warnings
โโโ usage.py # UsageTracker callback โ tokens/cost โ usage.jsonl
โโโ agent.py # deep agent wiring, model selection, subagent, HITL
โโโ tools.py # search (JSearch>JobSpy>sample), profile, references, save, apply
โโโ profile_builder.py # CV file โ LLM โ profile/profile.md
โโโ main.py # interactive CLI
โโโ ui.py # Streamlit chat UI (approvals, downloads, cost metrics)
โโโ sample_jobs.py # sample postings (last-resort fallback)
profile/ # profile.md (gitignored) + profile.example.md
references/ # CV writing guides (XYZ formula, Harvard guide, ...)
logs/ # job_agent.log + usage.jsonl (gitignored)
applications/ # generated packages (gitignored)
Honesty notes
- No automated form submission: neither the job boards' APIs nor their terms of service allow auto-filling application forms. The agent prepares the package, logs the application and opens the apply page; you press the final "Submit".
- No fabrication on CVs: the subagent is instructed to use only the real
facts in
profile/profile.mdโ still, read everything before sending it.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file job_hunt_agent-0.1.0.tar.gz.
File metadata
- Download URL: job_hunt_agent-0.1.0.tar.gz
- Upload date:
- Size: 194.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0a618e1632dbb1f4a52b837bc2492016be6ceb822969a083d78709a40fcdcf3f
|
|
| MD5 |
494e2ef2e296ccb86dc75afdcdb00117
|
|
| BLAKE2b-256 |
c7369cf11c05e7039bde3b9b1bbf66d0ece5e753bee6e2f205cbfcf6fe305b66
|
Provenance
The following attestation bundles were made for job_hunt_agent-0.1.0.tar.gz:
Publisher:
publish.yml on EceDalpolat/job-hunt-agent
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
job_hunt_agent-0.1.0.tar.gz -
Subject digest:
0a618e1632dbb1f4a52b837bc2492016be6ceb822969a083d78709a40fcdcf3f - Sigstore transparency entry: 2084641967
- Sigstore integration time:
-
Permalink:
EceDalpolat/job-hunt-agent@387e524c94658272125e15781c5c5276e51b9b28 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/EceDalpolat
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@387e524c94658272125e15781c5c5276e51b9b28 -
Trigger Event:
release
-
Statement type:
File details
Details for the file job_hunt_agent-0.1.0-py3-none-any.whl.
File metadata
- Download URL: job_hunt_agent-0.1.0-py3-none-any.whl
- Upload date:
- Size: 25.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fdba4be1794b7fa33d9900a41da6645b12a23f7cc458921d8fbf51de445af9b4
|
|
| MD5 |
1a7a47ab7409dfcf844abe2efbb118f0
|
|
| BLAKE2b-256 |
2bd311dcfaecce484fcd1e51225bf875a870415110f6e5e1fe3e35d259e6da95
|
Provenance
The following attestation bundles were made for job_hunt_agent-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on EceDalpolat/job-hunt-agent
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
job_hunt_agent-0.1.0-py3-none-any.whl -
Subject digest:
fdba4be1794b7fa33d9900a41da6645b12a23f7cc458921d8fbf51de445af9b4 - Sigstore transparency entry: 2084641974
- Sigstore integration time:
-
Permalink:
EceDalpolat/job-hunt-agent@387e524c94658272125e15781c5c5276e51b9b28 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/EceDalpolat
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@387e524c94658272125e15781c5c5276e51b9b28 -
Trigger Event:
release
-
Statement type: