给Javaer看的大模型开发指南

Posted on 2025-06-12

概述

伴随着大模型的性能提升、成本下降,在Web在线对话场景以外,大模型也越来多的被集成到传统业务场景。

在大模型API交互模式、业务集成模式经百家争鸣后已趋于稳定的背景下,Spring作为Java生态里的OSS巨头也下场为LLM提供生态支持,于近期释出 spring-ai 正式版。

需要说明的是,Spring-AI 所提供的能力并不神秘,业务上也不是必须用Spring-AI不可。但是,就像过去Spring对新的数据库、新的中间件提供生态支持一样,Spring-AI提供了一套和Spring全家桶兼容的、语义一致的、良好设计的、易于拓展的大模型交互的Java API,可以极大的降低LLM集成和开发的成本。

从大模型的工程化、实用化角度来说,当你厘清Spring-AI这一套API设施的逻辑后,事情最后还是会回归到我们业务开发仔最熟悉的CRUD领域。就像使用Mybatis操作mysql一样,我们用spring-ai来操作大模型。

那我们开始吧!

什么是大模型

大模型的舞台上,从来不缺新面孔。自ChatGPT开启AI新纪元后,各类大模型层出不穷。

但是我们不去考虑大模型的训练原理、推理/运算架构、参数调优等那些我们看不懂的数学上的东西,就像我们不会去关心mysql是怎么用代码实现的一样。

我们就类比我们熟悉的知识,对大模型有个盲人摸象式的、自洽的、基础的认识就好了。

  1. 从某种意义上来说,模型训练就是通过分析海量文本(如维基百科、图书、网页)寻找到人类语言的规律,再将这个规律固化成一个包含数十亿【参数】的超级【数学公式】。就像简单公式 y = 5x + 8 中的 58,这两个【参数】决定了输入X如何转化为输出Y。
  2. 训练好的【数学公式】就像代码,需要部署在算力平台上,借助【显卡】的并行运算能力来实现高效运算。
  3. 用户的输入作为这个【数学公式】的入参,经公式运算后,得到相关的【输出】。

img

假设大模型是一个上述数学公式,不同的大模型(ChatGPT/DeepSeek)是不同的架构、不同的公式。

那么模型训练就是通过对海量文本的分析、学习,找到合适的参数值。

大模型的特点

接下来我们看下,在工程应用场景下,需要我们开发仔关注的大模型的特点。

就像mysql,我们集成时也需要关注下不同的存储引擎(InnoDB/MyISAM)的特点。

无状态

image-20250804211627809

大模型是没有记忆、没有状态的,它就是一个纯函数。

它不知道它之前跟你说过什么。所以每次给大模型输入的时候,我们需要根据业务场景把之前的【输入】,【反馈】一并给它,避免大模型失忆导致对话不流畅。

img

结构化输出

大模型是具备结构化输出能力的,当然了有些模型支持的不够好。但是不重要,只是支持的程度不同,但是它们都支持!

所谓的结构化输出是指,大模型除了可以返回口语化、没有模式的的自然语言文本外,它还可以按你需求给你返回其他的文本格式比如:JSON。

img

你看,这像不像在调一个REST接口?甚至是一个万能接口!毕竟大模型啥都会,不会它也可以给你现编!

img

函数调用

其实看到这里我们就可以实现一个大模型驱动的RPC调用引擎了!

img

大模型帮你推理、规划得到了需要执行的函数和对应的函数参数,至于这个【函数名】对应的到底是一个进程内方法、HTTP接口、Dubbo接口还是MCP接口都不重要,只是智能体实现的一个技术细节而已。

我们可以用自然语言表述需求,同时告诉大模型有哪些辅助【工具/函数】可以供他备用。它会推理、编排这些工具来达成需求!

image-20250804211719398

  1. 把用户输入和可用函数输入给大模型,大模型推理发现需要调用外部函数,于是返回函数名+函数调用参数。
  2. 智能体捕获输出,对指定函数发起调用,再将用户输入和函数结果一起输入到大模型,大模型基于这些上下文推理输出结果。

