基于Spring AI RAG 的AI知识库前端交互实现

基于Spring AI RAG 的AI知识库前端交互实现

在企业时负责企业AI知识库项目的前端模块开发,核心工作是对接 Spring AI RAG 检索服务,落地整套AI问答交互能力。很多人觉得AI前端只是简单调接口,但实际落地中,流式应答、富文本渲染、会话状态管控都是实打实的工程化问题。

本文结合我的项目代码,分享核心实现思路:SSE流式问答、Markdown安全渲染、历史会话管理、RAG检索结果展示,聚焦前端工程化实现与踩坑优化

一、业务背景与技术选型

后端基于 Spring AI 搭建 RAG 链路:文档向量化、语义检索、上下文增强、大模型流式生成。前端不再是传统HTTP一次性请求响应模式,需要适配服务端分片流式输出特性。

技术选型核心思路:

  • 通信:采用 SSE(Server-Sent Events) 替代轮询/WebSocket,适配RAG单向流式推送场景,轻量无心跳开销

  • 渲染:自定义 Markdown 解析渲染,解决AI回答代码块、表格、公式解析错乱及XSS问题

  • 状态:中心化管理会话列表、当前对话、检索上下文,保证状态可回溯

二、核心代码实现与思路解析

2.1 封装SSE流式请求(对接Spring AI RAG)

RAG问答的核心痛点是响应时长不可控、数据分片返回。传统axios请求会等待全部数据返回后才渲染,用户体验极差。因此我封装了通用SSE请求工具,支持增量接收、自动重连、手动销毁、异常拦截。

typescript 复制代码
// 封装SSE RAG流式请求工具
export const createRagStreamRequest = (
  url: string,
  params: Record<string, any>,
  onMessage: (chunk: string) => void,
  onEnd: () => void,
  onError: (err: any) => void
) => {
  // 序列化RAG检索参数:问题、知识库ID、检索条数、上下文ID
  const searchParams = new URLSearchParams({
    ...params,
    timestamp: String(Date.now())
  })

  const sse = new EventSource(`${url}?${searchParams.toString()}`)

  // 接收Spring AI分片数据
  sse.onmessage = (e) => {
    if (!e.data) return
    // 增量推送分片数据,实时渲染
    onMessage(e.data)
  }

  // 流式响应结束回调
  sse.addEventListener('end', onEnd)

  // 异常处理:网络中断、服务熔断、超时
  sse.onerror = (err) => {
    sse.close()
    onError(err)
  }

  // 返回销毁方法,防止内存泄漏、多请求冲突
  return {
    destroy: () => sse.close()
  }
}

核心优化点 :单例管控SSE实例,每次发起新问答前销毁上一次链接,彻底解决快速提问导致的数据流错乱、旧数据残留问题。

2.2 增量数据拼接与流式渲染

Spring AI RAG 返回的是碎片化文本分片,不能直接渲染,需要前端有序拼接、去空、去重,同时实时更新视图,实现打字机效果。

typescript 复制代码
// 组件内流式数据处理
const streamContent = ref('')
let streamTimer: number | null = null

// 初始化流式问答
const startRagChat = async (question: string) => {
  // 重置本次问答内容
  streamContent.value = ''
  // 销毁上一次SSE链接
  if (sseInstance) sseInstance.destroy()

  sseInstance = createRagStreamRequest(
    '/api/springai/rag/stream',
    { question, sessionId: currentSessionId.value },
    // 分片接收回调
    (chunk) => {
      // 防抖更新视图,避免高频渲染卡顿
      if (streamTimer) clearTimeout(streamTimer)
      streamTimer = window.setTimeout(() => {
        streamContent.value += chunk
      }, 16)
    },
    // 问答结束
    () => {
      saveChatHistory()
    },
    // 异常兜底
    () => {
      streamContent.value += '\n\n问答异常,请重试'
    }
  )
}

2.3 安全可控的Markdown渲染方案

AI RAG检索结果常包含代码块、表格、引用、公式,原生渲染容易出现样式错乱、XSS注入风险。我基于 marked 做了二次封装,增加白名单过滤、代码高亮、安全转义。

