中州养老项目接入百度千帆大模型
Spring Boot 项目接入百度千帆大模型(openai-java SDK)踩坑记录
项目环境:若依(RuoYi)v3.8.8 + Spring Boot 2.5.15 + JDK 11 + Maven 多模块项目
一、我想做什么
需求很简单:在项目中通过openai-javaSDK 调用百度千帆 V2 接口(它兼容 OpenAI 协议),实现一个流式对话——就是像 ChatGPT 那样,回答一个字一个字地"蹦"出来。
代码总共不到 30 行,这篇博客把整个排错过程记录下来,这些坑对于"老项目 + 新 SDK"的组合几乎是必踩的,希望能帮你少走弯路。
先给出最终能跑通的完整代码。
二、最终成功的代码
Maven 依赖(子模块 pom.xml):
<!-- OpenAI Java SDK --><dependency><groupId>com.openai</groupId><artifactId>openai-java</artifactId><version>2.20.1</version></dependency>Java 代码:
packagecom.zzyl.common;importcom.openai.client.OpenAIClient;importcom.openai.client.okhttp.OpenAIOkHttpClient;importcom.openai.core.http.StreamResponse;importcom.openai.models.chat.completions.ChatCompletionChunk;importcom.openai.models.chat.completions.ChatCompletionCreateParams;importjava.util.function.Consumer;publicclassMain{publicstaticvoidmain(String[]args){OpenAIClientclient=OpenAIOkHttpClient.builder()// ⚠️ 不要把真实 API Key 硬编码在代码里提交到仓库!// 建议放到环境变量中读取。获取方式:https://console.bce.baidu.com/iam/#/iam/apikey/list.apiKey(System.getenv("QIANFAN_API_KEY"))// 形如 bce-v3/ALTAK-xxx/xxx.baseUrl("https://qianfan.baidubce.com/v2/")// 千帆 ModelBuilder 平台地址.build();ChatCompletionCreateParamsparams=ChatCompletionCreateParams.builder().addUserMessage("你能做什么")// 对话内容.model("ernie-4.5-turbo-32k")// 可用模型列表可通过 GET https://qianfan.baidubce.com/v2/models 查询.build();StreamResponse<ChatCompletionChunk>chatCompletion=client.chat().completions().createStreaming(params);Consumer<ChatCompletionChunk>consumer=s->s.choices().stream().findFirst().flatMap(c->c.delta().content()).ifPresent(System.out::println);chatCompletion.stream().forEach(consumer);}}💡 安全提示:博客/仓库中的代码永远不要出现真实 API Key。上面用
System.getenv("QIANFAN_API_KEY")从环境变量读取,本地运行前先设置环境变量即可(IDEA 的 Run Configuration 里也可以配)。如果 Key 不小心泄露了,第一时间去控制台重置。
三、第一类坑:依赖版本被 Spring Boot “偷偷降级”
现象:编译全部通过,一运行就报各种奇怪的错
| 报错 | 缺的东西 | Spring Boot 锁定的版本 | SDK 实际需要 |
|---|---|---|---|
NoSuchFieldError: Companion | okhttp 4.x 的 Kotlin 伴生对象 | okhttp3.14.9 | okhttp4.12.0 |
NoSuchMethodError(堆栈含MapperBuilder) | Jackson 2.14+ 才有的withCoercionConfig()方法 | jackson-databind2.12.7 | Jackson2.16.2 |
NoClassDefFoundError: kotlin/enums/EnumEntriesKt | kotlin-stdlib 1.8.20+ 才有的类 | kotlin-stdlib1.5.32 | kotlin-stdlib1.9.25 |
原因:Spring Boot 的"版本仲裁"机制
Spring Boot 项目继承(或导入)了一个叫spring-boot-dependencies的BOM(Bill of Materials,依赖版本清单),它把几百个常用库的版本都"锁死"了,保证它们互相兼容。
这本来是好事,但问题在于:Spring Boot 2.5 是 2021 年的版本,它锁定的都是老版本。而 openai-java 是用 Kotlin 1.9 编译的现代 SDK,需要新版的 okhttp / Jackson / kotlin-stdlib。于是:
- 你在 pom 里引入 openai-java,Maven 会把它依赖的 okhttp 4.x 一起下载
- 但 Spring Boot 的 BOM 说:“okhttp 必须用 3.14.9”,于是 Maven 听 BOM 的,把版本降下去了
- 编译时不缺类(编译只看方法签名大体存在),运行时才发现类里少了字段/方法,于是抛出
NoSuchFieldError/NoSuchMethodError/NoClassDefFoundError“三兄弟”
解决方案:在父 pom 中"抢先声明"高版本 BOM
Maven 的dependencyManagement有一个规则:先声明者优先。所以只要在父 pom的dependencyManagement中、spring-boot-dependencies的前面,导入高版本的 BOM,就能覆盖 Spring Boot 的锁定:
<dependencyManagement><dependencies><!-- ⚠️ 以下三个 BOM 必须声明在 spring-boot-dependencies 之前!顺序很重要 --><!-- 覆盖 Jackson 版本,openai-java 需要 2.14+ --><dependency><groupId>com.fasterxml.jackson</groupId><artifactId>jackson-bom</artifactId><version>2.16.2</version><type>pom</type><scope>import</scope></dependency><!-- 覆盖 okhttp 版本,openai-java 需要 4.x --><dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp-bom</artifactId><version>4.12.0</version><type>pom</type><scope>import</scope></dependency><!-- 覆盖 kotlin-stdlib 版本,openai-java 由 Kotlin 1.9 编译 --><dependency><groupId>org.jetbrains.kotlin</groupId><artifactId>kotlin-bom</artifactId><version>1.9.25</version><type>pom</type><scope>import</scope></dependency><!-- spring-boot-dependencies 放在它们后面 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.5.15</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>改完后一定要验证:
mvn dependency:tree-Dincludes=org.jetbrains.kotlin mvn dependency:tree-Dincludes=com.squareup.okhttp3确认输出中的版本是你期望的新版本,再重新运行。
📌老 Spring Boot 项目 + 新 SDK,遇到运行时"三兄弟"异常(NoSuchFieldError / NoSuchMethodError / NoClassDefFoundError),第一反应就是查依赖版本是不是被 BOM 降级了。okhttp、Jackson、kotlin-stdlib 是重灾区。
四、第二类坑:云平台的模型说下线就下线
坑 5:403 PermissionDeniedException: model_offline
我最开始照着网上教程用的模型是deepseek-r1-distill-qianfan-70b,直接报 403:
PermissionDeniedException: OpenAIError{error={code=model_offline, message=The model is offline}}意思很直白:这个模型已经被平台下线了。
坑 6:401 UnauthorizedException: invalid_model
于是我换成了另一个教程里常见的ernie-speed-8k,结果又报 401:
UnauthorizedException: OpenAIError{error={code=invalid_model, message=The model does not exist or you do not have access to it}}这次是模型名根本不存在(免费系列已整体下线)。注意:这个 401 很容易让人误以为是 API Key 错了,其实 Key 没问题,是模型名的问题。
解决方案:别抄教程里的模型名,实时查询可用列表
千帆提供了查询接口,用自己的 API Key 请求一下就知道当前能用哪些模型(PowerShell 示例):
Invoke-RestMethod-Uri'https://qianfan.baidubce.com/v2/models'`-Headers @{Authorization ='Bearer <你的API Key>'}|%data|%id我实测(2026 年 7 月)可用的对话模型有:ernie-4.5-turbo-32k、ernie-4.5-turbo-128k、ernie-5.0、deepseek-v3.2、kimi-k2.6、glm-5、qwen3.5-*系列等。最终选了ernie-4.5-turbo-32k。
📌 初学者记忆点:云平台的模型名是"易变资源",网上教程里的模型名很快会过时。写代码前先调 models 接口确认;报 401/403 时先怀疑模型名,别急着重置 Key。
五、第三类坑:所谓"OpenAI 兼容"并不是 100% 兼容(最隐蔽的问题)
坑 7:OpenAIInvalidDataException: 'choices' is invalid
依赖修好了、模型换对了,满心欢喜地运行,结果:
Exception in thread "main" com.openai.errors.OpenAIInvalidDataException: 'choices' is invalid, received [{index=0, delta={content=你好, role=assistant}, flag=0}]注意看报错里的内容:模型其实已经成功回复了"你好"!数据都拿到了,却在 SDK 解析这一步挂掉了。
排查过程:绕过 SDK,直接看原始 HTTP 响应
排查这类问题有个很有用的思路:把 SDK 甩开,直接用 HTTP 工具请求接口,看服务器到底返回了什么。我用 PowerShell 直接请求千帆的流式接口,发现它返回的 chunk 和 OpenAI 官方规范有两处偏差:
- choices 元素里缺少
finish_reason字段(OpenAI 规范中必须有,可以为 null) - 多了一个非标准的
flag字段
而且我对比了 ernie 系和 deepseek 系模型,chunk 格式完全一样——说明换模型没用,这是千帆平台的统一行为。
问题出在 SDK 侧:openai-java0.22.0(0.x 老版本)会对响应做严格校验,字段和规范对不上就直接抛异常。
解决方案:升级 openai-java 到 1.0+(我用的 2.20.1)
1.0 之后的版本改成了宽松校验,能容忍这种字段差异。但升级后有两处要跟着改:
① 包路径变了(1.0+ 重构了包结构):
// 旧版(0.x)importcom.openai.models.ChatCompletionChunk;importcom.openai.models.ChatCompletionCreateParams;// 新版(1.0+)importcom.openai.models.chat.completions.ChatCompletionChunk;importcom.openai.models.chat.completions.ChatCompletionCreateParams;② 流式消费建议用空安全写法,避免某些 chunk 的 content 为空时报错:
chatCompletion.stream().forEach(s->s.choices().stream().findFirst().flatMap(c->c.delta().content()).ifPresent(System.out::println));📌 初学者记忆点:“OpenAI 兼容” ≠ 100% 兼容。第三方平台的响应经常有字段增减。遇到 SDK 解析报错,先绕过 SDK 抓原始 HTTP 响应对比,确认是数据问题还是 SDK 问题,再决定是升级 SDK 还是换调用方式。
六、完整踩坑链路回顾
① NoSuchFieldError: Companion → okhttp 被降级,前置 okhttp-bom 4.12.0 ② NoSuchMethodError (MapperBuilder) → Jackson 被降级,前置 jackson-bom 2.16.2 ③ NoClassDefFoundError: EnumEntriesKt → kotlin-stdlib 被降级,前置 kotlin-bom 1.9.25 ④ 403 model_offline → 模型下线,换模型 ⑤ 401 invalid_model → 换的模型也下线了,GET /v2/models 查真实列表 ⑥ 'choices' is invalid → 千帆 chunk 非标准 + SDK 严格校验, 升级 openai-java 0.22.0 → 2.20.1 并适配新包结构 ⑦ 最终运行成功,流式输出模型回复 ✅七、结语
编译通过 ≠ 能跑。运行时的
NoSuchFieldError/NoSuchMethodError/NoClassDefFoundError几乎都是依赖版本冲突。看堆栈里缺的类/方法属于哪个库,用mvn dependency:tree查它被解析成了什么版本、被谁锁定了。改依赖后要验证,别凭感觉。每次改完 pom,用
mvn dependency:tree -Dincludes=xxx确认版本真的变了;IDEA 里记得 Reload Maven Project,否则 IDE 还在用旧依赖。对接第三方服务时,学会"降到 HTTP 层"排查。SDK 只是 HTTP 的封装,当 SDK 行为诡异时,用 Postman / curl / PowerShell 直接请求接口看原始响应,很多"玄学问题"立刻现出原形。
最后再强调一下安全问题:API Key 千万不要硬编码进代码提交到 Git 仓库(尤其是公开仓库),用环境变量或配置中心管理;万一泄露立刻去控制台重置。
记录时间:2026 年 7 月。文中模型可用性、SDK 版本均为当时实测,读者实践时请以最新情况为准。