Orchestrate

Report an issue

Something wrong, out of date or unclear? Tell us. Corrections are prioritized over new topics. You don't need to sign in.

0 / 2000

Don't include personal or confidential company details. Reports are reviewed with AI assistance.

Buy vs. build for SAP AI

Decide whether to switch on, configure, extend, build or buy an SAP AI capability, and defend the choice with gates, scores and total cost.

Updated Oct 9, 2026Foundational 9 minDeep 35 min
Foundational layer · 9 min read

The 60-second version

Every AI idea in an SAP landscape ends at the same fork: do we use something that exists, or do we make our own? In practice there are more than two answers. There are five, and they form a ladder:

  1. Use what SAP already ships: an embedded feature or an SAP-delivered agent you switch on.
  2. Configure on SAP's platform: a Joule skill or a Joule agent you assemble in Joule Studio.
  3. Extend on SAP BTP: your own app beside S/4HANA, using SAP's AI services and released APIs.
  4. Build your own stack: your models, your hosting, your code, calling SAP through its APIs.
  5. Partner: buy a finished solution from a software partner.

Each rung down gives you more control and more to own. You own the prompts, the evaluation, the upgrades and the on-call phone. The right rung depends on the use case, not on taste. And the answer is often a mix: use SAP's feature for the standard cases and extend for the rest.

The leader's job is to make the choice on purpose, write down why, and check it again when SAP's catalog changes.

Why it matters to the business

Picking the wrong rung is expensive in three quiet ways.

Paying twice. A team spends six months building an assistant that explains blocked sales orders. Then SAP ships a similar feature, already included in the subscription. The build now competes with a free, maintained alternative.

Buying what doesn't fit. A team switches on a standard feature that handles half of its cases. Clerks stop trusting it because it fails on the company's own credit rules. Adoption drops, and nobody measures why.

Underestimating what you own. Building is not a one-off cost. A well-known Google paper on machine learning systems found that the model code is only a small part of a real system. The rest is data pipelines, glue code and configuration, and all of it needs care for years. Every custom build also has to survive SAP upgrades. SAP's clean core guidance exists to keep that survivable.

Take the running example from order-to-cash: clerks spend minutes per blocked sales order working out why it is blocked and what to do. A decision here touches cost (licences and people), speed (months until clerks get help), risk (where credit data goes) and control (who can change the logic when credit policy changes).

How SAP does it

SAP's offerings map onto the ladder. As of October 2026:

  • Use. SAP ships embedded AI features and SAP-delivered agents across its applications. Base AI is included in standard cloud subscriptions. Premium AI runs on AI Units, a prepaid credit bought each year, shared across SAP solutions, which expires after 12 months. SAP's Discovery Center has a Business AI Catalog of almost 400 features and agents, plus cost and ROI estimators. See embedded AI across SAP applications.
  • Configure. Joule Studio lets business and IT teams build Joule skills and agents on SAP's platform. SAP announced free design-time access for customers and partners through the end of 2026, subject to fair use. SAP's Sapphire 2026 guide expected general availability in Q3 2026; check the status with your account team. See Joule and Joule Studio.
  • Extend. Custom apps on SAP BTP use the generative AI hub in SAP AI Core for model access, which SAP prices by capacity units per year. They call S/4HANA through released APIs, which SAP rates as the cleanest extension level.
  • Build. You can host your own models and code anywhere. SAP's role is the data and the APIs you call.
  • Partner. Partners sell AI solutions through SAP Store. For example, SAP's guide lists Parloa's agents for SAP Service Cloud as available there. SAP's AI Agent Hub is described as a vendor-neutral place to discover, inventory, govern and evaluate agents, including ones SAP didn't build.

The sourcing ladder: a decision guide

Rung You get You own Pick it when Watch out for
Use A maintained feature inside the screen people already use Rollout, roles, measuring value The standard process fits most of your cases Low fit on company-specific rules; AI Unit consumption
Configure A Joule skill or agent built on SAP's runtime The skill logic, testing, its lifecycle You need conversation or a few steps across SAP systems Licence prerequisites; features still maturing
Extend Your app on BTP using SAP AI services Code, prompts, evaluation, operations Your rules differ from the standard, or you mix SAP and non-SAP data Build and run team for years
Build Full control of models and hosting Everything, including security and data location Hard needs SAP's services can't meet, such as a model or region Highest cost; data leaving governed systems
Partner A finished product for a niche Contract, integration, vendor oversight A partner already solved your exact problem Lock-in; partner's roadmap and stability

Three rules of thumb help leaders:

  • Rent the commodity, own the differentiator. If every competitor needs the same capability, such as summarizing a document, use what SAP or a partner sells. Build only where your process is the advantage.
  • Start high on the ladder. Check Use and Configure before funding Extend or Build. Moving down later is easy. Moving up after a build means admitting sunk cost.
  • Hybrids are normal. "Switch on for 60% of cases, extend for the rest" is often the best answer.

Questions to ask

Your team:

  • Which rung did we check first, and what evidence ruled out the higher rungs?
  • What are our hard requirements: data region, generally available only, clean core level, minimum coverage of cases?
  • What does it cost to run this for three years, not just to build it?
  • Who owns it after go-live: prompts, evaluation, upgrades, incidents?
  • What would make us change the decision, and when do we look again?

Your SAP account team:

  • Does SAP ship or plan a feature for this process, and is it Base or Premium?
  • How many AI Units would our volume use, and what happens if we run out?
  • Which parts are generally available today, and which are planned?

