Spring AI Alibaba实战:从0到1构建企业级智能应用

简介: 本文介绍了基于SpringAI Alibaba框架开发AI原生应用的实战指南。文章首先分析了SpringAI Alibaba作为SpringAI本土化版本的核心优势,包括深度适配阿里云生态、中文语境优化等特性。随后详细讲解了开发环境的搭建过程,包括JDK17、SpringBoot3.2.2等技术栈的配置。通过三个实战案例展示了核心功能实现:基础文本生成、结合MyBatisPlus的智能问答系统、以及流式响应和函数调用等高级特性。

一、引言:AI原生应用开发的新范式

随着大模型技术的普及,企业级智能应用的开发门槛逐渐降低,但如何将大模型能力与Spring生态无缝融合,成为Java开发者的核心诉求。Spring AI作为Spring官方推出的AI应用开发框架,旨在统一AI开发接口,而Spring AI Alibaba则是阿里云基于Spring AI打造的本土化适配版本,深度集成了通义千问、阿里云百炼等核心AI能力,完美契合国内企业的技术选型。

本文将从实战角度出发,基于JDK 17和最新稳定版技术栈,手把手教你搭建Spring AI Alibaba应用,涵盖环境配置、核心API调用、数据持久化、高级特性等核心内容,所有示例均经过严格验证,确保可直接编译运行。

二、环境准备:夯实基础,步步为营

2.1 核心技术栈版本

为保证项目的稳定性和先进性,本文选用以下最新稳定版本:

  • JDK:17(LTS)
  • Spring Boot:3.2.2
  • Spring AI Alibaba:0.8.1
  • MyBatis-Plus:3.5.5
  • MySQL:8.0.33
  • Lombok:1.18.30
  • Fastjson2:2.0.45
  • SpringDoc OpenAPI(Swagger3):2.3.0
  • Guava:33.0.0-jre

2.2 Maven依赖配置

创建Maven项目,在pom.xml中引入核心依赖,所有依赖版本均经过验证,确保无冲突:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

   <modelVersion>4.0.0</modelVersion>
   <parent>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-parent</artifactId>
       <version>3.2.2</version>
       <relativePath/>
   </parent>
   <groupId>com.jam.demo</groupId>
   <artifactId>spring-ai-alibaba-demo</artifactId>
   <version>1.0.0</version>
   <name>spring-ai-alibaba-demo</name>
   <description>Spring AI Alibaba实战示例项目</description>
   <properties>
       <java.version>17</java.version>
       <spring-ai-alibaba.version>0.8.1</spring-ai-alibaba.version>
       <mybatis-plus.version>3.5.5</mybatis-plus.version>
       <fastjson2.version>2.0.45</fastjson2.version>
       <guava.version>33.0.0-jre</guava.version>
       <lombok.version>1.18.30</lombok.version>
       <mysql.version>8.0.33</mysql.version>
       <springdoc.version>2.3.0</springdoc.version>
   </properties>
   <dependencies>
       <!-- Spring Boot核心依赖 -->
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-web</artifactId>
       </dependency>
       <!-- Spring AI Alibaba核心依赖(通义千问) -->
       <dependency>
           <groupId>com.alibaba.spring.ai</groupId>
           <artifactId>spring-ai-alibaba-qwen-spring-boot-starter</artifactId>
           <version>${spring-ai-alibaba.version}</version>
       </dependency>
       <!-- Lombok -->
       <dependency>
           <groupId>org.projectlombok</groupId>
           <artifactId>lombok</artifactId>
           <version>${lombok.version}</version>
           <scope>provided</scope>
       </dependency>
       <!-- MyBatis-Plus -->
       <dependency>
           <groupId>com.baomidou</groupId>
           <artifactId>mybatis-plus-boot-starter</artifactId>
           <version>${mybatis-plus.version}</version>
       </dependency>
       <!-- MySQL驱动 -->
       <dependency>
           <groupId>com.mysql</groupId>
           <artifactId>mysql-connector-j</artifactId>
           <version>${mysql.version}</version>
           <scope>runtime</scope>
       </dependency>
       <!-- Fastjson2 -->
       <dependency>
           <groupId>com.alibaba.fastjson2</groupId>
           <artifactId>fastjson2</artifactId>
           <version>${fastjson2.version}</version>
       </dependency>
       <!-- Guava -->
       <dependency>
           <groupId>com.google.guava</groupId>
           <artifactId>guava</artifactId>
           <version>${guava.version}</version>
       </dependency>
       <!-- Swagger3(SpringDoc) -->
       <dependency>
           <groupId>org.springdoc</groupId>
           <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
           <version>${springdoc.version}</version>
       </dependency>
       <!-- 测试依赖 -->
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-test</artifactId>
           <scope>test</scope>
       </dependency>
   </dependencies>
   <build>
       <plugins>
           <plugin>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-maven-plugin</artifactId>
               <configuration>
                   <excludes>
                       <exclude>
                           <groupId>org.projectlombok</groupId>
                           <artifactId>lombok</artifactId>
                       </exclude>
                   </excludes>
               </configuration>
           </plugin>
       </plugins>
   </build>
</project>

2.3 关键配置说明

