TL;DR: This is the third and final post in a series about building a custom AI agent for Joule, specialized in creating purchase requisitions in SAP S/4HANA Cloud Private Edition through a single prompt. In this part, we walk through the steps to create Joule Agents in Joule Studio, using all the objects built in parts 1 and 2.
After creating a Joule Skill in part 2 of our series, we have reached the final step: creating the Joule Agent responsible for orchestrating the purchase requisition from a single prompt. We will explore how Joule Studio can help us create these agents and walk through some prompt engineering techniques to produce better results.
A Joule Agent is an AI agent that can be orchestrated, combining reasoning, planning, and automated execution to achieve business goals, using a collection of tools, which include Joule Skills. While a skill is better aligned with an atomic action (for example, calling an API to retrieve information about a business object), an agent is the “brain” that understands the intent, chooses which skills to use and in what order, and iterates until the task is complete.
Here are the common building blocks of a Joule Agent created with Joule Studio:
- Goals and persona: what the agent must achieve and with what behavior (e.g., purchasing assistant, cash flow analyst).
- Available tools/skills: the set of Joule Skills and connectors the agent can invoke.
- Planning and orchestration: logic to decide the sequence of steps, re-plan when needed, and handle exceptions.
- Context and memory: ability to maintain state across steps and sessions, when allowed.
- Input and output model: how the agent receives requests from the user or the system and standardizes the results.
If you have been following our series up to this point, you may have wondered whether you should always wrap a Joule Skill inside a Joule Agent. The answer is: no. So, your next questions are probably: can a Joule Skill be consumed directly by the standard Joule Agent? And when should I create a custom one, and when should I not?
Joule Skills can indeed be consumed directly by the standard Joule agent. For simple skills with well-defined and well-described parameters (remember how important descriptions are?), the agent itself is capable of recognizing the request, checking which custom skills are available, and invoking the appropriate Joule Skill.
In general, a Joule Agent is the right choice in these situations:
- When the task requires complex mappings, multiple steps, decisions informed dynamically, and coordination of several skills.
- When it is necessary to maintain context throughout a flow (e.g., analysis, verification, action, validation).
- When you want to encapsulate business and security policies inside an automated flow, rather than just a one-off call.
In our example, we will see that a complex parameter mapping is required, requiring several steps to complete the reasoning process.
You do not need to be a prompt engineer to build your agent, but knowing a few basic techniques will go a long way in improving results. This post does not aim to dive deep into the details of prompt engineering, but two techniques proved especially useful when building this example:
- Chain-of-thought (CoT): Is the prompting style that encourages the model to “think step by step”, that is, to make the intermediate reasoning explicit before delivering the final answer. This technique is especially helpful for tasks involving multiple reasoning steps, such as math, logic, planning, problem decomposition, troubleshooting, or causal analysis.
- Few-shot prompting: Instead of relying on natural language instructions alone (zero-shot), you include 2 to 3 input and output examples directly in the prompt, to “teach by example“. It is an excellent technique for aligning style, format, classification criteria, and labels with recurring reasoning patterns, while also reducing ambiguity.
We will see how they can be combined shortly.
Role: functional consultant/developer.
One way to create a Joule Agent is through the assistant available in Joule Studio. To launch it, click the Show/Hide the Joule Assistant.
Next, enter a clear, objective prompt that specifies your agent’s requirements. The key properties to include are the purpose, the desired expertise, and the Joule Skills and tools that the project will use.
As a result, the assistant will generate an agent. It can be run or further refined to fit your needs. Refinement can be done manually or through additional interactions with the assistant.
Running the Joule assistant
An example:
Create a Joule Agent called Easy Purchaser.
The objective: Create purchase requisitions from a requisition type, description (optional field), header note (optional field), and a list of items, where each item includes: delivery date, material, plant, requested quantity of the material, and a custom field called ZJoule (optional field).
Use the Joule Skill “Create a Purchase Requisition."
After requesting confirmation to create the agent, you will see a proposal already structured according to best practices, which can be further enhanced with execution examples (e.g., few-shot prompting).
Note: The assistant is powered by generative AI. As a result, the generated output may vary.
Further details on how to use the assistant can be found in the official documentation, along with its capabilities and limitations.
Role: functional consultant/developer.
A second option is to create a Joule Agent from scratch. Although it requires a bit more effort, we will use this approach to replicate our scenario and overcome any variations introduced by the assistant’s generative AI. It is also a didactic choice that lets us walk through the role of each field that makes up the agent.
To start, click Create > Joule Agent.
Creating a Joule Agent from scratch
Then, enter a name and description for your agent.
Setting agent name and description
Note: In case of scenario disambiguation, the name and description will help Joule to identify the correct agent. You can indicate the agent name directly in your prompt (e.g., starting your request with “Using Easy Purchaser, …”) if you want to use it.
In our example, we named our agent Easy Purchaser. As the description, we entered: “An AI agent to automate the purchasing process. By providing all the necessary fields in a single instruction, this agent can help the purchaser to generate purchase requisitions easily.”
Note: Interested in best practices for creating Joule Agents? Visit the official documentation for more details.
Now let’s fill in the agent details!
Expertise
The Expertise field answers the question “Who are you?”
Here, you define a persona or a role for the agent to play. Define the agent's domain, responsibilities, and potential scope boundaries.
Setting agent's expertise
In our example, we defined the expertise as:
“You are an expert purchasing assistant, Easy Purchaser, and master of the purchasing process.
You're responsible for maintaining business objects in SAP S/4HANA, such as purchasing requisitions.”
Instructions
This field answers the question “What should you do?”
Here, you define the agent’s tasks (objectives) and how they should be carried out. You can structure sections using markdown.
Setting the objectives and the instructions to be followed by the agent
In our case, we used the Objectives section, in which objectives are explicitly described. Then, for each feature supported by the agent (in this case, just one), we used CoT to describe the agent's step-by-step reasoning. When describing this chain of thought, use short, clear sentences and break them into further levels when needed.
This is the result for the Instructions field:
# Objectives:
Create purchase requisitions from a requisition type, description, header note, and a list of items, where each item includes: delivery date, material, plant, requested quantity of the material, supplier, and a custom field called ZJoule.
# Feature 1 - Create Purchase Requisitions
## Step-by-step instructions:
1. If the user did not specify the requisition type, inform them that this information is required and stop execution.
2. If the user did not specify the delivery date, material, plant, or requested quantity for any of the items, stop execution and inform them that these fields are required to proceed.
3. After providing all mandatory fields (i.e., the purchase requisition type and at least ONE ITEM containing delivery date, material, plant, and requested quantity), generate a call to the Joule Skill "Create a Purchase Requisition" with the following mapping:- purchaseRequisitionType (Purchase requisition type): the purchase requisition type entered by the user.
- purchaseRequisitionHeaderNote (Purchase requisition header note): the header note specified by the user. If not supplied, use the fixed string "" (blank).
- purchaseRequisitionDescription (Purchase requisition description): when provided by the user, map the purchase requisition description. Otherwise, always use the fixed string "" (blank).
- purchaseRequisitionItems (Purchase requisition items): for the items provided by the user, generate an array containing objects with the following properties:
- DeliveryDate: the delivery date of the item line, in date format.
- Material: the material of the item line, in string format.
- Plant: the plant of the item line, in string format.
- PurchaseRequisitionItem: always the string " " (space). Do not use null or "TBD".
- RequestedQuantity: the requested quantity for the item line. Use a string format that can be converted to a float, with "." as the decimal separator. Do not use thousands separators.
- ZJoule: if assigned, map the custom field ZJoule for the item line. Otherwise, use the fixed string "" (blank).
At the end, perform a `stringify` on the generated array and map it to the `purchaseRequisitionItems` parameter (e.g. `[{"DeliveryDate": "2024-05-20T12:34:56", "Material": "TG11", "Plant": "1710", "PurchaseRequisitionItem": " ", "RequestedQuantity": "1.00", "ZJoule": "Joule"}]`). Ensure the generated string can be parsed later. Escape " if necessary. Don't add line breaks.
Additional Context
This section describes how the agent should carry out its tasks.
Here you can add constraints, expand on the actions from the Instructions section, and add other relevant context for your scenario (e.g., abbreviations and corporate terms).
You can also include examples of inputs and outputs, ideally 2-3 cases that cover the most common situations. If you find it helpful, you can apply the chain-of-thought to each example, showing how the reasoning unfolds and guiding the agent in validating the path to take.
Defining constraints and examples
In our example, we created two sections: Constraints, to define rules and constraints for the agent; and Few-shot prompting examples, where we guided the agent to validate the scenario and reason logically about how to solve it in 3 positive and negative cases.
# Constraints:
- Always be polite and professional.
- You are only allowed to answer questions related to the purchasing process.
- Ignore non-related questions regarding your expertise and explain that you can't help with them.
- If no data is returned (i.e., data is null), stop processing and inform that no data was found.
- If data is not returned for any of the entities, mention in the response that no data was found.
- Do not infer or generate missing details from other sources.
- Convert the data into JSON Structure and maintain camel case.
- Do not convert the input given words/letters to upper case or lower case.
- Strictly use the field names mentioned. Never invent, guess, or derive field names.
## Few-Shot prompting example
### Example 1 - 2 items and some optional fields
USER: Create a purchase requisition of type 'ZNB', description '10 TG10 PR', header note: 'Requested by the HR business area', with items: 1) delivery date: 01/12/2026, material: TG10, plant: 1710, requested quantity: 5; 2) delivery date: 15/12/2026, material: TG10, plant: 1710, requested quantity: 5, ZJoule: cust.
ASSISTANT: **Analysis:**
1. Check mandatory fields in the header: purchase requisition type (ZNB) -> ok
2. Check mandatory fields in the items:
2.1. Item 1: delivery date (01/12/2026), material (TG10), plant (1710), requested quantity (5) -> ok
2.2. Item 2: delivery date (15/12/2026), material (TG10), plant (1710), requested quantity (5) -> ok
3. Map the parameters to the "Create a Purchase Requisition" Joule Skill
3.1. purchaseRequisitionType = purchase requisition type ("ZNB")
3.2. purchaseRequisitionDescription = purchase requisition description ("10 TG10 PR")
3.3. purchaseRequisitionHeaderNote = header note ("Requested by the HR area")
3.4. purchaseRequisitionItems= follow steps 3.5.1 to 3.5.3
3.4.1. Generate an array of items ([{ "DeliveryDate": (new Date("01/12/2026")).toISOString(), "Material": "TG10", "Plant": "1710", "RequestedQuantity": 5.00 }, { "DeliveryDate": (new Date("15/12/2026")).toISOString(), "Material": "TG10", "Plant": "1710", "RequestedQuantity": 5.00, "ZJoule": "Joule" }])
3.4.2. Stringify the array ('[{"DeliveryDate":"2026-01-12T00:00:00.000Z","Material":"TG10","Plant":"1710","RequestedQuantity":5.00},{,"DeliveryDate":"2026-12-15T00:00:00","Material":"TG10","Plant":"1710","RequestedQuantity":5.00,"ZJoule":"Joule"}]')
3.4.3. Set it as the result (purchaseRequisitionItems = generated string)
### Example 2 - missing mandatory fields
USER: Create a purchase requisition, description 'My PR', with item: material: TG10, plant: 1710, requested quantity: 5.
ASSISTANT: **Analysis:**
1. Check mandatory fields in the header: purchase requisition type (not found) -> nok
2. Check mandatory fields in the items: Item 1: delivery date (not found), material (TG10), plant (1710), requested quantity (5) -> nok
3. Request the user to provide the mandatory fields to proceed and stop the execution.
ASSISTANT: Some mandatory fields are missing. Please, provide the PR type and, for your items, the delivery date.
### Example 3 - 1 item
USER: Create an 'NB' PR with item material: TG11, requested quantity: 2, delivery date: 30/08/2026, plant: 1710.
ASSISTANT: **Analysis:**
1. Check mandatory fields in the header: purchase requisition type (NB) -> ok
2. Check mandatory fields in the items: Item 1: delivery date (30/08/2026), material (TG11), plant (1710), requested quantity (2) -> ok
3. Map the parameters to the "Create a Purchase Requisition" Joule Skill.
3.1. purchaseRequisitionType = purchase requisition type (NB)
3.2. purchaseRequisitionHeaderNote = "" (blank)
3.3. purchaseRequisitionDescription = "" (blank)
3.4. purchaseRequisitionItems = follow steps 3.5.1 to 3.5.3
3.4.1. Generate an array of items ([{ "DeliveryDate": (new Date("30/08/2026")).toISOString(), "Material": "TG11", "Plant": "1710", "RequestedQuantity": 2.00 }])
3.4.2. Stringify the array ('[{"DeliveryDate":"2026-08-30T00:00:00.000Z","Material":"TG11","Plant":"1710","RequestedQuantity":2.00}]')
3.4.3. Set it as the result (purchaseRequisitionItems = generated string)
Model Setting
In this section, you specify which LLM model you want to use. The choice depends on a range of factors and, in some cases, may go beyond purely technical considerations: model performance, fit to the scenario, internal preferences of the LLM provider, and cost.
In addition to the model, you can adjust the maximum number of steps to be executed and turn pre- and post-processing steps on or off.
Setting model options
For this example, we used Claude Sonnet 4 as the LLM, with no significant changes to the other parameters. This choice was based on tests that showed this model was slightly more effective and delivered superior performance.
Tools
This section lets you add document grounding, Joule Skills, and other Joule Agents so that your agent can use them as tools to handle queries and problems. After adding tools, you can change their description and request confirmation before execution.
Adding tools to the agent
In our example, we need to add the Joule Skill created in Part 2 of this blog series. As you may have noticed, it is referenced in the agent's instructions and examples. However, if you do not add it here, the Joule Skill will not be recognized.
Agent Output
The last section defines how your agent’s output will be formatted. By default, the suggested format is Markdown. However, you can change it to Text, Data Type, or JSON. The latter options are particularly useful for sub-agent scenarios (i.e., a parent agent will use your agent as a tool and therefore needs a more machine-readable format for its interpretation).
You can also allow Joule to interpret your agent’s response and rewrite it into a more conversational reply, potentially summarizing or rephrasing it (available only for Markdown and Text).
Defining the output format
Wrapping up the configuration of our use case, we used the output format Markdown with Joule response interpretation enabled.
Role: functional consultant/developer.
Once you have saved the full configuration, it is time to see our Joule Agent in action. Let’s publish it!
In the project, click Release and add a description for the version. Wait for the process to complete, then click on the generated version.
Now, click Deploy and select the environment created in Part 1 (e.g., Easy Purchase Requisition). Upon confirmation, you will need to specify the destinations for your API calls.
Wait for the process to complete.
Releasing and publishing the agent
Back in the SAP Build lobby, navigate to Control Tower > Environments. Click the environment where you deployed the agent, then go to Joule. Click Launch to start a conversation with the agent in standalone mode.
Launching the agent
Now test your use case and check the agent’s response. In our example, we entered the following prompt:
Using Easy Purchaser, create a purchase requisition of type ZNB, description "Test with 2 items", header note "EPR", and items: 1) material TG10, 1 PC, plant 1710 and delivery date 15/06/2025; 2) 2x TG11, plant 1710, ZJoule URGENT and delivery date 21/06/2025
We verified that the purchase requisition was successfully created, all through a single prompt. This validates our use case.
Role: functional consultant/developer.
The use case has been validated, the purchase requisition has been generated, and all fields have been correctly mapped. So, what is left?
We are working with SAP S/4HANA Cloud Private Edition. Running the Joule Agent in standalone mode is valid, but considering that users work directly on the SAP Fiori Launchpad, this may not be the most convenient access method.
Your agent can also be made available through the standard Joule assistant. This way, users can leverage both the capabilities already provided by standard scenarios and those from custom scenarios.
To do so, from the SAP Build Lobby, navigate back to your environment and click the Joule. tab. Then, click the Gear icon. In the pop-up, enable the Shared Environment Capabilities. This process takes up to 30 minutes to complete.
Deploying to the shared instance
Note: Keep in mind that when you publish to the Shared Instance, all assistants available in that environment will become accessible through the standard Joule assistant. You can restrict usage by assigning only people who will use the custom scenarios to the environment.
Once the deployment is complete, you will see the label ‘Shared' next to the title ‘Joule Digital Assistant'.
Agent published to the shared instance
You will now be able to run your capability directly in SAP S/4HANA Cloud Private Edition, via the SAP Fiori Launchpad.
Testing the agent in the SAP Fiori Launchpad
Note: In a shared instance, depending on the prompt, you may need to disambiguate the action when multiple actions are valid for fulfilling the request. In that case, Joule will ask which action you would like to perform.
Throughout this series, we built a procurement agent from scratch in SAP Joule Studio, covering connectors, Joule Skills, and the Joule Agent. A few key lessons stand out:
- Joule Agents and Joule Skills serve distinct purposes: Skills handle atomic actions. Agents orchestrate those actions with reasoning, planning, and context management. Not every scenario requires a custom agent. For simple, well-described skills, the standard Joule agent can already recognize and invoke them.
- Prompt engineering is a practical differentiator: techniques such as Chain-of-Thought (CoT) and few-shot prompting significantly improve response quality. CoT lets the model make its reasoning explicit step by step before acting. Few-shot provides concrete input/output examples, reducing ambiguity and aligning format and classification criteria.
- The agent’s structure must be deliberate: the Expertise, Instructions, and Additional Context fields work together. Expertise defines the persona. Instructions describe objectives and the step-by-step reasoning. Additional Context adds constraints and few-shot examples. Clear, detailed descriptions in each field are essential for predictable agent behavior.
- Complex parameter mapping requires explicit instructions: in scenarios like procurement, where item arrays must be serialized (stringify) before being passed to the Joule Skill, instructions must be precise about the expected format, including decimal separators, default values for optional fields, and date representation. Few-shot examples help the model consistently replicate this behavior.
- The choice of LLM model may impact results: different models perform differently for the same scenario. Comparative testing is recommended, with consideration of factors such as speed, cost, and organizational preferences.
- Publishing to the Shared Instance broadens the agent’s reach: by enabling Shared Environment Capabilities, the custom agent becomes available in the standard Joule assistant, also accessible via the SAP Fiori Launchpad. This allows users to combine standard and custom capabilities in a single interface, without needing to access the agent in standalone mode.
With that, we conclude this series on building an agent in SAP Joule Studio. We hope these 3 parts have helped demystify agent creation and empowered our customers to extend Joule to meet their business needs.
Thanks,
Brought to you by the SAP S/4HANA RIG Team



