The Problem
A product manager pings you: "Legal wants to know if the new resume-screening feature is 'high risk' under the EU AI Act." The honest answer at most companies is: nobody knows, because nobody has actually read Annex III, and the question gets forwarded to outside counsel who bills by the hour to answer something that a five-minute structured lookup should have caught in the design review.
This is the recurring failure mode with the EU AI Act: engineering teams either ignore it entirely (risky — it's not optional, and fines run up to the greater of €35 million or 7% of global annual turnover for the most serious violations) or treat every AI feature as a legal black box requiring outside counsel (slow and expensive). Neither is necessary. The Act's classification logic is genuinely mechanical for most cases — you can build a checklist that gets you 90% of the way to the right answer, and know precisely when the remaining 10% actually needs a lawyer.
The core thing to internalize: the Act classifies by use case, not by technology. Using an LLM doesn't make something high risk. Using a simple logistic regression model to screen job applicants does — because the risk is about the consequence to a person (losing access to employment), not the sophistication of the model producing it.
The four risk tiers
| Tier | What triggers it | Compliance path |
|---|
| Unacceptable | Article 5 prohibited practices: social scoring by public authorities, real-time remote biometric ID in public spaces for law enforcement (narrow exceptions), subliminal/manipulative techniques causing harm, exploiting vulnerabilities of specific groups, untargeted facial-recognition scraping, predictive policing based solely on profiling | None — cannot be placed on the market or used in the EU |
| High | Annex III use-case domains: biometric ID, critical infrastructure, education/vocational training, employment/worker management, essential private/public services (credit, insurance, benefits), law enforcement, migration/asylum/border control, justice/democratic processes | Risk management system, data governance, technical documentation, logging, human oversight, conformity assessment, CE marking, EU database registration, post-market monitoring |
| Limited | Transparency-obligation use cases: chatbots/conversational AI, synthetic or deepfake content generation, emotion recognition or biometric categorization | Disclose AI interaction to users; label synthetic content; disclose emotion-recognition/biometric-categorization use |
| Minimal | Everything else | No mandatory obligations beyond existing law; voluntary codes of conduct encouraged |
Notice the ordering matters: check for prohibited practices first — if a system is prohibited, its tier is settled regardless of what domain it operates in. Then check Annex III domain membership. Only if neither applies do you check for transparency triggers. A system can look superficially like a "chatbot" (limited risk) but actually be doing employment screening (high risk) — the domain check has to win.
GPAI obligations are a separate track, not a fifth tier
General-purpose AI model providers — the companies building foundation models other companies build on top of — have their own obligation set that applies regardless of what any downstream application does with the model: technical documentation, a training-data content summary, and a copyright-compliance policy for every GPAI model, plus adversarial testing, systemic-risk mitigation, and incident reporting for models above a compute threshold or designated by the Commission as carrying systemic risk. These obligations become fully applicable in August 2026. A minimal-risk internal tool can be built on a GPAI model that itself carries systemic-risk obligations — always check both axes, not just the application-level tier.
A team is building a feature that uses an LLM to draft personalized responses to loan applicants, including automated approve/deny recommendations that loan officers can override but rarely do in practice. What risk tier applies, and why?
Credit and access to essential services is an Annex III high-risk domain — the classification is driven by the use case (deciding who gets credit) not by whether a human is nominally in the loop. A human-override option that's rarely exercised in practice is a documentation problem for the human-oversight requirement, not a reason to reclassify the system as lower risk; regulators and auditors specifically look for 'rubber stamp' oversight as a red flag. It's not unacceptable risk — social scoring by public authorities is a distinct, narrower prohibited practice under Article 5 and doesn't cover private credit decisions.
Turning annex III into a classifier
The engineering task is building a structured description of what a system does — not what it's called or what model powers it — and running it through the Act's actual decision order: prohibited practice check, then Annex III domain check, then transparency-trigger check, then GPAI check as a separate pass. That's the shape of the code below.
A frontier lab releases an open-weight foundation model with training compute well above the systemic-risk threshold, and a startup fine-tunes it into a minimal-risk internal documentation search tool. Whose obligation is the systemic-risk adversarial testing and incident reporting?
GPAI obligations attach to whoever is the provider of the general-purpose model — in this case the frontier lab that trained it, regardless of the license terms it's released under. The startup's downstream application can be correctly classified as minimal risk based on what THEIR product does (internal doc search), but that classification has no bearing on the model provider's separate systemic-risk obligations, which are triggered by the model's training compute and apply upstream. Open-weight release does not exempt a provider from GPAI obligations — it changes distribution mechanics, not regulatory status.
Build
Build it
The code artifact lesson_07_02_eu_ai_act.py implements an EU AI Act risk-tier classifier:
AISystemDescription — a structured description of an AI system's use case: domain, Article 5 prohibited-practice flags, transparency-trigger flags, and GPAI-model flagsclassify_risk_tier() — applies the Act's decision order: prohibited practice, then Annex III domain, then transparency trigger, else minimalobligations_for_tier() — the concrete obligation list for each tiergpai_obligations() — the separate GPAI track, including the systemic-risk compute threshold check- Five sample systems spanning all four tiers plus a GPAI model, run through the full classifier
"""
lesson_07_02_eu_ai_act.py
An EU AI Act risk-tier classifier.
Given a structured description of an AI system's use case, classifies it
into one of the Act's four risk tiers — unacceptable, high, limited,
minimal — using the criteria actually laid out in the Act (Article 5
prohibited practices, Annex III high-risk use-case categories,
transparency-obligation use cases), and lists the resulting obligations.
Also evaluates general-purpose AI (GPAI) model obligations, which sit on
a separate track layered on top of the risk-tier system.
Reference: EU AI Act (https://artificialintelligenceact.eu/).
This is a teaching approximation of the Act's classification logic, not
a substitute for a legal conformity assessment. Real classification
requires legal review — this tool is the first-pass engineering triage
that tells you whether you need that review urgently.
Usage:
python lesson_07_02_eu_ai_act.py
No external dependencies, no network calls, no API keys required.
"""
from __future__ import annotations
from dataclasses import dataclass, field
from enum import Enum
class RiskTier(Enum):
UNACCEPTABLE = "Unacceptable risk (prohibited)"
HIGH = "High risk"
LIMITED = "Limited risk (transparency obligations)"
MINIMAL = "Minimal risk"
# Annex III high-risk use-case domains (abridged to the categories most
# relevant to software/AI teams — the Act's actual Annex III list is longer
# and more detailed, and is amended over time by the Commission).
HIGH_RISK_DOMAINS = {
"biometric_identification",
"critical_infrastructure",
"education_vocational_training",
"employment_worker_management",
"essential_private_public_services", # e.g. credit scoring, insurance pricing, benefits eligibility
"law_enforcement",
"migration_asylum_border_control",
"justice_democratic_processes",
}
# GPAI systemic-risk compute threshold used in the Act (measured in
# cumulative training compute, FLOPs). Models at or above this threshold —
# or specifically designated by the Commission — carry additional
# systemic-risk obligations on top of the baseline GPAI transparency duties.
GPAI_SYSTEMIC_RISK_FLOPS_THRESHOLD = 1e25
@dataclass
class AISystemDescription:
name: str
description: str
domain: str = "none" # one of HIGH_RISK_DOMAINS, or "none"
# Article 5 prohibited-practice flags
social_scoring_by_public_authority: bool = False
real_time_remote_biometric_id_law_enforcement_public_space: bool = False
subliminal_or_manipulative_technique: bool = False
exploits_vulnerabilities_of_specific_groups: bool = False
untargeted_facial_recognition_scraping: bool = False
predictive_policing_solely_on_profiling: bool = False
# Transparency-obligation (limited-risk) use cases
is_chatbot_or_conversational_ai: bool = False
generates_synthetic_or_deepfake_content: bool = False
is_emotion_recognition_or_biometric_categorization: bool = False
# GPAI track (evaluated independently of the risk tier above)
is_gpai_model: bool = False
gpai_training_compute_flops: float | None = None
gpai_designated_systemic_risk_by_commission: bool = False
@dataclass
class Classification:
tier: RiskTier
tier_rationale: str
obligations: list[str] = field(default_factory=list)
gpai_obligations: list[str] = field(default_factory=list)
def classify_risk_tier(system: AISystemDescription) -> tuple[RiskTier, str]:
"""Article 5 prohibited practices take priority over everything else."""
if system.social_scoring_by_public_authority:
return RiskTier.UNACCEPTABLE, "Social scoring by a public authority (Article 5)"
if system.real_time_remote_biometric_id_law_enforcement_public_space:
return RiskTier.UNACCEPTABLE, (
"Real-time remote biometric identification in public spaces for "
"law enforcement purposes (Article 5, narrow exceptions require "
"prior judicial or administrative authorization)"
)
if system.subliminal_or_manipulative_technique:
return RiskTier.UNACCEPTABLE, "Subliminal or manipulative technique causing harm (Article 5)"
if system.exploits_vulnerabilities_of_specific_groups:
return RiskTier.UNACCEPTABLE, "Exploiting vulnerabilities of a specific group, e.g. age or disability (Article 5)"
if system.untargeted_facial_recognition_scraping:
return RiskTier.UNACCEPTABLE, "Untargeted scraping to build facial recognition databases (Article 5)"
if system.predictive_policing_solely_on_profiling:
return RiskTier.UNACCEPTABLE, "Predictive policing based solely on profiling a natural person (Article 5)"
if system.domain in HIGH_RISK_DOMAINS:
return RiskTier.HIGH, f"Falls within the Annex III high-risk domain: {system.domain}"
if (
system.is_chatbot_or_conversational_ai
or system.generates_synthetic_or_deepfake_content
or system.is_emotion_recognition_or_biometric_categorization
):
return RiskTier.LIMITED, "Triggers a transparency obligation (user must be informed they are interacting with AI, or that content is synthetic)"
return RiskTier.MINIMAL, "Does not match a prohibited practice, an Annex III domain, or a transparency-obligation use case"
def obligations_for_tier(tier: RiskTier) -> list[str]:
if tier is RiskTier.UNACCEPTABLE:
return [
"Must not be placed on the market, put into service, or used in the EU — no compliance path exists for this tier",
]
if tier is RiskTier.HIGH:
return [
"Establish and maintain a risk management system across the system's lifecycle",
"Apply data governance to training/validation/test data (relevance, representativeness, error checking)",
"Produce technical documentation sufficient for a conformity assessment",
"Implement automatic logging (record-keeping) covering the system's operation",
"Design for human oversight — a person must be able to understand and intervene in the system's outputs",
"Meet accuracy, robustness, and cybersecurity requirements appropriate to the use case",
"Complete a conformity assessment before market placement (self-assessment or third-party, depending on the specific Annex III category)",
"Affix CE marking and register the system in the EU high-risk AI database",
"Establish post-market monitoring and report serious incidents",
]
if tier is RiskTier.LIMITED:
return [
"Inform natural persons that they are interacting with an AI system, unless obvious from context",
"Label AI-generated or manipulated (deepfake) audio, image, video, or text content as artificially generated",
"Disclose the use of emotion recognition or biometric categorization systems to the people exposed to them",
]
return [
"No mandatory obligations under the AI Act beyond existing sector law",
"Voluntary codes of conduct are encouraged (e.g. adopting parts of the high-risk requirements proactively)",
]
def gpai_obligations(system: AISystemDescription) -> list[str]:
"""
GPAI obligations apply to providers of general-purpose AI models — the
foundation models teams build on top of, not the downstream application.
They apply on a separate track from the four risk tiers above and become
fully applicable in August 2026.
"""
if not system.is_gpai_model:
return []
obligations = [
"Maintain technical documentation of the model, including training and testing process and evaluation results",
"Provide documentation to downstream providers sufficient for them to understand the model's capabilities and limitations",
"Publish a sufficiently detailed summary of the content used for training",
"Put in place a policy to comply with EU copyright law",
]
is_systemic_risk = (
system.gpai_designated_systemic_risk_by_commission
or (
system.gpai_training_compute_flops is not None
and system.gpai_training_compute_flops >= GPAI_SYSTEMIC_RISK_FLOPS_THRESHOLD
)
)
if is_systemic_risk:
obligations += [
"Perform model evaluation, including adversarial testing, to identify and mitigate systemic risk",
"Assess and mitigate possible systemic risks at the EU level, including their sources",
"Track, document, and report serious incidents to the AI Office without undue delay",
"Ensure an adequate level of cybersecurity protection for the model and its infrastructure",
]
return obligations
def classify(system: AISystemDescription) -> Classification:
tier, rationale = classify_risk_tier(system)
return Classification(
tier=tier,
tier_rationale=rationale,
obligations=obligations_for_tier(tier),
gpai_obligations=gpai_obligations(system),
)
def render_report(system: AISystemDescription, result: Classification) -> str:
lines = [
f"=== {system.name} ===",
system.description,
"",
f"Risk tier: {result.tier.value}",
f"Rationale: {result.tier_rationale}",
"",
"Obligations:",
]
for o in result.obligations:
lines.append(f" - {o}")
if result.gpai_obligations:
lines.append("")
lines.append("GPAI obligations (separate track, effective August 2026):")
for o in result.gpai_obligations:
lines.append(f" - {o}")
return "\n".join(lines)
# ── Sample systems ────────────────────────────────────────────────────────────
SAMPLE_SYSTEMS = [
AISystemDescription(
name="Resume Screening Classifier",
description="Ranks job applicants and filters candidates before a human recruiter sees them.",
domain="employment_worker_management",
),
AISystemDescription(
name="Support Copilot Chatbot",
description="Customer-facing conversational assistant embedded in a support widget.",
is_chatbot_or_conversational_ai=True,
),
AISystemDescription(
name="Internal Spam Filter",
description="Classifies inbound support emails as spam vs. legitimate for internal triage.",
),
AISystemDescription(
name="Live Public CCTV Face Matching (Police)",
description="Real-time facial recognition against a watchlist run on public street camera feeds by a police department.",
real_time_remote_biometric_id_law_enforcement_public_space=True,
),
AISystemDescription(
name="Frontier Foundation Model (internal codename Helios)",
description="A general-purpose foundation model offered via API to third-party developers.",
is_gpai_model=True,
gpai_training_compute_flops=3e25,
),
]
if __name__ == "__main__":
print("EU AI ACT RISK-TIER CLASSIFICATION")
print("=" * 70)
for system in SAMPLE_SYSTEMS:
result = classify(system)
print()
print(render_report(system, result))
print()
print(
"Takeaway: risk tier and GPAI status are independent axes. A company "
"can ship a MINIMAL-risk chatbot application built on top of a GPAI "
"model that itself carries systemic-risk obligations — the "
"obligations attach to whoever plays the 'provider' role for each "
"layer, and a compliance gap analysis has to check both."
)