跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
JavaAIjava

Spring AI Alibaba 快速入门指南

Spring AI Alibaba 快速入门指南涵盖环境配置、基础对话、提示词工程、Advisor 拦截、对话记忆、结构化输出、工具调用、MCP 协议、RAG 检索增强生成、AI Agent 及可观测性等内容。通过 Maven 依赖引入核心组件,配置 DashScope API Key 或本地 Ollama 模型。演示了 ChatClient 与 ChatModel 的流式与非流式调用方式,支持多模型动态切换。深入讲解了基于 MySQL 的对话记忆存储、自定义文档分割器与向量数据库结合的 RAG 实现流程。此外还包含 Tool Calling 动态注册、MCP 服务构建以及 Graph 状态图编排工作流的实践示例,为开发者提供从基础接入到复杂应用落地的完整技术路径。

机器人发布于 2026/1/26更新于 2026/9/985 浏览

Spring AI Alibaba 快速入门指南

1. 环境准备

1.1 API Key 配置

访问 阿里云百炼平台 获取 API Key,并在 Windows 环境变量中配置:

AI_DASHSCOPE_API_KEY=your_api_key_here 

配置完成后重启电脑使环境变量生效,可通过命令行验证:

echo %AI_DASHSCOPE_API_KEY% 
1.2 Ollama 本地模型安装(可选)

Ollama 是一个用于在本地运行大型语言模型(LLM)的工具和平台。

Windows 安装步骤:
  1. 下载安装包:https://github.com/ollama/ollama/releases/download/v0.12.3/OllamaSetup.exe

验证安装:

ollama --version 

自定义安装路径:

OllamaSetup.exe /DIR="D:\zhengqingya\soft\soft-dev\AI\LLM\Ollama"

配置环境变量(可选):

OLLAMA_MODELS=D:\zhengqingya\soft\soft-dev\AI\LLM\Ollama\models 
常用 Ollama 命令:
# 启动 Ollama 服务(默认端口:11434)
ollama serve
# 列出已下载的模型
ollama list
# 下载指定模型
ollama pull <model_name>
# 运行指定模型
ollama run <model_name>
# 停止运行中的模型
ollama stop <model_name>
# 删除已下载的模型
ollama rm <model_name>
# 查看当前运行的模型实例
ollama ps

2. 项目依赖配置

2.1 Maven 依赖
<properties>
    <!-- Spring Boot -->
    <spring-boot.version>3.4.0</spring-boot.version>
    <!-- Spring AI -->
    <spring-ai.version>1.0.0</spring-ai.version>
    <!-- Spring AI Alibaba -->
    <spring-ai-alibaba.version>1.0.0.3</spring-ai-alibaba.version>
