Spring AI 源码大白话解析

「鸿蒙心迹」“2025・领航者闯关记“主题征文活动 10w+人浏览 622人参与

📖 一、先从“用”开始,理解“它干了啥”

1. 想象一下:点外卖的流程

Spring AI 就像是“AI 服务外卖平台”:

// 你点外卖的代码(实际使用)
@Service
public class CustomerService {
    
    // 这个就是 Spring AI 给你的“外卖小哥”
    @Autowired
    private ChatClient chatClient;
    
    public String askAi(String question) {
        // 点餐(问问题)
        String answer = chatClient.call(question);
        
        // 拿到外卖(AI 回答)
        return answer;
    }
}

你看,你就做了三件事:

  1. 有个“外卖小哥”(ChatClient)在等你
  2. 告诉他“我要问什么”(调用 call 方法)
  3. 他给你“带回来答案”

完全不用管:

  • 小哥骑什么车(HTTP 还是 gRPC)
  • 去哪个餐厅(OpenAI 还是文心一言)
  • 怎么付钱(API Key 怎么传)
  • 路上堵不堵(网络连接、重试)

🏗️ 二、拆开“外卖小哥”看看里面有什么

1. 外卖小哥的“接单流程”

// 这是外卖小哥的“大脑”(简化版)
public class AiDeliveryGuy {
    
    // 小哥的手机(联系餐厅用)
    private Phone phone = new Phone();
    
    // 小哥的钱包(付钱用)
    private Wallet wallet = new Wallet();
    
    // 接单流程
    public String takeOrder(String customerQuestion) {
        // 1. 先看看顾客要什么
        System.out.println("顾客说:" + customerQuestion);
        
        // 2. 查查要去哪家餐厅
        String restaurant = decideRestaurant(customerQuestion);
        
        // 3. 打电话给餐厅
        String order = makeOrder(restaurant, customerQuestion);
        
        // 4. 付钱
        pay(restaurant, order);
        
        // 5. 等餐厅做菜
        String food = waitForFood(restaurant, order);
        
        // 6. 拿回给顾客
        return food;
    }
    
    private String decideRestaurant(String question) {
        // 如果问题里有“中文”,去“文心一言”餐厅
        if (question.contains("中文") || question.contains("中国")) {
            return "wenxin";
        }
        // 否则默认去“OpenAI”餐厅
        return "openai";
    }
}

🔧 三、Spring AI 的“核心部件”

1. 最重要的“接口”——ChatClient

想象成“外卖APP里的点餐按钮”

// 这就是那个“点餐按钮”
public interface ChatClient {
    
    // 点击按钮,传入问题
    String call(String question);
    
    // 高级功能:边等边看进度(流式)
    void stream(String question, Consumer<String> onChunk);
}

这个接口的意义:

  • 只知道有个按钮能点
  • 不知道按钮后面是谁在做
  • 可能是美团外卖,也可能是饿了么
  • Spring AI 帮你“选最好用的那个”

2. 自动配置——Spring 的“智能管家”

想象成“你搬家后,管家自动布置好一切”

// Spring AI 的自动配置(简化版)
@Configuration
public class AiAutoConfig {
    
    // 如果配置里有 openai 的 key,就创建 OpenAI 小哥
    @Bean
    @ConditionalOnProperty(name = "spring.ai.openai.api-key")
    public ChatClient openAiGuy() {
        System.out.println("老板,我雇了个 OpenAI 外卖小哥!");
        return new OpenAiDeliveryGuy();
    }
    
    // 如果配置里有 wenxin 的 key,就创建文心一言小哥
    @Bean
    @ConditionalOnProperty(name = "spring.ai.wenxin.api-key")
    public ChatClient wenxinGuy() {
        System.out.println("老板,我雇了个文心一言外卖小哥!");
        return new WenxinDeliveryGuy();
    }
}

配置文件(application.yml):

