# SpringAI1.0.1教程 **Repository Path**: Lapper/ChatAI ## Basic Information - **Project Name**: SpringAI1.0.1教程 - **Description**: 本项目是一个基于 Spring AI 1.0.1 的聊天应用示例,集成了 Ollama 模型,展示了如何使用 Spring AI 的核心功能: - 多模型动态切换 - 聊天记忆管理 - 自定义 Advisor(日志记录、拦截器等) - 用户会话隔离 - 流式响应处理 - **Primary Language**: Java - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: https://lapper.site - **GVP Project**: No ## Statistics - **Stars**: 4 - **Forks**: 2 - **Created**: 2025-08-10 - **Last Updated**: 2025-09-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Spring AI 1.0.1 使用教程-2025年八月最新版 ![输入图片说明](image.png) > 很高兴JAVA也有自己的AI框架 作为一个Java重度爱好者,也是本来打算网上找个教程直接学习 > 但是网上的教程由于跟不上SpringAI的迭代速度 很多接口可能被废弃了 学起来反而一堆问题 于是就自己看着api 慢慢弄出了这套稳定的最新接口示例 > 自己踩了坑 就不想让其他人也踩坑 > 我平时热爱学习编程 目前是在职开发人员 wx:EonNetWork 邮箱:shibaizhelianmeng@163.com 欢迎大家交流 也有交流群 ## 项目简介 本项目是一个基于 Spring AI 1.0.1 的聊天应用示例,集成了 Ollama 模型,展示了如何使用 Spring AI 的核心功能: - 多模型动态切换 - 聊天记忆管理 - 自定义 Advisor(日志记录、拦截器等) - 用户会话隔离 - 流式响应处理 ## 环境要求 - Java 17+ - Spring Boot 3.5.4 - Spring AI 1.0.1 - Ollama(本地运行) ## 快速开始 ### 1. 安装 Ollama ```bash # 下载并安装 Ollama curl -fsSL https://ollama.ai/install.sh | sh # 拉取模型(示例) ollama pull deepseek-r1:7b ollama pull qwen2:7b ollama pull llama3.2:3b ``` ### 2. 启动项目 ```bash # 克隆项目 git clone cd ChatAI # 启动应用 ./mvnw spring-boot:run ``` ### 3. 测试接口 ```bash # 基本聊天 curl "http://localhost:8080/ai/chat?prompt=你好&chatid=1&model=deepseek-r1:7b" # 切换模型 curl "http://localhost:8080/ai/chat?prompt=讲个笑话&chatid=1&model=qwen2:7b" ``` ## 核心功能详解 ### 1. ChatClient 配置 **文件位置:** `src/main/java/com/example/chatai/Config/CommonConfigOllama.java` ```java @Configuration public class CommonConfigOllama { @Bean public ChatMemory chatMemory(){ return MessageWindowChatMemory.builder().build(); } @Bean public ChatClient client(OllamaChatModel model){ return ChatClient.builder(model) .defaultSystem("你是一个可爱的小女孩你的回答语气 非常可爱是一个智能助手") .defaultAdvisors( new SimpleLoggerAdvisor(), MessageChatMemoryAdvisor.builder(chatMemory()).build(), new CustomLoggingAdvisor() ) .build(); } } ``` **核心接口说明:** - `ChatClient.builder(model)`: 创建聊天客户端构建器 - `.defaultSystem(String)`: 设置默认系统提示词 - `.defaultAdvisors(...)`: 注册默认的 Advisor 链 ### 2. 动态模型切换 **文件位置:** `src/main/java/com/example/chatai/Controller/ChatController.java` ```java @GetMapping(value = "/chat", produces = "text/html;charset=utf-8") public Flux chat(String prompt, int chatid, @RequestParam(defaultValue = "deepseek-r1:7b") String model) { return client.prompt() .options(OllamaOptions.builder() .model(model) .build()) .user(prompt) .advisors(a -> a.param(CONVERSATION_ID, chatid)) .stream() .content(); } ``` **接口定义:** - `client.prompt()`: 开始构建提示 - `.options(OllamaOptions)`: 设置模型参数 - `.user(String)`: 设置用户消息 - `.advisors(...)`: 配置会话级 Advisor - `.stream()`: 启用流式响应 - `.content()`: 获取内容流 **OllamaOptions 常用配置:** ```java OllamaOptions.builder() .model("deepseek-r1:7b") // 模型名称 .temperature(0.7f) // 温度参数 .topP(0.9f) // Top-P 采样 .maxTokens(1000) // 最大 Token 数 .build() ``` ### 3. 聊天记忆管理 #### 3.1 基础记忆配置 ```java @Bean public ChatMemory chatMemory(){ // 滑动窗口记忆(保留最近 N 条消息) return MessageWindowChatMemory.builder() .maxMessages(10) // 最大消息数 .build(); } ``` #### 3.2 用户会话隔离 通过 `CONVERSATION_ID` 实现多用户隔离: ```java .advisors(a -> a.param(CONVERSATION_ID, chatid)) ``` **不同用户示例:** ```bash # 用户1的对话 curl "http://localhost:8080/ai/chat?prompt=我叫张三&chatid=1" curl "http://localhost:8080/ai/chat?prompt=我叫什么名字?&chatid=1" # 用户2的对话 curl "http://localhost:8080/ai/chat?prompt=我叫李四&chatid=2" curl "http://localhost:8080/ai/chat?prompt=我叫什么名字?&chatid=2" ``` #### 3.3 持久化记忆(扩展) **自定义持久化记忆实现:** ```java @Component public class DatabaseChatMemory implements ChatMemory { @Autowired private ChatMessageRepository repository; @Override public void add(String conversationId, List messages) { // 保存到数据库 messages.forEach(msg -> { ChatMessageEntity entity = new ChatMessageEntity(); entity.setConversationId(conversationId); entity.setContent(msg.getContent()); entity.setRole(msg.getMessageType().getValue()); entity.setTimestamp(LocalDateTime.now()); repository.save(entity); }); } @Override public List get(String conversationId, int lastN) { // 从数据库查询 return repository.findTopNByConversationIdOrderByTimestampDesc( conversationId, lastN) .stream() .map(this::toMessage) .collect(Collectors.toList()); } } ``` ### 4. 自定义 Advisor 开发 #### 4.1 日志记录 Advisor **文件位置:** `src/main/java/com/example/chatai/Config/CustomLoggingAdvisor.java` ```java @Component public class CustomLoggingAdvisor implements CallAdvisor, StreamAdvisor, Ordered { private static final Logger logger = LoggerFactory.getLogger(CustomLoggingAdvisor.class); @Override public ChatClientResponse adviseCall(ChatClientRequest request, CallAdvisorChain chain) { // 请求前处理 logger.info("请求开始: {}", request.userText()); long startTime = System.currentTimeMillis(); // 调用下一个节点 ChatClientResponse response = chain.nextCall(request); // 响应后处理 long duration = System.currentTimeMillis() - startTime; logger.info("请求完成: 耗时{}ms, 响应长度: {}", duration, response.getResult().getOutput().getContent().length()); return response; } @Override public Flux adviseStream(ChatClientRequest request, StreamAdvisorChain chain) { logger.info("流式请求开始: {}", request.userText()); return chain.nextStream(request) .doOnNext(chunk -> logger.debug("接收到分片: {}", chunk.getResult().getOutput().getContent())) .doOnComplete(() -> logger.info("流式响应完成")) .doOnError(error -> logger.error("流式响应错误", error)); } @Override public int getOrder() { return 100; // 执行顺序,数字越小优先级越高 } } ``` #### 4.2 权限验证 Advisor ```java @Component public class AuthorizationAdvisor implements CallAdvisor { @Override public ChatClientResponse adviseCall(ChatClientRequest request, CallAdvisorChain chain) { // 从请求中获取用户信息 String userId = request.advisorParams().get("userId"); // 验证用户权限 if (!hasPermission(userId)) { throw new UnauthorizedException("用户无权限访问"); } // 检查敏感词 if (containsSensitiveWords(request.userText())) { throw new IllegalArgumentException("包含敏感词汇"); } return chain.nextCall(request); } private boolean hasPermission(String userId) { // 实现权限检查逻辑 return true; } private boolean containsSensitiveWords(String text) { // 实现敏感词检测逻辑 return false; } } ``` #### 4.3 速率限制 Advisor ```java @Component public class RateLimitAdvisor implements CallAdvisor { private final Map limiters = new ConcurrentHashMap<>(); @Override public ChatClientResponse adviseCall(ChatClientRequest request, CallAdvisorChain chain) { String userId = request.advisorParams().get("userId"); // 获取或创建限流器(每分钟10次请求) RateLimiter limiter = limiters.computeIfAbsent(userId, k -> RateLimiter.create(10.0 / 60.0)); if (!limiter.tryAcquire()) { throw new TooManyRequestsException("请求过于频繁,请稍后再试"); } return chain.nextCall(request); } } ``` ### 5. 配置文件详解 **文件位置:** `src/main/resources/application.yaml` ```yaml spring: application: name: ChatAI ai: ollama: base-url: http://localhost:11434 # Ollama 服务地址 chat: model: deepseek-r1:7b # 默认模型 options: temperature: 0.2 # 创造性参数 # 日志配置 logging: level: com.example.chatai.Config.CustomLoggingAdvisor: INFO org.springframework.ai.chat.client.advisor: DEBUG org.springframework.ai: INFO pattern: console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n" ``` ### 6. 完整的使用示例 #### 6.1 基础聊天示例 ```java @RestController public class ExampleController { @Autowired private ChatClient chatClient; // 简单聊天 @GetMapping("/simple-chat") public String simpleChat(@RequestParam String message) { return chatClient.prompt() .user(message) .call() .content(); } // 带系统提示的聊天 @GetMapping("/system-chat") public String systemChat(@RequestParam String message) { return chatClient.prompt() .system("你是一个专业的代码审查员") .user(message) .call() .content(); } // 流式聊天 @GetMapping("/stream-chat") public Flux streamChat(@RequestParam String message) { return chatClient.prompt() .user(message) .stream() .content(); } } ``` #### 6.2 高级功能示例 ```java @RestController public class AdvancedController { @Autowired private ChatClient chatClient; // 多轮对话与记忆 @PostMapping("/conversation") public ResponseEntity conversation(@RequestBody ConversationRequest request) { String response = chatClient.prompt() .user(request.getMessage()) .advisors(advisor -> advisor .param(CONVERSATION_ID, request.getUserId()) .param("userId", request.getUserId())) .call() .content(); return ResponseEntity.ok(response); } // 带选项的聊天 @PostMapping("/chat-with-options") public String chatWithOptions(@RequestBody ChatRequest request) { return chatClient.prompt() .options(OllamaOptions.builder() .model(request.getModel()) .temperature(request.getTemperature()) .maxTokens(request.getMaxTokens()) .build()) .user(request.getMessage()) .call() .content(); } // 函数调用示例(如果模型支持) @PostMapping("/function-call") public String functionCall(@RequestParam String query) { return chatClient.prompt() .user(query) .functions("getCurrentWeather", "searchDatabase") .call() .content(); } } ``` ## 最佳实践 ### 1. 错误处理 ```java @RestController public class ChatController { @GetMapping("/safe-chat") public ResponseEntity safeChat(@RequestParam String message) { try { String response = chatClient.prompt() .user(message) .call() .content(); return ResponseEntity.ok(response); } catch (Exception e) { logger.error("聊天请求失败", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body("抱歉,服务暂时不可用"); } } } ``` ### 2. 性能优化 ```java @Configuration public class ChatClientConfig { @Bean @Primary public ChatClient optimizedChatClient(OllamaChatModel model) { return ChatClient.builder(model) .defaultAdvisors( // 缓存 Advisor new CacheAdvisor(), // 限流 Advisor new RateLimitAdvisor(), // 日志 Advisor(低优先级) new SimpleLoggerAdvisor() ) .build(); } } ``` ### 3. 监控指标 ```java @Component public class MetricsAdvisor implements CallAdvisor { private final MeterRegistry meterRegistry; private final Counter requestCounter; private final Timer responseTimer; public MetricsAdvisor(MeterRegistry meterRegistry) { this.meterRegistry = meterRegistry; this.requestCounter = Counter.builder("ai.requests.total").register(meterRegistry); this.responseTimer = Timer.builder("ai.response.duration").register(meterRegistry); } @Override public ChatClientResponse adviseCall(ChatClientRequest request, CallAdvisorChain chain) { return Timer.Sample.start(meterRegistry) .stop(responseTimer, () -> { requestCounter.increment(); return chain.nextCall(request); }); } } ``` ## 故障排除 ### 常见问题 1. **模型连接失败** - 检查 Ollama 服务是否启动:`ollama list` - 确认模型已下载:`ollama pull model-name` 2. **日志不显示** - 检查 `application.yaml` 中的日志级别配置 - 确认 Advisor 已正确注册 3. **记忆功能不生效** - 确认 `ChatMemory` Bean 正确配置 - 检查 `CONVERSATION_ID` 参数传递 ### 调试技巧 ```yaml # 开启详细日志 logging: level: org.springframework.ai: DEBUG org.springframework.web: DEBUG com.example.chatai: DEBUG ``` ## 扩展开发 ### 自定义模型适配 ```java @Configuration public class CustomModelConfig { @Bean @ConditionalOnProperty(name = "app.model.provider", havingValue = "custom") public ChatModel customChatModel() { // 实现自定义模型适配 return new CustomChatModel(); } } ``` ### 多租户支持 ```java @Component public class MultiTenantAdvisor implements CallAdvisor { @Override public ChatClientResponse adviseCall(ChatClientRequest request, CallAdvisorChain chain) { String tenantId = request.advisorParams().get("tenantId"); // 设置租户上下文 TenantContext.setCurrentTenant(tenantId); try { return chain.nextCall(request); } finally { TenantContext.clear(); } } } ``` ## 参考资源 - [Spring AI 官方文档](https://docs.spring.io/spring-ai/reference/) - [Ollama 官网](https://ollama.ai/) - [Spring Boot 文档](https://spring.io/projects/spring-boot) ## 联系方式 - 作者:胖虎爱java - 邮箱:shibaizhelianmeng@163.com - CSDN:[胖虎爱java](https://blog.csdn.net/)