Spring AI Alibaba入门教程

Spring AI Alibaba入门教程

一、什么是 Spring AI Alibaba

Spring AI Alibaba 是阿里巴巴推出的企业级 AI 应用开发框架,致力于为 Java 开发者提供构建 AI 应用的完整解决方案。它主要解决两大核心问题:

  1. 统一接口:消除不同 AI 服务的 API 差异,支持阿里云通义千问、智谱 AI 等多种模型。
  2. 企业级生态整合:提供企业级的观测、评估、MCP 集成等功能。

🛠️ 核心功能

Spring AI Alibaba 具备以下核心能力:

1. 基于图的多智能体框架

  • 通过 Spring AI Alibaba Graph 构建工作流和多智能体应用
  • 支持可视化调试和 Dify DSL 集成

2. 企业级 AI 生态集成

  • 支持阿里云百炼平台、ARMS 和 Langfuse 等观测产品
  • 提供 Nacos MCP Registry 等企业级组件

3. Plan-Act 智能体产品

  • JManus:企业级智能体实现
  • DeepResearch:研究和报告智能体

二、快速入门

效果展示

我们要完成的案例:智能天气预报助手,提供准确、便捷的天气信息服务。
在这里插入图片描述

环境搭建

创建项目

使用 IDEA 创建 Spring Boot 项目:

  • JDK 17+
  • Spring Boot 3.2.x 或 3.3.x
  • 勾选 Spring WebSpring Reactive Web

添加依赖

pom.xml 中添加:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-bom</artifactId>
            <version>1.0.0.2</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>com.alibaba.cloud.ai</groupId>
        <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
    </dependency>
</dependencies>

配置文件

创建 application.yml

spring:
  ai:
    dashscope:
      api-key: ${DASHSCOPE_API_KEY}
      chat:
        options:
          model: qwen-turbo

环境变量配置:

DASHSCOPE_API_KEY=你的阿里云API_KEY

后端代码编写

创建 WeatherController

package com.example.weather.controller;

import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;

@RestController
@RequestMapping("/weather")
public class WeatherController {

    private final DashScopeChatModel chatModel;

    private static final String SYSTEM_PROMPT = """
            你是一个专业的天气预报机器人,擅长:
            1. 解答天气相关的问题
            2. 提供天气预报建议
            3. 解释天气现象
            4. 提供合适的穿衣建议
            5. 分析天气对出行的影响

            请始终以专业、友好的口吻回答问题。如果问题与天气无关,请礼貌地提醒用户你是一个天气预报助手。
            """;

    public WeatherController(DashScopeChatModel chatModel) {
        this.chatModel = chatModel;
    }

    /**
     * 生成单次天气相关回复
     */
    @GetMapping("/generate")
    public String generate(@RequestParam(value = "message") String message) {
        String prompt = SYSTEM_PROMPT + "\n用户问题:" + message;
        return this.chatModel.call(prompt);
    }

    /**
     * 生成流式天气相关回复
     */
    @GetMapping(value = "/generateStream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> generateStream(@RequestParam(value = "message") String message) {
        String promptText = SYSTEM_PROMPT + "\n用户问题:" + message;
        var prompt = new Prompt(new UserMessage(promptText));
        
        Flux<ChatResponse> stream = this.chatModel.stream(prompt);
        return stream.map(response -> {
            if (response.getResult() != null && response.getResult().getOutput() != null) {
                return response.getResult().getOutput().getContent();
            }
            return "";
        });
    }
}

前端代码编写

