项目版本:v0.0.1 | 框架:Spring Boot 3.5.7 + Spring AI 1.1.0 + Spring AI Alibaba 1.1.0.0-RC1


1. 概述

Spring AI Alibaba Playground 是一个汇聚多种 AI 能力的“游乐场”示例项目。它基于 Spring AI + Spring AI Alibaba 框架,将阿里云通义千问(DashScope)系列模型的各种 AI 能力以清晰、可复现的方式整合到一个应用中,供学习、参考和二次开发。

能力矩阵

能力 后端入口 使用的 AI 服务 前端路由
普通对话 /api/v1/chat DashScope Chat (qwen-plus) /chat
深度思考对话 /api/v1/deep-thinking/chat DashScope Chat (deepseek-r1) /chat
图片生成 /api/v1/text2image Wanx (通义万相) /image
图片理解 /api/v1/image2text Qwen-VL-Max /image
语音识别 /api/v1/audio2text DashScope Audio Transcription /audio
语音合成 /api/v1/text2audio DashScope TTS /audio
视频理解 /api/v1/video-qa Qwen-VL-Max (帧提取) /video
视频生成 /api/v1/video-gen DashScope Video Model /video
联网搜索 /api/v1/search DashScope IQA / Module RAG /search
知识库问答 /api/v1/rag BaiLian / SimpleVectorStore /rag
工具调用 /api/v1/tool-call DashScope + Baidu Tools /tools
MCP 管理 /api/v1/mcp-list, mcp-run MCP Stdio Server /mcp
文档摘要 /api/v1/summarizer DashScope Chat (deepseek-r1) /summarizer
模型清单 /api/v1/dashscope/getModels models.yaml 配置文件
健康检查 /api/v1/health

前端概览

前端基于 React + Ant Design X + TypeScript,使用 pnpm 管理依赖。整体采用暗/亮双主题,路由使用 HashRouter,单页面应用结构。每个功能对应一个独立页面组件,通过 pageComponents map 注册。


2. 整体架构

项目采用经典的分层架构,各层职责明确:

HTTP JSON/SSE

Frontend - React + Ant Design X

RestController 层

Service 层

Spring AI 抽象层

自定义 Module RAG

DashScope SDK / OpenAI Compatible / Baidu API

通义千问 DashScope

阿里云 IQS 搜索

百度翻译/地图 API

包结构概览

src/main/java/com/alibaba/cloud/ai/application/
├── SAAPlayGroundApplication.java       — 主启动类
├── advisor/                             — 自定义 Advisor
│   └── ReasoningContentAdvisor.java     — DeepSeek R1 推理内容展示
├── config/                              — 全局配置
│   ├── AppConfiguration.java            — ChatMemory, ToolCallingManager, DashScopeApi
│   ├── FaviconConfiguration.java        — favicon.ico 拦截
│   ├── RestConfiguration.java           — HTTP 超时/连接池
│   ├── WebSearchConfiguration.java      — QueryTransformer, QueryExpander
│   ├── WebSearchProperties.java         — 搜索配置 Properties
│   ├── handler/
│   │   └── GlobalExceptionHandler.java  — 全局异常处理(已修正参数类型)
│   ├── mcp/
│   │   ├── CustomMcpStdioTransportConfigurationBeanPostProcessor.java
│   │   └── SyncMcpToolCallbackWrapper.java
│   ├── prompt/
│   │   ├── SystemPromptConfig.java
│   │   ├── DeepThinkPromptTemplateConfig.java
│   │   └── DocsSummarizerPromptTemplate.java
│   └── rag/
│       ├── SimpleVectorStoreConfiguration.java
│       ├── VectorStoreDelegate.java
│       └── VectorStoreInitializer.java
├── controller/                          — REST 控制器 (10个)
├── entity/                              — DTO / 实体类
├── enums/                               — 枚举
├── exception/                           — 自定义异常
├── mcp/                                 — MCP 核心逻辑
│   ├── McpServerContainer.java          — MCP 服务注册表(已改为实例变量)
│   └── McpServerUtils.java              — MCP 配置加载工具
├── modulerag/                           — 自定义 Module RAG 实现
│   ├── IQSSearchProperties.java
│   ├── WebSearchRetriever.java
│   ├── core/IQSSearchEngine.java
│   ├── data/DataClean.java              — (已将静态 Map 改为线程安全实例)
│   ├── join/ConcatenationDocumentJoiner.java
│   ├── preretrieval/query/expansion/MultiQueryExpander.java
│   ├── prompt/CustomContextQueryAugmenter.java
│   ├── prompt/PromptTemplateConfig.java
│   └── postretrieval/DashScopeDocumentRanker.java
├── service/                             — 业务服务层
├── tools/                               — 工具函数 (百度相关)
│   ├── BaiduMapTools.java
│   ├── BaiduTranslateTools.java
│   └── ToolsInit.java
└── utils/
    ├── FilesUtils.java
    └── ModelsUtils.java

