Once a problem is framed, someone has to decide what kind of fix it needs. That decision is the opportunity assessment. It has more answers than "AI" or "no AI":
Fix the cause. The work exists because something upstream is broken: stale master data, a missing setting, an unclear policy. Fix that, and the work disappears.
Use standard SAP. A function you already own does the job, but nobody switched it on or scheduled it.
Automate with rules. The decision can be written down, experts agree on it, and the inputs are clean fields. A rule engine or workflow does it reliably and cheaply.
Use AI. The input is text, documents or messy context, or the decision depends on patterns in history that nobody can write down. Then a prototype is worth it.
Do nothing. The prize is too small, or the data or risk isn't ready yet.
Work through them in that order. Each step up adds cost, risk and things to maintain. Use AI where the problem needs it, not because the request said "AI".
The output is a short ranked list: each framed problem, the decision, the size of the prize and the next step, signed by the sponsor.
AI where rules would do costs more to run, needs evaluation and monitoring, and can be wrong in ways a rule never is. Google's machine learning guidance puts it bluntly. Its first rule is "Don't be afraid to launch a product without machine learning."
Rules where AI is needed become brittle. Google's third rule warns that a complex heuristic becomes hard to maintain, and that a learned model is then the better choice.
AI on top of a broken process hides the real cause. The assistant gets better at handling cases that should never have existed.
Take the course's procure-to-pay example: supplier invoices blocked for payment after the three-way match. The request was "an AI agent that clears blocked invoices". An assessment might find that almost half the blocks come from purchase order prices nobody updated, and that SAP's automatic release of invoices whose blocking reasons no longer apply was never scheduled. Fixing prices and scheduling the job could clear most of the backlog with no AI at all. What remains may suit rules. The AI budget then goes to a problem that needs it, such as the blocked sales orders, where analysts spend their time reading notes and context.
The assessment also tells a sponsor what to stop. Parking an idea until its data exists is a result, not a failure.
SAP offers pieces of an assessment rather than one tool. As of October 2026:
Finding fixable causes. SAP Signavio Process Insights reads process data from SAP ERP and reports on more than 200 process performance indicators. SAP Learning says it offers over 160 correction recommendations, such as configuration changes or master data fixes.
Checking standard first. SAP's own processes already automate a lot. Invoice verification has automatic release of invoices blocked due to variances. Credit Management has scheduled jobs that update credit limits and scores. Situation Handling can alert planners to selected MRP exceptions.
Checking embedded AI. The SAP Business AI Feature Catalog in SAP Discovery Center lists embedded AI features with use cases, value, metrics and release stage. The Feature Estimator estimates the AI units a feature would consume.
Automating with rules. SAP Build Process Automation offers workflows, business rules in "spreadsheet-like decision tables", bots and document extraction.
Prioritizing. SAP AppHaus workshops place use cases on a Value Heatmap, usually business value against complexity, and end with a Next Steps Matrix of what, when and who.
None of these decides "AI, automation or neither" for you. That judgment is the forward deployed engineer's job, with the process owner.
Ask the questions in this order. The first answer that applies usually decides.
Question
If yes
SAP example
Do experts disagree on what the right decision is?
Clarify the policy first. Neither people nor software can be measured against an unclear rule
Planners rank the same exceptions differently
Does a fixable cause explain a large share of the cases?
Fix first: data, settings, policy, training
Invoice price blocks caused by outdated purchase order prices
Does a standard function already do it?
Use standard: switch on, schedule, configure
Automatic release of blocked invoices (MRBR)
Can the decision be written as rules, on clean fields?
Automate with rules or workflow. No AI needed
Proposing the next step for a quantity block with no goods receipt
Is the input text or documents, or the output a draft?
Prototype generative AI
Drafting a context note for a blocked sales order
Is the output a prediction from history, with enough recorded outcomes?
Prototype a predictive model
Which deliveries will be late
Does one case need actions across several systems?
Prototype an agent, with approval steps
Gathering context from SAP and email, then proposing a release
Is the prize small, or are data, risk or approvals not ready?
Drop or park with a named condition
Fewer than 20 hours a month to save
Two reminders. First, hybrids are normal: rules handle the clear cases and AI handles the rest. Second, every decision is provisional. After a fix or a standard function goes live, measure again and reassess what is left.
"If we don't use AI, we've failed at AI." The goal is the metric in the frame. A scheduled standard job that moves it is a win.
"Rules are old-fashioned; AI replaces them." Rules are cheap, explainable and exact when the decision is well defined. Most good AI systems keep rules for the clear cases.
"We can fix the data later." AI on bad data learns the bad data. Fixable causes come first.
"A big prize means do it now." A large prize with no data or no clear policy is a park, not a go.
"The assessment is a one-off." Each fix changes what is left. Reassess after every change that goes live.
Pick one answer for each question. The explanation appears after you choose.
1A sponsor asks for "an AI agent that clears blocked invoices". What should the opportunity assessment check first?
Answer: D. The assessment climbs from the cheapest fix upward. Outdated purchase order prices and an unscheduled automatic release could remove most of the work with no AI. The model choice only matters once AI is the right answer.
2Two experienced planners rank the same 30 MRP exceptions very differently. What does that tell you?
Answer: B. If experts disagree, there is no agreed right answer to automate or to measure a tool against. The first step is to clarify the policy with the process owner. Cleaning data or buying a tool doesn't settle what the right decision is.
3Which situation fits rules automation better than AI?
Answer: A. When the decision can be written down, experts agree on it, and the inputs are clean fields, rules are cheaper, exact and explainable. The other three involve text, documents or patterns in history, where AI earns its place.
4An idea would save many hours, but only 150 past cases have the outcome recorded. What is the sensible decision?
Answer: D. A predictive model learns from recorded outcomes, and 150 is too few to start. Parking with a named condition, such as recording outcomes for a few months, keeps the idea alive. A big prize doesn't fix missing data.
5Your partner presents an AI design for a process step. Which question best tests their assessment?
Answer: D. The best non-AI fix is the benchmark. If the partner can't name it, they haven't assessed whether AI is needed. Hosting and timing questions come after the decision.
6Fixing purchase order prices removed half the invoice blocks. What should happen next?
Answer: B. Each fix changes the remaining problem. The leftover cases may suit a standard function, rules, AI or nothing. The assessment is repeated, not done once.
7What does SAP AppHaus's Value Heatmap help a team do?
Answer: D. The Value Heatmap places use cases relative to each other, usually business value against complexity, to find quick wins. It doesn't produce a return on investment or pick technology. The business case and the Feature Catalog do other jobs.
Ask a question
Testing: only staff see this
Stuck on something in this layer? Ask it here. Questions are answered in the order they arrive, and the answer appears under My questions.
Sign in (free) to ask a question. You can ask anonymously.
Picture a ladder. Each rung adds machinery, and each piece of machinery is something you build, run, test and explain:
Rung
What you add
What you now own
0. Fix the cause
Nothing new; you remove work
A data or settings change, and keeping it fixed
1. Standard SAP
Configuration or a scheduled job
Settings, a job, monitoring it
2. Rules automation
Decision tables or a workflow
Rule maintenance when policy changes
3. Predictive AI
A model trained on past outcomes
Training data, retraining, drift, evaluation
4. Generative AI
A language model with prompts and grounding
Prompts, evaluation, token cost, guardrails
5. Agent
A model that chooses tools and steps
All of rung 4, plus tool security and approvals
You climb only when the rung below can't meet the frame. Google's problem framing guidance makes the same point for machine learning: optimize a non-ML solution first, then use it as the benchmark. Its Rules of Machine Learning add the counterweight: once rules turn into a complex heuristic and you have data, a learned model becomes easier to maintain. Both rules are true. The assessment finds where on the ladder a given problem sits.
This is a different question from Buy vs. build for SAP AI. That topic asks who supplies the solution: use, configure, extend, build or partner. This topic asks what kind of solution the problem needs. Answer this one first. A "use standard" result here often ends the sourcing question before it starts.
The assessment takes one signed frame from Problem framing and walks it through six passes. Each pass has a question, the evidence that answers it, and what a "yes" means.
flowchart TD
F[Signed frame] --> P1{Experts agree?}
P1 -->|no| C[CLARIFY policy]
P1 -->|yes| P2{Large fixable cause?}
P2 -->|yes| X[FIX FIRST]
P2 -->|no| P3{Standard does it?}
P3 -->|yes| S[USE STANDARD]
P3 -->|no| P4[Shape: rules, predict, generate, agent]
P4 --> P5{Gates pass?}
P5 -->|no| K[PARK]
P5 -->|yes| P6{Prize big enough?}
P6 -->|no| D[DROP]
P6 -->|yes| G[AUTOMATE or PROTOTYPE]
X -.measure again.-> F
S -.measure again.-> F
Take 20 to 30 past cases. Ask two experts to decide each one on their own. Count how often they agree.
High agreement means a correct answer exists and can be measured. Low agreement means the policy is unwritten or contested. Automating that, with rules or AI, just picks one expert's habits and hides the disagreement. The fix is a conversation with the process owner, not software. This course uses 60% as the line below which you clarify first, and 90% as the line above which rules are realistic. Both are starting points to adjust, not standards.
The agreement sample also becomes your first evaluation set later. If you prototype, these cases tell you how good "good" is: a tool that matches experts as often as they match each other is doing well.
For each case, ask why it exists. Then count the reasons from system data where you can. Typical fixable causes in SAP processes:
Master data: outdated purchase order prices causing price blocks, wrong planned delivery times causing MRP exceptions, credit limits nobody reviewed.
Settings: tolerances set too tight, a standard job not scheduled, an output or workflow not configured.
Policy and training: a rule people follow that nobody wrote down, a step done out of order.
SAP Signavio Process Insights exists for this pass. SAP Learning describes performance indicators across process flows and correction recommendations that include configuration changes and master data fixes. Without it, a few queries over the documents from the discovery baseline do the same job on a smaller scale.
If a fixable cause explains a large share of cases, fix it first. This course uses 30%. Then measure again: the rest of the assessment runs on what is left.
The process's own functions. For blocked invoices, SAP Learning describes automatic release with MRBR for invoices blocked due to variances. The system deletes blocking reasons that no longer apply, such as a price block after the purchase order price changed, and releases the invoice once none are left. Program RM08RELEASE runs it in the background. Invoices with a manual payment block in the header are not released automatically.
Alerts and jobs. SAP Credit Management has scheduled jobs such as Update Credit Limit for Business Partner and Update Score for Business Partner. For planning, an SAP S/4HANA RIG post shows Situation Handling with the template SAP_PP_MRP_MATERIAL_EXCEPTION. It notifies a team of planners about selected exceptions and opens Manage Material Coverage.
Record what you checked, in which release, and the result: yes, partial or no. "Partial" is common. Situation Handling alerts on cancel exceptions, but it doesn't rank the rest. Write down the gap.
Now look at the work itself. Four questions pick the shape:
Question
Points to
Can an expert write the decision down, experts agree, and are the inputs clean fields?
Rules
Is the output a prediction (a number or category) learned from structured history?
Predict
Is the input text, documents or mixed context, or is the output a draft?
Generate
Does one case need actions across several systems, in an order that varies?
Agent
Google's framing guidance draws the predictive and generative line clearly. Predictive models make predictions you can check against what really happened. Generative models produce content from the user's intent. They need different data and different evaluation, so they need different prototypes.
When rules cover some cases but not all, write it down as a hybrid. In the blocked sales order example, rules can flag orders under a value threshold for a fast path. A generative assistant then drafts context for the rest.
Use the frame's numbers for a ceiling, not a forecast:
hours saved by the fix = volume x fixable share x baseline minutes / 60
hours saved on the rest = volume x (1 - fixable share) x (baseline - target) / 60
For the blocked sales orders: 6,000 orders a month, 15% fixable, 17 minutes today, 10 minutes target. The fix saves up to 6,000 × 0.15 × 17 / 60 = 255 hours a month. Reaching the target on the other 5,100 orders saves up to 5,100 × 7 / 60 = 595 hours.
These are ceilings at the target. Adoption, realization and run cost come later, in The AI business case. Here you only need to know whether the prize is big enough to bother with. This course drops anything under 20 hours a month unless it can be bundled with a related problem.
The passes produce one of seven decisions, checked in this order:
Decision
When
Next step
CLARIFY
Experts agree on under 60% of cases
Agree the policy, re-run the agreement test
FIX FIRST
A fixable cause explains 30% or more
Fix it, measure, reassess
USE STANDARD
A standard function does it
Configure or schedule it, measure, reassess
PARK
A gate fails
Meet the named condition, then reassess
DROP
Under 20 hours a month
Bundle with a related problem, or stop
AUTOMATE
The shape is rules
Build a decision table or workflow
PROTOTYPE
The shape is predict, generate or agent
Prototype against the benchmark (next in Unit 14)
Finally, rank the list for the sponsor. SAP AppHaus uses a Value Heatmap for this, placing use cases relative to each other on business value and complexity, and ends with a Next Steps Matrix: what, when, who. Your ranked list does the same job in text, and goes in Git.
You will build a small command-line tool that walks each framed problem through the six passes, explains its reasoning, and ranks the results into a one-page report for the sponsor.
Before you start: complete Set up your computer for this course and Set up for Unit 14. They create your orchestrate-course folder, its .venv and the unit14 folder. This script uses only Python's built-in modules, so there is nothing new to install. If you did the problem framing exercise, the tool reads your frames too, but you don't need them.
flowchart LR
S[sample or new] --> A[(assessment files)]
Fr[(frame files, optional)] -.-> A
A --> R[assess: six passes]
R --> K[rank]
K --> M[assessment.md for sign-off]
In VS Code, right-click the unit14 folder, choose New File, name it opportunity_assessor.py, paste the code below and save.
"""Opportunity assessor: decide whether a framed problem needs AI, automation or neither.
Commands:
sample write three made-up assessments for SAP processes
new write an empty assessment to fill in
assess walk one or more assessments through the passes and print the reasoning
rank print one line per opportunity, best first; --md writes a report
Uses only Python's built-in modules. No account, no network.
"""
import argparse
import glob
import sys
from pathlib import Path
HERE = Path(__file__).resolve().parent
DEFAULT_DIR = HERE / "assessment"
# ---------------------------------------------------------------------------
# 1. The fields of an assessment. One "FIELD: value" per line.
# ---------------------------------------------------------------------------
FIELDS = {
"NAME": "short name of the opportunity",
"FRAME": "optional path to the problem frame; missing fields are read from it",
"PROBLEM": "the framed problem, in business words",
"UNITS": "unit of the metric; value is only computed for minutes",
"BASELINE": "today's value of the metric (a number; text after | is a note)",
"TARGET": "the target from the frame",
"VOLUME": "cases per month (orders, invoices, planner days)",
"HOURLY_COST": "loaded cost of one hour of the actor's time",
"CAUSE_FIXABLE": "share of cases (0 to 1) caused by something you can fix: data, config, policy",
"FIX": "what that fix is",
"STANDARD": "yes, partial or no: does a standard SAP function already do this?",
"STANDARD_DETAIL": "which function, and what you checked",
"RULES_WRITABLE": "yes, partial or no: can an expert write the decision down as rules?",
"EXPERTS_AGREE": "share of sample cases (0 to 1) where two experts decided the same",
"INPUT": "structured, unstructured or mixed",
"OUTPUT": "decision, prediction, text or actions",
"LABELED_HISTORY": "past cases with the real outcome recorded",
"TOOLS": "how many systems or tools the solution must use for one case",
"DATA_ACCESS": "yes or no: can the solution read its inputs at decision time, within authorizations?",
"ERROR_COST": "low, medium or high: what a wrong output costs if nobody catches it",
"HUMAN_REVIEW": "yes or no: does a person check every output before it takes effect?",
"CHANGES_SAP": "yes or no: would the output change SAP data?",
"PERSONAL_DATA": "yes or no",
"HIGH_RISK_DOMAIN": "yes or no: HR decisions about people, credit decisions about people, health",
}
CHOICES = {
"STANDARD": ["yes", "partial", "no"],
"RULES_WRITABLE": ["yes", "partial", "no"],
"INPUT": ["structured", "unstructured", "mixed"],
"OUTPUT": ["decision", "prediction", "text", "actions"],
"DATA_ACCESS": ["yes", "no"],
"ERROR_COST": ["low", "medium", "high"],
"HUMAN_REVIEW": ["yes", "no"],
"CHANGES_SAP": ["yes", "no"],
"PERSONAL_DATA": ["yes", "no"],
"HIGH_RISK_DOMAIN": ["yes", "no"],
}
NUMBERS = ["BASELINE", "TARGET", "VOLUME", "HOURLY_COST", "CAUSE_FIXABLE",
"EXPERTS_AGREE", "LABELED_HISTORY", "TOOLS"]
REQUIRED = ["NAME", "BASELINE", "TARGET", "VOLUME", "CAUSE_FIXABLE", "STANDARD",
"RULES_WRITABLE", "EXPERTS_AGREE", "INPUT", "OUTPUT", "DATA_ACCESS",
"ERROR_COST", "HUMAN_REVIEW", "CHANGES_SAP"]
FROM_FRAME = ["PROBLEM", "UNITS", "BASELINE", "TARGET", "CHANGES_SAP"]
# Starting thresholds for this course. They are judgment calls, not standards:
# change them for your customer and say so in your report.
FIX_FIRST_SHARE = 0.30 # fix the cause first if it explains this share of cases
ALSO_FIX_SHARE = 0.10 # mention the fix if it explains at least this share
CLARIFY_BELOW = 0.60 # experts agree less often than this: the task is not defined
RULES_AGREE = 0.90 # rules need experts to agree at least this often
LABELS_OK = 1000 # labeled past cases to try a predictive model
LABELS_MIN = 200 # below this, collect labels before predicting
BLANK = "# Opportunity assessment. One FIELD: value per line. Lines starting with # are ignored.\n" + \
"\n".join(f"{name}: ({help_text})" for name, help_text in FIELDS.items()) + "\n"
SAMPLES = {
"blocked_orders.txt": """# Made-up assessment for order-to-cash. Frame: Unit 14, problem framing.
NAME: Blocked sales orders
FRAME: unit14/framing/blocked_orders.txt
PROBLEM: Credit analysts spend most of each blocked order gathering context from several screens and sales reps
UNITS: minutes
BASELINE: 17 | measured, shadowing of 8 orders
TARGET: 10
VOLUME: 6000
HOURLY_COST: 55
CAUSE_FIXABLE: 0.15 | system, blocks on customers whose credit limit was not reviewed in 12 months
FIX: Schedule the credit limit and score update jobs; review stale limits
STANDARD: partial
STANDARD_DETAIL: Credit Management jobs update scores and limits, but nothing gathers the context per order
RULES_WRITABLE: partial
EXPERTS_AGREE: 0.8 | measured, two analysts decided 20 past orders
INPUT: mixed
OUTPUT: text
LABELED_HISTORY: 0
TOOLS: 1
DATA_ACCESS: yes
ERROR_COST: medium
HUMAN_REVIEW: yes
CHANGES_SAP: no
PERSONAL_DATA: yes
HIGH_RISK_DOMAIN: no
""",
"invoice_agent.txt": """# Made-up assessment for procure-to-pay. Frame: Unit 14, problem framing (reframed version).
NAME: Blocked supplier invoices
FRAME: unit14/framing/invoice_agent.txt
PROBLEM: Clerks spend most of each blocked invoice finding out why it is blocked and whether the reason still holds
UNITS: minutes
BASELINE: 22 | measured, shadowing of 10 invoices
TARGET: 12
VOLUME: 2500
HOURLY_COST: 50
CAUSE_FIXABLE: 0.45 | system, price blocks where the purchase order price was never updated
FIX: Buyers update purchase order prices when a supplier price changes
STANDARD: yes
STANDARD_DETAIL: Automatic release of invoices blocked due to variances (MRBR, background program RM08RELEASE) is not scheduled
RULES_WRITABLE: yes
EXPERTS_AGREE: 0.95 | measured, two clerks on 20 past invoices
INPUT: structured
OUTPUT: decision
LABELED_HISTORY: 30000
TOOLS: 1
DATA_ACCESS: yes
ERROR_COST: high
HUMAN_REVIEW: yes
CHANGES_SAP: no
PERSONAL_DATA: no
HIGH_RISK_DOMAIN: no
""",
"mrp_exceptions.txt": """# Made-up assessment for plan-to-produce. Numbers are estimates on purpose.
NAME: MRP exception review
FRAME: unit14/framing/mrp_exceptions.txt
PROBLEM: Planners spend the start of each day working through exception messages before they can act on shortages
UNITS: minutes
BASELINE: 180 | estimate, the planning lead's estimate
TARGET: 90
VOLUME: 170 | 8 planners, about 21 working days a month
HOURLY_COST: 65
CAUSE_FIXABLE: 0.1 | estimate, exceptions caused by outdated lead times
FIX: Review planned delivery times for the materials with the most exceptions
STANDARD: partial
STANDARD_DETAIL: Situation Handling can alert planners to cancel exceptions; it does not rank the rest
RULES_WRITABLE: partial
EXPERTS_AGREE: 0.7 | measured, two planners ranked the same 30 exceptions
INPUT: structured
OUTPUT: prediction
LABELED_HISTORY: 150 | exceptions where we know whether the planner acted
TOOLS: 1
DATA_ACCESS: yes
ERROR_COST: medium
HUMAN_REVIEW: yes
CHANGES_SAP: no
PERSONAL_DATA: no
HIGH_RISK_DOMAIN: no
""",
}
# ---------------------------------------------------------------------------
# 2. Reading files
# ---------------------------------------------------------------------------
def show(path):
"""Print a path relative to the current folder when possible."""
try:
return str(Path(path).resolve().relative_to(Path.cwd().resolve()))
except ValueError:
return str(path)
def read_pairs(path):
"""Read FIELD: value lines into a dict. Unknown fields are kept but reported."""
data, problems = {}, []
for number, raw in enumerate(Path(path).read_text(encoding="utf-8").splitlines(), start=1):
line = raw.strip()
if not line or line.startswith("#"):
continue
if ":" not in line:
problems.append(f"line {number}: no 'FIELD:' at the start")
continue
name, value = line.split(":", 1)
data.setdefault(name.strip().upper(), value.strip())
return data, problems
def read_assessment(path):
"""Read an assessment. Fields it leaves out are taken from its FRAME file, if found."""
data, problems = read_pairs(path)
frame_note = "no FRAME given"
if data.get("FRAME"):
frame_path = Path(data["FRAME"])
if frame_path.exists():
frame, _ = read_pairs(frame_path)
taken = [f for f in FROM_FRAME if f not in data and f in frame]
for field in taken:
data[field] = frame[field]
frame_note = f"frame found, {len(taken)} fields taken from it"
else:
frame_note = "frame not found; using this file only"
for name in data:
if name not in FIELDS:
problems.append(f"unknown field '{name}'")
return data, problems, frame_note
def number(text):
"""'0.15 | system, ...' -> 0.15. Returns None if there is no number first."""
try:
return float(text.split("|")[0].strip())
except (ValueError, AttributeError):
return None
def check_fields(data):
"""Return a list of problems that stop the assessment."""
problems = []
missing = [f for f in REQUIRED if not data.get(f) or data[f].startswith("(")]
if missing:
problems.append("missing fields: " + ", ".join(missing))
for name in NUMBERS:
if data.get(name) and not data[name].startswith("(") and number(data[name]) is None:
problems.append(f"{name} must start with a number")
for name in ["CAUSE_FIXABLE", "EXPERTS_AGREE"]:
value = number(data.get(name, ""))
if value is not None and not 0 <= value <= 1:
problems.append(f"{name} must be between 0 and 1")
for name, allowed in CHOICES.items():
value = data.get(name, "").split("|")[0].strip().lower()
if value and not value.startswith("(") and value not in allowed:
problems.append(f"{name} must be one of: {', '.join(allowed)}")
return problems
def choice(data, name, default="no"):
return data.get(name, default).split("|")[0].strip().lower()
PHRASE = {"GENERATE": "a generative AI assistant", "PREDICT": "a predictive model",
"AGENT": "an agent with approval steps", "RULES": "rules"}
# ---------------------------------------------------------------------------
# 3. The passes. Each adds lines to the report; the last one decides.
# ---------------------------------------------------------------------------
def assess(data, min_hours):
lines = [] # (label, text) for the printed report
gates = [] # (level, text) with level PASS, WARN or FAIL
volume = number(data["VOLUME"])
baseline, target = number(data["BASELINE"]), number(data["TARGET"])
fixable = number(data["CAUSE_FIXABLE"])
agree = number(data["EXPERTS_AGREE"])
labels = number(data.get("LABELED_HISTORY", "0")) or 0
tools = number(data.get("TOOLS", "1")) or 1
hourly = number(data.get("HOURLY_COST", "")) or 0
in_minutes = data.get("UNITS", "").strip().lower().startswith("minute")
# Pass 1: is the work itself defined?
lines.append(("Pass 1 Defined", f"experts agree on {agree:.0%} of sample cases"))
# Pass 2: can the cause be removed instead of handled?
fix_text = data.get("FIX", "(no fix named)")
lines.append(("Pass 2 Cause", f"{fixable:.0%} of cases have a fixable cause: {fix_text}"))
# Pass 3: does standard SAP already do it?
standard = choice(data, "STANDARD")
detail = data.get("STANDARD_DETAIL", "(not described)")
lines.append(("Pass 3 Standard", f"{standard}: {detail}"))
# Pass 4: which kind of solution does the work call for?
rules = choice(data, "RULES_WRITABLE")
source = choice(data, "INPUT")
output = choice(data, "OUTPUT")
if rules == "yes" and source == "structured" and agree >= RULES_AGREE:
kind = "RULES"
why = "the rules can be written down, experts agree, and the inputs are structured"
elif output == "actions" and tools >= 2:
kind = "AGENT"
why = f"one case needs actions across {tools:g} systems, in an order that varies"
elif output == "prediction" and source == "structured":
kind = "PREDICT"
why = "the output is a prediction from structured history"
else:
kind = "GENERATE"
why = f"the input is {source} and the output is {output}; rules can't cover it"
if kind != "RULES" and rules == "partial":
why += "; rules can handle the clear cases first"
lines.append(("Pass 4 Shape", f"{kind}: {why}"))
# Pass 5: gates. Any FAIL stops the AI path until it is fixed.
if choice(data, "DATA_ACCESS") == "no":
gates.append(("FAIL", "Data: the inputs can't be read at decision time within authorizations"))
else:
gates.append(("PASS", "Data: inputs readable at decision time"))
if kind == "PREDICT":
if labels >= LABELS_OK:
gates.append(("PASS", f"Labels: {labels:g} labeled past cases"))
elif labels >= LABELS_MIN:
gates.append(("WARN", f"Labels: only {labels:g} labeled past cases; expect a weak first model"))
else:
gates.append(("FAIL", f"Labels: {labels:g} labeled past cases; record outcomes before modeling"))
review = choice(data, "HUMAN_REVIEW")
error_cost = choice(data, "ERROR_COST")
if error_cost == "high" and review == "no":
gates.append(("FAIL", "Errors: wrong outputs are costly and nobody checks them"))
elif error_cost == "high":
gates.append(("WARN", "Errors: costly when wrong; the human check must be real, not a click"))
else:
gates.append(("PASS", f"Errors: cost when wrong is {error_cost}"))
if choice(data, "CHANGES_SAP") == "yes" and review == "no":
gates.append(("FAIL", "Approval: the output changes SAP data without a person approving it"))
elif choice(data, "CHANGES_SAP") == "yes":
gates.append(("WARN", "Approval: changes SAP data; enforce the approval in code"))
if choice(data, "HIGH_RISK_DOMAIN") == "yes":
gates.append(("WARN", "Risk: high-risk domain; involve legal and works council before a prototype"))
if choice(data, "PERSONAL_DATA") == "yes":
gates.append(("WARN", "Privacy: personal data; involve data protection early"))
# Pass 6: size the prize. A ceiling at the target, not a business case.
fix_hours = ai_hours = None
if in_minutes and volume is not None:
fix_hours = volume * fixable * baseline / 60
ai_hours = volume * (1 - fixable) * max(baseline - target, 0) / 60
money = f" (about {ai_hours * hourly:,.0f} a month at {hourly:g} an hour)" if hourly else ""
value = ("Pass 6 Value", f"fixing the cause saves up to {fix_hours:,.0f} h a month; "
f"reaching the target on the rest saves up to {ai_hours:,.0f} h a month{money}")
else:
value = ("Pass 6 Value", "not computed: the metric is not in minutes; use the business case calculator")
# Decision, in order. The first rule that matches wins.
fails = [text for level, text in gates if level == "FAIL"]
also = []
if ALSO_FIX_SHARE <= fixable < FIX_FIRST_SHARE:
also.append(f"also fix: {fix_text}")
if standard == "partial":
also.append(f"compare with standard: {detail}")
if standard == "yes" and fixable >= FIX_FIRST_SHARE and agree >= CLARIFY_BELOW:
also.append(f"then use standard: {detail}")
if agree < CLARIFY_BELOW:
decision, step = "CLARIFY", "Agree the decision policy with the process owner; re-run the agreement test"
elif fixable >= FIX_FIRST_SHARE:
decision, step = "FIX FIRST", f"{fix_text}; then measure again and reassess what is left"
elif standard == "yes":
decision, step = "USE STANDARD", f"{detail}; configure it, measure, and reassess what is left"
elif fails:
decision, step = "PARK", fails[0]
elif ai_hours is not None and ai_hours < min_hours:
decision, step = "DROP", f"under {min_hours:g} h a month; bundle with a related problem or stop"
elif kind == "RULES":
decision, step = "AUTOMATE", "Write the rules as a decision table and automate them; no AI needed"
else:
decision, step = "PROTOTYPE", f"Prototype {PHRASE[kind]} and compare it with the benchmark"
return {"lines": lines, "gates": gates, "value": value, "kind": kind, "decision": decision, "step": step,
"also": also, "ai_hours": ai_hours, "fix_hours": fix_hours, "hourly": hourly}
# ---------------------------------------------------------------------------
# 4. Commands
# ---------------------------------------------------------------------------
def expand(patterns):
"""Expand *.txt patterns ourselves, so quoting works the same on Windows."""
files = []
for pattern in patterns:
matches = sorted(glob.glob(pattern))
files.extend(matches if matches else [pattern])
return [Path(f) for f in files]
def load(files):
"""Read and check every file. Return (path, data, frame_note, problems) tuples."""
loaded = []
for path in expand(files):
if not path.exists():
loaded.append((path, None, "", [f"file not found: {show(path)}"]))
continue
data, problems, note = read_assessment(path)
loaded.append((path, data, note, problems + check_fields(data)))
return loaded
def cmd_sample(args):
base = Path(args.dir)
base.mkdir(parents=True, exist_ok=True)
existing = [name for name in SAMPLES if (base / name).exists()]
if existing and not args.force:
print(f"Sample assessments already exist in {show(base)}. Add --force to overwrite them.")
return 1
for name, text in SAMPLES.items():
(base / name).write_text(text, encoding="utf-8")
print(f"Wrote {len(SAMPLES)} assessments to {show(base)}: " + ", ".join(SAMPLES))
print('All of them are made up. Next: python unit14/opportunity_assessor.py assess "unit14/assessment/*.txt"')
return 0
def cmd_new(args):
out = Path(args.out)
if out.exists() and not args.force:
print(f"{show(out)} already exists. Add --force to overwrite it.")
return 1
out.parent.mkdir(parents=True, exist_ok=True)
out.write_text(BLANK, encoding="utf-8")
print(f"Wrote an empty assessment to {show(out)}. Replace every (...) with your answer.")
return 0
def cmd_assess(args):
errors = 0
for path, data, note, problems in load(args.files):
print(f"== {show(path)}")
if problems:
errors += 1
for problem in problems:
print(f" ERROR {problem}")
print(" -> fix these lines and run again\n")
continue
result = assess(data, args.min_hours)
print(f" {data['NAME']} ({note})")
for label, text in result["lines"]:
print(f" {label:<16} {text}")
for level, text in result["gates"]:
print(f" {'Pass 5 ' + level:<16} {text}")
print(f" {result['value'][0]:<16} {result['value'][1]}")
print(f" -> {result['decision']}: {result['step']}")
for extra in result["also"]:
print(f" {extra}")
print()
return 1 if errors else 0
ORDER = ["FIX FIRST", "USE STANDARD", "AUTOMATE", "PROTOTYPE", "CLARIFY", "PARK", "DROP"]
def cmd_rank(args):
rows, errors = [], 0
for path, data, _, problems in load(args.files):
if problems:
errors += 1
print(f"Skipped {show(path)}: {problems[0]}")
continue
result = assess(data, args.min_hours)
hours = (result["fix_hours"] or 0) + (result["ai_hours"] or 0)
rows.append((ORDER.index(result["decision"]), -hours, data, result, hours))
rows.sort(key=lambda r: (r[0], r[1]))
print(f"{'Opportunity':<28} {'Decision':<13} {'Shape':<9} {'h/month':>8} Next step")
for _, _, data, result, hours in rows:
print(f"{data['NAME'][:28]:<28} {result['decision']:<13} {result['kind']:<9} {hours:>8,.0f} {result['step']}")
if args.md and rows:
out = Path(args.md)
out.parent.mkdir(parents=True, exist_ok=True)
md = ["# Opportunity assessment", "",
"Hours are ceilings at the target, not forecasts. Decisions use the course's starting thresholds.", "",
"| Opportunity | Decision | Shape | h/month | Next step |", "| --- | --- | --- | ---: | --- |"]
for _, _, data, result, hours in rows:
md.append(f"| {data['NAME']} | {result['decision']} | {result['kind']} | {hours:,.0f} | {result['step']} |")
md += ["", "## Open points", ""]
for _, _, data, result, _ in rows:
for level, text in result["gates"]:
if level != "PASS":
md.append(f"- {data['NAME']}: {level} {text}")
for extra in result["also"]:
md.append(f"- {data['NAME']}: {extra}")
md += ["", "Signed off by: ____________________ Date: __________", ""]
out.write_text("\n".join(md), encoding="utf-8")
print(f"\nWrote {show(out)}")
return 1 if errors else 0
def main(argv=None):
p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
sub = p.add_subparsers(dest="cmd", required=True)
s = sub.add_parser("sample", help="write three made-up assessments")
s.add_argument("--dir", default=str(DEFAULT_DIR), help="folder for the files (default: unit14/assessment)")
s.add_argument("--force", action="store_true", help="overwrite existing sample files")
n = sub.add_parser("new", help="write an empty assessment")
n.add_argument("out", help="file to create, e.g. unit14/assessment/my_problem.txt")
n.add_argument("--force", action="store_true")
for name, text in [("assess", "explain the decision for each file"), ("rank", "one line per file, best first")]:
c = sub.add_parser(name, help=text)
c.add_argument("files", nargs="+")
c.add_argument("--min-hours", type=float, default=20,
help="below this many hours saved a month, drop or bundle (default 20)")
if name == "rank":
c.add_argument("--md", help="also write a Markdown report to this file")
args = p.parse_args(argv)
handler = {"sample": cmd_sample, "new": cmd_new, "assess": cmd_assess, "rank": cmd_rank}[args.cmd]
return handler(args) or 0
if __name__ == "__main__":
sys.exit(main())
Wrote 3 assessments to unit14/assessment: blocked_orders.txt, invoice_agent.txt, mrp_exceptions.txt
All of them are made up. Next: python unit14/opportunity_assessor.py assess "unit14/assessment/*.txt"
Open unit14/assessment/blocked_orders.txt. Each line is one field:
Field
What it holds
NAME, FRAME, PROBLEM
A short name, the path to the frame (optional), and the problem in business words
UNITS, BASELINE, TARGET
From the frame. Value is only computed when the units are minutes
VOLUME, HOURLY_COST
Cases per month, and what an hour of the actor's time costs
CAUSE_FIXABLE, FIX
Share of cases with a fixable cause (0 to 1), and what the fix is
STANDARD, STANDARD_DETAIL
yes, partial or no, and which SAP function you checked
RULES_WRITABLE, EXPERTS_AGREE
Can the decision be written as rules, and how often two experts agreed (0 to 1)
INPUT, OUTPUT
structured, unstructured or mixed; decision, prediction, text or actions
LABELED_HISTORY, TOOLS
Past cases with the real outcome recorded; systems one case touches
Text after a | is a note for people. The script reads only the number or word before it. Always write where a number came from: system, measured or estimate, as in the discovery and framing topics.
If you see Sample assessments already exist, you ran this before. Add --force to overwrite them.
Keep the quotes. They make the pattern work the same way in PowerShell and on macOS or Linux.
You should see three reports. The first one looks like this:
== unit14/assessment/blocked_orders.txt
Blocked sales orders (frame not found; using this file only)
Pass 1 Defined experts agree on 80% of sample cases
Pass 2 Cause 15% of cases have a fixable cause: Schedule the credit limit and score update jobs; review stale limits
Pass 3 Standard partial: Credit Management jobs update scores and limits, but nothing gathers the context per order
Pass 4 Shape GENERATE: the input is mixed and the output is text; rules can't cover it; rules can handle the clear cases first
Pass 5 PASS Data: inputs readable at decision time
Pass 5 PASS Errors: cost when wrong is medium
Pass 5 WARN Privacy: personal data; involve data protection early
Pass 6 Value fixing the cause saves up to 255 h a month; reaching the target on the rest saves up to 595 h a month (about 32,725 a month at 55 an hour)
-> PROTOTYPE: Prototype a generative AI assistant and compare it with the benchmark
also fix: Schedule the credit limit and score update jobs; review stale limits
compare with standard: Credit Management jobs update scores and limits, but nothing gathers the context per order
If you did the problem framing exercise, the second line says frame found, 0 fields taken from it instead. That is fine: the sample file already holds the numbers.
Read the last lines of the other two reports:
-> FIX FIRST: Buyers update purchase order prices when a supplier price changes; then measure again and reassess what is left
then use standard: Automatic release of invoices blocked due to variances (MRBR, background program RM08RELEASE) is not scheduled
Pass 5 FAIL Labels: 150 labeled past cases; record outcomes before modeling
...
-> PARK: Labels: 150 labeled past cases; record outcomes before modeling
Three problems, three different answers. The blocked orders are a good AI candidate. The invoice "agent" needs a data fix and a scheduled standard job first. The MRP ranking idea has value, but no recorded outcomes to learn from yet, so it is parked with a clear condition.
Suppose the buyers fixed their prices and automatic release is now scheduled. Fewer invoices are blocked, and the reasons that remain are different. Reassess what is left.
In VS Code, right-click unit14/assessment, choose New File, name it invoice_after.txt, paste this and save:
# Made-up reassessment after the price fix and the scheduled automatic release.
NAME: Blocked invoices, after fix
PROBLEM: Clerks spend most of each blocked invoice finding out why it is blocked and whether the reason still holds
UNITS: minutes
BASELINE: 22 | measured, shadowing of 10 invoices
TARGET: 12
VOLUME: 900 | system, blocks left after automatic release was scheduled
HOURLY_COST: 50
CAUSE_FIXABLE: 0.05 | system, after the buyers fixed purchase order prices
FIX: Buyers update purchase order prices when a supplier price changes
STANDARD: no
STANDARD_DETAIL: Automatic release is now scheduled; these blocks are the ones it can't clear
RULES_WRITABLE: yes
EXPERTS_AGREE: 0.95 | measured, two clerks on 20 past invoices
INPUT: structured
OUTPUT: decision
LABELED_HISTORY: 30000
TOOLS: 1
DATA_ACCESS: yes
ERROR_COST: high
HUMAN_REVIEW: yes
CHANGES_SAP: no
PERSONAL_DATA: no
HIGH_RISK_DOMAIN: no
Pass 6 Value fixing the cause saves up to 16 h a month; reaching the target on the rest saves up to 142 h a month (about 7,125 a month at 50 an hour)
-> AUTOMATE: Write the rules as a decision table and automate them; no AI needed
The request was for an AI agent. The honest answer, after two cheaper steps, is a decision table that proposes the next step for each remaining block, with a clerk still deciding. SAP Build Process Automation's decision tables are one way to build it; a small rules function in your own code is another.
Opportunity Decision Shape h/month Next step
Blocked supplier invoices FIX FIRST RULES 642 Buyers update purchase order prices when a supplier price changes; then measure again and reassess what is left
Blocked invoices, after fix AUTOMATE RULES 159 Write the rules as a decision table and automate them; no AI needed
Blocked sales orders PROTOTYPE GENERATE 850 Prototype a generative AI assistant and compare it with the benchmark
MRP exception review PARK PREDICT 280 Labels: 150 labeled past cases; record outcomes before modeling
Wrote unit14/assessment/assessment.md
Open unit14/assessment/assessment.md. It holds the table, an Open points list with every warning and failed gate, and a line for the sponsor's signature.
The order is deliberate. Cheap fixes come first because they change everything after them, then rules, then AI prototypes, then parked and dropped ideas. Within a decision, bigger prizes come first. The hours column adds both savings and is a ceiling, not a promise.
python unit14/opportunity_assessor.py new unit14/assessment/my_problem.txt
Open it and replace every (...) with your answer. If you have a frame, set FRAME: unit14/framing/my_frame.txt: the tool then reads PROBLEM, UNITS, BASELINE, TARGET and CHANGES_SAP from it, so you can delete those lines.
SAP Signavio Process Insights extracts data from SAP ERP and reports process performance indicators. SAP Learning lists over 200 indicators across more than 90 process flows, and over 160 correction recommendations, such as configuration changes or master data fixes. It flags blockers by the share of documents affected. Use it, or your own queries, for pass 2.
Invoice verification. Automatic release with MRBR, in the background with RM08RELEASE, deletes blocking reasons that no longer apply and releases the invoice. Authorization object M_RECH_SPG controls which blocking reasons a user may delete. Any rules or AI built on top must not do more than the clerk is allowed to.
Credit Management. Scheduled jobs update credit limits and scores for business partners. A blocked order caused by a stale limit is a job-scheduling problem before it is an AI problem.
Situation Handling. For MRP, the template SAP_PP_MRP_MATERIAL_EXCEPTION alerts a team of planners about selected exceptions and opens Manage Material Coverage. The RIG post is from 2021; check the template in your release.
Embedded AI. The SAP Business AI Feature Catalog and Feature Estimator in SAP Discovery Center list embedded features with release stage, and estimate AI units. Licensing and AI units are covered in Embedded AI across SAP applications.
SAP Build Process Automation offers a workflow builder, a business rules editor with decision tables and flowcharts, attended and unattended bots, and document information extraction. sap.com says LLM-based extraction without templates is in the premium edition. Joule can generate processes, forms and decisions from natural language at design time. That is AI helping you build the rules; the rules still run as rules.
The AppHaus Explore workshop places use cases on a Value Heatmap. The guide's examples are business value against complexity, or against time to market. It also lists reach, risks and costs as possible dimensions. Its Use Case Brief has a technical view with Limitation Cards, and an ethical view asking about personal data, automated decisions and high-risk domains such as HR. The workshop ends with a Next Steps Matrix: what, when, who. The assessor's gates and report cover the same ground, in a form you can keep in Git and rerun.
An assessment is not software in production, but it drives decisions that are. Treat it with the same care.
Evidence labels. Every number carries system, measured or estimate. A decision built on estimates is provisional; say so in the report.
Authorizations. The data access gate means the solution reads data under the user's own authorizations, not a broad technical user. An assessment that assumes broad access will fail later in security review.
Approval. Anything that changes SAP data needs a person approving it, enforced in code. Mark it at assessment time so the prototype is designed for it.
Privacy. Assessment files describe business processes and may hold volumes and costs. Keep them in the private part of your repository, as Set up for Unit 14 describes for discovery notes.
Clean core. Rungs 0 to 2 often mean configuration and scheduling, not custom code. Prefer them. When you build, build on released APIs beside the core.
Cost. Lower rungs are cheaper to run. A rule costs nothing per case; a language model call costs tokens; an agent costs several calls per case. Token economics shows the arithmetic.
Reassessment. Schedule a reassessment after each fix or go-live. The thresholds in the script are judgment calls; record any you change.
Assess your own framed problem next to the samples, and write the report your next prototype starts from.
Create your assessment, unless my_problem.txt already exists from Use your own problems:
python unit14/opportunity_assessor.py new unit14/assessment/my_problem.txt
Fill it in from unit14/framing/my_frame.txt, your problem framing exercise. Set FRAME to that file. If you have no frame, use the late-delivery example: deliveries predicted from shipping history, OUTPUT: prediction, INPUT: structured.
For STANDARD, check at least one SAP source for your process step, and put the source and date in STANDARD_DETAIL.
For EXPERTS_AGREE, if you can, ask two people to decide the same ten cases. Otherwise write a number and label it estimate.
Done whenassess on my_problem.txt prints a decision with no ERROR lines, assessment.md lists your problem with its decision and next step, and your sentence names the fact that would change the decision. Keep it: rapid prototyping, next in Unit 14, starts from a problem marked PROTOTYPE.
Pick one answer for each question. The explanation appears after you choose.
1Why does the assessment check fixable causes before the shape of the work?
Answer: D. Removing a cause removes cases, and the remaining cases can have a different shape. In the invoice sample, fixing prices and scheduling automatic release left 900 blocks that suited rules. The shape question only makes sense on what is left.
2In the script, a file has RULES_WRITABLE: yes, INPUT: structured and EXPERTS_AGREE: 0.8. Which shape does pass 4 pick, and why?
Answer: C. The rules branch needs all three conditions, and 0.8 is below the 0.9 threshold. The script then checks the other shapes. CLARIFY is a separate decision, used only below 0.6.
3What does the data access gate check?
Answer: C. Google's guidance says inputs must be available when the prediction is made. Reading them under the user's own authorizations keeps the solution from seeing more than the user may. Row counts belong to the labels gate, and only for predictive work.
4You are assessing MRP exception ranking. Planners agree on 70% of cases, there is no large fixable cause, and only 150 exceptions have a recorded planner action. What would you do?
Answer: B. The output is a prediction from structured history, so the labels gate applies and fails below 200. Parking with a named condition keeps the idea. Switching to a generative assistant just to dodge the gate changes the solution, not the problem.
5Which standard SAP function does the course suggest checking before any AI for blocked supplier invoices?
Answer: C. Automatic release deletes blocking reasons that no longer apply and releases the invoice when none are left. RM08RELEASE runs it in the background. The other functions belong to planning, credit or AI-unit estimates.
6How does the script compute the hours on the rest for blocked sales orders?
Answer: A. The rest is the orders the fix doesn't remove, at the minutes the target saves. That gives 595 hours, a ceiling. The fix's 255 hours is the separate line, and adoption comes later in the business case.
7An assessment returns AUTOMATE for an output that would change SAP data. What must the build include?
Answer: B. This course requires a person to approve any change to SAP data, whatever the technology. Deterministic rules can still be wrong for a case. A broad technical user would also break the authorization principle.
8How is SAP AppHaus's Value Heatmap different from the assessor's ranked report?
Answer: A. The heatmap places use cases relative to each other, usually on value and complexity, to build agreement. The report records each decision with its evidence, and you rerun it when numbers change. The two work well together.
Ask a question
Testing: only staff see this
Stuck on something in this layer? Ask it here. Questions are answered in the order they arrive, and the answer appears under My questions.
Sign in (free) to ask a question. You can ask anonymously.
Related topics
Picked from the terms, links and tags this topic shares with others.
SAP Build Process Automation features (sap.com)— workflow, business rules in decision tables and flowcharts, attended and unattended bots, document information extraction including LLM-based extraction in the premium edition, Joule for generating artifacts
Learning SAP Signavio Process Insights (SAP Learning)— over 200 performance indicators; over 160 correction recommendations such as configuration changes or master data fixes; process improvement and automation opportunities