Redrob-hackathon / lib /hiring_intent.py
Mohit0708's picture
Initial commit
7b833a7
Raw
History Blame Contribute Delete
10.2 kB
"""
lib/hiring_intent.py — V6 Hiring Intent Engine
Infers WHY the company is hiring, not just WHAT skills they need.
A hiring manager doesn't think "I want Python" — they think
"I need someone who can own production systems" or "I need a founding engineer."
The intent drives downstream weight adjustments, feature emphasis,
and reasoning quality.
Outputs a HiringIntent dataclass with:
- philosophy: list of hiring philosophy tags
- ownership_expectation: how much ownership is expected
- shipping_culture: scrappy vs methodical
- depth_requirement: specialist vs generalist
- scale_requirement: startup vs large scale
- team_context: founding vs established
- independence: how independently they expect work
- mentorship: whether they expect mentoring
- primary_need: the core thing this hire must deliver
"""
from __future__ import annotations
import re
from dataclasses import dataclass, field
from lib.jd_parser import get_jd, JDUnderstanding
@dataclass
class HiringIntent:
"""Structured inference of WHY the company is hiring."""
# Hiring philosophy tags (multiple can apply)
philosophy: list[str] = field(default_factory=list)
# Primary thing this hire must deliver
primary_need: str = "general"
# How much ownership is expected (0.0-1.0)
ownership_expectation: float = 0.5
# Shipping culture
shipping_culture: str = "balanced" # scrappy, methodical, balanced
# Depth vs breadth
depth_requirement: str = "generalist" # specialist, generalist, t_shaped
# Scale context
scale_requirement: str = "mid_scale" # startup_scale, mid_scale, large_scale
# Team context
team_context: str = "established" # founding, early, growing, established
# Independence expected (0.0-1.0)
independence: float = 0.5
# Whether they expect mentoring juniors
mentorship: bool = False
# Raw signals extracted from JD
raw_signals: dict = field(default_factory=dict)
# ---------------------------------------------------------------------------
# Intent signal detectors
# ---------------------------------------------------------------------------
_PHILOSOPHY_PATTERNS = {
"startup_builder": [
r"founding", r"first\s+hire", r"early\s+stage", r"build\s+from\s+scratch",
r"ship\s+a\s+working", r"scrappy", r"0\s+to\s+1", r"ground\s+up",
],
"hands_on_ic": [
r"hands\s+on", r"write\s+code", r"coding", r"implement",
r"no\s+pure\s+research", r"not\s+a\s+research", r"product.engineering",
],
"fast_shipper": [
r"ship\s+in\s+a\s+week", r"fast", r"quickly", r"rapid",
r"iterate", r"agile", r"move\s+fast",
],
"research_heavy": [
r"research", r"paper", r"publication", r"novel", r"state.of.the.art",
r"cutting.edge", r"advance\s+the\s+field",
],
"platform_engineer": [
r"platform", r"infrastructure", r"scalable", r"system\s+design",
r"architecture", r"end.to.end",
],
"specialist_depth": [
r"deep\s+technical\s+depth", r"expertise\s+in", r"specialist",
r"deep\s+knowledge", r"domain\s+expert",
],
"team_leader": [
r"mentor", r"lead", r"senior", r"guide", r"coach",
r"team\s+of", r"manage\s+engineers",
],
"customer_facing": [
r"customer", r"user.facing", r"production", r"real\s+users",
r"live\s+traffic", r"on.call",
],
"scale_focused": [
r"at\s+scale", r"large.scale", r"millions?\s+of\s+users", r"high\s+throughput",
r"low\s+latency", r"distributed",
],
}
_OWNERSHIP_PATTERNS = {
"founding": [r"founding", r"first\s+hire", r"build\s+the", r"own\s+the"],
"senior_ic": [r"senior", r"staff", r"lead", r"own", r"independent"],
"team_lead": [r"lead\s+a\s+team", r"manage", r"mentor", r"guide"],
"contributor": [r"contribute", r"part\s+of", r"join", r"work\s+with"],
}
_SHIPPING_PATTERNS = {
"scrappy": [r"ship\s+in\s+a\s+week", r"scrappy", r"just\s+ship\s+it", r"willing\s+to\s+ship"],
"methodical": [r"rigorous", r"systematic", r"process", r"methodology", r"thorough"],
"balanced": [], # default
}
_DEPTH_PATTERNS = {
"specialist": [r"deep\s+technical\s+depth", r"specialist", r"expert\s+in", r"deep\s+dive"],
"generalist": [r"full\s+stack", r"generalist", r"versatile", r"broad"],
"t_shaped": [r"deep\s+in\s+one", r"broad\s+knowledge", r"t.shaped", r"depth.*breadth"],
}
_SCALE_PATTERNS = {
"startup_scale": [r"startup", r"early\s+stage", r"small\s+team", r"series\s+[ab]"],
"mid_scale": [r"scale", r"growing", r"product.company", r"thousands"],
"large_scale": [r"millions", r"enterprise", r"global", r"large.scale"],
}
_TEAM_PATTERNS = {
"founding": [r"founding\s+team", r"first\s+engineer", r"employee\s+#"],
"early": [r"early\s+team", r"small\s+team", r"core\s+team"],
"growing": [r"growing\s+team", r"scaling\s+team", r"hiring\s+team"],
"established": [r"team\s+of\s+\d+", r"large\s+team", r"department"],
}
def _detect_signals(jd_text: str) -> dict[str, list[str]]:
"""Detect all intent signals from JD text."""
signals = {}
# Philosophy
signals["philosophy"] = []
for tag, patterns in _PHILOSOPHY_PATTERNS.items():
if any(re.search(p, jd_text, re.IGNORECASE) for p in patterns):
signals["philosophy"].append(tag)
# Ownership
signals["ownership"] = []
for level, patterns in _OWNERSHIP_PATTERNS.items():
if any(re.search(p, jd_text, re.IGNORECASE) for p in patterns):
signals["ownership"].append(level)
# Shipping culture
signals["shipping"] = []
for culture, patterns in _SHIPPING_PATTERNS.items():
if culture == "balanced":
continue
if any(re.search(p, jd_text, re.IGNORECASE) for p in patterns):
signals["shipping"].append(culture)
# Depth
signals["depth"] = []
for depth, patterns in _DEPTH_PATTERNS.items():
if any(re.search(p, jd_text, re.IGNORECASE) for p in patterns):
signals["depth"].append(depth)
# Scale
signals["scale"] = []
for scale, patterns in _SCALE_PATTERNS.items():
if any(re.search(p, jd_text, re.IGNORECASE) for p in patterns):
signals["scale"].append(scale)
# Team context
signals["team"] = []
for ctx, patterns in _TEAM_PATTERNS.items():
if any(re.search(p, jd_text, re.IGNORECASE) for p in patterns):
signals["team"].append(ctx)
return signals
def _compute_ownership_expectation(signals: dict) -> float:
"""Compute ownership expectation level from signals."""
levels = signals.get("ownership", [])
if "founding" in levels:
return 0.95
if "senior_ic" in levels:
return 0.80
if "team_lead" in levels:
return 0.70
return 0.50
def _determine_primary_need(jd: JDUnderstanding, signals: dict) -> str:
"""Determine the primary thing this hire must deliver."""
# Check for explicit production ownership
if "customer_facing" in signals.get("philosophy", []):
return "production_systems"
if "platform_engineer" in signals.get("philosophy", []):
return "platform_engineering"
if "research_heavy" in signals.get("philosophy", []):
return "research"
if "startup_builder" in signals.get("philosophy", []):
return "build_from_scratch"
if "specialist_depth" in signals.get("philosophy", []):
return "specialist_contribution"
return "general"
def _compute_independence(jd_text: str, signals: dict) -> float:
"""How independently is this person expected to work?"""
score = 0.5 # baseline
indie_patterns = [
(r"independently", 0.15), (r"own\s+initiative", 0.10),
(r"async", 0.08), (r"self.directed", 0.10),
(r"decides?\s+quickly", 0.08), (r"disagrees?\s+openly", 0.05),
(r"minimal\s+supervision", 0.10), (r"autonom", 0.10),
]
for pattern, bonus in indie_patterns:
if re.search(pattern, jd_text, re.IGNORECASE):
score = min(1.0, score + bonus)
return score
# ---------------------------------------------------------------------------
# Main API
# ---------------------------------------------------------------------------
def analyze(jd: JDUnderstanding | None = None) -> HiringIntent:
"""
Analyze a JD to infer hiring intent.
Returns a HiringIntent dataclass that drives downstream weight adjustments,
feature emphasis, and reasoning.
"""
if jd is None:
jd = get_jd()
jd_text = jd.raw_text.lower()
signals = _detect_signals(jd_text)
# Build the intent
philosophy = signals.get("philosophy", [])
if not philosophy:
philosophy = ["general"]
ownership_levels = signals.get("ownership", [])
ownership_expectation = _compute_ownership_expectation(signals)
shipping_cultures = signals.get("shipping", [])
shipping_culture = shipping_cultures[0] if shipping_cultures else "balanced"
depths = signals.get("depth", [])
depth_requirement = depths[0] if depths else "generalist"
scales = signals.get("scale", [])
scale_requirement = scales[0] if scales else "mid_scale"
teams = signals.get("team", [])
team_context = teams[0] if teams else "established"
independence = _compute_independence(jd_text, signals)
mentorship = bool(
re.search(r"mentor|guide|coach|senior\s+to|teach", jd_text, re.IGNORECASE)
)
primary_need = _determine_primary_need(jd, signals)
return HiringIntent(
philosophy=philosophy,
primary_need=primary_need,
ownership_expectation=ownership_expectation,
shipping_culture=shipping_culture,
depth_requirement=depth_requirement,
scale_requirement=scale_requirement,
team_context=team_context,
independence=independence,
mentorship=mentorship,
raw_signals=signals,
)
# Singleton
_intent_cache: HiringIntent | None = None
def get_intent() -> HiringIntent:
"""Get the hiring intent (cached after first analysis)."""
global _intent_cache
if _intent_cache is None:
_intent_cache = analyze()
return _intent_cache