考虑到大模型发起函数调用的普遍需求,大模型供应商一般都在API层面提供了【function call】能力,用于将文本输出和函数调用输出区分开。

但是原理就是这么个原理,只是API抽象层次的问题!

大模型接口

考虑到大模型对硬件资源的特别需求(如显卡),所以大模型一般是独立部署,以SaaS模式提供能力。就像mysql对资源有特别的需求(如大内存),所以一般也是独立部署一样!

image-20250804211801219

训练好的大模型就是一套二进制数据集,SaaS化需要做外围的服务化、产品化封装,同一套模型可以在不同的算力平台部署,提供截然不同的服务化API。

img

我们可以简单看下当下比较热门的几大供应商提供的API文档:

  1. OpenAI-会话补全
  2. DeepSeek-会话补全
  3. 硅基流动-会话补全
  4. Ollama-会话补全

硅基流动和Ollama都属于大模型算力/治理平台。他们不研发大模型,他们只是大模型的搬运工。

你把大模型理解成微服务集群,把硅基流动和Ollama理解成微服务发布平台就欧了。

大概扫一眼,你会发现核心API都差不多,毕竟有OpenAI珠玉在前,好多系统都已经对接了OpenAI的API了。后发的大模型为了兼容,降低接入难度,基本上都和OpenAI的API大差不差。

就像是mysql,尽管数据库产品类型枝繁叶茂,但是大家都兼容SQL语法。

我们这里只讨论【会话补全】这一个点,我们发现会话补全接口的输入/输出大概都是酱紫:

输入

{
  "stream": false, // 是否是流式输出(要不要SSE)
  "model": "deepseek-chat", //选用的哪个模型
  "messages": [ // 历史对话消息,因为大模型无状态,所以按场景提供一定数量的历史消息
    {
      "content": "You are a helpful assistant",
      "role": "system"
    },
    {
      "content": "Hi", //消息内容
      "role": "user" //消息类型
    }
  ],
  "tools": null, //外部函数列表,【函数调用】能力在 API 层面的支持
  "frequency_penalty": 0,  //无关紧要的模型行为控制参数
  "presence_penalty": 0, //无关紧要的模型行为控制参数
  "temperature": 1, //无关紧要的模型行为控制参数
  "top_p": 1, //无关紧要的模型行为控制参数
  "logprobs": false, //无关紧要的模型行为控制参数
  "top_logprobs": null //无关紧要的模型行为控制参数
}

所有我们不理解的参数都无关紧要!

输出

{
  "id": "<string>", //无关紧要
  "choices": [
    {
      "message": {
        "role": "assistant",
        "content": "<string>", // 大模型生成的内容
        "reasoning_content": "<string>",
        "tool_calls": [  //需要发起的【函数调用】
          {
            "id": "<string>",
            "type": "function",
            "function": {
              "name": "<string>",
              "arguments": "<string>"
            }
          }
        ]
      },
      "finish_reason": "stop" //有点重要,但是我们先不管
    }
  ],
  "usage": {  //token使用量 计数、计费
    "prompt_tokens": 123,
    "completion_tokens": 123,
    "total_tokens": 123
  },
  "created": 123,  //无关紧要
  "model": "<string>",  //无关紧要
  "object": "chat.completion"  //无关紧要
}

看到这里是不是已经开始跃跃欲试了?是不是感觉搞个垂直领域的智能体有手就行了?

RAG架构

除非是围绕特定业务场景结合私域数据训练的专用大模型,不然涉及到一些企业内部的私域信息时,通用大模型也只能不懂装懂,给你现编。

比如说,你找大模型问【DJob如何接入、使用?】,那除非是有训练大模型时投喂了相关的资料,不然大模型只能是给你现编了。

img

考虑到专用大模型的成本,工程上解决这个问题一般是通过外挂知识库来实现。

  1. 结合具体业务场景,将相关的文档啊、资料啊提前录入到【知识库】中。
  2. 用户提交一个【输入】后,我们先使用用户【输入】作为搜索条件,去【知识库】中搜索得到相关的【资料】。
  3. 将用户【输入】和【资料】一起提供给大模型。