创建 src/main/resources/static/weather.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>智能天气预报助手</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
            background: linear-gradient(120deg, #89f7fe 0%, #66a6ff 100%);
            height: 100vh;
        }

        .chat-container {
            max-width: 800px;
            margin: 20px auto;
            height: calc(100vh - 40px);
            display: flex;
            flex-direction: column;
            background: rgba(255, 255, 255, 0.95);
            border-radius: 12px;
            box-shadow: 0 8px 32px rgba(31, 38, 135, 0.15);
            padding: 20px;
        }

        .chat-header {
            text-align: center;
            padding: 20px 0;
            border-bottom: 1px solid #eee;
            margin-bottom: 20px;
        }

        .chat-header h1 {
            color: #1a73e8;
            font-size: 24px;
            margin-bottom: 10px;
        }

        .messages-container {
            flex: 1;
            overflow-y: auto;
            padding: 20px;
            background: rgba(255, 255, 255, 0.8);
            border-radius: 8px;
            margin-bottom: 20px;
        }

        .message {
            margin-bottom: 20px;
            padding: 15px;
            border-radius: 8px;
            max-width: 80%;
        }

        .user-message {
            background: #e3f2fd;
            margin-left: auto;
            color: #1565c0;
        }

        .assistant-message {
            background: #f5f5f5;
            margin-right: auto;
            color: #333;
        }

        .input-container {
            position: relative;
            padding: 20px;
            background: rgba(255, 255, 255, 0.9);
            border-radius: 8px;
        }

        #message-input {
            width: 100%;
            padding: 12px;
            border: 2px solid #e0e0e0;
            border-radius: 8px;
            font-size: 16px;
            resize: none;
            height: 50px;
        }

        #send-button {
            position: absolute;
            right: 30px;
            bottom: 30px;
            padding: 8px 20px;
            background: #1a73e8;
            color: white;
            border: none;
            border-radius: 20px;
            cursor: pointer;
        }

        .example-questions {
            margin-top: 10px;
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
        }

        .example-question {
            background: #e3f2fd;
            color: #1565c0;
            padding: 8px 16px;
            border-radius: 16px;
            font-size: 14px;
            cursor: pointer;
        }

        .typing {
            display: inline-block;
        }

        .typing span {
            display: inline-block;
            width: 6px;
            height: 6px;
            background: #666;
            border-radius: 50%;
            margin: 0 2px;
            animation: typing 1s infinite;
        }

        @keyframes typing {
            0%, 100% { transform: translateY(0); }
            50% { transform: translateY(-4px); }
        }
    </style>
</head>
<body>
    <div class="chat-container">
        <div class="chat-header">
            <h1>🌤️ 智能天气预报助手</h1>
            <p>基于 Spring AI Alibaba 构建</p>
        </div>
        
        <div class="messages-container" id="messages">
            <div class="message assistant-message">
                您好!我是智能天气预报助手,可以为您提供天气预报、穿衣建议和出行建议。
            </div>
        </div>

        <div class="example-questions">
            <div class="example-question" onclick="askExample(this)">北京今天天气怎么样?</div>
            <div class="example-question" onclick="askExample(this)">今天适合户外运动吗?</div>
            <div class="example-question" onclick="askExample(this)">明天要出门,需要带伞吗?</div>
        </div>

        <div class="input-container">
            <textarea id="message-input" placeholder="请输入您的天气相关问题..." 
                onkeydown="if(event.keyCode === 13 && !event.shiftKey) { event.preventDefault(); sendMessage(); }"></textarea>
            <button id="send-button" onclick="sendMessage()">发送</button>
        </div>
    </div>

    <script>
        const messagesContainer = document.getElementById('messages');
        const messageInput = document.getElementById('message-input');
        const sendButton = document.getElementById('send-button');

        function askExample(element) {
            messageInput.value = element.textContent;
            sendMessage();
        }

        function createMessageElement(content, isUser) {
            const messageDiv = document.createElement('div');
            messageDiv.className = `message ${isUser ? 'user-message' : 'assistant-message'}`;
            messageDiv.textContent = content;
            return messageDiv;
        }

        function createTypingIndicator() {
            const typingDiv = document.createElement('div');
            typingDiv.className = 'message assistant-message';
            typingDiv.innerHTML = '正在查询天气信息<div class="typing"><span></span><span></span><span></span></div>';
            return typingDiv;
        }

        async function sendMessage() {
            const message = messageInput.value.trim();
            if (!message) return;

            messageInput.disabled = true;
            sendButton.disabled = true;

            messagesContainer.appendChild(createMessageElement(message, true));
            messageInput.value = '';

            const typingIndicator = createTypingIndicator();
            messagesContainer.appendChild(typingIndicator);
            messagesContainer.scrollTop = messagesContainer.scrollHeight;

            try {
                const response = await fetch(`/weather/generate?message=${encodeURIComponent(message)}`);
                const data = await response.text();

                typingIndicator.remove();
                messagesContainer.appendChild(createMessageElement(data, false));
                messagesContainer.scrollTop = messagesContainer.scrollHeight;
            } catch (error) {
                console.error('API调用错误:', error);
                typingIndicator.remove();
                const errorMessage = document.createElement('div');
                errorMessage.className = 'message assistant-message';
                errorMessage.textContent = '抱歉,发生了错误,请稍后重试。';
                messagesContainer.appendChild(errorMessage);
            } finally {
                messageInput.disabled = false;
                sendButton.disabled = false;
                messageInput.focus();
            }
        }

        window.onload = () => {
            messageInput.focus();
        };
    </script>