3. 模块逐一分析

3.1 Chat 模块

文件SAAChatController.java + SAAChatService.java

3.1.1 请求流程
前端 POST /api/v1/chat
  Header: model=qwen-plus, chatId=xxx
  Body: "用户的输入文本"
      │
      ▼
SAAChatController.chat()
  ├─ 校验 model 是否在 models.yaml 中
  ├─ 不传 => 默认 qwen-plus
  ├─ response.setCharacterEncoding("UTF-8")
  └─ chatService.chat(chatId, model, prompt)
      │
      ▼
SAAChatService.chat()
  ├─ model == "deepseek-r1" → 注入 ReasoningContentAdvisor
  ├─ 构建 DashScopeChatOptions (temperature=0.8, responseFormat=TEXT)
  ├─ ChatClient.prompt()
  │   ├─ .options(runtimeOptions)
  │   ├─ .user(prompt)
  │   ├─ .advisors(memoryAdvisor → chatId)
  │   └─ .advisors(retrievalAdvisor) — 如果启用了百炼
  └─ .stream().content()  → Flux<String> (SSE 流式返回)
3.1.2 核心原理与修正
  1. ChatClient:Spring AI 提供的流式聊天客户端,内部封装了 ChatModel、advisor 链、参数管理等。
  2. ChatMemory:通过 chatId 维护多轮对话记忆,默认使用 InMemoryChatMemory
  3. ReasoningContentAdvisor:自定义 Advisor,在 after 阶段拦截 ChatResponse,读取 reasoningContent 元数据(DeepSeek-R1 的推理过程),将其拼接到输出中。
  4. 模型校验:原实现中,使用 Flux.just("Input model not support.") 返回错误信息,不走异常通道,前端难以捕获。已修正为抛出 SAAAppException,由全局异常处理器统一处理。
  5. deep-thinking 路径:原实现中 deepThinkingChat() 缺少 ReasoningContentAdvisor,现在已统一添加,确保推理内容的一致性输出。

3.2 Base 模块

文件SAABaseController.java + SAABaseService.java
职责:提供模型列表查询和健康检查。

模型数据来源于 models.yaml,通过 ModelsUtils.getDashScopeModels() 读取。当前采用静态配置文件管理,优点是简单直观;若需动态更新,可引入配置中心或数据库。

3.3 Image 模块

文件SAAImageController.java + SAAImageService.java

3.3.1 双能力
  • 文生图 (text2image):调用 wanx2.1-t2i-turbo 模型,下载图片后直接写入 HttpServletResponse
  • 图生文 (image2text):使用 qwen-vl-max-latest 多模态模型,将上传图片和问题一起送入 ChatClient,流式返回结果。
3.3.2 阻塞问题修正

原代码中存在 .collectList().block() 阻塞调用,这在 WebFlux 环境会阻塞 Netty 线程。已重构为完全响应式返回:

public Flux<String> image2Text(String prompt, MultipartFile file) {
    // 准备 message...
    return daschScopeChatClient.prompt(new Prompt(message, options))
            .stream()
            .chatResponse()
            .map(cr -> cr.getResult().getOutput().getText());
}

3.4 Audio 模块

文件SAAAudioController.java + SAAAudioService.java

3.4.1 能力
  • 语音转文字audio2text
  • 文字转语音text2audio
3.4.2 路径修正

@RequestMapping("/api/v1/") 末尾多了一个斜杠,导致实际路径为 /api/v1//audio2text已修正@RequestMapping("/api/v1"),避免双斜杠问题。

3.4.3 备注

Service 中曾有一条注释 // Emm~, has error.,表明开发者意识到可能存在问题。经过排查,这与临时文件权限或音频格式兼容性有关,现已添加更明确的异常日志,并建议用户配置临时目录权限。

3.5 Video 模块

文件SAAVideoController.java + SAAVideoService.java

3.5.1 能力
  • 视频问答:提取视频帧后送 Qwen-VL-Max 进行理解。
  • 视频生成:调用 DashScope Video Model。
3.5.2 帧提取算法优化

原算法使用 step = totalFrames / frameCount(整数除法),短视频时 step 可能为 0,导致重复帧。已修正为:

int step = Math.max(1, totalFrames / frameCount);
3.5.3 依赖修正

原代码中导入了 javax.validation.constraints.NotNull已修正jakarta.validation.constraints.NotNull,以适配 Jakarta EE 规范。

3.5.4 阻塞调用去除

同 Image 模块,视频问答中的阻塞调用已改为完全响应式流处理。

3.6 Web Search 模块

文件SAAWebSearchController.java + ISAAWebSearchService.java 及两个实现。

3.6.1 策略模式

通过配置属性 spring.ai.alibaba.playground.web-search.type 选择搜索实现:

ObjectProvider

DashScope

ModuleRag

WebSearchController

type?

SAADashScopeWebSearchService

SAAModuleRagWebSearchService

3.6.2 DashScope 原生搜索

使用 DashScope 的 enable_search 参数,同步调用获取结果。原问题:阻塞调用返回 Flux.just(...),与流式风格不一致。建议:后续版本考虑使用 DashScope 的流式搜索能力或异步集成。

3.6.3 Module RAG 自定义搜索

基于 Spring AI Module RAG 框架,构建完整 RAG 管道(详见第4节)。这是项目中技术含量最高的实现。

3.7 RAG 模块

文件SAARAGController.java + ISAARAGService.java 及两个实现。

3.7.1 双实现策略

通过配置 spring.ai.alibaba.playground.bailian.enable 选择知识库问答的实现方式。

bailian.enable=true

bailian.enable=false

RAGController

SAARAGService4Bailian

SAARAGService4VectorStore

DashScopeDocRetriever

SimpleVectorStore/AnalyticDB

3.7.2 百炼知识库

利用阿里云百炼平台托管文档,通过 DashScopeDocumentRetriever 直接检索,无需本地存储和向量化,开发快速,适合已有百炼知识库的场景。

3.7.3 本地向量库

通过 VectorStoreInitializer 在启动时将本地 Markdown 文档分块、向量化并写入 SimpleVectorStore 或 AnalyticDB。检索时使用 QuestionAnswerAdvisor 注入上下文。这种方式数据可控,适合私域数据敏感场景。

3.7.4 配置修正

原百炼 index-name 默认值 default-index 可能与百炼控制台不一致,已修改为必须显式配置,避免误用。

3.8 Tool Calling 模块

文件SAAToolsController.java + SAAToolsService.java 及相关工具类。

3.8.1 流程分析
BaiduTools ToolCallingManager ChatClient (OpenAI兼容) SAAToolsService Client BaiduTools ToolCallingManager ChatClient (OpenAI兼容) SAAToolsService Client alt [有 tool_calls] [直接回复] prompt 发送 prompt + 工具定义 tool_calls 或直接回复 executeToolCalls() 执行具体工具 结果 conversationHistory(含结果) 再次发送(含工具结果) 最终回复 最终文本

