【AI 转型实战 1】SpringAI 入门:搭建基础大模型对话能力

基础知识详解

LLM

中文通常译为大语言模型 ,日常也常简称为大模型。不过严格来说,"大模型"的范围更宽一些------它还可以包括多模态大模型(如能处理图像、音频的模型),而 LLM 特指以文本(语言)为核心的大规模神经网络模型。

Token

是模型眼里的最小单位。它既不是字也不是词,是从语料里统计出来的、最省事的一套切法

越常见的东西,越可能被压成一个 token;越少见的,越会被拆碎。

提示词(prompt)

输入给大模型的指令或文本,用来引导模型生成你想要的输出。它是你和模型之间"对话"的起点,也是控制模型行为最直接的方式。

提示词工程不是找到某个神奇句式,是系统地把该说的都说清楚

提示词四部分:背景、要求、限制、验收。

上下文与 RAG

向量:向量把「意思」变成了坐标。有了坐标,「哪段话和这个问题最像」就成了一道可以计算的距离题。负责这件事的是一个专门的模型是向量模型。

上下文窗口:模型在一次请求里能看到的全部内容的上限。它是按 Token 算的,而且------要装的东西比大多数人以为的多

RAG知识库:先从你的资料里找出相关的几段,连同问题一起交给模型。绝大多数「企业知识库」就是这个。RAG 不是一种模型,是一套工程流程------你不需要训练任何东西,用现成的模型加一个检索层就能搭起来

① 检索。 把用户问题也转成向量,在库里找最接近的几段。成熟做法是混合检索 + 重排:先多召回一批,再挑出真正能回答问题的三五段。

**② 拼进上下文。**把检索到的片段、用户问题、以及一组约束,组装成一次请求。

**③ 模型回答。**模型基于给它的材料作答,并标明出处。

Agent与Skill

Agent:以大模型为"大脑",能够自主感知、规划、调用工具并执行多步任务来完成目标的系统。它不只是"你问一句它答一句",而是能自己决定下一步做什么

Agent 的典型组成:

  • 规划(Planning):把大目标拆成子任务,决定先后顺序,必要时反思和修正

  • 工具调用(Tool Use):调用搜索、代码执行、数据库、API、文件操作等外部能力

  • 记忆(Memory):短期记忆(当前对话上下文)和长期记忆(向量库、历史记录)

  • 执行与循环:观察结果→决定下一步→再行动,直到任务完成

常见工作模式:

  • ReAct:推理(Reasoning)和行动(Acting)交替进行,"想一步、做一步、看结果"

  • Plan-and-Execute:先制定完整计划,再逐步执行

  • 多 Agent 协作:多个角色分工(如研究员、程序员、审核员)协同完成任务

Skill是给模型的「操作手册」,做某类任务时要遵循的步骤、要用的格式、要注意的边界、可以参考的例子。它不碰任何外部系统,只影响模型「用脑子办事」的方式。

MCP

它是一套接口接入的标准:把你的系统(数据库、订单服务、邮件)按统一格式包装起来,让 Agent 通过一套规范就能调用这些能力。它解决的是连接问题,不管模型会不会用这些能力做事。

解决的是接入的标准化问题。在它出现之前,每个 Agent 要接一个新系统,都得写一套专用的适配代码,格式五花八门。MCP 定义了一套统一的形状:

**工具。**外部系统暴露的可调用操作,以及各自的参数说明。

**资源。**可读取的数据和文件。

**提示。**可复用的指令模板。

Spring AI

Spring AI 就是让你用写 Spring Boot 微服务的方式,写 LLM 应用。 不用自己拼 HTTP 请求、不用自己处理 token 计数、不用自己管流式协议

ChatModel

Spring AI 的核心入口,是直接与各类大模型提供商(如 OpenAI、Ollama、DeepSeek 等)交互的低层接口。它负责统一请求格式、发送提示并解析原始响应,是框架实现"可移植性"的基础。

定义:继承自通用的 Model 接口,并提供了同步调用与流式调用的能力。它的核心方法设计得比较简洁:

  • 简化调用 :call(String message) 方法允许你直接传入一段字符串,快速获取模型的文本回复,适合入门测试。

  • 标准调用 :call(Prompt prompt) 是实际开发中更常用的方式。它接受封装好的 Prompt 对象,并返回包含完整元数据(如 token 用量、结束原因)的 ChatResponse 对象。

  • 流式响应 :通过 stream(Prompt prompt) 方法,它使用响应式编程(Reactor 的 Flux)逐块返回数据,适合需要打字机效果或处理长回复的场景