A partner:

  • Which SAP APIs does your solution call, and are they released for extension?
  • Where is our data processed and stored, and which model providers see it?
  • How do we leave: data export, contract terms, notice period?

Common misconceptions

  • "Buy is always cheaper." Licence and consumption costs grow with volume. At high volume, a lean extension can cost less per item. Compare total cost over several years.
  • "Build gives us control, so it's safer." Control also means you own every security fix, model change and SAP upgrade test.
  • "Once decided, decided." SAP's catalog changes every quarter. A build that made sense in January may be redundant by July.
  • "It's buy or build." Most good answers are hybrids, and there are five rungs, not two.
  • "Partners are just resellers." A partner can carry deep process know-how. But you inherit their roadmap, their stability and their exit terms.

Key terms

All course terms, A to Z, in the glossary

  • Sourcing ladder: the five options (use, configure, extend, build, partner), ordered from least to most you own.
  • Base AI and Premium AI: SAP's split between AI included in cloud subscriptions and AI paid for with AI Units.
  • AI Units: SAP's prepaid, annual credit for premium AI, pooled across solutions.
  • Joule Studio: SAP's environment for building Joule skills and agents.
  • Generative AI hub: model access and orchestration inside SAP AI Core, used by custom apps on BTP.
  • Clean core: SAP's guidance to extend through stable, released interfaces so upgrades stay safe.
  • Total cost of ownership (TCO): build cost plus run cost over the life of the solution.
  • Gate: a hard requirement that rules an option out, whatever its other strengths.

Check yourself

Pick one answer for each question. The explanation appears after you choose.

1A team proposes building a custom AI assistant for a standard SAP process. What should a leader ask first?

Answer: A. Start high on the ladder. If SAP already ships a feature, much of the value may come at no build cost, and building first risks paying twice.

2Which situation points most clearly toward extending on SAP BTP rather than using a standard feature?

Answer: C. Extending pays off where your rules differ from the standard, or where you mix SAP and non-SAP data. When the standard fits, using SAP's feature is faster and maintained for you.

3Why is "build gives us control, so it's cheaper long term" risky reasoning?

Answer: A. The model code is a small part of a real AI system. Data pipelines, glue code, configuration and upgrade testing are ongoing costs, so compare total cost over several years.

4What is a "gate" in a buy-vs-build decision?

Answer: C. Gates such as "data stays in the agreed region" or "generally available only" remove options before any scoring. A strong score can't make up for a failed hard requirement.

5Your team chose a hybrid: SAP's feature for standard cases and a BTP extension for the rest. What does that mean for operations?

Answer: D. A hybrid splits ownership. SAP maintains its feature, but your team owns the extension's prompts, tests and upgrades, so plan a team for it.

6Why should a buy-vs-build decision be revisited on a schedule?

Answer: D. SAP adds and repackages AI features often. A build that made sense earlier may now duplicate a feature you already pay for, or a new option may fit better.

7What should you ask a partner before buying their AI solution?

Answer: C. Released APIs keep your core clean and upgrades safe, and exit terms protect you from lock-in. Model novelty matters less than fit, data handling and the way out.
Deep layer · 35 min read

Mental model: gates, then score, then money, then robustness

A buy-vs-build decision is a small multi-criteria decision. Treat it like one, in four passes:

  1. Gates. Hard requirements remove options. Data residency, generally available only, clean core level, minimum share of cases covered. No score rescues a failed gate.
  2. Score. The survivors get scored 1 to 5 on a few criteria, with weights agreed before anyone scores.
  3. Money. Total cost of ownership and net value over a fixed horizon. A late go-live costs benefit too.
  4. Robustness. Does the winner survive a small change of weights? If not, the decision is a judgement call, and the memo should say so.

Behind the four passes sits one principle: own the differentiator, rent the commodity. Each rung down the ladder (use, configure, extend, build) moves more of the system into your hands. Only move down when the thing you'd own is worth owning.

How it works

flowchart LR
  U[Use case and volume] --> O[List options on the ladder]
  O --> G{Gates}
  G -- fail --> X[Out, with reason]
  G -- pass --> S[Weighted score]
  S --> M[TCO and net value]
  M --> R{Robust to weights?}
  R -- yes --> D[Decision memo]
  R -- no --> J[Judgement call, written down]
  J --> D

What each rung makes you own

The clearest way to compare rungs is to list who owns each layer of the running system.

Layer Use Configure Extend Build Partner
Model choice and hosting SAP SAP You choose, SAP hosts You Partner
Prompts and logic SAP You, in SAP's tool You You Partner
Data access and authorizations SAP roles SAP roles plus your design You, via released APIs You, via APIs Partner, via APIs
Evaluation on your data You You You You You
Runtime and scaling SAP SAP You on BTP You Partner
Upgrades and SAP release testing SAP Shared You You Partner, then you

One row never changes: evaluation on your data is always yours. No vendor can tell you how well a feature works on your blocked orders. That's why the pilot scorecard and success metrics matter on every rung.

Gates

Gates are yes/no. Typical ones for SAP AI:

  • Generally available. Planned or beta features can't carry a production commitment. SAP's own announcements give planned quarters, and quarters move.
  • Clean core level. SAP's clean core guidance has four levels. Level A uses only released, stable SAP interfaces. Level B adds classic APIs. Level C reaches into SAP-internal objects. Level D (modifications, direct table writes) is not recommended. Most companies cap AI extensions at A or B.
  • Data region. Where prompts and SAP data are processed, and which model providers see them. See data security and PII.
  • Coverage. The share of real cases an option handles. A feature that covers 30% of your blocked orders may not be worth the change effort.