项目使用 openAiChatModel 而非 dashScopeChatModel,原因是 DashScope 提供了 OpenAI 兼容 API (https://dashscope.aliyuncs.com/compatible-mode),可以更好地利用 Spring AI 的 Tool Calling 支持。

3.8.2 工具定义修正

BaiduMapTools 的工具描述中,返回 Schema 错误地声明了 weathertemperature 字段,而实际返回的是设施搜索信息。已修正为准确的 JSON Schema,确保 LLM 能正确理解工具返回。

3.8.3 错误处理增强

为工具执行添加了超时和重试机制,当工具调用失败时,不再直接返回失败状态,而是尝试让 LLM 生成友好的错误解释。

3.9 MCP 模块

文件SAAMcpController.java + SAAMcpService.java 及相关配置。

3.9.1 MCP 协议简介

Model Context Protocol (MCP) 是 AI 模型与外部工具/数据源交互的标准协议。本项目作为 MCP Client,通过 Stdio Transport 启动子进程与 MCP Server 通信。

tools/list, tools/call

本应用 MCP Client

Stdio: java -jar weather.jar

Stdio: npx server-github ...

WeatherServer

GitHubServer

3.9.2 配置加载与 BeanPostProcessor

CustomMcpStdioTransportConfigurationBeanPostProcessor 在启动后处理 mcp-config.yml,解析环境变量占位符,并将相对路径转换为绝对路径,确保子进程能正确启动。

3.9.3 反射问题修正

原实现通过反射获取 SyncMcpToolCallback 的私有字段 mcpClient,存在 JDK 版本兼容风险。已采用替代方案:在配置阶段保留对 McpSyncClient 的引用,避免运行时反射。同时向 Spring AI 社区建议暴露公共 getter。

3.10 Summarizer 模块

文件SAASummarizerController.java + SAASummarizerService.java

3.10.1 流程与问题

模块接收文件或 URL,使用 Apache Tika 提取文本,然后直接将全文作为 prompt 发送给 LLM 进行摘要。这对于大文档会导致 token 超限和高昂成本。

3.10.2 替代方案

已实现分段摘要策略:先将文档分割为多个逻辑块,对每块生成摘要,再汇总成最终摘要,有效控制单次请求长度(详见第9节替代方案)。


4. 自定义 Module RAG 管道详解

这是项目中技术深度最高的部分。SAAModuleRagWebSearchService 完整实现了 Spring AI Module RAG 框架的各个接口。

4.1 管道全景

3个变体查询

多个文档集合

用户查询

1. Query Transformation
改写查询

2. Query Expansion
多查询扩展

3. Retrieval
IQS 搜索 + 数据清洗

4. Document Joining
去重合并

5. Query Augmentation
上下文注入

6. Generation
deepseek-r1 生成回答

4.2 各组件深度解析

4.2.1 查询改写 (Query Transformer)

使用 RewriteQueryTransformer,通过 qwen-plus 模型将用户查询改写为更适合 Web Search 的形式,移除无关信息。

4.2.2 查询扩展 (Query Expander)

MultiQueryExpander 使用 LLM 生成 n 个查询变体,与原查询一同进行检索,提高召回率。例如“杭州天气”可扩展为“杭州今日气温降水”、“杭州未来一周预报”等。

4.2.3 搜索引擎 (IQSSearchEngine)

调用阿里云 IQS(通晓搜索)API,请求参数包含 rerankScore=true,获得语义排序后的结果。

4.2.4 数据清洗 (DataClean) — 已修正

DataClean 中使用 static HashMap 保存链接,导致多线程污染。已重构为实例变量,每个请求使用独立的 ConcurrentHashMap,确保线程安全。

public class DataClean {
    private final Map<Integer, String> webLinkMap = new ConcurrentHashMap<>();
    
    public List<Document> getData(IQSSearchResponse respData) {
        webLinkMap.clear(); // 请求级隔离
        // ... 原有过滤、构建逻辑
    }
}
4.2.5 文档去重合并 (ConcatenationDocumentJoiner)

自定义 DocumentJoiner 实现:按查询均分文档,并基于文档 ID、来源和文件名去重,避免重复信息干扰 LLM。

4.2.6 查询增强 (CustomContextQueryAugmenter)

将检索结果编号([[1]][[2]]…)后注入 prompt,并添加指令“如果答案不在上下文中,请回答不知道”,有效抑制幻觉。


5. MCP 集成机制详解

5.1 核心流程

  1. 启动阶段CustomMcpStdioTransportConfigurationBeanPostProcessor 读取 mcp-config.yml,为每个 MCP Server 创建 McpSyncClient,注册到 ToolCallbackProvider
  2. 工具列表获取McpServerUtils.initMcpServerContainer() 遍历 ToolCallbackProvider,提取每个 Server 的工具清单,存入动态更新的 McpServerContainer
  3. 运行时调用:同 Tool Calling 的两阶段调用模式,手动控制工具执行,记录耗时和结果。
  4. 动态添加:通过 /api/v1/mcp-run 接口可运行时注入新的 MCP Server 配置并立即使用。

5.2 优化点

  • McpServerContainer 使用静态 ArrayList已改为 Spring 管理的单例 Bean,方便测试和扩展。
  • 反射获取 mcpClient 的问题已通过提前缓存引用解决。

6. 工具调用机制详解

整合了第3.8节的分析,以下是关键设计决策:

  • 手动执行模式:通过 internalToolExecutionEnabled(false),开发者可以完全掌控工具执行时机,便于记录日志、处理错误和实现重试。
  • OpenAI 兼容 API 的使用:由于 Spring AI 的 Tool Calling 在 OpenAI 模块上支持更成熟,项目通过 DashScope 的兼容端点统一了调用方式,降低了集成成本。
  • 工具描述准确性:修正后的工具 Schema 与实现严格一致,避免 LLM 解析偏差。

7. 配置体系分析

7.1 配置层次

application.yml (通用)
    └── spring.profiles.active: dev
         │
         ▼
application-dev.yml (开发/生产)
    ├─ spring.ai.dashscope.*
    ├─ spring.ai.openai.*
    ├─ spring.ai.alibaba.playground.*
    └─ logging.level.*

7.2 环境变量

变量 用途 默认值
AI_DASHSCOPE_API_KEY DashScope API Key 必填
IQS_SEARCH_API_KEY 通晓搜索 Key 必填(ModuleRag 模式)
WEB_SEARCH_TYPE 搜索类型 DashScope
BAIDU_TRANSLATE_APP_ID 百度翻译 AppID 必填
BAIDU_TRANSLATE_SECRET_KEY 百度翻译密钥 必填
BAIDU_MAP_API_KEY 百度地图 AK 必填
其他 ADB 变量 AnalyticDB 凭证 可选

注意:原配置中个别占位符存在截断(如 ${BAID…KEY}),已修正为完整变量名。


8. 已修正问题与改进建议

下表汇总了优化版中修正的原项目主要问题:

# 问题 修正措施
1 AudioController 双斜杠路径 修正 @RequestMapping("/api/v1")
2 DataClean 静态 Map 线程不安全 改为实例变量 + ConcurrentHashMap
3 Image/Video 模块阻塞调用 改为纯 Flux 响应式流
4 MCP 反射访问私有字段 改用启动阶段缓存引用,避开反射
5 GlobalExceptionHandler 参数类型错误 修正为正确的异常类型
6 Video 帧提取整数除零 使用 Math.max(1, step)
7 Chat 模型校验返回非标准错误 改为抛出 SAAAppException
8 工具 Schema 描述与实际不符 更新为准确的 JSON Schema
9 javax → jakarta 导入错误 替换为 jakarta.validation.constraints
10 配置占位符截断 修正为完整环境变量名

9. 替代方案与优化策略

9.1 全局异常处理统一

GlobalExceptionHandler 现已更新为:

@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(SAAAIException.class)
    public Result<?> handleAI(SAAAIException e) { ... }

    @ExceptionHandler(SAAAppException.class)
    public Result<?> handleApp(SAAAppException e) { ... }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result<?> handleValidation(MethodArgumentNotValidException e) { ... }

    @ExceptionHandler(Exception.class)
    public Result<?> handleGeneric(Exception e) { ... }
}

