002-Spring AI Alibaba Prompt 功能完整案例

本案例将引导您一步步构建一个 Spring Boot 应用,演示如何利用 Spring AI Alibaba 的 Prompt 模板功能,实现角色扮演和基于上下文的智能问答。

1. 案例目标

我们将创建一个包含两个核心功能的 Web 应用:

  1. AI 角色扮演 (/example/ai/roles):通过系统提示词模板,让 AI 扮演一个具有特定名字和声音风格的角色,并以该角色的口吻回答问题。
  2. 上下文问答 (/prompt/ai/stuff):根据请求参数,决定是否将一篇关于"冰壶"的维基百科文档作为上下文"填充"到提示词中,让 AI 基于该文档内容回答相关问题。

2. 技术栈与核心依赖

  • Spring Boot 3.x
  • Spring AI Alibaba (用于对接阿里云 DashScope 通义大模型)
  • Maven (项目构建工具)

pom.xml 中,你需要引入以下核心依赖:

复制代码
<dependencies>
    <!-- Spring AI Alibaba 核心启动器,集成 DashScope -->
    <dependency>
        <groupId>com.alibaba.cloud.ai</groupId>
        <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
        <version>1.0.0-M2</version> <!-- 请使用最新版本 -->
    </dependency>
    <!-- Spring Web 用于构建 RESTful API -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>
<!-- 添加 Spring Boot 和 Spring Cloud 的版本管理 -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>3.3.1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>2023.0.1.2</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

3. 项目配置

src/main/resources/application.yml 文件中,配置你的 DashScope API Key。

复制代码
spring:
  ai:
    dashscope:
      api-key: ${DASHSCOPE_API_KEY} # 建议使用环境变量,更安全
      # 可选:指定使用的模型,默认为 qwen-turbo
      chat:
        options:
          model: qwen-plus

重要提示 :请将 DASHSCOPE_API_KEY 环境变量设置为你从阿里云获取的有效 API Key。你也可以直接将其写在配置文件中,但这不推荐用于生产环境。

4. 准备 Prompt 模板文件

src/main/resources 目录下创建以下文件结构:

复制代码
src/main/resources/
├── docs/
│   └── wikipedia-curling.md
└── prompts/
    ├── system-message.st
    └── qa-prompt.st

4.1 docs/wikipedia-curling.md

这是我们将用作上下文的文档内容。

复制代码
# Curling
Curling is a sport in which players slide stones on a sheet of ice toward a target area which is segmented into four concentric circles. It is related to bowls, boules and shuffleboard. Two teams, each with four players, take turns sliding heavy, polished granite stones, also called rocks, across the ice curling sheet toward the house, a circular target marked on the ice. Each team has eight stones. The purpose is to accumulate the highest score for a game; points are scored for the stones resting closest to the centre of the house at the conclusion of each end, which is completed when both teams have thrown all of their stones. A game usually consists of eight or ten ends.
The curler can induce a curved path by causing the stone to slowly turn as it slides, and the path of the rock may be further influenced by two sweepers with brooms who accompany it as it slides down the sheet, using the brooms to alter the state of the ice in front of the stone. A great deal of strategy and teamwork goes into choosing the ideal path and placement of a stone for each situation, and the skills of the curlers determine how close to the desired result the stone will achieve. This gives curling its nickname of "chess on ice".

4.2 prompts/system-message.st

系统提示词模板,用于定义 AI 的角色。

复制代码
你是一个乐于助人的 AI 助手。你的名字是 {name},你的说话风格是 {voice}。
请始终保持这个角色设定与用户进行对话。

4.3 prompts/qa-prompt.st

问答提示词模板,用于注入上下文和问题。

复制代码
请根据提供的上下文信息来回答用户的问题。如果上下文中没有相关信息,请说你不知道。
上下文信息:
---
{context}
---
用户问题: {question}

5. 编写 Java 代码

5.1 RoleController.java

实现 AI 角色扮演功能。

复制代码
package com.example.demo.controller;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.SystemPromptTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/example/ai")
public class RoleController {
    private final ChatClient chatClient;
    // 注入系统提示词模板文件
    @Value("classpath:/prompts/system-message.st")
    private Resource systemResource;
    // 构造函数注入 ChatClient
    public RoleController(ChatClient.Builder chatClientBuilder) {
        this.chatClient = chatClientBuilder.build();
    }
    /**
     * 生成角色扮演对话
     * @param message 用户消息
     * @param name AI 角色名字
     * @param voice AI 说话风格
     * @return 流式响应
     */
    @GetMapping("/roles")
    public Flux<String> generate(String message, String name, String voice) {
        // 1. 创建用户消息
        UserMessage userMessage = new UserMessage(message);
        // 2. 使用系统提示词模板并填充变量
        SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemResource);
        Message systemMessage = systemPromptTemplate.createMessage(
                Map.of("name", name, "voice", voice)
        );
        // 3. 调用大模型,传入系统消息和用户消息
        return chatClient.prompt(new Prompt(List.of(systemMessage, userMessage)))
                .stream()
                .content();
    }
}

5.2 StuffController.java

实现基于上下文的问答功能。