Why gates instead of heavy weights? A weight lets a big strength cancel a fatal weakness. A gate doesn't.

Weighted criteria

Six criteria cover most decisions. Score each 1 (poor) to 5 (strong):

Criterion Question Evidence that anchors the score
fit How well does it handle our cases? Pilot or demo on your own sample
control Can we change the logic when the business changes? Who edits prompts, rules, thresholds
time_to_value How soon do users get help? Months to go-live
governance Does it fit our security, audit and authorization model? Role concept, logging, AI Agent Hub or equivalent
exit How hard is it to leave? Data export, contract, standard interfaces
skills Do we have the people to run it? Named owners, not a hiring plan

Agree the weights before scoring. Scoring first and then picking weights is the most common way a decision gets reverse-engineered.

Total cost of ownership

Build cost is the easy part. A three-year view has four running lines:

  • Fixed run cost: subscriptions, service plans, hosting.
  • Per-item cost: AI Units, tokens or capacity used per order handled.
  • Maintenance: a share of the build cost every year, for fixes, model changes and SAP release testing. The Google paper on ML technical debt describes why this never reaches zero: glue code, pipeline jungles, configuration and data dependencies all drift.
  • Operations: people-days for monitoring, incidents and evaluation.

The benefit side uses your own estimate of time saved, times the cases the option covers, from go-live onward. A seven-month build loses seven months of benefit. For discounting, payback and sensitivity of benefits, use the AI business case. This topic compares options; that one proves the winner is worth funding.

Robustness

Two checks before you write the memo:

  • Weight sensitivity. Move each weight up and down by 10 points. If the winner changes, the decision depends on a value judgement. Name it.
  • Score versus money. If the best-scoring option isn't the one with the highest net value, someone must decide which matters more, and write down why.

SAP's technology decision tree is a different question

SAP's Architecture Center publishes an AI Golden Path with a technology decision tree (updated April 2026). It answers which kind of AI, not who builds it:

  • A pre-check on business impact, data, feasibility and readiness, and whether plain automation would do.
  • Classic AI versus generative AI. For tabular prediction it points to the sap-rpt-1 foundation model first, and to classic ML only for constraints such as very low latency or data locality. See tabular AI.
  • For generative AI with little agency: a conversational Joule skill, or AI embedded as a background service behind a button or process.
  • For agents: a process agent for a narrow, predictable task, or a broad-scope agent, then pro-code or prompt-driven tooling.

Use it first to decide the technology, then use the sourcing ladder to decide the rung. The two answers combine: "a background generative service (tree), switched on from SAP's catalog (ladder)" is a complete decision.

Build it yourself: a buy-vs-build decision tool

You'll write a small tool that takes one use case and a list of options, and runs the four passes: gates, weighted score, three-year money, and a robustness check. It prints a recommendation and writes a short report for your decision memo.

You don't need an SAP system or any account. The script writes a made-up case for the blocked sales orders example. Then you write your own case file.

Before you start: complete Set up your computer for this course and Set up for Unit 12: local models. This walkthrough uses only Python's built-in modules, so there's nothing new to install.

flowchart LR
  C[Case file JSON] --> T[buy_vs_build.py]
  T --> G[Gates]
  T --> S[Scores]
  T --> M[TCO and net value]
  G --> R[Recommendation and report]
  S --> R
  M --> R

What you need

  • Your course folder orchestrate-course with its .venv, from the setup topics.
  • 25 to 35 minutes.
  • Cost: free. Everything runs on your computer with made-up numbers.

Step 1: Open your course folder

  1. Open VS Code, choose File > Open Folder, and open orchestrate-course.

  2. Open a terminal with Terminal > New Terminal. Turn on the virtual environment if the prompt doesn't start with (.venv):

    Windows (PowerShell):

    .venv\Scripts\Activate.ps1

    macOS or Linux:

    source .venv/bin/activate
  3. Check that the unit12 folder exists. If it doesn't, create it:

    mkdir unit12

Run every command in this topic from the course folder, not from inside unit12.

Step 2: Save the script

  1. In VS Code, choose File > New File, and save it as unit12/buy_vs_build.py.
  2. Paste this code and save again.
"""Buy vs. build decision for one SAP AI use case.

Compares sourcing options (use what SAP ships, configure on SAP's platform,
extend on BTP, build your own, buy from a partner) in three passes:

  1. Gates: hard requirements an option must meet, or it is out.
  2. Score: weighted criteria (fit, control, time to value, ...), 1 to 5.
  3. Money: total cost of ownership over N years and the net value
     (benefit minus cost), with a late start costing you benefit.

Then it tests whether the winner survives a change of weights.

Usage:
  python unit12/buy_vs_build.py --sample
  python unit12/buy_vs_build.py --case unit12/my_case.json
  python unit12/buy_vs_build.py --sample --weight exit=25 --years 5
  python unit12/buy_vs_build.py --sample --report unit12/decision_report.md

All sample numbers are made up. Replace them with your own estimates.
Only Python's built-in modules are used.
"""
import argparse
import json
import sys
from pathlib import Path

CRITERIA = ["fit", "control", "time_to_value", "governance", "exit", "skills"]
CLEAN_CORE_ORDER = "ABCD"  # A is cleanest, D is not recommended

