Skip to content

Job Scout — High-Level Design

graph TB
    %% ═══ USER LAYER ═══
    subgraph USERS["🧑 Users"]
        CLI["CLI / python -m src.harness.run_cycle"]
        WEB["Static Dashboard<br/>dashboard.html + data.js"]
        AGENT["Agent Harness<br/>python -m src.harness (SDK tools)"]
    end

    %% ═══ HARNESSES ═══
    subgraph HARNESS["⚙️ Scouting Cycle Harness<br/>src/harness/run_cycle.py"]
        PIPELINE["run_scouting_cycle()<br/>(7-step pipeline)"]
        PKG["package_output()<br/>(render + export)"]
        CLI_MAIN["main() — argparse CLI<br/>--list-seen / --resume --query"]
    end

    %% ═══ CORE MODULES ═══
    subgraph RESUME_PARSING["📄 Resume Parsing"]
        RP_PARSE["parse_resume() → ParsedResume"]
        RP_FORMATS["PDF (pypdf) / DOCX (python-docx) / .txt"]
        RP_MODEL["ParsedResume<br/>pydantic model"]
    end

    subgraph JOB_SEARCHING["🔍 Job Search"]
        JS_SEARCH["search_jobs.handler() → List[JobResult]"]
        LS_LINKEDIN["linkedin_search.py<br/>parse.bot API"]
        JD_RESULT["JobResult / JobDetail<br/>pydantic models"]
    end

    subgraph MATCHING_SCORING["🎯 Matching & Scoring"]
        SCORER["match_and_score() → MatchResult"]
        SCORE_FORMULA["0.6 × cosine_sim + 0.4 × skill_overlap"]
        FILTERER["filter_top_matches()"]
        MR_RESULT["MatchResult<br/>score, matched_skills, missing_skills, rationale"]
    end

    subgraph CONTENT_GENERATION["📝 Content Generation"]
        RT_TAILOR["tailor_resume() → TailoredResume"]
        CLW_WRITE["write_cover_letter() → CoverLetter"]
        DR_RENDER["doc_renderer.py<br/>render_*_docx / render_*_pdf"]
    end

    subgraph PERSISTENCE["💾 Persistence"]
        DB_MGR["DatabaseManager<br/>(SQLite seen_jobs / apps)"]
        EXCEL_TRACKER["tracker.xlsx<br/>openpyxl output"]
        FOLDERS["applications/<br/>per-job folders"]
    end

    subgraph LOCAL_SERVICES["🖥️ Local Services"]
        OLLAMA["Ollama<br/>nomic-embed-text (768-dim)"]
        CHROMA_DB["(ChromaDB<br/>data/chroma_db/)"]
    end

    subgraph CLOUD_SERVICES["☁️ Cloud Services"]
        LLM_API["OpenAI-compatible LLM API<br/>Claude / GPT via MODEL_NAME + API_URL"]
        PARSE_BOT["(parse.bot API<br/>LinkedIn scraper)"]
    end

    subgraph TRACING["📊 Observability"]
        LANGFUSE["Langfuse (optional)<br/>CycleTrace + spans"]
    end

    %% ═══ FLOWS ═══
    CLI --> PIPELINE
    AGENT --> PIPELINE
    WEB --> EXCEL_TRACKER

    PIPELINE --> RP_PARSE
    RP_PARSE --> RP_MODEL
    PIPELINE --> JS_SEARCH
    JS_SEARCH --> LS_LINKEDIN
    LS_LINKEDIN --> PARSE_BOT

    PIPELINE --> SCORER
    SCORER --> SCORE_FORMULA
    SCORER --> CHROMA_DB
    SCORER --> OLLAMA
    SCORE_FORMULA --> MR_RESULT

    PIPELINE --> RT_TAILOR
    PIPELINE --> CLW_WRITE
    RT_TAILOR --> LLM_API
    CLW_WRITE --> LLM_API

    PIPELINE --> PKG
    PKG --> DR_RENDER
    DR_RENDER --> FOLDERS
    PKG --> EXCEL_TRACKER

    PIPELINE --> DB_MGR

    PIPELINE -. trace .-> LANGFUSE
    SCORER -. trace .-> LANGFUSE
    RT_TAILOR -. trace .-> LANGFUSE
    CLW_WRITE -. trace .-> LANGFUSE

    CLI_MAIN --> PIPELINE
    CLI_MAIN --> DB_MGR

    style USERS fill:#e8f4fd,stroke:#333,stroke-width:2px
    style HARNESS fill:#fff3cd,stroke:#d4a017,stroke-width:2px
    style RESUME_PARSING fill:#d1ecf1,stroke:#333,stroke-width:1px
    style JOB_SEARCHING fill:#e2d5f1,stroke:#333,stroke-width:1px
    style MATCHING_SCORING fill:#d4edda,stroke:#28a745,stroke-width:2px
    style CONTENT_GENERATION fill:#fce4ec,stroke:#333,stroke-width:1px
    style PERSISTENCE fill:#f8d7da,stroke:#dc3545,stroke-width:1px
    style LOCAL_SERVICES fill:#e8f5e9,stroke:#2c7be5,stroke-width:2px
    style CLOUD_SERVICES fill:#fff3cd,stroke:#ffc107,stroke-width:2px
    style TRACING fill:#f3e5f5,stroke:#6f42c1,stroke-width:1px