代码

1.配置yml

java 复制代码
server:
  port: 6066

spring:
  application:
    name: day-01-chatmodel

  # ========== Spring AI OpenAI 配置 ==========
  # API Key 从环境变量读取,绝不硬编码
  # Windows PowerShell: $env:OPENAI_API_KEY="sk-xxx"
  # 也支持设置 base-url 对接兼容 API(如 DashScope / 智谱 / 本地 Ollama)
  ai:
    openai:
      # 官方 OpenAI: https://api.openai.com
      # 国内兼容示例: https://dashscope.aliyuncs.com/compatible-mode/v1
      base-url: https://api.deepseek.com
      api-key: sk-a2************************e39f
      chat:
        options:
          # 默认模型,可按需切换
          # OpenAI: gpt-4o-mini, gpt-4o
          # Ollama 本地: qwen2.5:7b, llama3.1:8b
          model: deepseek-flash
          # 温度:0=稳定确定,1=创意发散;Day 1 先用 0.7 体验
          temperature: 0.7
          # 最大生成 token 数,避免成本失控
          max-tokens: 256

logging:
  level:
    # 让我们能看到 Spring AI 的调用日志,Day 1 方便学习链路
    org.springframework.ai: DEBUG

2:接口

java 复制代码
@GetMapping("/chat")
    public Map<String, Object> chat(@RequestParam(defaultValue = "你好") String msg) {
        Map<String, Object> result = new HashMap<>();
        result.put("question", msg);
        result.put("timestamp", LocalDateTime.now().toString());

        try {
            String answer = chatModel.call(msg);
            result.put("answer", answer);
            result.put("success", true);
        } catch (Exception e) {
            result.put("answer", null);
            result.put("success", false);
            result.put("error", e.getMessage());
        }

        return result;
    }

3.结果

ChatClient

是 Spring AI 1.0 主推的 Fluent API 入口 。类比熟悉的 Spring 生态:

ChatClient 是一个 Builder 模式 的链式调用器,帮你把这些要素组合到一次对话请求里:

ChatClient.prompt() ← 开始构建请求

.system(...) ← 系统提示词(角色设定)

.user(...) ← 用户提问

.assistant(...) ← 历史助手回复(Day 3 多轮再讲)

.options(ChatOptions) ← 模型参数覆盖

.advisors(...) ← 拦截器(Day 5 学)

.call() ← 同步调用,返回 ChatResponse

.stream() ← 流式调用,返回 Flux<ChatResponse>

代码

1.提示词

@Configuration

public class ChatClientConfig {

/**

* 默认 system prompt:设定 AI 的角色。

* 先放一个固定字符串,后面 Day 3 会学模板化 prompt。

*/

public static final String DEFAULT_SYSTEM_PROMPT = """

你是一个专业的 Java 后端工程师导师,擅长把复杂概念讲得通俗易懂。

回答时请遵循:

  1. 先给一句话总结(TL;DR)

  2. 再展开详细解释

  3. 适当时给代码示例

""";

@Bean

public ChatClient chatClient(ChatClient.Builder builder) {

return builder

// 预先灌进去 system prompt,所有通过这个 Bean 的调用都自动带上

.defaultSystem(DEFAULT_SYSTEM_PROMPT)

.build();

}

}

2.请求体

