← Back to all files
File 01 · Case StudyStatus: Live

Lead Qualification Agent

A pipeline that resolves messy inbound leads against real company data, scores them against a transparent ICP model, and routes them — so an SDR never has to eyeball 400 form fills a week.

Classification
Systems design · SQL as business logic · Applied AI judgment
Stack
Python · SQLite · SQL · Ollama (local LLM) · SEC EDGAR API
Subject
Inbound triage, SLA decay, and the protection of an AE's time.
The Brief

Inbound funnels drown sales teams in volume. Most of it is noise — students, competitors, wrong-fit companies — and buried inside is the handful of real buyers who go stale while triage takes three days.

This is the exact problem I own at Tipalti every week, running point on AP-automation deal qualification. So I built the system I wished existed: one that scores leads against a rubric a RevOps lead could read and retune — without asking anyone to triage by hand.

Design Thesis

Deterministic where possible, agentic where valuable.

Scoring is SQL — auditable, testable, retunable without touching code. Judgment — resolving an ambiguous company name, reading intent from a vague message — is the LLM's job, and only where it earns its keep. Everywhere else, the AI-first instinct is the wrong instinct.

The Pipeline

One lead, five stages.

A lead flows left to right. Solid stages are deterministic. One step — and only one — hands off to a local LLM, and only when the answer is genuinely ambiguous.

lead_0047
Stage 01
INGEST
Raw form fill arrives — name, email, company string, free-text message.
DETERMINISTIC
Stage 02
RESOLVE
Messy company string matched to real data (SEC EDGAR, Wikipedia, site scrape).
LLM · ONLY WHERE AMBIGUOUS
Stage 03
ENRICH
Firmographics attached: size, industry, public/private, geography. Free-tier only.
DETERMINISTIC
Stage 04
SCORE
CORE · SQL RUBRIC
Transparent SQL rubric → ICP score + priority tier. The heart of the system.
DETERMINISTIC
Stage 05
ROUTE
P1 to an AE now, P2 to nurture, P3 archived with a reason.
DETERMINISTIC
SECURITY LAYERINJECTION / SPAM / PERSONA — DETERMINISTIC BY DESIGNRuns across all five stages. A security classifier shouldn't depend on the judgment of the model an attacker is trying to manipulate.
P1
AE, now
Hot buyer, routed to a human immediately.
P2
Nurture
Real, but not ready. Into a sequence.
P3
Archive, with reason
Logged and closed — every rejection auditable.
Solid ink = deterministic Dashed oxblood = LLM-assisted
The Evidence

The numbers do the bragging.

99.7%
Company name resolution
Zero wrong guesses. Bad resolution poisons everything downstream.
99.1%
P1 precision
A false-positive P1 wastes an AE’s morning. This protects their time.
100%
P1 recall
Nothing hot got missed.
10/10
Prompt-injection resistance
Adversarial attempts refused. Published with evidence, not just claimed.
What This Demonstrates

Three things, defended with the code.

01

Design a scoring model — and defend it under real data.

The rubric wasn't set once and left alone. A hand-audit against real judgment calls found company size overriding strong buying signals — a CFO with budget approved shouldn't score lower just because their company is large. I rebuilt the scorer around that finding, which exposed and fixed a second gap. The rubric that shipped is the one that survived being wrong twice.

02

Know where to put the AI — and where not to.

Injection detection, spam filtering, persona classification: deterministic, on purpose. The LLM only runs where the task is genuinely ambiguous — disambiguating a company name with two plausible matches, or reading intent from a message that could honestly go either way. Nowhere else.

03

Test for the failure, not just the happy path.

Every phase turned up a real bug. None showed up by accident — they showed up because I went looking, and the fixes are documented alongside the code.

FIELD NOTE · RESOLVE

A text-cleaning step erased the exact detail meant to prevent an ambiguous match — the fix that broke the fix.

FIELD NOTE · ENRICH

An SEC data-extraction gap silently failed for every foreign-incorporated public company. No error — just missing data.

FIELD NOTE · SCORE

A similarity feature looked plausible but was quietly comparing companies with nothing in common.

EXHIBIT A — SCORING RUBRIC (SQL)
-- ICP scoring rubric: a RevOps lead can read and retune this without touching app code.
WITH signals AS (
SELECT lead_id,
title_seniority_score, -- VP+ / CxO weighted highest
budget_authority_flag, -- explicit budget language in message
company_size_band, -- 1 (SMB) .. 5 (enterprise)
industry_fit_score
FROM enriched_leads
)
SELECT lead_id,
-- FIX: a buying signal must be able to override size.
(budget_authority_flag * 40)
+ (title_seniority_score * 30)
+ LEAST(company_size_band * 4, 20) -- size capped, never dominant
+ (industry_fit_score * 10) AS icp_score,
CASE WHEN icp_score >= 70 THEN 'P1'
WHEN icp_score >= 40 THEN 'P2'
ELSE 'P3' END AS priority_tier
FROM signals;

Highlighted: the fix that lets a CFO with approved budget outrank a large-company size penalty. Placeholder fragment — the shipped rubric lives in the repo.

Notes & Provenance
  • No customer data touches this project. Every lead is synthetic; every company is real.
  • Free-tier enrichment only (SEC EDGAR, Wikipedia, direct site scraping). No paid vendor data.
  • The LLM runs locally via Ollama, swappable to any hosted provider in one line.