spring:
  ai:
    openai:
      api-key: sk-1234567890  # 有了这个,就雇 OpenAI 小哥
    # wenxin:
    #   api-key: abcdefg  # 注释掉,就不雇文心一言小哥

3. OpenAI 外卖小哥的真实工作

// OpenAI 外卖小哥的“工作手册”
public class OpenAiDeliveryGuy implements ChatClient {
    
    private String apiKey;  // 餐厅会员卡
    private String baseUrl = "https://api.openai.com/v1";  // 餐厅地址
    
    @Override
    public String call(String question) {
        // 1. 准备“点菜单”(JSON格式)
        String orderForm = """
            {
                "model": "gpt-3.5-turbo",
                "messages": [
                    {"role": "user", "content": "%s"}
                ],
                "temperature": 0.7
            }
            """.formatted(question);
        
        // 2. 打电话给餐厅(发HTTP请求)
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(baseUrl + "/chat/completions"))
            .header("Content-Type", "application/json")
            .header("Authorization", "Bearer " + apiKey)
            .POST(HttpRequest.BodyPublishers.ofString(orderForm))
            .build();
        
        // 3. 等待餐厅回复
        HttpResponse<String> response = client.send(request, 
            HttpResponse.BodyHandlers.ofString());
        
        // 4. 解析回复,取出“菜”(AI回答)
        String responseJson = response.body();
        // 假设 responseJson 是: {"choices":[{"message":{"content":"你好!"}}]}
        String answer = parseAnswer(responseJson);
        
        return answer;
    }
    
    private String parseAnswer(String json) {
        // 简单解析JSON,实际用Jackson/Gson
        int start = json.indexOf("\"content\":\"") + 11;
        int end = json.indexOf("\"", start);
        return json.substring(start, end);
    }
}

🔄 四、完整流程动画演示

1. 场景:小明用 Spring AI 问天气

文心一言OpenAI服务Spring AI框架Spring Boot应用用户小明文心一言OpenAI服务Spring AI框架Spring Boot应用用户小明Spring AI 的“智能决策”alt[配置了 OpenAI][配置了文心一言][没配置任何AI]访问 /ask?q=今天天气怎样chatClient.call("今天天气怎样")POST /chat/completions{"回答":"今天天气晴朗..."}调用文心一言API返回回答抛出异常“没找到外卖小哥”返回AI回答显示“今天天气晴朗...”

2. 代码级别的流程

// 这是实际 Spring AI 内部的大概流程
public class SpringAiMagic {
    
    public String processQuestion(String question) {
        // 第1步:找“外卖小哥”(ChatClient)
        ChatClient deliveryGuy = findDeliveryGuy();
        
        // 第2步:检查“小哥”的健康状况
        if (!deliveryGuy.isHealthy()) {
            // 小哥生病了,换个健康的
            deliveryGuy = findBackupGuy();
        }
        
        // 第3步:记录“开始送餐时间”
        long startTime = System.currentTimeMillis();
        
        try {
            // 第4步:小哥去送餐(调用AI)
            String answer = deliveryGuy.call(question);
            
            // 第5步:记录“送餐完成时间”
            long endTime = System.currentTimeMillis();
            System.out.println("送餐耗时:" + (endTime - startTime) + "ms");
            
            return answer;
            
        } catch (Exception e) {
            // 第6步:如果出错了(比如餐厅关门)
            System.out.println("送餐失败:" + e.getMessage());
            
            // 尝试重试
            return retryDelivery(question);
        }
    }
    
    private ChatClient findDeliveryGuy() {
        // 这里就是 Spring AI 的“智能”所在:
        // 1. 查看配置文件
        // 2. 查看类路径
        // 3. 决定用哪个外卖平台
        return new OpenAiDeliveryGuy();  // 假设配置了 OpenAI
    }
}

🎯 五、Spring AI 的“智能”体现在哪?

1. 自动选择“最优外卖小哥”

