当前位置: 首页 > news >正文

Spring AI 集成 DeepSeek 原生供应商:告别 OpenAI 兼容层,获取结构化推理过程

背景

项目中一直用 Spring AI 的 OpenAI 兼容层调用 DeepSeek API(把spring.ai.openai.base-url指向https://api.deepseek.com)。这种方式能跑,但有几个痛点:

  • 拿不到reasoning_content:DeepSeek 的推理过程(CoT)不会以结构化字段返回,只能让模型把思考过程包在<think>标签里,前端再用状态机做标签解析,极其脆弱
  • 缺失 DeepSeek 特有 API:Prefix Completion、DeepSeek 特有的参数等都无法使用
  • 配置语义不清晰:写着openai,实际调的却是 DeepSeek,维护成本高

Spring AI 在 1.x 版本已经官方支持了 DeepSeek,本文记录完整的迁移过程。


一、添加依赖

pom.xml中添加 DeepSeek Starter(版本由 BOM 1.1.3 统一管理):

<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-model-deepseek</artifactId> </dependency>

二、配置供应商

application-dev.yml中添加 DeepSeek 配置块:

spring: ai: deepseek: api-key: sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx chat: options: model: deepseek-v4-flash temperature: 1.0

API Key 建议通过环境变量注入,避免硬编码。


三、注册 Bean

AiConfig.java中注册 DeepSeek 的 ChatModel 和 ChatClient:

import org.springframework.ai.deepseek.DeepSeekChatModel; @Bean("deepseekChatModel") public ChatModel deepseekChatModel(DeepSeekChatModel deepSeekChatModel) { return deepSeekChatModel; } @Bean("deepseekChatClient") public ChatClient deepseekChatClient( @Qualifier("deepseekChatModel") ChatModel deepseekChatModel, MessageFormatAdvisor messageFormatAdvisor, LifecycleToolCallAdvisor lifecycleToolCallAdvisor, TaskProgressAdvisor taskProgressAdvisor, RetryAdvisor retryAdvisor) { return ChatClient.builder(deepseekChatModel) .defaultToolContext(new HashMap<>(Map.of("debug", true))) .defaultAdvisors( messageFormatAdvisor, lifecycleToolCallAdvisor, taskProgressAdvisor, retryAdvisor ) .build(); }

四、Controller 改造 — 原生推理流式输出

改造前:每个 SSE Chunk 拿到的是AssistantMessage<think>标签可能被切碎在多个 Chunk 里,需要维护复杂的状态机做拼接。

改造后:使用DeepSeekAssistantMessagereasoningContenttext是两个独立字段:

import org.springframework.ai.deepseek.DeepSeekAssistantMessage; .concatMap(response -> { AssistantMessage output = response.getResult().getOutput(); List<ServerSentEvent<ChatChunk>> events = new ArrayList<>(); if (output.getToolCalls() != null && !output.getToolCalls().isEmpty()) { // handle tool calls return Flux.fromIterable(events); } if (output instanceof DeepSeekAssistantMessage dsMsg) { String reasoning = dsMsg.getReasoningContent(); if (reasoning != null && !reasoning.isEmpty()) { state.accumulateReasoning(reasoning, events); } } String text = output.getText(); if (text != null && !text.isEmpty()) { state.flushReasoning(events); events.add(createEvent("message", state.messageId(), text, null)); } return Flux.fromIterable(events); })

五、推理内容缓冲优化

reasoningContent以 Token 粒度到达,每个 SSE Chunk 可能只有一个字,直接推给前端会导致渲染碎片化。需要在服务端按语义边界缓冲:

private static class StreamState { private static final int REASONING_FLUSH_THRESHOLD = 50; private static final Pattern SENTENCE_BOUNDARY = Pattern.compile("[。!?.!?\n]+"); private final StringBuilder reasoningBuffer = new StringBuilder(); public void accumulateReasoning(String delta, List<ServerSentEvent<ChatChunk>> target) { reasoningBuffer.append(delta); String buf = reasoningBuffer.toString(); var matcher = SENTENCE_BOUNDARY.matcher(buf); int lastEnd = 0; while (matcher.find()) { String segment = buf.substring(lastEnd, matcher.end()).trim(); if (!segment.isEmpty()) { target.add(createEvent("thought", "reasoning", segment, null)); } lastEnd = matcher.end(); } reasoningBuffer.delete(0, lastEnd); if (reasoningBuffer.length() > REASONING_FLUSH_THRESHOLD) { target.add(createEvent("thought", "reasoning", reasoningBuffer.toString(), null)); reasoningBuffer.setLength(0); } } }

触发策略:

  • 遇到句号/问号/感叹号/换行 → 按标点切分,整句发出
  • 缓冲区积累超过 50 字符无标点 → 强制整块发出
  • 切换到文本输出或工具调用 → 排空缓存

六、效果对比

改造前:前端收到逐个单词的thought事件:

event: thought data: {"content":"The","role":"thought"} event: thought data: {"content":"user","role":"thought"} event: thought data: {"content":"wants","role":"thought"}

改造后:前端收到完整的语义段落:

event: thought data: {"content":"The user wants me to add a new feature.","role":"thought"} event: thought data: {"content":"Let me think about the best approach.","role":"thought"}

总结

Spring AI 官方 DeepSeek Starter 带来的核心收益:

1.结构化推理内容DeepSeekAssistantMessage.getReasoningContent()直接获取 CoT,无需<think>标签 hack

2.服务端缓冲:按语义边界批量下发,前端零改动即可获得平滑渲染

3.配置语义化spring.ai.deepseek.*一目了然

4.扩展性:无缝使用 DeepSeek 特有功能(Prefix Completion、Reasoning 多轮对话等)

http://www.cnnetsun.cn/news/3035278.html

相关文章:

  • OSINT Cheat Sheet:一份覆盖全场景的开源情报工具速查表
  • RSS 生态全收录:一份持续更新的资源清单
  • Query Loop 如何驱动任务闭环
  • YOLO检测头改进- 第38篇:Anchor-Free与Anchor-Based检测头融合方案
  • UnityUI中使用中文文本
  • 2026上海小程序开发公司排行:哪家好?商城、会员与预约项目怎么选
  • 【JAVA毕设源码分享】基于SpringBoot的智慧医疗问诊系统的设计与实现(程序+文档+代码讲解+一条龙定制)
  • 紫外线变色硅胶垫片,为您的防晒衣加上一双“慧眼”
  • 除了大厂算法岗,AI大模型应用开发还能做什么?这5个方向缺口
  • 【Springboot毕设全套源码+文档】基于SpringBoot的停车管理系统的设计与实现(丰富项目+远程调试+讲解+定制)
  • 终于找到免费开源TTS模型,克隆声音不要钱,本地电脑也能跑
  • 【信道估计】太赫兹集成UM-MIMO和IRS系统的混合球面与平面波信道建模与估计【含Matlab源码 15665期】
  • 手把手教你用8款AI论文写作软件,极速搞定各类论文
  • 专业级AI无人直播系统
  • K-431S高性能增韧型瞬干胶技术参数与选型参考
  • playwright-setInputFiles 上传文件
  • 2026年GEO优化系统源码如何选?三大核心指标帮你避坑
  • 道歉声明登报如何写?道歉声明登报咋办理?多少钱?
  • 【朱红大漆戗金梅花碗】一剪红梅献祖国
  • 选题指导,LW(文理科均可)
  • 2026年泉州GEO优化优质服务商最新推荐
  • 重塑创业思维、培养创新能力必看书籍推荐
  • 帝国理工学院研究团队破解“电力调度难题“
  • 斯坦福、伯克利等:如何从零开始“喂饱“一个能干活的AI智能体?
  • 在 ComfyUI 中遇到 “Image blocked by safety filter” 提示
  • 什么是描述性统计?它在数据分析中的作用是什么?
  • 第03课:GDB 常用的调试命令概览
  • 如何分析干部队伍年龄结构,找准“盖层”和“断层”?
  • 老中青比例不是“数字游戏”,如何科学把握年轻干部选拔标准?
  • 101010旁观:芒格与巴菲特,力荐思维模型