A minimal Python CLI tool that reads an interview transcript text file and returns a structured recruiter-style summary using the Google Gemini API.
The implementation is intentionally simple: one transcript in, one Gemini request, validated JSON out.
.
├── summarizer.py
├── requirements.txt
├── .env.example
├── .gitignore
├── README.md
├── prompt_iterations.md
├── sample_outputs.md
├── sample_transcript_assignment_1.txt
├── sample_transcript_assignment_2.txt
└── outputs/
├── sample_transcript_assignment_1_summary.json
└── sample_transcript_assignment_2_summary.json
The CLI prints formatted JSON to stdout and can also save the same JSON to a file with --output.
{
"topics_covered": [
"topic 1",
"topic 2"
],
"profile": {
"role": "",
"level": "",
"justification": ""
},
"candidate_summary": ""
}Use Python 3.10 or newer. Python 3.9 may still run the script, but some dependencies now warn that Python 3.9 is past end of life.
Create and activate a virtual environment:
python3 -m venv .venv
source .venv/bin/activateInstall dependencies:
pip install -r requirements.txtCreate an API key from Google AI Studio:
- Open https://aistudio.google.com/
- Sign in with a Google account.
- Create an API key.
- Keep the key private.
- Do not commit real API keys.
Copy the environment template:
cp .env.example .envSet your key in .env:
GEMINI_API_KEY=your_real_api_key_hereThe CLI loads .env automatically with python-dotenv.
Run with the default assignment model, gemini-2.0-flash:
python summarizer.py path/to/transcript.txtSave JSON to a file:
python summarizer.py path/to/transcript.txt --output outputs/summary.jsonUse a different Gemini model when your API project has quota limitations:
python summarizer.py path/to/transcript.txt --model gemini-2.5-flash-liteYou can also set the model through an environment variable for one command:
GEMINI_MODEL=gemini-2.5-flash-lite python summarizer.py path/to/transcript.txtOr export it for the current shell session:
export GEMINI_MODEL=gemini-2.5-flash-lite
python summarizer.py path/to/transcript.txtThis repo includes two sample transcripts:
sample_transcript_assignment_1.txtsample_transcript_assignment_2.txt
Run and save both sample outputs:
python summarizer.py sample_transcript_assignment_1.txt \
--model gemini-2.5-flash-lite \
--output outputs/sample_transcript_assignment_1_summary.json
python summarizer.py sample_transcript_assignment_2.txt \
--model gemini-2.5-flash-lite \
--output outputs/sample_transcript_assignment_2_summary.jsonSaved sample JSON files:
outputs/sample_transcript_assignment_1_summary.jsonoutputs/sample_transcript_assignment_2_summary.json
Markdown copies of sample outputs are available in sample_outputs.md.
Run a syntax check:
python -m compileall -q -b summarizer.pyCheck the CLI arguments:
python summarizer.py --helpTest basic error handling with a missing file:
python summarizer.py missing_transcript.txtRun a live Gemini test with a sample transcript:
python summarizer.py sample_transcript_assignment_1.txt \
--model gemini-2.5-flash-lite \
--output outputs/sample_transcript_assignment_1_summary.jsonValidate the saved JSON:
python -m json.tool outputs/sample_transcript_assignment_1_summary.json
python -m json.tool outputs/sample_transcript_assignment_2_summary.jsonExpected behavior:
- Successful runs print formatted JSON to stdout.
- When
--outputis provided, the same JSON is saved to that file. - Invalid input or API failures print an error to stderr and exit with status code
1.
The assignment default is Google Gemini gemini-2.0-flash, called through the official google-genai SDK.
gemini-2.0-flash is appropriate for this task because transcript summarization is a deterministic extraction problem, not a retrieval or agent workflow. The request uses:
temperature=0.2response_mime_type="application/json"response_json_schemafor structured output- local JSON validation before printing or saving
The optional --model flag is included only for operational flexibility when a Gemini API project does not have quota for the default model.
The code is contained in summarizer.py and uses these functions:
load_transcript()reads the transcript file.build_prompt()creates the recruiter-style extraction prompt.generate_summary()calls Gemini once.validate_response()parses and validates the JSON response.main()handles CLI arguments, output printing, and optional file writing.
The project does not use LangChain, agents, vector databases, embeddings, or RAG.
The prompt asks Gemini to:
- infer major interview themes
- infer likely role and seniority from evidence
- provide a concise justification
- avoid inventing experience or seniority
- state uncertainty when evidence is weak
- return valid JSON only
Prompt development notes are documented in prompt_iterations.md.
The CLI handles common failures:
- missing transcript file
- empty transcript file
- non-UTF-8 transcript text
- missing
GEMINI_API_KEY - Gemini API errors
- invalid or malformed JSON responses
Errors are printed to stderr and the command exits with status code 1.
What surprised me most was how much of the output quality came from small prompt constraints rather than code complexity. The first versions could produce reasonable-looking summaries, but they were too willing to infer seniority or role confidence from weak evidence. Adding explicit grounding rules, uncertainty language, and a strict JSON schema improved the output more than adding another processing step would have.
With another day, I would improve validation beyond schema shape. Right now validate_response() confirms that required fields exist and have the right types, but it does not check whether the summary is actually grounded in the transcript. A useful next step would be a lightweight post-processing check that flags unsupported seniority claims, overly broad role labels, or missing uncertainty language when the transcript has limited evidence. I would also add unit tests with mocked Gemini responses and a few regression transcripts to make prompt changes safer.
The final prompt is intentionally simple and works well for one-call recruiter summaries, but it has limits. Long transcripts may need chunking, and the model can still compress nuance too aggressively. The prompt also asks for evidence-based justification, but it does not require exact transcript quotes or timestamps, so traceability is limited. The output should be treated as a recruiter aid, not a source of truth for hiring decisions.
- Very long transcripts may exceed model input limits.
- Summary quality depends on transcript quality.
- JSON validation checks structure, not factual perfection.
- Role and seniority inference should be treated as recruiter-supporting context, not a hiring decision.
- API quota depends on the selected Gemini model and Google project configuration.