From 9af9b9bfcb59d90f6a4aedf858e43398a0839efa Mon Sep 17 00:00:00 2001 From: Jonas Israel Date: Mon, 16 Jun 2025 17:43:49 +0200 Subject: [PATCH 1/6] First draft --- docs-java/tutorials/agentic-workflows.mdx | 238 ++++++++++++++++++++++ sidebarsDocsJava.js | 7 + 2 files changed, 245 insertions(+) create mode 100644 docs-java/tutorials/agentic-workflows.mdx diff --git a/docs-java/tutorials/agentic-workflows.mdx b/docs-java/tutorials/agentic-workflows.mdx new file mode 100644 index 000000000..b9d8f98a3 --- /dev/null +++ b/docs-java/tutorials/agentic-workflows.mdx @@ -0,0 +1,238 @@ +--- +id: agentic-workflows +title: Getting Started with Agentic Workflows +hide_title: false +hide_table_of_contents: false +description: A tutorial on how to build agentic workflows using the AI SDK. +keywords: + - sap + - ai + - sdk + - cloud + - orchestration + - agentic +--- + +## Introduction + +In this tutorial we will build a simple agentic travel assistant using the AI SDK and its SpringAI integration. +The agent will create a short itinerary for a one-day trip to a specified city. +In order to do that, it will collect weather information as well as restaurant recommendations through tool usage and combine the collected information using chat memory. + +The agent will perform the following steps in a chain-like structure: +- Accept a travel request by the user. +- Use a custom tool to recommend restaurants for the requested city. +- Use a custom tool to get the current weather for the requested city. +- Combine the collected information into a short itinerary using chat memory and return it to the user. + +For further reading on this topic, we recommend the Spring article [Building Effective Agents with Spring AI](https://spring.io/blog/2025/01/21/spring-ai-agentic-patterns). +Our examples follow some of the ideas shown there and throughout this tutorial we will use terms introduced in this article. + +:::info +An example implementation of this tutorial can be found in [our Spring Boot application](https://github.com/SAP/ai-sdk-java/tree/main/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiAgenticWorkflowService.java). +::: + +## Prerequisites + +This tutorial requires basic knowledge of Java, the AI SDK, and Spring. +Also, a basic understanding of concepts regarding LLMs and the above mentioned [Spring article](https://spring.io/blog/2025/01/21/spring-ai-agentic-patterns) are assumed. + +For technical requirements please refer to our [general prerequisites](../overview-cloud-sdk-for-ai-java#general-requirements). + +## Create Mock Tools + +We first create two mocked tools. +For more information on tool usage, we refer to the respective [documentation](../spring-ai/orchestration#tool-calling). + +The first tool will return (pre-defined) restaurant suggestion for a provided city. + +```java +class RestaurantMethod { + + /** + * Request for list of restaurants + * + * @param location the city + */ + record Request(String location) {} + + /** + * Response for restaurant recommendations + * + * @param restaurants the list of restaurants + */ + record Response(List restaurants) {} + + @Nonnull + @Tool(description = "Get recommended restaurants for a location") + static RestaurantMethod.Response getRestaurants( + @ToolParam @Nonnull final RestaurantMethod.Request request) { + var recommendations = + Map.of( + "paris", + List.of("Le Comptoir du Relais", "L'As du Fallafel", "Breizh Café"), + "reykjavik", + List.of("Dill Restaurant", "Fish Market", "Grillmarkaðurinn")); + return new RestaurantMethod.Response( + recommendations.getOrDefault( + request.location.toLowerCase(Locale.ROOT), List.of("No recommendations for this city."))); + } +} +``` + +The second tool will return a (fake) weather forecast for a given city. + +```java +class WeatherMethod { + + /** Unit of temperature */ + enum Unit { + /** Celsius */ + @SuppressWarnings("unused") + C, + /** Fahrenheit */ + @SuppressWarnings("unused") + F + } + + /** + * Request for the weather + * + * @param location the city + * @param unit the unit of temperature + */ + record Request(String location, Unit unit) {} + + /** + * Response for the weather + * + * @param temp the temperature + * @param unit the unit of temperature + */ + record Response(double temp, Unit unit) {} + + @Nonnull + @SuppressWarnings("unused") + @Tool(description = "Get the weather in location") + static Response getCurrentWeather(@ToolParam @Nonnull final Request request) { + final int temperature = request.location.hashCode() % 30; + return new Response(temperature, request.unit); + } +} +``` + +## Set up the Agent + +We are now ready to create our agent. +For this, we start by configuring the chat memory and adding the tools from above. +Note that in order to integrate this easily into a Spring app, we create the class as a Spring service. +The agentic workflows will be triggered when calling the `chain()` method with the user input. + +```java +@Service +public class SpringAiAgenticWorkflowService { + private final ChatModel client = new OrchestrationChatModel(); + private final OrchestrationModuleConfig config = + new OrchestrationModuleConfig().withLlmConfig(GPT_4O_MINI); + + public ChatResponse chain(String userInput) { + + // Configure chat memory + val memory = new InMemoryChatMemory(); + val advisor = new MessageChatMemoryAdvisor(memory); + val cl = ChatClient.builder(client).defaultAdvisors(advisor).build(); + + // Add (mocked) tools + val options = new OrchestrationChatOptions(config); + options.setToolCallbacks( + List.of(ToolCallbacks.from(new WeatherMethod(), new RestaurantMethod()))); + options.setInternalToolExecutionEnabled(true); + + // The actual agentic workflow will be added here. + + return null; + } +} +``` + +For more information on how to use chat memory, we refer to the respective [documentation](../spring-ai/orchestration#chat-memory). + +## Implementing the Agentic Workflow + +Next, we implement the actual, chain-like agentic workflow by adapting the class introduced above. +For this, we first define the system prompts that we will use in the call to the LLM and then loop over them, each time using the output from the previous call as input for the next. +This results in the following class. + +```java +@Service +public class SpringAiAgenticWorkflowService { + private final ChatModel client = new OrchestrationChatModel(); + private final OrchestrationModuleConfig config = + new OrchestrationModuleConfig().withLlmConfig(GPT_4O_MINI); + + @Nonnull + public ChatResponse chain(String userInput) { + + // Configure chat memory + val memory = new InMemoryChatMemory(); + val advisor = new MessageChatMemoryAdvisor(memory); + val cl = ChatClient.builder(client).defaultAdvisors(advisor).build(); + + // Add (mocked) tools + val options = new OrchestrationChatOptions(config); + options.setToolCallbacks( + List.of(ToolCallbacks.from(new WeatherMethod(), new RestaurantMethod()))); + options.setInternalToolExecutionEnabled(true); + + // Prompts for the chain workflow + List systemPrompts = + List.of( + "You are a traveling planning agent for a single day trip. Where appropriate, use the provided tools. First, start by suggesting some restaurants for the mentioned city.", + "Now, check the whether for the city.", + "Finally, combine the suggested itinerary from this conversation into a short, one-sentence plan for the day trip."); + + // Perform the chain workflow + int step = 0; + String responseText = userInput; + ChatResponse response = null; + + System.out.printf("\nSTEP %s:\n %s%n", step++, responseText); + + for (String systemPrompt : systemPrompts) { + + // 1. Compose the input using the response from the previous step. + String input = String.format("{%s}\n {%s}", systemPrompt, responseText); + val prompt = new Prompt(input, options); + + // 2. Call the chat client with the new input and get the new response. + response = + Objects.requireNonNull( + cl.prompt(prompt).call().chatResponse(), "Chat response is null in step " + step); + responseText = response.getResult().getOutput().getText(); + + System.out.printf("\nSTEP %s:\n %s%n", step++, responseText); + } + + return response; + } +} +``` + +## Running the Agentic Workflow + +We can now run this agentic workflow by calling the `chain()` method with some user input. +This can, for example, be done through a Spring app controller class as below. + +```java +@Autowired +private SpringAiAgenticWorkflowService service; + +String runChainWorkflow() { +final var response = service.chain("I want to do a one-day trip to Paris. Help me make an itinerary, please"); +return response.getResult().getOutput().getText(); +} +``` + +## Conclusion + +This tutorial showed how to build a simple, chain-like agentic workflow using the AI SDK's SpringAI integration. diff --git a/sidebarsDocsJava.js b/sidebarsDocsJava.js index 8f01d35ac..094a7804d 100644 --- a/sidebarsDocsJava.js +++ b/sidebarsDocsJava.js @@ -36,6 +36,13 @@ module.exports = { 'ai-core/prompt-registry' ] }, + { + type: 'category', + label: 'Tutorials', + items: [ + 'tutorials/agentic-workflows' + ] + }, 'release-notes', 'frequently-asked-questions', { From f28178f6aff4092d9b7d6eb86ee1835b4b3e9917 Mon Sep 17 00:00:00 2001 From: Jonas Israel Date: Tue, 17 Jun 2025 10:33:05 +0200 Subject: [PATCH 2/6] Improve text --- docs-java/tutorials/agentic-workflows.mdx | 61 +++++++++++------------ 1 file changed, 28 insertions(+), 33 deletions(-) diff --git a/docs-java/tutorials/agentic-workflows.mdx b/docs-java/tutorials/agentic-workflows.mdx index b9d8f98a3..4ae921ac8 100644 --- a/docs-java/tutorials/agentic-workflows.mdx +++ b/docs-java/tutorials/agentic-workflows.mdx @@ -25,8 +25,8 @@ The agent will perform the following steps in a chain-like structure: - Use a custom tool to get the current weather for the requested city. - Combine the collected information into a short itinerary using chat memory and return it to the user. -For further reading on this topic, we recommend the Spring article [Building Effective Agents with Spring AI](https://spring.io/blog/2025/01/21/spring-ai-agentic-patterns). -Our examples follow some of the ideas shown there and throughout this tutorial we will use terms introduced in this article. +For further reading on this topic, we recommend the Spring article [Building Effective Agents with SpringAI](https://spring.io/blog/2025/01/21/spring-ai-agentic-patterns). +Our examples follow some of the ideas outlined there and throughout this tutorial we will use terms introduced in this article. :::info An example implementation of this tutorial can be found in [our Spring Boot application](https://github.com/SAP/ai-sdk-java/tree/main/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiAgenticWorkflowService.java). @@ -35,14 +35,14 @@ An example implementation of this tutorial can be found in [our Spring Boot appl ## Prerequisites This tutorial requires basic knowledge of Java, the AI SDK, and Spring. -Also, a basic understanding of concepts regarding LLMs and the above mentioned [Spring article](https://spring.io/blog/2025/01/21/spring-ai-agentic-patterns) are assumed. +Also, a basic understanding of concepts regarding LLMs and the above mentioned [article on building agents with SpringAI](https://spring.io/blog/2025/01/21/spring-ai-agentic-patterns) are assumed. -For technical requirements please refer to our [general prerequisites](../overview-cloud-sdk-for-ai-java#general-requirements). +For technical prerequisites please refer to our [general requirements](../overview-cloud-sdk-for-ai-java#general-requirements). ## Create Mock Tools -We first create two mocked tools. -For more information on tool usage, we refer to the respective [documentation](../spring-ai/orchestration#tool-calling). +We first create two mock tools. +For more information on tool usage, we refer to the respective [chapter in our documentation](../spring-ai/orchestration#tool-calling). The first tool will return (pre-defined) restaurant suggestion for a provided city. @@ -88,10 +88,8 @@ class WeatherMethod { /** Unit of temperature */ enum Unit { /** Celsius */ - @SuppressWarnings("unused") C, /** Fahrenheit */ - @SuppressWarnings("unused") F } @@ -112,7 +110,6 @@ class WeatherMethod { record Response(double temp, Unit unit) {} @Nonnull - @SuppressWarnings("unused") @Tool(description = "Get the weather in location") static Response getCurrentWeather(@ToolParam @Nonnull final Request request) { final int temperature = request.location.hashCode() % 30; @@ -126,7 +123,7 @@ class WeatherMethod { We are now ready to create our agent. For this, we start by configuring the chat memory and adding the tools from above. Note that in order to integrate this easily into a Spring app, we create the class as a Spring service. -The agentic workflows will be triggered when calling the `chain()` method with the user input. +The agentic workflows will be triggered when calling the `runAgent()` method with the user input. ```java @Service @@ -135,15 +132,15 @@ public class SpringAiAgenticWorkflowService { private final OrchestrationModuleConfig config = new OrchestrationModuleConfig().withLlmConfig(GPT_4O_MINI); - public ChatResponse chain(String userInput) { + public ChatResponse runAgent(String userInput) { // Configure chat memory - val memory = new InMemoryChatMemory(); - val advisor = new MessageChatMemoryAdvisor(memory); - val cl = ChatClient.builder(client).defaultAdvisors(advisor).build(); + var memory = new InMemoryChatMemory(); + var advisor = new MessageChatMemoryAdvisor(memory); + var cl = ChatClient.builder(client).defaultAdvisors(advisor).build(); // Add (mocked) tools - val options = new OrchestrationChatOptions(config); + var options = new OrchestrationChatOptions(config); options.setToolCallbacks( List.of(ToolCallbacks.from(new WeatherMethod(), new RestaurantMethod()))); options.setInternalToolExecutionEnabled(true); @@ -155,13 +152,13 @@ public class SpringAiAgenticWorkflowService { } ``` -For more information on how to use chat memory, we refer to the respective [documentation](../spring-ai/orchestration#chat-memory). +For more information on how to use chat memory, we refer to the respective [chapter in our documentation](../spring-ai/orchestration#chat-memory). ## Implementing the Agentic Workflow -Next, we implement the actual, chain-like agentic workflow by adapting the class introduced above. +Next, we implement the actual agentic workflow by adapting the class introduced above. +To keep it simple in the beginning, we start with a chain-like workflow. For this, we first define the system prompts that we will use in the call to the LLM and then loop over them, each time using the output from the previous call as input for the next. -This results in the following class. ```java @Service @@ -171,15 +168,15 @@ public class SpringAiAgenticWorkflowService { new OrchestrationModuleConfig().withLlmConfig(GPT_4O_MINI); @Nonnull - public ChatResponse chain(String userInput) { + public String runAgent(String userInput) { // Configure chat memory - val memory = new InMemoryChatMemory(); - val advisor = new MessageChatMemoryAdvisor(memory); - val cl = ChatClient.builder(client).defaultAdvisors(advisor).build(); + var memory = new InMemoryChatMemory(); + var advisor = new MessageChatMemoryAdvisor(memory); + var cl = ChatClient.builder(client).defaultAdvisors(advisor).build(); // Add (mocked) tools - val options = new OrchestrationChatOptions(config); + var options = new OrchestrationChatOptions(config); options.setToolCallbacks( List.of(ToolCallbacks.from(new WeatherMethod(), new RestaurantMethod()))); options.setInternalToolExecutionEnabled(true); @@ -194,18 +191,17 @@ public class SpringAiAgenticWorkflowService { // Perform the chain workflow int step = 0; String responseText = userInput; - ChatResponse response = null; System.out.printf("\nSTEP %s:\n %s%n", step++, responseText); for (String systemPrompt : systemPrompts) { - // 1. Compose the input using the response from the previous step. + // Combine the pre-defined prompt with the previous answer to get the new input String input = String.format("{%s}\n {%s}", systemPrompt, responseText); - val prompt = new Prompt(input, options); + var prompt = new Prompt(input, options); - // 2. Call the chat client with the new input and get the new response. - response = + // Make a call to the LLM with the new input + var response = Objects.requireNonNull( cl.prompt(prompt).call().chatResponse(), "Chat response is null in step " + step); responseText = response.getResult().getOutput().getText(); @@ -213,23 +209,22 @@ public class SpringAiAgenticWorkflowService { System.out.printf("\nSTEP %s:\n %s%n", step++, responseText); } - return response; + return responseText; } } ``` ## Running the Agentic Workflow -We can now run this agentic workflow by calling the `chain()` method with some user input. -This can, for example, be done through a Spring app controller class as below. +We can now run our agentic workflow by calling the `runAgent()` method with some user input. ```java @Autowired private SpringAiAgenticWorkflowService service; String runChainWorkflow() { -final var response = service.chain("I want to do a one-day trip to Paris. Help me make an itinerary, please"); -return response.getResult().getOutput().getText(); + final var response = service.runAgent("I want to do a one-day trip to Paris. Help me make an itinerary, please"); + return response; } ``` From a7717f851540974b5448a071abad64b32da3adfd Mon Sep 17 00:00:00 2001 From: Jonas Israel Date: Tue, 17 Jun 2025 10:34:30 +0200 Subject: [PATCH 3/6] lint --- docs-java/tutorials/agentic-workflows.mdx | 1 + sidebarsDocsJava.js | 12 +++++------- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/docs-java/tutorials/agentic-workflows.mdx b/docs-java/tutorials/agentic-workflows.mdx index 4ae921ac8..a41f9c585 100644 --- a/docs-java/tutorials/agentic-workflows.mdx +++ b/docs-java/tutorials/agentic-workflows.mdx @@ -20,6 +20,7 @@ The agent will create a short itinerary for a one-day trip to a specified city. In order to do that, it will collect weather information as well as restaurant recommendations through tool usage and combine the collected information using chat memory. The agent will perform the following steps in a chain-like structure: + - Accept a travel request by the user. - Use a custom tool to recommend restaurants for the requested city. - Use a custom tool to get the current weather for the requested city. diff --git a/sidebarsDocsJava.js b/sidebarsDocsJava.js index 094a7804d..8ff195e7c 100644 --- a/sidebarsDocsJava.js +++ b/sidebarsDocsJava.js @@ -36,13 +36,11 @@ module.exports = { 'ai-core/prompt-registry' ] }, - { - type: 'category', - label: 'Tutorials', - items: [ - 'tutorials/agentic-workflows' - ] - }, + { + type: 'category', + label: 'Tutorials', + items: ['tutorials/agentic-workflows'] + }, 'release-notes', 'frequently-asked-questions', { From 21f8c7992f2aed9b57b6d233988479d6695f5842 Mon Sep 17 00:00:00 2001 From: Jonas Israel Date: Tue, 17 Jun 2025 10:52:06 +0200 Subject: [PATCH 4/6] align with sample code --- docs-java/tutorials/agentic-workflows.mdx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/docs-java/tutorials/agentic-workflows.mdx b/docs-java/tutorials/agentic-workflows.mdx index a41f9c585..49e86a0f8 100644 --- a/docs-java/tutorials/agentic-workflows.mdx +++ b/docs-java/tutorials/agentic-workflows.mdx @@ -190,11 +190,8 @@ public class SpringAiAgenticWorkflowService { "Finally, combine the suggested itinerary from this conversation into a short, one-sentence plan for the day trip."); // Perform the chain workflow - int step = 0; String responseText = userInput; - System.out.printf("\nSTEP %s:\n %s%n", step++, responseText); - for (String systemPrompt : systemPrompts) { // Combine the pre-defined prompt with the previous answer to get the new input @@ -204,10 +201,8 @@ public class SpringAiAgenticWorkflowService { // Make a call to the LLM with the new input var response = Objects.requireNonNull( - cl.prompt(prompt).call().chatResponse(), "Chat response is null in step " + step); + cl.prompt(prompt).call().chatResponse(), "Chat response is null."); responseText = response.getResult().getOutput().getText(); - - System.out.printf("\nSTEP %s:\n %s%n", step++, responseText); } return responseText; From e39b55c30985fc8a58e0891c30f1e259d7a83c13 Mon Sep 17 00:00:00 2001 From: Jonas Israel Date: Tue, 17 Jun 2025 11:11:34 +0200 Subject: [PATCH 5/6] review dog --- docs-java/tutorials/agentic-workflows.mdx | 10 +++++----- styles/config/vocabularies/SAP/accept.txt | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/docs-java/tutorials/agentic-workflows.mdx b/docs-java/tutorials/agentic-workflows.mdx index 49e86a0f8..d7383b366 100644 --- a/docs-java/tutorials/agentic-workflows.mdx +++ b/docs-java/tutorials/agentic-workflows.mdx @@ -3,7 +3,7 @@ id: agentic-workflows title: Getting Started with Agentic Workflows hide_title: false hide_table_of_contents: false -description: A tutorial on how to build agentic workflows using the AI SDK. +description: A tutorial on how to build agentic workflows using the SAP AI SDK. keywords: - sap - ai @@ -15,7 +15,7 @@ keywords: ## Introduction -In this tutorial we will build a simple agentic travel assistant using the AI SDK and its SpringAI integration. +In this tutorial we will build a simple agentic travel assistant using the SAP AI SDK and its SpringAI integration. The agent will create a short itinerary for a one-day trip to a specified city. In order to do that, it will collect weather information as well as restaurant recommendations through tool usage and combine the collected information using chat memory. @@ -35,7 +35,7 @@ An example implementation of this tutorial can be found in [our Spring Boot appl ## Prerequisites -This tutorial requires basic knowledge of Java, the AI SDK, and Spring. +This tutorial requires basic knowledge of Java, the SAP AI SDK, and Spring. Also, a basic understanding of concepts regarding LLMs and the above mentioned [article on building agents with SpringAI](https://spring.io/blog/2025/01/21/spring-ai-agentic-patterns) are assumed. For technical prerequisites please refer to our [general requirements](../overview-cloud-sdk-for-ai-java#general-requirements). @@ -123,7 +123,7 @@ class WeatherMethod { We are now ready to create our agent. For this, we start by configuring the chat memory and adding the tools from above. -Note that in order to integrate this easily into a Spring app, we create the class as a Spring service. +Note that in order to integrate this into a Spring app, we create the class as a Spring service. The agentic workflows will be triggered when calling the `runAgent()` method with the user input. ```java @@ -226,4 +226,4 @@ String runChainWorkflow() { ## Conclusion -This tutorial showed how to build a simple, chain-like agentic workflow using the AI SDK's SpringAI integration. +This tutorial showed how to build a simple, chain-like agentic workflow using the SAP AI SDK's SpringAI integration. diff --git a/styles/config/vocabularies/SAP/accept.txt b/styles/config/vocabularies/SAP/accept.txt index 35430059c..7769e1357 100644 --- a/styles/config/vocabularies/SAP/accept.txt +++ b/styles/config/vocabularies/SAP/accept.txt @@ -149,3 +149,5 @@ pseudonymization LLM LLMs AIMessage + +[Aa]gentic From e0cde334dba0fdfea58211f5ba333de3a30a4658 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20D=C3=BCmont?= <22489773+newtork@users.noreply.github.com> Date: Thu, 7 Aug 2025 17:56:32 +0200 Subject: [PATCH 6/6] Update agentic-workflows.mdx --- docs-java/tutorials/agentic-workflows.mdx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs-java/tutorials/agentic-workflows.mdx b/docs-java/tutorials/agentic-workflows.mdx index d7383b366..9a10a0834 100644 --- a/docs-java/tutorials/agentic-workflows.mdx +++ b/docs-java/tutorials/agentic-workflows.mdx @@ -172,8 +172,9 @@ public class SpringAiAgenticWorkflowService { public String runAgent(String userInput) { // Configure chat memory - var memory = new InMemoryChatMemory(); - var advisor = new MessageChatMemoryAdvisor(memory); + val repository = new InMemoryChatMemoryRepository(); + val memory = MessageWindowChatMemory.builder().chatMemoryRepository(repository).build(); + val advisor = MessageChatMemoryAdvisor.builder(memory).build(); var cl = ChatClient.builder(client).defaultAdvisors(advisor).build(); // Add (mocked) tools