</body>
</html>

三、打字机效果实现

流式响应版本

创建 src/main/resources/static/stream.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>智能天气预报助手 - 流式响应版</title>
    <style>
        /* 样式与普通版本相同 */
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
            background: linear-gradient(120deg, #89f7fe 0%, #66a6ff 100%);
            height: 100vh;
        }

        .chat-container {
            max-width: 800px;
            margin: 20px auto;
            height: calc(100vh - 40px);
            display: flex;
            flex-direction: column;
            background: rgba(255, 255, 255, 0.95);
            border-radius: 12px;
            box-shadow: 0 8px 32px rgba(31, 38, 135, 0.15);
            padding: 20px;
        }

        .chat-header {
            text-align: center;
            padding: 20px 0;
            border-bottom: 1px solid #eee;
            margin-bottom: 20px;
        }

        .chat-header h1 {
            color: #1a73e8;
            font-size: 24px;
            margin-bottom: 10px;
        }

        .messages-container {
            flex: 1;
            overflow-y: auto;
            padding: 20px;
            background: rgba(255, 255, 255, 0.8);
            border-radius: 8px;
            margin-bottom: 20px;
        }

        .message {
            margin-bottom: 20px;
            padding: 15px;
            border-radius: 8px;
            max-width: 80%;
            white-space: pre-wrap;
            word-wrap: break-word;
        }

        .user-message {
            background: #e3f2fd;
            margin-left: auto;
            color: #1565c0;
        }

        .assistant-message {
            background: #f5f5f5;
            margin-right: auto;
            color: #333;
        }

        .input-container {
            position: relative;
            padding: 20px;
            background: rgba(255, 255, 255, 0.9);
            border-radius: 8px;
        }

        #message-input {
            width: 100%;
            padding: 12px;
            border: 2px solid #e0e0e0;
            border-radius: 8px;
            font-size: 16px;
            resize: none;
            height: 50px;
        }

        #send-button {
            position: absolute;
            right: 30px;
            bottom: 30px;
            padding: 8px 20px;
            background: #1a73e8;
            color: white;
            border: none;
            border-radius: 20px;
            cursor: pointer;
        }

        .example-questions {
            margin-top: 10px;
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
        }

        .example-question {
            background: #e3f2fd;
            color: #1565c0;
            padding: 8px 16px;
            border-radius: 16px;
            font-size: 14px;
            cursor: pointer;
        }

        .typing {
            display: inline-block;
        }

        .typing span {
            display: inline-block;
            width: 6px;
            height: 6px;
            background: #666;
            border-radius: 50%;
            margin: 0 2px;
            animation: typing 1s infinite;
        }

        @keyframes typing {
            0%, 100% { transform: translateY(0); }
            50% { transform: translateY(-4px); }
        }
    </style>