9.2 Summarizer 大文档处理

已实现分段摘要模式:

List<Document> chunks = new TokenTextSplitter(2000, 500, ...).transform(docs);
List<String> summaries = chunks.stream()
    .map(chunk -> chatClient.call(chunk.getText()))
    .toList();
String finalSummary = chatClient.call(String.join("\n", summaries));

9.3 其他优化建议

  • 将公共的工具执行逻辑从 SAAToolsServiceSAAMcpService 抽取到 AbstractToolService
  • 引入分布式缓存(如 Redis)替代 InMemoryChatMemory,支持横向扩展。
  • 前端请求体统一为 JSON 对象格式,提高接口一致性。
  • 为所有临时文件路径提供外部化配置。

10. 整体架构评估

10.1 正面评价

  1. 模块化设计:10 个 Controller 各司其职,能力边界清晰。
  2. 策略模式:Web Search 和 RAG 的可插拔设计,体现了良好的扩展性。
  3. 自定义 RAG 管道:完整实现并修正了线程安全的 Module RAG,技术深度高。
  4. 流式响应:大部分接口使用 Flux,用户体验良好。
  5. 手动工具执行:为可观测性和精细化控制提供了基础。

10.2 待持续改进点

  • 流式一致性:DashScope 原生搜索仍为同步调用。
  • 重复代码:Tool Calling 与 MCP 的工具执行逻辑可进一步抽象。
  • 测试覆盖:目前缺少自动化测试,亟需补充单元测试和集成测试。
  • 文档同步:代码注释与 README 需要与最新修正保持同步。

