How Hiring Agent Handles Negative Signals and Deductions: A Code-Level Guide
Hiring Agent applies negative signals by storing them as positive values in a Pydantic Deductions model, then subtracting the total from the aggregated score before displaying warnings in the CLI and exporting to CSV.
The interviewstreet/hiring-agent repository provides an LLM-powered resume evaluation system that scores candidates across multiple categories. Understanding how Hiring Agent handles negative signals requires examining the data structures in models.py, the aggregation logic in score.py, and the export formatting in transform.py.
The Deductions Data Model in models.py
Deductions are formally defined as a Pydantic model that stores a positive floating-point number representing the penalty amount. According to the source code, the total field uses a ge=0 constraint because the value is stored as a positive number but applied as a negative adjustment during scoring.
In models.py (lines 236-242), the Deductions class is implemented as follows:
class Deductions(BaseModel):
total: float = Field(ge=0,
description="Total deduction points (stored as positive, applied as negative)")
reasons: str = Field(description="Reasons for deductions")
This design ensures type safety while keeping the mathematical operation explicit: the evaluator subtracts this positive value from the running total.
How Deductions Are Applied in the Scoring Pipeline
The scoring logic in score.py aggregates positive scores first, then applies penalties. After summing category scores and adding any bonus points, the evaluator checks for the presence of a deductions attribute and subtracts the total field.
In score.py (lines 57-64), the flow follows this strict sequence:
# Add bonus points
if hasattr(evaluation, "bonus_points") and evaluation.bonus_points:
total_score += evaluation.bonus_points.total
# Subtract deductions
if hasattr(evaluation, "deductions") and evaluation.deductions:
total_score -= evaluation.deductions.total
Key implementation detail: The code uses defensive hasattr checks to ensure backward compatibility with evaluation objects that may not contain deduction data.
Reporting Deductions in CLI Output and CSV Exports
When presenting results to users, the system surfaces deductions with visual warnings in the terminal and structured fields in CSV exports.
Console Display
In score.py (lines 131-140), the print_evaluation_results function checks for non-zero deductions and renders them with a warning icon:
if (hasattr(evaluation, "deductions") and evaluation.deductions
and evaluation.deductions.total > 0):
print(f"\n⚠️ DEDUCTIONS: -{evaluation.deductions.total}")
if evaluation.deductions.reasons:
print(f" {evaluation.deductions.reasons}")
CSV Export Serialization
For data pipeline integration, transform.py (lines 720-727) maps deductions to dedicated columns. If no deductions exist, the fields default to zero and empty strings:
if evaluation and hasattr(evaluation, "deductions"):
csv_row["deductions"] = evaluation.deductions.total
csv_row["deduction_reasons"] = evaluation.deductions.reasons
else:
csv_row["deductions"] = 0
csv_row["deduction_reasons"] = ""
This guarantees consistent schema across all exported rows, regardless of whether the LLM identified negative signals for a specific candidate.
Complete Working Example
The following example demonstrates how to construct an EvaluationData object containing deductions and render the results using the same functions called by the Hiring Agent CLI:
from models import EvaluationData, Scores, CategoryScore, BonusPoints, Deductions
# Example evaluation data that includes a deduction
example = EvaluationData(
scores=Scores(
open_source=CategoryScore(score=30, max=35, evidence="Contributed to 5 repos"),
self_projects=CategoryScore(score=25, max=30, evidence="Built 3 apps"),
production=CategoryScore(score=20, max=25, evidence="2 years in SaaS"),
technical_skills=CategoryScore(score=8, max=10, evidence="Python, Go"),
),
bonus_points=BonusPoints(total=5, breakdown="Extra certifications"),
deductions=Deductions(total=4, reasons="Missing senior‑level leadership experience"),
key_strengths=["Strong open‑source contributions"],
areas_for_improvement=["Leadership experience"],
)
# Print the formatted results (calls the function used by the CLI)
from score import print_evaluation_results
print_evaluation_results(example, candidate_name="Alice Example")
Running this snippet produces output including the penalty notification:
⚠️ DEDUCTIONS: -4
Missing senior‑level leadership experience
Summary
- Data Structure: Deductions are encapsulated in the
DeductionsPydantic model inmodels.py, storing a positivetotaland stringreasons. - Score Calculation: The evaluator in
score.pysubtracts the deduction total after adding bonus points but before finalizing the score. - User Interface: Negative signals render with a warning icon (⚠️) in CLI output, showing both the numeric penalty and explanatory text.
- Data Export:
transform.pyserializes deductions intodeductionsanddeduction_reasonsCSV columns, defaulting to zero and empty strings when absent.
Frequently Asked Questions
What triggers a deduction in Hiring Agent?
Deductions are generated by the LLM evaluator when it identifies negative signals in a resume, such as missing critical experience, employment gaps, or lack of required technical skills. The model returns a Deductions object with the penalty amount and justification text.
Why are deduction values stored as positive numbers?
The total field in the Deductions model uses a positive value with a ge=0 constraint to maintain clarity in the data layer. The scoring logic explicitly subtracts this value (total_score -= evaluation.deductions.total), making the mathematical operation transparent in the codebase rather than hiding it in the data model.
How are deductions displayed when there are no negative signals?
When no deductions exist or the total is zero, the CLI output skips the warning section entirely. In CSV exports, the deductions column contains 0 and deduction_reasons contains an empty string, ensuring consistent column presence across all output rows.
Can deductions be customized in the evaluation schema?
Yes. Since Deductions is a standard Pydantic model defined in models.py, you can extend it with additional fields (such as severity levels or category tags) and update the serialization logic in transform.py and the display handlers in score.py to accommodate custom negative signal types.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →