0Pricing
Claude Architect · 강의

집계 지표가 실패를 숨기는 경우

전체 97%라는 수치가 한 문서 유형의 실패를 가릴 수 있습니다

집계 지표가 실패를 숨기는 경우은(는) CoddyKit의 무료 Claude Architect 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Claude Architect 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Claude Architect 강의에는 총 4개의 강의가 포함되어 있습니다.

이 강의의 일부는 아직 번역되지 않았으며 영어로 표시됩니다.

The Headline Number Lies

Your extraction pipeline reports 97% accuracy. The dashboard is green, the stakeholders are happy, and someone proposes turning off human review entirely.

Stop. A single aggregate number is one of the most dangerous artifacts in a production Claude system. That 97% is an average over a mixed population. Averages are excellent at smoothing away exactly the failures that hurt you most.

In this lesson you'll learn why aggregate-only accuracy is a documented anti-pattern, and what to measure instead before you automate human oversight away.

Anatomy of a Misleading Average

Imagine your pipeline processes three document types in equal volume. The blended score is 97%. Looks uniform, right?

But blended numbers are weighted by volume, not by risk. A small, high-stakes document type can be drowned out entirely. The aggregate tells you nothing about where the 3% of errors land — and in practice, errors are almost never spread evenly.

# Same 97% aggregate, two very different realities
docs = {
    "invoices":   {"n": 1000, "correct": 990},  # 99.0%
    "receipts":   {"n": 1000, "correct": 985},  # 98.5%
    "contracts":  {"n": 1000, "correct": 935},  # 93.5%
}
total = sum(d["n"] for d in docs.values())
hits = sum(d["correct"] for d in docs.values())
print(f"aggregate = {hits/total:.1%}")  # 97.0% — hides contracts
for name, d in docs.items():
    print(name, f"{d['correct']/d['n']:.1%}")

One Failing Document Type

Here is the failure mode the exam wants you to recognize: aggregate accuracy can hide poor performance on a specific document type or field.

Contracts at 93.5% might be your highest-value, highest-liability documents. A handful of wrong clauses extracted from contracts can cost more than thousands of correct receipts ever saved. Yet the dashboard happily reports 97% and invites you to automate.

The number isn't wrong. It's just answering the wrong question. "How good are we on average?" is rarely the question that matters. "Where are we weakest, and how much does that weakness cost?" is.

Field-Level Failures Hide Even Deeper

It gets subtler. Even within a single document type, the failure may live in one field. A contract extractor can nail the parties, dates, and addresses — and quietly mangle the termination_clause or liability_cap 20% of the time.

Average those fields together and you still see a comfortable score. So stratify by both axes: by document type AND by field. The cell where a critical document type meets a critical field is where your real risk concentrates.

# Stratify a labeled validation set by (doc_type, field)
import collections
stats = collections.defaultdict(lambda: [0, 0])  # [correct, total]
for row in labeled_validation_set:
    key = (row["doc_type"], row["field"])
    stats[key][1] += 1
    stats[key][0] += int(row["pred"] == row["gold"])

for (doc, field), (ok, n) in sorted(stats.items()):
    acc = ok / n
    flag = "  <-- REVIEW" if acc < 0.95 else ""
    print(f"{doc:10} {field:18} {acc:.1%} (n={n}){flag}")

Stratified Random Sampling

How do you surface these hidden cells? Not by sampling 100 random documents — random sampling reproduces your volume distribution, so rare-but-critical types get almost no coverage.

Use stratified random sampling: partition the population into strata (document type, source, field criticality), then sample randomly within each stratum. Now your low-volume contract type gets a statistically meaningful sample instead of three lucky draws.

This is the exam's recommended technique for catching what aggregates hide.

from collections import defaultdict
import random

def stratified_sample(docs, key_fn, per_stratum=50):
    strata = defaultdict(list)
    for d in docs:
        strata[key_fn(d)].append(d)
    sample = []
    for stratum, items in strata.items():
        k = min(per_stratum, len(items))
        sample += random.sample(items, k)  # random WITHIN stratum
    return sample

audit_set = stratified_sample(all_docs, key_fn=lambda d: d["doc_type"])

Field-Level Confidence, Calibrated

Stratified sampling tells you where you stand offline. To decide per document, in production whether to auto-accept or route to a human, you need field-level confidence calibrated on a labeled validation set.

"Calibrated" is the load-bearing word. A raw confidence-looking score means nothing until you've checked, against ground truth, that documents marked 0.9 are actually correct ~90% of the time. Calibrate first; only then can a threshold like "auto-accept above 0.97" mean what you think it means.

# Calibrate, then gate per field
def route_extraction(field_name, value, confidence, thresholds):
    # thresholds[field] derived from a LABELED validation set,
    # tighter for high-stakes fields
    if confidence >= thresholds[field_name]:
        return "auto_accept"
    return "human_review"

thresholds = {
    "vendor_name":      0.92,
    "liability_cap":    0.99,   # critical -> stricter gate
    "termination_clause": 0.99,
}

Self-Correction Surfaces Discrepancies

Calibrated confidence isn't the only signal. For numeric extraction you can have the model expose its own work so you can catch errors deterministically.

Extract both calculated_total (summed from line items) and stated_total (the printed total). When they disagree, you've found a discrepancy no aggregate metric would ever reveal — a verifiable, document-level red flag that routes straight to review.

# Self-correction: extract both, compare deterministically
schema = {
  "type": "object",
  "properties": {
    "line_items": {"type": "array", "items": {"type": "number"}},
    "calculated_total": {"type": "number"},  # model sums line items
    "stated_total": {"type": "number"}       # printed on the doc
  },
  "required": ["line_items", "calculated_total", "stated_total"]
}

def needs_review(out):
    return abs(out["calculated_total"] - out["stated_total"]) > 0.01

Don't Retry What Calibration Reveals

When a low-confidence or discrepant document surfaces, be precise about the fix. Retry-with-feedback repairs format, structural, and arithmetic errors — send the original document, the wrong output, and the exact validation error back to the model.

But retry does not help when the information is simply absent from the source. If the contract never states a liability cap, no amount of re-prompting will conjure one — and a model that fabricates one is exactly the failure your metrics must catch. Absent data is an escalation case, not a retry case.

def handle_low_confidence(doc, output, error):
    if error.kind in ("format", "arithmetic", "schema"):
        # retry with original doc + wrong output + exact error
        return retry_with_feedback(doc, output, error)
    if error.kind == "absent":
        # info not in source -> never retry; route to human
        return escalate_to_human(doc, reason="field absent in source")

Schemas Must Not Force Fabrication

This connects to a structured-output rule that directly affects your metrics. Mark a schema field required only if it is always present. Never require a field that may be absent — the model will fabricate a value to satisfy the schema, and a fabricated value still counts as a confident answer.

That fabrication can sail straight past an aggregate accuracy check while corrupting your highest-stakes field. Make optional fields optional, and use an enum with an "other" value plus a free-text detail field for extensibility.

{
  "type": "object",
  "properties": {
    "vendor_name":   {"type": "string"},
    "liability_cap": {"type": ["number", "null"]},
    "doc_category": {
      "type": "string",
      "enum": ["invoice", "receipt", "contract", "other"]
    },
    "category_detail": {"type": "string"}
  },
  "required": ["vendor_name", "doc_category"]
}

Provenance Makes Failures Auditable

To investigate a hidden failure you must be able to trace any extracted claim back to its origin. Maintain claim-to-source mappings: source document name, the exact quote, location, and publication date.

When a stratified audit flags the contract stratum, provenance lets a reviewer jump straight to the quote that produced a bad liability_cap — instead of re-reading the whole document. Provenance turns "our metric looks off somewhere" into "this field, from this quote, on this page, is wrong."

extraction = {
    "field": "liability_cap",
    "value": 500000,
    "provenance": {
        "source_doc": "acme_msa_2026.pdf",
        "quote": "liability shall not exceed five hundred thousand dollars",
        "page": 7,
        "published": "2026-01-15"
    }
}

Decide on Evidence, Not Vibes

Put it together into an automation decision. You earn the right to reduce human oversight on a stratum only when the evidence for that specific stratum supports it.

  • Stratified audit shows the stratum meets target accuracy.
  • Field-level confidence is calibrated on labeled data.
  • Self-correction and provenance catch the residual errors.

And note what is not on this list: a model self-rated confidence (1-10) or sentiment score is a bad escalation trigger. Good triggers are threshold violations, policy gaps, no progress, and explicit human requests — not the model's own untrained self-assessment.

Quick Check: Reading the 97%

A scenario-style question on metrics and oversight.

Recap: Make Hidden Failures Visible

Key takeaways:

  • Aggregate accuracy hides per-type and per-field failures — it's volume-weighted, not risk-weighted.
  • Stratify before you trust: stratified random sampling by document type and field surfaces weak cells that random sampling misses.
  • Calibrate field-level confidence on a labeled validation set before using any threshold to auto-accept.
  • Self-correction (extract calculated_total and stated_total) and provenance (claim-to-source quote, page, date) catch and explain residual errors.
  • Don't require possibly-absent fields (fabrication) and don't escalate on self-rated confidence or sentiment — use threshold violations, policy gaps, no progress, and explicit human requests.

Earn automation per stratum, on evidence. The green dashboard is the start of the investigation, not the end.

자주 묻는 질문

“집계 지표가 실패를 숨기는 경우” 강의는 무료인가요?

네 — “집계 지표가 실패를 숨기는 경우” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Claude Architect 강의 전체를 잠금 해제할 수 있습니다. Claude Architect 강의에는 총 4개의 강의가 포함되어 있습니다.

“집계 지표가 실패를 숨기는 경우”에서 뭘 배우나요?

전체 97%라는 수치가 한 문서 유형의 실패를 가릴 수 있습니다 브라우저에서 직접 실행하는 실습 코드로 Claude Architect을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Claude Architect을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Claude Architect은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.

“집계 지표가 실패를 숨기는 경우” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Claude Architect 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Claude Architect 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 주장과 출처 매핑
  2. 충돌하는 데이터와 날짜
  3. 집계 지표가 실패를 숨기는 경우
  4. 층화 표본 추출 및 보정
← Claude Architect(으)로 돌아가기