java 复制代码
@PostMapping("/chat")
    public ApiResult<ChatApiResponse> chat(@Valid @RequestBody ChatRequest request) {
        long start = System.currentTimeMillis();
        LocalDateTime timestamp = LocalDateTime.now();

        ChatOptions options = buildChatOptions(request);

        // ------ ChatClient Fluent API 核心演示 ------
        ChatResponse resp = chatClient.prompt()
                .system(systemSpec -> {
                    String sp = request.getSystemPrompt();
                    if (sp != null && !sp.isBlank()) {
                        systemSpec.text(sp);
                    }
                })
                .user(request.getQuestion())
                .options(options)
                .call()
                .chatResponse();

        // ------ 从 Spring AI ChatResponse 提取字段,组装成我们的 ChatApiResponse ------
        String answer = resp.getResult().getOutput().getText();
        String model = resp.getMetadata().getModel() != null
                ? resp.getMetadata().getModel()
                : "unknown";

        // Spring AI 1.0 的 Usage 接口:getPromptTokens()=输入, getCompletionTokens()=输出, getTotalTokens()=总
        // 返回 Integer(可能为 null,有些模型不返回 usage),所以用 intValue() 前先判 null
        int inputTokens = 0;
        int outputTokens = 0;
        Usage usage = resp.getMetadata().getUsage();
        if (usage != null) {
            inputTokens = usage.getPromptTokens() != null ? usage.getPromptTokens() : 0;
            outputTokens = usage.getCompletionTokens() != null ? usage.getCompletionTokens() : 0;
        }

        List<String> candidates = resp.getResults().stream()
                .map(r -> r.getOutput().getText())
                .toList();

        ChatApiResponse body = new ChatApiResponse();
        body.setAnswer(answer);
        body.setModel(model);
        body.setTimestamp(timestamp);
        body.setDurationMs(System.currentTimeMillis() - start);
        body.setUsage(new ChatApiResponse.TokenUsage(inputTokens, outputTokens, inputTokens + outputTokens));
        body.setCandidates(candidates);

        return ApiResult.ok(body);
    }

    private ChatOptions buildChatOptions(ChatRequest request) {
        ChatOptions.Builder builder = ChatOptions.builder();
        if (request.getTemperature() != null) {
            builder.temperature(request.getTemperature());
        }
        return builder.build();
    }

区别

在 Spring AI 中,ChatModel 与 ChatClient 是分层协作的关系,而非二选一。ChatClient 是构建在 ChatModel 之上的高层门面(Facade) ,旨在提升开发效率;而 ChatModel 是直接与模型提供商通信的底层接口,提供最原始的控制力。

核心区别

定位与层级

  • ChatModel:底层接口,直接封装与 AI 服务商的通信。类似于 JDBC 的核心库。

  • ChatClient :高层封装,内部持有一个 ChatModel 来执行实际请求。提供链式(Fluent)API,更符合现代 Java 开发习惯。

工具调用(Tool Calling)

  • ChatModel :不自动执行工具。它只返回模型请求调用的指令,需要调用方手动编写循环来执行工具并再次请求模型。

  • ChatClient :自动处理 工具调用循环。通过内置的 ToolCallingAdvisor,它能自动执行工具并将结果回传给模型,对开发者透明。

内置能力与便利性

  • ChatModel :功能单一,仅负责发送请求和接收原始响应。需要手动构造 Prompt、管理消息列表。

  • ChatClient:内置丰富功能。支持默认系统提示词、对话记忆(Advisor)、RAG(检索增强生成)、结构化输出(直接映射为 Java 对象)等。

适用场景

  • ChatModel :适用于构建基础设施(如自定义的 ChatClient 实现)、集成到非 Spring 的自定义编排引擎,或你需要对请求细节进行极致控制的场景。

  • ChatClient :官方推荐的入口。适用于绝大多数应用开发,包括聊天应用、RAG、智能体(Agent)工作流以及工具密集型的助手。

如何选择

对于绝大多数业务开发,优先使用 ChatClient 。它能帮你省去处理工具调用循环、记忆管理和提示词组装的大量样板代码。只有当你需要实现一个高度定制化的流程,且 ChatClient 的 Advisor 链反而成为阻碍时,才考虑直接使用底层的 ChatModel。

相关推荐
vilya1 小时前
Agent 语音引擎换心记:从「够用」到「稳且好」
人工智能·agent
goehou1 小时前
ReAct 循环跑偏的三种样子,以及怎么在工程上兜住
llm·agent·react·工具调用·工程实践
Wang's Blog1 小时前
Java 项目实战:交友项目 - 用户体系集成总结
java·开发语言·交友
学到老活到老1 小时前
第 24 集:配置文件写了 80,Spring Boot 为什么读到 90?
java·后端·spring
Flynn Liang1 小时前
RAG 答非所问/幻觉怎么破?Spring AI QuestionAnswerAdvisor 召回优化实战(TopK/重排/混合检索)
人工智能·算法·spring·rag·幻觉·springai
哥不是小萝莉1 小时前
让 Agent 可靠运行:幂等、持久化与失败恢复
ai
tongyiixiaohuang1 小时前
供应商主数据同步实战:从易快报到金蝶云星空的单一策略落地
java·网络·数据库
鱼宵2 小时前
取集合第一个元素,Java 为什么要写三种写法?
java·windows·python·java21
学长毕设2 小时前
基于SpringBoot的餐厅管理系统-附源码
java·spring boot·后端