Skip to content

Latest commit

 

History

History
324 lines (255 loc) · 8.68 KB

File metadata and controls

324 lines (255 loc) · 8.68 KB

🎯 LangGraph Job Coach — Azure WebApps Deployment

A production-grade multi-agent LLM application built with LangGraph, deployed on Azure Web App via Azure Container Registry (ACR) with full Azure DevOps CI/CD pipeline.


📌 Project Overview

Smart Job Application Coach is an end-to-end LLMOps project that leverages a 3-agent sequential LangGraph pipeline to help job seekers analyze their resume against a job description, get tailored resume advice, and generate a personalized cover letter — all powered by GPT-4o with structured outputs.

This project demonstrates:

  • Multi-agent orchestration using LangGraph
  • Structured LLM outputs using Pydantic models
  • Production API with FastAPI
  • Full containerization with Docker
  • LLMOps deployment on Azure Web App via ACR
  • Automated CI/CD pipeline with Azure DevOps

📸 Demo

App Demo


🏗️ Architecture

User (Browser)
      │
      ▼
FastAPI Backend (Azure Web App)
      │
      ▼
LangGraph Sequential Pipeline
      │
      ├──▶ Agent 1: Skill Gap Analyzer
      │         └── Reads resume + JD
      │         └── Returns structured SkillGapAnalysis
      │
      ├──▶ Agent 2: Resume Advisor
      │         └── Takes gap analysis
      │         └── Returns section-by-section ResumeAdvice
      │
      └──▶ Agent 3: Cover Letter Writer
                └── Takes resume + JD + gap analysis
                └── Returns personalized CoverLetter

🤖 Agent Design

Agent Role Input Output
Skill Gap Analyzer Compares resume vs JD Resume + JD SkillGapAnalysis
Resume Advisor Suggests improvements Gap analysis + Resume ResumeAdvice
Cover Letter Writer Writes cover letter All prior context CoverLetter

Shared LangGraph State (TypedDict)

class JobCoachState(TypedDict):
    resume_text: str
    job_description: str
    applicant_name: str
    gap_analysis: Optional[SkillGapAnalysis]
    resume_advice: Optional[ResumeAdvice]
    cover_letter: Optional[CoverLetter]
    error: Optional[str]

Each agent receives the full shared state as a dict and returns only the keys it updates — LangGraph merges them automatically.


🛠️ Tech Stack

Layer Technology
LLM Framework LangGraph + LangChain
LLM Model GPT-4o (gpt-4o-2024-08-06)
Structured Output Pydantic v2
Backend API FastAPI
Frontend Vanilla HTML/JS (single file)
Containerization Docker
Container Registry Azure Container Registry (ACR)
Cloud Deployment Azure Web App (Linux Container)
CI/CD Azure DevOps Pipelines
Package Manager uv

📁 Project Structure

LangGraph-Job-Coach-Azure-WebApps/
│
├── app/
│   ├── __init__.py
│   ├── main.py                  # FastAPI app & endpoints
│   ├── graph.py                 # LangGraph pipeline definition
│   │
│   ├── agents/
│   │   ├── __init__.py
│   │   ├── skill_gap.py         # Agent 1 — Skill Gap Analyzer
│   │   ├── resume_advisor.py    # Agent 2 — Resume Advisor
│   │   └── cover_letter.py      # Agent 3 — Cover Letter Writer
│   │
│   ├── models/
│   │   ├── __init__.py
│   │   └── schemas.py           # Pydantic models & LangGraph State
│   │
│   └── static/
│       └── index.html           # Frontend UI
│
├── Dockerfile
├── azure-pipelines.yml
├── pyproject.toml
├── .env.example
└── README.md

⚙️ CI/CD Pipeline

Automated pipeline via Azure DevOps triggered on every push to main or dev.

Push to Repo
     │
     ▼
┌─────────────────────────────┐
│  STAGE 1 — CI               │
│  ├── Login to ACR            │
│  ├── Build Docker Image      │
│  └── Push Image to ACR       │
└─────────────────────────────┘
     │
     ▼
