01-第一个AI程序

  • 教程代码:https://github.com/mszlu521/spring-ai-alibaba

  • 教程制作:码神之路(https://www.mszlu.com/docs/ai/msai/01.html)

  • AI Agent实战教程:https://www.mszlu.com/docs/ai/msai/01.html

    • 提供Go和Java版本,目前Go版本已经完结

1. 现代AI Agent架构的演进

现代AI系统的起点是大语言模型(LLM)。它的本质是一个通过海量文本训练得到的"概率预测机器":基于前文语境,预测下一个最可能出现的token。这种机制赋予了它惊人的语言理解和逻辑推理能力,但也带来了根本性的局限:模型是"静态"的——训练完成后知识就定格在某一时间点,且只能进行"文本进、文本出"的运算,无法直接操作外部世界。

当我们要求AI完成"查询今日股价并分析"这类任务时,矛盾就显现了:模型"想得出"分析思路,但"做不到"实时查询。这正是Tool Calling(工具调用) 机制出现的原点。

1.1.1 Tool Calling:打破模型的"封闭牢笼"

Tool Calling解决的核心问题是:如何让大模型从"纸上谈兵"转向"动手实践"

其实现逻辑非常巧妙:我们不再只是向模型提问,而是同时告诉它"我为你准备了哪些工具"(如股票API、计算器、搜索引擎),并用结构化格式(如JSON Schema)描述每个工具的用途和参数。当大模型推理时,它会判断当前任务是否需要外部工具——如果需要,它不是直接回答问题,而是输出一段调用指令(例如:"我需要调用get_stock_price,参数ticker为AAPL")。系统接收到这个指令后,实际执行工具,并将结果再次注入上下文,让模型基于真实数据给出最终回答。

这就像给一位智者配上了手脚:他仍然是决策者,但可以通过工具触达物理世界。然而,随着工具数量的增加,新的痛苦诞生了——每个API的接口格式、认证方式、错误处理都各不相同。开发者需要为每个工具写大量的适配代码,这催生了标准化需求。

1.1.2 MCP:工具世界的"通用插座"

MCP(Model Context Protocol) 的出现,本质上是为了解决工具生态的碎片化问题

在没有MCP之前,如果你开发了一个让AI操作Photoshop的工具,你需要为ChatGPT、Claude、Gemini分别开发不同的插件适配层。MCP借鉴了USB接口的理念:它定义了一套标准协议,规定工具如何描述自己的能力(Schema)、如何建立安全连接、如何传输上下文。任何支持MCP的AI应用,都能即插即用地使用任何支持MCP的工具,无需重复开发。

这极大地扩展了AI的行动边界——从本地文件系统到企业数据库,从邮件客户端到云服务,AI Agent获得了一个标准化的"数字四肢"。

1.1.3 RAG:为模型接上"外部大脑"

由于大模型是静态的,所以当它在面对未知问题时,它倾向于"自信地编造"而非坦诚不知。当企业试图将AI应用于需要准确事实的场景(如医疗咨询、法律分析)时,就成为了致命的缺陷。

这正是 RAG(Retrieval-Augmented Generation,检索增强生成) 技术诞生的背景。

RAG解决的核心问题是:如何让大模型在不重新训练的情况下,获取训练数据之外的准确知识?

其工作原理类似于"开卷考试":当用户提问时,系统首先通过嵌入模型(Embedding Model) 将问题转化为向量,在向量数据库中检索语义相关的文档片段(这些文档可以是企业内部的最新规章制度、刚发布的论文、或实时更新的产品手册)。检索到的内容被注入到Prompt的上下文中,作为"参考资料"呈现给大模型,模型基于此进行生成回答。

这带来了三重革命性价值:

  • 知识时效性:无需重新训练模型,只需更新外部知识库,AI就能"学会"新信息;
  • 可解释性:答案可以标注来源出处,解决了"黑盒"信任问题;
  • 成本效益:相比微调(Fine-tuning)整个大模型,维护向量数据库的成本几乎可以忽略不计。

然而,RAG的本质仍然是**"被动检索、主动回答"——它让模型从"背诵者"变成了"查阅者",但仍局限于文本生成。当任务需要主动操作外部系统**(如发送邮件、预订酒店、执行代码)时,仍旧需要Tool Calling的技术支持。

1.1.3 Agent Skills:程序性知识的"可移植容器"

当我们解决了RAG(知识检索)和Tool Calling(工具使用)后,AI系统面临一个新的隐性痛点:模型虽然能查资料、调工具,但缺乏"做事的方法论"——即程序性知识(Procedural Knowledge)

举个例子:模型可能知道Python语法(显性知识),也知道如何调用数据库API(工具能力),但它不知道"你们公司特定的代码审查流程是什么步骤"、"数据分析报告的标准模板是什么格式"、"遇到异常时应该按什么顺序排查"。这些组织特定的、流程性的、最佳实践类的知识,无法通过RAG的简单文本检索获得,也无法通过MCP的工具接口传递。

Agent Skills 正是为了解决**"agent缺乏完成任务所需的上下文和程序性知识"**而生。它由Anthropic提出并开源,核心定义是:Skills是包含指令、脚本和资源的文件夹,作为可移植、版本控制的包存在

1.1.3.1 与MCP、Tool Calling的关系

需要明确区分这三个概念的不同抽象层级

  • Tool Calling(工具调用):解决**"能不能做"的问题,是模型与外部系统的实时交互机制**;
  • MCP(Model Context Protocol):解决**"如何连接"的问题,是工具接口的标准化协议**;
  • Agent Skills(Skills标准):解决**"知道怎么做"的问题,是程序性知识的封装与交付格式**。

三者的协作关系是:Agent Skills提供了**"操作手册"**(知道应该按什么步骤、用什么标准来做),当执行到具体步骤需要调用外部工具时,通过MCP协议发现可用工具,再通过Tool Calling机制实际执行。

例如,一个"安全漏洞分析"Skill会包含分析流程、检查清单、报告模板(程序性知识);当需要查询漏洞数据库时,它指示模型通过MCP协议连接的CVE查询工具(标准化接口),实际执行Tool Calling获取数据(实时交互)。

1.1.4 Multi-Agent:从"通才"到"专家团队"

然而,当任务复杂度达到一定程度(例如"开发一款APP"),单一Agent的局限性暴露无遗:它既要做架构设计,又要写代码,还要做UI设计,容易顾此失彼。这就好比让一个人同时扮演产品经理、程序员和设计师,效率和质量都难以保证。

Multi-Agent System(多智能体系统) 的出现,是为了解决单一Agent的能力过载问题,借鉴了人类社会分工协作的智慧。

在这种架构中,我们不再追求一个"万能Agent",而是构建Agent Team(智能体团队):研究Agent专精于信息搜集,分析师Agent专精于数据处理,程序员Agent专精于代码生成。每个Agent拥有自己的Skill集合、系统提示词和记忆空间。

1.1.5 A2A协议:Agent之间的"通用语言"

但新的问题随之而来:这些Agent可能运行在不同的框架或者是服务中,使用不同的通信格式,它们如何"听懂"彼此?这就像让说中文的研究员和说法语的程序员协作,必须有翻译机制。

A2A(Agent-to-Agent)协议 正是为了解决异构Agent之间的互操作性而生。它定义了Agent发现、能力广播、任务委托、状态同步、安全认证等标准。基于A2A,一个用Python编写的数据分析Agent可以无缝调用另一个用Java开发的图表生成Agent,就像互联网上的HTTP协议让不同服务器能够通信一样。

1.1.6 ReAct与工作流:串联一切的思考与编排

在这一切的背后,ReAct(Reasoning + Acting) 模式提供了认知层面的串联逻辑。它描述了Agent如何工作:不是一次性得出答案,而是通过"思考→行动→观察→再思考"的循环逐步逼近目标

ReAct强制要求大模型以显式的结构化格式输出这个思维过程:

  1. Thought(思考):基于当前上下文,模型显式写出推理过程("我需要先获取Q3的销售数据才能分析趋势,我应该调用sales_query工具");
  2. Action(行动):基于上述思考,输出结构化的工具调用指令({"tool": "sales_query", "params": {"quarter": "Q3"}});
  3. Observation(观察):系统执行工具,将结果(如"Q3销售额同比下降15%")重新注入上下文;
  4. Reflection(反思/调整):模型基于新的观察,决定下一步("数据确认了下降趋势,现在需要调用competitor_analysis工具查看竞品动态")。

这种显式的思维链(Chain-of-Thought) 带来了三重革命性改进:

  • 可解释性:我们不再看到模型的"直觉反应",而是能完整阅读它的"心路历程",知道它为什么调用某个工具;
  • 错误自纠:当工具返回异常或空结果时,模型可以在Reflection步骤识别"这个查询没有返回有效数据,我需要调整参数重试",而不是继续瞎猜;
  • 幻觉抑制:通过强制"先思考再行动",模型必须基于逻辑推导而非统计概率做决定,大幅减少了"为了调用工具而调用工具"的随机性行为。

ReAct本质上定义了单Agent的认知节奏——它让每个决策步骤都变得透明、可追溯、可调试。

1.1.7 Workflow:驯服AI不确定性的"确定性编排"

当AI执行长周期、多步骤的复杂任务时,AI的不确定性成为了最后一道障碍。

ReAct的核心是"让模型自己决定下一步做什么"——这带来了灵活性,但也带来了不可控性:同样的输入,模型这次可能先查知识库再分析,下次可能直接瞎编;遇到复杂分支,模型可能陷入循环或跳过关键步骤;更糟糕的是,企业无法审计和复现模型的决策路径,因为每次ReAct循环的"思维链"都是现场生成的。

工作流平台正是解决如何既利用AI能力,又确保业务流程的确定性、可重复性、可视化可控这一核心矛盾。

其本质是一种**预定义管道(Pre-defined Pipeline)**架构:

  • 节点化封装:把AI关在可控的"笼子"里 工作流将AI能力拆解为可视化节点:开始节点、LLM节点、知识库节点(RAG)、工具节点、条件分支节点、循环节点、代码执行节点、结束节点。每个节点的输入、输出、Prompt模板、温度参数、超时限制都是人类预先配置的。

    • 这与ReAct的"自由思考"截然相反——在工作流中,LLM节点不是"自主决定调用什么工具",而是**"人类指定它在这个步骤必须做什么事"。例如:第一步必须是"意图识别节点"(用低温度参数确保分类稳定),第二步根据意图分支到不同的知识库检索节点,第三步是"答案生成节点"(用高温度参数生成友好回复)。AI的随机性被限制在节点内部**,而节点之间的流转是确定性的代码逻辑
  • 流程固化:消除累积误差 ReAct的风险在于"一步错步步错"——如果早期的Observation被误解,后续推理会基于错误前提螺旋下滑。而工作流通过显式分支解决这个问题:人类预设"如果知识库检索结果为空,则走'兜底回复'分支;如果置信度低于0.7,则走人工审核分支"。这些条件判断是硬编码的,不依赖模型的"反思"能力,从而确保异常路径被显式处理,而非依赖模型的"自觉性"。

  • 3. 解决AI不确定性的工程化手段 工作流通过以下机制驯服不确定性:

    • 输入输出标准化:每个节点定义严格的JSON Schema,确保上游节点的输出能被下游节点正确解析,避免模型"自由发挥"格式导致下游崩溃;

    • 版本控制与A/B测试:工作流可以保存版本、灰度发布,让业务流程的变更可审计、可回滚,而ReAct的"思维"是临时的、不可版本化的;

    • 人工介入点的刚性插入:在关键节点(如转账确认、内容发布)强制插入"人类审核节点",流程在此暂停并等待外部信号,而不是依赖模型自己决定"要不要问人类";

    • 知识库与LLM的解耦:通过显式的"知识库节点"将RAG过程可视化,人类可以清楚看到"哪些上下文被注入了",而不是像ReAct那样由模型隐式决定检索时机。

在这些技术的加持下,现在已经全面进入了AI Agent时代

2. 什么是 Spring AI Alibaba?

网址:https://java2ai.com/

Spring AI Alibaba 是一个 面向 Java 生态的企业级 AI Agent 开发框架。作为 Agent 框架,它提供了构建生产级 Agent 所需的完整技术栈:

  • 上下文管理
  • 记忆管理
  • Tool Calling机制
  • 模型管理
  • 多智能体
  • 工作流
  • RAG
  • A2A
  • MCP
  • Agent Skills
  • 等等

3. 环境准备

  • Java环境:JDK 17 或更高版本。
  • Maven:Maven 3.6 或更高版本。
  • AI API Key:
    • 访问 阿里云百炼open in new window
    • 登录阿里云账号(没有就注册一个)
    • 进入「模型广场」,点击「API-KEY 管理」
    • 创建一个新的 API Key
    • 复制保存这个 Key(格式像 sk-xxxxxxxxxxxx
    • 注意:key不要泄露给别人

4. 创建第一个项目

4.1 使用Spring Initializr初始化项目

image-20260315235025619

image-20260315235102040

4.2 添加 Spring AI Alibaba 依赖

修改pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!--suppress ALL -->
<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 https://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.5.11</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.mszlu.ai</groupId>
    <artifactId>alibaba</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-ai-alibaba</name>
    <description>spring-ai-alibaba</description>
    <url/>
    <licenses>
        <license/>
    </licenses>
    <developers>
        <developer/>
    </developers>
    <scm>
        <connection/>
        <developerConnection/>
        <tag/>
        <url/>
    </scm>
    <properties>
        <java.version>21</java.version>
        <!-- Spring AI Alibaba 版本 -->
        <spring-ai-alibaba.version>1.1.2.2</spring-ai-alibaba.version>
        <!-- Spring AI 版本 -->
        <spring-ai.version>1.1.2</spring-ai.version>
    </properties>
    <dependencies>
        <!-- Spring Boot Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Spring AI Alibaba 核心框架 -->
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-agent-framework</artifactId>
            <version>${spring-ai-alibaba.version}</version>
        </dependency>

        <!-- 阿里云 DashScope 模型支持 -->
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
            <version>${spring-ai-alibaba.version}</version>
        </dependency>

        <!-- 测试 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <!-- 依赖版本管理 -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.ai</groupId>
                <artifactId>spring-ai-bom</artifactId>
                <version>${spring-ai.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89

4.3 配置 API Key

注意:IDEA不能实时同步系统环境变量,配置环境变量后,需要重启IDEA使环境变量生效

配置环境变量:

软件地址:https://github.com/mszlu521/devToolsBox

image-20260315235728347

spring:
  application:
    name: spring-ai-alibaba
  ai:
    dashscope:
      # 你的阿里云 DashScope API Key,这里配置环境变量
      api-key: ${AI_DASHSCOPE_API_KEY:default-key}
      chat:
        options:
          model: qwen3-max
# 日志级别
logging:
  level:
    com.alibaba.cloud.ai: DEBUG
1
2
3
4
5
6
7
8
9
10
11
12
13
14

在 IDEA 中运行的话,可以直接在 Run Configuration 里设置环境变量

5. 编写第一个 AI 程序

5.1 创建 AI 服务

package com.mszlu.ai.alibaba.service;

import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * AI 对话服务
 *
 * 这是你的第一个 AI 服务,它可以和 AI 模型对话!
 */
@Service
public class ChatService {

    /**
     * ChatModel 就是 AI 模型的"遥控器"
     * Spring Boot 会自动注入配置好的模型
     */
    private final ChatModel chatModel;

    public ChatService(ChatModel chatModel) {
        this.chatModel = chatModel;
    }

    /**
     * 最简单的对话方式
     *
     * @param userInput 用户说的话
     * @return AI 的回复
     */
    public String simpleChat(String userInput) {
        // 直接调用,传入字符串,返回字符串
        return chatModel.call(userInput);
    }

    /**
     * 更完整的对话方式
     *
     * @param userInput 用户说的话
     * @return AI 的回复(包含更多元信息)
     */
    public String advancedChat(String userInput) {
        // 1. 创建用户消息
        UserMessage userMessage = new UserMessage(userInput);
        SystemMessage systemMessage = new SystemMessage("You are a helpful assistant.");
        // 2. 创建 Prompt
        Prompt prompt = new Prompt(systemMessage, userMessage);
        // 3. 调用模型
        ChatResponse response = chatModel.call(prompt);

        // 4. 获取 AI 的回复内容
        return response.getResult().getOutput().getText();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

5.2 创建 REST API

package com.mszlu.ai.alibaba.controller;

import com.mszlu.ai.alibaba.service.ChatService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

/**
 * AI 对话接口
 *
 * 通过 HTTP 请求和 AI 对话
 */
@RestController
@RequestMapping("/api/chat")
public class ChatController {

    private final ChatService chatService;

    public ChatController(ChatService chatService) {
        this.chatService = chatService;
    }

    /**
     * 简单对话接口
     *
     * 使用方法:
     * POST http://localhost:8080/api/chat/simple
     * Body: {"message": "你好,请介绍一下自己"}
     */
    @PostMapping("/simple")
    public Map<String, String> simpleChat(@RequestBody Map<String, String> request) {
        String userMessage = request.get("message");
        String aiResponse = chatService.simpleChat(userMessage);

        return Map.of(
                "user", userMessage,
                "ai", aiResponse
        );
    }

    /**
     * GET 方式对话(方便浏览器测试)
     *
     * 使用方法:
     * http://localhost:8080/api/chat/ask?message=你好
     */
    @GetMapping("/ask")
    public Map<String, String> ask(@RequestParam String message) {
        String aiResponse = chatService.simpleChat(message);

        return Map.of(
                "user", message,
                "ai", aiResponse
        );
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

6. 启动测试

运行SpringAiAlibabaApplication

image-20260316001325374

简单点,我们使用浏览器进行测试:

http://localhost:8080/api/chat/ask?message=你好,请用一句话介绍Spring AI Alibaba

image-20260316004701373

🎉 恭喜!你已经成功运行了第一个 AI 程序!

7. 代码解析

让我们理解一下刚才写的代码:

用户请求 ──► ChatController ──► ChatService ──► ChatModel ──► AI模型
                │                    │              │
                └──── 返回 JSON ◄────┴── 返回文字 ◄─┘
1
2
3
组件作用
ChatController接收 HTTP 请求
ChatService业务逻辑处理
ChatModel调用 AI 模型
AI 模型生成回复

ChatModel是 Spring AI Alibaba 的核心接口,它:

  • 封装了不同 AI 模型的调用细节
  • 提供统一的调用方式
  • 自动处理连接、认证、重试等

8. 课后练习

  1. 修改提示词:让 AI 以"诗人"的身份回答问题
  2. 添加新接口:实现一个 GET 接口,返回 AI 对任意话题的简短总结
  3. 错误处理:给 ChatService 添加 try-catch,处理 API 调用失败的情况