复制代码
package com.example.demo.controller;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/prompt/ai")
public class StuffController {
    private final ChatClient chatClient;
    // 注入要填充的文档资源
    @Value("classpath:/docs/wikipedia-curling.md")
    private Resource docsToStuffResource;
    // 注入问答提示词模板
    @Value("classpath:/prompts/qa-prompt.st")
    private Resource qaPromptResource;
    public StuffController(ChatClient.Builder chatClientBuilder) {
        this.chatClient = chatClientBuilder.build();
    }
    /**
     * 根据上下文回答问题
     * @param message 用户问题
     * @param stuffit 是否填充上下文
     * @return 流式响应
     */
    @GetMapping(value = "/stuff")
    public Flux<String> completion(String message, boolean stuffit) {
        // 1. 创建提示词模板
        PromptTemplate promptTemplate = new PromptTemplate(qaPromptResource);
        Map<String, Object> map = new HashMap<>();
        map.put("question", message);
        // 2. 根据参数决定是否填充上下文信息
        if (stuffit) {
            map.put("context", docsToStuffResource);
        } else {
            map.put("context", ""); // 不填充时,传入空字符串
        }
        // 3. 创建最终的 Prompt 并调用模型
        return chatClient.prompt(promptTemplate.create(map))
                .stream()
                .content();
    }
}

6. 运行与测试

  1. 启动应用:运行你的 Spring Boot 主程序。
  2. 使用浏览器或 API 工具(如 Postman, curl)进行测试

测试 1:AI 角色扮演

访问以下 URL,让 AI 扮演一个名叫"阿尔法"、声音"沉稳而富有磁性"的助手,询问它今天天气如何。

复制代码
http://localhost:8080/example/ai/roles?message=今天天气怎么样?&name=阿尔法&voice=沉稳而富有磁性

预期响应(流式输出)

你好,我是阿尔法。关于今天的天气,我无法直接获取实时信息,建议您查看当地的天气预报应用。不过,无论天气如何,都希望您有愉快的一天。

测试 2:带上下文的问答

访问以下 URL,开启上下文填充,并提问"冰壶运动有几个队员?"。

复制代码
http://localhost:8080/prompt/ai/stuff?message=冰壶运动有几个队员?&stuffit=true

预期响应(流式输出)

根据提供的上下文信息,冰壶运动有两支队伍,每支队伍有四名队员。

测试 3:不带上下文的问答

访问相同的 URL,但将 stuffit 设为 false,再问同样的问题。

复制代码
http://localhost:8080/prompt/ai/stuff?message=冰壶运动有几个队员?&stuffit=false

预期响应(流式输出)

根据提供的上下文信息,我没有找到关于"冰壶运动有几个队员"的相关信息,所以我无法回答这个问题。

7. 实现思路与扩展建议

实现思路

本案例的核心思想是**"模板与数据分离"**。我们将固定的提示词结构(模板)与动态变化的变量(如角色名、上下文文档)分离开来。这使得:

  • 可维护性高 :修改提示词风格只需编辑 .st 文件,无需改动 Java 代码。
  • 复用性强:同一个模板可以用于不同的数据填充场景。
  • 功能强大:通过动态注入上下文,为构建检索增强生成(RAG)应用奠定了基础。

扩展建议

  • 多模板管理:可以设计一个更复杂的模板管理器,根据业务场景动态选择不同的模板。
  • 模板缓存:对于频繁使用的模板,可以增加缓存层,避免每次请求都从磁盘加载,提升性能。
  • A/B 测试:通过配置或请求头,动态切换不同版本的提示词模板,用于效果对比和优化。
  • 集成向量数据库 :将 StuffController 中的静态文档替换为从向量数据库(如 Milvus, Chroma)中检索到的相关文本片段,构建真正的 RAG 系统。
相关推荐
Giser探索家3 小时前
无人机数字资产采集技术架构与实践:从多维度感知到云端化建模的实现路径
大数据·人工智能·算法·计算机视觉·分类·无人机
星星落进兜里3 小时前
Spring全家桶面试题, 只补充细节版本
java·后端·spring
飞飞是甜咖啡3 小时前
读论文AI prompt
人工智能·prompt
GIS数据转换器3 小时前
基于GIS的智慧畜牧数据可视化监控平台
人工智能·安全·信息可视化·无人机·智慧城市·制造
千年奇葩3 小时前
Unity性能优化之:利用CUDA加速Unity实现大规模并行计算。从环境搭建到实战案例
c++·人工智能·unity·游戏引擎·cuda
三无少女指南3 小时前
分享一个自用的AI Coding Prompt
prompt
攻城狮7号3 小时前
蚂蚁开源高性能扩散语言模型框架dInfe,推理速度提升十倍
人工智能·dinfer·扩散语言模型·蚂蚁开源模型
LONGZETECH3 小时前
【龙泽科技】汽车电子电气与空调舒适系统技术1+X仿真教学软件(1.3.2 -中级)【威朗&科鲁兹】
人工智能·科技·汽车·汽车仿真教学软件·汽车教学软件
机器之心3 小时前
为什么95%的智能体都部署失败了?这个圆桌讨论出了一些常见陷阱
人工智能·openai