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

1. Dry Run (Recommended for testing without submitting)

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

Download files

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

Source Distribution

jobagent-1.0.9.tar.gz (114.9 kB view details)

Uploaded Source

Built Distribution

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

jobagent-1.0.9-py3-none-any.whl (125.4 kB view details)

Uploaded Python 3

File details

Details for the file jobagent-1.0.9.tar.gz.

File metadata

  • Download URL: jobagent-1.0.9.tar.gz
  • Upload date:
  • Size: 114.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.5

File hashes

Hashes for jobagent-1.0.9.tar.gz
Algorithm Hash digest
SHA256 1c78aa460e2f2c6a50c24d18018b387a1815030fa17fa45d0d037a10796528e4
MD5 7f21e45c3e1b464967b33d9041d62208
BLAKE2b-256 30c9d45eb577084c962f1f2dea6821717cbab4f780ec9b1c269c2f2438f7a680

See more details on using hashes here.

File details

Details for the file jobagent-1.0.9-py3-none-any.whl.

File metadata

  • Download URL: jobagent-1.0.9-py3-none-any.whl
  • Upload date:
  • Size: 125.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.5

File hashes

Hashes for jobagent-1.0.9-py3-none-any.whl
Algorithm Hash digest
SHA256 99c690c6883cfb2cb7d322a6e96e3587b125c7751421fe5e0998772898a6573e
MD5 16d0d2dbae513726b79591def5e4d942
BLAKE2b-256 e040cef2223b36e13d113b21265d8302fee3dad6dcd191251a30158e67b96f30

See more details on using hashes here.

Release history Release notifications | RSS feed

1.0.11

2 files

1.0.10

2 files

This release

1.0.9 This release

2 files

1.0.5

2 files

1.0.4

2 files

1.0.3

2 files

1.0.2

2 files

1.0.1

2 files

1.0.0

2 files

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