typescript 复制代码
import { marked } from 'marked'
import DOMPurify from 'dompurify'

// 自定义Markdown渲染
export const renderRagMarkdown = (content: string) => {
  // 1. 基础解析MD语法
  const rawHtml = marked.parse(content)
  // 2. XSS安全过滤,过滤恶意标签
  const safeHtml = DOMPurify.sanitize(rawHtml, {
    ADD_TAGS: ['iframe'],
    REMOVE_ATTR: ['onclick', 'onload']
  })
  return safeHtml
}

同时针对RAG场景做了适配:统一代码块样式、检索引用文本高亮、知识库来源信息单独展示,极大提升回答可读性。

2.4 历史会话与检索状态管理

为了实现多会话切换、历史问答回溯,我通过状态中心化管理会话生命周期,绑定唯一 sessionId,与Spring AI服务端会话上下文打通。

typescript 复制代码
// 会话核心状态简易封装
export const useChatSession = () => {
  const sessionList = ref<SessionItem[]>([])
  const currentSessionId = ref('')

  // 创建新会话
  const createNewSession = () => {
    const id = `rag_session_${Date.now()}`
    currentSessionId.value = id
    sessionList.value.unshift({
      id,
      title: '新知识库问答',
      createTime: Date.now(),
      chatList: []
    })
  }

  // 切换会话、持久化会话数据
  const switchSession = (id: string) => {
    currentSessionId.value = id
    // 切换时销毁当前SSE链接,避免数据串流
    if (sseInstance) sseInstance.destroy()
  }

  return { sessionList, currentSessionId, createNewSession, switchSession }
}

三、项目核心亮点与工程化总结

  1. 流式体验优化:基于SSE轻量长链接实现RAG问答流式输出,通过单例销毁、防抖渲染解决数据流混乱、页面卡顿问题,体验优于传统轮询方案。

  2. 安全富文本渲染:结合marked+DOMPurify实现安全MD渲染,适配AI知识库专属内容格式,规避XSS风险,统一全局展示样式。

  3. 状态闭环管理:会话ID与后端Spring AI上下文联动,实现会话创建、切换、持久化、销毁全生命周期管理,保证检索问答上下文连贯性。

  4. 高可扩展性:通信、渲染、状态逻辑分层解耦,后续可快速接入知识库检索溯源、问答打分、对话记忆等扩展功能。

四、写在最后

这次开发最大的感悟:AI前端交互核心不在于"调通接口",而在于适配大模型的异步、流式、不确定性特性。通过工程化封装,解决了RAG场景下的体验与稳定性问题,也让整个知识库交互模块具备了生产级落地能力。

相关推荐
AI产品库1 小时前
控制电脑,接管你的浏览器,PC端Agent工具解析
人工智能·电脑
硅基流动1 小时前
宇信科技与硅基流动达成战略合作,加速金融智能化升级
人工智能·科技·金融
breeze jiang1 小时前
React + TypeScript 编辑表单:为什么要区分 name 和 editingName
前端·typescript
莉莉周的成长实验室1 小时前
2026年七夕海报用什么AI工具可以做?品牌运营实测3个高频场景
大数据·人工智能
工业机器视觉设计和实现1 小时前
cifar10训练突破80分(三,摸到pytorch尾灯!)
人工智能·pytorch·cudnn微积分
chatexcel1 小时前
ChatExcel AI Word 升级:多模态读资料、按模板生成、智能排版配图,还能批注修订和联动Excel 复核
人工智能·ai·word·excel·ai写作
Listen·Rain1 小时前
用AI开发出一个AI
java·人工智能·spring boot·tomcat·intellij-idea·mybatis·visual studio
2601_950790681 小时前
2026年新手八字排盘应用推荐:AI提示词、小白复盘和天乙八字排盘App怎么选?附完整实测测评
人工智能·天乙八字排盘·命枢
刘小八1 小时前
Spring AI 结构化输出:稳定生成可校验的业务 JSON
人工智能·spring·json