同一个问题写了 4 遍:官方 SDK / HTTP / Spring AI / LangChain4j,到底该选哪个

适合人群:Spring Boot 会一点,想接大模型,但被"四种接入方式"卡在第一步的同学。

上一篇把 12 个名词理清了,但那只是纸面功夫。真正打开 IDEA,第一个问题立刻变成:

这四种写法,我到底敲哪种?

于是我把同一句话------「你是谁?」------用四种方式各写了一遍。代码全部贴出来,谁能用、谁坑多,跑完就清楚了。


一、先把结论放这儿

四种方式的横向对比

如果你只想记住一句话:

先用官方 SDK 打通,确认 Key 和网络没问题;真正写业务时切 Spring AI。

理由很简单:SDK 是官方维护的,出错信息最直接,能帮你把「Key 对不对、模型名对不对、网络通不通」这三个基础问题一次排干净。等问题都排除了,再上框架,才不会把「环境问题」误判成「框架问题」。

HTTP 直连不是给日常开发用的,它属于「SDK 不支持这门语言」或「我要看清协议到底发了什么」的场景。


二、统一测试条件(很重要)

四种方式对比,最怕变量不一致。我固定了这几个条件:

项 值
模型 qwen-plus(同一家的同一个模型)
项目 Spring Boot 3.4.4 + JDK 21
提示词 都是「你是谁?」
Key 同一个,走环境变量

为什么强调"同一个模型":不同厂商返回结构完全不同。拿 A 家的 SDK 和 B 家的框架比"代码行数",比的其实是厂商差异,不是框架差异。

Key 别写死在代码里:

yaml 复制代码
# application.yml
spring:
  ai:
    dashscope:
      api-key: ${AI_DASHSCOPE_API_KEY}
powershell 复制代码
# Windows 设置环境变量(设完要重开 IDEA)
setx AI_DASHSCOPE_API_KEY "sk-xxxxxx"

顺手一句:Key 提交到 Git 等于公开。公开仓库有爬虫专门扫 sk- 开头的字符串,几分钟就会被抓走。


三、方式一:官方 SDK(最直接)

xml 复制代码
<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>dashscope-sdk-java</artifactId>
  <version>2.19.1</version>
</dependency>
java 复制代码
public class SdkAiInvoke {

    public static GenerationResult callWithMessage() throws Exception {
        Generation gen = new Generation();

        Message systemMsg = Message.builder()
                .role(Role.SYSTEM.getValue())
                .content("You are a helpful assistant.")
                .build();
        Message userMsg = Message.builder()
                .role(Role.USER.getValue())
                .content("你是谁?")
                .build();

        GenerationParam param = GenerationParam.builder()
                .apiKey(System.getenv("AI_DASHSCOPE_API_KEY"))
                .model("qwen-plus")
                .messages(Arrays.asList(systemMsg, userMsg))
                .resultFormat(GenerationParam.ResultFormat.MESSAGE)
                .build();

        return gen.call(param);
    }
}

跑通之后,控制台会打印类似这样的结构:

ini 复制代码
usage: input_tokens=22, output_tokens=75
choices[0].message.content: 我是通义千问......

两个细节,教程通常一笔带过:

① .resultFormat(MESSAGE) 这一行不能省。 不设的话返回的是扁平文本,取值方式完全不同。你按 getChoices().get(0).getMessage().getContent() 去取会拿到 null,然后开始怀疑人生。

② model 传的是模型名,不是应用 ID。 换成 qwen-max、qwen-turbo 只改这一行,这是 SDK 最舒服的地方。

它的问题也很明显:绑死一家。哪天要换 DeepSeek,代码得重写一遍;要做 RAG、工具调用,这些高级能力它基本不管,全得自己搭。


四、方式二:HTTP 直连(理解协议的最佳方式)

bash 复制代码
curl -X POST https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation \
--header "Authorization: Bearer $DASHSCOPE_API_KEY" \
--header 'Content-Type: application/json' \
--data '{
    "model": "qwen-plus",
    "input": { "messages": [
        { "role": "system", "content": "You are a helpful assistant." },
        { "role": "user", "content": "你是谁?" }
    ]},
    "parameters": { "result_format": "message" }
}'