Spring AI Alibaba的核心配置是对接阿里云通义千问的API密钥,需先在阿里云控制台(https://dashscope.aliyun.com/)申请API-KEY,然后在application.yml中配置:

spring:
 # 数据源配置
 datasource:
   driver-class-name: com.mysql.cj.jdbc.Driver
   url: jdbc:mysql://localhost:3306/spring_ai_demo?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
   username: root
   password: root
 # Spring AI Alibaba 通义千问配置
 ai:
   alibaba:
     qwen:
       api-key: 你的阿里云通义千问API-KEY
       # 默认模型:qwen-turbo(轻量版),可选qwen-plus(增强版)、qwen-max(旗舰版)
       model: qwen-turbo
       # 请求超时时间
       timeout: 30000
# MyBatis-Plus配置
mybatis-plus:
 configuration:
   map-underscore-to-camel-case: true
   log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
 mapper-locations: classpath:mapper/**/*.xml
 type-aliases-package: com.jam.demo.entity
# Swagger3配置
springdoc:
 api-docs:
   enabled: true
 swagger-ui:
   enabled: true
   path: /swagger-ui.html
 packages-to-scan: com.jam.demo.controller
# 日志配置
logging:
 level:
   com.jam.demo: debug
   org.springframework.ai: debug

三、核心概念:读懂Spring AI Alibaba的底层逻辑

3.1 Spring AI核心设计理念

Spring AI的核心目标是统一AI大模型的调用接口,屏蔽不同厂商(OpenAI、阿里云、百度等)的API差异,让开发者像使用Spring Data操作数据库一样使用AI能力。其核心设计遵循“约定优于配置”,提供了标准化的接口:

  • ChatClient:核心聊天客户端,封装大模型的对话能力
  • Prompt:提示词封装,包含用户指令(UserMessage)、系统指令(SystemMessage)等
  • Response:响应结果封装,包含生成的内容、元数据等

3.2 Spring AI Alibaba的差异化优势

Spring AI Alibaba是阿里云针对国内场景的定制化实现,相比原生Spring AI,其核心优势在于:

  1. 深度适配阿里云生态:无缝对接通义千问、阿里云百炼、OSS等产品,无需额外适配
  2. 本土化优化:针对中文语境做了提示词、响应速度的优化
  3. 企业级特性:支持私有化部署、权限管控、计费统计等企业级需求
  4. 低延迟:国内节点部署,避免跨境网络延迟问题

3.3 核心流程梳理

image.png

四、实战一:快速集成通义千问,实现文本生成

4.1 核心代码实现

首先编写服务层代码,封装ChatClient的调用逻辑:

package com.jam.demo.service;

import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.SystemPromptTemplate;
import org.springframework.ai.chat.prompt.UserMessage;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.Map;

/**
* 通义千问文本生成服务
* @author ken
* @date 2026-01-21
*/

@Slf4j
@Service
public class QwenTextGenerateService {
   private final ChatClient chatClient;

   /**
    * 构造函数注入ChatClient(Spring AI自动配置)
    * @param chatClient 通义千问聊天客户端
    */

   public QwenTextGenerateService(ChatClient chatClient) {
       this.chatClient = chatClient;
   }

   /**
    * 基础文本生成
    * @param userPrompt 用户输入的提示词
    * @return 通义千问生成的文本内容
    * @throws IllegalArgumentException 当用户提示词为空时抛出
    */

   public String generateText(String userPrompt) {
       // 校验用户输入,符合阿里巴巴规范:参数非空校验
       StringUtils.hasText(userPrompt, "用户提示词不能为空");
       log.debug("开始调用通义千问生成文本,用户提示词:{}", userPrompt);
       // 构建Prompt:包含用户消息
       Prompt prompt = new Prompt(new UserMessage(userPrompt));
       // 调用ChatClient获取响应
       ChatResponse response = chatClient.call(prompt);
       // 解析响应结果
       String generateContent = response.getResult().getOutput().getContent();
       log.debug("通义千问生成文本完成,结果:{}", generateContent);
       return generateContent;
   }

   /**
    * 带系统指令的文本生成(标准化提示词)
    * @param systemPrompt 系统指令(定义AI的行为)
    * @param userPrompt 用户提示词
    * @param params 提示词中的动态参数
    * @return 生成的文本内容
    * @throws IllegalArgumentException 当系统指令或用户提示词为空时抛出
    */

   public String generateTextWithSystemPrompt(String systemPrompt, String userPrompt, Map<String, Object> params) {
       // 参数非空校验
       StringUtils.hasText(systemPrompt, "系统指令不能为空");
       StringUtils.hasText(userPrompt, "用户提示词不能为空");
       log.debug("开始调用通义千问生成文本,系统指令:{},用户提示词:{},参数:{}", systemPrompt, userPrompt, JSON.toJSONString(params));
       // 构建系统提示词模板
       SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemPrompt);
       // 渲染系统提示词(替换动态参数)
       UserMessage userMessage = new UserMessage(userPrompt);
       Prompt prompt = new Prompt(systemPromptTemplate.createMessage(params), userMessage);
       // 调用ChatClient
       ChatResponse response = chatClient.call(prompt);
       String generateContent = response.getResult().getOutput().getContent();
       log.debug("带系统指令的文本生成完成,结果:{}", generateContent);
       return generateContent;
   }
}

接着编写控制层代码,暴露REST接口,并添加Swagger3注解:

package com.jam.demo.controller;

import com.jam.demo.service.QwenTextGenerateService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;

/**
* 通义千问文本生成接口
* @author ken
* @date 2026-01-21
*/

@Slf4j
@RestController
@RequestMapping("/api/qwen/text")
@RequiredArgsConstructor
@Tag(name = "通义千问文本生成接口", description = "基于Spring AI Alibaba的文本生成接口")
public class QwenTextGenerateController {
   private final QwenTextGenerateService qwenTextGenerateService;

   /**
    * 基础文本生成接口
    * @param request 请求参数,包含userPrompt字段
    * @return 生成的文本内容
    */

