Skip to main content

Hybrid Browser AI Agent for LinkedIn Easy Apply

A Python-based browser automation AI agent built with Playwright, a Finite State Machine (FSM), Page Object Model (POM), and an LLM-driven Semantic Matching & Form Interpretation Engine with a strict Human-in-the-Loop (HITL) approval gate.


Architecture Overview

d:\Gravity\
├── config/
│   ├── candidate_profile.json  # Single source of truth for candidate data
│   └── settings.py             # Pydantic BaseSettings (.env loader)
├── models/
│   ├── profile.py              # Pydantic models for Candidate Profile
│   ├── job.py                  # JobCardSummary and JobDetails schemas
│   ├── evaluation.py           # MatchBreakdown and JobEvaluationResult schemas
│   ├── form.py                 # FormField, FormStep, ApplicationSummary schemas
│   └── application.py          # Database ApplicationRecord and Status
├── services/
│   ├── profile_loader.py       # Profile reader & dynamic field calculation
│   ├── db_service.py           # SQLite/PostgreSQL persistence & deduplication
│   ├── llm_service.py          # Multi-backend LLM client & NLP heuristic engine
│   └── resume_service.py       # Resume path manager & reportlab PDF generator
├── automation/
│   └── pages/
│       ├── base_page.py        # Playwright POM base with anti-detection & typing jitter
│       ├── login_page.py       # Auth state verification & manual checkpoint polling
│       ├── job_search_page.py  # Easy Apply search navigation & job card extraction
│       ├── job_details_page.py # Description extraction & Easy Apply launcher
│       └── easy_apply_modal.py # Modal step traversal, dynamic filling, review
├── agents/
│   ├── job_evaluator.py        # LLM semantic fit scorer & experience analysis
│   ├── form_agent.py           # Dynamic form question interpreter & HITL routing
│   └── fsm_runner.py           # Finite State Machine orchestrator
├── tests/                      # Pytest automated test suite
├── main.py                     # Rich CLI runner & dashboard
├── requirements.txt            # Python dependencies
└── .env.example                # Example environment variables

Finite State Machine (FSM) Lifecycle

stateDiagram-v2
    [*] --> INIT
    INIT --> LAUNCH_BROWSER: Load profile & init database
    LAUNCH_BROWSER --> CHECK_LOGIN: Launch persistent Chromium context
    CHECK_LOGIN --> CHECK_LOGIN: Pause & wait if CAPTCHA / 2FA / Login needed
    CHECK_LOGIN --> SEARCH_JOBS: Authenticated
    SEARCH_JOBS --> EXTRACT_CARDS: Search with f_AL=true (Easy Apply)
    EXTRACT_CARDS --> EVALUATE_FIT: Filter out applied jobs
    EVALUATE_FIT --> NEXT_JOB: Score < min_score OR Experience Reject
    EVALUATE_FIT --> FILL_FORM: Score >= min_score -> Launch Modal
    FILL_FORM --> AWAIT_APPROVAL: Reached final review step
    AWAIT_APPROVAL --> SUBMIT: User Approved [y]
    AWAIT_APPROVAL --> NEXT_JOB: User Skipped [n]
    SUBMIT --> TRACK: Click submit
    TRACK --> NEXT_JOB: Persist to applications.db
    NEXT_JOB --> EXTRACT_CARDS: Next unapplied card
    NEXT_JOB --> [*]: Max jobs reached or finished