Java 里用 Hutool 拼一遍,核心就三行:

java 复制代码
HttpResponse response = HttpRequest.post(url)
        .addHeaders(headers)
        .body(requestBody.toString())
        .execute();

这里有个非常容易搞混的点,我第一次就踩了:

DashScope 有两个长得很像的地址,用途完全不同:

地址 传什么 谁在用
/api/v1/apps/{APP_ID}/completion app_id + prompt,调平台上建好的应用 平台应用调用
/api/v1/services/aigc/text-generation/generation model + messages,直连模型 SDK、Spring AI、LangChain4j 全走这条

很多教程给的 curl 是前者(带 APP_ID),但代码里要的是后者。照抄 curl 到 Java 里 404,八成就是这个原因。

HTTP 直连真正的价值不在生产,而在于:它是唯一能让你看见"框架到底替你做了什么"的方式。把这条 curl 跑通一次,后面看 Spring AI 的报错会清晰很多。


五、方式三:Spring AI(推荐日常用)

先说明一个前提:Spring AI 官方对国产模型覆盖不全 ,很多只提供 OpenAI 兼容接入。所以在 Spring Boot 里接通义,实际用的是 Spring AI Alibaba------它是 Spring AI 的扩展,用法完全一致。

xml 复制代码
<dependency>
  <groupId>com.alibaba.cloud.ai</groupId>
  <artifactId>spring-ai-alibaba-starter</artifactId>
  <version>1.0.0-M6.1</version>
</dependency>

注意:这个版本要额外加仓库,否则依赖解析不了。

xml 复制代码
<repositories>
  <repository>
    <id>spring-milestones</id>
    <name>Spring Milestones</name>
    <url>https://repo.spring.io/milestone</url>
    <snapshots><enabled>false</enabled></snapshots>
  </repository>
</repositories>

配置只有三行:

yaml 复制代码
spring:
  ai:
    dashscope:
      api-key: ${AI_DASHSCOPE_API_KEY}
      chat:
        options:
          model: qwen-plus

代码分两层,别搞混:

java 复制代码
// 第一层:ChatModel ------ 底层,够简单就直接用
@Resource
private ChatModel dashscopeChatModel;

AssistantMessage output = dashscopeChatModel
        .call(new Prompt("你是谁?"))
        .getResult()
        .getOutput();
System.out.println(output.getText());
java 复制代码
// 第二层:ChatClient ------ 高层,带记忆、Advisor、流式,业务里用它
ChatClient chatClient = ChatClient.builder(dashscopeChatModel).build();

String content = chatClient.prompt()
        .user("你是谁?")
        .call()
        .content();

两种调用方式的区别,用一句话记住:ChatModel 是引擎,ChatClient 是方向盘。

引擎能发动车,但你要拐弯、要开空调,得靠方向盘。后面课程里的多轮记忆、RAG、工具调用,全是往 ChatClient 上挂。

Spring AI 最大的价值是最后这一点:

换模型只改一行配置。 从 qwen-plus 换到 deepseek-chat,代码一个字不动------前提是这家模型有对应的 starter。

代价是多了一层抽象,报错信息经常被包一层,排查时要学会往上翻 cause。


六、方式四:LangChain4j(代码最短)

xml 复制代码
<dependency>
  <groupId>dev.langchain4j</groupId>
  <artifactId>langchain4j-community-dashscope</artifactId>
  <version>1.0.0-beta2</version>
</dependency>
java 复制代码
ChatModel model = QwenChatModel.builder()
        .apiKey(System.getenv("AI_DASHSCOPE_API_KEY"))
        .modelName("qwen-plus")
        .build();

String answer = model.chat("你是谁?");

五行。 这是四种里最少的。

代价是:LangChain 官方不支持 阿里系模型,只能用社区版(community 包)。社区版的通病是------版本号带 -beta、更新节奏跟着主版本走、文档比主库少一大截。

