拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

SpringAI+Vue3实战:AI书法鉴赏系统设计与答辩指南

SpringAI+Vue3实战:AI书法鉴赏系统设计与答辩指南

写这篇博文之前,我先交代一下背景:我去年带过几个本科生的毕业设计,其中有一个选题就是"基于SpringAI+Vue3墨韵书法鉴赏系统"。后来这学生顺利过了答辩,源码也整理过一版。今天就把这个项目的完整思路拆开讲,从功能规划、技术选型、SpringAI的接入方式,到Vue3前端的流式输出,再到答辩时老师最爱追问的几个点,一次性打包写清楚。如果你正在准备做类似"AI + 传统文化"方向的毕设,或者想快速上手SpringAI + Vue3这套前后端分离组合,这篇文章可以直接作为你的"抄作业"参考。

1. 先清楚"墨韵书法鉴赏系统"到底是什么

1.1 选题动机:AI+传统文化怎么落到毕设里

很多同学选毕设题目会陷入两个极端:要么是烂大街的"某某管理系统",CRUD写一遍,答辩时老师看一眼就想睡觉;要么是太偏算法,自己搞不定,最后变成了论文堆砌。书法鉴赏系统这个题目的妙处在于,它有一个很明确的"数字文化"场景——用户上传一幅书法作品,系统能自动生成鉴赏文字,包括风格分析、章法点评、用笔特征、文化背景,甚至还能对话式地回答"这幅字和颜真卿早期风格有什么区别"之类的问题。

这个选题能站住脚的核心逻辑是:传统文化数字化是国家支持的方向,而AI大模型恰好擅长"看图说话+知识盘点",书法鉴赏又是一个高度依赖视觉与知识结合的领域。用SpringAI把大模型能力接入后端,用Vue3做交互界面,既体现了工程能力,又有一个新颖的"AI+文化"亮点,毕设查重和创新点都不愁。

1.2 功能全景与角色边界

毕业设计最怕"什么都想做,最后什么都不完整"。我在规划时把系统切成了三个角色:游客、普通用户、管理员。游客只能浏览作品列表和基础详情;普通用户可以上传作品、触发AI鉴赏、收藏、点赞、评论、查看自己的鉴赏历史;管理员负责审核作品、管理用户、管理系统公告和词库标签。

功能上分六块:作品管理(上传、审核、分类)、作品展示(列表、筛选、详情大图)、AI鉴赏(单次生成、对话式提问)、互动模块(收藏/点赞/评论)、个人中心(我的上传、我的鉴赏记录)、系统管理(用户、标签、参数配置)。

这里有一个非常关键的经验:不要一上来就做"AI对话百科",那是大公司做的事。毕设的边界要控制在"以作品为核心,AI围绕作品产生价值"。用户提问也只能围绕当前作品进行,这样Prompt设计和大模型调用都更可控,老师问起来你也能讲清楚。

1.3 技术选型复盘:为什么是SpringAI + Vue3

选型时我对比过几个方案:Python Flask + 直接调大模型API、Spring Boot + HttpClient手动调用、Spring Boot + SpringAI、前端纯静态页面。最后选择了SpringAI + Vue3,原因有三个。

第一,SpringAI是Spring官方生态里相对新的项目,它的价值在于把大模型接入做了统一抽象,你不需要自己写HttpClient封装、鉴权、重试、流式解析那一堆胶水代码。对接阿里云通义千问、本地Ollama这类模型,只需要配置一个spring.ai开头的配置项,然后注入ChatClient就能用了。这对于毕业设计来说,学习成本和代码量都比手动封装低一大截,而且答辩时可以说"用了Spring官方提供的标准化集成方案",属于加分项。

第二,Vue3配合Vite、Pinia、Element Plus,是目前前端的主流组合。Vue3的组合式API(Composition API)比Vue2的Options API更适合写复杂交互,尤其是AI流式输出这种状态不断变化的场景。项目里我用ref和reactive管理会话消息列表,体验比Vue2的data直观很多。

第三,前后端分离的架构本身就是一个成熟工程标准,Spring Boot 3 + MyBatis-Plus + MySQL + Redis做后端,Vue3做前端,这份技术栈写在简历上完全拿得出手。

2. SpringAI在系统里怎么"鉴赏"书法

2.1 Spring AI在你的后端里放在哪个位置

先明确一点,SpringAI不是一个独立的AI能力提供商,它更像是一个"适配器层"。你在后端写业务代码时,只需要面向ChatClient编程,它底层会去调用具体的模型服务。