这个【知识库】组件的具体选型属于技术细节,简单的可以用mysql、elasticsearch,如果想提升【知识库搜索结果】的匹配度,也可以用最近比较火的【向量数据库】。

添加了RAG后,流程大概酱紫:

image-20250804211841867

详见:一文读懂:大模型RAG(检索增强生成)含高级方法

MCP协议

可以看到,将大模型作为一个【函数调用】的规划引擎,借助它的推理和生成能力,可以实现复杂的业务流程。如果说大模型是【脑】,那提供给大模型规划、使用的【函数】就是它的【手】和【脚】。有脑有手的大模型,可以迸发出巨大的业务潜力!

那如何打通大模型和传统软件系统(如存量微服务)呢?

我们关注的问题,开源社区也在积极关注。这就是MCP协议诞生的背景和目的。

image-20250804211914150

MCP协议介绍

在这里我们不去展开MCP协议的细节,但是我说说我对MCP协议的思考,主要是想打破神秘感、破除MCP迷信。

  1. MCP协议本身并不是什么高精尖的东西,就是一群人合计了下,约定了系统间调用的流程、格式。其实如果不考虑通用谁都可以设计符合自己需求的、领域特定的交互协议。
  2. MCP协议的优势在于它出现的及时且基本满足了常规交互需求,所以很快在社区达成共识了!你想想看五月初那会你刷公众号看到多少MCP的文章!
  3. 不管是MAP、MBP还是MCP,都不重要;但是形成共识很重要!协议达成共识了,开源社区才可以形成合力围绕协议进行生态建设!

我最近在围绕MCP构建中间件智能体,我就发现MCP协议搞得太复杂了!

Spring-AI

好了,现在开始搞Java代码了,首先我们需要熟悉下spring-ai的整套代码架构,一步一步来,一下子扎入细节是很痛苦的!

模型抽象

核心的API实体是Model,这是一个带泛型的纯函数,提供了对大模型能力的顶层抽象:

img

org.springframework.ai.model.Model

大模型的能力本质就是:给它一个输入(request),它给你返回一个输出。

至于输入/输出的具体类型,由细分的子类限定:

img

不同模态的大模型支持不同类型的输入/输出,在这里我们只讨论ChatModel

img

org.springframework.ai.chat.model.ChatModel

img

spring-ai提供了不同平台、不同模型的API集成,开发者只需要提供接口地址、调用凭证即可开箱即用~

聊天会话

考虑到大模型对话是热点场景,spring-ai针对性的提供了会话接口抽象。

img

org.springframework.ai.chat.client.ChatClient

RAG拓展

类似Spring-AOP,spring-ai基于请求横切提供了开箱即用的RAG能力抽象。

imgimg

org.springframework.ai.rag.advisor.RetrievalAugmentationAdvisor

代码示例

基于供应商构建ChatModel

img

构建ChatClient发起会话

img

智能体示例

好了,我们已经自顶向下的理解了大模型的工程化了。现在我们来开发一个【DJob智能助手】吧!

接口骨架

img

大概这么一个POST接口,响应Content-Typetext/event-stream

构造外部函数定义

假设我有这么几个函数可以给大模型提供能力:

img

把上述3个本地方法封装成 ChatClient API认识的【抽象函数】:

img

构建可用的 函数/工具 信息,这里用本地方法来mock。实际使用时可以利用MCP/HTTP/gRPC/Dubbod等实现跨系统调用。

系统提示词

不能让大模型自由发挥,我们要在用户输入的内容以外,给大模型一些定向信息补充或场景限定,帮助大模型更好的解决问题!

img

发起调用

img

  1. 考虑到大模型无状态,所以每次会话时历史消息需要一并输入。
  2. 历史消息可以由前端收集、提交,也可以由后端每次会话存储、收集。这个细节不是很重要。

总结

总之,太阳底下没有新鲜事,工程领域所有的新生事物我们都先当它是mysql好了,没有人比我们Java工程师更懂mysql了!

以上,除了Java代码以外,都是我盲人摸象、脑补出来的内容。如有错误,欢迎指正。