What Joule does for users, how Joule skills and Joule agents differ, what changed with the new Joule Studio in 2026, and how to tell which one a request needs.
Joule is SAP's AI assistant. People type a request inside SAP cloud applications, and Joule answers, takes them to the right screen, or does a task for them.
What Joule can do comes from a catalog of two kinds of building blocks:
A Joule skill does one fixed job. "Show sales order 4711" is a skill: it reads the order and shows it, the same way every time.
A Joule agent works toward a goal. "Why is order 4711 blocked and what should happen next?" needs an agent: it decides which steps to take, often by calling several skills in turn.
SAP ships many skills and agents. Joule Studio is where your team builds its own. As of October 2026 there are two generations of it: the Joule Studio inside SAP Build, described in SAP's April 2026 architecture guidance, and a new, fully managed Joule Studio that SAP announced in May 2026 and is still rolling out.
The decision for you: when someone says "let's do this with Joule", ask whether it is a fixed job (a skill), a goal with judgement in it (an agent), or something SAP already ships.
Take the running example from this course: sales orders blocked by the credit check in order-to-cash. A clerk today opens the order, looks up the customer's exposure, checks the credit policy and files a review request. That is four screens and some judgement for every blocked order.
Joule changes where that work happens, in three steps of ambition:
Request
What answers it
Business effect
"Show me order 4711"
A skill
Saves a few clicks; low risk
"Take me to order 4711"
A navigational skill
Helps occasional users find the right app
"Why is 4711 blocked and what next?"
An agent using several skills
Saves minutes per order; needs controls
The value grows down the table, and so does the risk. A skill that reads an order can show wrong data at worst. An agent that files requests or changes data can do the wrong thing, many times, quickly.
Cost follows the same shape. SAP's commercial model splits Joule into Joule Base, which comes with your SAP cloud subscription, and Joule Premium, paid for in AI Units. SAP Learning's Joule course places navigational, informational, transactional and simple analytical capabilities in Base, and "agent actions executed by Joule Agents" in Premium. So moving from skills to agents is also a move from included to metered.
Joule runs as one central instance on SAP BTP, shared across SAP cloud applications. SAP Learning says all applications in that setup must use the same SAP Cloud Identity Services tenant, so Joule knows who the user is everywhere.
Joule Studio in SAP Build lets teams build skills and agents with low-code tools. SAP's Architecture Center describes skills that call OData APIs through destinations, and agents that run on SAP AI Core. Deploying registers them in Joule's catalog.
The new Joule Studio, announced on 13 May 2026, is a fully managed service for agents, apps and workflows. It adds pro-code development with frameworks such as LangChain, Pydantic AI and LlamaIndex, an embedded n8n environment, and a managed Joule Studio runtime.
Status. SAP's Sapphire 2026 guide said the new Joule Studio was in an early customer adoption program with general availability "expected in Q3 2026". We did not find a general availability announcement in the pages we opened. SAP's August 2026 reference architecture still says some capabilities are "not yet generally available".
Free periods. SAP offers free design-time access for customers and partners through the end of 2026, under fair-use limits. Its September 2026 announcement says the runtime is free "through October 2026". Both are for customers and partners, and the runtime window ends this month.
Work down this list for each request your business wants Joule to handle.
Question
If yes
Example from order-to-cash
Does SAP already ship a skill or agent for it in our applications?
Turn it on and measure adoption
Ask your account team what exists for your release
Is it one fixed job with clear inputs and one system call?
Build a skill
Show an order, list blocked orders, open an app
Does it need several steps, and the steps depend on what it finds?
Build an agent that uses skills
Explain a block and propose the right reviewer
Does it change data or start a process?
Add a confirmation step, whatever you build
File a credit review request
Is the logic large, custom, or owned by a pro-code team?
Consider pro-code, connected to Joule
An agent that already exists in Python
Two rules sit across every row. First, Joule has to pick your skill or agent for the right requests, so test that with real requests from real users before go-live. Second, SAP's own guidance says to "always implement confirmation dialogs" for create, update and delete operations.
Which Joule capabilities are in Joule Base for our applications, and which consume AI Units? What would a year cost at our volumes?
Is our Joule set up as one instance across our SAP cloud applications, with one SAP Cloud Identity Services tenant?
Which Joule Studio do we have access to: the one in SAP Build, the new managed one, or both? What happens to our free access after October and after December 2026?
For each custom skill or agent: what does it change, who confirms it, and how do we test that Joule picks it for the right requests?
Who owns the catalog of custom skills and agents, and who retires them?
If we already have a pro-code agent, do we rebuild it in Joule Studio or connect it to Joule?
"Joule is a chatbot on top of SAP." Joule is an entry point to a catalog of skills and agents. What it can do depends on what is in that catalog for your applications.
"Everything should be an agent." A fixed job is cheaper, more predictable and easier to test as a skill. Agents earn their place when the steps depend on what they find.
"Joule Studio is one product with one status." As of October 2026 there is the Joule Studio in SAP Build and a new, managed Joule Studio still moving toward general availability. Ask which one a proposal means.
"A confirmation step is the security control." Confirmation stops accidents. Who may do what is still decided by the user's identity and SAP authorizations.
"Free Joule Studio means free agents in production." The free offers SAP announced are time-limited and for customers and partners. Plan the running cost separately.
Pick one answer for each question. The explanation appears after you choose.
1A sales lead wants Joule to "show the open items for customer 10023". What should the team build, if SAP doesn't ship it?
Answer: B. Reading one customer's open items is a fixed job with a clear input and one system call, which is what a skill is for. An agent would add planning, cost and risk without adding value here.
2When does an agent earn its place over a skill?
Answer: C. An agent plans and chooses its next step from what it has learned, such as checking credit only when the block is a credit block. If the steps are always the same, a skill does the job more cheaply and predictably.
3Your CFO asks why moving from skills to agents changes the budget. What is the accurate answer?
Answer: D. SAP's commercial model puts navigational, informational, transactional and simple analytical capabilities in Joule Base. Agent actions by Joule Agents are Joule Premium and draw on AI Units, which are bought annually.
4A partner proposes a demo "in Joule Studio" next month. What should you ask first?
Answer: A. As of October 2026 there is the Joule Studio in SAP Build and a new, managed Joule Studio that SAP said was in early adoption with GA expected in Q3 2026. Free runtime access ends in October 2026, so access and terms decide what can be demonstrated.
5A custom skill files credit review requests. What does SAP's guidance say it must include?
Answer: C. SAP's Architecture Center says to always implement confirmation dialogs for create, update and delete operations. A confirmation prevents accidents; the user's authorizations still decide what they may do.
6Why does SAP ask all applications in a unified Joule setup to share one SAP Cloud Identity Services tenant?
Answer: B. One identity tenant gives each user one consistent identity across the applications Joule serves. That is what lets skills and agents act as that user rather than as an all-powerful technical account.
7Someone says "we got Joule Studio free, so our agents will run free". What is wrong?
Answer: D. SAP announced free design-time access through the end of 2026 under fair-use limits, and a free runtime through October 2026. Production cost after those windows needs its own answer from your account team.
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.
Joule is a router in front of a catalog. A request comes in; Joule decides which entry in the catalog should handle it; that entry does its work as the signed-in user; Joule shows the result.
Everything Joule Studio does is about adding entries to that catalog:
A skill is an entry with a fixed recipe: a purpose, parameters, one or more actions, and a reply.
An agent is an entry with a goal, instructions and tools. Its tools are usually skills, plus retrieval over documents (RAG) and, in SAP's newer architecture, tools reached over MCP.
So two things decide whether your custom work succeeds: whether Joule picks your entry for the right requests, and whether the entry does the right thing once picked. You test both separately. That is exactly what the lab in this topic builds on your laptop.
sequenceDiagram
participant U as User in SAP app
participant J as Joule
participant C as Catalog
participant S as Skill or agent
participant B as SAP backend
U->>J: "Why is 4711 blocked?"
J->>C: match request to an entry
C-->>J: agent: order exceptions
J->>S: run with user's identity
S->>B: read order (OData)
B-->>S: order data
S-->>J: answer, or ask to confirm
J-->>U: reply
What each part does, as SAP describes it:
One Joule, many applications. SAP Learning describes a unified Joule instance on SAP BTP, with dev, test and prod, shared across SAP cloud applications. All of them must use the same SAP Cloud Identity Services tenant.
The catalog. SAP's golden path for agents says deploying from Joule Studio "creates all necessary Joule artifacts (scenarios, dialog functions)" and registers the agent in Joule's catalog. Those are the entries Joule chooses from. SAP does not describe the matching logic in the pages we opened, so don't build assumptions on it; test it.
The entry runs. A skill calls APIs through actions and destinations. A low-code agent runs on SAP AI Core and calls its tools.
Identity. The new architecture adds an Agent Gateway, which SAP's August 2026 reference architecture describes as handling "authentication, principal propagation, policy enforcement and tenancy" for agents.
SAP's Architecture Center lists the build steps for a skill in Joule Studio in SAP Build:
Create a project in Joule Studio from the SAP Build lobby.
Design the skill logic by defining parameters and actions.
Configure destinations, using action projects that wrap OData APIs.
Map data with the skill editor and the Formula Editor.
Test in the standalone Joule assistant.
It names the kinds of skill as navigation, list and search, transactions (create, update, delete), decision-making and data access. It describes two places to deploy: a standalone environment for isolated testing and a shared environment where users of SAP Build Work Zone, SAP S/4HANA and SAP SuccessFactors reach it. And it gives one hard rule: confirmation dialogs for every transactional operation.
SAP Learning puts the difference this way: skills handle "simpler, rule-based tasks", agents handle goal-oriented scenarios "that require dynamic planning and reasoning". The golden path describes low-code agents with "planning, RAG (Retrieval-Augmented Generation) and tool chaining", running on SAP AI Core "with built-in metering, tracing and security".
You have built this loop already in Agents from first principles and Multi-step agents. Joule Studio's agent builder gives you the same loop as configuration: instructions and tools, run on SAP AI Core.
As of 6 October 2026, you will meet both in SAP material:
Joule Studio in SAP Build
New Joule Studio (announced May 2026)
What you build
Skills and agents
Agents, apps and workflows
How you build
Low-code builders in SAP Build
Browser-based low-code builder, or your own IDE with the Joule Studio CLI and a coding agent over MCP
Pro-code frameworks
Pro-code agents connect separately (Bring Your Own Agent, A2A)
LangChain, Pydantic AI and LlamaIndex named by SAP; embedded n8n
Where it runs
Agents on SAP AI Core
SAP-managed Joule Studio runtime
Status
Documented by SAP's golden path (April 2026)
Early adoption; GA "expected in Q3 2026" per Sapphire guide; not confirmed in pages we opened
The August 2026 reference architecture says the low-code and pro-code flows in the new Joule Studio "produce the same deployable artifact, automatically registered with Joule". It also describes intent-based development: a six-phase flow from intent through requirements, specification, code generation and testing to deployment.
You will build a small assistant on your laptop that works the way the mental model says: a catalog of six skills, a router that picks one skill per request, and an agent that chains skills toward a goal. One skill changes something, so it stops and asks before it runs. Then you measure how often the router picks the right skill.
This is a teaching model, not SAP's code. Joule's real matching uses SAP's own logic, which SAP doesn't describe in the pages we opened. The stand-in router here matches words in each skill's description, which makes one thing very visible: in this router, the description is what gets a skill picked, and you can measure how well.
Before you start: complete Set up your computer for this course and Set up for Unit 9, which creates the orchestrate-course folder, the .venv virtual environment and the unit09 subfolder. This walkthrough doesn't repeat those steps.
flowchart LR
R[Request] --> RT[Router]
RT -->|one skill| SK[Skill]
R2[Goal] --> AG[Agent]
AG -->|several skills| SK
SK --> D[Made-up SAP data]
SK -->|changes data?| CF[Confirm first]
In VS Code, right-click unit09, choose New File, name it joule_lab.py, paste the code below and save.
"""Unit 9: a Joule-shaped assistant on your laptop.
Skills are small, fixed jobs with a description, parameters, one action and a reply.
A router picks one skill per request. An agent chains several skills toward a goal.
Transactional skills never run without an explicit confirmation.
This is a teaching model of the ideas, not SAP's implementation. All data is made up.
Nothing here changes SAP data: the one "write" appends a line to a local file.
python unit09/joule_lab.py ask "show me sales order 4711"
python unit09/joule_lab.py ask "request a credit review for order 4711" --confirm yes
python unit09/joule_lab.py agent "why is order 4711 blocked and what should happen next"
python unit09/joule_lab.py eval
"""
import argparse
import json
import re
import sys
import time
from pathlib import Path
HERE = Path(__file__).resolve().parent
REQUESTS = HERE / "joule_requests.jsonl"
# ---------------------------------------------------------------- made-up, SAP-shaped data
# Field names match the sales order examples in earlier units; "block_note" is made up.
ORDERS = {
"4711": {"SalesOrder": "4711", "SoldToParty": "10023", "TotalNetAmount": "1800.00",
"TransactionCurrency": "EUR", "block_note": "Blocked by the credit check."},
"4723": {"SalesOrder": "4723", "SoldToParty": "10051", "TotalNetAmount": "640.00",
"TransactionCurrency": "EUR", "block_note": "Incomplete: delivery address data missing."},
"4725": {"SalesOrder": "4725", "SoldToParty": "10077", "TotalNetAmount": "3900.00",
"TransactionCurrency": "EUR", "block_note": "Pricing: customer disputes the price."},
"4730": {"SalesOrder": "4730", "SoldToParty": "10023", "TotalNetAmount": "250.00",
"TransactionCurrency": "EUR", "block_note": ""},
}
CREDIT = {
"10023": {"credit_limit": 50000.0, "open_items": 50700.0, "currency": "EUR"},
"10051": {"credit_limit": 30000.0, "open_items": 4100.0, "currency": "EUR"},
"10077": {"credit_limit": 80000.0, "open_items": 12000.0, "currency": "EUR"},
}
POLICY = [
"Credit blocks up to 5 percent over the limit are decided by the credit manager.",
"Credit blocks more than 5 percent over the limit are decided by the head of finance.",
"Incomplete orders are fixed by completing master data, never by a credit review.",
]
# ---------------------------------------------------------------- the actions skills call
def act_show_order(p):
order = ORDERS.get(p["order"])
if not order:
return {"error": f"sales order {p['order']} not found"}
return order
def act_list_blocked(p):
return {"blocked": [o["SalesOrder"] for o in ORDERS.values() if o["block_note"]]}
def act_credit(p):
credit = CREDIT.get(p["customer"])
if not credit:
return {"error": f"customer {p['customer']} not found"}
return {"customer": p["customer"], **credit}
def act_open_app(p):
# Made-up link. A real navigational skill would return a link into your launchpad.
return {"link": f"https://launchpad.example.com/#orders/{p['order']}"}
def act_policy(p):
# Keep only the passages that share the most words with the question.
words = set(tokens(p["question"]))
scored = [(len(words & set(tokens(line))), line) for line in POLICY]
best = max(score for score, _ in scored)
hits = [line for score, line in scored if score == best and score > 1]
return {"passages": hits or ["No policy passage matched."]}
def act_request_review(p):
if p["order"] not in ORDERS:
return {"error": f"sales order {p['order']} not found"}
request_id = f"CR-{p['order']}" # one per order: a retry files nothing new
existing = REQUESTS.read_text(encoding="utf-8") if REQUESTS.exists() else ""
if f'"request_id": "{request_id}"' in existing:
return {"request_id": request_id, "status": "already filed"}
with open(REQUESTS, "a", encoding="utf-8") as handle:
handle.write(json.dumps({"request_id": request_id, "order": p["order"],
"filed_at": time.strftime("%Y-%m-%dT%H:%M:%S")}) + "\n")
return {"request_id": request_id, "status": "filed"}
# ---------------------------------------------------------------- the skill catalog
# Each skill: what it is for (description), what it needs (parameters), what it does
# (action), what the user sees (reply), and whether it changes something (confirm).
SKILLS = {
"show_sales_order": {
"kind": "informational",
"description": "Show one sales order: customer, net value, currency and block reason.",
"parameters": ["order"],
"action": act_show_order,
"reply": lambda r: (f"Order {r['SalesOrder']}: customer {r['SoldToParty']}, "
f"{r['TotalNetAmount']} {r['TransactionCurrency']}. "
f"Block: {r['block_note'] or 'none'}"),
"confirm": False,
},
"list_blocked_orders": {
"kind": "informational",
"description": "List every order that is blocked right now.",
"parameters": [],
"action": act_list_blocked,
"reply": lambda r: "Blocked orders: " + ", ".join(r["blocked"]),
"confirm": False,
},
"show_credit_exposure": {
"kind": "informational",
"description": "Show a customer's credit limit and open items.",
"parameters": ["customer"],
"action": act_credit,
"reply": lambda r: (f"Customer {r['customer']}: limit {r['credit_limit']:,.0f}, "
f"open items {r['open_items']:,.0f} {r['currency']}"),
"confirm": False,
},
"open_sales_order_app": {
"kind": "navigational",
"description": "Open the app screen so I can work on an order myself.",
"parameters": ["order"],
"action": act_open_app,
"reply": lambda r: f"Open this link: {r['link']}",
"confirm": False,
},
"credit_policy": {
"kind": "informational",
"description": "Answer questions about the credit policy: who decides, rules, limits.",
"parameters": ["question"],
"action": act_policy,
"reply": lambda r: " ".join(r["passages"]),
"confirm": False,
},
"request_credit_review": {
"kind": "transactional",
"description": "Request a credit review for a blocked order.",
"parameters": ["order"],
"action": act_request_review,
"reply": lambda r: f"Credit review {r['request_id']}: {r['status']}.",
"confirm": True,
},
}
STOP = {"a", "an", "the", "for", "of", "to", "me", "my", "i", "is", "and", "so", "can", "on",
"it", "what", "who", "please", "show", "all", "this", "that", "with", "do", "does", "be"}
def tokens(text):
"""Lowercase words without stop words, with a crude plural strip ('orders' -> 'order')."""
words = re.findall(r"[a-z]+", text.lower())
return [w[:-1] if w.endswith("s") and len(w) > 3 else w for w in words if w not in STOP]
def route(utterance):
"""Pick the skill whose description shares the most words with the request.
A stand-in for the model-based selection a real assistant uses. Returns (skill, score),
or (None, 0) when nothing matches or two skills tie: then the assistant should say so.
"""
asked = set(tokens(utterance))
scores = sorted(((len(asked & set(tokens(s["description"]))), name)
for name, s in SKILLS.items()), reverse=True)
best, runner_up = scores[0], scores[1]
if best[0] == 0 or best[0] == runner_up[0]:
return None, best[0]
return best[1], best[0]
def extract(skill, utterance):
"""Fill parameters from the text: 4-digit order numbers, 5-digit customer numbers."""
found = {}
if "order" in skill["parameters"]:
m = re.search(r"\b(\d{4})\b", utterance)
if m:
found["order"] = m.group(1)
if "customer" in skill["parameters"]:
m = re.search(r"\b(\d{5})\b", utterance)
if m:
found["customer"] = m.group(1)
if "question" in skill["parameters"]:
found["question"] = utterance
return found
def run_skill(name, params, confirm):
"""Run one skill. Transactional skills stop and ask unless confirm is 'yes'."""
skill = SKILLS[name]
missing = [p for p in skill["parameters"] if p not in params]
if missing:
return {"status": "need_input", "text": f"Which {missing[0]}? ({name} needs it.)"}
if skill["confirm"] and confirm != "yes":
if confirm == "no":
return {"status": "cancelled", "text": "Cancelled. Nothing was changed."}
return {"status": "confirm",
"text": f"I am about to run {name} with {params}. Run again with --confirm yes "
"to go ahead, or --confirm no to cancel."}
result = skill["action"](params)
if "error" in result:
return {"status": "error", "text": result["error"]}
return {"status": "done", "text": skill["reply"](result), "result": result}
def ask(utterance, confirm=None):
name, score = route(utterance)
if not name:
return name, {"status": "no_skill",
"text": "I don't have a skill for that. Try rephrasing, or ask about an order."}
return name, run_skill(name, extract(SKILLS[name], utterance), confirm)
def agent(goal, confirm=None):
"""Chain skills toward a goal. A fixed plan stands in for a model's planning step."""
trace = []
m = re.search(r"\b(\d{4})\b", goal)
if not m:
return trace, "Which order should I look at?"
order = m.group(1)
def step(name, params, conf=None):
out = run_skill(name, params, conf)
trace.append({"skill": name, "params": params, "status": out["status"]})
return out
header = step("show_sales_order", {"order": order})
if header["status"] != "done":
return trace, header["text"]
note = header["result"]["block_note"].lower()
if not note:
return trace, f"Order {order} is not blocked. Nothing to do."
if not note.startswith("blocked by the credit"):
return trace, f"Order {order}: {header['result']['block_note']} This is not a credit block, so no credit review."
customer = header["result"]["SoldToParty"]
credit = step("show_credit_exposure", {"customer": customer})["result"]
exposure = credit["open_items"] + float(header["result"]["TotalNetAmount"])
over = (exposure / credit["credit_limit"] - 1) * 100
step("credit_policy", {"question": "who decides a credit block over the limit"})
decider = "the credit manager" if round(over, 2) <= 5 else "the head of finance"
review = step("request_credit_review", {"order": order}, confirm)
summary = (f"Order {order} is credit-blocked: exposure {exposure:,.0f} {credit['currency']} is "
f"{over:.1f}% over the {credit['credit_limit']:,.0f} limit, so {decider} decides "
f"under the credit policy.\nNext: {review['text']}")
return trace, summary
# ---------------------------------------------------------------- evaluation
EVAL = [
("show me sales order 4711", "show_sales_order"),
("what is the block reason on order 4723", "show_sales_order"),
("list blocked sales orders", "list_blocked_orders"),
("which orders are blocked right now", "list_blocked_orders"),
("credit limit for customer 10023", "show_credit_exposure"),
("how much does customer 10051 owe in open items", "show_credit_exposure"),
("open order 4725 in the app", "open_sales_order_app"),
("take me to the screen for 4730 so I can fix it myself", "open_sales_order_app"),
("who decides on a credit block over the limit", "credit_policy"),
("what are the policy rules for incomplete orders", "credit_policy"),
("request a credit review for order 4711", "request_credit_review"),
("please get finance to look at the credit block on 4711", "request_credit_review"),
("book a meeting room for tomorrow", None),
]
def evaluate(out=None):
rows = []
for utterance, expected in EVAL:
got, score = route(utterance)
rows.append({"request": utterance, "expected": expected, "got": got, "score": score,
"ok": got == expected})
print(f"{'OK ' if got == expected else 'MISS'} {utterance!r:60} expected={expected} got={got}")
right = sum(r["ok"] for r in rows)
print(f"\nRouting accuracy: {right}/{len(rows)} = {right / len(rows):.0%}")
if out:
report = {"run_at": time.strftime("%Y-%m-%dT%H:%M:%S"), "skills": len(SKILLS),
"correct": right, "total": len(rows), "rows": rows}
Path(out).write_text(json.dumps(report, indent=2), encoding="utf-8")
print(f"Report saved to {out}")
def main():
parser = argparse.ArgumentParser(description="A Joule-shaped assistant on made-up SAP data.")
sub = parser.add_subparsers(dest="cmd", required=True)
p_ask = sub.add_parser("ask", help="route one request to one skill")
p_ask.add_argument("text")
p_ask.add_argument("--confirm", choices=["yes", "no"])
p_agent = sub.add_parser("agent", help="chain several skills toward a goal")
p_agent.add_argument("text")
p_agent.add_argument("--confirm", choices=["yes", "no"])
p_eval = sub.add_parser("eval", help="measure the router on a fixed set of requests")
p_eval.add_argument("--out", help="also save the results as a JSON report")
sub.add_parser("skills", help="print the skill catalog")
args = parser.parse_args()
if args.cmd == "ask":
name, out = ask(args.text, args.confirm)
print(f"skill: {name}\nstatus: {out['status']}\nreply: {out['text']}")
elif args.cmd == "agent":
trace, summary = agent(args.text, args.confirm)
for i, t in enumerate(trace, 1):
print(f"step {i}: {t['skill']} {t['params']} -> {t['status']}")
print(f"\n{summary}")
elif args.cmd == "eval":
evaluate(args.out)
else:
for name, s in SKILLS.items():
flag = " (asks for confirmation)" if s["confirm"] else ""
print(f"{name:22} {s['kind']:14} {s['description']}{flag}")
if __name__ == "__main__":
sys.exit(main())
show_sales_order informational Show one sales order: customer, net value, currency and block reason.
list_blocked_orders informational List every order that is blocked right now.
show_credit_exposure informational Show a customer's credit limit and open items.
open_sales_order_app navigational Open the app screen so I can work on an order myself.
credit_policy informational Answer questions about the credit policy: who decides, rules, limits.
request_credit_review transactional Request a credit review for a blocked order. (asks for confirmation)
The kinds mirror the ones SAP names for Joule: navigational, informational and transactional. Only the transactional one asks for confirmation.
python unit09/joule_lab.py ask "show me sales order 4711"
skill: show_sales_order
status: done
reply: Order 4711: customer 10023, 1800.00 EUR. Block: Blocked by the credit check.
Ask without the order number:
python unit09/joule_lab.py ask "show me sales order"
skill: show_sales_order
status: need_input
reply: Which order? (show_sales_order needs it.)
The router found the skill, but a parameter is missing, so it asks instead of guessing.
Ask for something no skill covers:
python unit09/joule_lab.py ask "book a meeting room for tomorrow"
skill: None
status: no_skill
reply: I don't have a skill for that. Try rephrasing, or ask about an order.
This "empty" result is correct behaviour. An assistant that always picks something will run the wrong skill.
Ask the policy question:
python unit09/joule_lab.py ask "who decides on a credit block over the limit"
skill: credit_policy
status: done
reply: Credit blocks up to 5 percent over the limit are decided by the credit manager. Credit blocks more than 5 percent over the limit are decided by the head of finance.
python unit09/joule_lab.py ask "request a credit review for order 4711"
skill: request_credit_review
status: confirm
reply: I am about to run request_credit_review with {'order': '4711'}. Run again with --confirm yes to go ahead, or --confirm no to cancel.
Confirm it:
python unit09/joule_lab.py ask "request a credit review for order 4711" --confirm yes
python unit09/joule_lab.py agent "why is order 4711 blocked and what should happen next"
What success looks like:
step 1: show_sales_order {'order': '4711'} -> done
step 2: show_credit_exposure {'customer': '10023'} -> done
step 3: credit_policy {'question': 'who decides a credit block over the limit'} -> done
step 4: request_credit_review {'order': '4711'} -> confirm
Order 4711 is credit-blocked: exposure 52,500 EUR is 5.0% over the 50,000 limit, so the credit manager decides under the credit policy.
Next: I am about to run request_credit_review with {'order': '4711'}. Run again with --confirm yes to go ahead, or --confirm no to cancel.
The agent called four skills, chose the second one only because the first showed a credit block, and stopped before the one that writes.
Try order 4723 instead. The agent stops after one step: the block is missing address data, so a credit review would be the wrong action.
python unit09/joule_lab.py agent "why is order 4723 blocked"
step 1: show_sales_order {'order': '4723'} -> done
Order 4723: Incomplete: delivery address data missing. This is not a credit block, so no credit review.
What success looks like (the request column is shortened here):
OK 'show me sales order 4711' expected=show_sales_order got=show_sales_order
...
MISS 'please get finance to look at the credit block on 4711' expected=request_credit_review got=None
OK 'book a meeting room for tomorrow' expected=None got=None
Routing accuracy: 12/13 = 92%
Report saved to unit09/joule_eval.json
The one miss is a paraphrase: the request shares only "credit" with the right description, which ties with the policy skill, so the router declines. A model-based router handles paraphrases better than word matching, but the lesson holds: you only know how often Joule picks your skill if you keep a list of real requests and test it.
The lab's SKILLS entries map onto what SAP's golden path describes:
Lab
Joule Studio in SAP Build
description
The skill's name and stated purpose (how Joule uses it for matching isn't described in the pages we opened)
parameters
Skill parameters
act_* function
Actions; an action project wraps an OData API, reached through a destination
reply
The response, with data mapped in the skill editor and Formula Editor
confirm: True
A confirmation dialog, which SAP says to always add for create, update and delete
Running ask locally
Testing in the standalone Joule assistant before the shared environment
The golden path also says skills can start SAP Build Process Automation workflows. That is how a request such as the lab's credit review would usually reach a person in a real system: as a workflow task, not a direct write.
Low-code, Joule Studio in SAP Build. You define the agent's instructions and configure its tools. The golden path says these agents run on SAP AI Core, and that deploying creates the Joule scenarios and dialog functions and registers the agent in Joule's catalog.
Pro-code, connected to Joule. An agent you write yourself, for example with the frameworks from Agent frameworks compared, connects through SAP's Bring Your Own Agent pattern by exposing an A2A server endpoint.
The new Joule Studio. SAP's August 2026 reference architecture describes a browser builder and a pro-code flow with the Joule Studio CLI and a coding agent connected over MCP, deploying to the SAP-managed runtime. SAP's May 2026 announcement names LangChain, Pydantic AI and LlamaIndex for pro-code work.
SAP's 28 September 2026 announcement says the Joule Studio runtime "helps determine whether an action should execute at all", by applying business authorization, role-based policy and process context. The runtime uses NVIDIA OpenShell for isolation. SAP's May 2026 announcement adds persistent memory in SAP HANA Cloud.
The golden path pages for skills and agents in SAP Build were updated on 23 April 2026 and describe them without a preview label.
The new Joule Studio: early adoption, with general availability "expected in Q3 2026" according to the Sapphire 2026 guide. The guide also lists the Joule Studio runtime as planned for GA in Q3 2026. We found no GA announcement in the pages opened on 6 October 2026.
Third-party agents calling Joule Agents over A2A in both directions: planned for Q4 2026 per the Sapphire guide.
SAP's August 2026 reference architecture: "some components and capabilities of this reference architecture are not yet generally available".
Joule Base is included in standard cloud subscriptions, per SAP's pricing page.
Joule Premium capabilities, including agent actions, consume AI Units. SAP's pricing page says AI Units are bought annually and expire after 12 months, and Premium packages range from 8 to 1 AI Units per user per month by volume. SAP Learning adds overages at 2 AI Units per 1,000 requests.
Joule Studio is accessed through SAP BTP credits, per the pricing page. Free design-time access runs through the end of 2026 and the free runtime through October 2026, for customers and partners.
Prices and terms change. Confirm them with your account team before a business case.
Identity and SAP authorizations. A skill or agent must act as the signed-in user, so SAP's own authorization checks apply. SAP's unified Joule setup needs one SAP Cloud Identity Services tenant; the new architecture adds the Agent Gateway for principal propagation and policy. Don't let a destination use a technical user with broad rights "to make it work". See Grounding on SAP data with authorizations.
Confirmation is not authorization. The lab's --confirm yes stops accidents. It doesn't check who confirmed. In SAP, the user's roles decide what the confirmed action may do.
Evaluation. Keep an evaluation set per skill: real requests that should reach it and near misses that shouldn't. Re-run it whenever you add or reword a skill, because a new description can steal requests from an old one.
Catalog hygiene. Every custom entry competes for requests. Give each an owner, a description reviewed like an API contract, and a retirement date.
Landscapes. SAP Learning describes dev, test and prod for the unified Joule instance. Test new skills against non-production systems first, as SAP's standalone and shared environments allow.
Cost. Agent actions consume AI Units; skills mostly don't. Count requests in testing and estimate a year at production volume before you choose an agent over a skill.
Clean core. Skills reach SAP through APIs and destinations, and agents through skills and tools. Neither needs a modification in the core. Prefer released APIs, as in SAP tools for agents.
Vague descriptions. In the lab, "Handles orders" ties with every other order skill. In any catalog, say what the skill does, on what, and for whom.
Agents for fixed jobs. If the steps never change, an agent adds cost and variance for nothing.
Writing without confirmation. SAP's guidance is explicit, and the lab shows how little code it takes.
Always answering. An assistant that never says "I can't do that" will run the wrong skill. Test the empty case.
Planning on access you don't have. The new Joule Studio's status and free terms are moving. Check before you promise a demo.
Treating the lab's router as Joule. The lab shows why descriptions matter. Joule's own matching is SAP's, and you test it through the standalone assistant.
#Exercise: add a skill without breaking the others
You will add a seventh skill, extend the evaluation set, and prove the router still picks every skill correctly. The report you save is used again when the same order-exception agent is built three ways later in Unit 9.
Open unit09/joule_lab.py.
Above the # ---- the skill catalog line, add an action that lists a customer's orders:
def act_customer_orders(p):
found = [o["SalesOrder"] for o in ORDERS.values() if o["SoldToParty"] == p["customer"]]
return {"customer": p["customer"], "orders": found}
Inside SKILLS, after show_credit_exposure, add this entry:
In EVAL, add two lines before the meeting-room line:
("which orders did customer 10023 place", "show_customer_orders"),
("show orders placed by customer 10051", "show_customer_orders"),
Run python unit09/joule_lab.py ask "which orders did customer 10023 place" and check the reply lists 4711 and 4730.
Run python unit09/joule_lab.py eval --out unit09/joule_eval.json. If an older line now misses, the new description is stealing requests: reword it and run again.
Done whenskills lists seven skills, the ask in step 5 names orders 4711 and 4730, and eval scores at least 14 of 15 with every line except the finance paraphrase marked OK.
Pick one answer for each question. The explanation appears after you choose.
1In the mental model, what does Joule Studio add when you deploy a skill or agent?
Answer: B. SAP's golden path says deploying creates Joule artifacts such as scenarios and dialog functions and registers the agent in Joule's catalog. Joule then routes requests to catalog entries; it doesn't get a new instance or a new model.
2In the lab, why does route return no skill when two descriptions score the same?
Answer: C. A tie means the request fits two skills equally well, so any choice is a guess. Declining and asking is safer, which is also why the meeting-room request correctly returns no skill.
3In the lab, a new skill described as "Manage orders" starts taking requests meant for show_sales_order. Why?
Answer: D. The lab's router scores descriptions, and a broad one competes with every narrower skill. The fix is a specific description and a re-run of the evaluation set to prove nothing else moved.
4How does SAP's golden path say a Joule skill reaches an SAP S/4HANA OData API?
Answer: A. The golden path's build steps configure destinations through action projects that wrap OData APIs, with data mapped in the skill editor and Formula Editor. That keeps skills on APIs, which also fits clean core.
5A skill creates purchase requisitions. A tester says "it asks for confirmation, so it's secure". What is missing?
Answer: B. Confirmation, like the lab's --confirm yes, prevents accidents but doesn't check who confirmed. The action must run as the signed-in user so SAP authorizations decide what they may create.
6Which statement about the new Joule Studio is accurate as of 6 October 2026?
Answer: D. The Sapphire 2026 guide said early adoption with GA expected in Q3 2026, and the August 2026 reference architecture says some capabilities are not yet GA. The new Joule Studio adds pro-code development with the CLI and frameworks rather than removing anything.
7Your team has a tested Python agent for order exceptions and needs it inside Joule soon. What would you do?
Answer: C. SAP's golden path describes pro-code agents exposing an A2A server endpoint so Joule can call them. That keeps your tested code and its evaluation, while users reach it through Joule.
8Why does the lab's agent stop after one step for order 4723?
Answer: D. The agent reads the order first and decides the next step from what it finds. An incomplete-data block needs master data fixed, not a credit review, which is the kind of judgement that makes this an agent rather than a skill.
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.
Joule Skills (SAP Architecture Center, AI golden path, updated 23 April 2026)— build steps (project, parameters and actions, destinations through action projects wrapping OData APIs, Formula Editor, test in the standalone Joule assistant); skill types; standalone and shared environments; "always implement confirmation dialogs" for create, update and delete
Agentic AI and AI Agents (SAP Architecture Center reference architecture, updated 27 August 2026)— new Joule Studio with a browser-based low-code builder and a pro-code flow using the Joule Studio CLI and a coding agent connected over MCP; both produce the same artifact, registered with Joule; intent-based development in six phases; SAP-managed runtime; Agent Gateway for discovery, authentication, principal propagation and policy; some capabilities not yet GA
Announcing the New Joule Studio for Enterprise-Scale Agentic Development (SAP News, 13 May 2026)— fully managed offering for agents, apps and workflows; pro-code with LangChain, Pydantic AI and LlamaIndex; embedded n8n; Vercel; Joule Studio runtime on NVIDIA OpenShell with memory in SAP HANA Cloud; free design-time access for customers and partners through the end of 2026 under fair-use limits
SAP Sapphire Innovation News Guide 2026 (sap.com)— Joule Studio in an early customer adoption program with GA expected in Q3 2026; new MCP builder; Joule Studio runtime GA planned Q3 2026; Joule Work in Early Adopter Care; bi-directional A2A for third-party agents calling Joule Agents planned Q4 2026
Understanding the Commercial Model (SAP Learning, Introducing Joule)— Joule Base (navigational, informational, transactional and simple analytical; no AI Units) versus Joule Premium (advanced capabilities and agent actions, paid in AI Units); per-user-per-month packages with overages at 2 AI Units per 1,000 requests
SAP Business AI packages and pricing (sap.com)— Joule Base included in standard cloud subscriptions; AI Units bought annually and expire after 12 months; Joule Premium tiered from 8 to 1 AI Units per user per month; Joule Studio accessed through SAP BTP credits
Setting up a unified Joule instance (SAP Learning)— one Joule instance on SAP BTP with dev, test and prod, shared across SAP cloud applications; all participating applications must use the same SAP Cloud Identity Services tenant