> Use when integrating LLMs, chat clients, embeddings, RAG pipelines, or AI agents into Spring Boot. Covers Spring AI ChatClient, prompt templates, embeddings, vector stores, and structured output. Use when user mentions Spring AI, LLM, ChatGPT, Claude, RAG, embeddings.
npx skills add https://github.com/rrezartprebreza/spring-boot-skills --skill spring-ai-integration
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Choose your model provider — 1.0 GA renamed every starter to spring-ai-starter-* -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-anthropic</artifactId>
</dependency>
<!-- OR -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
<!-- For RAG / vector search -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-vector-store-pgvector</artifactId>
</dependency>
</dependencies>
> Watch the artifact names. 1.0 GA dropped the old spring-ai-<x>-spring-boot-starter
> coordinates. The pattern is now spring-ai-starter-model-<provider> (e.g. -model-anthropic,
> -model-openai) and spring-ai-starter-vector-store-<store>. Agents trained on pre-GA Spring AI
> will emit the dead names — they resolve to nothing in Maven Central.
@Service
@RequiredArgsConstructor
public class DocumentSummaryService {
private final ChatClient chatClient;
public String summarize(String conversationId, String content) {
return chatClient.prompt()
.advisors(a -> a.param(ChatMemory.CONVERSATION_ID, conversationId))
.user(u -> u.text("Summarize the following document in 3 bullet points:\n\n{content}")
.param("content", content))
.call()
.content();
}
// With system prompt
public String analyzeFinancial(String conversationId, String document, String language) {
return chatClient.prompt()
.advisors(a -> a.param(ChatMemory.CONVERSATION_ID, conversationId))
.system("You are a financial analyst. Respond in {language}.")
.system(s -> s.param("language", language))
.user(document)
.call()
.content();
}
}
Every call using the configured memory advisor must provide a user- or session-scoped
ChatMemory.CONVERSATION_ID. Never use one shared conversation ID for all users.
@Configuration
public class AiConfig {
@Bean
public ChatMemory chatMemory() {
// 1.0 GA: InMemoryChatMemory is GONE. Use MessageWindowChatMemory —
// it caps history to a sliding window and defaults to an in-memory repository.
return MessageWindowChatMemory.builder()
.maxMessages(20)
.build();
}
@Bean
public ChatClient chatClient(ChatClient.Builder builder, ChatMemory chatMemory) {
return builder
.defaultSystem("You are a helpful assistant for an e-commerce platform.")
.defaultAdvisors(
MessageChatMemoryAdvisor.builder(chatMemory).build(), // GA: builder, not new(...)
new SimpleLoggerAdvisor() // logs prompts/responses
)
.build();
}
}
// src/main/resources/prompts/analyze-order.st
// Analyze this order and identify any anomalies:
// Customer: {customer}
// Items: {items}
// Total: {total}
// Flag any unusual patterns.
@Service
public class OrderAnalysisService {
@Value("classpath:prompts/analyze-order.st")
private Resource promptTemplate;
public String analyzeOrder(String conversationId, Order order) {
return chatClient.prompt()
.advisors(a -> a.param(ChatMemory.CONVERSATION_ID, conversationId))
.user(u -> u.text(promptTemplate)
.param("customer", order.getCustomerEmail())
.param("items", order.getItems().toString())
.param("total", order.getTotal()))
.call()
.content();
}
}
// Define the target record
public record OrderClassification(
String category,
String priority,
List<String> tags,
boolean requiresManualReview
) {}
@Service
public class OrderClassifier {
public OrderClassification classify(String orderDescription) {
return chatClient.prompt()
.user("Classify this order: " + orderDescription)
.call()
.entity(OrderClassification.class); // Spring AI handles JSON parsing
}
}
@Configuration
public class RagConfig {
// No manual VectorStore bean — the spring-ai-starter-vector-store-pgvector
// starter auto-configures one. Just inject it. (The old `new PgVectorStore(...)`
// constructor is removed in GA; if you must build one, use PgVectorStore.builder(...).)
@Bean
public ChatClient ragChatClient(ChatClient.Builder builder, VectorStore vectorStore) {
return builder
.defaultAdvisors(
QuestionAnswerAdvisor.builder(vectorStore)
.searchRequest(SearchRequest.builder().topK(5).build()) // GA: builder, not defaults().withTopK()
.build()
)
.build();
}
}
@Service
@RequiredArgsConstructor
public class KnowledgeService {
private final VectorStore vectorStore;
private final ChatClient ragChatClient;
// Ingest documents
public void ingest(List<String> documents) {
List<Document> docs = documents.stream()
.map(content -> new Document(content))
.toList();
vectorStore.add(docs);
}
// Query with RAG
public String ask(String question) {
return ragChatClient.prompt()
.user(question)
.call()
.content();
}
}
@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> stream(@RequestParam String prompt) {
return chatClient.prompt()
.user(prompt)
.stream()
.content();
}
spring:
ai:
anthropic:
api-key: ${ANTHROPIC_API_KEY}
chat:
options:
model: claude-sonnet-4-20250514
max-tokens: 2048
temperature: 0.7
# OR for OpenAI:
openai:
api-key: ${OPENAI_API_KEY}
chat:
options:
model: gpt-4o
vectorstore:
pgvector:
initialize-schema: true
dimensions: 1536
spring-ai-anthropic-spring-boot-starter) — GA is spring-ai-starter-model-anthropicnew MessageChatMemoryAdvisor(new InMemoryChatMemory()) — both removed in GA; use MessageChatMemoryAdvisor.builder(chatMemory) + MessageWindowChatMemorySearchRequest.defaults().withTopK(n) — GA is SearchRequest.builder().topK(n).build()${...}.param() template variablessrc/main/resources/prompts/.entity(MyClass.class) instead of parsing manually.entity(List.class) for a list — generics erase; pass new ParameterizedTypeReference<List<X>>() {}NonTransientAiException (don't retry) vs TransientAiException (retry)conversationId on the memory advisor — all users share one chat historyCreate new skills, modify and improve existing skills, and measure skill performance. Use when users want to create a skill from scratch, edit, or optimize an existing skill, run evals to test a skill, benchmark skill performance with variance analysis, or optimize a skill's description for better triggering accuracy.
Access NCBI GEO for gene expression/genomics data. Search/download microarray and RNA-seq datasets (GSE, GSM, GPL), retrieve SOFT/Matrix files, for transcriptomics and expression analysis.
Bayesian modeling with PyMC. Build hierarchical models, MCMC (NUTS), variational inference, LOO/WAIC comparison, posterior checks, for probabilistic programming and inference.
Multi-objective optimization framework. NSGA-II, NSGA-III, MOEA/D, Pareto fronts, constraint handling, benchmarks (ZDT, DTLZ), for engineering design and optimization problems.
Statistical modeling toolkit. OLS, GLM, logistic, ARIMA, time series, hypothesis tests, diagnostics, AIC/BIC, for rigorous statistical inference and econometric analysis.
Add unsigned integer (uint) type support to PyTorch operators by updating AT_DISPATCH macros. Use when adding support for uint16, uint32, uint64 types to operators, kernels, or when user mentions enabling unsigned types, barebones unsigned types, or uint support.
Convert PyTorch AT_DISPATCH macros to AT_DISPATCH_V2 format in ATen C++ code. Use when porting AT_DISPATCH_ALL_TYPES_AND*, AT_DISPATCH_FLOATING_TYPES*, or other dispatch macros to the new v2 API. For ATen kernel files, CUDA kernels, and native operator implementations.
Write docstrings for PyTorch functions and methods following PyTorch conventions. Use when writing or updating docstrings in PyTorch code.
Take rrezartprebreza/spring-ai-integration from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.