我的项目里分了两条AI链路:

  • 作品自动鉴赏:用户点击"AI鉴赏",后端把作品图片URL、OCR识别出的文字内容、作品元数据(作者、朝代、字体)拼进Prompt,调用多模态模型,返回一段结构化的鉴赏结果。
  • 对话式提问:用户基于当前作品继续追问,后端把历史对话消息一起发给模型,让模型结合上下文回答。

在pom.xml里添加了这些关键依赖:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud.ai</groupId> <artifactId>spring-ai-alibaba-starter</artifactId> </dependency>

配置项也很简单:

spring: ai: alibaba: datascope: false api-key: ${DASHSCOPE_API_KEY} model: qwen-vl-plus

这里我用的是国内云服务商提供的模型API,从部署和合规角度考虑都比直接连境外服务省心得多。如果你的服务器没有联网条件,也可以用spring-ai-ollama-spring-boot-starter在本地跑一个小模型,比如qwen2.5或llava,效果虽然差一些,但演示完全够用。

2.2 设计一套书法鉴赏的Prompt模板

AI的输出质量,80%由Prompt决定,这一点在书法鉴赏场景里体现得特别明显。如果只丢一张图片过去说"请鉴赏这幅书法",模型大概率会给出"这幅作品笔力遒劲、气韵生动"这类正确的废话。

我最后沉淀下来的Prompt模板分为三个层次:

  1. 角色限定:告诉模型"你是一位精通中国书法史、擅长书法鉴赏的专家,尤其熟悉篆隶楷行草五种字体的技法特征与代表性书家风格"。
  2. 上下文注入:把作品元数据(作者如果已知、朝代、字体、内容文本)和OCR识别到的文字放进去。注意,OCR结果可能不完整,要明确告诉模型"OCR文本仅供参考,不要过度解读"。
  3. 输出约束:要求模型必须从"整体风格判断""用笔特征""结体与章法""墨色与节奏""文化背景与个人感受"五个维度输出,并且以JSON格式返回。

一个简化的Prompt模板长这样:

请以书法鉴赏专家的身份,鉴赏以下书法作品。 作品信息:作者{author},朝代{dynasty},书体{script},释文内容{ocrText}。 输出要求: 1. 整体风格判断(50字以内) 2. 用笔特征(80字以内) 3. 结体与章法(80字以内) 4. 墨色与节奏(50字以内) 5. 文化背景与个人感受(100字以内) 严格输出JSON格式,不要有任何多余解释,格式为: {"style":"","brushwork":"","structure":"","ink":"","culture":""}

为什么要把输出维度拆得这么细?因为答辩时老师需要看到你的"鉴赏结果"是可解释、可评估的。如果你让AI自由发挥,它可能生成一大段散文,你说不清好在哪里。拆成固定维度后,前端可以用卡片形式展示,用户也能逐项查看,这个产品逻辑在答辩时就是加分项。

2.3 OCR+视觉模型:让AI"看见"作品

书法作品鉴赏有一个特殊点:AI不仅要"看"到图片,还要"认"出文字。我测试过几种方案:

  • 只传图片给qwen-vl-plus,它能看懂整体布局,但对单个字内容的识别不稳定,尤其草书。
  • 先用OCR提取文字,再把文字作为上下文传给模型,效果明显提升。

OCR这块我用的是PaddleOCR。考虑到后端是Java,我单独部署了一个OCR微服务,或者直接用Python脚本离线先把作品库里所有图片的文字提取好,存入ocr_text字段。用户上传新作品时,前端先把图片交给OCR服务,返回的文本再连同图片一起提交给后端。这一步的时序很重要,我踩过坑,后面会细说。

用SpringAI的ImagePrompt也可以直接传图片,但多模态模型的"看图+推理"能力目前在书法这种高精度视觉任务上还不够稳。我的建议是:OCR负责"读字",模型负责"品评",两者互补,这才是真正把技术用对地方的做法。

2.4 结果格式化成可解析的JSON

SpringAI的ChatClient默认返回字符串,你需要把它转成对象。我推荐两种方式结合:

  • 如果模型服务支持JSON模式(比如通义千问的response_format设为json_object),在Prompt里强调输出JSON,然后直接用Jackson或Fastjson解析。
  • 更稳妥的做法是让SpringAI使用entity转换器。在Spring AI新版中,可以这样写:
ChatResult result = chatClient.prompt() .text(promptText) .call(); String content = result.getResult().getOutput().getText();

然后解析内容里的JSON字段。注意:模型偶尔会输出前后多余的引号或文字,我在解析器里加了一个方法,先用正则找出第一个{到最后一个}之间的内容,再交给JSON解析,这样基本能避免"格式错误"的崩溃。

如果返回内容里没有JSON,我会做一次兜底:设置一个默认的鉴赏文案,并提示用户"暂时无法获取结构化结果,请重试"。这种容错机制在演示现场非常重要,因为你永远不知道现场网络好不好、模型会不会抽风。

3. Vue3前端把鉴赏体验做顺的几个关键点

3.1 Pinia状态设计:用户、作品、会话

Vue3项目我用了Vite构建,Pinia做全局状态管理。很多新手喜欢把所有请求数据全塞进store,这是大忌。合理的拆法是三个store:

  • userStore:保存token、用户信息、角色权限。
  • workStore:保存当前作品列表、筛选条件、分页参数。这样用户在列表页和详情页之间跳转时,列表状态不会丢。
  • chatStore:保存当前作品下的AI鉴赏会话,包括消息数组、加载状态、是否流式输出中。

前端请求封装我用Axios拦截器统一加token,后端接口如果返回401就跳去登录页。这个标准动作看似简单,但很多毕设代码里没处理好,导致页面刷新后接口全部报错,显得很不专业。

3.2 图片上传与大图预览

书法作品都是高分辨率图片,动辄几MB甚至十几MB。我做了三层处理:

  1. 前端在上传前用canvas压缩图片,长边限制为2000px,质量0.85,转成WebP格式。
  2. 后端接收图片后存入MinIO或本地磁盘,同时生成一个缩略图。
  3. 列表页用缩略图,详情页点开才加载原图。

Vue3里用el-upload组件搭配el-dialog做大图预览,这个已经是标准操作,不多说。有一个容易忽略的点:上传组件一定要限制文件类型和大小,并且在后端也要做校验,两个地方都要判断,不要只靠前端。

3.3 SSE流式输出的前端消费方式

AI鉴赏如果等模型生成完整段落后再返回,用户会等10秒以上,体验非常差。所以我用SSE(Server-Sent Events)做流式输出,后端用SseEmitter或Spring AI自带的流式API,前端通过fetch或EventSource逐段渲染。

Spring AI的流式接口长这样:

Flux<String> stream = chatClient.prompt() .text(promptText) .stream() .content();

前端用fetch读取流式的逻辑可以这样写:

const response = await fetch('/api/ai/chat-stream', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ workId, question }) }); const reader = response.body.getReader(); const decoder = new TextDecoder('utf-8'); let text = ''; while (true) { const { value, done } = await reader.read(); if (done) break; text += decoder.decode(value, { stream: true }); chatStore.updateLastMessage(text); }

注意TextDecoder一定要用{ stream: true },否则中文字符被拆分后会出现乱码。这个问题我排查了整整一个晚上,后面会细讲。

3.4 后台管理界面快速搭建思路

后台管理模块我用的是Element Plus的表格、表单、Tab选项卡组合。整体布局是左侧菜单+右侧路由出口。管理员可以审核作品、管理用户、看统计图表(比如各种书体占比、朝代分布,用ECharts画饼图和柱状图)。

这里有一个小技巧:很多毕设前端都是自己从头写后台,费时费力。你可以参考成熟开源项目(比如若依Vue3版本)的目录和权限思路,但不要直接照搬,因为答辩时老师问到你某个文件做了什么,你可能都说不清。我的做法是简化权限:前端根据userStore里的角色字段控制路由和按钮显隐,后端在接口上做简单的@PreAuthorize或自定义注解校验,够用即可。

4. 后端工程结构、数据库与AI调用封装(答辩重点)

4.1 模块划分:避免"毕业设计=一个Controller"

很多同学的毕设后端就是Controller里写SQL,Service层形同虚设。老师一旦问"你某个业务逻辑怎么复用",他就卡住了。我的建议是后端按功能分包,不一定用微服务,但包结构必须清晰:

com.moyun ├── controller // 接口层 ├── service // 业务层(接口+实现) ├── mapper // MyBatis-Plus Mapper ├── entity // 数据库实体 ├── dto // 请求/响应DTO ├── config // 配置类,比如AI配置、CORS配置 └── common // 通用返回结果、异常处理、工具类

