Articles 9 through 15 are the technical backbone of the EU AI Act. They define what high-risk AI systems must do for risk management, data governance, documentation, record-keeping, transparency, human oversight, and robustness. This guide breaks each article into engineering terms with code examples, compliance checks, and a standards crosswalk to ISO 42001 and NIST AI RMF.
pip install air-blackbox && air-blackbox comply --scan . -v to find gaps across all seven articles.Article 9 requires that you establish and implement a risk management system covering the entire lifecycle of your high-risk AI system. You must identify and analyze potential harms, implement mitigation measures, and continuously monitor risks as the system operates. The regulation does not mandate a specific methodology but requires that risk management be documented, systematic, and updated throughout the system's lifecycle.
In practice, this means five things in your codebase: threat modeling (identify how the system could cause harm), risk classification (document severity and likelihood for each risk), mitigation strategies (implement controls for risks above your tolerance threshold), monitoring and alerting (detect when risks are materializing in production), and lifecycle updates (rerun risk analysis on new releases, distribution shift, or vulnerabilities).
from air_blackbox import scan_code, risk_level
agent_code = """
from langchain import LLMChain
from some_tool import access_credit_database
class LoanApprovalAgent:
def approve_loan(self, applicant_data):
# RISK: This agent has access to credit data
# Severity: HIGH (financial harm to individuals)
# Likelihood: MEDIUM (human review exists but intermittent)
credit_score = access_credit_database(applicant_data['id'])
# Guard rail: log every decision for audit
self.logger.info(f"Loan decision: {applicant_data['name']}")
# Mitigation: require human approval for borderline cases
if 600 < credit_score < 700:
return {"decision": "HOLD", "reason": "Requires human review"}
return {"decision": "APPROVED" if credit_score > 700 else "DENIED"}
"""
# Scan returns findings aligned with Article 9
results = scan_code(agent_code)
for finding in results.article_9_findings:
print(f"Risk: {finding.description}")
print(f"Severity: {finding.severity}")
print(f"Mitigation: {finding.mitigation_found}")
The pattern: identify the risk (high-stakes financial decision), classify it (HIGH severity, MEDIUM likelihood), implement a guard rail (logging), and a mitigation (human review trigger for borderline cases).
Article 10 requires that training data, validation data, and test data for high-risk AI systems meet strict quality standards. You must document where your data came from, check it for bias and discrimination, ensure it is representative of the populations your system will serve, and continue assessing data quality in production. If real-world data diverges significantly from training distribution, your risk assessments need updating.
The concrete technical obligations are: data sourcing documentation (provenance, collection methods, exclusions), bias testing (per-group performance on protected characteristics), data quality metrics (label agreement, missing values, freshness), representativeness assessment (compare training distribution to deployment population), and drift monitoring (statistical tests comparing live data to training data).
import pandas as pd
from sklearn.metrics import confusion_matrix
import hashlib
class DataGovernance:
def __init__(self):
self.data_lineage = {}
self.bias_report = {}
def document_dataset(self, df, source, collection_date, annotators=None):
"""Record where data came from and how it was created."""
data_hash = hashlib.sha256(
pd.util.hash_pandas_object(df, index=True).values
).hexdigest()
self.data_lineage[data_hash] = {
"source": source,
"collection_date": collection_date,
"row_count": len(df),
"missing_values": df.isnull().sum().to_dict(),
"annotators": annotators,
}
def measure_fairness(self, y_true, y_pred, protected_attr):
"""Measure performance disparity across protected groups."""
results = {}
for group in protected_attr.unique():
mask = protected_attr == group
cm = confusion_matrix(y_true[mask], y_pred[mask])
fpr = cm[0, 1] / (cm[0, 0] + cm[0, 1])
results[group] = {"false_positive_rate": fpr}
self.bias_report = results
return results
def check_drift(self, current_data, training_data, threshold=0.1):
"""Detect when live data diverges from training distribution."""
from scipy.stats import ks_2samp
for col in current_data.columns:
stat, p_value = ks_2samp(current_data[col], training_data[col])
if p_value < threshold:
print(f"DRIFT DETECTED: {col} p-value={p_value}")
governance = DataGovernance()
governance.document_dataset(
train_df,
source="Company HR records Jan-Dec 2025",
collection_date="2025-12-31",
annotators=["[email protected]", "[email protected]"]
)
Article 11 requires comprehensive technical documentation sufficient for regulators, conformity assessors, and your own teams to understand how the system works, what assumptions it makes, and what its limitations are. The documentation must cover the system's purpose, algorithm architecture, training process, performance metrics, known limitations, and risk mitigations. This is the model card, data sheet, and architecture document combined.
In engineering terms, you need four documents: architecture and algorithm documentation (model type, structure, mathematical formulation), training process documentation (hyperparameters, optimization, convergence criteria), evaluation and performance documentation (accuracy, fairness metrics, per-group breakdowns), and limitations and failure modes (conditions where the system performs poorly).
import json
from datetime import datetime
class ModelCard:
def __init__(self, model_name, version):
self.name = model_name
self.version = version
self.created_date = datetime.now().isoformat()
def document_architecture(self, model_object):
return {
"model_type": type(model_object).__name__,
"parameters": model_object.count_params()
if hasattr(model_object, 'count_params') else "N/A",
"layers": str(model_object) if hasattr(model_object, '__str__')
else "See architecture diagram",
}
def document_training(self, training_config):
return {
"dataset": training_config.get("data_source"),
"training_steps": training_config.get("epochs"),
"batch_size": training_config.get("batch_size"),
"optimizer": training_config.get("optimizer"),
"learning_rate": training_config.get("learning_rate"),
"loss_function": training_config.get("loss"),
}
def document_evaluation(self, metrics_dict):
return {
"overall_accuracy": metrics_dict.get("accuracy"),
"precision": metrics_dict.get("precision"),
"recall": metrics_dict.get("recall"),
"fairness_gap": metrics_dict.get("max_fairness_disparity"),
"robustness_score": metrics_dict.get("adversarial_accuracy"),
}
def document_limitations(self, known_issues):
return {
"performance_by_group": known_issues.get("disparities"),
"failure_modes": known_issues.get("edge_cases"),
"data_requirements": known_issues.get("distribution_assumptions"),
}
def to_json(self):
return json.dumps(self.__dict__, indent=2, default=str)
# Generate and save for Article 11 compliance
card = ModelCard("BiasDetector", "1.0.0")
card.architecture = card.document_architecture(model)
card.training = card.document_training(training_cfg)
card.evaluation = card.document_evaluation(test_metrics)
card.limitations = card.document_limitations(known_issues)
with open("technical_documentation.json", "w") as f:
f.write(card.to_json())
Article 12 requires that high-risk AI systems automatically keep tamper-proof records of their operation, sufficient to demonstrate compliance and enable auditing. Records must be retained for at least three years. Critically, these records must be tamper-evident: if a regulator requests logs, they must have confidence the logs are genuine and unchanged. This is where HMAC-SHA256 cryptographic chains enter the picture.
Three technical capabilities are required: comprehensive logging (every input, prediction, override, and error with metadata), tamper-evidence (HMAC-SHA256 chains where each record signs the previous one, making modification detectable), and long-term retention (immutable storage for at least three years).
import hmac
import hashlib
import json
from datetime import datetime
import uuid
class TamperProofAuditLog:
"""Article 12: Tamper-proof logging with HMAC-SHA256 chains."""
def __init__(self, secret_key):
self.secret_key = secret_key.encode()
self.chain = []
self.last_signature = None
def log_decision(self, event_type, model_name, input_data,
output, user_id, confidence=None):
entry = {
"id": str(uuid.uuid4()),
"timestamp": datetime.utcnow().isoformat(),
"event_type": event_type,
"model_version": model_name,
"user_id": user_id,
"input_hash": hashlib.sha256(
json.dumps(input_data, sort_keys=True).encode()
).hexdigest(),
"output": output,
"confidence": confidence,
"previous_signature": self.last_signature,
}
entry_string = json.dumps(entry, sort_keys=True)
signature = hmac.new(
self.secret_key, entry_string.encode(), hashlib.sha256
).hexdigest()
entry["signature"] = signature
self.chain.append(entry)
self.last_signature = signature
return entry
def verify_chain_integrity(self):
"""Detect if any log entry has been tampered with."""
for i, entry in enumerate(self.chain):
stored_signature = entry.pop("signature")
entry_string = json.dumps(entry, sort_keys=True)
expected = hmac.new(
self.secret_key, entry_string.encode(), hashlib.sha256
).hexdigest()
if stored_signature != expected:
return False, f"Entry {i} has been modified"
if i > 0 and entry["previous_signature"] != self.chain[i-1]["signature"]:
return False, f"Entry {i} is not linked correctly"
entry["signature"] = stored_signature
return True, "Chain is intact"
# Usage
logger = TamperProofAuditLog(secret_key="your-secret-from-kms")
logger.log_decision(
event_type="loan_approval_decision",
model_name="CreditRisk-v2.1",
input_data={"credit_score": 750, "income": 80000},
output="APPROVED", user_id="agent-001", confidence=0.92
)
is_valid, message = logger.verify_chain_integrity()
print(f"Audit log valid: {is_valid} - {message}")
Why standard logs fail Article 12: Datadog, Splunk, and CloudWatch lack tamper-evidence, completeness detection, and structured auditability. Anyone with write access can alter records. HMAC chains make modification detectable. See our deep dive: Integrity Is Not Completeness.
Article 14 requires that high-risk AI systems be designed so humans can understand what the AI recommends, why it recommends it, and can override or reject the recommendation. The human must have the authority, the information, and the technical capability to intervene at any point. This breaks down into five technical obligations: explainability, confidence thresholds that trigger review, override mechanisms, monitoring of override rates, and access controls for reviewers.
from enum import Enum
from datetime import datetime
import logging
class DecisionGate(Enum):
APPROVED = "approved"
HELD_FOR_REVIEW = "held_for_review"
REJECTED = "rejected"
class HumanOversightGate:
"""Article 14: Require human review for high-risk decisions."""
def __init__(self, confidence_threshold=0.85):
self.confidence_threshold = confidence_threshold
self.review_queue = []
self.override_log = []
self.logger = logging.getLogger(__name__)
def evaluate_decision(self, model_output, confidence,
feature_importance, case_id):
if confidence < self.confidence_threshold:
return self.require_review(
case_id, model_output, confidence,
reason=f"Confidence {confidence} below {self.confidence_threshold}",
explanation=feature_importance
)
return DecisionGate.APPROVED
def require_review(self, case_id, recommendation, confidence,
reason, explanation):
review_item = {
"case_id": case_id,
"recommendation": recommendation,
"confidence": confidence,
"reason_for_review": reason,
"explanation": explanation,
"created_at": datetime.utcnow().isoformat(),
"reviewed": False,
}
self.review_queue.append(review_item)
self.logger.info(f"Case {case_id} requires human review: {reason}")
return DecisionGate.HELD_FOR_REVIEW
def human_review(self, case_id, reviewer_id, final_decision, notes):
"""Human makes the final decision. AI does not override."""
review_item = next(
(item for item in self.review_queue
if item["case_id"] == case_id), None
)
if not review_item:
raise ValueError(f"Case {case_id} not found")
review_item["reviewed"] = True
review_item["reviewer_id"] = reviewer_id
review_item["final_decision"] = final_decision
review_item["reviewed_at"] = datetime.utcnow().isoformat()
if final_decision != review_item["recommendation"]:
self.override_log.append({
"case_id": case_id,
"ai_recommendation": review_item["recommendation"],
"human_decision": final_decision,
"reviewer_id": reviewer_id,
})
return final_decision
def get_override_rate(self):
total = len([i for i in self.review_queue if i["reviewed"]])
overrides = len(self.override_log)
return (overrides / total * 100) if total > 0 else 0
# Usage
gate = HumanOversightGate(confidence_threshold=0.80)
status = gate.evaluate_decision(
model_output="DENIED", confidence=0.72,
feature_importance=importance, case_id="loan-12345"
)
if status == DecisionGate.HELD_FOR_REVIEW:
gate.human_review("loan-12345", "[email protected]", "APPROVED",
"Strong credit history despite low model score")
Article 15 requires high-risk AI systems to achieve and maintain appropriate accuracy, robustness, and cybersecurity. Your AI must work correctly under normal conditions, fail gracefully under adversarial conditions, and be protected against attack and misuse. The regulation does not specify exact accuracy thresholds (these depend on the use case) but requires you to measure accuracy, document it, test robustness, and implement security controls including adversarial testing and prompt injection defense.
Three technical areas: accuracy and performance (per-group metrics with a performance floor below which the system should not operate), robustness testing (adversarial inputs, distribution shift, missing data, boundary conditions), and cybersecurity (prompt injection defense, input validation, secure access controls).
import numpy as np
from air_blackbox import check_prompt_injection
class RobustnessTest:
"""Article 15: Test accuracy, robustness, and cybersecurity."""
def __init__(self, model, X_test, y_test):
self.model = model
self.X_test = X_test
self.y_test = y_test
self.results = {}
def test_accuracy(self):
predictions = self.model.predict(self.X_test)
accuracy = (predictions == self.y_test).mean()
self.results["accuracy"] = accuracy
return accuracy
def test_adversarial_robustness(self, perturbation=0.1):
X_perturbed = self.X_test + np.random.normal(
0, perturbation, self.X_test.shape)
predictions = self.model.predict(X_perturbed)
adv_accuracy = (predictions == self.y_test).mean()
drop = self.results["accuracy"] - adv_accuracy
self.results["adversarial_accuracy"] = adv_accuracy
self.results["robustness_drop"] = drop
if drop > 0.15:
print(f"WARNING: Accuracy dropped {drop*100:.1f}% under perturbation")
return adv_accuracy
def test_edge_cases(self):
cases = {
"all_zeros": np.zeros_like(self.X_test[:1]),
"all_ones": np.ones_like(self.X_test[:1]),
"max_values": np.max(self.X_test) * np.ones_like(self.X_test[:1]),
}
for name, inp in cases.items():
try:
self.model.predict(inp)
self.results[f"edge_{name}"] = "handled"
except Exception as e:
self.results[f"edge_{name}"] = f"error: {e}"
# Run tests
test = RobustnessTest(model, X_test, y_test)
print(f"Baseline: {test.test_accuracy():.2%}")
print(f"Adversarial: {test.test_adversarial_robustness():.2%}")
test.test_edge_cases()
Articles 9-15 are not independent requirements - they form a connected governance framework where each article feeds the next. Article 9 risk analysis identifies what can go wrong. Articles 10 and 11 ensure the system is built safely with trusted data and thorough documentation. Article 12 adds accountability with tamper-evident logging. Article 14 puts humans in control. Article 15 ensures quality and security. When a regulator audits your system, they walk through all six in sequence.
| Article | Responsibility | Outcome |
|---|---|---|
| Article 9 | Identify and manage risks | Risk register with mitigation strategies |
| Article 10 | Ensure training data is trustworthy | Data sheet with bias testing and representativeness |
| Article 11 | Document the system comprehensively | Technical documentation and model card |
| Article 12 | Keep tamper-proof records | HMAC-SHA256 audit trail, 3-year retention |
| Article 14 | Enable human oversight | Review gates, override mechanisms, monitoring |
| Article 15 | Ensure accuracy, robustness, security | Performance metrics, adversarial testing, injection defense |
If you operate internationally or in regulated sectors, you may need to comply with ISO 42001 (AI Management System) or NIST AI Risk Management Framework alongside the EU AI Act. The three frameworks align closely. Implementing Articles 9-15 gets you most of the way to all three frameworks simultaneously - the documents you create (risk register, data sheet, technical documentation, audit logs) become artifacts for all three.
| EU AI Act Article | ISO 42001 Section | NIST AI RMF Function |
|---|---|---|
| Article 9: Risk Management | Clause 8.2: Risk management | GOVERN: Risk management framework |
| Article 10: Data Governance | Clause 8.4: Data management | MEASURE: Data quality assessment |
| Article 11: Documentation | Clause 8.3: Model management | MEASURE: Model documentation |
| Article 12: Audit Logs | Clause 8.5: Monitoring | GOVERN: Audit and accountability |
| Article 14: Human Oversight | Clause 8.6: Human oversight | GOVERN: Human review mechanisms |
| Article 15: Robustness | Clause 8.7: Performance evaluation | MEASURE: Performance and robustness |
AIR Blackbox maps scan findings to all three frameworks from a single command: air-blackbox comply --scan . -v outputs EU AI Act article, ISO 42001 clause, and NIST AI RMF function for every finding.
Install AIR Blackbox and run a compliance scan with two commands. The scanner checks all seven articles and gives you a detailed report of what passes, what warns, and what fails - with specific fix hints and code locations for every finding.
pip install air-blackbox
air-blackbox comply --scan . -v
After implementing fixes, re-scan and iterate. Add the scan to your CI/CD pipeline to maintain compliance as your code evolves:
# Fail the build if critical findings are present
air-blackbox comply --scan . --fail-on FAIL
Find compliance gaps before an auditor does. One command, all seven articles.
pip install air-blackbox
air-blackbox comply --scan . -v
Articles 9-15 define the technical obligations for high-risk AI systems: risk management (9), data governance (10), technical documentation (11), tamper-evident record-keeping (12), transparency (13), human oversight (14), and accuracy, robustness, and cybersecurity (15). Together they form a complete governance framework enforced from August 2, 2026.
Article 12 requires that high-risk AI systems automatically keep tamper-proof records of their operation for at least three years. HMAC-SHA256 audit chains satisfy this by creating cryptographic links between records so that any modification becomes immediately detectable.
The three frameworks align closely. Article 9 maps to ISO 42001 Clause 8.2 and NIST GOVERN. Article 10 maps to Clause 8.4 and NIST MEASURE. Article 12 maps to Clause 8.5 and NIST GOVERN. Implementing Articles 9-15 gets you most of the way to all three frameworks simultaneously.
AIR Blackbox is an open-source scanner that checks Python AI projects against 51+ requirements across Articles 9 through 15. Install with pip install air-blackbox and run air-blackbox comply --scan . -v. It maps findings to EU AI Act, ISO 42001, NIST AI RMF, and Colorado SB 24-205.
Article 14 requires that high-risk AI systems enable human oversight. Humans must understand what the AI recommends and why, override or reject recommendations, and have the authority and technical capability to intervene. This includes explainability, confidence thresholds, override mechanisms, and monitoring of override rates.
Last updated: April 17, 2026. This article is reviewed and updated monthly to reflect the latest regulatory developments and scanner capabilities.