Inspiration

Job hunting today is a second job. You scroll a dozen boards, re-type the same "why do you want to work here" answer fifty different ways, and lose track of which application you left half-finished. We wanted an agent that treats the whole pipeline — discover, match, fill, review — as one connected workflow instead of a pile of disconnected browser tabs, and that never crosses the line from "assistant" to "impersonator": it drafts, it never clicks submit for you.

What it does

JobAgent runs quietly in the background and surfaces a ready-to-review application in your dashboard:

  • Discovers roles continuously from Arbeitnow and Himalayas, deduplicating against jobs it's already seen.
  • Matches each opening against your profile — skills, experience, projects, preferences — and scores it before ever creating an application.
  • Fills the real application form itself: it opens the page with Playwright, inspects every field, and classifies each one as deterministic (name, phone, education — pulled straight from your profile), semantic (open-ended questions — answered from your resume/profile via embeddings), or sensitive (salary expectations, visa status — always left for you).
  • Stops and asks the moment it hits something it genuinely can't answer, instead of guessing.
  • Never submits. The pipeline has a hard ceiling at "draft ready" — a human always reviews and sends the final application.
  • Gives you an AI co-pilot alongside the draft to refine any answer in plain language before you approve it.

How we built it

  • Backend: FastAPI + Pydantic models for Users, Profiles, Jobs, and Applications, backed by Firestore for structured data and Qdrant for semantic search over profile chunks.
  • Discovery layer: A scheduled workflow that pulls from job-board APIs, normalizes postings with Gemini, and runs matching against active user profiles.
  • Automation layer: A dedicated orchestrator that drives an explicit state machine — DISCOVERED → SELECTED → APPLICATION_STARTED → FILLING → WAITING_FOR_HUMAN / DRAFT_READY — with Playwright doing the actual form interaction, gated by a deterministic action validator that refuses to ever emit a "submit" action.
  • Frontend: React + Vite, with a workspace view that shows the live application form next to the AI co-pilot chat, and a profile builder that feeds the whole matching/filling pipeline.

Challenges we ran into

  • The automation pipeline had every piece but no wiring. The job-matching agent, the field-classification agent, and the browser tool all existed independently — nothing actually called them in sequence. We had to build the orchestrator that ties discovery → state transitions → browser automation → field storage into one resumable pipeline.
  • Windows + Playwright's async API don't mix well under uvicorn. The async browser driver needs subprocess support that uvicorn's event loop doesn't reliably provide on Windows, which surfaced as a cryptic NotImplementedError deep in asyncio internals. We solved it by running the browser step with Playwright's sync API inside a dedicated worker thread with its own event loop policy, decoupling it entirely from the main server loop.
  • Credentials silently not reaching Google's SDK. .env values loaded into our settings object never actually reached google.auth.default(), since that reads real OS environment variables — a subtle gap that looked like a CORS error in the browser before we traced it back to a missing Firestore client.
  • Migrating job sources mid-build, swapping a key-gated provider for a free, keyless one with a completely different response shape and no per-job detail endpoint, without breaking the normalization pipeline downstream.

Accomplishments that we're proud of

  • A genuinely end-to-end pipeline: a job posted today can be discovered, matched, and have a draft application waiting for review — with zero manual form-filling.
  • A safety boundary that isn't just a comment in the code: the "never auto-submit" guarantee is enforced at the action-validation layer, not just by convention.
  • A field-classification system that knows the difference between "fill this automatically," "answer this thoughtfully," and "ask the human" — and actually stops instead of hallucinating an answer to something like visa sponsorship status.

What we learned

  • Orchestration is its own piece of engineering — having well-built agents and tools in isolation means nothing until something explicitly sequences and error-handles them together.
  • Platform-specific quirks (Windows event loops, credential propagation) cost more debugging time than the "hard" AI parts of the project, and are worth budgeting for early.
  • Designing the state machine before the automation logic made every later feature — retries, human hand-off, resuming — dramatically simpler to reason about.

What's next for JobAgent

  • Resume-aware cover letter generation tailored per posting, not just per profile.
  • A confidence score per filled field, so reviewers can scan drafts in seconds instead of re-reading every answer.
  • Support for more job sources and ATS platforms (Greenhouse, Lever, Workday) with source-specific field-detection strategies.
  • An "agent score" dashboard showing match quality and time saved over a raw manual job search.

Built With

Share this project:

Updates

Submission history