// Spring AI 的“智能调度中心”
public class AiDispatcher {
    
    private Map<String, ChatClient> deliveryGuys = new HashMap<>();
    
    public AiDispatcher() {
        // 注册所有可用的小哥
        deliveryGuys.put("openai", new OpenAiDeliveryGuy());
        deliveryGuys.put("wenxin", new WenxinDeliveryGuy());
        deliveryGuys.put("azure", new AzureDeliveryGuy());
        deliveryGuys.put("local", new LocalDeliveryGuy());
    }
    
    public ChatClient dispatch(String model) {
        // 规则1:如果指定了模型,用对应的
        if (model != null) {
            if (model.contains("gpt")) return deliveryGuys.get("openai");
            if (model.contains("ernie")) return deliveryGuys.get("wenxin");
        }
        
        // 规则2:看配置文件里启用了哪个
        if (config.hasOpenAiKey()) return deliveryGuys.get("openai");
        if (config.hasWenxinKey()) return deliveryGuys.get("wenxin");
        
        // 规则3:都没配置,用本地的(如果有)
        if (deliveryGuys.get("local") != null) {
            return deliveryGuys.get("local");
        }
        
        throw new RuntimeException("一个外卖小哥都没雇!");
    }
}

2. 自动处理“各种意外情况”

// Spring AI 的“应急预案”
public class EmergencyPlan {
    
    public String askWithBackup(String question) {
        try {
            // 首选方案:OpenAI
            return openAiGuy.call(question);
            
        } catch (TooManyRequestsException e) {
            // 情况1:OpenAI 说“太忙了,等会儿”
            System.out.println("OpenAI 忙,换文心一言试试");
            return wenxinGuy.call(question);
            
        } catch (NetworkException e) {
            // 情况2:网络不通
            System.out.println("网络不通,用本地模型");
            return localGuy.call(question);
            
        } catch (AllFailedException e) {
            // 情况3:全都挂了
            return "抱歉,AI 服务暂时不可用,请稍后再试";
        }
    }
}

📦 六、Spring AI 的“工具箱”里还有什么?

1. 向量数据库(给 AI 加“记忆”)

// 想象成“AI 的笔记本”
public class AiNotebook {
    
    private Map<String, String> memory = new HashMap<>();
    
    public void teachAi(String topic, String knowledge) {
        // 教 AI 知识
        memory.put(topic, knowledge);
        System.out.println("已教会 AI 关于:" + topic);
    }
    
    public String askWithMemory(String question) {
        // 1. 先在“笔记本”里找答案
        for (String topic : memory.keySet()) {
            if (question.contains(topic)) {
                System.out.println("从笔记本找到了答案!");
                return memory.get(topic);
            }
        }
        
        // 2. 笔记本里没有,问 AI
        return chatClient.call(question);
    }
}

2. 流式响应(“边做边吃”)

// 传统方式:等菜全做好再上
public String getFullAnswer(String question) {
    // AI 要思考 10 秒才能给出完整答案
    String fullAnswer = chatClient.call(question);  // 等 10 秒
    return fullAnswer;  // 10 秒后一次返回
}

// 流式:上一道吃一道
public void getStreamAnswer(String question, Consumer<String> onChunk) {
    // AI 边想边说
    chatClient.stream(question, chunk -> {
        // 每想到一点,就返回一点
        onChunk.accept(chunk);
        // 用户能先看到“今天”,然后看到“天气”,最后看到“晴朗”
    });
}

🎪 七、实际项目中的“实战场景”

场景1:智能客服系统

@Service
public class CustomerServiceBot {
    
    @Autowired
    private ChatClient aiAssistant;  // Spring AI 提供
    
    // 传统客服的“知识库”
    private Map<String, String> faq = Map.of(
        "退货", "7天内可无理由退货",
        "物流", "一般3-5天送达",
        "客服电话", "400-123-4567"
    );
    
