agentic-career-search
AI-agent backend for autonomous job discovery, explainable decisions, and production-style operations.
Demo Gallery





Why this exists
Most job-search automation demos fail in real usage because they: - cannot explain why a role is ranked highly, - cannot recover cleanly when providers fail, - have no durable event trace for debugging, - become hard to maintain once features grow.
This project solves those issues with explicit agent engineering primitives: - deterministic decision engine with rationale traces, - state-machine run lifecycle and durable event log, - tool/adapters abstraction for external integrations, - safety controls (timeouts, bounded scope, cancellation), - optional LLM enrichment via multiple providers.
Real use cases (problem -> solution)
| Problem | Why it hurts | How this repo solves it |
|---|---|---|
| Teams can scrape jobs but cannot justify recommendations | Low trust from users and reviewers | AgentDecisionEngine stores score, matched terms, priority tier, and rationale |
| Background runs are hard to debug | Silent failures block iteration speed | Durable run events (run.*, source.*, agent.*) support replay-style troubleshooting |
| Vendor lock-in around one model provider | High migration cost and brittle integrations | Configurable LLM enrichment supports GPT-5.5, Claude Sonnet 4.6, Gemini 3.x, and Kimi K2-style APIs |
| Model/API outages break the entire flow | System appears unreliable | Graceful fallback preserves deterministic baseline output when LLM enrichment is unavailable |
| Repo quality degrades over time | Contributors lose confidence | CI checks + daily automation loop maintain quality and push incremental improvements |
LLM API integration (consumes model outputs)
Provider integration is built into the code path: - Gemini API - Kimi (Moonshot, OpenAI-compatible) - Claude (Anthropic Messages API) - GPT-compatible APIs through OpenAI-style endpoint patterns
Enable provider enrichment:
LLM_ENABLE_ENRICHMENT=true
LLM_PROVIDER=gemini # or kimi / claude / gpt
Then set matching API keys in .env (see CONFIGURATION.md).
Engineering standards covered
This repository follows the requested standards:
1. standalone repo architecture (not coupled to source repo internals),
2. AI-agent-first design with deterministic decision traces,
3. LLM output consumption from Claude/Gemini/Kimi and GPT-style integrations,
4. production-minded layout (src, tests, scripts, CI, env config, migrations),
5. high-quality docs (README, QUICKSTART, CONFIGURATION, SAFETY, ARCHITECTURE),
6. branch-based merge workflow for controlled integration (no direct unsafe merges),
7. lint/type/test validation before finalization,
8. no Docker requirement for standard local verification,
9. phase branches for development roadmap (phase/01 to phase/10),
10. commit-forward workflow with frequent incremental pushes.
API snapshot
POST /source-configscreate source adapter configsGET /source-configslist enabled sourcesPOST /runsenqueue autonomous runGET /runs/{run_id}inspect run stateGET /runs/{run_id}/eventsinspect event timelinePOST /runs/{run_id}/cancelrequest cancellationGET /jobsinspect normalized, scored, and enriched outputsGET /health/liveandGET /health/ready
Supported job sources
Each SourceConfig selects a source adapter by source_type:
| catsone | CatsoneAdapter | Recognises CATS careers posting anchors by /jobs/{id}, /job/{id}, /careers/{id}, /careers/job/{id}, or /postings/{id} URL shapes | CATS (*.catsone.com) careers boards |
| adp | AdpAdapter | Recognises ADP Recruiting posting anchors by /jobs/{id}, /job/{id}, /careers/{id}, /careers/job/{id}, or /requisitions/{id} URL shapes | ADP (*.adp.com) recruiting boards |
| paradox | ParadoxAdapter | Recognises Paradox careers posting anchors by /jobs/{id}, /job/{id}, /careers/{id}, /careers/job/{id}, or /opportunities/{id} URL shapes | Paradox Olivia (*.paradox.ai) careers boards |
| applicantpro | ApplicantProAdapter | Recognises ApplicantPro careers posting anchors by /jobs/{id}, /job/{id}, /careers/{id}, /careers/job/{id}, or /openings/{id} URL shapes | ApplicantPro (*.applicantpro.com) careers boards |
| brassring | BrassringAdapter | Recognises BrassRing careers posting anchors by /jobs/{id}, /job/{id}, /jobdetail/{id}, /FgJobDetail/{id}, or /careers/{id} URL shapes | IBM Kenexa BrassRing (*.brassring.com) careers boards |
| source_type | Adapter | How it parses | Best for |
|---|---|---|---|
| greenhouse | GreenhouseAdapter | Scrapes div.opening anchors on public Greenhouse boards | Greenhouse-hosted boards |
| lever | LeverAdapter | Scrapes div.posting anchors on public Lever pages | Lever-hosted boards |
| ashby | AshbyAdapter | Recognises jobs.ashbyhq.com/{org}/{uuid} posting anchors by URL shape | Ashby-hosted boards |
| workable | WorkableAdapter | Recognises apply.workable.com/{company}/j/{shortcode} posting anchors by URL shape | Workable-hosted boards |
| recruitee | RecruiteeAdapter | Recognises {company}.recruitee.com/o/{slug} posting anchors by URL shape | Recruitee-hosted careers sites |
| smartrecruiters | SmartRecruitersAdapter | Recognises jobs.smartrecruiters.com/{company}/{jobId}-{slug} posting anchors by URL shape | SmartRecruiters-hosted careers sites |
| teamtailor | TeamtailorAdapter | Recognises {company}.teamtailor.com/jobs/{jobId}-{slug} posting anchors by URL shape | Teamtailor-hosted careers sites |
| personio | PersonioAdapter | Recognises {tenant}.jobs.personio.de/.com/job/{jobId} posting anchors by URL shape | Personio-hosted careers sites (DACH/EU) |
| bamboohr | BambooHrAdapter | Reads the public {tenant}.bamboohr.com/careers/list JSON board and maps each opening to /careers/{id} | BambooHR-hosted careers sites (SMB tech/healthcare/services) |
| jobvite | JobviteAdapter | Recognises jobs.jobvite.com/{company}/job/{jobId} posting anchors by URL shape (terminal singular job, alphanumeric id) | Jobvite-hosted careers sites |
| icims | IcimsAdapter | Recognises careers-{tenant}.icims.com/jobs/{jobId}/{slug}/job posting anchors by URL shape (terminal literal job, numeric id; slug optional) | iCIMS-hosted careers portals (enterprise) and vanity-domain proxies |
| workday | WorkdayAdapter | POSTs the public {tenant}.wd{N}.myworkdayjobs.com/wday/cxs/{tenant}/{site}/jobs JSON CXS board (page size 20) and maps each posting to {origin}/{locale}/{site}{externalPath} | Workday-hosted enterprise careers sites |
| oracle_taleo | OracleTaleoAdapter | Recognises Taleo/Oracle Cloud posting anchors via job= query ids or terminal /job/{id} / /jobs/{id} path shapes | Oracle Taleo (*.taleo.net) and Oracle Cloud HCM careers portals |
| successfactors | SuccessFactorsAdapter | Recognises SuccessFactors posting anchors via jobId / career_job_req_id query ids or terminal /job/{id} / /jobs/{id} path shapes | SAP SuccessFactors (*.successfactors.com / *.successfactors.eu) careers portals |
| zoho_recruit | ZohoRecruitAdapter | Recognises Zoho Recruit posting anchors via jobId / jid / job_id query ids or terminal /job/{id} / /jobs/{id} / /careers/{id} path shapes | Zoho Recruit (*.zohorecruit.com) careers portals and vanity-domain proxies |
| jazzhr | JazzHrAdapter | Recognises JazzHR posting anchors via /apply/{jobId} or /apply/{jobId}/{slug} path shapes | JazzHR (*.applytojob.com/apply) careers portals and vanity-domain proxies |
| breezyhr | BreezyHrAdapter | Recognises {company}.breezy.hr/p/{positionId} posting anchors by URL shape (terminal p, alphanumeric id; slug optional) | Breezy HR-hosted careers sites (startup/SMB) |
| freshteam | FreshteamAdapter | Recognises Freshteam careers posting anchors by job URL shape | Freshworks Freshteam-hosted careers boards |
| phenom | PhenomPeopleAdapter | Recognises Phenom posting anchors via /job/{jobId}/{slug} or /jobs/{jobId} path shapes, rejecting list/index/login/apply-step links | Phenom People-hosted enterprise and branded careers sites |
| rippling | RipplingAdapter | Recognises Rippling posting anchors via terminal /jobs/{uuid} paths on *.rippling.com domains | Rippling-hosted public careers boards |
| pinpoint | PinpointAdapter | Recognises Pinpoint HR careers posting anchors by /postings/{uuid} or /jobs/{id} URL shape | Pinpoint (*.pinpointhq.com) careers boards |
| comeet | ComeetAdapter | Recognises Comeet careers posting anchors by /jobs/{company}/{companyId}/{jobSlug}/{jobId} URL shape | Comeet (www.comeet.co / www.comeet.com) careers boards |
| fountain | FountainAdapter | Recognises Fountain careers posting anchors by /apply/{company}/{positionId}, /apply/{slug}, /jobs/{id}, /openings/{id}, or /positions/{id} URL shape | Fountain (*.fountain.com, web.fountain.com) careers boards |
| gem | GemAdapter | Recognises Gem careers posting anchors by jobs.gem.com/{company}/{jobId}, /jobs/{jobId}, /openings/{id}, or {company}.gem.com/careers/... URL shapes | Gem (jobs.gem.com / *.gem.com) careers boards |
| avature | AvatureAdapter | Recognises Avature careers posting anchors by /JobDetail/{id}, /JobDetail.aspx?JobId={id}, /careers/{id}, /careers/job/{id}, /careers/VacancyDetail/{id}, /Vacancy/{id}, or /vacancies/{id} URL shapes | Avature-hosted public careers portals |
| eightfold | EightfoldAdapter | Recognises Eightfold careers posting anchors by /careers/job/{id}, /careers/job/{id}/{slug}, /career_detail/{id}, /position/{id}, or /jobs/{id} URL shapes | Eightfold (*.eightfold.ai) careers boards |
| jobscore | JobScoreAdapter | Recognises JobScore careers posting anchors by /careers/{company}/jobs/{slug}-{id}, /careers/{company}/jobs/{id}, /jobs/{id}, /jobs/{slug}/{id}, or /position(s)/{id} URL shapes | JobScore (careers.jobscore.com / *.jobscore.com) careers boards |
| hireology | HireologyAdapter | Recognises Hireology careers posting anchors by /jobs/{id}, /careers/job/{id}, or /job/{id}/{slug} URL shapes | Hireology (careers.hireology.com) careers boards |
| dayforce | DayforceAdapter | Recognises Dayforce careers posting anchors by /JobDetail/{id}, /careers/job/{id}, /MyCareer/JobDetail?jobId={id}, or /positions/{id} URL shapes | Dayforce (*.dayforcehcm.com) careers boards |
| homerun | HomerunAdapter | Recognises Homerun careers posting anchors by /jobs/{id}-{slug}, /o/{id}, or /vacancies/{id} URL shapes | Homerun (*.homerun.co) careers boards |
| clearcompany | ClearCompanyAdapter | Recognises ClearCompany careers posting anchors by /careers/job/{id}, /careers/{id}, /jobs/{id}, /job/{id}-{slug}, or /position/{id} URL shapes | ClearCompany (*.clearcompany.com) careers boards |
| applied | AppliedAdapter | Recognises Applied careers posting anchors by /jobs/{id}, /j/{id}, /role/{id}, /roles/{id}, or /job/{id} URL shapes | Applied (*.applied.co) careers boards || jsonld | JsonLdAdapter | Reads embedded schema.org/JobPosting JSON-LD | Any board emitting Google-Jobs structured data (SmartRecruiters, custom career sites, ...) |
| recruiterflow | RecruiterflowAdapter | Recognises Recruiterflow careers posting anchors by /jobs/{id}, /job/{id}, /careers/job/{id}, /openings/{id}, or /opening/{id} URL shapes | Recruiterflow (*.recruiterflow.com) careers boards |
| manatal | ManatalAdapter | Recognises Manatal careers posting anchors by /jobs/{id}, /job/{id}, /careers/{id}, /careers/job/{id}, or /openings/{id} URL shapes | Manatal (*.manatal.com) careers boards |
| join | JoinAdapter | Recognises Join careers posting anchors by /companies/{slug}/jobs/{id}, /jobs/{id}, /job/{id}, or /positions/{id} URL shapes | Join (join.com) careers boards |
| softgarden | SoftgardenAdapter | Recognises Softgarden careers posting anchors by /job/{id}, /jobs/{id}, /vacancies/{id}, /vacancy/{id}, or /position/{id} URL shapes | Softgarden (*.softgarden.io) careers boards |
| factorial | FactorialAdapter | Recognises Factorial HR careers posting anchors by /jobs/{id}, /job/{id}, /careers/{id}, /careers/job/{id}, or /open-positions/{id} URL shapes | Factorial (*.factorialhr.com) careers boards |
| ukg | UkgAdapter | Recognises UKG/UltiPro careers posting anchors by /jobs/{id}, /job/{id}, /opportunities/{id}, /opportunity/{id}, or /careers/job/{id} URL shapes | UKG (*.ultipro.com / *.ukg.net) careers boards |
| bullhorn | BullhornAdapter | Recognises Bullhorn careers posting anchors by /jobs/{id}, /Job/{id}, /careers/{id}, /careers/job/{id}, or /position/{id} URL shapes | Bullhorn (*.bullhornstaffing.com) careers boards |
| paylocity | PaylocityAdapter | Recognises Paylocity careers posting anchors by /jobs/{id}, /JobDetails/{id}, /careers/{id}, /careers/job/{id}, or /openings/{id} URL shapes | Paylocity (*.paylocity.com) careers boards |
| polymer | PolymerAdapter | Recognises Polymer careers posting anchors by /jobs/{id}, /job/{id}, /careers/{id}, /careers/job/{id}, or /positions/{id} URL shapes | Polymer (*.polymer.co) careers boards |
| jobadder | JobAdderAdapter | Recognises JobAdder careers posting anchors by /jobs/{id}, /job/{id}, /careers/{id}, /careers/job/{id}, or /position/{id} URL shapes | JobAdder (*.jobadder.com) careers boards |
| jobylon | JobylonAdapter | Recognises Jobylon careers posting anchors by /jobs/{id}, /job/{id}, /careers/{id}, /careers/job/{id}, /positions/{id}, or /vacancies/{id} URL shapes | Jobylon (jobs.jobylon.com) careers boards |
| ceipal | CeipalAdapter | Recognises Ceipal careers posting anchors by /jobs/{id}, /job/{id}, /careers/{id}, /careers/job/{id}, or /requisitions/{id} URL shapes | Ceipal (*.ceipal.com / jobs.ceipal.com) careers boards |
| pageup | PageUpAdapter | Recognises PageUp careers posting anchors by /jobs/{id}, /job/{id}, /careers/{id}, /careers/job/{id}, or /opportunities/{id} URL shapes | PageUp (*.pageuppeople.com / careers.pageuppeople.com) careers boards |
| talentlyft | TalentLyftAdapter | Recognises TalentLyft careers posting anchors by /jobs/{id}, /job/{id}, /careers/{id}, /careers/job/{id}, or /openings/{id} URL shapes | TalentLyft (*.talentlyft.com / apply.talentlyft.com) careers boards |
| applicantstack | ApplicantStackAdapter | Recognises ApplicantStack careers posting anchors by /jobs/{id}, /job/{id}, /careers/{id}, /careers/job/{id}, or /postings/{id} URL shapes | ApplicantStack (*.applicantstack.com) careers boards |
| dover | DoverAdapter | Recognises Dover careers posting anchors by /jobs/{id}, /job/{id}, /careers/{id}, /careers/job/{id}, or /openings/{id} URL shapes | Dover (app.dover.com) careers boards |
| loxo | LoxoAdapter | Recognises Loxo careers posting anchors by /jobs/{id}, /job/{id}, /positions/{id}, /careers/{id}, or /careers/job/{id} URL shapes | Loxo (*.loxo.co) careers boards |
| jsonld | JsonLdAdapter | Reads embedded schema.org/JobPosting JSON-LD | Any board emitting Google-Jobs structured data (SmartRecruiters, custom career sites, ...) |
| hibob | HibobAdapter | Recognises HiBob careers posting anchors by /jobs/{id}, /job/{id}, /careers/{id}, /careers/job/{id}, or /positions/{id} URL shapes | HiBob (*.hibob.com) / Bob careers boards |
| trackerrms | TrackerRmsAdapter | Recognises TrackerRMS careers posting anchors by /jobs/{id}, /job/{id}, /careers/{id}, /careers/job/{id}, or /vacancies/{id} URL shapes | TrackerRMS (*.tracker-rms.com and branded) careers boards |
| careerplug | CareerPlugAdapter | Recognises CareerPlug careers posting anchors by /jobs/{id}, /job/{id}, /careers/{id}, /careers/job/{id}, or /listings/{id} URL shapes | CareerPlug (*.careerplug.com) careers boards |
| recruitcrm | RecruitCrmAdapter | Recognises RecruitCRM careers posting anchors by /jobs/{id}, /job/{id}, /careers/{id}, /careers/job/{id}, or /opening/{id} URL shapes | RecruitCRM (*.recruitcrm.io) careers boards |
| vincere | VincereAdapter | Recognises Vincere careers posting anchors by /jobs/{id}, /job/{id}, /careers/{id}, /careers/job/{id}, or /job-detail/{id} URL shapes | Vincere (*.vincere.io) careers boards |
| tribepad | TribepadAdapter | Recognises Tribepad careers posting anchors by /jobs/{id}, /job/{id}, /careers/{id}, /careers/job/{id}, or /vacancy/{id} URL shapes | Tribepad (*.tribepad.com) careers boards |
| crelate | CrelateAdapter | Recognises Crelate careers posting anchors by /jobs/{id}, /job/{id}, /careers/{id}, /careers/job/{id}, or /opportunity/{id} URL shapes | Crelate (*.crelate.com) careers boards |
| jobdiva | JobDivaAdapter | Recognises JobDiva careers posting anchors by /jobs/{id}, /job/{id}, /careers/{id}, /careers/job/{id}, or /jd/{id} URL shapes | JobDiva (*.jobdiva.com) careers boards |
| pcrecruiter | PCRecruiterAdapter | Recognises PCRecruiter careers posting anchors by /jobs/{id}, /job/{id}, /careers/{id}, /careers/job/{id}, or /requisition/{id} URL shapes | PCRecruiter (*.pcrecruiter.com) careers boards |
| cornerstone | CornerstoneAdapter | Recognises Cornerstone careers posting anchors by /jobs/{id}, /job/{id}, /careers/{id}, /careers/job/{id}, or /opening/{id} URL shapes | Cornerstone OnDemand (*.csod.com) careers boards |
| eploy | EployAdapter | Recognises Eploy careers posting anchors by /jobs/{id}, /job/{id}, /careers/{id}, /careers/job/{id}, or /role/{id} URL shapes | Eploy (*.eploy.net) careers boards |
| beamery | BeameryAdapter | Recognises Beamery careers posting anchors by /jobs/{id}, /job/{id}, /careers/{id}, /careers/job/{id}, or /campaign/{id} URL shapes | Beamery (*.beamery.com) careers boards |
| hireez | HireezAdapter | Recognises HireEZ careers posting anchors by /jobs/{id}, /job/{id}, /careers/{id}, /careers/job/{id}, or /positions/{id} URL shapes | HireEZ / Hiretual (*.hireez.com) careers boards |
Unlike the HTML-scraping adapters, bamboohr and workday are structured-JSON
sources: BambooHR and Workday careers pages are client-rendered apps, so those
adapters read the tenant's public JSON listing endpoints directly (stable titles,
locations, and ids) instead of parsing rendered markup. Workday uses the public
CXS POST API — see docs/guides/WORKDAY_SOURCE_GUIDE.md.
The jsonld adapter is vendor-neutral: modern ATS platforms publish
<script type="application/ld+json"> JobPosting payloads so their roles appear
in Google Jobs, so a single adapter covers boards that would otherwise each need
a bespoke scraper. It understands bare objects, arrays, @graph/ItemList
containers, TELECOMMUTE remote roles, and PropertyValue identifiers, and it
skips malformed blocks instead of failing the whole page.
# Register a JSON-LD source
curl -X POST localhost:8000/source-configs \
-H 'content-type: application/json' \
-d '{"name":"acme-careers","source_type":"jsonld","base_url":"https://acme.example.com/careers"}'
Quick start
git clone https://github.com/Francis1998/agentic-career-search.git
cd agentic-career-search
uv venv
source .venv/bin/activate
uv sync --extra dev --frozen
cp .env.example .env
uv run uvicorn autoapply_agent.main:app --reload
Documentation
| Document | Description |
|---|---|
| ARCHITECTURE.md | Core agent architecture and lifecycle |
| CONFIGURATION.md | Runtime and provider configuration |
| QUICKSTART.md | Fast local setup and verification |
| SAFETY.md | Scope boundaries and operational guardrails |
| docs/DEPLOYMENT.md | Deployment guidance |
| docs/TROUBLESHOOTING.md | Common failure recovery paths |
| CHANGELOG.md | Release history |
Regenerate demos
./scripts/generate_demo_gif.sh
License
MIT © Francis1998