顺带一个版本坑:老教程里写的是 ChatLanguageModel,新版本已经统一叫 ChatModel。跟着两年前的博客敲,第一个报错就是 cannot find symbol。

如果你团队本来就在用 LangChain4j,那没问题;如果是新项目、Spring 技术栈,我不建议为了省这几行代码换生态。


七、怎么选:一张图对号入座

按场景对号入座

补一句图里没写的:别用"哪个更强"来选,用"我的下一步要做什么"来选。

  • 下一步是做 RAG、工具调用、多轮对话 → Spring AI,因为这些东西它内置
  • 下一步只是"把模型接进来试试" → SDK,够用且最省心
  • 下一步要写网关、要跨语言 → HTTP
  • 下一步要做复杂链式编排 → LangChain4j

八、真正拉开差距的:加一个「多轮对话」

上面都是单轮。但真实的 AI 应用几乎都要多轮------用户问「那它多少钱」,模型得记得「它」指什么。

这一加,四种方式的差距立刻出来了:

方式 要改什么 大概工作量
官方 SDK 自己维护一个 List<Message>,每次请求把历史全塞进去,还要自己控制长度不超 token +15 行,且容易写出内存泄漏
HTTP 直连 同上,还得手动拼 JSON 数组、算 token +20 行
Spring AI 加一个 Advisor +3 行
LangChain4j 用 AiServices 声明一个接口,框架代理实现 +5 行

Spring AI 那三行长这样:

java 复制代码
ChatMemory chatMemory = MessageWindowChatMemory.builder()
        .maxMessages(20)   // 只保留最近 20 条
        .build();

ChatClient chatClient = ChatClient.builder(chatModel)
        .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
        .build();

之后的每一轮对话,框架自动帮你做三件事:读历史 → 拼进 prompt → 把新的一轮写回存储。你只写业务。

注意 MessageChatMemoryAdvisor.builder(chatMemory),不是 new MessageChatMemoryAdvisor(...)。老版本是构造器,新版本统一成了 builder------又一个版本坑。

这就是我说「真正写业务时切 Spring AI」的实际含义:省下来的不是最初那 20 行,而是后面每一次加功能时的重复劳动。


九、5 个坑,全是我踩过的

坑 1:教程的版本,和现在的版本对不上

这是最要命的一个。你搜到的教程大多写于 2025 年初,那时 Spring AI 还在 1.0.0-M6,starter 叫 spring-ai-openai-spring-boot-starter;现在早就变了。

组件 2025 年初教程 截至 2026 年 11 月
Spring AI 1.0.0-M6,starter 名 spring-ai-*-spring-boot-starter 1.0.x / 1.1.x / 2.0.x,starter 名 spring-ai-starter-model-*
Spring AI Alibaba 1.0.0-M6.1,坐标 spring-ai-alibaba-starter 1.0.0.2 GA 起,1.1.x 线拆成 spring-ai-alibaba-starter-dashscope 等
LangChain4j 1.0.0-beta2,接口 ChatLanguageModel 主库 1.x,community 已到 1.22.0-beta32,接口统一为 ChatModel
dashscope-sdk-java 2.19.1 坐标不变,版本号持续更新

判断口诀:编译不过先怀疑版本,别怀疑自己。 去 Maven 中央仓库看一眼当前版本号,比在报错里泡半小时有用。

坑 2:spring-ai 依赖一直解析失败

早期版本没发到中央仓库,必须加 spring-milestones 仓库(见上面第五节)。报错长得像这样:

复制代码
Could not resolve dependencies ... spring-ai-core:jar:1.0.0-M6

不是网络问题,是仓库没配。

坑 3:base-url 多写一个 /v1,直接 404

用 OpenAI 兼容模式接通义时,地址是:

arduino 复制代码
https://dashscope.aliyuncs.com/compatible-mode

结尾停在 compatible-mode,不要自作多情加 /v1。 我在这上面排查了快两小时,最后发现就是多敲了四个字符。