    public String answerCustomer(String question) {
        // 1. 先看 FAQ 里有没有
        for (String keyword : faq.keySet()) {
            if (question.contains(keyword)) {
                return faq.get(keyword);
            }
        }
        
        // 2. FAQ 没有,问 AI
        String prompt = """
            你是客服助手,请用友好、专业的语气回答用户问题。
            用户问题:%s
            """.formatted(question);
        
        return aiAssistant.call(prompt);
    }
}

场景2:代码助手

@Service
public class CodeAssistant {
    
    @Autowired
    private ChatClient aiCoder;
    
    public String explainCode(String code) {
        String prompt = """
            请解释这段代码是做什么的:
            ```java
            %s
            ```
            """.formatted(code);
        
        return aiCoder.call(prompt);
    }
    
    public String fixBug(String error, String code) {
        String prompt = """
            遇到这个错误:%s
            在代码中:%s
            请帮我修复。
            """.formatted(error, code);
        
        return aiCoder.call(prompt);
    }
}

🔧 八、自己动手“造轮子”(理解原理)

1. 最简化的 Spring AI 实现

// 第一步:定义“外卖小哥”接口
public interface SimpleAiClient {
    String ask(String question);
}

// 第二步:实现“OpenAI 小哥”
@Component
@ConditionalOnProperty(name = "ai.provider", havingValue = "openai")
public class OpenAiClient implements SimpleAiClient {
    
    @Value("${ai.openai.key}")
    private String apiKey;
    
    @Override
    public String ask(String question) {
        // 最简单的 HTTP 调用
        String url = "https://api.openai.com/v1/chat/completions";
        String json = String.format("""
            {
                "model": "gpt-3.5-turbo",
                "messages": [{"role": "user", "content": "%s"}]
            }
            """, question);
        
        // 发送请求,解析响应(省略细节)
        return sendHttpRequest(url, json, apiKey);
    }
}

// 第三步:自动配置
@Configuration
public class SimpleAiConfig {
    
    @Bean
    @ConditionalOnMissingBean
    public SimpleAiClient aiClient() {
        // 根据配置决定用哪个
        String provider = environment.getProperty("ai.provider", "openai");
        
        if ("openai".equals(provider)) {
            return new OpenAiClient();
        } else if ("mock".equals(provider)) {
            return new MockAiClient();  // 测试用的假 AI
        }
        
        throw new IllegalStateException("不支持的 AI 提供商");
    }
}

// 第四步:使用
@Service
public class MyService {
    @Autowired
    private SimpleAiClient aiClient;  // 注入
    
    public void doSomething() {
        String answer = aiClient.ask("你好吗?");
        System.out.println("AI 回答:" + answer);
    }
}

📚 九、总结:Spring AI 到底做了什么?

用一句话总结:

Spring AI 把“调用各种 AI 服务”这件麻烦事,变成了“点外卖”一样简单。

具体来说:

你的烦恼Spring AI 的解决方案
每个 AI 服务 API 不一样统一接口,你只用学一种
HTTP 请求、JSON 解析好麻烦框架帮你搞定
出错要重试、监控、日志内置,开箱即用
换 AI 服务要改代码改配置就行
要自己管理 API Key、连接池自动管理

最后记住这个比喻:

  • ChatClient = 外卖小哥
  • @Autowired = 小哥到你公司报到
  • application.yml = 小哥的工作安排表
  • call() 方法 = 告诉小哥“去帮我买个饭”
  • Spring AI 框架 = 外卖平台(美团/饿了么)

你不需要知道:

  • 小哥骑什么车
  • 走哪条路
  • 怎么跟餐厅沟通
  • 怎么付钱

你只需要:

@Autowired
ChatClient 外卖小哥;

String= 外卖小哥.买饭("我要鱼香肉丝");
System.out.println("吃到了:" +);

这就是 Spring AI 的核心思想:让复杂的事情变简单,让你专注于业务,而不是技术细节。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Coder_Boy_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值