diff --git a/spring-ai-modules/pom.xml b/spring-ai-modules/pom.xml
index a8a9b050cb30..6220718d90a2 100644
--- a/spring-ai-modules/pom.xml
+++ b/spring-ai-modules/pom.xml
@@ -20,6 +20,6 @@
spring-ai-mcp
spring-ai-text-to-sql
spring-ai-vector-stores
+ spring-ai-agentic-patterns
-
-
\ No newline at end of file
+
diff --git a/spring-ai-modules/spring-ai-agentic-patterns/pom.xml b/spring-ai-modules/spring-ai-agentic-patterns/pom.xml
new file mode 100644
index 000000000000..1b97120497fd
--- /dev/null
+++ b/spring-ai-modules/spring-ai-agentic-patterns/pom.xml
@@ -0,0 +1,62 @@
+
+
+ 4.0.0
+
+
+ com.baeldung
+ spring-ai-modules
+ 0.0.1
+ ../pom.xml
+
+
+ spring-ai-agentic-patterns
+ spring-ai-agentic-patterns
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.ai
+ spring-ai-model
+
+
+ org.springframework.ai
+ spring-ai-client-chat
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+ org.springframework.ai
+ spring-ai-bom
+ ${spring-ai.version}
+ pom
+ import
+
+
+
+
+
+ 21
+ 1.0.2
+ 3.5.5
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/Application.java b/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/Application.java
new file mode 100644
index 000000000000..e120d55e6573
--- /dev/null
+++ b/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/Application.java
@@ -0,0 +1,12 @@
+package com.baeldung.springai.agenticpatterns;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+}
diff --git a/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/aimodels/CodeReviewClient.java b/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/aimodels/CodeReviewClient.java
new file mode 100644
index 000000000000..07e556a3c390
--- /dev/null
+++ b/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/aimodels/CodeReviewClient.java
@@ -0,0 +1,6 @@
+package com.baeldung.springai.agenticpatterns.aimodels;
+
+import org.springframework.ai.chat.client.ChatClient;
+
+public interface CodeReviewClient extends ChatClient {
+}
diff --git a/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/aimodels/CodeReviewClientPrompts.java b/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/aimodels/CodeReviewClientPrompts.java
new file mode 100644
index 000000000000..f2d9d2e667ae
--- /dev/null
+++ b/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/aimodels/CodeReviewClientPrompts.java
@@ -0,0 +1,32 @@
+package com.baeldung.springai.agenticpatterns.aimodels;
+
+public final class CodeReviewClientPrompts {
+
+ private CodeReviewClientPrompts() {
+ }
+
+ /**
+ * Prompt for the code review of a given PR
+ */
+ public static final String CODE_REVIEW_PROMPT = """
+ Given a PR link -> generate a map with proposed code improvements.
+ The key should be {class-name}:{line-number}:{short-description}.
+ The value should be the code in one line. For example, 1 proposed improvement could be for the line 'int x = 0, y = 0;':
+ {"Client:23:'no multiple variables defined in 1 line'", "int x = 0;\\n int y = 0;"}
+ Rules are, to follow the checkstyle and spotless rules set to the repo. Keep java code clear, readable and extensible.
+
+ Finally, if it is not your first attempt, there might feedback provided to you, including your previous suggestion.
+ You should reflect on it and improve the previous suggestions, or even add more.""";
+
+ /**
+ * Prompt for the evaluation of the result
+ */
+ public static final String EVALUATE_PROPOSED_IMPROVEMENTS_PROMPT = """
+ Evaluate the suggested code improvements for correctness, time complexity, and best practices.
+
+ Return a Map with one entry. The key is the value the evaluation. The value will be your feedback.
+
+ The evaluation field must be one of: "PASS", "NEEDS_IMPROVEMENT", "FAIL"
+ Use "PASS" only if all criteria are met with no improvements needed.
+ """;
+}
diff --git a/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/aimodels/DummyCodeReviewClient.java b/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/aimodels/DummyCodeReviewClient.java
new file mode 100644
index 000000000000..47d0a3d72653
--- /dev/null
+++ b/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/aimodels/DummyCodeReviewClient.java
@@ -0,0 +1,33 @@
+package com.baeldung.springai.agenticpatterns.aimodels;
+
+import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.lang.NonNull;
+import org.springframework.stereotype.Component;
+
+@Component
+public class DummyCodeReviewClient implements CodeReviewClient {
+
+ @Override
+ @NonNull
+ public ChatClientRequestSpec prompt() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ @NonNull
+ public ChatClientRequestSpec prompt(@NonNull String input) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ @NonNull
+ public ChatClientRequestSpec prompt(@NonNull Prompt prompt) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ @NonNull
+ public Builder mutate() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+}
diff --git a/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/aimodels/DummyOpsClient.java b/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/aimodels/DummyOpsClient.java
new file mode 100644
index 000000000000..e18793781168
--- /dev/null
+++ b/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/aimodels/DummyOpsClient.java
@@ -0,0 +1,33 @@
+package com.baeldung.springai.agenticpatterns.aimodels;
+
+import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.lang.NonNull;
+import org.springframework.stereotype.Component;
+
+@Component
+public class DummyOpsClient implements OpsClient {
+
+ @Override
+ @NonNull
+ public ChatClientRequestSpec prompt() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ @NonNull
+ public ChatClientRequestSpec prompt(@NonNull String input) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ @NonNull
+ public ChatClientRequestSpec prompt(@NonNull Prompt prompt) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ @NonNull
+ public Builder mutate() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+}
diff --git a/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/aimodels/DummyOpsOrchestratorClient.java b/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/aimodels/DummyOpsOrchestratorClient.java
new file mode 100644
index 000000000000..e73c0e93070b
--- /dev/null
+++ b/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/aimodels/DummyOpsOrchestratorClient.java
@@ -0,0 +1,33 @@
+package com.baeldung.springai.agenticpatterns.aimodels;
+
+import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.lang.NonNull;
+import org.springframework.stereotype.Component;
+
+@Component
+public class DummyOpsOrchestratorClient implements OpsOrchestratorClient {
+
+ @Override
+ @NonNull
+ public ChatClientRequestSpec prompt() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ @NonNull
+ public ChatClientRequestSpec prompt(@NonNull String request) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ @NonNull
+ public ChatClientRequestSpec prompt(@NonNull Prompt prompt) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ @NonNull
+ public Builder mutate() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+}
diff --git a/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/aimodels/DummyOpsRouterClient.java b/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/aimodels/DummyOpsRouterClient.java
new file mode 100644
index 000000000000..abcfade2acac
--- /dev/null
+++ b/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/aimodels/DummyOpsRouterClient.java
@@ -0,0 +1,33 @@
+package com.baeldung.springai.agenticpatterns.aimodels;
+
+import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.lang.NonNull;
+import org.springframework.stereotype.Component;
+
+@Component
+public class DummyOpsRouterClient implements OpsRouterClient {
+
+ @Override
+ @NonNull
+ public ChatClientRequestSpec prompt() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ @NonNull
+ public ChatClientRequestSpec prompt(@NonNull String request) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ @NonNull
+ public ChatClientRequestSpec prompt(@NonNull Prompt prompt) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ @NonNull
+ public Builder mutate() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+}
diff --git a/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/aimodels/OpsClient.java b/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/aimodels/OpsClient.java
new file mode 100644
index 000000000000..1556262eccdd
--- /dev/null
+++ b/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/aimodels/OpsClient.java
@@ -0,0 +1,6 @@
+package com.baeldung.springai.agenticpatterns.aimodels;
+
+import org.springframework.ai.chat.client.ChatClient;
+
+public interface OpsClient extends ChatClient {
+}
diff --git a/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/aimodels/OpsClientPrompts.java b/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/aimodels/OpsClientPrompts.java
new file mode 100644
index 000000000000..e53369de7835
--- /dev/null
+++ b/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/aimodels/OpsClientPrompts.java
@@ -0,0 +1,65 @@
+package com.baeldung.springai.agenticpatterns.aimodels;
+
+public final class OpsClientPrompts {
+
+ private OpsClientPrompts() {
+ }
+
+ /**
+ * Array of steps to be taken for the dev pipeline
+ */
+ public static final String[] DEV_PIPELINE_STEPS = {
+ // Checkout from VCS
+ """
+ Checkout the PR from the link.
+ If error occurs, return the error,
+ else return the path of the checked-out code""",
+ // Build the code and package
+ """
+ Identify the build tool and build the code of the given path.
+ If error occurs, return the error,
+ else return the path of the input""",
+ // Containerize and push to docker repo
+ """
+ On the given path, create the docker container. Then push to our private repo.
+ If error occurs, return the error,
+ else return the link of the container and the path to the code""",
+ // Deploy to test environment
+ """
+ Deploy the given docker image to test.
+ If error occurs, return the error,
+ else return the path of the input""",
+ // Run integration tests
+ """
+ From the PR code, execute the integration tests against test environment.
+ If error occurs, return the error,
+ else return success""" };
+
+ /**
+ * Prompt for the deployment of a container to one or many environments
+ */
+ public static final String NON_PROD_DEPLOYMENT_PROMPT =
+ // Prompt For Deployment
+ """
+ Deploy the given container to the given environment.
+ If any prod environment is requested, fail.
+ If error occurs, return the error,
+ else return success message.""";
+
+ /**
+ * Array of steps to be taken for deployment and test execution against this environment
+ */
+ public static final String[] EXECUTE_TEST_ON_DEPLOYED_ENV_STEPS = {
+ // Prompt For Deployment. If successful, pass to the next step the PR link and the environment.
+ """
+ Deploy the container associated to the given PR, on the given environment.
+ If any prod environment is requested, fail.
+ If error occurs, return the error,
+ else return an array of strings: '{the PR link] on {the environment}'.""",
+ // Continue with running the tests from the code base that are related to this env.
+ // Which means, run the Functional Tests on 'test' env, the Integration Tests on 'int', etc.
+ """
+ Execute the tests from the codebase version provided, that are related to the environment provided.
+ If error occurs, return the error,
+ else return the environment as title and then the test outcome.""" };
+}
diff --git a/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/aimodels/OpsOrchestratorClient.java b/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/aimodels/OpsOrchestratorClient.java
new file mode 100644
index 000000000000..4c2b2b4a5555
--- /dev/null
+++ b/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/aimodels/OpsOrchestratorClient.java
@@ -0,0 +1,7 @@
+package com.baeldung.springai.agenticpatterns.aimodels;
+
+import org.springframework.ai.chat.client.ChatClient;
+
+public interface OpsOrchestratorClient extends ChatClient {
+
+}
diff --git a/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/aimodels/OpsOrchestratorClientPrompts.java b/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/aimodels/OpsOrchestratorClientPrompts.java
new file mode 100644
index 000000000000..b24b73676488
--- /dev/null
+++ b/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/aimodels/OpsOrchestratorClientPrompts.java
@@ -0,0 +1,15 @@
+package com.baeldung.springai.agenticpatterns.aimodels;
+
+public final class OpsOrchestratorClientPrompts {
+
+ private OpsOrchestratorClientPrompts() {
+ }
+
+ /**
+ * Prompt to identify the environments which the given PR need to be tested on
+ */
+ public static final String REMOTE_TESTING_ORCHESTRATION_PROMPT = """
+ The user should provide a PR link. From the changes of each PR, you need to decide on which environments
+ these changes should be tested against. The outcome should be an array of the the PR link and then all the environments.
+ User input:\s""";
+}
diff --git a/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/aimodels/OpsRouterClient.java b/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/aimodels/OpsRouterClient.java
new file mode 100644
index 000000000000..803064f641f7
--- /dev/null
+++ b/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/aimodels/OpsRouterClient.java
@@ -0,0 +1,7 @@
+package com.baeldung.springai.agenticpatterns.aimodels;
+
+import org.springframework.ai.chat.client.ChatClient;
+
+public interface OpsRouterClient extends ChatClient {
+
+}
diff --git a/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/aimodels/OpsRouterClientPrompts.java b/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/aimodels/OpsRouterClientPrompts.java
new file mode 100644
index 000000000000..1591bdc76121
--- /dev/null
+++ b/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/aimodels/OpsRouterClientPrompts.java
@@ -0,0 +1,23 @@
+package com.baeldung.springai.agenticpatterns.aimodels;
+
+import java.util.Map;
+
+public final class OpsRouterClientPrompts {
+
+ private OpsRouterClientPrompts() {
+ }
+
+ /**
+ * Array of available routing options of Ops Model
+ */
+ public static final Map OPS_ROUTING_OPTIONS = Map.of(
+ // option 1: route to run pipeline
+ "pipeline", """
+ We'll need make a request to ChainWorkflow. Return only the PR link the user provided""",
+ // option 2: route to deploy an image in 1 or more envs
+ "deployment", """
+ We'll need make a request to ParallelizationWorkflow. Return 3 lines.
+ First: the container link the user provided, eg 'host/service/img-name/repo:1.12.1'.
+ Second: the environments, separated with comma, no spaces. eg: 'test,dev,int'
+ Third: the max concurent workers the client asked for, eg: '3'.""");
+}
diff --git a/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/workflows/chain/ChainWorkflow.java b/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/workflows/chain/ChainWorkflow.java
new file mode 100644
index 000000000000..87576d42939e
--- /dev/null
+++ b/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/workflows/chain/ChainWorkflow.java
@@ -0,0 +1,41 @@
+package com.baeldung.springai.agenticpatterns.workflows.chain;
+
+import org.springframework.ai.chat.client.ChatClient;
+import org.springframework.stereotype.Component;
+
+import com.baeldung.springai.agenticpatterns.aimodels.OpsClient;
+import com.baeldung.springai.agenticpatterns.aimodels.OpsClientPrompts;
+
+@Component
+public class ChainWorkflow {
+
+ private final OpsClient opsClient;
+
+ public ChainWorkflow(OpsClient opsClient) {
+ this.opsClient = opsClient;
+ }
+
+ public String opsPipeline(String userInput) {
+ String response = userInput;
+ System.out.printf("User input: [%s]\n", response);
+
+ for (String prompt : OpsClientPrompts.DEV_PIPELINE_STEPS) {
+ // Compose the request using the response from the previous step.
+ String request = String.format("{%s}\n {%s}", prompt, response);
+ System.out.printf("PROMPT: %s:\n", request);
+
+ // Call the ops client with the new request and get the new response.
+ ChatClient.ChatClientRequestSpec requestSpec = opsClient.prompt(request);
+ ChatClient.CallResponseSpec responseSpec = requestSpec.call();
+ response = responseSpec.content();
+ System.out.printf("OUTCOME: %s:\n", response);
+
+ // If there is an error, print the error and break
+ if (response.startsWith("ERROR:")) {
+ break;
+ }
+ }
+
+ return response;
+ }
+}
diff --git a/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/workflows/evaluator/EvaluatorOptimizerWorkflow.java b/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/workflows/evaluator/EvaluatorOptimizerWorkflow.java
new file mode 100644
index 000000000000..18a7c6106080
--- /dev/null
+++ b/spring-ai-modules/spring-ai-agentic-patterns/src/main/java/com/baeldung/springai/agenticpatterns/workflows/evaluator/EvaluatorOptimizerWorkflow.java
@@ -0,0 +1,72 @@
+package com.baeldung.springai.agenticpatterns.workflows.evaluator;
+
+import static com.baeldung.springai.agenticpatterns.aimodels.CodeReviewClientPrompts.CODE_REVIEW_PROMPT;
+import static com.baeldung.springai.agenticpatterns.aimodels.CodeReviewClientPrompts.EVALUATE_PROPOSED_IMPROVEMENTS_PROMPT;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.springframework.ai.chat.client.ChatClient;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.stereotype.Component;
+
+import com.baeldung.springai.agenticpatterns.aimodels.CodeReviewClient;
+
+@Component
+public class EvaluatorOptimizerWorkflow {
+
+ private final CodeReviewClient codeReviewClient;
+ static final ParameterizedTypeReference