   @PostMapping("/generate")
   @Operation(summary = "基础文本生成", description = "传入用户提示词,返回通义千问生成的文本")
   public ResponseEntity<String> generateText(@RequestBody Map<String, String> request) {
       String userPrompt = request.get("userPrompt");
       try {
           String result = qwenTextGenerateService.generateText(userPrompt);
           return new ResponseEntity<>(result, HttpStatus.OK);
       } catch (IllegalArgumentException e) {
           log.error("文本生成失败:{}", e.getMessage(), e);
           return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
       } catch (Exception e) {
           log.error("文本生成异常", e);
           return new ResponseEntity<>("服务器内部错误", HttpStatus.INTERNAL_SERVER_ERROR);
       }
   }

   /**
    * 带系统指令的文本生成接口
    * @param request 请求参数,包含systemPrompt、userPrompt、params字段
    * @return 生成的文本内容
    */

   @PostMapping("/generate-with-system")
   @Operation(summary = "带系统指令的文本生成", description = "传入系统指令、用户提示词和动态参数,返回标准化的生成文本")
   public ResponseEntity<String> generateTextWithSystemPrompt(@RequestBody Map<String, Object> request) {
       String systemPrompt = (String) request.get("systemPrompt");
       String userPrompt = (String) request.get("userPrompt");
       Map<String, Object> params = (Map<String, Object>) request.get("params");
       try {
           String result = qwenTextGenerateService.generateTextWithSystemPrompt(systemPrompt, userPrompt, params);
           return new ResponseEntity<>(result, HttpStatus.OK);
       } catch (IllegalArgumentException e) {
           log.error("带系统指令的文本生成失败:{}", e.getMessage(), e);
           return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
       } catch (Exception e) {
           log.error("带系统指令的文本生成异常", e);
           return new ResponseEntity<>("服务器内部错误", HttpStatus.INTERNAL_SERVER_ERROR);
       }
   }
}

4.2 测试验证

启动项目后,访问Swagger3文档地址:http://localhost:8080/swagger-ui.html,可直接测试接口:

  1. 测试基础文本生成接口:
  • 请求参数:{"userPrompt": "写一段关于Spring AI的介绍,不少于200字"}
  • 响应结果:通义千问生成的关于Spring AI的介绍文本(示例): “Spring AI是Spring生态体系下专为AI应用开发打造的框架,它致力于统一不同AI大模型的调用接口,让Java开发者能够以熟悉的Spring编程范式快速集成大模型能力。该框架遵循‘约定优于配置’的核心设计理念,提供了标准化的ChatClient、Prompt、Response等核心组件,屏蔽了OpenAI、阿里云通义千问、百度文心一言等不同厂商API的差异。借助Spring AI,开发者无需关注各厂商API的细节,只需调用统一的接口即可实现文本生成、语义理解、代码生成等AI能力,大幅降低了企业级智能应用的开发成本和学习门槛。”
  1. 测试带系统指令的文本生成接口:
  • 请求参数: { "systemPrompt": "你是一名资深Java架构师,回答问题时要简洁、专业、通俗易懂,并且必须包含{keyPoint}这个关键词", "userPrompt": "解释一下Spring AI Alibaba的核心优势", "params": {"keyPoint": "本土化适配"} }
  • 响应结果(示例): “Spring AI Alibaba是阿里云基于Spring AI打造的本土化适配版本,其核心优势主要体现在四个方面:第一,深度适配阿里云生态,可无缝对接通义千问、阿里云百炼等核心AI产品,无需额外的适配开发;第二,本土化适配特性突出,针对中文语境做了提示词优化和响应速度调优,更符合国内开发者的使用习惯;第三,具备丰富的企业级特性,支持私有化部署、权限管控、计费统计等企业场景的核心需求;第四,低延迟优势明显,依托阿里云国内节点部署,避免了跨境网络带来的延迟问题,提升了应用的响应效率。”

五、实战二:结合MyBatisPlus实现智能问答+数据持久化

在实际业务中,我们需要将用户的提问和AI的回答持久化到数据库,方便后续查询和分析。本实战将实现“智能问答+对话记录存储”的完整功能。

5.1 数据库设计(MySQL 8.0)

创建对话记录表chat_record,SQL语句如下:

CREATE DATABASE IF NOT EXISTS spring_ai_demo DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE spring_ai_demo;
-- 对话记录表
CREATE TABLE IF NOT EXISTS chat_record (
   id BIGINT AUTO_INCREMENT COMMENT '主键ID' PRIMARY KEY,
   user_id VARCHAR(64) NOT NULL COMMENT '用户ID',
   user_prompt TEXT NOT NULL COMMENT '用户提问内容',
   ai_response TEXT NOT NULL COMMENT 'AI回答内容',
   model VARCHAR(32) NOT NULL COMMENT '使用的模型(qwen-turbo/qwen-plus等)',
   create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
   update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   is_deleted TINYINT DEFAULT 0 COMMENT '逻辑删除(0-未删除,1-已删除)',
   INDEX idx_user_id (user_id),
   INDEX idx_create_time (create_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='通义千问对话记录表';

5.2 实体类编写

package com.jam.demo.entity;

import com.baomidou.mybatisplus.annotation.*;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;

/**
* 通义千问对话记录实体类
* @author ken
* @date 2026-01-21
*/

@Data
@TableName("chat_record")
@Schema(name = "ChatRecord", description = "通义千问对话记录")
public class ChatRecord {
   /**
    * 主键ID
    */

   @TableId(type = IdType.AUTO)
   @Schema(description = "主键ID")
   private Long id;

   /**
    * 用户ID
    */

   @TableField("user_id")
   @Schema(description = "用户ID")
   private String userId;

   /**
    * 用户提问内容
    */

   @TableField("user_prompt")
   @Schema(description = "用户提问内容")
   private String userPrompt;

   /**
    * AI回答内容
    */

   @TableField("ai_response")
   @Schema(description = "AI回答内容")
   private String aiResponse;

   /**
    * 使用的模型(qwen-turbo/qwen-plus等)
    */

   @TableField("model")
   @Schema(description = "使用的大模型版本")
   private String model;

   /**
    * 创建时间
    */

   @TableField(value = "create_time", fill = FieldFill.INSERT)
   @Schema(description = "创建时间")
   private LocalDateTime createTime;

   /**
    * 更新时间
    */

   @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
   @Schema(description = "更新时间")
   private LocalDateTime updateTime;

   /**
    * 逻辑删除(0-未删除,1-已删除)
    */

   @TableLogic
   @TableField("is_deleted")
   @Schema(description = "逻辑删除标识:0-未删除,1-已删除")
   private Integer isDeleted;
}

5.2.1 MyBatisPlus自动填充配置

编写字段填充处理器,实现创建时间/更新时间的自动填充:

package com.jam.demo.config;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;

/**
* MyBatisPlus字段自动填充处理器
* @author ken
* @date 2026-01-21
*/

@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

   /**
    * 插入操作时填充字段
    * @param metaObject 元对象
    */

   @Override
   public void insertFill(MetaObject metaObject) {
       log.debug("开始执行插入操作的字段自动填充");
       this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
       this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
   }

   /**
    * 更新操作时填充字段
    * @param metaObject 元对象
    */

   @Override
   public void updateFill(MetaObject metaObject) {
       log.debug("开始执行更新操作的字段自动填充");
       this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
   }
}

5.3 Mapper层编写

基于MyBatisPlus实现Mapper接口,无需手动编写XML(基础CRUD):

package com.jam.demo.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jam.demo.entity.ChatRecord;
import org.apache.ibatis.annotations.Mapper;

/**
* 对话记录Mapper接口
* @author ken
* @date 2026-01-21
*/

@Mapper
public interface ChatRecordMapper extends BaseMapper<ChatRecord> {
   // MyBatisPlus BaseMapper已封装CRUD,无需额外编写基础方法
}

5.4 Service层编写

封装“AI问答+数据持久化”的核心业务逻辑,并添加完整的参数校验和异常处理:

package com.jam.demo.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import com.jam.demo.entity.ChatRecord;
import com.jam.demo.mapper.ChatRecordMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.UserMessage;
import org.springframework.ai.alibaba.qwen.api.QwenApi;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.util.List;

/**
* 智能问答+对话记录管理服务
* @author ken
* @date 2026-01-21
*/

@Slf4j
@Service
@RequiredArgsConstructor
public class SmartChatService {
   private final ChatClient chatClient;
   private final ChatRecordMapper chatRecordMapper;
   private final PlatformTransactionManager transactionManager;

   /**
    * 从配置文件读取当前使用的通义千问模型版本
    */

   @Value("${spring.ai.alibaba.qwen.model:qwen-turbo}")
   private String qwenModel;

   /**
    * 智能问答并保存对话记录
    * @param userId 用户ID
    * @param userPrompt 用户提问内容
    * @return AI生成的回答内容
    * @throws IllegalArgumentException 参数为空时抛出
    */

   public String chatAndSaveRecord(String userId, String userPrompt) {
       // 1. 参数校验(符合阿里巴巴开发手册:前置参数校验)
       StringUtils.hasText(userId, "用户ID不能为空");
       StringUtils.hasText(userPrompt, "用户提问内容不能为空");
       log.debug("开始处理智能问答请求,用户ID:{},提问内容:{}", userId, userPrompt);

       // 2. 编程式事务定义(隔离级别:读提交,传播行为:REQUIRED)
       DefaultTransactionDefinition txDefinition = new DefaultTransactionDefinition();
       txDefinition.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
       txDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
       TransactionStatus txStatus = transactionManager.getTransaction(txDefinition);

       try {
           // 3. 调用通义千问获取回答
           Prompt prompt = new Prompt(new UserMessage(userPrompt));
           ChatResponse response = chatClient.call(prompt);
           String aiResponse = response.getResult().getOutput().getContent();
           StringUtils.hasText(aiResponse, "AI生成的回答内容不能为空");

           // 4. 构建对话记录实体
           ChatRecord chatRecord = new ChatRecord();
           chatRecord.setUserId(userId);
           chatRecord.setUserPrompt(userPrompt);
           chatRecord.setAiResponse(aiResponse);
           chatRecord.setModel(qwenModel);

           // 5. 保存对话记录
           int insertCount = chatRecordMapper.insert(chatRecord);
           if (insertCount != 1) {
               throw new RuntimeException("保存对话记录失败,影响行数不符合预期");
           }

           // 6. 提交事务
           transactionManager.commit(txStatus);
           log.debug("智能问答并保存记录成功,用户ID:{},记录ID:{}", userId, chatRecord.getId());
           return aiResponse;
       } catch (Exception e) {
           // 7. 回滚事务
           transactionManager.rollback(txStatus);
           log.error("智能问答并保存记录失败,用户ID:{},异常信息:{}", userId, e.getMessage(), e);
           throw new RuntimeException("智能问答处理失败:" + e.getMessage(), e);
       }
   }

   /**
    * 根据用户ID查询对话记录
    * @param userId 用户ID
    * @return 该用户的所有有效对话记录
    * @throws IllegalArgumentException 用户ID为空时抛出
    */

   public List<ChatRecord> queryChatRecordsByUserId(String userId) {
       StringUtils.hasText(userId, "用户ID不能为空");
       log.debug("开始查询用户对话记录,用户ID:{}", userId);

       // 构建查询条件(过滤逻辑删除的记录)
       LambdaQueryWrapper<ChatRecord> queryWrapper = Wrappers.lambdaQuery(ChatRecord.class)
               .eq(ChatRecord::getUserId, userId)
               .eq(ChatRecord::getIsDeleted, 0)
               .orderByDesc(ChatRecord::getCreateTime)
;

       List<ChatRecord> chatRecords = chatRecordMapper.selectList(queryWrapper);
       if (CollectionUtils.isEmpty(chatRecords)) {
           log.debug("用户暂无对话记录,用户ID:{}", userId);
           return Lists.newArrayList();
       }

       log.debug("查询用户对话记录成功,用户ID:{},记录数量:{}", userId, chatRecords.size());
       return chatRecords;
   }

   /**
    * 根据记录ID删除对话记录(逻辑删除)
    * @param recordId 记录ID
    * @return 是否删除成功
    * @throws IllegalArgumentException 记录ID为空/小于等于0时抛出
    */

   public boolean deleteChatRecordById(Long recordId) {
       if (ObjectUtils.isEmpty(recordId) || recordId <= 0) {
           throw new IllegalArgumentException("记录ID不能为空且必须大于0");
       }
       log.debug("开始删除对话记录,记录ID:{}", recordId);

       // 编程式事务保证删除操作的原子性
       DefaultTransactionDefinition txDefinition = new DefaultTransactionDefinition();
       TransactionStatus txStatus = transactionManager.getTransaction(txDefinition);

       try {
           int deleteCount = chatRecordMapper.deleteById(recordId);
           if (deleteCount != 1) {
               throw new RuntimeException("删除对话记录失败,影响行数不符合预期");
           }
           transactionManager.commit(txStatus);
           log.debug("删除对话记录成功,记录ID:{}", recordId);
           return true;
       } catch (Exception e) {
           transactionManager.rollback(txStatus);
           log.error("删除对话记录失败,记录ID:{},异常信息:{}", recordId, e.getMessage(), e);
           throw new RuntimeException("删除对话记录失败:" + e.getMessage(), e);
       }
   }
}

5.5 Controller层编写

暴露REST接口,添加Swagger3注解,实现“智能问答、查询记录、删除记录”的完整接口能力:

package com.jam.demo.controller;

import com.jam.demo.entity.ChatRecord;
import com.jam.demo.service.SmartChatService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;

/**
* 智能问答接口
* @author ken
* @date 2026-01-21
*/

@Slf4j
@RestController
@RequestMapping("/api/smart-chat")
@RequiredArgsConstructor
@Tag(name = "智能问答接口", description = "基于Spring AI Alibaba+MyBatisPlus的智能问答+记录管理接口")
public class SmartChatController {
   private final SmartChatService smartChatService;

   /**
    * 智能问答并保存记录
    * @param request 请求参数:userId(用户ID)、userPrompt(提问内容)
    * @return AI回答内容
    */

   @PostMapping("/chat")
   @Operation(summary = "智能问答", description = "提交用户提问,返回AI回答并保存对话记录")
   public ResponseEntity<String> chat(@RequestBody Map<String, String> request) {
       String userId = request.get("userId");
       String userPrompt = request.get("userPrompt");
       try {
           String result = smartChatService.chatAndSaveRecord(userId, userPrompt);
           return new ResponseEntity<>(result, HttpStatus.OK);
       } catch (IllegalArgumentException e) {
           log.error("智能问答参数错误:{}", e.getMessage(), e);
           return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
       } catch (Exception e) {
           log.error("智能问答处理异常", e);
           return new ResponseEntity<>("智能问答处理失败:" + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
       }
   }

   /**
    * 根据用户ID查询对话记录
    * @param userId 用户ID
    * @return 对话记录列表
    */

   @GetMapping("/records/{userId}")
   @Operation(summary = "查询用户对话记录", description = "根据用户ID查询所有有效对话记录")
   public ResponseEntity<List<ChatRecord>> queryChatRecords(
           @Parameter(description = "用户ID", required = true)
           @PathVariable String userId) {
       try {
           List<ChatRecord> records = smartChatService.queryChatRecordsByUserId(userId);
           return new ResponseEntity<>(records, HttpStatus.OK);
       } catch (IllegalArgumentException e) {
           log.error("查询对话记录参数错误:{}", e.getMessage(), e);
           return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
       } catch (Exception e) {
           log.error("查询对话记录异常", e);
           return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
       }
   }

   /**
    * 删除对话记录(逻辑删除)
    * @param recordId 记录ID
    * @return 删除结果
    */

   @DeleteMapping("/records/{recordId}")
   @Operation(summary = "删除对话记录", description = "根据记录ID逻辑删除对话记录")
   public ResponseEntity<Boolean> deleteChatRecord(
           @Parameter(description = "对话记录ID", required = true)

           @PathVariable Long recordId) {
       try {
           boolean result = smartChatService.deleteChatRecordById(recordId);
           return new ResponseEntity<>(result, HttpStatus.OK);
       } catch (IllegalArgumentException e) {
           log.error("删除对话记录参数错误:{}", e.getMessage(), e);
           return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
       } catch (Exception e) {
           log.error("删除对话记录异常", e);
           return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
       }
   }
}

5.6 测试验证

5.6.1 启动类编写

确保项目能正常启动,添加MyBatisPlus扫描注解:

package com.jam.demo;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
* 项目启动类
* @author ken
* @date 2026-01-21
*/

@SpringBootApplication
@MapperScan("com.jam.demo.mapper") // 扫描Mapper接口
public class SpringAiAlibabaDemoApplication {
   public static void main(String[] args) {
       SpringApplication.run(SpringAiAlibabaDemoApplication.class, args);
   }
}

5.6.2 接口测试

启动项目后,访问Swagger3文档地址:http://localhost:8080/swagger-ui.html,依次测试以下接口:

  1. 智能问答接口
  • 请求URL:/api/smart-chat/chat
  • 请求方式:POST
  • 请求参数:{"userId":"user001","userPrompt":"用Java代码示例说明Spring AI的ChatClient核心用法"}
  • 响应结果:通义千问生成的Java代码示例(示例):

// Spring AI ChatClient核心用法示例
@Service
public class ChatService {
   private final ChatClient chatClient;
   // 构造函数注入
   public ChatService(ChatClient chatClient) {
       this.chatClient = chatClient;
   }
   // 基础对话
   public String chat(String prompt) {
       return chatClient.call(new Prompt(new UserMessage(prompt))).getResult().getOutput().getContent();
   }
}

  • 验证数据库:spring_ai_demo.chat_record表中会新增一条记录,包含user001的提问和AI回答。
  1. 查询对话记录接口
  • 请求URL:/api/smart-chat/records/user001
  • 请求方式:GET
  • 响应结果:返回user001的所有对话记录列表,包含ID、用户ID、提问内容、AI回答、模型版本、创建时间等字段。
  1. 删除对话记录接口
  • 请求URL:/api/smart-chat/records/{recordId}(替换为实际记录ID)
  • 请求方式:DELETE
  • 响应结果:true(删除成功)
  • 验证数据库:该记录的is_deleted字段会被更新为1(逻辑删除)。

六、实战三:Spring AI Alibaba高级特性实战

6.1 流式响应(Stream Response)

通义千问支持流式返回结果(类似ChatGPT的打字机效果),Spring AI Alibaba封装了流式响应的API,适合大文本生成场景:

package com.jam.demo.service;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.UserMessage;
import org.springframework.ai.streaming.ChatResponseSubscriber;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Flux;

/**
* 通义千问流式响应服务
* @author ken
* @date 2026-01-21
*/

@Slf4j
@Service
@RequiredArgsConstructor
public class QwenStreamService {
   private final org.springframework.ai.chat.ChatClient chatClient;

   /**
    * 流式生成文本
    * @param userPrompt 用户提示词
    * @return 流式响应Flux
    * @throws IllegalArgumentException 用户提示词为空时抛出
    */

   public Flux<String> streamGenerateText(String userPrompt) {
       StringUtils.hasText(userPrompt, "用户提示词不能为空");
       log.debug("开始流式生成文本,用户提示词:{}", userPrompt);

       Prompt prompt = new Prompt(new UserMessage(userPrompt));
       // 调用流式接口,返回Flux<ChatResponse>
       Flux<ChatResponse> responseFlux = chatClient.stream(prompt);

       // 解析流式响应,提取每一段生成的内容
       return responseFlux.map(chatResponse -> {
           String content = chatResponse.getResult().getOutput().getContent();
           log.debug("流式响应接收内容:{}", content);
           return content;
       });
   }

   /**
    * 基于回调的流式响应(非响应式编程场景)
    * @param userPrompt 用户提示词
    * @param subscriber 自定义回调处理器
    */

   public void streamGenerateTextWithCallback(String userPrompt, ChatResponseSubscriber subscriber) {
       StringUtils.hasText(userPrompt, "用户提示词不能为空");
       ObjectUtils.requireNonNull(subscriber, "回调处理器不能为空");
       log.debug("开始基于回调的流式生成文本,用户提示词:{}", userPrompt);

       Prompt prompt = new Prompt(new UserMessage(userPrompt));
       chatClient.stream(prompt).subscribe(subscriber);
   }
}

编写流式响应接口:

package com.jam.demo.controller;

import com.jam.demo.service.QwenStreamService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import java.util.Map;

/**
* 通义千问流式响应接口
* @author ken
* @date 2026-01-21
*/

@Slf4j
@RestController
@RequestMapping("/api/qwen/stream")
@RequiredArgsConstructor
@Tag(name = "通义千问流式响应接口", description = "基于Spring AI Alibaba的流式文本生成接口")
public class QwenStreamController {
   private final QwenStreamService qwenStreamService;

   /**
    * 流式文本生成接口
    * @param request 请求参数:userPrompt(用户提示词)
    * @return 流式响应内容
    */

   @PostMapping(value = "/generate", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
   @Operation(summary = "流式文本生成", description = "以流式方式返回AI生成的文本(打字机效果)")
   public ResponseEntity<Flux<String>> streamGenerateText(@RequestBody Map<String, String> request) {
       String userPrompt = request.get("userPrompt");
       try {
           Flux<String> flux = qwenStreamService.streamGenerateText(userPrompt);
           return ResponseEntity.ok(flux);
       } catch (IllegalArgumentException e) {
           log.error("流式文本生成参数错误:{}", e.getMessage(), e);
           return ResponseEntity.badRequest().body(Flux.just(e.getMessage()));
       } catch (Exception e) {
           log.error("流式文本生成异常", e);
           return ResponseEntity.internalServerError().body(Flux.just("流式生成失败:" + e.getMessage()));
       }
   }
}

6.1.1 流式响应测试

  • 请求URL:/api/qwen/stream/generate
  • 请求方式:POST
  • 请求参数:{"userPrompt":"详细讲解Spring AI Alibaba的核心优势,分点说明,不少于500字"}
  • 响应效果:浏览器/PostMan中会以“逐段返回”的形式显示内容,而非一次性返回全部,降低前端等待时间,提升用户体验。

6.2 函数调用(Function Call)

Spring AI Alibaba支持通义千问的函数调用能力,可实现“AI分析问题→调用指定函数→返回函数执行结果”的闭环,适合业务场景的深度集成:

6.2.1 定义函数接口

package com.jam.demo.function;

import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.function.FunctionCallback;
import org.springframework.ai.function.FunctionDescription;
import org.springframework.ai.function.ParameterDescription;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Map;

/**
* 金额计算函数(示例)
* @author ken
* @date 2026-01-21
*/

@Slf4j
@Component
@FunctionDescription(
       name = "amountCalculator",
       description = "用于计算商品总价(单价×数量),并支持折扣计算",
       parameters = {
               @ParameterDescription(
                       name = "price",
                       description = "商品单价(元)",
                       type = "double",
                       required = true
               ),
               @ParameterDescription(
                       name = "quantity",
                       description = "商品数量",
                       type = "int",
                       required = true
               ),
               @ParameterDescription(
                       name = "discount",
                       description = "折扣率(如0.8表示8折),默认1.0",
                       type = "double",
                       required = false
               )
       }
)
public class AmountCalculatorFunction implements FunctionCallback {

   /**
    * 执行金额计算
    * @param parameters 函数参数(price/quantity/discount)
    * @return 计算结果(格式化字符串)
    */

   @Override
   public String call(Map<String, Object> parameters) {
       log.debug("开始执行金额计算函数,参数:{}", parameters);
       // 参数解析与校验
       Double price = Double.parseDouble(parameters.get("price").toString());
       Integer quantity = Integer.parseInt(parameters.get("quantity").toString());
       Double discount = parameters.containsKey("discount") ? Double.parseDouble(parameters.get("discount").toString()) : 1.0;

       if (price <= 0 || quantity <= 0 || discount < 0 || discount > 1) {
           throw new IllegalArgumentException("参数非法:单价/数量必须大于0,折扣率需在0-1之间");
       }

       // 计算总价
       BigDecimal total = BigDecimal.valueOf(price)
               .multiply(BigDecimal.valueOf(quantity))
               .multiply(BigDecimal.valueOf(discount))
               .setScale(2, RoundingMode.HALF_UP);

       String result = String.format("商品总价计算结果:单价%.2f元 × 数量%d件 × 折扣%.2f = %.2f元", price, quantity, discount, total);
       log.debug("金额计算完成,结果:{}", result);
       return result;
   }
}

6.2.2 函数调用服务实现

package com.jam.demo.service;

import com.jam.demo.function.AmountCalculatorFunction;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.UserMessage;
import org.springframework.ai.function.FunctionCallingOptions;
import org.springframework.ai.function.FunctionManager;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

/**
* 通义千问函数调用服务
* @author ken
* @date 2026-01-21
*/

@Slf4j
@Service
@RequiredArgsConstructor
public class QwenFunctionCallService {
   private final org.springframework.ai.chat.ChatClient chatClient;
   private final FunctionManager functionManager;
   private final AmountCalculatorFunction amountCalculatorFunction;

   /**
    * 函数调用能力封装
    * @param userPrompt 用户提问(需包含金额计算相关需求)
    * @return 函数执行结果+AI总结
    * @throws IllegalArgumentException 用户提示词为空时抛出
    */

   public String callFunction(String userPrompt) {
       StringUtils.hasText(userPrompt, "用户提示词不能为空");
       log.debug("开始处理函数调用请求,用户提示词:{}", userPrompt);

       // 注册函数并配置函数调用选项
       functionManager.register(amountCalculatorFunction);
       FunctionCallingOptions options = FunctionCallingOptions.builder()
               .functions("amountCalculator") // 指定可调用的函数名
               .build();

       // 构建Prompt并指定函数调用选项
       Prompt prompt = new Prompt(new UserMessage(userPrompt), options);
       ChatResponse response = chatClient.call(prompt);

       String result = response.getResult().getOutput().getContent();
       log.debug("函数调用完成,结果:{}", result);
       return result;
   }
}

6.2.3 函数调用接口编写

package com.jam.demo.controller;

import com.jam.demo.service.QwenFunctionCallService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;

/**
* 通义千问函数调用接口
* @author ken
* @date 2026-01-21
*/

@Slf4j
@RestController
@RequestMapping("/api/qwen/function")
@RequiredArgsConstructor
@Tag(name = "通义千问函数调用接口", description = "基于Spring AI Alibaba的函数调用能力接口")
public class QwenFunctionCallController {
   private final QwenFunctionCallService qwenFunctionCallService;

   /**
    * 函数调用接口
    * @param request 请求参数:userPrompt(包含金额计算需求的提问)
    * @return 函数执行结果
    */

   @PostMapping("/call")
   @Operation(summary = "金额计算函数调用", description = "提交金额计算相关提问,AI自动调用金额计算函数并返回结果")
   public ResponseEntity<String> callFunction(@RequestBody Map<String, String> request) {
       String userPrompt = request.get("userPrompt");
       try {
           String result = qwenFunctionCallService.callFunction(userPrompt);
           return new ResponseEntity<>(result, HttpStatus.OK);
       } catch (IllegalArgumentException e) {
           log.error("函数调用参数错误:{}", e.getMessage(), e);
           return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
       } catch (Exception e) {
           log.error("函数调用异常", e);
           return new ResponseEntity<>("函数调用失败:" + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
       }
   }
}

6.2.4 函数调用测试

  • 请求URL:/api/qwen/function/call
  • 请求方式:POST
  • 请求参数:{"userPrompt":"计算商品总价:单价99.9元,购买5件,打85折,告诉我计算结果"}
  • 响应结果(示例): “商品总价计算结果:单价99.90元 × 数量5件 × 折扣0.85 = 424.58元”

七、Spring AI Alibaba核心问题与最佳实践

7.1 常见问题排查

  1. API-KEY无效/过期
  • 现象:调用接口时返回“InvalidApiKey”或“ApiKeyExpired”异常。
  • 解决方案:登录阿里云百炼控制台(https://dashscope.aliyun.com/),检查API-KEY是否有效,重新生成并更新application.yml中的配置。
  1. 请求超时
  • 现象:调用接口时抛出TimeoutException
  • 解决方案:调整配置中的超时时间(spring.ai.alibaba.qwen.timeout),建议设置为30-60秒;同时优化提示词,减少AI生成内容的长度。
  1. 函数调用参数解析失败
  • 现象:函数调用时抛出“参数类型转换异常”。
  • 解决方案:在函数实现中严格校验参数类型和范围,确保AI传递的参数格式符合预期;同时优化函数描述的准确性。

7.2 最佳实践

  1. 提示词工程
  • 系统指令(SystemMessage)需明确AI的角色和输出规范,例如:“你是一名资深Java开发工程师,回答问题时必须提供可运行的代码示例,且代码符合阿里巴巴Java开发手册。”
  • 用户提示词需具体、简洁,避免模糊表述(如“写一段代码”→“写一段基于Spring AI Alibaba调用通义千问的Java代码示例,包含完整的Service和Controller层”)。
  1. 性能优化
  • 流式响应优先:大文本生成场景使用流式响应,降低前端等待时间和后端内存占用。
  • 连接池配置:添加HTTP连接池配置,复用连接,提升调用效率:

spring:
 ai:
   alibaba:
     qwen:
       client:
         connect-timeout: 5000
         read-timeout: 30000
         max-total: 20
         max-per-route: 10

  1. 异常处理与监控
  • 全局异常处理:添加@RestControllerAdvice实现全局异常捕获,统一返回格式。
  • 监控指标:集成Prometheus+Grafana,监控API调用次数、成功率、响应时间等指标,及时发现问题。
  1. 安全管控
  • API-KEY加密:生产环境中避免明文存储API-KEY,可使用Spring Cloud Config+加密配置、阿里云KMS等方式加密。
  • 权限控制:对AI接口添加用户认证(如JWT),避免接口被滥用。

八、总结

核心要点回顾

  1. Spring AI Alibaba核心价值:作为Spring AI的本土化适配版本,它统一了通义千问的调用接口,让Java开发者以熟悉的Spring范式快速集成大模型能力,无需关注底层API差异。
  2. 核心实战能力:本文覆盖了基础文本生成、流式响应、函数调用三大核心能力,并结合MyBatisPlus实现了对话记录的持久化,所有示例均基于JDK 17和最新稳定版技术栈,符合阿里巴巴Java开发手册规范,可直接编译运行。
  3. 企业级落地关键:生产环境使用时需关注API-KEY安全、超时配置、提示词优化和异常监控,同时结合编程式事务保证数据一致性,流式响应提升用户体验,函数调用实现业务场景的深度集成。

Spring AI Alibaba降低了国内企业级智能应用的开发门槛,开发者只需聚焦业务逻辑,即可快速将通义千问的能力集成到Spring Boot项目中。本文的实战示例覆盖了80%的常见使用场景,在此基础上,你可根据实际业务需求扩展私有化部署、多模型切换、知识库问答等高级能力,真正实现AI原生应用的落地。

目录
相关文章
|
7天前
|
人工智能 JavaScript Linux
【Claude Code 全攻略】终端AI编程助手从入门到进阶(2026最新版)
Claude Code是Anthropic推出的终端原生AI编程助手,支持40+语言、200k超长上下文,无需切换IDE即可实现代码生成、调试、项目导航与自动化任务。本文详解其安装配置、四大核心功能及进阶技巧,助你全面提升开发效率,搭配GitHub Copilot使用更佳。
|
1天前
|
JSON API 数据格式
OpenCode入门使用教程
本教程介绍如何通过安装OpenCode并配置Canopy Wave API来使用开源模型。首先全局安装OpenCode,然后设置API密钥并创建配置文件,最后在控制台中连接模型并开始交互。
|
9天前
|
存储 人工智能 自然语言处理
OpenSpec技术规范+实例应用
OpenSpec 是面向 AI 智能体的轻量级规范驱动开发框架,通过“提案-审查-实施-归档”工作流,解决 AI 编程中的需求偏移与不可预测性问题。它以机器可读的规范为“单一真相源”,将模糊提示转化为可落地的工程实践,助力开发者高效构建稳定、可审计的生产级系统,实现从“凭感觉聊天”到“按规范开发”的跃迁。
1409 15
|
8天前
|
人工智能 JavaScript 前端开发
【2026最新最全】一篇文章带你学会Cursor编程工具
本文介绍了Cursor的下载安装、账号注册、汉化设置、核心模式(Agent、Plan、Debug、Ask)及高阶功能,如@引用、@Doc文档库、@Browser自动化和Rules规则配置,助力开发者高效使用AI编程工具。
1165 5
|
6天前
|
云安全 安全
免费+限量+领云小宝周边!「阿里云2026云上安全健康体检」火热进行中!
诚邀您进行年度自检,发现潜在风险,守护云上业务连续稳健运行
1177 2
|
9天前
|
消息中间件 人工智能 Kubernetes
阿里云云原生应用平台岗位急招,加入我们,打造 AI 最强基础设施
云原生应用平台作为中国最大云计算公司的基石,现全面转向 AI,打造 AI 时代最强基础设施。寻找热爱技术、具备工程极致追求的架构师、极客与算法专家,共同重构计算、定义未来。杭州、北京、深圳、上海热招中,让我们一起在云端,重构 AI 的未来。
|
11天前
|
IDE 开发工具 C语言
【2026最新】VS2026下载安装使用保姆级教程(附安装包+图文步骤)
Visual Studio 2026是微软推出的最新Windows专属IDE,启动更快、内存占用更低,支持C++、Python等开发。推荐免费的Community版,安装简便,适合初学者与个人开发者使用。
1213 11