基于本地化部署的deepseek大模型,编程使用springboot+vue3+yolo11实现一个在BS模式的植物叶片分类识别系统。demo演示
📁 第一步:项目结构概览
在开始之前,先了解一下我们整个系统的构成,它由三个项目组成,就像三条生产线协同工作:
flowchart TD
A[前端项目<br>vue-plant-demo] --> B[后端项目<br>spring-boot-plant-demo]
B --> C[AI中台项目<br>flask-plant-ai]
C --> D[视觉识别引擎<br>模拟YOLOv11]
C --> E[认知推理引擎<br>本地DeepSeek (Ollama)]
B --> F[(MySQL数据库<br>存储记录)]
现在,我们逐一攻克这三个项目。
⚙️ 第二步:AI中台 (Flask-Python) - 系统的“智能大脑”
这是最核心的部分,负责“看”图和“想”问题。我们把它建在一个独立的文件夹 flask-plant-ai 里。
1. 环境准备
确保你已经安装并启动了Ollama,并拉取了DeepSeek模型。在命令行执行:
ollama pull deepseek-r1:7b
ollama serve
服务默认运行在 http://localhost:11434 。
2. 安装依赖
在 flask-plant-ai 目录下,创建 requirements.txt:
Flask==2.3.3
flask-cors==4.0.0
requests==2.31.0
然后打开终端,执行 pip install -r requirements.txt 安装。
3. 核心代码 app.py
创建 app.py,这是AI中台的入口。为了让大家能快速跑通Demo,这里的视觉识别部分先用一个模拟函数代替,它代表了你未来会用YOLOv11训练出的真实模型。
# flask-plant-ai/app.py
from flask import Flask, request, jsonify
from flask_cors import CORS
import requests
import json
import random
app = Flask(__name__)
CORS(app) # 允许跨域请求,方便前端直接调用
# --- 模拟视觉识别引擎 (未来可替换为真实的YOLOv11模型) ---
# 模拟一个能识别几种植物叶片的“AI眼睛”
PLANT_CLASSES = {
"0": {"name": "健康的番茄叶", "latin": "Solanum lycopersicum"},
"1": {"name": "番茄早疫病", "latin": "Alternaria solani"},
"2": {"name": "健康的玉米叶", "latin": "Zea mays"},
"3": {"name": "玉米大斑病", "latin": "Exserohilum turcicum"},
"4": {"name": "玫瑰叶", "latin": "Rosa rugosa"}
}
def mock_vision_model(image_bytes):
"""
模拟视觉模型:根据图片内容(这里随机)返回识别结果。
在实际项目中,这里会加载你的YOLOv11模型,对image_bytes进行预测。
"""
# 模拟识别,随机返回一个类别和较高的置信度
class_id = str(random.randint(0, len(PLANT_CLASSES)-1))
confidence = round(random.uniform(0.85, 0.99), 4)
result = {
"class_id": class_id,
"class_name": PLANT_CLASSES[class_id]["name"],
"latin_name": PLANT_CLASSES[class_id]["latin"],
"confidence": confidence
}
print(f"[视觉模型] 识别结果: {result}")
return result
# --- 模拟视觉识别引擎结束 ---
@app.route('/api/analyze-plant', methods=['POST'])
def analyze_plant():
"""
核心接口:接收图片,返回识别结果 + DeepSeek的趣味解读
"""
if 'image' not in request.files:
return jsonify({"error": "请上传图片"}), 400
image_file = request.files['image']
image_bytes = image_file.read()
# 1. 调用视觉模型进行识别
vision_result = mock_vision_model(image_bytes)
# 2. 准备发给DeepSeek的提示词,让它基于识别结果进行“思考”
prompt = f"""你是一个充满热情的植物学家。用户上传了一张植物叶片的照片,我们的视觉模型识别它为“{vision_result['class_name']}”(学名:{vision_result['latin_name']}),置信度为{vision_result['confidence']}。
请用一段100字左右、生动有趣的话,向用户介绍这片叶子,可以包括它的特点、有趣的小知识,或者如果它是病害,给出一些友好的防治小建议。语气要亲切、自然。"""
# 3. 调用本地DeepSeek模型 (Ollama API)
try:
ollama_response = requests.post(
"http://localhost:11434/api/generate",
json={"model": "deepseek-r1:7b", "prompt": prompt, "stream": False}
)
ollama_result = ollama_response.json()
deepseek_comment = ollama_result.get("response", "哎呀,我被问住了,让我再学一学。")
print(f"[DeepSeek] 生成解读: {deepseek_comment}")
except Exception as e:
print(f"[DeepSeek] 调用失败: {e}")
deepseek_comment = "(AI大脑暂时离线,但这片叶子真的很特别!)"
# 4. 合并结果并返回
final_result = {
"vision": vision_result,
"comment": deepseek_comment
}
return jsonify(final_result)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5001, debug=True)
☕ 第三步:后端 (SpringBoot-Java) - 系统的“业务中台”
在另一个文件夹(如 spring-boot-plant-demo)中创建SpringBoot项目。它负责接收前端请求,并转发给AI中台。
1. pom.xml 关键依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 用于调用Python服务的HTTP客户端 -->
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.2.1</version>
</dependency>
</dependencies>
2. 核心Controller PlantController.java
创建一个控制器,提供一个接口供前端调用。这个接口会调用Python AI中台的接口。
package com.example.demobackend.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/plants")
@CrossOrigin(origins = "http://localhost:5173") // 允许Vue前端访问
public class PlantController {
@Autowired
private RestTemplate restTemplate;
// Python AI中台的地址
private final String pythonAiUrl = "http://localhost:5001/api/analyze-plant";
@PostMapping("/identify")
public ResponseEntity<?> identifyPlant(@RequestParam("image") MultipartFile imageFile) {
try {
// 1. 准备要发送给Python服务的数据 (multipart/form-data)
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
// 将Spring的MultipartFile转换为字节资源
body.add("image", new MultipartFileResource(imageFile.getOriginalFilename(), imageFile.getSize(), imageFile.getBytes()));
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
// 2. 调用Python AI中台
ResponseEntity<Map> response = restTemplate.postForEntity(pythonAiUrl, requestEntity, Map.class);
// 3. 将Python服务返回的结果直接转发给前端
return ResponseEntity.ok(response.getBody());
} catch (IOException e) {
e.printStackTrace();
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Map.of("error", "图片处理失败"));
} catch (Exception e) {
e.printStackTrace();
return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(Map.of("error", "AI服务暂时不可用,请稍后再试。"));
}
}
// 一个辅助类,用于将MultipartFile包装成RestTemplate能识别的资源
static class MultipartFileResource extends org.springframework.core.io.ByteArrayResource {
private final String filename;
private final long size;
public MultipartFileResource(String filename, long size, byte[] content) {
super(content);
this.filename = filename;
this.size = size;
}
@Override
public String getFilename() {
return this.filename;
}
@Override
public long contentLength() {
return this.size;
}
}
}
3. 配置 RestTemplate (在 DemoBackendApplication.java 或配置类中)
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class AppConfig {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
}
🖼️ 第四步:前端 (Vue3) - 系统的“颜值担当”
在第三个文件夹 vue-plant-demo 里,用Vue3搭建用户界面。
1. 创建Vue3项目并安装依赖
npm create vue@latest vue-plant-demo
cd vue-plant-demo
npm install axios element-plus
2. 核心页面代码 src/views/HomeView.vue
这个页面包含上传组件,并展示从后端返回的识别结果和DeepSeek的趣味解读。上传组件可以参考 vue3-dropzone 的实现。
<template>
<div class="home">
<h1>🌿 植物叶片智能识别系统 Demo</h1>
<p>上传一张叶子照片,让我告诉你它的故事~</p>
<div class="upload-area">
<label for="file-upload" class="custom-file-upload">
📤 点击上传图片
</label>
<input id="file-upload" type="file" accept="image/*" @change="handleFileUpload" />
</div>
<!-- 加载状态 -->
<div v-if="loading" class="loading">
<p>🔍 正在观察叶子... (视觉模型识别中)</p>
<p>🧠 正在询问DeepSeek... (AI生成解读中)</p>
</div>
<!-- 识别结果显示区 -->
<div v-if="result" class="result-card">
<h2>✨ 识别结果</h2>
<div class="vision-result">
<p><strong>🌱 植物名称:</strong> {{ result.vision.class_name }}</p>
<p><strong>🔬 学名:</strong> <i>{{ result.vision.latin_name }}</i></p>
<p><strong>📊 置信度:</strong> {{ (result.vision.confidence * 100).toFixed(2) }}%</p>
</div>
<div class="deepseek-comment">
<h3>📖 DeepSeek 的植物笔记</h3>
<p>{{ result.comment }}</p>
</div>
</div>
<!-- 错误提示 -->
<div v-if="error" class="error">
<p>❌ {{ error }}</p>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import axios from 'axios'
const loading = ref(false)
const result = ref(null)
const error = ref(null)
const handleFileUpload = async (event) => {
const file = event.target.files[0]
if (!file) return
// 重置状态
loading.value = true
result.value = null
error.value = null
// 准备表单数据
const formData = new FormData()
formData.append('image', file)
try {
// 调用SpringBoot后端接口
const response = await axios.post('http://localhost:8080/api/plants/identify', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
result.value = response.data
} catch (err) {
console.error('识别失败:', err)
error.value = err.response?.data?.error || '识别服务异常,请稍后再试'
} finally {
loading.value = false
// 清空input,以便再次上传同一文件
event.target.value = ''
}
}
</script>
<style scoped>
.home {
max-width: 600px;
margin: 0 auto;
padding: 20px;
text-align: center;
}
.upload-area {
margin: 30px 0;
}
.custom-file-upload {
border: 2px dashed #4caf50;
display: inline-block;
padding: 15px 30px;
cursor: pointer;
background-color: #f9f9f9;
border-radius: 8px;
transition: background-color 0.3s;
}
.custom-file-upload:hover {
background-color: #e8f5e9;
}
input[type="file"] {
display: none;
}
.loading p {
margin: 10px 0;
color: #666;
}
.result-card {
text-align: left;
border: 1px solid #ddd;
border-radius: 8px;
padding: 20px;
margin-top: 20px;
background-color: #fafafa;
}
.vision-result {
background-color: #e3f2fd;
padding: 15px;
border-radius: 8px;
margin-bottom: 20px;
}
.deepseek-comment {
background-color: #fff3e0;
padding: 15px;
border-radius: 8px;
font-style: italic;
}
.error {
color: #d32f2f;
background-color: #ffcdd2;
padding: 10px;
border-radius: 4px;
margin-top: 20px;
}
</style>
3. 配置 main.js
import { createApp } from 'vue'
import App from './App.vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')
🚀 第五步:启动与演示
现在,三条生产线都准备好了,我们来启动它们:
- 启动AI中台 (Python)
- 打开终端,进入
flask-plant-ai 目录。 - 执行
python app.py。 - 看到
Running on http://0.0.0.0:5001 即成功。
- 启动后端 (SpringBoot)
- 用IDE打开
spring-boot-plant-demo 项目。 - 运行
DemoBackendApplication.java 的 main 方法。 - 看到
Started DemoBackendApplication 即成功。
- 启动前端 (Vue3)
- 打开新终端,进入
vue-plant-demo 目录。 - 执行
npm run dev。 - 看到
VITE v... ready in ... ms 和本地访问地址(通常是 http://localhost:5173)。
- 打开浏览器,体验奇迹
- 访问
http://localhost:5173。 - 点击上传区域,选择一张植物叶片的图片(随便一张都行,因为我们的视觉模型目前是模拟的,会随机返回结果)。
- 稍等片刻,你就会看到:
- 视觉模型给出的“识别结果”(虽然是随机的,但流程是真实的)。
- DeepSeek 基于这个结果,生成的一段生动有趣的“植物笔记”。
这个Demo清晰地展示了数据是如何在Vue3、SpringBoot和Python AI中台之间流动的,以及“视觉识别”和“大语言模型”是如何分工协作的。
当你成功跑通这个Demo,看到来自本地DeepSeek生成的“植物笔记”时,那种成就感一定很棒。如果在任何一个环节遇到问题,比如端口冲突、依赖安装失败,随时把错误信息发给我,我们一起看看怎么解决。
更多推荐



所有评论(0)