SAMPLE = {
    "use_case": "Explain blocked sales orders to order-to-cash clerks",
    "volume_per_year": 24000,
    "minutes_saved_per_item": 6,
    "hourly_cost": 55,
    "day_rate": 900,
    "currency": "EUR",
    "requirements": {
        "needs_ga": True,
        "max_clean_core_level": "B",
        "data_region_ok": True,
        "min_coverage": 0.5,
    },
    "weights": {"fit": 25, "control": 10, "time_to_value": 20,
                "governance": 20, "exit": 10, "skills": 15},
    "options": [
        {"name": "Use: SAP-delivered feature", "path": "use",
         "ga": True, "clean_core_level": "A", "data_region_ok": True,
         "coverage": 0.55, "months_to_live": 2,
         "scores": {"fit": 2, "control": 1, "time_to_value": 5,
                    "governance": 5, "exit": 3, "skills": 5},
         "cost": {"setup_days": 15, "one_off_other": 0, "annual_fixed": 0,
                  "per_item": 0.20, "maintenance_rate": 0.0,
                  "ops_days_per_year": 5}},
        {"name": "Hybrid: SAP feature + BTP extension", "path": "use+extend",
         "ga": True, "clean_core_level": "A", "data_region_ok": True,
         "coverage": 0.85, "months_to_live": 5,
         "scores": {"fit": 4, "control": 3, "time_to_value": 4,
                    "governance": 5, "exit": 3, "skills": 3},
         "cost": {"setup_days": 70, "one_off_other": 5000,
                  "annual_fixed": 6000, "per_item": 0.24,
                  "maintenance_rate": 0.2, "ops_days_per_year": 20}},
        {"name": "Extend: custom app on BTP with gen AI hub", "path": "extend",
         "ga": True, "clean_core_level": "A", "data_region_ok": True,
         "coverage": 0.9, "months_to_live": 7,
         "scores": {"fit": 5, "control": 4, "time_to_value": 2,
                    "governance": 4, "exit": 3, "skills": 2},
         "cost": {"setup_days": 120, "one_off_other": 8000,
                  "annual_fixed": 12000, "per_item": 0.06,
                  "maintenance_rate": 0.2, "ops_days_per_year": 40}},
        {"name": "Build: own stack outside SAP", "path": "build",
         "ga": True, "clean_core_level": "A", "data_region_ok": False,
         "coverage": 0.9, "months_to_live": 9,
         "scores": {"fit": 5, "control": 5, "time_to_value": 1,
                    "governance": 2, "exit": 4, "skills": 2},
         "cost": {"setup_days": 160, "one_off_other": 10000,
                  "annual_fixed": 15000, "per_item": 0.03,
                  "maintenance_rate": 0.25, "ops_days_per_year": 60}},
        {"name": "Partner: solution from a marketplace", "path": "partner",
         "ga": False, "clean_core_level": "A", "data_region_ok": True,
         "coverage": 0.75, "months_to_live": 3,
         "scores": {"fit": 4, "control": 2, "time_to_value": 4,
                    "governance": 3, "exit": 2, "skills": 4},
         "cost": {"setup_days": 25, "one_off_other": 0,
                  "annual_fixed": 30000, "per_item": 0.0,
                  "maintenance_rate": 0.0, "ops_days_per_year": 8}},
    ],
}


def load_case(path):
    """Read a case file and stop with a clear message if something is missing."""
    try:
        case = json.loads(Path(path).read_text(encoding="utf-8"))
    except FileNotFoundError:
        sys.exit(f"Case file not found: {path}")
    except json.JSONDecodeError as err:
        sys.exit(f"{path} is not valid JSON: {err}")
    for key in ["use_case", "volume_per_year", "minutes_saved_per_item",
                "hourly_cost", "day_rate", "requirements", "weights", "options"]:
        if key not in case:
            sys.exit(f"Missing '{key}' in {path}")
    for opt in case["options"]:
        name = opt.get("name", "?")
        for key in ["ga", "clean_core_level", "data_region_ok", "coverage",
                    "months_to_live", "scores", "cost"]:
            if key not in opt:
                sys.exit(f"Option '{name}': missing '{key}'")
        for c in CRITERIA:
            value = opt["scores"].get(c)
            if not isinstance(value, (int, float)) or not 1 <= value <= 5:
                sys.exit(f"Option '{name}': score '{c}' must be 1 to 5")
        if opt["clean_core_level"] not in CLEAN_CORE_ORDER:
            sys.exit(f"Option '{name}': clean_core_level must be A, B, C or D")
    return case


def apply_weight_overrides(weights, overrides):
    """--weight exit=25 sets one weight; the rest keep their values."""
    weights = dict(weights)
    for item in overrides or []:
        key, _, value = item.partition("=")
        if key not in CRITERIA:
            sys.exit(f"Unknown criterion '{key}'. Use one of: {', '.join(CRITERIA)}")
        weights[key] = float(value)
    return weights


def check_gates(opt, req):
    """Return the list of hard requirements this option fails."""
    failed = []
    if req.get("needs_ga") and not opt["ga"]:
        failed.append("not generally available")
    limit = req.get("max_clean_core_level", "D")
    if CLEAN_CORE_ORDER.index(opt["clean_core_level"]) > CLEAN_CORE_ORDER.index(limit):
        failed.append(f"clean core level {opt['clean_core_level']} (max {limit})")
    if req.get("data_region_ok") and not opt["data_region_ok"]:
        failed.append("data leaves the agreed region")
    if opt["coverage"] < req.get("min_coverage", 0):
        failed.append(f"covers {opt['coverage']:.0%} of cases "
                      f"(min {req['min_coverage']:.0%})")
    return failed