</properties>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>${spring-boot.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <!-- 用于为所有组件做统一版本管理 -->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-bom</artifactId>
            <version>${spring-ai.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-bom</artifactId>
            <version>${spring-ai-alibaba.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<!-- 核心依赖 -->
<dependency>
    <groupId>com.alibaba.cloud.ai</groupId>
    <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
</dependency>
<!-- Ollama 本地模型支持(可选) -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-model-ollama</artifactId>
    <version>1.0.0</version>
</dependency>
2.2 基础配置
application.yml 配置
server:
  port: 8888
servlet:
  encoding:
    enabled: true
    charset: utf-8
    force: true
spring:
  ai:
    dashscope:
      api-key: ${AI_DASHSCOPE_API_KEY}
    # Ollama 配置(可选)
    ollama:
      base-url: http://localhost:11434
      chat:
        model: deepseek-r1 # qwen3:8b | deepseek-r1

3. 基础使用

3.1 简单对话示例
import lombok.RequiredArgsConstructor;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

@RequiredArgsConstructor
@RestController
public class HelloWorldController {
    private final ChatModel chatModel;

    /**
     * 简单调用
     * http://localhost:888/simple/chat?msg=你是谁?
     */
    @GetMapping("/simple/chat")
    public String simpleChat(@RequestParam String msg) {
        return chatModel.call(msg);
    }

    /**
     * 流式调用
     * http://localhost:888/stream/chat?msg=你是谁?
     */
    @GetMapping("/stream/chat")
    public Flux<String> streamChat(@RequestParam String msg) {
        return chatModel.stream(msg);
    }
}
3.2 ChatClient 使用

ChatClient 是更高层次的抽象,提供了简化的聊天接口和流式处理能力。

import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

@RestController
public class ChatClientController {
    private final ChatClient chatClient;

    public ChatClientController(DashScopeChatModel dashScopeChatModel) {
        chatClient = ChatClient.builder(dashScopeChatModel).build();
    }

    /**
     * 简单调用
     * http://localhost:888/chat-client/simple/chat?msg=你是谁?
     */
    @GetMapping("/chat-client/simple/chat")
    public String simpleChat(@RequestParam String msg) {
        return chatClient.prompt().user(msg).call().content();
    }

    /**
     * 流式调用
     * http://localhost:888/chat-client/stream/chat?msg=你是谁?
     */
    @GetMapping("/chat-client/stream/chat")
    public Flux<String> streamChat(@RequestParam String msg) {
        return chatClient.prompt().user(msg).stream().content();
    }
}
3.3 多模型动态配置
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import com.google.common.collect.Maps;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.ollama.OllamaChatModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import java.util.Map;

@RestController
@RequestMapping("/mutli-platform-model")
@Tag(name = "多模型动态配置")
public class MutliPlatformModelController {
    private Map<String, ChatModel> STRATEGY = Maps.newHashMap();

    public MutliPlatformModelController(DashScopeChatModel dashScopeChatModel, OllamaChatModel ollamaChatModel) {
        STRATEGY.put("dashscope", dashScopeChatModel);
        STRATEGY.put("ollama", ollamaChatModel);
    }

    /**
     * http://localhost:888/mutli-platform-model/stream/chat?platform=dashscope&model=qwen3-max&temperature=0.8&msg=你是谁?
     * http://localhost:888/mutli-platform-model/stream/chat?platform=ollama&model=deepseek-r1&temperature=0.8&msg=你是谁?
     */
    @Operation(summary = "动态切换模型")
    @GetMapping("/stream/chat")
    public Flux<String> streamChat(@ModelAttribute MutliPlatformModelOptions options) {
        ChatModel chatModel = STRATEGY.get(options.platform());
        ChatClient chatClient = ChatClient.builder(chatModel)
                // 模型配置参数
                .defaultOptions(ChatOptions.builder()
                        .model(options.model())
                        .temperature(options.temperature())
                        .build()).build();
        return chatClient.prompt().user(options.msg()).stream().content();
    }
}

4. 提示词(Prompt)使用

4.1 四大角色
public enum MessageType {
    USER("user"),       // 用户发起的消息或请求
    ASSISTANT("assistant"), // AI 助手生成的回复或响应
    SYSTEM("system"),   // 系统级别的指令或信息
    TOOL("tool")        // 工具调用产生的消息
}
4.2 提示词模板
@RestController
@RequestMapping("/prompt")
@Tag(name = "提示词")
public class PromptController {
    private ChatClient chatClient;
    private ChatModel chatModel;

    @Value("classpath:/prompt-template/tell-joke.txt")
    private org.springframework.core.io.Resource promptTemplateRes;

    public PromptController(DashScopeChatModel dashScopeChatModel) {
        chatClient = ChatClient.builder(dashScopeChatModel).build();
        chatModel = dashScopeChatModel;
    }

    private static final String DEFAULT_PROMPT = """
        你是一位资深 Java 架构师,专注于企业级 Java 后端开发。
        请严格按照以下规则回答:
        1. 只回答 Java 及相关技术栈的问题;
        2. 提供准确、专业的技术解答;
        3. 对于非 Java 后端相关问题,请礼貌说明超出了专业范围。
        """;

    /**
     * http://localhost:888/prompt/chat-client?msg=今天吃什么?
     */
    @GetMapping("/chat-client")
    public String chatClient(@RequestParam String msg) {
        return chatClient.prompt().system(DEFAULT_PROMPT).user(msg).call().content();
    }

    /**
     * 提示词模板使用
     * http://localhost:888/prompt/prompt-template?topic=无聊
     */
    @GetMapping("/prompt-template")
    public Flux<String> promptTemplate(@RequestParam String topic) {
        // 创建提示词模板
        PromptTemplate promptTemplate = new PromptTemplate("给我讲一个有关于{topic}的笑话");
        // 创建 Prompt 实例
        Prompt prompt = promptTemplate.create(Map.of("topic", topic));
        // 流式返回结果
        return chatModel.stream(prompt).map(chatResponse -> chatResponse.getResult().getOutput().getText());
    }
}

5. Advisor 对话拦截

Advisor 主要用于拦截 ChatClient 的对话请求和响应,在对话过程中添加通用处理逻辑。

5.1 日志记录
@RestController
@RequestMapping("/advisor")
@Tag(name = "Advisor 对话拦截--日志记录")
public class AdvisorLogController {
    private ChatClient chatClient;

    public AdvisorLogController(DashScopeChatModel dashScopeChatModel) {
        chatClient = ChatClient.builder(dashScopeChatModel).defaultAdvisors(new SimpleLoggerAdvisor()).build();
    }

    /**
     * http://localhost:888/advisor/log?msg=你好
     */
    @GetMapping("/log")
    public Flux<String> log(@RequestParam String msg) {
        return chatClient.prompt().user(msg).stream().content();
    }
}
5.2 敏感词拦截
@RestController
@RequestMapping("/advisor")
@Tag(name = "Advisor 对话拦截--敏感词拦截")
public class AdvisorSafeGuardController {
    private ChatClient chatClient;

    public AdvisorSafeGuardController(DashScopeChatModel dashScopeChatModel) {
        chatClient = ChatClient.builder(dashScopeChatModel)
                .defaultAdvisors(
                        new SimpleLoggerAdvisor(),
                        new SafeGuardAdvisor(Lists.newArrayList("敏感词","WC"),
                                "由于包含敏感内容,我无法对此进行回复。我们可以重新表述或讨论其他话题吗?",
                                1)).build();
    }

    /**
     * http://localhost:888/advisor/safe-guard?msg=你好
     * http://localhost:888/advisor/safe-guard?msg=WC
     */
    @GetMapping("/safe-guard")
    public Flux<String> safeGuard(@RequestParam String msg) {
        return chatClient.prompt().user(msg).stream().content();
    }
}
5.3 自定义拦截器
/**
 * <p> 自定义 Advisor 拦截器 -- 实现重写提示词 </p>
 */
public class RewritePromptAdvisor implements BaseAdvisor {
    private static final String REWRITE_TEXT = """
        让我们仔细分析这个问题:
        问题:{input_msg}
        分析步骤:
        1. 问题理解:这个问题在问什么?
        2. 关键信息:有哪些重要的细节需要注意?
        3. 回答构建:基于以上分析,给出准确详细的回答:
        回答要求:
        - 使用通俗易懂的语言
        - 结合实际情况举例说明
        - 如涉及技术内容,提供具体的操作步骤
        - 返回 html 格式内容
        """;

    @Override
    public ChatClientRequest before(ChatClientRequest chatClientRequest, AdvisorChain advisorChain) {
        String contents = chatClientRequest.prompt().getContents();
        String inputMsg = PromptTemplate.builder().template(REWRITE_TEXT).build().render(Map.of("input_msg", contents));
        return chatClientRequest.mutate().prompt(Prompt.builder().content(inputMsg).build()).build();
    }

    @Override
    public ChatClientResponse after(ChatClientResponse chatClientResponse, AdvisorChain advisorChain) {
        return chatClientResponse;
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

6. 对话记忆(Chat Memory)

用于维护对话上下文状态。

6.1 MySQL 存储示例
依赖配置
<dependency>
    <groupId>com.alibaba.cloud.ai</groupId>
    <artifactId>spring-ai-alibaba-starter-memory-jdbc</artifactId>
    <version>1.0.0.2</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.32</version>
</dependency>
配置文件
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/spring-ai-alibaba-demo?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF8&zeroDateTimeBehavior=convertToNull&useSSL=false
    username: root
    password: root
代码示例
@RestController
@RequestMapping("/chat-memory")
@Tag(name = "对话记忆")
public class ChatMemoryController {
    private final ChatClient chatClient;

    // 注入 JdbcTemplate, ChatClient
    public ChatMemoryController(JdbcTemplate jdbcTemplate, DashScopeChatModel dashScopeChatModel) {
        // 构造 ChatMemoryRepository 和 ChatMemory
        ChatMemoryRepository chatMemoryRepository = MysqlChatMemoryRepository.mysqlBuilder().jdbcTemplate(jdbcTemplate).build();
        ChatMemory chatMemory = MessageWindowChatMemory.builder()
                .chatMemoryRepository(chatMemoryRepository)
                .maxMessages(10) // 消息存储条数
                .build();
        this.chatClient = ChatClient.builder(dashScopeChatModel)
                // 增加聊天记忆能力
                .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
                // 实现 Logger 的 Advisor
                .defaultAdvisors(new SimpleLoggerAdvisor())
                .build();
    }

    /**
     * 使用自定义的 Advisor 增加聊天记忆能力
     * eg:
     * http://127.0.0.1:888/chat-memory/chat/123?msg=你好,我叫郑清,之后的会话中都带上我的名字
     * http://127.0.0.1:888/chat-memory/chat/123?msg=我叫什么名字?
     * http://127.0.0.1:888/chat-memory/chat/111?msg=我叫什么名字?
     */
    @GetMapping("/chat/{id}")
    public Flux<String> advisorChat(HttpServletResponse response, @PathVariable String id, @RequestParam String msg) {
        response.setCharacterEncoding("UTF-8");
        return this.chatClient.prompt(msg).advisors(a -> a.param(ChatMemory.CONVERSATION_ID, id)).stream().content();
    }
}

7. 结构化输出

将模型的输出转换为特定的数据结构。

// 定义数据结构
public record StructuredVO(String category, String question, String answer) {}

@RestController
@RequestMapping("/structured-output")
@Tag(name = "结构化输出")
public class StructuredOutputController {
    private ChatClient chatClient;

    public StructuredOutputController(DashScopeChatModel dashScopeChatModel) {
        chatClient = ChatClient.builder(dashScopeChatModel).build();
    }

    /**
     * http://localhost:888/structured-output/test
     * {"category":"售后服务","question":"这件商品的质量不好,我可以申请退货吗?","answer":"如果商品存在质量问题,通常可以申请退货。请查看商家的退换货政策或联系客服确认具体流程。"}
     */
    @GetMapping("/test")
    public StructuredVO structuredOutput() {
        return chatClient.prompt()
                .system("对问题分类并简要总结,并给出答案")
                .user("这件商品的质量不好,我可以申请退货吗?")
                .call()
                .entity(StructuredVO.class);
    }
}

8. 工具调用(Tool Calling)

允许 AI 模型调用外部工具和函数来执行特定任务。

8.1 定义工具
方法工具
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Service;

@Service
public class TimeTools {
    @Tool(description = "获取当前时间,默认时间格式:YYYY-MM-DD HH:mm:ss")
    public String getCurrentTime(@ToolParam(description = "时间格式") String format) {
        log.info("获取当前时间格式:{}", format);
        return DateUtil.now();
    }
}
函数工具
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Description;
import org.springframework.stereotype.Service;
import java.util.function.Function;

@Slf4j
@Service(value = "getWeather")
public class WeatherFunction implements Function<String, String> {
    @Override
    @Description("获取天气")
    public String apply(@JsonPropertyDescription("城市") String city) {
        log.info("获取{}天气", city);
        return city + "天气:晴";
    }
}
8.2 注册工具 & 调用
@RestController
@RequestMapping("/tool-calling")
@Tag(name = "工具调用")
public class ToolCallingController {
    private ChatClient chatClient;

    @Autowired
    private TimeTools timeTools;

    public ToolCallingController(DashScopeChatModel dashScopeChatModel) {
        chatClient = ChatClient.builder(dashScopeChatModel).build();
    }

    /**
     * 法 1:方法工具
     * http://localhost:888/tool-calling/time?msg=现在时间?
     */
    @GetMapping("/time")
    public Flux<String> time(@RequestParam String msg) {
        return chatClient.prompt().user(msg).tools(timeTools).stream().content();
    }

    /**
     * 法 2:函数工具
     * http://localhost:888/tool-calling/weather?msg=成都天气?
     */
    @GetMapping("/weather")
    public Flux<String> weather(@RequestParam String msg) {
        return chatClient.prompt().user(msg).toolNames(new String[]{"getWeather"}).stream().content();
    }
}
8.3 动态注册工具
@RestController
@RequestMapping("/tool-calling")
@Tag(name = "工具调用 - 动态注册")
public class ToolCallingDynamicController {
    private ChatClient chatClient;

    @Autowired
    private TimeTools timeTools;

    public ToolCallingDynamicController(DashScopeChatModel dashScopeChatModel, TimeTools timeTools) {
        chatClient = ChatClient.builder(dashScopeChatModel).build();
    }

    /**
     * 法一:方法工具
     * http://localhost:888/tool-calling/dynamic/1?msg=现在时间?
     * http://localhost:888/tool-calling/dynamic/2?msg=现在时间?
     */
    @GetMapping("/dynamic/{userId}")
    public Flux<String> dynamic(@PathVariable Long userId, @RequestParam String msg) {
        return chatClient.prompt().user(msg)
                // 动态注册工具
                .toolCallbacks(getToolCallbacks(userId))
                .stream().content();
    }

    private List<ToolCallback> getToolCallbacks(Long userId) {
        List<ToolCallback> toolCallbacks = Lists.newArrayList();
        if (userId != 1) {
            return toolCallbacks;
        }
        Method method = ReflectionUtils.findMethod(TimeTools.class, "getCurrentTime", String.class);
        if (method == null) {
            throw new RuntimeException("Method not found");
        }
        String inputSchema = JsonSchemaGenerator.generateForMethodInput(method);
        ToolCallback timeToolCallback = MethodToolCallback.builder()
                .toolDefinition(ToolDefinition.builder()
                        .name("getCurrentTime")
                        .description("获取当前时间,默认时间格式:YYYY-MM-DD HH:mm:ss")
                        .inputSchema(inputSchema)
                        .build())
                .toolMethod(method)
                .toolObject(timeTools)
                .build();
        toolCallbacks.add(timeToolCallback);
        return toolCallbacks;
    }

    /**
     * 法二:函数工具
     * http://localhost:888/tool-calling/dynamic2/1?msg=成都天气?
     * http://localhost:888/tool-calling/dynamic2/2?msg=成都天气?
     */
    @GetMapping("/dynamic2/{userId}")
    public Flux<String> dynamic2(@PathVariable Long userId, @RequestParam String msg) {
        String[] toolNames = userId == 1 ? new String[]{"getWeather"} : new String[0];
        return chatClient.prompt().user(msg)
                // 动态注册工具
                .toolNames(toolNames)
                .stream().content();
    }
}

9. MCP(Model Context Protocol)

MCP 是一种模型上下文协议,主要用于 AI 模型调用外部工具和函数。

9.1 传输方式
  1. STDIO(标准输入输出):通过标准输入输出流进行数据传输
  2. SSE(Server-Sent Events):基于 HTTP 的单向服务器推送技术
  3. Streamable HTTP:基于 HTTP 协议的流式传输
9.2 自定义 MCP 服务 - STDIO
服务端实现
<!-- pom.xml 依赖 -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-mcp-server-webflux</artifactId>
</dependency>
# application.yml 配置
spring:
  main:
    web-application-type: none # 设置为非 Web 应用类型
    banner-mode: off
  ai:
    mcp:
      server:
        stdio: true
        name: my-weather-server
        version: 1.0.0
// 实现 MCP 工具
@Service
public class MyWeatherService {
    @Tool(description = "获取天气")
    public String getWeather(@ToolParam(description = "城市") String city) {
        return city + "天气:雨";
    }
}

// 注册 MCP 工具
@Bean
public ToolCallbackProvider weatherTools(MyWeatherService myWeatherService) {
    return MethodToolCallbackProvider.builder().toolObjects(myWeatherService).build();
}
客户端接入
spring:
  ai:
    mcp:
      client:
        stdio:
          servers-configuration: classpath:/mcp/mcp-servers-config.json
{
  "mcpServers": {
    "my-weather-server": {
      "command": "java",
      "args": [
        "-Dspring.ai.mcp.server.stdio=true",
        "-Dspring.main.web-application-type=none",
        "-Dlogging.pattern.console=",
        "-jar",
        "D:\\path\\to\\your\\mcp-server.jar"
      ]
    }
  }
}
9.3 自定义 MCP 服务 - SSE
服务端实现
# application.yml 配置
server:
  port: 10088
spring:
  main:
    banner-mode: off
  ai:
    mcp:
      server:
        name: my-sse-server
        version: 1.0.0
        type: ASYNC
        sse-endpoint: /sse
        sse-message-endpoint: /mcp
        capabilities:
          tool: true
          resource: true
          prompt: true
          completion: true
客户端接入
spring:
  ai:
    mcp:
      client:
        sse:
          connections:
            my-sse-server:
              url: http://localhost:10088

10. RAG(Retrieval-Augmented Generation)

RAG 是一种结合检索和生成的技术架构,通过引入'外接知识库'让模型在回答问题前先查阅权威资料。

10.1 嵌入模型 (Embedding Model)
spring:
  ai:
    dashscope:
      embedding:
        options:
          model: text-embedding-v4
          dimensions: 64
@RestController
@RequestMapping("/rag")
@Tag(name = "RAG")
public class RagController {
    private EmbeddingModel embeddingModel;

    public RagController(DashScopeEmbeddingModel dashScopeEmbeddingModel) {
        embeddingModel = dashScopeEmbeddingModel;
    }

    /**
     * http://localhost:888/rag/embedding?msg=你好
     */
    @GetMapping("/embedding")
    public Object embedding(@RequestParam String msg) {
        EmbeddingResponse embeddingResponse = this.embeddingModel.embedForResponse(List.of(msg));
        return Map.of("embedding", embeddingResponse);
    }
}
10.2 向量数据库

使用 SimpleVectorStore 轻量级向量数据库:

@RestController
@RequestMapping("/rag")
@Tag(name = "RAG")
public class RagController {
    private VectorStore vectorStore;

    public RagController(DashScopeEmbeddingModel dashScopeEmbeddingModel) {
        vectorStore = SimpleVectorStore.builder(dashScopeEmbeddingModel).build();
    }

    /**
     * http://localhost:888/rag/vectorStore?topK=3&threshold=0.1&msg=有哪些开源的向量数据库?
     */
    @GetMapping("/vectorStore")
    public Object vectorStore(@RequestParam String msg, @RequestParam int topK, @RequestParam double threshold) {
        // 存储向量
        vectorStore.add(Lists.newArrayList(
                Document.builder().text("LangChain 是一个用于开发由语言模型驱动的应用程序的框架。").build(),
                Document.builder().text("Milvus 是一款开源向量数据库,专为大规模向量相似性搜索而设计。").build()
        ));
        // 相似性搜索
        return vectorStore.similaritySearch(SearchRequest.builder().query(msg).topK(topK).similarityThreshold(threshold).build());
    }
}
10.3 ELT (Extract, Load, Transform)
文本读取器
@Value("classpath:rag/pet.txt")
private Resource textRes;

/**
 * 文本读取器
 * http://localhost:888/rag/elt/text
 */
@GetMapping("/text")
public Object text() {
    // 1. 创建文本读取器
    TextReader textReader = new TextReader(textRes);
    // 2. 读取文档内容
    List<Document> documents = textReader.read();
    // 3. 返回结果
    return documents;
}
Markdown 读取器
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-markdown-document-reader</artifactId>
</dependency>
@Value("classpath:rag/iphone.md")
private Resource MdRes;

/**
 * markdown 读取器
 * http://localhost:888/rag/elt/markdown
 */
@GetMapping("/markdown")
public Object markdown() {
    // 1. 创建 Markdown 文档读取器
    MarkdownDocumentReader markdownReader = new MarkdownDocumentReader(MdRes,
            MarkdownDocumentReaderConfig.builder()
                    .withAdditionalMetadata("filename", MdRes.getFilename())
                    .withHorizontalRuleCreateDocument(false)
                    .withIncludeCodeBlock(false)
                    .withIncludeBlockquote(false)
                    .build());
    // 2. 读取 Markdown 文档内容
    List<Document> documents = markdownReader.read();
    // 3. 返回文档列表
    return documents;
}
10.4 文档分割器

TokenTextSplitter 是 RAG 应用中关键的文档预处理工具。

/**
 * 文本读取器
 * http://localhost:888/rag/elt/split/text
 */
@GetMapping("/text")
public Object text() {
    // 1. 创建文本读取器
    TextReader textReader = new TextReader(textRes);
    // 2. 读取文档内容
    List<Document> documents = textReader.read();
    // 3. 文档分割处理
    List<Document> splitDocs = new TokenTextSplitter(800, 350, 5, 10000, true).split(documents);
    // 4. 返回结果
    return splitDocs;
}
10.5 自定义文档分割器
/**
 * 学校规章制度专用文档分割器 - 针对规章制度结构优化
 * 支持按"第.*条"分割,保留规章制度的结构完整性
 */
public class RegulationDocumentSplitter extends TextSplitter {
    // 实现细节...
    public static Builder builder() {
        return new Builder();
    }
    // ... 其他方法实现
}
10.6 文档转换器
关键字提取
@GetMapping("/keyword")
public Object keyword() {
    // 1. 创建 Markdown 文档读取器
    MarkdownDocumentReader markdownReader = new MarkdownDocumentReader(MdRes,
            MarkdownDocumentReaderConfig.builder().withAdditionalMetadata("filename", MdRes.getFilename()).build());
    // 2. 读取文档内容
    List<Document> documents = markdownReader.read();
    // 3. 关键字提取
    KeywordMetadataEnricher enricher = new KeywordMetadataEnricher(chatModel, 3);
    List<Document> enricherDocs = enricher.apply(documents);
    return enricherDocs;
}
摘要生成
@GetMapping("/summary")
public Object summary() {
    // 1. 创建 Markdown 文档读取器
    MarkdownDocumentReader markdownReader = new MarkdownDocumentReader(MdRes,
            MarkdownDocumentReaderConfig.builder().withAdditionalMetadata("filename", MdRes.getFilename()).build());
    // 2. 读取文档内容
    List<Document> documents = markdownReader.read();
    // 3. 摘要生成
    SummaryMetadataEnricher enricher = new SummaryMetadataEnricher(chatModel, List.of(SummaryMetadataEnricher.SummaryType.CURRENT, SummaryMetadataEnricher.SummaryType.NEXT),
            """请为以下文档内容生成摘要:
            {context_str}
            要求:
            1. 摘要长度不超过 100 字
            2. 突出关键信息
            3. 保持原意不变
            """, MetadataMode.ALL);
    List<Document> enricherDocs = enricher.apply(documents);
    return enricherDocs;
}
元数据过滤
@GetMapping("/metadata-filter")
public Object metadataFilter() {
    // 1. 创建 Markdown 文档读取器
    MarkdownDocumentReader markdownReader = new MarkdownDocumentReader(MdRes,
            MarkdownDocumentReaderConfig.builder().withAdditionalMetadata("filename", MdRes.getFilename()).build());
    // 2. 读取文档内容
    List<Document> documents = markdownReader.read();
    // 3. 关键字提取
    KeywordMetadataEnricher enricher = new KeywordMetadataEnricher(chatModel, 3);
    List<Document> enricherDocs = enricher.apply(documents);
    // 4. 存储向量
    vectorStore.add(enricherDocs);
    // 5. 相似性搜索
    List<Document> result = vectorStore.similaritySearch(SearchRequest.builder()
            .query("手机处理器")
            .topK(3)
            // 基于元数据的过滤条件
            .filterExpression(new FilterExpressionBuilder().eq("filename", "phone.md").build())
            .build());
    // 6. 返回结果
    return result;
}
10.7 检索增强生成
@RestController
@RequestMapping("/rag/retrieval-augmentation")
@Tag(name = "RAG-检索增强生成")
public class RagRetrievalAugmentationAdvisorController {
    @Value("classpath:rag/iphone.md")
    private Resource MdRes;
    private ChatModel chatModel;
    private ChatClient chatClient;
    private VectorStore vectorStore;

    public RagRetrievalAugmentationAdvisorController(DashScopeChatModel dashScopeChatModel, DashScopeEmbeddingModel dashScopeEmbeddingModel) {
        chatModel = dashScopeChatModel;
        chatClient = ChatClient.builder(dashScopeChatModel).defaultAdvisors(new SimpleLoggerAdvisor()).build();
        vectorStore = SimpleVectorStore.builder(dashScopeEmbeddingModel).build();
    }

    /**
     * RetrievalAugmentationAdvisor 检索增强生成
     * http://localhost:888/rag/retrieval-augmentation/chat?msg=下雨天,我不想出门,你能不能帮我了解下手机店里的 iphone18 价格?
     */
    @GetMapping("/chat")
    public Flux<String> chat(@RequestParam String msg) {
        // 1. 创建 Markdown 文档读取器
        MarkdownDocumentReader markdownReader = new MarkdownDocumentReader(MdRes,
                MarkdownDocumentReaderConfig.builder().withAdditionalMetadata("filename", MdRes.getFilename()).build());
        // 2. 读取文档内容
        List<Document> documents = markdownReader.read();
        vectorStore.add(documents);
        // 3. 检索增强生成
        Advisor advisor = RetrievalAugmentationAdvisor.builder()
                // documentRetriever:文档检索器
                .documentRetriever(VectorStoreDocumentRetriever.builder()
                        .vectorStore(vectorStore)
                        .similarityThreshold(0.5)
                        .topK(3)
                        .build())
                // queryAugmenter:查询增强器
                .queryAugmenter(ContextualQueryAugmenter.builder()
                        .allowEmptyContext(false)
                        .emptyContextPromptTemplate(PromptTemplate.builder()
                                .template("用户查询位于知识库之外。礼貌地告知用户您无法回答。").build())
                        .build())
                // QueryTransformer:查询转换器
                .queryTransformers(
                        // 查询重写
                        RewriteQueryTransformer.builder()
                                .chatClientBuilder(ChatClient.builder(chatModel))
                                .targetSearchSystem("知识库")
                                .build(),
                        // 翻译转换器
                        TranslationQueryTransformer.builder()
                                .chatClientBuilder(ChatClient.builder(chatModel))
                                .targetLanguage("中文")
                                .build())
                .build();
        return chatClient.prompt().user(msg).advisors(advisor).stream().content();
    }
}
10.8 重排序
@RestController
@RequestMapping("/rag/rerank")
@Tag(name = "重排序")
public class RerankController {
    private RerankModel rerankModel;
    private ChatClient chatClient;
    private VectorStore vectorStore;

    public RerankController(DashScopeChatModel dashScopeChatModel, DashScopeEmbeddingModel dashScopeEmbeddingModel, DashScopeRerankModel dashScopeRerankModel) {
        chatClient = ChatClient.builder(dashScopeChatModel).defaultAdvisors(new SimpleLoggerAdvisor()).build();
        vectorStore = SimpleVectorStore.builder(dashScopeEmbeddingModel).build();
        rerankModel = dashScopeRerankModel;
    }

    /**
     * http://localhost:888/rag/rerank/chat?msg=iphone20 价格
     */
    @GetMapping("/chat")
    public Flux<String> chat(@RequestParam String msg) {
        vectorStore.add(Lists.newArrayList(
                Document.builder().text("iPhone18 基础价格:5999 元,税率:13%, 运费:0 元").build(),
                Document.builder().text("iPhone20 基础价格:12999 元,税率:13%, 运费:50 元").build(),
                Document.builder().text("iPhone10 基础价格:1299 元,税率:13%, 运费:0 元").build()
        ));
        // 重排序
        RetrievalRerankAdvisor rerankAdvisor = new RetrievalRerankAdvisor(vectorStore, rerankModel, SearchRequest.builder().topK(10).build());
        return chatClient.prompt().user(msg).advisors(rerankAdvisor).stream().content();
    }
}
10.9 幻觉评估
@RestController
@RequestMapping("/rag/fact-check")
@Tag(name = "RAG-幻觉评估")
public class RagFactCheckController {
    private ChatModel chatModel;

    public RagFactCheckController(DashScopeChatModel dashScopeChatModel) {
        chatModel = dashScopeChatModel;
    }

    @GetMapping("/test")
    public Object test() {
        // 准备评估数据
        ArrayList<Document> documents = Lists.newArrayList(
                Document.builder().text("iPhone18 基础价格:5999 元,税率:13%, 运费:0 元").build(),
                Document.builder().text("iPhone20 基础价格:12999 元,税率:13%, 运费:50 元").build(),
                Document.builder().text("iPhone10 基础价格:1299 元,税率:13%, 运费:0 元").build()
        );
        // AI 回答
        String aiRes = "iPhone20 6000 元";
        // 执行评估
        FactCheckingEvaluator evaluator = new FactCheckingEvaluator(ChatClient.builder(chatModel));
        EvaluationRequest evaluationRequest = new EvaluationRequest(documents, aiRes);
        EvaluationResponse evaluationResponse = evaluator.evaluate(evaluationRequest);
        return evaluationResponse; // {"pass":false,"score":0.0,"feedback":"","metadata":{}}
    }
}

11. AI Agent

AI Agent 是一种能够自主感知环境、做出决策并执行动作以实现特定目标的智能实体。

11.1 三种人机协同模式
模式名称核心关系 (人类 : AI)特点与工作方式典型场景
嵌入模式 (Embedded)我做你看将 AI 能力作为功能模块嵌入现有产品智能客服中的自动回复、推荐系统
副驾驶模式 (Copilot)我们一起做AI 作为实时协作的伙伴,提供建议和辅助GitHub Copilot 代码补全
智能体模式 (Agent)你做我看AI 拥有高度自主权,可独立规划、决策、执行复杂任务自动交易系统、智能助手
11.2 AI Agent 的五大工作模式
  1. 提示链模式:将复杂任务拆解成一系列顺序执行的子任务
  2. 路由模式:根据输入内容特点,动态选择最合适的处理路径
  3. 并行化模式:同时启动多个 Agent 处理独立的子任务
  4. 协调者 - 工作者模式:模拟指挥体系,由协调者负责任务分解和调度
  5. 评估者 - 优化者模式:引入自我反思和优化的闭环

12. 可观测性

软件的可观测性是指通过系统输出(如日志、指标、跟踪等)来推断其内部状态的能力。

12.1 Zipkin 部署

参考官方文档部署 Zipkin。

12.2 Java 项目配置
依赖配置
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud.ai</groupId>
    <artifactId>spring-ai-alibaba-starter-tool-calling-weather</artifactId>
</dependency>
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-tracing-bridge-brave</artifactId>
    <version>1.5.0-M2</version>
</dependency>
<dependency>
    <groupId>io.zipkin.reporter2</groupId>
    <artifactId>zipkin-reporter-brave</artifactId>
    <version>3.4.3</version>
</dependency>
配置文件
spring:
  ai:
    dashscope:
      api-key: ${AI_DASHSCOPE_API_KEY}
  observations:
    log-completion: true
    log-prompt: true
    # Chat config items
    chat:
      client:
        observations:
          log-prompt: true
          log-completion: true
          include-error-logging: true
    # tools config items
    tools:
      observability:
        include-content: true
    http:
      client:
        read-timeout: 60s
  management:
    endpoints:
      web:
        exposure:
          include: "*"
    endpoint:
      health:
        show-details: always
    tracing:
      sampling:
        probability: 1.0
  zipkin:
    tracing:
      endpoint: http://localhost:9411/api/v2/spans

13. Spring AI Alibaba Graph

Spring AI Alibaba Graph 是一个强大且直观的框架,能让你像搭积木一样,通过编排节点和流程来构建复杂的 AI 应用。

13.1 快速入门
依赖配置
<dependency>
    <groupId>com.alibaba.cloud.ai</groupId>
    <artifactId>spring-ai-alibaba-graph-core</artifactId>
</dependency>
创建自定义节点
public class ExpanderNode implements NodeAction {
    private final ChatClient chatClient;

    public ExpanderNode(DashScopeChatModel chatModel) {
        this.chatClient = ChatClient.builder(chatModel).build();
    }

    @Override
    public Map<String, Object> apply(OverAllState state) throws Exception {
        // 1. 从全局状态中获取输入
        String query = state.value("query", "");
        // 2. 构建提示词并调用大模型
        String promptTemplate = "请为以下问题生成 3 个不同角度的变体问题:{query}";
        String result = chatClient.prompt().user(u -> u.text(promptTemplate).param("query", query)).call().content();
        // 3. 处理结果
        List<String> queryVariants = Arrays.asList(result.split("\n"));
        // 4. 将结果放入 Map
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("expander_content", queryVariants);
        return resultMap;
    }
}
配置状态图
@Configuration
public class SimpleGraphConfiguration {
    @SneakyThrows
    @Bean
    public StateGraph simpleGraph(DashScopeChatModel chatModel) {
        // 全局变量的替换策略
        KeyStrategyFactory keyStrategyFactory = () -> {
            HashMap<String, KeyStrategy> strategies = new HashMap<>();
            strategies.put("query", new ReplaceStrategy());
            strategies.put("expander_content", new ReplaceStrategy());
            return strategies;
        };
        // 构建状态图
        StateGraph stateGraph = new StateGraph("问题扩展工作流", keyStrategyFactory)
                // 添加节点
                .addNode("expander", AsyncNodeAction.node_async(new ExpanderNode(chatModel)))
                // 添加边
                .addEdge(StateGraph.START, "expander")
                .addEdge("expander", StateGraph.END);
        return stateGraph;
    }
}
编译并运行工作流
@RestController
@RequestMapping("/graph")
@Tag(name = "Graph")
public class SimpleGraphController {
    private final CompiledGraph compiledGraph;

    // 注入定义好的 StateGraph,并编译成 CompiledGraph
    public SimpleGraphController(@Qualifier("simpleGraph") StateGraph stateGraph) throws GraphStateException {
        this.compiledGraph = stateGraph.compile();
    }

    /**
     * http://localhost:888/graph/expand?query=什么是人工智能
     */
    @GetMapping("/expand")
    public Map<String, Object> expandQuery(@RequestParam String query) throws GraphRunnerException {
        // 设置初始状态
        Map<String, Object> initialState = Map.of("query", query);
        // 执行工作流
        Optional<OverAllState> result = compiledGraph.invoke(initialState);
        // 从最终状态中获取结果
        return result.map(OverAllState::data).orElse(Map.of());
    }
}

目录

  1. Spring AI Alibaba 快速入门指南
  2. 1. 环境准备
  3. 1.1 API Key 配置
  4. 1.2 Ollama 本地模型安装(可选)
  5. Windows 安装步骤:
  6. 常用 Ollama 命令:
  7. 启动 Ollama 服务(默认端口:11434)
  8. 列出已下载的模型
  9. 下载指定模型
  10. 运行指定模型
  11. 停止运行中的模型
  12. 删除已下载的模型
  13. 查看当前运行的模型实例
  14. 2. 项目依赖配置
  15. 2.1 Maven 依赖
  16. 2.2 基础配置
  17. application.yml 配置
  18. 3. 基础使用
  19. 3.1 简单对话示例
  20. 3.2 ChatClient 使用
  21. 3.3 多模型动态配置
  22. 4. 提示词(Prompt)使用
  23. 4.1 四大角色
  24. 4.2 提示词模板
  25. 5. Advisor 对话拦截
  26. 5.1 日志记录
  27. 5.2 敏感词拦截
  28. 5.3 自定义拦截器
  29. 6. 对话记忆(Chat Memory)
  30. 6.1 MySQL 存储示例
  31. 依赖配置
  32. 配置文件
  33. 代码示例
  34. 7. 结构化输出
  35. 8. 工具调用(Tool Calling)
  36. 8.1 定义工具
  37. 方法工具
  38. 函数工具
  39. 8.2 注册工具 & 调用
  40. 8.3 动态注册工具
  41. 9. MCP(Model Context Protocol)
  42. 9.1 传输方式
  43. 9.2 自定义 MCP 服务 - STDIO
  44. 服务端实现
  45. application.yml 配置
  46. 客户端接入
  47. 9.3 自定义 MCP 服务 - SSE
  48. 服务端实现
  49. application.yml 配置
  50. 客户端接入
  51. 10. RAG(Retrieval-Augmented Generation)
  52. 10.1 嵌入模型 (Embedding Model)
  53. 10.2 向量数据库
  54. 10.3 ELT (Extract, Load, Transform)
  55. 文本读取器
  56. Markdown 读取器
  57. 10.4 文档分割器
  58. 10.5 自定义文档分割器
  59. 10.6 文档转换器
  60. 关键字提取
  61. 摘要生成
  62. 元数据过滤
  63. 10.7 检索增强生成
  64. 10.8 重排序
  65. 10.9 幻觉评估
  66. 11. AI Agent
  67. 11.1 三种人机协同模式
  68. 11.2 AI Agent 的五大工作模式
  69. 12. 可观测性
  70. 12.1 Zipkin 部署
  71. 12.2 Java 项目配置
  72. 依赖配置
  73. 配置文件
  74. 13. Spring AI Alibaba Graph
  75. 13.1 快速入门
  76. 依赖配置
  77. 创建自定义节点
  78. 配置状态图
  79. 编译并运行工作流

更多推荐文章

查看全部
  • MySQL 基础:数据创建与查询入门指南
  • Java 开发中常见的误导性错误及排查指南
  • OpenClaw 开源 AI 助手中文发行版部署指南:Docker 与 NPM 安装配置
  • 无需部署服务器:内网穿透实现本地服务对外演示
  • 基于 CoT+PoT 验证器提升 LLM 推理准确性与一致性的方法
  • Trae IDE 自定义规则配置指南
  • 数据结构初阶:详解线性表之顺序表
  • 安卓 Termux 部署 AstrBot 与 NapCat 搭建 QQ 机器人
  • 小米智能家居 Miloco 分离式部署实战
  • 使用 OpenClaw 集成飞书构建专属 AI 机器人指南
  • 安卓手机使用 Termux 部署 AstrBot 与 NapCat 搭建 QQ 机器人
  • 激光雷达点云滤波:5 种主流算法对比与选型指南
  • Python 实现图像像素化特效
  • Python 面试核心知识点汇总:装饰器与数据结构
  • Python 临床知识问答与检索项目落地实践
  • llama.cpp 多 GPU 分布式计算优化实践指南
  • GitHub 身份验证方式 PAT 和 SSH 详解
  • Sudachi 模拟器架构解析与跨平台实现
  • Amazon DocumentDB Serverless 架构特性与弹性扩缩容指南
  • Ubuntu 22.04 环境下 ROS2 Humble 复现 FAST-LIVO2 实战指南

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • RSA密钥对生成器

    生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online

  • Mermaid 预览与可视化编辑

    基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online