Key Features & Adherence to Requirements

  1. Finite State Machine & Page Object Model:

    • Clean separation between Playwright UI interactions (automation/pages/), agent decision logic (agents/), data storage (services/), and schema contracts (models/).
  2. Stealth & Persistent Browser Context:

    • Uses Chromium in non-headless mode by default (HEADLESS=false).
    • Uses persistent profile directory (.browser_context/) to preserve cookies, sessions, and tokens.
    • Zero Credential Storage: LinkedIn passwords are never stored in code, .env, or logs.
    • No Automated Solvers: If LinkedIn prompts with 2FA, OTP, or CAPTCHA, the agent halts, prints an alert to the terminal, and waits for manual completion in the open browser before resuming.
  3. Candidate Profile as Single Source of Truth:

    • Located at config/candidate_profile.json.
    • All downstream calculations (compensation, experience years, notice period, skill lookups) dynamically read from this profile without hardcoded values.
  4. Semantic LLM Fit Scoring:

    • Evaluates:
      • Experience Match: Candidate has 4 years; if job asks for 5–8 years, score is downgraded proportionally; if >8 years or Director/Lead, hard rejection.
      • Skill Match: Semantic clustering recognizing synonyms (e.g. RestAssured/Postman <-> API testing, Selenium/Playwright <-> Web automation).
      • Role Match: Checks relevance against preferred roles (QA Automation Engineer, SDET, etc.).
    • Skips postings when overall_score < minimum_match_score (70%).
  5. Dynamic Form Filling:

    • Maps standard personal details, current/expected CTC, 60 days notice period, and skill-specific experience years.
    • Handles dropdowns, radio groups, and file uploads.
    • Flags sensitive/ambiguous declarations (clearance, disability, citizenship) for explicit HITL prompt.
  6. Human-in-the-Loop (HITL) Gate:

    • Pauses on the final review step of the modal.
    • Renders a Rich summary table (Job Title, Company, Match Score, File Uploaded).
    • Requires explicit user approval ([y] Approve & Submit / [n] Skip / [q] Quit) before clicking the submit button.
  7. Application Tracking & Deduplication:

    • Async SQLite database (applications.db) with an applications table.
    • Queries by job_id or job_url + company before opening any job card to avoid duplicates.

Installation & Setup

  1. Install Dependencies:

    pip install -r requirements.txt
    
  2. Initialize Playwright Browser:

    playwright install chromium
    
  3. Configure Environment (Optional):

    cp .env.example .env
    

    Add your OPENAI_API_KEY or GEMINI_API_KEY if you want live cloud LLM reasoning. If left empty, the built-in deterministic NLP extraction and semantic scoring engine will run offline automatically.


Usage Guide

Simulates the workflow, extracts cards, computes fit scores, fills forms up to the final review step, and tests the HITL prompt without submitting:

python main.py --keyword "QA Automation Engineer" --location "Hyderabad" --max-jobs 3 --dry-run

2. Live Apply Mode

Executes the live workflow with non-headless browser:

python main.py --keyword "SDET" --location "Hyderabad" --min-score 70 --max-jobs 5

3. CLI Command Options

--keyword      Job title or search keyword (default: "QA Automation Engineer")
--location     Search location (default: "Hyderabad")
--max-jobs     Maximum applications to process (default: 10)
--min-score    Minimum fit score required to proceed (default: 70.0)
--headless     Run Chromium in headless mode (default: False)
--dry-run      Fill forms to review step without clicking final submit
--auto-approve Bypass interactive HITL prompt at review step
--profile      Custom path to candidate_profile.json
--log-level    Logging level [DEBUG, INFO, WARNING, ERROR] (default: INFO)

Running the Automated Test Suite

Execute pytest across all test modules:

pytest tests/ -v

Metadata

Release files for jobagent 1.0.14

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for jobagent 1.0.14
File Size Uploaded Provenance Actions
jobagent-1.0.14.tar.gz 123.3 kB Without provenance Details for jobagent-1.0.14.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for jobagent 1.0.14
File Interpreter ABI Platform Actions
jobagent-1.0.14-py3-none-any.whl Python 3 none any Details for jobagent-1.0.14-py3-none-any.whl

Total release size: 256.9 kB

Release files / jobagent-1.0.14.tar.gz

Download URL jobagent-1.0.14.tar.gz
Size 123.3 kB
Tags Source
SHA-256 checksum
How to use checksums
feb44c210138702e6fda01e891699e8f5109ce375e93859a9328150a5e2c81ef
BLAKE2b-256 checksum
How to use checksums
229f57f13b4998f53f698b3315c50bdd5cecb8084b0d4ba1b2c5d2cfbab8f036
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.5

Release files / jobagent-1.0.14-py3-none-any.whl

Download URL jobagent-1.0.14-py3-none-any.whl
Size 133.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7bea60807d546fbb2f3bcd70bc27dc6a1e17095273c5d4521253ad01f032c6b6
BLAKE2b-256 checksum
How to use checksums
7486a7b62d20c6b0d15e44495597a8760134ffe3e330d6f71aaeeb1256081dca
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.5

Security for jobagent 1.0.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page