AI-powered course recommendation engine built using Azure OpenAI for Data Science and AI careers.
MIA helps users discover the most relevant learning resources for Data Science and AI careers. It follows a multi-agent architecture where every agent has a single responsibility, with Azure OpenAI's inbuilt guardrails protecting every LLM interaction.
flowchart TD
%% ── Entry point ───────────────────────────────────────────────
START([User Query]) --> IG{Input Guard Agent}
%% ── Input guard decision ──────────────────────────────────────
IG -->|REJECTED| REJ[Return Error Response]
IG -->|VALID| IE[Intent Extraction Agent]
%% ── Core pipeline ─────────────────────────────────────────────
IE --> REC[Recommendation Agent]
CAT[(Course Catalog)] --> REC
REC --> RG[Response Generation Agent]
RG --> OG{Output Guard Agent}
%% ── Output guard decision ─────────────────────────────────────
OG -->|REJECTED| FALLBACK[Return Safe Fallback Response]
OG -->|VALID| FINAL([Final Response])
%% ── Grouping by responsibility ────────────────────────────────
subgraph LLM["Azure OpenAI (LLM)"]
IE
RG
end
subgraph DETERMINISTIC["Deterministic (No LLM)"]
REC
end
subgraph GUARDRAILS["Azure Inbuilt Guardrails"]
IG
OG
end
%% ── Legend ────────────────────────────────────────────────────
subgraph LEGEND["Legend"]
direction LR
L1([Start / End])
L2{Decision Point}
L3[Agent / Process]
L4[(Data Source)]
end
How to read this diagram: rounded nodes are entry/exit points, diamonds are guardrail decision points, rectangles are agents/processes, and cylinders are data sources. The three subgraphs group components by responsibility: LLM-backed agents, the deterministic recommendation engine, and the guardrails.
- Input Guard Agent — Validates the request using Azure's inbuilt content filters + domain relevance. Rejects off-topic or unsafe queries before they reach the LLM.
- Intent Extraction Agent — Uses Azure OpenAI to extract the user's profile (career goal, skills, missing skills, experience level).
- Recommendation Agent — Deterministically matches courses from
app/data/courses.json. Never calls the LLM. - Response Generation Agent — Uses Azure OpenAI to generate a friendly mentor explanation of the recommended courses.
- Output Guard Agent — Validates the LLM output for hallucinated courses and unsafe content before returning it.
flowchart TD
MIA[MIA - Course Recommendation Engine]
MIA --> GUARD[Guardrails]
MIA --> LLM[LLM Agents]
MIA --> DET[Deterministic]
MIA --> DATA[Data]
GUARD --> IG[Input Guard Agent<br/>validates requests]
GUARD --> OG[Output Guard Agent<br/>validates responses]
LLM --> IE[Intent Extraction Agent<br/>extracts user profile]
LLM --> RG[Response Generation Agent<br/>writes mentor response]
DET --> REC[Recommendation Agent<br/>matches courses]
DATA --> CAT[(Course Catalog<br/>courses.json)]
How to read this tree: the engine splits into four responsibility branches — Guardrails (input/output validation), LLM Agents (Azure OpenAI reasoning), Deterministic (no-LLM course matching), and Data (the course catalog). Each leaf shows the agent and its single responsibility.
| Agent | Responsibility | Uses LLM? |
|---|---|---|
| Input Guard | Validates requests, ensures domain relevance, content safety | No (Azure inbuilt guardrails) |
| Intent Extraction | Extracts user profile (career goal, skills, missing skills, level) | ✅ Azure OpenAI |
| Recommendation | Deterministic course matching from app/data/courses.json |
❌ Never |
| Response Generation | Generates natural language mentor responses | ✅ Azure OpenAI |
| Output Guard | Validates output for hallucinations and unsafe content | No (Azure inbuilt guardrails) |
MIA relies on Azure OpenAI's inbuilt guardrails rather than custom heuristics:
| Guardrail | Applied to | Where |
|---|---|---|
| Content filters (Hate, Self-Harm, Sexual, Violence) | Prompts & Completions | Azure AI Content Safety SDK + server-side |
| Prompt Shields (jailbreak / prompt injection) | User prompts | Configured in Azure AI Foundry (server-side) |
| Protected Material (text & code) | Completions | Configured in Azure AI Foundry (server-side) |
Setup: In Azure AI Foundry → Guardrails + controls → Content filters, enable Prompt Shields and Protected Material detection on the deployments used by the Intent Extraction and Response Generation agents.
- Python 3.12+ with type hints
- FastAPI + Uvicorn — REST API
- Azure OpenAI — LLM for intent extraction & response generation
- Azure AI Content Safety — inbuilt content-filter guardrails
- Pydantic — data validation & settings
- structlog — structured logging
course-recommendation-engine/
├── app/
│ ├── main.py # FastAPI app entry point
│ ├── api/routes.py # REST endpoints
│ ├── agents/ # The 5 agents
│ │ ├── input_guard.py # Input validation (Azure guardrails + domain)
│ │ ├── intent_extraction.py # User profile extraction (LLM)
│ │ ├── recommendation.py # Deterministic course matching (no LLM)
│ │ ├── response_generation.py # Mentor response (LLM)
│ │ └── output_guard.py # Output validation (Azure guardrails + hallucination)
│ ├── llm/
│ │ ├── azure_client.py # Azure OpenAI client
│ │ ├── azure_content_safety.py # Azure AI Content Safety client
│ │ ├── extractor.py # Intent extraction LLM logic
│ │ ├── explainer.py # Response generation LLM logic
│ │ └── prompts.py # Centralized system prompts
│ ├── models/schemas.py # Pydantic models
│ ├── config/settings.py # Environment configuration
│ ├── data/courses.json # Course catalog
│ └── utils/ # Shared helpers & constants
├── tests/ # pytest test suite
├── requirements.txt
├── run.py # CLI entry point
└── .env.example # Environment variable template
- Python 3.12+
- An Azure OpenAI resource (deployment for Intent Extraction + Response Generation)
- An Azure AI Content Safety resource
python -m venv .venv
.venv\Scripts\activate # Windows
source .venv/bin/activate # macOS / Linux
pip install -r requirements.txtcp .env.example .envFill in your Azure credentials in .env:
AZURE_OPENAI_ENDPOINT=https://<your-resource>.openai.azure.com/
AZURE_OPENAI_API_KEY=<your-api-key>
AZURE_OPENAI_DEPLOYMENT_NAME=gpt-4o
AZURE_CONTENT_SAFETY_ENDPOINT=https://<your-contentsafety>.cognitiveservices.azure.com/
AZURE_CONTENT_SAFETY_API_KEY=<your-content-safety-key># Windows
.venv\Scripts\python.exe -m uvicorn app.main:app --reload
# macOS / Linux
.venv/bin/python -m uvicorn app.main:app --reloadThe API will be available at:
- Swagger UI:
http://localhost:8000/docs - Health check:
http://localhost:8000/api/v1/health - Base URL:
http://localhost:8000
Interactive API documentation (Swagger UI). Open it in your browser:
http://localhost:8000/docs
Health check.
curl http://localhost:8000/api/v1/healthGet course recommendations for a user query.
curl -X POST http://localhost:8000/api/v1/recommend \
-H "Content-Type: application/json" \
-d '{"query": "I know Python and Pandas. I want to become a Data Scientist."}'Response:
{
"status": "SUCCESS",
"response": "Great choice! Here are some courses to help you become a Data Scientist...",
"recommendations": [
{
"course": {
"id": "course-003",
"title": "SQL for Data Analysis",
"url": "https://learn.microsoft.com/training/paths/get-started-querying-with-transact-sql/"
},
"score": 0.58,
"reasoning": "Matches 1 of your missing skills"
}
]
}pytest tests -v- Single Responsibility — each agent does one thing
- Stateless agents — no shared mutable state
- Deterministic recommendation engine — no LLM in course matching
- LLM only for reasoning and explanation
- Guardrails before and after every LLM interaction (Azure inbuilt)
The architecture supports adding new agents without modifying existing ones:
- Resume Review Agent
- Interview Preparation Agent
- Career Roadmap Agent
- Skill Gap Analysis Agent
- Learning Progress Agent