坑 4:把「应用调用」的地址当成「模型调用」

就是第四节那张表。/apps/{APP_ID}/completion 和 /services/aigc/.../generation 是两个接口,参数结构完全不同。

坑 5:Key 写进 yml,然后推到了 Git

提交前检查一遍,.gitignore 里应该有:

gitignore 复制代码
target/
.idea/
application-local.yml
.env

已经提交了也别慌------去控制台把这个 Key 删掉重新生成,光删 Git 记录没用,历史提交里还在。

顺手整理了一张报错对照表,搜报错的人可以直接命中:

报错原文 真实原因 怎么修
Could not resolve dependencies ... spring-ai-core:jar 早期版本没发中央仓库 加 spring-milestones 仓库
401 Unauthorized / InvalidApiKey Key 错,或环境变量没生效 setx 之后重开 IDEA ;终端 echo $env:AI_DASHSCOPE_API_KEY 验证
404 Not Found base-url 多了 /v1,或误用了 /apps/{APP_ID} 接口 换回 /compatible-mode 或 /services/aigc/...
cannot find symbol: ChatLanguageModel LangChain4j 新版改名 ChatModel 改类型名,或锁定老版本
No qualifying bean of type 'ChatModel' starter 没引,或包扫描不到 检查依赖坐标 + @ComponentScan 范围

十、我的实际迁移路径(三步)

不是一上来就写 Spring AI,而是分三步,每步都能独立验证:

步骤 做什么 验证标准
1 用 SDK 跑通一句「你是谁」 控制台能打印模型回复
2 换成 HTTP 跑同一个问题 返回的 JSON 结构和 SDK 对得上
3 切到 Spring AI,用 ChatClient 重写 代码里不再出现 dashscope 包名

第三步有个隐藏好处:切完之后,你的业务代码里就不该再出现任何厂商相关的类名了。 如果还有,说明抽象没做干净,将来换模型会很痛。


十一、速查表(建议截图)

我要...... 用这个 关键配置
最快验证 Key 是否可用 官方 SDK resultFormat(MESSAGE) 不能省
看清请求到底发了什么 HTTP 直连 直连模型用 /services/aigc/.../generation
在 Spring 项目里做 AI 应用 Spring AI + Alibaba 加 spring-milestones 仓库
换模型不改代码 Spring AI spring.ai.dashscope.chat.options.model
多轮记忆 / RAG / 工具调用 ChatClient ChatModel 是引擎,ChatClient 是方向盘
代码最短 LangChain4j 社区版 接口叫 ChatModel,不是 ChatLanguageModel

下一篇想写流式输出:为什么你的 AI 回复总是要等三五秒才「啪」地一下蹦出整段,而别人的是一字一字往外冒。这事和框架无关,和 SSE 有关。

相关推荐
hai_android14 分钟前
AudioTrack 深度解析:从原理到实战
android·java·kotlin
胡汉三已被占用35 分钟前
女儿问我“毛毛虫怎么画“,我用 AI 把答案做成了一个开源小程序
ai编程
斯维赤36 分钟前
限流、熔断、降级,你真的分得清吗?
java·后端·架构
誰在花里胡哨3 小时前
Qoder + Blender MCP:对话建模到网页渲染
ai编程·three.js·mcp
aiks_yuan3 小时前
Java CPU 100% 怎么排查:JDK 11 起 jstack 自带线程 CPU 时间,不用再 top -H 换算十六进制(JDK 8、11、17 实测)
java
AI入职记3 小时前
Spring AI 实测:不挂工具,模型真的会瞎编吗?
后端·ai编程
fish8274 小时前
知识库明明有答案,为什么搜不到?我换掉 RRF 修好了混合检索
ai编程
Flynn Liang5 小时前
AiServices 源码级拆解:一个接口如何变成 RAG Agent
java·langchain·源码分析·rag·langchain4j·aiservices
Flynn Liang5 小时前
LangChain4j 1.23 架构全景:Java 大模型应用的 6 层抽象
java·langchain·大模型·rag·ai agent·langchain4j