Skip to main content

๐Ÿ’ผ Job Hunt Agent

PyPI CI Python License: MIT

An end-to-end job search + tailored CV generation agent built with LangChain deepagents:

  1. builds your master profile (profile/profile.md) from an uploaded CV,
  2. searches postings on LinkedIn/Indeed and more (JobSpy scraper โ€” no API key needed; optional JSearch API),
  3. ranks them against your profile with honest fit scores,
  4. for every job you pick, a cv-tailor subagent writes a job-specific CV + cover letter (XYZ-formula bullets, ATS-friendly) into applications/,
  5. 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, recall_memory, remember_fact, forget_fact
backend=FilesystemBackend(...) Built-in file tools read the real project dir (profile/, applications/), path-locked to the repo root
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

pip install job-hunt-agent   # or: uv tool install job-hunt-agent

or from source:

git clone https://github.com/EceDalpolat/job-hunt-agent && cd job-hunt-agent
uv sync
cp .env.example .env         # fill in ONE of the API keys (see below)
  • For the model, set one of ANTHROPIC_API_KEY, OPENROUTER_API_KEY or OPENAI_API_KEY (priority in that order). Override the default model with JOB_AGENT_MODEL โ€” e.g. openai/gpt-5.1 on 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_KEY switches 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. A structured profile/profile.json (typed via a Pydantic schema) is generated alongside it for reliable programmatic access. Review both before applying anywhere. See profile/profile.example.md for the expected shape. (profile/profile.md, profile/profile.json and profile/career_memory.json are gitignored โ€” your personal data stays local.)

Career memory

The agent keeps long-term memory across chat sessions:

  • Facts (profile/career_memory.json): target companies, remote/salary preferences, decisions โ€” saved via the remember_fact tool, recalled at the start of every conversation with recall_memory, removable with forget_fact.
  • Application history: every approved application (applications/applications_log.jsonl) and every generated CV package are surfaced through recall_memory, so the agent won't re-suggest a job you already applied to โ€” even in a brand-new chat.

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:

  1. main.py/ui.py passes the user message to agent.invoke(...); the UsageTracker callback captures the tokens of every LLM call (main agent AND subagents).
  2. The main agent (prompt: prompts/system.md) calls search_jobs โ€” tools.py tries JSearch API โ†’ JobSpy scraper โ†’ sample data in order, normalizes results to schemas.Job and caches them in _JOB_CACHE.
  3. For each selected job the main agent spawns the cv-tailor subagent via the task tool (prompt: prompts/cv_tailor.md). The subagent reads the profile and the writing guides in references/, writes the CV/cover letter and saves them with save_application_package under applications/.
  4. submit_application pauses on interrupt_on; y/n in the CLI or โœ…/โŒ in the UI resumes the flow with Command(resume={"decisions": ...}).
  5. Every step is logged to logs/job_agent.log; every LLM call's tokens/cost go to logs/usage.jsonl (rates in config.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/            # bring-your-own CV writing guides (gitignored; see its README)
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.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

job_hunt_agent-0.2.0.tar.gz (182.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

job_hunt_agent-0.2.0-py3-none-any.whl (30.5 kB view details)

Uploaded Python 3

File details

Details for the file job_hunt_agent-0.2.0.tar.gz.

File metadata

  • Download URL: job_hunt_agent-0.2.0.tar.gz
  • Upload date:
  • Size: 182.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for job_hunt_agent-0.2.0.tar.gz
Algorithm Hash digest
SHA256 6df771b23a715adb74ff6599e4ccfd86a7cc493c7b6aa1cb2cdb406951469db2
MD5 1e8b2a0eeb49e6ce87880fcbb2541fdb
BLAKE2b-256 f39d33b318dc5a6510132a789f75fe82922e6bb59b4e24372e0c9fd6aea498c9

See more details on using hashes here.

Provenance

The following attestation bundles were made for job_hunt_agent-0.2.0.tar.gz:

Publisher: publish.yml on EceDalpolat/job-hunt-agent

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file job_hunt_agent-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: job_hunt_agent-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 30.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for job_hunt_agent-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 809f7441bd1a59d173ca071fdf9998f0327a274fa51b752b694d543f57590095
MD5 bfec764815513d2f94b7ef69ebe24472
BLAKE2b-256 2acbdf0d90acb56b732ac7aae1b6647b797d005bf5ad01dc814256a95e12b8ef

See more details on using hashes here.

Provenance

The following attestation bundles were made for job_hunt_agent-0.2.0-py3-none-any.whl:

Publisher: publish.yml on EceDalpolat/job-hunt-agent

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page