mcpField
S
spring-ai-integration

spring-ai-integration

github.com/rrezartprebreza开发328100 次安装6 天前更新
代码仓库

关于 spring-ai-integration

面向 Java 开发者的 Spring AI 集成参考技能,帮助在 Spring 应用中构建 LLM 功能,涵盖聊天、记忆、提示词、结构化输出、RAG、流式响应及 GA 版本用法。

主要功能

  1. 01ChatClient 配置与用法
  2. 02对话记忆与提示词模板
  3. 03结构化输出映射
  4. 04RAG 流程配置
  5. 05流式响应
  6. 06Spring AI GA 迁移指引

适用场景

  1. 01使用 Spring AI 构建聊天服务
  2. 02按用户隔离对话历史
  3. 03构建检索增强生成流程
  4. 04返回结构化或流式模型响应

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/rrezartprebreza/spring-boot-skills --skill spring-ai-integration

安装后在 Claude Code 中输入/spring-ai-integration确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

agents/
openai.yaml
examples/
bad-ai-service.java
good-ai-service.java
templates/
AiServiceTemplate.java
analyze-order.st

详细介绍

Spring AI 集成

依赖项

<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>
    
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-model-anthropic</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-model-openai</artifactId>
    </dependency>

    
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-vector-store-pgvector</artifactId>
    </dependency>
</dependencies>

注意制品名称。 1.0 GA 移除了旧的 spring-ai-<x>-spring-boot-starter 坐标。现在的命名模式为 spring-ai-starter-model-<provider>(例如 -model-anthropic、-model-openai)和 spring-ai-starter-vector-store-<store>。基于 GA 前 Spring AI 训练的 Agent 会生成已失效的名称——这些名称在 Maven Central 中无法解析。

ChatClient — 基本用法

@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();
    }
}

使用已配置的记忆顾问进行的每次调用,都必须提供用户级或会话级的 ChatMemory.CONVERSATION_ID。切勿让所有用户共用同一个会话 ID。

ChatClient Bean 配置

@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: {customerEmail}
// Items: {items}
// Total: {total}
// Flag any unusual patterns.

@Service
@RequiredArgsConstructor
public class OrderAnalysisService {

    private final ChatClient chatClient;

    @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("customerEmail", order.getCustomerEmail()) // must match {customerEmail} in the .st
                .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
    }
}

RAG 流水线

@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();
}

application.yml

spring:
  ai:
    anthropic:
      api-key: ${ANTHROPIC_API_KEY}
      chat:
        options:
          model: ${ANTHROPIC_MODEL}
          max-tokens: 2048
          temperature: 0.7
    # OR for OpenAI:
    openai:
      api-key: ${OPENAI_API_KEY}
      chat:
        options:
          model: ${OPENAI_MODEL}
    vectorstore:
      pgvector:
        initialize-schema: true
        dimensions: 1536

注意事项

  • Agent 在 .st 文件中写的是 {customerEmail} 时,却绑定了 .param("customer", ...)——默认渲染器会报错 "Not all variables were replaced";请确保参数名称与模板占位符完全一致
  • Agent 使用 GA 前的制品名称(spring-ai-anthropic-spring-boot-starter)——GA 版本使用 spring-ai-starter-model-anthropic
  • Agent 编写 new MessageChatMemoryAdvisor(new InMemoryChatMemory())——这两个类在 GA 版本中都已移除;请改用 MessageChatMemoryAdvisor.builder(chatMemory) 和 MessageWindowChatMemory
  • Agent 编写 SearchRequest.defaults().withTopK(n)——GA 版本使用 SearchRequest.builder().topK(n).build()
  • Agent 将 API 密钥硬编码——请始终使用环境变量 / ${...}
  • Agent 将提供商模型 ID 硬编码——请在外部配置,因为模型目录会变化
  • Agent 通过字符串拼接来构建提示词——请使用 .param() 模板变量
  • Agent 将提示词直接写在代码中——请将其外部化到 src/main/resources/prompts/
  • Agent 忽略结构化输出——请使用 .entity(MyClass.class),而不是手动解析
  • Agent 使用 .entity(List.class) 返回列表——泛型会被擦除;请传入 new ParameterizedTypeReference<List<X>>() {}
  • Agent 跳过 API 调用的错误处理——请使用 try/catch,并区分处理 NonTransientAiException(不要重试)与 TransientAiException(重试)
  • Agent 忘记为记忆顾问设置用户级 conversationId——所有用户会共用一份聊天记录
  • Agent 使用了错误的模型字符串——请根据提供商文档核对模型名称

评论

支持 Markdown