def weighted_score(opt, weights):
    """Weighted average on the 1-5 scale, so it reads like a score."""
    total = sum(weights[c] for c in CRITERIA)
    if total <= 0:
        sys.exit("Weights must add up to more than zero")
    return sum(opt["scores"][c] * weights[c] for c in CRITERIA) / total


def money(opt, case, years):
    """Total cost of ownership and net value over the horizon (no discounting)."""
    c = opt["cost"]
    build = c["setup_days"] * case["day_rate"] + c.get("one_off_other", 0)
    run = (c.get("annual_fixed", 0)
           + c.get("per_item", 0) * case["volume_per_year"] * opt["coverage"]
           + c.get("maintenance_rate", 0) * build
           + c.get("ops_days_per_year", 0) * case["day_rate"])
    live_months = max(0, years * 12 - opt["months_to_live"])
    tco = build + run * live_months / 12
    hours = (case["volume_per_year"] * opt["coverage"]
             * case["minutes_saved_per_item"] / 60)
    benefit = hours * case["hourly_cost"] * live_months / 12
    return {"build": build, "run": run, "tco": tco,
            "benefit": benefit, "net": benefit - tco}


def winner(options, weights):
    """Best weighted score among options that passed every gate."""
    passed = [o for o in options if not o["failed"]]
    if not passed:
        return None
    return max(passed, key=lambda o: weighted_score(o, weights))


def sensitivity(options, weights, step=10):
    """Move each weight up and down by `step` points; report flips."""
    base = winner(options, weights)
    flips = []
    for c in CRITERIA:
        for delta in (step, -step):
            trial = dict(weights)
            trial[c] = max(0, trial[c] + delta)
            new = winner(options, trial)
            if new and base and new["name"] != base["name"]:
                sign = "+" if delta > 0 else "-"
                flips.append(f"{c} {sign}{step} -> {new['name']}")
    return flips


def evaluate(case, weights, years):
    options = case["options"]
    for opt in options:
        opt["failed"] = check_gates(opt, case["requirements"])
        opt["score"] = weighted_score(opt, weights)
        opt["money"] = money(opt, case, years)
    return options


def describe(case, options, weights, years):
    cur = case.get("currency", "")
    span = f"{years} year" + ("s" if years != 1 else "")
    lines = [f"Use case: {case['use_case']}",
             f"Volume: {case['volume_per_year']:,} items a year, horizon {span}",
             "Weights: " + ", ".join(f"{c}={weights[c]:g}" for c in CRITERIA),
             "", "== Gates =="]
    for o in options:
        status = "pass" if not o["failed"] else "OUT: " + "; ".join(o["failed"])
        lines.append(f"{o['name']:<44} {status}")
    lines += ["", "== Score and money (options that passed) ==",
              f"{'Option':<44} {'Score':>5} {'Build':>9} {'Run/yr':>9}"
              f" {'TCO':>9} {'Net':>9}"]
    passed = sorted([o for o in options if not o["failed"]],
                    key=lambda o: o["score"], reverse=True)
    for o in passed:
        m = o["money"]
        lines.append(f"{o['name']:<44} {o['score']:>5.2f} {m['build']:>9,.0f}"
                     f" {m['run']:>9,.0f} {m['tco']:>9,.0f} {m['net']:>9,.0f}")
    lines.append(f"(money in {cur} over {span}; run cost and benefit"
                 " start at go-live)")
    lines.append("")
    best = winner(options, weights)
    if not best:
        lines.append("Recommendation: NONE. No option passes every gate."
                     " Revisit the requirements or look for other options.")
        return lines, None
    richest = max(passed, key=lambda o: o["money"]["net"])
    lines.append(f"Recommendation: {best['name']} (score {best['score']:.2f})")
    if best["money"]["net"] < 0:
        lines.append("Warning: the recommended option loses money over the"
                     " horizon. Check the benefit assumptions before going on.")
    if richest["name"] != best["name"]:
        gap = richest["money"]["net"] - best["money"]["net"]
        lines.append(f"Note: {richest['name']} has the highest net value"
                     f" (+{gap:,.0f} {cur}). Score and money disagree:"
                     " decide which matters more and write down why.")
    flips = sensitivity(options, weights)
    if flips:
        lines.append("Fragile: the winner changes if " + "; ".join(flips))
    else:
        lines.append("Robust: no single weight change of 10 points changes"
                     " the winner.")
    return lines, best


def main():
    parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
    source = parser.add_mutually_exclusive_group(required=True)
    source.add_argument("--sample", action="store_true",
                        help="write and use a made-up case for blocked sales orders")
    source.add_argument("--case", help="path to your own case JSON")
    parser.add_argument("--weight", action="append",
                        help="override a weight, e.g. --weight exit=25")
    parser.add_argument("--years", type=int, default=3,
                        help="horizon in years (default 3)")
    parser.add_argument("--report", help="also write a Markdown report here")
    args = parser.parse_args()

    if args.sample:
        path = Path("unit12/decision_sample.json")
        path.parent.mkdir(exist_ok=True)
        path.write_text(json.dumps(SAMPLE, indent=2), encoding="utf-8")
        print(f"Wrote made-up case: {path.as_posix()}\n")
        case = load_case(path)
    else:
        case = load_case(args.case)
    if args.years < 1:
        sys.exit("--years must be 1 or more")

    weights = apply_weight_overrides(case["weights"], args.weight)
    options = evaluate(case, weights, args.years)
    lines, _ = describe(case, options, weights, args.years)
    print("\n".join(lines))

    if args.report:
        fence = "`" * 3
        text = ["# Buy vs. build decision", "",
                "All numbers are estimates. Gates, weights and scores are the",
                "decision team's judgement, recorded in the case file.", "", fence + "text"]
        Path(args.report).write_text("\n".join(text + lines + [fence, ""]),
                                     encoding="utf-8")
        print(f"\nReport written to {args.report}")


