Pre-requisites
- Knowledge of basic prompting, such as searching using AI Search tools like ChatGPT
- Java, Spring Boot (Only for implementation; however, the concepts are independent of programming language)
The Problem
Imagine a support team at an e-commerce company receiving a service request where a user wants a replacement for an item. Typically, the process looks like this:
- The customer representative interprets the customer query.
- Checks the rule book to determine whether the item is replaceable.
- If yes, informs the store representative to create a replacement order.
- The store representative checks the inventory for product availability.
- If available, informs the customer representative of the availability of stock.
- The customer representative replies to the customer, confirming acceptance of the replacement request.
Can we automate this workflow using AI?
To automate this workflow using tools like ChatGPT, we face the following challenges:
- How can we dynamically include the customer’s email in the prompt?
- How can we check inventory details and inform the LLM?
- We need to check inventory details only if order item is replaceable. How can we manage conditional execution?
- …
The Solution
The workflow defined above is a multistep process with various conditional flows. Each step will take an input from its previous step. Each step can be executed by an LLM independently, provided we have all necessary input data. Steps could include reading of email, checking the rule book, verifying stock availability, composing an email, etc. If we are able to set up this chain of steps (workflow) along with the capabilities of an LLM, we will be able to automate this problem. This process is often referred to as Agentic AI, where the LLM responsible for executing each step is called an Agent. The term Agentic AI is often credited to Andrew Ng.
Architecture
Depending on the eligibility for item replacement and availability of stock, the workflow takes different routes. The LLM’s capability to interpret natural language, combined with domain-specific knowledge, makes this automation efficient.
Implementation
To implement above workflow, we will use Java, Spring AI, and the Embabel Agent Framework.
Embabel (Em-BAY-bel) is built on top of Spring AI and seamlessly integrates with Spring Boot applications. Embabel makes it simple to wire together various steps in an Agent. Thanks to SivaLabs for introducing me to the Embabel framework.
Before getting into implementation details, let’s understand some terminologies used in Embabel:
- An AI component that can perform various steps to achieve a goal is referred to as an Agent.
- Each step of an agentic flow is referred to as an Action.
- The final step of an agentic flow is referred to as a Goal. All agentic workflows end with a goal.
- The dynamic sequence of steps determined by Embabel is referred to as a Plan.
Embabel uses GOAP (Goal Oriented Action Planning) technique to determine the sequence of steps based on the current agent state. This way of planning sequence of actions differentiates Embabel from other agentic frameworks.
A reference implementation for an Ecommerce Order Agent using Embabel is available here.
Dependencies
- Agent can be run as part of a Spring Application along with other Spring components. This is the preferred approach while integrating with enterprise applications. For this, we need to have following dependency:
<dependency>
<groupId>com.embabel.agent</groupId>
<artifactId>embabel-agent-starter</artifactId>
<version>${embabel-agent.version}</version>
</dependency>
- Another way to see the Agent working is through Spring Shell. It enables an interactive command-line interface to interact with Agent.
<dependency>
<groupId>com.embabel.agent</groupId>
<artifactId>embabel-agent-starter-shell</artifactId>
<version>${embabel-agent.version}</version>
</dependency>
This will run the agent in Spring Shell. Spring Shell is an interactive command line tool to interact with a Spring application.
For the interactive demo below, we will use Spring Shell.
Agent, Actions, and Goal
- Classes annotated with
@Agentare treated as agents.
@Agent(description = "Support agent to check replacement of items and schedule delivery of replacement item")
public class OrderSupportAgent {
- Each action (step) is marked with
@Action.
@Action(description = "Receives order replacement request from customer")
public ReplacementReport replacementReport(UserInput userInput, Ai ai) {
UserInput contains the input prompt from the user.
- The action that achieves the goal and ends the workflow is marked with
@AchievesGoal.
@AchievesGoal(description = "Checked for eligibility, stock, delivery schedule")
@Action
CustomerConfirmationReport customerConfirmationReport(DeliveryReport deliveryReport, Ai ai) {
- Each response from a LLM can be professional, friendly, humorous, or helpful based on the context. This is normally referred to as a Persona in prompting. In this case, we are using following personas:
static final Persona CUSTOMER_REPRESENTATIVE = Persona.create(
"Customer Representative",
"Customer Representative",
"Professional and helpful",
"Help customer on his/her order replacement"
);
static final Persona STORE_REPRESENTATIVE = Persona.create(
"Store Representative",
"Store Representative",
"Professional and precise",
"Help Customer Representative with order replacement delivery"
);
Tools
Each action can use various tools to bring in domain-specific knowledge. Tools make agents more capable of solving complex problems.
In this case, we are using the following tools:
- Rule Book tool to check whether an item is replaceable or not
- Orders tool to check order history
- Catalog tool to get item details such as name and category
- Inventory tool to check stock availability
We use @LlmTool annotation to mark a method as a tool.
@Component
public class RuleBook {
@LlmTool(description = "Rules to decide whether an item in the order is eligible for replacement or not")
public String rules() {
return """
- Check whether the item is eatable or not.
- Eatable items like bakery, dairy, snacks products aren't eligible for replacement.
- Non-eatable items like Electronics, Books are eligible for replacement.
""";
}
}
Testing
Emabel agents can be integrated with other Spring component like @Controller and @Service. For the sake of this demo, we will use scripts to run agent independently.
- Set environment variable of API_KEY for the model provider.
Eg.,
export OPENAI_API_KEY=xxxxxxxx - Run
./scripts/shell.sh
This will run the agent in Spring Shell. Embabel defines the default model to be used by Agent depending on the provider. We can also configure a particular model to be used by each action.
Embabel makes agent workflow logging interesting using logging personalities based on various themes.
By default, it is set to starwars.
_______ _______ _______ __ .___________. __ __ _______ _______ ______ .______ ______ _______ __
| ____|| ____|| ____|| | | || | | | | ____| | ____| / __ \ | _ \ / || ____|| |
| |__ | |__ | |__ | | `---| |----`| |__| | | |__ | |__ | | | | | |_) | | ,----'| |__ | |
| __| | __| | __| | | | | | __ | | __| | __| | | | | | / | | | __| | |
| | | |____ | |____ | `----. | | | | | | | |____ | | | `--' | | |\ \----.| `----.| |____ |__|
|__| |_______||_______||_______| |__| |__| |__| |_______| |__| \______/ | _| `._____| \______||_______|(__)
|/
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣤⣤⠤⠐⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡌⡦⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡀⣼⡊⢀⠔⠀⠀⣄⠤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣤⣤⣄⣀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣶⠃⠉⠡⡠⠤⠊⠀⠠⣀⣀⡠⠔⠒⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣾⣿⢟⠿⠛⠛⠁
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⡇⠀⠀⠀⠀⠑⠶⠖⠊⠁⠀⠀⠀⡀⠀⠀⠀⢀⣠⣤⣤⡀⠀⠀⠀⠀⠀⢀⣠⣤⣶⣿⣿⠟⡱⠁⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣾⣿⡇⠀⢀⡠⠀⠀⠀⠈⠑⢦⣄⣀⣀⣽⣦⣤⣾⣿⠿⠿⠿⣿⡆⠀⠀⢀⠺⣿⣿⣿⣿⡿⠁⡰⠁⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⣧⣠⠊⣠⣶⣾⣿⣿⣶⣶⣿⣿⠿⠛⢿⣿⣫⢕⡠⢥⣈⠀⠙⠀⠰⣷⣿⣿⣿⡿⠋⢀⠜⠁⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠠⢿⣿⣿⣿⣿⣰⣿⣿⠿⣛⡛⢛⣿⣿⣟⢅⠀⠀⢿⣿⠕⢺⣿⡇⠩⠓⠂⢀⠛⠛⠋⢁⣠⠞⠁⠀⠀⠀⠀⠀⠀⠀⠀
⠘⢶⡶⢶⣶⣦⣤⣤⣤⣤⣤⣀⣀⣀⣀⡀⠀⠘⣿⣿⣿⠟⠁⡡⣒⣬⢭⢠⠝⢿⡡⠂⠀⠈⠻⣯⣖⣒⣺⡭⠂⢀⠈⣶⣶⣾⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠙⠳⣌⡛⢿⣿⣿⣿⣿⣿⣿⣿⣿⣻⣵⣨⣿⣿⡏⢀⠪⠎⠙⠿⣋⠴⡃⢸⣷⣤⣶⡾⠋⠈⠻⣶⣶⣶⣷⣶⣷⣿⣟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠈⠛⢦⣌⡙⠛⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡀⠀⠀⠩⠭⡭⠴⠊⢀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣿⣿⣿⣿⣿⡇⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠈⠙⠓⠦⣄⡉⠛⠛⠻⢿⣿⣿⣿⣷⡀⠀⠀⠀⠀⢀⣰⠋⠀⠀⠀⠀⠀⣀⣰⠤⣳⣿⣿⣿⣿⣟⠑⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠓⠒⠒⠶⢺⣿⣿⣿⣿⣦⣄⣀⣴⣿⣯⣤⣔⠒⠚⣒⣉⣉⣴⣾⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠛⠹⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣭⣉⣉⣤⣿⣿⣿⣿⣿⣿⡿⢀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠟⡁⡆⠙⢶⣀⠀⢀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣴⣶⣾⣿⣟⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠿⢛⣩⣴⣿⠇⡇⠸⡆⠙⢷⣄⠻⣿⣦⡄⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣼⣿⣿⣿⣿⣿⣿⣿⣎⢻⣿⣿⣿⣿⣿⣿⣿⣭⣭⣭⣵⣶⣾⣿⣿⣿⠟⢰⢣⠀⠈⠀⠀⠙⢷⡎⠙⣿⣦⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣼⣿⣿⣿⣿⣿⣿⣿⣿⡟⣿⡆⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⠿⠿⠟⠛⠋⠁⢀⠇⢸⡇⠀⠀⠀⠀⠈⠁⠀⢸⣿⡆⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡜⡿⡘⣿⣿⣿⣿⣿⣶⣶⣤⣤⣤⣤⣤⣤⣤⣴⡎⠖⢹⡇⠀⠀⠀⠀⠀⠀⠀⠀⣿⣷⡄⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⡀⠘⢿⣿⣿⣿⣿⣿⣿⣿⣿⠿⠿⠛⠋⡟⠀⠀⣸⣷⣀⣤⣀⣀⣀⣤⣤⣾⣿⣿⣿⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣭⣓⡲⠬⢭⣙⡛⠿⣿⣿⣶⣦⣀⠀⡜⠀⠀⣰⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣭⣛⣓⠶⠦⠥⣀⠙⠋⠉⠉⠻⣄⣀⣸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣶⣆⠐⣦⣠⣷⠊⠁⠀⠀⡭⠙⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡆⠀⠀
⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⢉⣛⡛⢻⡗⠂⠀⢀⣷⣄⠈⢆⠉⠙⠻⢿⣿⣿⣿⣿⣿⠇⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⡟⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡟⣉⢁⣴⣿⣿⣿⣾⡇⢀⣀⣼⡿⣿⣷⡌⢻⣦⡀⠀⠈⠙⠛⠿⠏⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⣿⡄⠙⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠿⠛⠛⠛⢯⡉⠉⠉⠉⠉⠛⢼⣿⠿⠿⠦⡙⣿⡆⢹⣷⣤⡀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠿⠄⠈⠻⠿⠿⠿⠿⠿⠿⠛⠛⠿⠛⠉⠁⠀⠀⠀⠀⠀⠀⠻⠿⠿⠿⠿⠟⠉⠀⠀⠤⠴⠶⠌⠿⠘⠿⠿⠿⠿⠶⠤⠀⠀⠀⠀
10:37:15.920 [main] INFO SSEController - SSEController initialized, ready to stream AgentProcessEvents...
10:37:15.955 [main] INFO LlmRanker - Using auto LLM for ranking
10:37:15.959 [main] INFO AgentDeployer - AgentDeployer scanning disabled: not looking for agents defined as Spring beans
10:37:15.960 [main] INFO AgentPlatformPropertiesLoader - Agent platform properties loaded from classpath:agent-platform.properties and embabel-agent-properties
10:37:16.038 [main] INFO CommonPlatformPropertiesLoader - Common properties loaded from classpath:embabel-platform.properties and embabel-application.properties
10:37:16.040 [main] INFO ScanConfiguration - ComponentConfiguration initialized: Scanning com.embabel.agent and com.embabel.example packages.
10:37:16.042 [main] INFO AgentPlatformAutoConfiguration - AgentPlatformAutoConfiguration has been initialized.
10:37:16.042 [main] INFO AgentPlatformAutoConfiguration - AgentPlatformAutoConfiguration about to be processed...
10:37:16.149 [main] WARN SyncMcpSamplingProvider - No sampling methods found
10:37:16.150 [main] WARN SyncMcpElicitationProvider - No elicitation methods found
10:37:16.156 [main] INFO DelegatingAgentScanningBeanPostProcessor - Application context has been refreshed and all beans are initialized.
10:37:16.239 [main] INFO starwars - Deployed an agent I have: OrderSupportAgent
description: Support agent to check replacement of items and schedule delivery of replacement item
10:37:16.241 [main] INFO DelegatingAgentScanningBeanPostProcessor - All deferred beans were post-processed.
10:37:16.252 [main] INFO ProjectNameApplication - Started ProjectNameApplication in 1.73 seconds (process running for 1.916)
Let the past die. Kill it if you have to.
starwars>
For testing purposes, I have already populated some orders, catalog, and inventory data in memory.
To initiate the agentic workflow, we need to provide a prompt.
starwars> x "I want to return order ORD007"
Note, the x command is used to execute an agent with the given prompt.
In my case, the final response is
You asked: UserInput(content=I want to return ORD007, timestamp=2026-01-23T05:13:22.558813Z)
{
"text" : "Dear Customer,\n\nWe are pleased to inform you that the item Mouse Wireless (ITM002) is currently in stock with a quantity of 120 units available. Please let us know if you would like to proceed with your order or require any further assistance.\n\nBest regards,\nCustomer Service Team"
}
For orders with no stock (like ORD003), I get the following response
You asked: UserInput(content=I want to return ORD003, timestamp=2026-01-23T05:15:57.571181Z)
{
"text" : "Dear Customer,\n\nThank you for your order. We regret to inform you that the item ITM001 (Laptop Dell XPS) is currently out of stock and cannot be delivered at this time. We apologize for the inconvenience this may cause.\n\nPlease let us know if you would like to wait until the item is back in stock or if you prefer to select an alternative product. We are here to assist you with any further questions or requests.\n\nThank you for your understanding.\n\nBest regards,\nCustomer Service Team"
}
For edible items that aren’t replaceable according to the rules:
You asked: UserInput(content=I want to return ORD005, timestamp=2026-01-23T05:17:17.774698Z)
{
"text" : "Dear Customer,\n\nThank you for reaching out to us. Please be informed that the Marie Biscuits Pack is categorized as an edible snack item. As per our store policy, items in this category are not eligible for replacement, regardless of stock availability.\n\nWe appreciate your understanding in this matter.\n\nBest regards,\nCustomer Service Team"
}
Congratulations!🎉 We have successfully built an Agentic AI system to automate an e-commerce order replacement workflow.
Next
- If you closely observe the logs, the agent needed to take various paths based on the conditions. E.g., take different actions based on whether the item is replaceable or not, check stock only if item is replaceable, etc. This we will explore in detail in upcoming posts.
- Agentic AI is rapidly evolving field. Main concern with any AI system is reliability and predictability. Various techniques have been developed and are being researched to increase confidence in agentic systems. We will discuss few of them in upcoming posts.
Credits
- Agentic AI - https://learn.deeplearning.ai/courses/agentic-ai
- Embabel Agent Framework - https://github.com/embabel/embabel-agent
- GenAI Grows Up: Building Production-Ready Agents on the JVM • Rod Johnson • GOTO 2025 - https://youtu.be/yMDw0nlWd7s?si=kc1QFCHQodkPjpoj
- Building AI Agents using Java and Embabel - https://youtu.be/lqQ_NL4y5Qg?si=x6475NBbhamxTkeL