AI调用不要散落在Controller里,单独建一个AiChatService,内部封装ChatClient、Prompt构建、JSON解析、异常兜底。这样如果以后要换模型服务,只改这一个类就够了。

4.2 核心表结构:作品、鉴赏记录、互动

数据库我设计了8张表,其中最重要的4张:

表名字段示例说明
s_workid, title, author, dynasty, script_type, image_url, ocr_text, status, create_time书法作品主表
s_appreciationid, work_id, user_id, style, brushwork, structure, ink, culture, full_content, create_timeAI鉴赏记录
s_chat_messageid, session_id, role, content, create_time会话消息表,支持流动输出
s_userid, username, password, nickname, role, avatar用户表

script_type用字典值:kaimi(楷书)、xingshu(行书)、caoshu(草书)、lìshu(隶书)、zhuanshu(篆书)。鉴赏记录里既要有拆开的五个维度字段,也要有全文的full_content,方便前端做"复制全部文案"的功能。

4.3 AI调用层封装与缓存降级策略

有一个坑是老师最爱问的:"用户频繁点击AI鉴赏,你怎么防止接口被刷爆?"我的方案是三级:

  1. 接口层加Redis限流:同一个用户对同一个作品,5分钟内只能触发一次AI鉴赏。
  2. 缓存层:如果某个作品已经生成过鉴赏结果,再次调用时直接返回已有记录,不再请求模型。
  3. 异常兜底:如果模型API超时或返回格式无法解析,返回预置文案,并在日志里记录失败原因。

这里的缓存设计其实很能体现工程能力。用Spring Cache注解就能实现:

@Cacheable(value = "appreciation", key = "#workId", unless = "#result == null")

但要注意,只有"用户明确要求重新鉴赏"时才清缓存,否则每次都命中缓存,用户会以为系统没有智能。我在前端把"重新鉴赏"做成了一个单独的按钮,点击后先删除缓存再调用接口。

5. 从能跑到好展示:踩过的6个细节坑

5.1 模型API超时与并发控制

第一次联调时,我用的是同步接口,用户点击鉴赏后整个请求挂起,前端一直转圈。后来我做了两个改动:一是把鉴赏接口改成异步+SSE,二是给HTTP客户端配置了连接超时和读取超时。Spring AI的配置里可以这样设置:

spring: ai: alibaba: connect-timeout: 10s read-timeout: 60s

同时在后端用Semaphore控制AI并发数,比如最多同时3个AI请求在跑,多的排队等待。否则演示时多个人同时点鉴赏,模型API一限流,所有请求全挂。

5.2 图片过大导致Base64传输太慢

我最初的设计是前端直接把图片Base64编码塞进请求体发给后端,再由后端转发给模型服务。结果一张6MB的图片转成Base64后接近8MB字符,传输耗时比生成还长。后来我把方案改成了"先上传图片到服务器,拿到URL,再让模型服务通过URL读取图片"。如果模型服务不支持URL,也可以在后端下载图片后压缩再转换Base64。记住:前后端之间传"URL",不传图片本体,这是AV。既减少了带宽,也方便后续复用。

5.3 SSE流式输出中文乱码与分段显示

这个问题排查起来很经典。一开始前端拿到流后发现每个字之间都有空格,或者中文变成乱码。原因有两层:一是Content-Type没有设置为text/event-stream;charset=utf-8,导致浏览器以ISO-8859-1解码;二是TextDecoder解码时没有使用stream: true。

正确的写法:

response.setContentType("text/event-stream;charset=UTF-8"); response.setCharacterEncoding("UTF-8");

前端如果使用EventSource,服务端需要按SSE协议格式输出data:前缀和空行。如果像我一样用fetch手动读流,就只需要用TextDecoder正确处理字节边界。这个知识点虽然基础,但在调试时很容易被忽略。

5.4 AI输出不稳定,如何用few-shot兜底

模型生成的JSON偶尔会多一个字段名,或者把双引号写成中文引号。除了用正则提取JSON外,我还用了一个技巧:在Prompt里加了两个示例(few-shot)。示例一展示完整正确的JSON输出;示例二展示一个"作品信息不完整"时的输出格式,比如作者未知时写作"不详"。这样模型会模仿示例的结构,格式错误率从30%降到了5%以下。

5.5 本地模型 vs 云端API的选型