if __name__ == "__main__":
    main()

Step 3: Run it on the sample case

  1. Run the script with the made-up case:

    Windows (PowerShell):

    python unit12\buy_vs_build.py --sample

    macOS or Linux:

    python unit12/buy_vs_build.py --sample
  2. You should see this:

Wrote made-up case: unit12/decision_sample.json

Use case: Explain blocked sales orders to order-to-cash clerks
Volume: 24,000 items a year, horizon 3 years
Weights: fit=25, control=10, time_to_value=20, governance=20, exit=10, skills=15

== Gates ==
Use: SAP-delivered feature                   pass
Hybrid: SAP feature + BTP extension          pass
Extend: custom app on BTP with gen AI hub    pass
Build: own stack outside SAP                 OUT: data leaves the agreed region
Partner: solution from a marketplace         OUT: not generally available

== Score and money (options that passed) ==
Option                                       Score     Build    Run/yr       TCO       Net
Hybrid: SAP feature + BTP extension           3.85    68,000    42,496   177,781   112,069
Use: SAP-delivered feature                    3.65    13,500     7,140    33,730   171,970
Extend: custom app on BTP with gen AI hub     3.45   116,000    72,496   291,199    -4,099
(money in EUR over 3 years; run cost and benefit start at go-live)

Recommendation: Hybrid: SAP feature + BTP extension (score 3.85)
Note: Use: SAP-delivered feature has the highest net value (+59,901 EUR). Score and money disagree: decide which matters more and write down why.
Fragile: the winner changes if fit -10 -> Use: SAP-delivered feature; control -10 -> Use: SAP-delivered feature; skills +10 -> Use: SAP-delivered feature
  1. Read it pass by pass:
    • Gates. The own stack is out because, in this made-up case, its model would process data outside the agreed region. The partner solution is out because it isn't generally available yet. Neither gets scored.
    • Score. The hybrid wins on score. It covers more cases than SAP's feature alone and still runs mostly on SAP's governed platform.
    • Money. SAP's feature alone has the highest net value. It covers fewer cases, but costs far less and goes live three months earlier.
    • Robustness. The result is fragile. Lower the weight on fit or control by 10 points, or raise skills, and SAP's feature alone wins.

That last line is the real finding. This isn't a calculation the script settles. It's a choice between "more cases handled, more to own" and "fewer cases, sooner, cheaper". The memo must say which one the business picks, and why.

A valid "empty" result: Recommendation: NONE. No option passes every gate. means your requirements rule out every option. That is useful news: relax a requirement on purpose, or look for another option.

Step 4: Change a weight

  1. Suppose the team has no one to run a custom extension next year. Raise the weight on skills:

    python unit12/buy_vs_build.py --case unit12/decision_sample.json --weight skills=30
  2. The end of the output now reads:

Recommendation: Use: SAP-delivered feature (score 3.83)
Fragile: the winner changes if fit +10 -> Hybrid: SAP feature + BTP extension; control +10 -> Hybrid: SAP feature + BTP extension; skills -10 -> Hybrid: SAP feature + BTP extension
  1. Try a longer horizon with --years 5. Build-heavy options often look better over five years, because the build cost spreads over more years of benefit. Check whether that happens here.

Step 5: Write a report

  1. Run it with --report:

    python unit12/buy_vs_build.py --sample --report unit12/decision_report.md
  2. You'll see Report written to unit12/decision_report.md at the end.

  3. Open the report in VS Code. It holds the same results in a Markdown file you can paste into a decision memo.

Step 6: Write your own case

  1. Copy the sample to a new file:

    Windows (PowerShell):

    Copy-Item unit12\decision_sample.json unit12\my_case.json

    macOS or Linux:

    cp unit12/decision_sample.json unit12/my_case.json
  2. Open unit12/my_case.json and change the values. The fields:

Field Meaning
use_case One sentence: who gets help with what
volume_per_year Items handled per year, such as blocked orders
minutes_saved_per_item Your estimate of time saved per covered item
hourly_cost, day_rate, currency Loaded cost of an hour of the users' time, and of a person-day of build or ops work
requirements Gates: needs_ga, max_clean_core_level (A to D), data_region_ok, min_coverage
weights Six criteria; any positive numbers, the script normalizes them
options[].scores 1 to 5 per criterion, with a reason you can defend
options[].cost setup_days, one_off_other, annual_fixed, per_item, maintenance_rate (share of build cost per year), ops_days_per_year
options[].months_to_live Months from decision to go-live
  1. Run it:

    python unit12/buy_vs_build.py --case unit12/my_case.json

Step 7: Save your work in Git

git add unit12/buy_vs_build.py unit12/my_case.json unit12/decision_report.md
git commit -m "Add buy vs. build decision tool"

What each part of the script does

Part What it does
SAMPLE The made-up blocked sales orders case with five options, one per rung plus a hybrid
load_case Reads your JSON and stops with a clear message on a missing field, a score outside 1 to 5 or an unknown clean core level
apply_weight_overrides Applies --weight name=value from the command line
check_gates Returns the hard requirements an option fails; any failure removes it
weighted_score Weighted average of the six scores, on the same 1 to 5 scale
money Build cost, yearly run cost, TCO and net value; benefit and run cost start only after go-live
winner Highest score among options that passed every gate
sensitivity Moves each weight by 10 points up and down and lists any change of winner
describe and main Print the passes, the recommendation and warnings, and write the report

