Skip to content

Commit 990fb4f

Browse files
author
Yuriy Bezsonov
committed
feat(aiagent-agentcore): add complete AI agent application with Spring AI and Bedrock integration
1 parent d3a780c commit 990fb4f

33 files changed

Lines changed: 4456 additions & 0 deletions
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
<parent>
6+
<groupId>org.springframework.boot</groupId>
7+
<artifactId>spring-boot-starter-parent</artifactId>
8+
<version>4.0.1</version>
9+
<relativePath/>
10+
</parent>
11+
<groupId>com.example</groupId>
12+
<artifactId>aiagent</artifactId>
13+
<version>0.0.1-SNAPSHOT</version>
14+
<name>aiagent</name>
15+
<description>AI Agent with Spring AI and Amazon Bedrock AgentCore</description>
16+
17+
<properties>
18+
<java.version>25</java.version>
19+
<spring-ai.version>2.0.0-M2</spring-ai.version>
20+
</properties>
21+
22+
<dependencies>
23+
<dependency>
24+
<groupId>org.springframework.boot</groupId>
25+
<artifactId>spring-boot-starter-web</artifactId>
26+
</dependency>
27+
<dependency>
28+
<groupId>org.springframework.boot</groupId>
29+
<artifactId>spring-boot-starter-webflux</artifactId>
30+
</dependency>
31+
<dependency>
32+
<groupId>org.springframework.boot</groupId>
33+
<artifactId>spring-boot-starter-actuator</artifactId>
34+
</dependency>
35+
36+
<!-- Bedrock model -->
37+
<dependency>
38+
<groupId>org.springframework.ai</groupId>
39+
<artifactId>spring-ai-starter-model-bedrock-converse</artifactId>
40+
</dependency>
41+
42+
<!-- AgentCore Starter - auto-configures /invocations and /ping endpoints -->
43+
<dependency>
44+
<groupId>org.springaicommunity</groupId>
45+
<artifactId>spring-ai-bedrock-agentcore-starter</artifactId>
46+
<version>1.0.0-RC5</version>
47+
</dependency>
48+
<!-- AgentCore Memory - ChatMemoryRepository implementation -->
49+
<dependency>
50+
<groupId>org.springaicommunity</groupId>
51+
<artifactId>spring-ai-memory-bedrock-agentcore</artifactId>
52+
<version>1.0.0-RC5</version>
53+
</dependency>
54+
55+
<!-- Vector Store for RAG (Knowledge Base) -->
56+
<dependency>
57+
<groupId>org.springframework.ai</groupId>
58+
<artifactId>spring-ai-starter-vector-store-bedrock-knowledgebase</artifactId>
59+
</dependency>
60+
<dependency>
61+
<groupId>org.springframework.ai</groupId>
62+
<artifactId>spring-ai-advisors-vector-store</artifactId>
63+
</dependency>
64+
65+
<!-- Security - OAuth2 Resource Server for JWT validation -->
66+
<dependency>
67+
<groupId>org.springframework.boot</groupId>
68+
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
69+
</dependency>
70+
71+
<!-- AWS Bedrock Runtime SDK (for Web Grounding) -->
72+
<dependency>
73+
<groupId>software.amazon.awssdk</groupId>
74+
<artifactId>bedrockruntime</artifactId>
75+
</dependency>
76+
77+
<!-- Playwright for Browser automation -->
78+
<dependency>
79+
<groupId>com.microsoft.playwright</groupId>
80+
<artifactId>playwright</artifactId>
81+
<version>1.49.0</version>
82+
</dependency>
83+
84+
<!-- Caffeine cache for Code Interpreter file store -->
85+
<dependency>
86+
<groupId>com.github.ben-manes.caffeine</groupId>
87+
<artifactId>caffeine</artifactId>
88+
</dependency>
89+
90+
<!-- MCP Client dependencies -->
91+
<dependency>
92+
<groupId>org.springframework.ai</groupId>
93+
<artifactId>spring-ai-starter-mcp-client</artifactId>
94+
</dependency>
95+
<!-- MCP Client Security - OAuth2 support for remote MCP servers -->
96+
<dependency>
97+
<groupId>org.springaicommunity</groupId>
98+
<artifactId>mcp-client-security</artifactId>
99+
<version>0.1.1</version>
100+
</dependency>
101+
<!-- OAuth2 Client - required for MCP client OAuth -->
102+
<dependency>
103+
<groupId>org.springframework.boot</groupId>
104+
<artifactId>spring-boot-starter-oauth2-client</artifactId>
105+
</dependency>
106+
107+
<!-- Testing -->
108+
<dependency>
109+
<groupId>org.springframework.boot</groupId>
110+
<artifactId>spring-boot-starter-test</artifactId>
111+
<scope>test</scope>
112+
</dependency>
113+
<dependency>
114+
<groupId>org.awaitility</groupId>
115+
<artifactId>awaitility</artifactId>
116+
<scope>test</scope>
117+
</dependency>
118+
</dependencies>
119+
120+
<dependencyManagement>
121+
<dependencies>
122+
<dependency>
123+
<groupId>org.springframework.ai</groupId>
124+
<artifactId>spring-ai-bom</artifactId>
125+
<version>${spring-ai.version}</version>
126+
<type>pom</type>
127+
<scope>import</scope>
128+
</dependency>
129+
<dependency>
130+
<groupId>software.amazon.awssdk</groupId>
131+
<artifactId>bom</artifactId>
132+
<version>2.40.3</version>
133+
<type>pom</type>
134+
<scope>import</scope>
135+
</dependency>
136+
</dependencies>
137+
</dependencyManagement>
138+
139+
<build>
140+
<plugins>
141+
<plugin>
142+
<groupId>org.springframework.boot</groupId>
143+
<artifactId>spring-boot-maven-plugin</artifactId>
144+
</plugin>
145+
<plugin>
146+
<groupId>io.spring.javaformat</groupId>
147+
<artifactId>spring-javaformat-maven-plugin</artifactId>
148+
<version>0.0.43</version>
149+
</plugin>
150+
<plugin>
151+
<groupId>org.apache.maven.plugins</groupId>
152+
<artifactId>maven-failsafe-plugin</artifactId>
153+
<configuration>
154+
<skipITs>${skipITs}</skipITs>
155+
</configuration>
156+
<executions>
157+
<execution>
158+
<goals>
159+
<goal>integration-test</goal>
160+
<goal>verify</goal>
161+
</goals>
162+
</execution>
163+
</executions>
164+
</plugin>
165+
</plugins>
166+
</build>
167+
168+
<profiles>
169+
<profile>
170+
<id>skip-integration-tests</id>
171+
<activation>
172+
<property>
173+
<name>!env.AGENTCORE_IT</name>
174+
</property>
175+
</activation>
176+
<properties>
177+
<skipITs>true</skipITs>
178+
</properties>
179+
</profile>
180+
</profiles>
181+
182+
</project>
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Expense Policy
2+
3+
## Purpose
4+
Guidelines for submitting and approving business expense reimbursements.
5+
6+
## Approval Matrix
7+
8+
- **Auto-approved:** Standard expenses (meals without alcohol, transportation, accommodation within limits)
9+
- **Manager approval:** Any expense with alcoholic beverages
10+
- **Director approval:** Accommodation over €130/night (Europe)
11+
- **Executive approval:** Any single expense over €500
12+
13+
## Required Documentation
14+
15+
All expense submissions must include:
16+
- Original receipt or invoice
17+
- Date in YYYY-MM-DD format
18+
- Amount and currency code (EUR, USD, GBP, etc.)
19+
- Business justification
20+
21+
## Category-Specific Requirements
22+
23+
### Accommodation
24+
Must show:
25+
- Check-in and check-out dates
26+
- Number of nights stayed
27+
- Rate per night
28+
- Location (for regional limit verification)
29+
30+
### Meals
31+
Must indicate:
32+
- Whether alcoholic beverages are included
33+
- Alcohol requires Manager approval regardless of amount
34+
35+
### Transportation
36+
Must show:
37+
- Type (flight, train, taxi, car rental, etc.)
38+
- Route or location
39+
40+
## Policy Compliance
41+
42+
### Violations
43+
- Expenses exceeding limits without approval will be rejected
44+
- Alcohol expenses without Manager approval will be denied
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Travel Policy
2+
3+
## Purpose
4+
Guidelines for booking business travel including flights, accommodation, and transportation.
5+
6+
## Accommodation
7+
8+
### Regional Limits
9+
- **Europe:** Maximum €130 per night
10+
- **North America:** Maximum $150 per night
11+
- **Asia Pacific:** Maximum $120 per night
12+
13+
### Booking Requirements
14+
- Use company-preferred vendors when available
15+
- Exceeding regional limits requires Director approval
16+
17+
## Transportation
18+
19+
### Air Travel
20+
- **Economy class** for standard flights
21+
- **Business class** requires Director approval
22+
23+
### Ground Transportation
24+
- Taxi/rideshare for airport transfers and business meetings
25+
- Car rentals require Manager approval
26+
27+
## Approval Requirements
28+
- All travel must be pre-approved by Manager
29+
- Exceptions to policy limits require Director approval
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.example.agent;
2+
3+
import org.springframework.boot.SpringApplication;
4+
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
6+
@SpringBootApplication
7+
public class AgentApplication {
8+
9+
public static void main(String[] args) {
10+
SpringApplication.run(AgentApplication.class, args);
11+
}
12+
13+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package com.example.agent;
2+
3+
// Core Spring AI
4+
import org.springframework.ai.chat.client.ChatClient;
5+
import org.springframework.stereotype.Service;
6+
import reactor.core.publisher.Flux;
7+
8+
// Memory
9+
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
10+
import org.springframework.ai.chat.client.advisor.api.Advisor;
11+
import org.springframework.ai.chat.memory.ChatMemory;
12+
import org.springframework.ai.chat.memory.ChatMemoryRepository;
13+
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
14+
15+
// Knowledge Base
16+
import org.springframework.ai.chat.client.advisor.vectorstore.QuestionAnswerAdvisor;
17+
import org.springframework.ai.vectorstore.VectorStore;
18+
19+
// Common
20+
import org.slf4j.Logger;
21+
import org.slf4j.LoggerFactory;
22+
import org.springframework.beans.factory.annotation.Autowired;
23+
import org.springframework.beans.factory.annotation.Qualifier;
24+
import org.springframework.beans.factory.annotation.Value;
25+
import java.util.ArrayList;
26+
import java.util.List;
27+
28+
@Service
29+
public class ChatService {
30+
31+
private static final Logger logger = LoggerFactory.getLogger(ChatService.class);
32+
33+
private static final String SYSTEM_PROMPT = """
34+
You are a helpful AI Agent for travel and expense management.
35+
Be friendly, helpful, and concise in your responses.
36+
37+
IMPORTANT: Always call getCurrentDateTime first to know the current date before processing any request.
38+
39+
Use the knowledge base context for internal company policies and procedures.
40+
Use searchWeb for current information from the internet.
41+
""";
42+
43+
private final ChatClient chatClient;
44+
45+
public ChatService(ChatClient.Builder chatClientBuilder,
46+
// Memory
47+
@Autowired(required = false) ChatMemoryRepository memoryRepository,
48+
@Autowired(required = false) List<Advisor> ltmAdvisors,
49+
// Knowledge Base
50+
@Autowired(required = false) VectorStore kbVectorStore,
51+
// Tools
52+
@Autowired(required = false) WebGroundingTools webGroundingTools) {
53+
54+
// Memory
55+
List<Advisor> advisors = new ArrayList<>();
56+
if (memoryRepository != null) {
57+
ChatMemory chatMemory = MessageWindowChatMemory.builder().chatMemoryRepository(memoryRepository).build();
58+
advisors.add(MessageChatMemoryAdvisor.builder(chatMemory).order(10).build());
59+
logger.info("STM enabled");
60+
}
61+
if (ltmAdvisors != null && !ltmAdvisors.isEmpty()) {
62+
advisors.addAll(ltmAdvisors);
63+
logger.info("LTM enabled: {} advisors", ltmAdvisors.size());
64+
}
65+
66+
// Knowledge Base
67+
if (kbVectorStore != null) {
68+
advisors.add(QuestionAnswerAdvisor.builder(kbVectorStore).order(1000).build());
69+
logger.info("KB RAG enabled");
70+
}
71+
72+
// Tools
73+
List<Object> localTools = new ArrayList<>();
74+
75+
localTools.add(new DateTimeTools());
76+
localTools.add(new WeatherTools());
77+
78+
if (webGroundingTools != null) {
79+
localTools.add(webGroundingTools);
80+
logger.info("Web Grounding enabled");
81+
}
82+
83+
// Build ChatClient
84+
this.chatClient = chatClientBuilder.defaultSystem(SYSTEM_PROMPT)
85+
.defaultAdvisors(advisors.toArray(new Advisor[0]))
86+
.defaultTools(localTools.toArray())
87+
.build();
88+
89+
logger.info("ChatService initialized: {} advisors, {} local tools", advisors.size(), localTools.size());
90+
}
91+
92+
public Flux<String> chat(InvocationRequest request, String sessionId) {
93+
return chat(request.prompt(), sessionId);
94+
}
95+
96+
public Flux<String> chat(String prompt, String sessionId) {
97+
return chatClient.prompt()
98+
.user(prompt)
99+
.advisors(a -> a.param(ChatMemory.CONVERSATION_ID, sessionId))
100+
.stream()
101+
.content()
102+
.onErrorResume(e -> {
103+
logger.error("Chat error", e);
104+
return Flux.just("Error processing request.");
105+
});
106+
}
107+
108+
}

0 commit comments

Comments
 (0)