如果你的演示现场没有外网,云端API就废了。我建议做两手准备:日常开发用云端API(通义千问),答辩前一天在本地Ollama跑一个量化版的多模态模型(比如llava:7b),并预生成好一批作品的鉴赏结果缓存。这样现场即使断网,你点"重新鉴赏"也不会崩溃,因为缓存兜底了。这个"主备切换"的设计,在答辩时一定会被老师当成亮点。

5.6 源码包整理和演示数据准备

做毕业设计源码包时,我见过太多人直接把IDEA项目文件夹压缩发出去,里面带着一堆target目录、node_modules、个人数据库配置,这是非常不好的习惯。正确的源码包应该是:

  • readme.md:项目介绍、技术栈、启动步骤、默认账号。
  • sql/:建库脚本+初始数据,初始数据里要放20幅以上书法作品,最好包含各朝代、各书体。
  • backend/:Maven项目,只保留代码和必要的配置文件。
  • frontend/:npm项目,只保留src和配置文件。
  • 数据库配置用.env.example示范,不要提交真实密码。

演示数据尤其重要。我准备作品时,特意选了《兰亭序》局部、颜真卿《多宝塔碑》拓片、赵孟頫《前后赤壁赋》等经典作品。因为老师自己对这些作品很熟悉,他会下意识判断AI鉴赏得准不准。如果AI对颜真卿风格说错,那就很尴尬了。

6. 答辩现场:这样讲创新点和项目亮点

6.1 创新点怎么说才不是"包装词"

很多同学的创新点写的是"使用SpringAI框架""使用Vue3技术",这只能算技术选型,不是创新点。真正的创新点要落到场景和问题:针对书法作品鉴赏这一垂直场景,构建了"OCR识别 + 结构化Prompt + 流式输出"的AI鉴赏流程,并实现了可解释的多维度鉴赏结果。这样讲,老师一听就知道你在解决具体问题,而不是在堆砌技术名词。

还可以提一个延伸亮点:系统通过对鉴赏维度的拆解,让AI输出可以沉淀为书法学习语料库,今后可以用于书法教育辅助。这是把"功能"上升为"价值",答辩时很加分。

6.2 老师常问的5个问题,回答思路参考

我整理了这5个高频问题,建议提前准备:

问题回答思路
为什么不用Python做AI?强调系统核心是工程化集成,SpringAI帮我把模型接入、重试、流式做了统一,Java后端与业务模块(用户、作品、权限)同工程维护,减少技术栈割裂。
大模型生成的内容错了怎么办?分三层:一是Prompt约束+审核规则;二是管理员可以修改/下架AI鉴赏结果;三是OCR和视觉模型双通道交叉验证,降低幻觉风险。
你的系统和直接问AI有什么区别?直接问AI没有知识边界;我的系统锁定"当前作品"上下文,AI只能围绕作品信息回答,并且结合了作品库元数据和OCR结果。
如果作品量大了怎么办?前端分页+缩略图,后端索引优化,AI服务独立部署,鉴赏结果做缓存。
最大坑是什么?诚实地说,中文SSE流式输出的乱码和模型JSON不稳定。然后补一句"通过调整Content-Type和TextDecoder以及few-shot解决了"。

6.3 演示时的"话术脚本"

演示千万不要现场让AI现写。最好提前准备好几个作品的鉴赏结果,但在屏幕上假装是实时的(其实走的是缓存)。如果老师要求"重新生成一个看看",你再切换到真实调用,那时候网络和模型状态都是未知的。我的建议是准备两台机器,一台断网也能用本地缓存演示,另一台联网以备不时之需。或者干脆在答辩前一晚凌晨,网络空闲时提前跑好5个作品的缓存数据,现场点击时走缓存,速度快、效果也好,没人知道你提前准备了。

这个看起来很"作弊",其实是所有商业软件发布前都会做的演示数据准备,属于工程习惯,不算虚。

最后再分享一个细节:我把ChatMessage表里的内容做了全文索引,这样用户能搜索自己历史提问过的问题。这个小功能一开始我懒得做,后来发现答辩时被老师注意到了,他就顺着问了一句"你历史记录怎么存?",正好把我准备好的表结构设计讲了出来。所以不要忽视任何一个小细节,你认真做的每一张表、每一个字段,都可能是答辩时帮你撑场面的材料。如果让我重做一次,我会把"多模态模型对草书识别的准确率对比实验"做得更细,录一段短视频,让老师直观感受OCR在行书和草书上的差异。这个素材往PPT里一放,项目厚度立刻就不一样了。

返回列表