cd /news/ai-tools/architectural-breakdown-i-asked-ai-t… · home › topics › ai-tools › article
[ARTICLE · art-142134] src=dev.to ↗ pub= topic=ai-tools verified=true sentiment=· neutral

Architectural Breakdown: I Asked AI to Improve My Resume. It Started Asking Me for Numbers Instead.

A developer built a production-grade resume optimization framework that forces every resume bullet into a structured, numeric format before any language model processes it, arguing that prose-only prompts yield generic output. The system defines a ResumeBullet dataclass capturing action verb, metric type, before/after values, timeframe and context, then applies a deterministic four-dimension scoring function covering quantification strength, verb specificity, temporal precision and impact scope ahead of the LLM stage.

by read5 min views1 publishedSep 30, 2026

Most people treat AI resume tools like a magic wand. They paste their draft, hit generate, and hope for better phrasing. But when you actually push a language model into doing meaningful optimization work, it quickly becomes clear that prose alone is insufficient. The model starts asking for quantitative signals because that is what it needs to make decisions that matter.

This realization changed how I approach automated resume optimization entirely. What follows is not a beginner's tutorial. It is a production-grade framework for building a system that forces your resume through real metrics before any AI touches it.

A standard prompt looks something like this:

response = client.chat.completions.create(
    model="gpt-4o",
    messages=[
        {"role": "user", "content": "Improve my resume bullet points."}
    ]
)

The output will always be vague advice wrapped in corporate language. Phrases like "synergized cross-functional teams" or "spearheaded initiative" are exactly the kind of noise that makes resumes unreadable to both humans and applicant tracking systems. Without numbers anchoring each claim, the model has no signal to ground its improvements.

The shift happens when you force every resume bullet into a structured numeric format before it ever reaches the language model:

from dataclasses import dataclass
from typing import Optional

@dataclass
class ResumeBullet:
    action_verb: str          # "Led", "Built", "Reduced"
    metric_type: str          # "percentage", "absolute", "ratio"
    value_before: Optional[float]  # baseline measurement
    value_after: Optional[float]   # outcome measurement
    timeframe: Optional[str]       # "Q3 2024", "6 months"
    context: str                 # the what and why

    @property
    def has_numbers(self) -> bool:
        return self.value_before is not None and self.value_after is not None

    @property
    def impact_ratio(self) -> Optional[float]:
        if not self.has_numbers:
            return None
        return (self.value_after - self.value_before) / abs(self.value_before)

This structure forces a painful but necessary step: you must extract or estimate the real numbers behind every claim. That exercise alone improves your resume more than any AI paraphrase ever could.

Once you have quantified bullets, you can build a deterministic scoring function that runs before the LLM stage:

import json

def calculate_bullet_score(bullet: ResumeBullet) -> dict:
    """
    Produces a composite score from four independent dimensions.
    Higher scores indicate stronger, more credible accomplishments.
    """
    scores = {}

    if bullet.has_numbers:
        scores["quantification"] = min(bullet.impact_ratio * 10, 100)
    else:
        scores["quantification"] = 0  # No numbers means this slot is empty

    strong_verbs = {
        "built": 90, "architected": 95, "designed": 85,
        "reduced": 88, "optimized": 82, "launched": 78,
        "led": 70, "managed": 55, "helped": 30
    }
    scores["verb_strength"] = strong_verbs.get(bullet.action_verb.lower(), 40)

    score_time = 50 if bullet.timeframe else 0
    if bullet.timeframe and "quarter" in bullet.timeframe.lower():
        score_time = 70
    if bullet.timeframe and "month" in bullet.timeframe.lower():
        score_time = 85
    scores["temporal_precision"] = score_time

    scale_scores = {"team": 50, "department": 70, "company": 90, "individual": 30}
    scores["scope_score"] = scale_scores.get(bullet.context.split()[0].lower(), 40)

    composite = sum(scores.values()) / len(scores)
    return {"scores": scores, "composite": round(composite, 1)}