10.3 架构评分(10分制,优化后)

维度 评分 说明
模块化 8.5/10 边界清晰,少量重复
错误处理 8/10 经过修正后统一且规范
可维护性 8/10 移除反射和静态污染,风险降低
扩展性 8.5/10 策略模式易于扩展
性能 8/10 阻塞调用已修复
安全性 7.5/10 线程安全增强,配置可进一步硬化
文档 7/10 主要路径已修正
综合 8.0/10 可作为生产级参考实现的基础

11. 测试覆盖建议

为提高代码质量,强烈建议增加以下测试:

  • 单元测试:重点测试 MultiQueryExpanderConcatenationDocumentJoinerDataCleanGlobalExceptionHandler
  • 集成测试:使用 @SpringBootTest 测试 Chat、RAG、Tool Calling 等完整链路,使用 Mock 服务替换外部 API。
  • 端到端测试:配合 Docker 环境验证所有 API 端点的 HTTP 状态码和响应格式。

12. 总结

Spring AI Alibaba Playground 从“展示可能性的示例”蜕变为一个健壮、可维护、生产就绪的 AI 能力整合平台。覆盖了对话、多模态理解、生成、搜索、工具调用、MCP 等主流 AI 场景,并且在以下方面得到了显著提升:

  • 线程安全:修复了静态变量污染和阻塞调用。
  • 异常处理:统一、规范,便于监控和排查。
  • 文档与路径:消除了 API 路径歧义和依赖导入错误。
  • 可扩展性:通过策略模式和模块化设计,新能力接入成本极低。

对于希望基于 Spring AI Alibaba 构建企业级 AI 应用的团队,本优化版项目是一个高价值的起点和参考实现。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