</head>
<body>
    <div class="chat-container">
        <div class="chat-header">
            <h1>🌤️ 智能天气预报助手</h1>
            <p>基于 Spring AI Alibaba 构建 - 流式响应版</p>
        </div>
        
        <div class="messages-container" id="messages">
            <div class="message assistant-message">
                您好!我是智能天气预报助手(流式响应版),可以为您提供天气预报、穿衣建议和出行建议。
            </div>
        </div>

        <div class="example-questions">
            <div class="example-question" onclick="askExample(this)">北京今天天气怎么样?</div>
            <div class="example-question" onclick="askExample(this)">今天适合户外运动吗?</div>
            <div class="example-question" onclick="askExample(this)">明天要出门,需要带伞吗?</div>
        </div>

        <div class="input-container">
            <textarea id="message-input" placeholder="请输入您的天气相关问题..." 
                onkeydown="if(event.keyCode === 13 && !event.shiftKey) { event.preventDefault(); sendMessage(); }"></textarea>
            <button id="send-button" onclick="sendMessage()">发送</button>
        </div>
    </div>

    <script>
        const messagesContainer = document.getElementById('messages');
        const messageInput = document.getElementById('message-input');
        const sendButton = document.getElementById('send-button');

        function askExample(element) {
            messageInput.value = element.textContent;
            sendMessage();
        }

        function createMessageElement(content, isUser) {
            const messageDiv = document.createElement('div');
            messageDiv.className = `message ${isUser ? 'user-message' : 'assistant-message'}`;
            messageDiv.textContent = content;
            return messageDiv;
        }

        function createTypingIndicator() {
            const typingDiv = document.createElement('div');
            typingDiv.className = 'message assistant-message';
            typingDiv.innerHTML = '正在查询天气信息<div class="typing"><span></span><span></span><span></span></div>';
            return typingDiv;
        }

        async function sendMessage() {
            const message = messageInput.value.trim();
            if (!message) return;

            messageInput.disabled = true;
            sendButton.disabled = true;

            messagesContainer.appendChild(createMessageElement(message, true));
            messageInput.value = '';

            const typingIndicator = createTypingIndicator();
            messagesContainer.appendChild(typingIndicator);
            messagesContainer.scrollTop = messagesContainer.scrollHeight;

            try {
                const assistantMessage = document.createElement('div');
                assistantMessage.className = 'message assistant-message';

                const eventSource = new EventSource(`/weather/generateStream?message=${encodeURIComponent(message)}`);

                typingIndicator.remove();
                messagesContainer.appendChild(assistantMessage);

                eventSource.onmessage = function (event) {
                    assistantMessage.textContent += event.data;
                    messagesContainer.scrollTop = messagesContainer.scrollHeight;
                };

                eventSource.onerror = function (error) {
                    console.error('EventSource错误:', error);
                    eventSource.close();

                    if (!assistantMessage.textContent) {
                        assistantMessage.textContent = '抱歉,发生了错误,请稍后重试。';
                    }

                    messageInput.disabled = false;
                    sendButton.disabled = false;
                    messageInput.focus();
                };

            } catch (error) {
                console.error('API调用错误:', error);
                const errorMessage = document.createElement('div');
                errorMessage.className = 'message assistant-message';
                errorMessage.textContent = '抱歉,发生了错误,请稍后重试。';
                messagesContainer.appendChild(errorMessage);

                messageInput.disabled = false;
                sendButton.disabled = false;
                messageInput.focus();
            }
        }

        window.onload = () => {
            messageInput.focus();
        };
    </script>
</body>
</html>

四、常用参数说明

Spring AI Alibaba 提供了丰富的配置选项:

属性描述默认值
spring.ai.dashscope.api-key阿里云API密钥-
spring.ai.dashscope.base-urlAPI基础URLhttps://dashscope.aliyuncs.com
spring.ai.dashscope.chat.enabled是否启用聊天模型true
spring.ai.dashscope.chat.options.model模型名称qwen-turbo
spring.ai.dashscope.chat.options.temperature温度参数0.7
spring.ai.dashscope.chat.options.top-p核采样参数1.0

参数配置示例

spring:
  ai:
    dashscope:
      api-key: ${DASHSCOPE_API_KEY}
      chat:
        enabled: true
        options:
          model: qwen-turbo           # 可选:qwen-turbo, qwen-plus, qwen-max
          temperature: 0.7            # 控制创造性
          top-p: 0.8                 # 核采样参数
温度和 topP 到底有什么区别?

举例子来对比一下温度和 topP。

温度:

当温度=0.5 时,AI 写诗只会用经典押韵格式;

当温度=2.0 时,AI 可能把"月亮"写成"会飞的咸蛋黄"

这个参数本质是在"稳定性"和"创造性"之间找平衡,温度越低越像教科书,温度越高越像科幻小说。

topP:

想象你在点外卖,模型要推荐菜品:

此时就算温度很高,只要 topP 压得低(比如 0.9),模型实际只能从"前三热门"里选,不会跳出厨房小白的常规选项。

为什么同时用这两个参数?

就像开车时既要控制油门(温度)又要控制方向盘(topP)。

五、总结

通过 Spring AI Alibaba,我们成功构建了一个智能天气预报助手,实现了:

  1. 快速集成:简单依赖配置即可接入阿里云通义千问
  2. 流式响应:使用 WebFlux 实现打字机效果
  3. 企业级特性:支持参数调优、错误处理
  4. 灵活配置:支持多种模型和参数配置

Spring AI Alibaba 提供了完整的企业级 AI 应用开发解决方案,是构建智能应用的理想选择。

参考资源

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值