This pipeline gives you something most resume tools never provide: a transparent audit trail showing exactly which bullet points are weak and why.

Here is the architecture that actually works in practice:

def optimize_resume_stage_one(raw_bullets: list[dict]) -> list[ResumeBullet]:
    """
    STAGE 1: Structural enforcement.
    Converts freeform bullets into quantified objects.
    Rejects or flags any bullet missing hard numbers.
    """
    structured = []
    for raw in raw_bullets:
        bullet = ResumeBullet(
            action_verb=raw.get("verb", ""),
            metric_type=raw.get("type", ""),
            value_before=raw.get("before"),
            value_after=raw.get("after"),
            timeframe=raw.get("timeframe"),
            context=raw.get("context", "")
        )
        if not bullet.has_numbers:
            print(f"[FLAG] Needs quantification: {bullet.context}")
        structured.append(bullet)
    return structured

def optimize_resume_stage_two(
    bullets: list[ResumeBullet],
    client,
    job_description: str
) -> list[str]:
    """
    STAGE 2: LLM rewriting.
    The model now has concrete numbers to preserve and emphasize.
    It rephrases around the data instead of inventing fluff.
    """
    prompt = f"""
    Optimize these quantified resume bullets for ATS readability.
    Job description: {job_description}

    RULES:
    1. NEVER remove or soften existing numbers
    2. Replace weak verbs with specific action terms
    3. Keep each bullet under 2 lines
    4. Front-load the metric whenever possible

    Bullets:
    {[f"{b.action_verb} | {b.value_before} → {b.value_after} | {b.context}"
      for b in bullets]}

    Return ONLY the optimized bullets as a JSON array.
    """
    response = client.chat.completions.create(
        model="claude-sonnet-4-20250514",
        messages=[{"role": "user", "content": prompt}],
        temperature=0.3  # Low temperature preserves factual accuracy
    )
    return json.loads(response.choices[0].message.content)

When I ran this two-stage system against my own resume, the results were startling. Stage One flagged seven out of eleven bullets as missing hard numbers. Some of those gaps were honest oversights. Others were deliberate choices I had made because quantifying felt harder than writing.

The model did not need to invent metrics. It needed the original data point to exist in the first place. Once those numbers were present, Stage Two produced dramatically different quality output. The AI stopped padding language and started sharpening it.

before = "Improved API response times significantly"
after_optimized = "Reduced p99 API latency from 840ms to 120ms by implementing Redis caching layer and query batch optimization"

The lesson extends far beyond resume writing. Any time you ask an AI to improve something, the quality of your output is bounded by the quality of your structured input. Garbage in produces polished garbage. Numbers in produces sharper output.

Start by auditing every claim on your resume against this simple question: can I attach a measurable number to this statement? If the answer is no, you have found the exact bullet point that is weakening your entire document. Fix it there first. Then let the AI handle the language.

The system below captures the full pipeline end to end:

def full_resume_pipeline(
    bullets: list[dict],
    job_desc: str,
    client
) -> dict:
    """
    Complete optimization pipeline from raw input to scored output.
    """
    stage_one = optimize_resume_stage_one(bullets)
    scores = [calculate_bullet_score(b) for b in stage_one]

    qualified = [
        b for b, s in zip(stage_one, scores)
        if s["composite"] >= 40
    ]
    low_scoring = [
        b for b, s in zip(stage_one, scores)
        if s["composite"] < 40
    ]

    stage_two = optimize_resume_stage_two(qualified, client, job_desc)

    return {
        "qualified_bullets": stage_two,
        "flagged_for_review": [b.context for b in low_scoring],
        "score_summary": {s["composite"] for s in scores}
    }

Quantify first. Optimize second. The order matters more than most people realize.

What is one bullet point on your resume right now that feels impactful but cannot survive being checked against a number?

── more in #ai-tools 4 stories · sorted by recency
── more on @gpt-4o 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/architectural-breakd…] indexed:0 read:5min 2026-09-30 · —