┌─────────────────────────────┐
│  STAGE 2 — CD               │
│  ├── Deploy to Azure Web App │
│  ├── Set environment vars    │
│  ├── Restart Web App         │
│  └── Health Check /health    │
└─────────────────────────────┘
  • Docker image tagged with both BuildId and latest
  • CD stage only runs if CI succeeds (condition: succeeded('CI'))
  • Health check hits /health endpoint — pipeline fails if response is not 200
  • OPENAI_API_KEY injected securely from Azure DevOps variable group (never stored in code or image)

🔌 API Endpoints

Method Endpoint Description
GET / Frontend UI
GET /health Health check
POST /analyze Run full 3-agent pipeline

/analyze Request Body

{
  "resume_text": "Full resume text...",
  "job_description": "Job description text...",
  "applicant_name": "John Doe"
}

/analyze Response

{
  "gap_analysis": {
    "missing_technical_skills": ["..."],
    "missing_soft_skills": ["..."],
    "experience_gaps": ["..."],
    "matching_strengths": ["..."],
    "overall_match_score": 72,
    "summary": "..."
  },
  "resume_advice": {
    "suggestions": ["..."],
    "priority_actions": ["..."],
    "keywords_to_add": ["..."]
  },
  "cover_letter": {
    "subject_line": "...",
    "body": "...",
    "key_selling_points": ["..."]
  }
}

🏃 Run Locally

Prerequisites

  • Python 3.11+
  • uv package manager
  • Docker Desktop
  • OpenAI API key

Setup

# Clone repo
git clone https://github.com/<your-username>/LangGraph-Job-Coach-Azure-WebApps.git
cd LangGraph-Job-Coach-Azure-WebApps

# Create and activate virtual environment
uv venv --python 3.11
.venv\Scripts\Activate.ps1      # Windows
source .venv/bin/activate        # Linux/Mac

# Install dependencies
uv pip install -e .

# Set environment variable
cp .env.example .env
# Edit .env and add your OPENAI_API_KEY

# Run the app
uvicorn app.main:app --reload --port 8000

Open http://localhost:8000

Run with Docker

docker build -t job-coach:latest .

docker run -p 8000:8000 \
  -e OPENAI_API_KEY=your-key-here \
  job-coach:latest

☁️ Azure Deployment

Infrastructure Used

  • Azure Container Registry (ACR) — stores Docker images
  • Azure App Service Plan — Linux B1 tier
  • Azure Web App — pulls container from ACR and runs it

Deploy via CLI

# Login to ACR
az acr login --name <your-acr-name>

# Tag and push image
docker tag job-coach:latest <your-acr-name>.azurecr.io/job-coach:latest
docker push <your-acr-name>.azurecr.io/job-coach:latest

# Create App Service Plan
az appservice plan create \
  --name <plan-name> \
  --resource-group <your-resource-group> \
  --is-linux \
  --sku B1

# Create Web App
az webapp create \
  --resource-group <your-resource-group> \
  --plan <plan-name> \
  --name <your-webapp-name> \
  --deployment-container-image-name <your-acr-name>.azurecr.io/job-coach:latest

# Configure container settings
az webapp config container set \
  --resource-group <your-resource-group> \
  --name <your-webapp-name> \
  --container-image-name <your-acr-name>.azurecr.io/job-coach:latest \
  --container-registry-url https://<your-acr-name>.azurecr.io

# Set environment variables
az webapp config appsettings set \
  --resource-group <your-resource-group> \
  --name <your-webapp-name> \
  --settings OPENAI_API_KEY="<your-key>" WEBSITES_PORT=8000

🔐 Environment Variables

Variable Description
OPENAI_API_KEY OpenAI API key for GPT-4o
WEBSITES_PORT Port exposed by container (8000)

Create a .env file from the provided template:

cp .env.example .env

🧠 Key LLMOps Concepts Demonstrated

  • Structured outputs — every agent returns a validated Pydantic model, not raw text
  • State management — TypedDict shared state flows through all agents with automatic merging
  • Sequential agent pipeline — strict data dependency enforced by LangGraph edges
  • LLM initialization at request time — avoids env var loading race conditions at startup
  • Containerized deployment — reproducible environment from dev to production
  • Secret management — API keys injected via Azure DevOps variable groups, never in code or image

📄 License

MIT License