If something goes wrong

What you see What it means What to do
python: command not found or 'python' is not recognized Python isn't on your path, or the environment is off Turn on .venv as in Step 1. On macOS or Linux try python3. See the Unit 1 setup.
ModuleNotFoundError You are running another file, or a very old Python This script uses only built-in modules. Check the file name and run python --version (3.10 or later).
can't open file ... buy_vs_build.py You're in the wrong folder, or the file has another name Run from orchestrate-course; check the file is unit12/buy_vs_build.py.
error: one of the arguments --sample --case is required You didn't say which case to use Add --sample, or --case with your file.
... is not valid JSON A comma, quote or bracket is wrong in your case file Read the line and column in the message. A common cause is a comma after the last item in a list.
Option '...': score 'fit' must be 1 to 5 A score is missing or out of range Give every option all six scores.
Unknown criterion A typo in --weight Use one of fit, control, time_to_value, governance, exit, skills.
A network or proxy error Not from this script; it makes no network calls Check that you ran the right file.

The SAP way

Here is how each rung maps to SAP offerings and what to check, as of October 2026.

Use: the catalog first

  • Find candidates. SAP's Discovery Center has a Business AI Catalog of almost 400 features and agents, with cost and ROI estimators that take inputs such as customizations, hyperscalers and user counts.
  • Licensing. Base AI is included in standard cloud subscriptions. Premium AI is paid with AI Units: bought annually under SKU 8019164, pooled across SAP solutions, expiring after 12 months, with usage and balance in SAP for Me.
  • Prove it. Pilot with a comparison group, as in embedded AI across SAP applications.

Configure: Joule skills and agents

  • Joule skills suit conversational access to SAP and non-SAP systems, built with low-code tooling. SAP's Golden Path recommends custom agents over chained skills when you need complex reasoning, state across sessions or external models and tools.
  • Prerequisites for custom skills, per SAP's Architecture Center: an enterprise BTP account (CPEA, BTPEA or subscription) with SAP Build entitlement, an SAP Build Developer License to build, test and deploy, and a Joule entitlement to deploy to production.
  • Joule Studio generates requirements, code and tests from a described intent, and developers can extend the result in their own IDE with open-source agent frameworks. SAP announced free design-time access through the end of 2026, subject to fair use. SAP's pricing page says Joule Studio is accessed through SAP BTP using credits rather than AI Units. Runtime pricing after 2026 was not published in the pages we checked. See Joule agents and agent orchestration.
  • Interoperability. SAP's Sapphire 2026 guide says Joule Studio agents support MCP and A2A, and plans bi-directional A2A so third-party agents can call Joule agents (planned Q4 2026). See the Model Context Protocol.

Extend: BTP and the generative AI hub

  • Model access. The generative AI hub in SAP AI Core gives access to foundation models from SAP and other providers, plus orchestration. It needs SAP AI Core's extended service plan, and moving from the free tier to extended costs money. SAP prices the hub by capacity units per year. See the generative AI hub and running AI on BTP.
  • Apps. Side-by-side apps with CAP call S/4HANA through released APIs. See CAP and side-by-side extensions.
  • On-stack. Inside S/4HANA Cloud, extensions use the ABAP Cloud development model and released APIs (see ABAP Cloud and RAP for AI builders).
  • Clean core. SAP's guidance favours a BTP-first approach for side-by-side extensions and defines levels A to D. Record the level of every AI extension in your decision.

Build: your own stack

SAP doesn't restrict where you build. What SAP governs is the data you take out and the APIs you call. Use released APIs (level A), and give the integration user only the authorizations it needs. See agent permissions and SAP authorizations. Compare frameworks in agent frameworks compared.

Partner: SAP Store and the AI Agent Hub

  • Partners list solutions on SAP Store. SAP's Sapphire 2026 guide names Parloa's agents for SAP Service Cloud as available there.
  • SAP AI Agent Hub is described as a vendor-neutral command center to discover, inventory, govern and evaluate AI agents. Per the same guide, its registry and evaluation were available, with observability, identity and access control planned for Q3 2026. Check what's live before you rely on it to govern a partner's agent.

Build vs. SAP

Situation Choose Why
SAP ships a Base AI feature that covers most cases Use, with a pilot Included and maintained; you own only rollout and measurement
Premium feature fits, volume is high Use, after comparing per-item cost with an extension AI Units scale with volume; check the break-even
Conversational access to a few SAP actions Configure a Joule skill Runs on SAP's platform with SAP's roles
Multi-step reasoning across SAP systems Configure a Joule agent, or extend with code if you need your own framework Agents beat chained skills for state and reasoning
Company-specific rules on top of a standard feature Hybrid: use plus extend Don't rebuild the part that works
A model, region or latency SAP's services can't meet Build, with released APIs and strict authorizations Only when a gate forces it
A partner already solves your exact niche Partner, after an exit review Faster than building, but you inherit their roadmap
The feature you need is planned, not GA Prototype the gap; plan to switch Avoid a production dependency on a moving date

For a worked comparison of three rungs on one problem, see an order exception agent, three ways.

