The Problem
A mid-size SaaS company closes a deal with a large enterprise customer. The customer's security team sends over a vendor questionnaire, and one section asks: "Describe your alignment with the NIST AI Risk Management Framework." The engineering team that built the product's generative AI features has never heard the phrase. They have a decent eval suite and a threat model from a security review six months ago — but no idea how to map that into an answer a procurement reviewer will accept, and no idea what's actually missing.
This happens constantly. NIST AI RMF 1.0 is voluntary — there's no certifying body, no exam, no badge. But it has become the default shared vocabulary between engineering, legal, procurement, and regulators in the US, because it's free, comprehensive, and (unlike a lot of compliance material) genuinely maps to real engineering practice. It's referenced by federal agencies implementing AI executive orders, cited in state-level AI legislation, and it's the document NIST used as the backbone for the official crosswalk NIST published between the RMF and ISO/IEC 42001 — the two are designed to interoperate, not compete.
The gap isn't understanding that the framework exists. It's translation: turning "establish policies and procedures for third-party risk" into a specific file in your repo, a specific CI check, a specific person who owns it. That translation is the actual job of an AI governance engineer, and it's what this lesson builds.
The four functions
NIST AI RMF 1.0 organizes AI risk management into four functions, each with several categories and subcategories detailed further in the companion RMF Playbook:
| Function | What it covers | What "done" looks like |
|---|
| Govern | Culture, policy, accountability, and organizational structures for managing AI risk | A written risk policy, assigned roles (RACI), a maintained risk register, incident escalation paths |
| Map | Understanding the AI system's context: intended use, data, stakeholders, and known limitations | A system context doc, dataset cards, a completed threat model, documented out-of-scope uses |
| Measure | Analyzing and tracking AI risks with quantitative or qualitative metrics | An automated eval suite, red-team results tracked over time, fairness metrics, drift monitoring |
| Manage | Allocating resources to respond to mapped and measured risks | Deployment gates, rollback criteria, an incident response plan, decommissioning triggers |
Notice the shape: Govern and Map are mostly about documentation and structure — they answer "do we know what we're building and who's accountable for it." Measure is about evidence — did we actually run the tests and what did they say. Manage is about action — what happens when the evidence says something is wrong. A team that's strong on Map but weak on Manage has done a great job describing its system and a terrible job deciding what to do when it breaks.
RMF and ISO 42001 are designed to interoperate, not compete
NIST published an official crosswalk mapping AI RMF subcategories to ISO/IEC 42001 controls. If you're building toward ISO 42001 certification (Lesson 3), doing your RMF self-assessment first isn't wasted work — most of the Govern and Map evidence you produce here (policies, risk register, system context docs) is reusable as ISO 42001 audit evidence. Treat RMF as the free, fast-moving first pass and ISO 42001 as the certifiable, auditable version of the same underlying controls.
A team has a one-page system context doc describing their AI system's intended use and out-of-scope uses, a dataset card for their RAG corpus, and a completed threat model. They have no written AI risk policy, no assigned RACI, and no risk register. Which function are they strong in, and which are they missing entirely?
System context, dataset cards, and threat models are all Map artifacts — they characterize the system and its context. A written risk policy, RACI, and a risk register are Govern artifacts — they establish organizational accountability. This team has done real technical characterization work but has no organizational structure around AI risk at all. The fix is organizational (write the policy, assign owners), not more system documentation.
The generative AI profile: NIST-AI-600-1
The base RMF is deliberately generic — it applies to any AI system, from a fraud-detection classifier to an image generator. NIST-AI-600-1, the Generative AI Profile, keeps the same four functions but adds a set of risks that are specific to or significantly amplified by generative AI, including confabulation (hallucination presented as fact), information integrity (synthetic content, disinformation at scale), data privacy leakage through model outputs, dangerous or violent content generation, intellectual property risks, toxic or discriminatory outputs, and risks introduced by the AI value chain (foundation model providers, fine-tuning vendors, plugin/tool integrations).
If you're building on top of an LLM — which, in this course, you are — the Generative AI Profile is the more directly relevant document. It doesn't ask you to invent new controls from scratch; it asks you to run your existing Govern/Map/Measure/Manage controls against this specific risk list. A confabulation risk, for example, maps to a Measure control (does your eval suite test for factual accuracy and flag confident-but-wrong outputs) and a Manage control (is there a user-facing disclaimer or a human-in-the-loop gate for high-stakes outputs).
Turning RMF language into a checklist
The actual work of an assessment is answering a concrete question for each subcategory: implemented, partial, or not implemented — and for anything short of "implemented," what's the specific engineering ticket that closes it. That's what the code below does: a checklist of nineteen items spanning all four functions, each with a paired recommended control, scored against a sample system.
Your evaluation suite runs automatically in CI and blocks merges on safety regressions — this is solid Measure evidence. But there's no rule that a failing eval actually stops a deployment; a developer can override CI and ship anyway. What RMF function has the actual gap, and what's the fix?
This is a Manage gap. Measure is working correctly — the eval suite generates the evidence and would have caught the regression. The failure is that nothing forces action on that evidence: an override lets a deployment proceed despite a failing safety check. A policy stating overrides are "discouraged" (Govern) doesn't stop the click. The actual fix is a Manage-function control: remove override permissions for safety-critical checks, or require a named second approver to override, with the override itself logged as an incident.
Build
Build it
The code artifact lesson_07_01_nist_rmf.py implements a NIST AI RMF self-assessment tool:
RMFItem — a checklist item: which function it belongs to, the assessment question, and the recommended engineering control if unmetCHECKLIST — nineteen items spanning Govern (6), Map (5), Measure (4), and Manage (4)SystemAssessment — scores a system's answers per function and overall, and returns the list of open gapsrender_gap_report() — produces a markdown gap report with per-function scores, a maturity label, and a recommended control for every open item- A sample assessment of "Northwind Support Copilot," a generative AI customer-support agent with a $500 refund tool, with deliberately mixed answers so the report has real substance
"""
lesson_07_01_nist_rmf.py
A NIST AI Risk Management Framework (AI RMF 1.0) self-assessment tool.
Implements a structured checklist across the four RMF functions —
Govern, Map, Measure, Manage — scores an AI system description against
it, and produces a gap report that translates each unmet item into a
concrete engineering action. This is the artifact a governance/security
engineer actually produces before an enterprise deal, an audit, or an
internal launch review.
Reference: NIST AI RMF 1.0 (https://www.nist.gov/itl/ai-risk-management-framework)
and the Generative AI Profile, NIST-AI-600-1
(https://nvlpubs.nist.gov/nistpubs/ai/NIST.AI.600-1.pdf).
Usage:
python lesson_07_01_nist_rmf.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 Function(Enum):
GOVERN = "GOVERN"
MAP = "MAP"
MEASURE = "MEASURE"
MANAGE = "MANAGE"
class Status(Enum):
NOT_IMPLEMENTED = 0.0
PARTIAL = 0.5
IMPLEMENTED = 1.0
NOT_APPLICABLE = None
@dataclass
class RMFItem:
item_id: str
function: Function
question: str
recommended_control: str
# ── The checklist ────────────────────────────────────────────────────────────
# Nineteen items across the four RMF functions. Each one is phrased as a
# yes/partial/no question an assessor can actually answer, paired with the
# concrete engineering control that closes the gap if the answer isn't
# "implemented". This is the translation step most teams skip: RMF language
# ("establish policies for third-party risk") -> ticket ("maintain a vendor
# AI risk register with model card links and SLA review status").
CHECKLIST: list[RMFItem] = [
# GOVERN — policy, accountability, culture
RMFItem(
"policy-ai-risk-tolerance", Function.GOVERN,
"Has the organization documented AI risk tolerance and escalation "
"thresholds, signed off by leadership?",
"Write an AI risk policy doc with defined risk tiers and a named "
"sign-off chain; keep it as RISK_POLICY.md, reviewed every release.",
),
RMFItem(
"governance-roles-raci", Function.GOVERN,
"Are roles and responsibilities for AI risk management assigned "
"(RACI) across legal, security, ML engineering, and product?",
"Publish a RACI matrix and require its named approver on any PR "
"that touches the model risk register.",
),
RMFItem(
"third-party-risk", Function.GOVERN,
"Are third-party/vendor AI components (foundation models, "
"datasets, fine-tuning services) inventoried and risk-assessed?",
"Maintain a vendor AI risk register: model card link, license "
"terms, and last security review date per vendor.",
),
RMFItem(
"workforce-training", Function.GOVERN,
"Do staff who build or operate AI systems receive AI-risk "
"training appropriate to their role?",
"Add an AI-risk module to engineering onboarding; gate prod "
"model-deploy permissions on completing it.",
),
RMFItem(
"incident-escalation-path", Function.GOVERN,
"Is there a defined escalation path for AI incidents (bias "
"complaint, safety failure, security breach)?",
"Adopt an AI incident runbook with severity tiers wired into the "
"existing on-call/paging rotation (see Lesson 5).",
),
RMFItem(
"risk-register-maintained", Function.GOVERN,
"Is there a live AI system risk register reviewed on a fixed "
"cadence?",
"Track every production AI system in a risk register (GRC tool "
"or a reviewed spreadsheet) with a quarterly owner sign-off.",
),
# MAP — context, data, stakeholders, known limitations
RMFItem(
"system-context-doc", Function.MAP,
"Is the AI system's intended use, users, and deployment context "
"documented, including explicit out-of-scope uses?",
"Write a one-page system context doc: intended use, excluded "
"uses, deployment environment, and who signed off on it.",
),
RMFItem(
"data-provenance", Function.MAP,
"Is training/fine-tuning/retrieval data provenance and licensing "
"documented?",
"Attach a dataset card (sources, licenses, PII handling) to "
"every training set and every RAG corpus.",
),
RMFItem(
"impacted-stakeholders", Function.MAP,
"Are impacted individuals or groups identified, including "
"potentially vulnerable populations?",
"Run a stakeholder-impact worksheet pre-launch; flag any "
"disparate-impact-sensitive use case for the bias audit in "
"Lesson 4.",
),
RMFItem(
"third-party-model-limitations", Function.MAP,
"Are the known limitations of any third-party/foundation model "
"documented for this specific use case?",
"Link the provider's model/system card in your system context "
"doc and note failure modes relevant to your use case.",
),
RMFItem(
"threat-model-completed", Function.MAP,
"Has a threat model (STRIDE-for-AI or MITRE ATLAS-based) been "
"completed for this system?",
"Complete a threat model before Measure activities begin — this "
"is the Phase 0 deliverable, not optional here.",
),
# MEASURE — evaluation, testing, monitoring signals
RMFItem(
"eval-suite-exists", Function.MEASURE,
"Is there an automated evaluation suite (accuracy, safety, "
"regression) that runs before every model or prompt change "
"ships?",
"Wire the eval suite into CI as a merge gate — no model/prompt "
"change ships without a passing eval run attached.",
),
RMFItem(
"redteam-results", Function.MEASURE,
"Has the system been red-teamed (e.g., Garak, PyRIT) with "
"results tracked over time?",
"Run the Phase 6 red-team suite on a fixed cadence and track "
"finding counts as a trend metric, not a one-off report.",
),
RMFItem(
"bias-fairness-metrics", Function.MEASURE,
"Are fairness metrics (demographic parity, equalized odds) "
"measured for any system that makes or informs consequential "
"decisions about people?",
"Run the bias-audit pipeline from Lesson 4 on every retrain and "
"record the metrics next to the eval results.",
),
RMFItem(
"monitoring-drift", Function.MEASURE,
"Is production model behavior monitored for drift, anomalies, "
"or degradation?",
"Add drift/anomaly dashboards (Langfuse, Phoenix, or equivalent) "
"with alert thresholds tied to on-call.",
),
# MANAGE — response, deployment control, decommissioning
RMFItem(
"incident-response-plan", Function.MANAGE,
"Is there a documented AI incident response plan with defined "
"severities and regulatory-reporting triggers?",
"Adopt the incident report template from Lesson 5; pre-map "
"severities to reporting obligations before you need it.",
),
RMFItem(
"deployment-gate", Function.MANAGE,
"Is there a go/no-go deployment gate requiring risk sign-off "
"before release to production?",
"Add a release checklist that requires the latest Measure "
"results attached before a model promotion is approved.",
),
RMFItem(
"decommission-criteria", Function.MANAGE,
"Are criteria defined for rolling back or retiring an AI system "
"or model version?",
"Define explicit rollback triggers (eval regression beyond X%, "
"incident severity >= 2) in the deployment runbook.",
),
RMFItem(
"third-party-monitoring", Function.MANAGE,
"Is there ongoing monitoring for third-party model/API changes "
"that could affect this system's risk posture?",
"Subscribe to vendor model changelogs/deprecation notices; "
"re-run the eval suite on any upstream model version bump.",
),
]
@dataclass
class SystemAssessment:
system_name: str
system_description: str
answers: dict[str, Status] = field(default_factory=dict)
def score_for_function(self, fn: Function) -> tuple[float, int]:
items = [i for i in CHECKLIST if i.function == fn]
scored = [
self.answers.get(i.item_id, Status.NOT_IMPLEMENTED)
for i in items
]
scored = [s for s in scored if s is not Status.NOT_APPLICABLE and s.value is not None]
if not scored:
return 0.0, 0
total = sum(s.value for s in scored)
return (total / len(scored)) * 100, len(scored)
def overall_score(self) -> float:
function_scores = [self.score_for_function(fn)[0] for fn in Function]
return sum(function_scores) / len(function_scores)
def gaps(self) -> list[RMFItem]:
"""Items scored PARTIAL or NOT_IMPLEMENTED, i.e. anything not fully closed."""
out = []
for item in CHECKLIST:
status = self.answers.get(item.item_id, Status.NOT_IMPLEMENTED)
if status in (Status.NOT_IMPLEMENTED, Status.PARTIAL):
out.append(item)
return out
def maturity_label(score: float) -> str:
if score >= 90:
return "Managed"
if score >= 70:
return "Developing"
if score >= 40:
return "Ad hoc"
return "Absent"
def render_gap_report(assessment: SystemAssessment) -> str:
lines = [
f"# NIST AI RMF Gap Report — {assessment.system_name}",
"",
f"**System:** {assessment.system_description}",
"",
f"**Overall maturity score:** {assessment.overall_score():.0f}/100 "
f"({maturity_label(assessment.overall_score())})",
"",
"## Scores by function",
"",
"| Function | Score | Maturity |",
"|---|---|---|",
]
for fn in Function:
score, n = assessment.score_for_function(fn)
lines.append(f"| {fn.value} | {score:.0f}/100 ({n} items) | {maturity_label(score)} |")
lines += ["", "## Gaps and recommended controls", ""]
gaps = assessment.gaps()
if not gaps:
lines.append("No open gaps — every checklist item is fully implemented.")
else:
for item in gaps:
status = assessment.answers.get(item.item_id, Status.NOT_IMPLEMENTED)
lines.append(f"- **[{item.function.value} / {status.name}]** {item.question}")
lines.append(f" - Recommended control: {item.recommended_control}")
return "\n".join(lines)
# ── Sample assessment ────────────────────────────────────────────────────────
# "Northwind Support Copilot": a generative AI customer-support assistant
# with RAG over the knowledge base and a tool to issue refunds up to $500.
# Deliberately mixed answers so the gap report has real substance.
SAMPLE_ANSWERS: dict[str, Status] = {
"policy-ai-risk-tolerance": Status.PARTIAL,
"governance-roles-raci": Status.NOT_IMPLEMENTED,
"third-party-risk": Status.IMPLEMENTED,
"workforce-training": Status.NOT_IMPLEMENTED,
"incident-escalation-path": Status.PARTIAL,
"risk-register-maintained": Status.NOT_IMPLEMENTED,
"system-context-doc": Status.IMPLEMENTED,
"data-provenance": Status.IMPLEMENTED,
"impacted-stakeholders": Status.PARTIAL,
"third-party-model-limitations": Status.IMPLEMENTED,
"threat-model-completed": Status.IMPLEMENTED,
"eval-suite-exists": Status.IMPLEMENTED,
"redteam-results": Status.PARTIAL,
"bias-fairness-metrics": Status.NOT_IMPLEMENTED,
"monitoring-drift": Status.PARTIAL,
"incident-response-plan": Status.NOT_IMPLEMENTED,
"deployment-gate": Status.IMPLEMENTED,
"decommission-criteria": Status.NOT_IMPLEMENTED,
"third-party-monitoring": Status.PARTIAL,
}
def build_sample_assessment() -> SystemAssessment:
return SystemAssessment(
system_name="Northwind Support Copilot",
system_description=(
"Generative AI customer-support assistant with RAG over the "
"internal knowledge base and a tool that can issue refunds up "
"to $500 without human approval."
),
answers=dict(SAMPLE_ANSWERS),
)
if __name__ == "__main__":
assessment = build_sample_assessment()
print(render_gap_report(assessment))
print()
print(
"Takeaway: MEASURE and MANAGE are the weakest functions here — the "
"system has good documentation (MAP) but no bias metrics and no "
"incident response plan. Those two gaps are exactly the kind of "
"finding that blocks a SOC 2 or enterprise-procurement review."
)