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
NotImplementedErrordeep 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.
.envvalues loaded into our settings object never actually reachedgoogle.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
- arbeitnow
- asyncio
- chromium
- css3
- embeddings
- fastapi
- firebase
- firestore
- gemini
- google-cloud
- google-genai
- html5
- javascript
- jwt
- node.js
- playwright
- pydantic
- python
- qdrant
- react
- rest-api
- uvicorn
- vector-search
- vite
Log in or sign up for Devpost to join the conversation.