graph TB
    subgraph LOCAL["🖥️ Local Machine"]
        ENV["Python ≥ 3.11 / venv<br/>pip install -e .[dev]"]
        PY_APP["JobScout Application"]

        subgraph OLLAMA_SVC["Ollama (local, port 11434)"]
            MODEL_EMB["nomic-embed-text<br/>(~768-dim)"]
        end

        CHROMA["(ChromaDB DB<br/>data/chroma_db/ on disk)"]

        subgraph LANGFUSE_OPT["Langfuse (optional)"]
            LCLOUD[cloud.langfuse.com or self-hosted]
        end
    end

    subgraph CLOUD["☁️ External APIs"]
        LLM_API["OpenAI-compatible LLM API&#10;(Claude, GPT-4, etc.)"]
        PARSE_BOT[parse.bot Scraper API]
    end

    PY_APP --> OLLAMA_SVC
    PY_APP --> CHROMA
    PY_APP --> LANGFUSE_OPT
    PY_APP --> LLM_API
    PY_APP --> PARSE_BOT

    style LOCAL fill:#f0f0f0,stroke:#333,stroke-width:2px
    style OLLAMA_SVC fill:#e8f5e9,stroke:#4caf50
    style CHROMA fill:#fce4ec,stroke:#e91e63
    style LANGFUSE_OPT fill:#f3e5f5,stroke:#9c27b0
    style CLOUD fill:#fff3cd,stroke:#ffc107

3. Agent Harness — SDK Tool Registration

Section titled “3. Agent Harness — SDK Tool Registration”
flowchart LR
    subgraph AGENT_HARNESS["src/harness/__main__.py (Agent)"]
        SDK_DISC["Discover __sdk_tools__<br/>from each tool module"]
        MCP_SVR["MCP Server<br/>(tool endpoints)"]
        INTERACT["Interactive Agent Loop<br/>(auto-approved permissions)"]
    end

    subgraph TOOL_MODULES["Tool Modules (each exposes __sdk_tools__)"]
        RP_TOOL["resume_parse tool<br/>from resume_parser.py"]
        SCORER_TOOL["match_and_score tool<br/>from scorer.py"]
        JS_TOOL["search_jobs tool<br/>from job_search.py"]
    end

    AGENT_HARNESS --> SDK_DISC
    SDK_DISC --> MCP_SVR
    MCP_SVR --> INTERACT

    RP_TOOL -. exported via __sdk_tools__ .-> SDK_DISC
    SCORER_TOOL -. exported via __sdk_tools__ .-> SDK_DISC
    JS_TOOL -. exported via __sdk_tools__ .-> SDK_DISC

    style AGENT_HARNESS fill:#e8f4fd,stroke:#2c7be5,stroke-width:2px
    style TOOL_MODULES fill:#d1ecf1,stroke:#333

4. Pipeline Orchestration — Internal Detail

Section titled “4. Pipeline Orchestration — Internal Detail”
flowchart LR
    subgraph ARG["CLI Args"]
        RESUM[--resume path/to/file]
        QUERY[--query senior&#32;designer]
        LOC[--location NYC]
        LIM[--limit 10]
        THR[--threshold 70]
        MAX[--max-tailored 5]
        PROV["--provider ollama|anthropic"]
    end

    ARG --> PIPELINE

    subgraph PIP["Pipeline Stages (run_scouting_cycle)"]
        S1(["Step 1: parse_resume"])
        S2(["Step 2: search_jobs"])
        S3(["Step 3: dedup via DBManager"])
        S4(["Step 4: match_and_score × N"])
        S5(["Step 5: filter_top_matches"])
        S6A(["Step 6a: tailor_resume"])
        S6B(["Step 6b: write_cover_letter"])
        S6C(["Step 6c: cap check"])
        S7(["Step 7: package_output"])
    end

    PIPELINE --> S1
    PIPELINE --> S2
    S2 --> S3
    S3 --> S4
    S4 --> S5
    S5 --> S6C{"score ≥ threshold?"}
    S6C --> S6A
    S6C --> SKIP["skip job"]
    S6A --> S6B
    S6A --> S6C2{"tailor < max_tailored?"}
    S6C2 --> S6A
    S6C2 --> CAP_OUT["cap out — skip tailoring"]
    S6B --> S7

    S1 --> RP_MODEL["(ParsedResume)"]
    S4 --> SCORER_MODEL["(MatchResult)"]
    S6A --> RT_MODEL["(TailoredResume)"]
    S6B --> CLW_MODEL["(CoverLetter)"]
    S7 --> PKG_RESULT["(CycleResult × N)"]

    style PIPELINE fill:#fff3cd,stroke:#d4a017,stroke-width:2px
    style S4 fill:#fce4ec,stroke:#e91e63,stroke-width:2px
    style S5 fill:#d4edda,stroke:#28a745,stroke-width:2px
    style S6A fill:#e8f4fd,stroke:#007bff,stroke-width:2px
    style S7 fill:#f8d7da,stroke:#dc3545,stroke-width:2px