Case study
CareerLens
Career counselling meetings, turned into a summary and a to-do list
- Python
- Unsloth + PEFT
- Llama 3.1 8B
- Flask
- FastAPI
- React
- DynamoDB
How it works
A bot joins the counselling call and what comes back is a speaker-labelled transcript. The model turns that into a fixed JSON payload (summary, action items, insights, who spoke) in whichever of English, Hindi or Marathi the session ran in. The model is the project’s own: three open-weight families were benchmarked on a hand-built dataset of counselling transcripts, and the best of them, Llama 3.1 8B, was fine-tuned with Unsloth and LoRA until it beat the others on quality and inference time at once.
- MeetingA bot joins the call as a participant
- TranscriptSpeaker-labelled turns, with durations
- Fine-tuned LLMLlama 3.1 8B, 4-bit, LoRA adapter
- JSON payloadSummary, action items, insights, speakers
- DashboardReview, talk-time split, ask follow-ups
The problem
The advice outlives the session; the record of it usually doesn’t
What makes a counselling session worth the hour is the conversation: what got covered, what the student is actually weighing up, what they agreed to do next. The write-up of it happens afterwards from memory, or it doesn’t happen. CareerLens produces that record from the session itself.
Capture
A bot in the room, not a file upload
Rather than ask a counsellor to record and upload audio, a containerised bot joins the meeting and the backend drives it: join, leave, fetch the transcript as it arrives, store it. Consecutive turns from the same speaker are merged while the transcript is being formatted, which is where the dashboard’s talk-time split comes from, with no separate diarisation step to maintain.
The dataset
References written in the shape the dashboard needed
Nothing public covers career counselling, so the reference set was built by hand: thirty-five transcripts in each of English, Hindi and Marathi, cleaned and annotated. The annotations aren’t prose. Each one is the JSON the interface expects, with the summary, the action items, the insights and the speakers already in their fields, so the model was trained to emit something renderable rather than something that then has to be salvaged by a parser.
The model
Three families in, one model out
Llama 3, Mistral and DeepSeek were each run over the transcripts zero-shot, one-shot and three-shot to find the strongest candidate in every family, and those three were then fine-tuned with Unsloth’s 4-bit quantisation and LoRA on a Kaggle P100: batch size two with eight-step gradient accumulation, a 4096-token context, 8-bit AdamW. Llama 3.1 8B won on both counts that mattered: ROUGE-L 0.518 and BERTScore F1 0.938, at 12.3 seconds a transcript against DeepSeek’s 19.2. DeepSeek was dropped for the more interesting reason: it summarised the transcripts it had trained on well and unseen ones badly, and neither a shorter context nor more dropout moved it.
Deployment
The model is real; the GPU to serve it wasn’t
The tuned adapter is merged back into the base model and served behind a FastAPI endpoint, and that is the version the numbers above describe. What a student project can’t do is keep an 8B model resident on a GPU a live demo can reach, so the deployed build sends the same prompt to a hosted Llama and expects the same JSON back. That’s a hosting constraint rather than a design decision, and it costs nothing structurally: the dashboard is written against the contract, so the endpoint can come back without anything above it changing.
At a glance
- Scope
- Meeting bot, dataset, fine-tuned model, dashboard
- Model
- Llama 3.1 8B, 4-bit, LoRA adapter
- Dataset
- 105 transcripts, 35 per language
- Result
- ROUGE-L 0.518, BERTScore F1 0.938
My part: The capture and transcript side: bot join/leave controls, live transcript fetching and storage, plus the summarisation and follow-up-question calls layered on top of it. Final-year project, four of us and our guide; a teammate built the React shell.