Production concerns

  • Exit plan. For every option, write down how you'd leave: data export, contract notice, which interfaces you'd rebuild. Lock-in is a cost you pay later.
  • Data terms. For bought options, check whether the vendor may use your data to improve its product, and which model providers process it. For built options, you sign those terms with each model provider yourself.
  • Authorizations. Every rung acts with some identity in SAP. Bought features use SAP roles; extensions and partner products use technical or principal-propagated users. Design them least-privilege and include them in access reviews.
  • Evaluation never moves to the vendor. Keep a test set from your own cases and re-run it after every SAP release or partner update. See building an evaluation harness.
  • Cost monitoring. Track AI Units in SAP for Me and BTP consumption monthly, per use case. Volume growth can make a cheap option expensive.
  • Clean core. Run your extensions through the ABAP test cockpit and record their clean core level. Level C or D AI extensions will slow every upgrade.
  • Ownership. Name the owner for prompts, evaluation and incidents before go-live, on every rung below Use.
  • Revisit date. Put the decision on the calendar for review after the next two SAP releases.

Pitfalls

  • Scoring before agreeing weights. The weights then bend to fit the favourite. Fix weights first, in writing.
  • Counting fit twice. Coverage drives both the fit score and the benefit in the money pass. That's deliberate here, but if you also add a "value" criterion you count it a third time.
  • Ignoring the late start. A seven-month build gives up seven months of benefit. Compare from the decision date, not from go-live.
  • Maintenance at zero. Every built system drifts: models change, APIs change, SAP releases change. Budget a yearly share of the build cost.
  • Scoring planned features as available. A planned quarter is not a go-live date. Use the GA gate.
  • One decision for a whole program. Decide per use case. A company can use SAP's feature for invoices and build for planning exceptions.
  • No written reason. Without a memo, the next team can't tell whether the choice still holds when SAP's catalog changes.

Exercise

You'll make a real sourcing decision for the feature you piloted in the last topic, and write a one-page memo. Your memo feeds the AI opportunity assessment in Unit 14.

  1. Open unit12/pilot-design.md from the embedded AI exercise. If you didn't do it, pick one process from this course's running examples: blocked sales orders, three-way match exceptions or MRP exceptions.
  2. Copy unit12/decision_sample.json to unit12/my_case.json (Step 6).
  3. Set use_case, volume_per_year and minutes_saved_per_item for your process. Write where each number comes from in your memo.
  4. Set the requirements. Decide needs_ga, max_clean_core_level, data_region_ok and min_coverage with one sentence of reasoning each.
  5. Agree the weights before you score anything. Write them in the memo first.
  6. Keep at least four options, from at least three different rungs. Score each criterion and write one line of evidence per score.
  7. Run python unit12/buy_vs_build.py --case unit12/my_case.json --report unit12/my_decision_report.md.
  8. If the result is fragile, write which value judgement decides it and who in the business should make it.
  9. Write unit12/decision-memo.md with these headings: ## Decision, ## Options considered, ## Why not the others, ## Risks and exit plan, ## Revisit when.
  10. Commit my_case.json, my_decision_report.md and decision-memo.md with Git.

Done when unit12/decision-memo.md names the chosen rung, gives a reason for every rejected option, and links to a report from your own case file that shows the gates, scores, three-year money and robustness line.

Check yourself

Pick one answer for each question. The explanation appears after you choose.

1Why does the decision tool apply gates before scoring?

Answer: D. A weight lets a big strength offset a fatal weakness, such as data leaving the agreed region. A gate removes the option outright, so no score can rescue it.

2In the sample, the hybrid wins on score but SAP's feature alone has the higher net value. What should you do?

Answer: B. The tool shows the disagreement and the fragile weights, but it can't settle a trade-off between more cases handled and lower cost with an earlier start. The memo must record who chose and why.

3Which layer stays your responsibility on every rung of the ladder?

Answer: A. Hosting, prompts and upgrade testing shift between SAP, you and partners depending on the rung. Only you can measure how well an option works on your own cases, so evaluation is always yours.

4Why does the money pass start benefit and run cost at go-live rather than at the decision?

Answer: C. Benefit only flows once users get help. An option that takes seven months to go live loses seven months of benefit, which the comparison must show.

5A team scores a custom BTP extension with zero yearly maintenance. What's wrong?

Answer: A. The ML technical debt paper shows that glue code, pipelines, configuration and data dependencies keep costing after go-live. SAP releases and model changes add testing work, so budget a yearly share of the build cost.

6What do you need, per SAP's Architecture Center, to build and deploy a custom Joule skill to production?

Answer: D. SAP's Golden Path lists an enterprise BTP account with SAP Build entitlement, an SAP Build Developer License to build and test, and a Joule entitlement for production. Check these before scoring Configure as quick to deliver.

7Your extension needs the generative AI hub, and your company only has SAP AI Core on the free tier. What follows?

Answer: C. SAP's tutorial says the generative AI hub is available only with the extended service plan, and upgrading from the free tier incurs cost. SAP prices the hub by capacity units per year, so it belongs in the fixed or per-item run cost.

8The tool reports "Fragile" for your case. What does a good decision memo do with that?

Answer: A. A fragile result means the answer depends on a value judgement, such as how much control matters. Changing weights after the fact reverse-engineers the answer; naming the judgement and its owner makes the decision honest.

Sources

Sign in to track your progress

We'll email you a one-time sign-in link. No password needed.

or

Tell us a little about you

Optional, every field. It helps us pitch answers to your questions at the right level and decide which topics to write next. It is never shown publicly, and you can change or clear it anytime from the account menu.

SAP areas you work in