
为SenseVoice-Small模型开发Web管理界面Flask快速入门你是不是已经部署好了SenseVoice-Small语音识别模型但每次测试都要在命令行里敲代码查看结果也不方便或者想给团队里不懂技术的同事提供一个简单的测试入口今天我们就来解决这个问题。我将手把手带你用Python里最轻量级的Web框架——Flask快速搭建一个专属的Web管理界面。这个界面不仅能让你通过网页上传音频文件、一键调用模型识别还能查看历史记录、监控服务状态整个过程就像点外卖一样简单。即使你之前没怎么接触过Web开发跟着这篇教程走也能在半小时内拥有一个功能实用、界面清爽的管理后台。我们用的是最基础的HTML和一点点Bootstrap来美化界面代码简单明了保证你能看懂每一步在做什么。1. 项目目标与环境准备在开始敲代码之前我们先明确一下这个Web界面要做什么以及需要准备哪些“食材”。1.1 我们要实现什么功能想象一下你有一个已经部署好的SenseVoice-Small服务假设它运行在本地http://localhost:8000并提供了识别接口。我们的Web管理界面需要围绕它提供三个核心功能音频识别测试在网页上选择一个音频文件比如.wav,.mp3点击上传后台自动调用SenseVoice-Small服务进行识别并把返回的文字结果显示在页面上。识别历史管理每次识别的记录包括文件名、识别文本、时间都能被保存下来并且可以在一个单独的页面里查看。当然也可以删除某条或全部历史记录。服务状态监控在一个仪表盘页面能直观地看到运行SenseVoice-Small服务的服务器的基本状态比如CPU和内存的使用情况。1.2 搭建你的开发环境你需要准备两样东西Python环境和几个必要的Python库。如果你已经熟悉Python可以快速跳过这部分。首先确保你的电脑上安装了Python建议3.7或以上版本。打开终端或命令行输入python --version检查一下。接下来安装我们需要的库。我们将使用pip这个Python包管理工具。在命令行中依次执行以下命令pip install flaskFlask是我们的核心Web框架非常轻巧。pip install requests我们将用requests库来向后端的SenseVoice-Small服务发送HTTP请求。pip install psutilpsutil是一个跨平台的库用来获取系统信息比如CPU、内存占用这是我们做状态监控所需要的。安装过程通常很快。安装完成后你可以创建一个新的文件夹比如叫做sensevoice_web_ui我们所有的代码文件都将放在这里。2. 从零开始构建Flask应用骨架Flask应用的结构非常清晰。我们从一个最简单的“Hello World”开始然后逐步添加功能。2.1 创建主程序文件在你的项目文件夹里新建一个名为app.py的文件。用任何文本编辑器如VS Code, Sublime Text, 甚至记事本打开它输入以下代码from flask import Flask, render_template, request, jsonify, redirect, url_for import os import json from datetime import datetime app Flask(__name__) # 一个简单的路由访问网站根目录时显示“Hello World” app.route(/) def index(): return Hello, SenseVoice Web UI! if __name__ __main__: app.run(debugTrue, host0.0.0.0, port5000)代码解释from flask import ...导入Flask框架中我们即将用到的各种组件。app Flask(__name__)创建一个Flask应用实例这是所有操作的起点。app.route(/)这是一个“装饰器”它告诉Flask当用户访问网站的根路径比如http://localhost:5000/时就执行下面定义的index()函数。app.run(...)启动Flask自带的开发服务器。debugTrue意味着开启调试模式代码修改后会自动重启服务器非常方便开发。host0.0.0.0表示允许同一网络下的其他设备访问port5000指定运行在5000端口。保存文件然后在命令行中进入到你的项目文件夹运行python app.py你会看到类似这样的输出* Serving Flask app app * Debug mode: on * Running on http://0.0.0.0:5000现在打开你的浏览器访问http://localhost:5000你应该能看到页面上显示着 “Hello, SenseVoice Web UI!”。恭喜你你的第一个Flask应用跑起来了按CtrlC可以停止服务器。2.2 设计我们的页面结构一个Web应用通常有多个页面。我们来规划一下首页 (/)音频文件上传和识别的主界面。历史记录页 (/history)展示所有过往的识别记录。状态监控页 (/status)显示服务器CPU和内存使用情况。我们还需要一个地方来存放前端页面文件。在项目根目录下创建一个名为templates的文件夹。Flask默认会在这个文件夹里寻找HTML模板文件。在templates文件夹里我们先创建三个最基础的HTML文件index.html(首页)history.html(历史记录页)status.html(状态监控页)为了让它们有个统一的样式我们引入Bootstrap。Bootstrap是一套现成的CSS样式和组件库能让我们用很少的代码做出好看的界面。我们直接使用Bootstrap官方提供的CDN链接这样连下载都不需要。以index.html为例它的基础结构如下!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 titleSenseVoice-Small 管理界面/title !-- 引入 Bootstrap 5 CSS -- link hrefhttps://cdn.jsdelivr.net/npm/bootstrap5.1.3/dist/css/bootstrap.min.css relstylesheet /head body nav classnavbar navbar-expand-lg navbar-dark bg-primary div classcontainer-fluid a classnavbar-brand href/SenseVoice-Small Web UI/a div classnavbar-nav a classnav-link href/识别测试/a a classnav-link href/history识别历史/a a classnav-link href/status服务状态/a /div /div /nav div classcontainer mt-4 h1欢迎使用语音识别测试界面/h1 p在这里上传音频文件测试SenseVoice-Small模型的识别效果。/p !-- 具体的内容表单等会在这里添加 -- /div !-- 引入 Bootstrap JS (可选用于一些交互组件) -- script srchttps://cdn.jsdelivr.net/npm/bootstrap5.1.3/dist/js/bootstrap.bundle.min.js/script /body /htmlhistory.html和status.html的head和导航栏部分可以完全一样只需要修改div classcontainer里面的内容即可。这样我们三个页面的基础框架和导航就搭好了。3. 核心功能实现连接前端与后端现在我们来让页面“活”起来实现具体的功能。这需要后端app.py和前端HTML模板配合。3.1 实现音频上传与识别这是最核心的功能。我们需要在index.html的表单里上传文件然后在app.py里接收文件调用SenseVoice-Small的API最后把结果返回给页面显示。第一步完善index.html的表单在templates/index.html的容器 (div classcontainer) 内添加一个文件上传表单和一个用来显示结果的区域。div classcontainer mt-4 h2音频文件识别测试/h2 form iduploadForm enctypemultipart/form-data div classmb-3 label foraudioFile classform-label选择音频文件/label input classform-control typefile idaudioFile namefile accept.wav,.mp3,.m4a required div classform-text支持 .wav, .mp3, .m4a 格式文件大小建议小于10MB。/div /div button typesubmit classbtn btn-primary上传并识别/button /form div idresultArea classmt-4 styledisplay:none; h4识别结果/h4 div classcard div classcard-body p idresultText classcard-text/p small classtext-muted idresultTime/small /div /div /div div idloadingSpinner classmt-4 text-center styledisplay:none; div classspinner-border text-primary rolestatus span classvisually-hidden识别中.../span /div p classmt-2正在识别音频请稍候.../p /div /div !-- 添加一点JavaScript来处理表单提交和显示结果 -- script document.getElementById(uploadForm).addEventListener(submit, async function(event) { event.preventDefault(); // 阻止表单默认提交行为 const formData new FormData(); const fileInput document.getElementById(audioFile); formData.append(file, fileInput.files[0]); // 显示加载动画隐藏结果区域 document.getElementById(loadingSpinner).style.display block; document.getElementById(resultArea).style.display none; try { const response await fetch(/recognize, { method: POST, body: formData }); const data await response.json(); // 隐藏加载动画 document.getElementById(loadingSpinner).style.display none; if (data.success) { // 显示识别结果 document.getElementById(resultText).textContent data.text; document.getElementById(resultTime).textContent 识别时间: data.timestamp; document.getElementById(resultArea).style.display block; } else { alert(识别失败: data.error); } } catch (error) { document.getElementById(loadingSpinner).style.display none; alert(请求出错: error); } }); /script第二步在app.py中处理上传和识别现在我们需要在app.py中创建/recognize这个路由来处理前端发送过来的请求。首先在文件顶部添加必要的导入和配置import os import requests from werkzeug.utils import secure_filename # 假设你的SenseVoice-Small服务地址 SENSEVOICE_API_URL http://localhost:8000/v1/audio/transcriptions # 请根据实际API地址修改 # 允许上传的文件类型 ALLOWED_EXTENSIONS {wav, mp3, m4a} # 设置一个临时上传文件夹 UPLOAD_FOLDER uploads os.makedirs(UPLOAD_FOLDER, exist_okTrue) app.config[UPLOAD_FOLDER] UPLOAD_FOLDER def allowed_file(filename): return . in filename and filename.rsplit(., 1)[1].lower() in ALLOWED_EXTENSIONS然后在app.py中定义首页路由让它渲染我们刚写好的index.html模板并添加处理识别的路由app.route(/) def index(): # 渲染 templates 文件夹下的 index.html return render_template(index.html) app.route(/recognize, methods[POST]) def recognize_audio(): if file not in request.files: return jsonify({success: False, error: 没有选择文件}) file request.files[file] if file.filename : return jsonify({success: False, error: 没有选择文件}) if file and allowed_file(file.filename): # 安全地保存文件名 filename secure_filename(file.filename) filepath os.path.join(app.config[UPLOAD_FOLDER], filename) file.save(filepath) try: # 调用 SenseVoice-Small API # 注意这里需要根据你实际部署的API格式调整 with open(filepath, rb) as audio_file: files {file: audio_file} # 假设API需要一些参数例如模型名称 data {model: sensevoice-small} response requests.post(SENSEVOICE_API_URL, filesfiles, datadata) if response.status_code 200: result response.json() # 假设API返回的识别文本在 text 字段中 recognized_text result.get(text, ) timestamp datetime.now().strftime(%Y-%m-%d %H:%M:%S) # TODO: 将记录保存到历史记录下一节实现 # save_to_history(filename, recognized_text, timestamp) # 返回成功结果给前端 return jsonify({ success: True, text: recognized_text, timestamp: timestamp, filename: filename }) else: return jsonify({success: False, error: f识别服务错误: {response.status_code}}) except Exception as e: return jsonify({success: False, error: str(e)}) finally: # 可选识别完成后删除临时文件 if os.path.exists(filepath): os.remove(filepath) else: return jsonify({success: False, error: 不支持的文件类型})代码解释recognize_audio函数处理/recognize的POST请求。检查前端是否上传了文件。检查文件类型是否允许。使用secure_filename处理文件名防止安全问题然后将文件保存到本地uploads文件夹。使用requests库以multipart/form-data格式将音频文件发送到我们假设的SenseVoice-Small API地址。解析API返回的JSON提取识别文本。将识别结果、时间戳等信息打包成JSON返回给前端。可选删除临时保存的音频文件。重要提示你需要将SENSEVOICE_API_URL替换成你实际部署的SenseVoice-Small服务的API地址和端点。同时API请求的格式files和data参数也需要根据该服务的具体文档进行调整。现在重启你的Flask应用 (python app.py)刷新浏览器。你应该能看到一个带文件选择框和按钮的页面。选择一个音频文件点击上传如果后端服务配置正确就能看到识别结果了3.2 实现历史记录功能历史记录功能需要持久化存储数据。为了简单起见我们用一个JSON文件来模拟数据库。第一步在app.py中添加历史记录操作函数在app.py的开头附近定义历史记录文件的路径和几个辅助函数HISTORY_FILE history.json def load_history(): 从JSON文件加载历史记录 if os.path.exists(HISTORY_FILE): with open(HISTORY_FILE, r, encodingutf-8) as f: try: return json.load(f) except json.JSONDecodeError: return [] return [] def save_history(history_list): 保存历史记录到JSON文件 with open(HISTORY_FILE, w, encodingutf-8) as f: json.dump(history_list, f, ensure_asciiFalse, indent4) def save_to_history(filename, text, timestamp): 将单条记录添加到历史并保存 history load_history() # 在列表开头插入新记录使得最新的记录显示在最前面 history.insert(0, { id: len(history) 1, filename: filename, text: text, timestamp: timestamp }) save_history(history)然后修改刚才的/recognize路由在识别成功后调用save_to_history函数取消注释TODO那行# 在识别成功的代码块内 recognized_text result.get(text, ) timestamp datetime.now().strftime(%Y-%m-%d %H:%M:%S) # 保存到历史记录 save_to_history(filename, recognized_text, timestamp)第二步创建历史记录页面路由和模板在app.py中添加/history路由app.route(/history) def show_history(): history_list load_history() # 将历史记录列表传递给模板 return render_template(history.html, historyhistory_list)现在创建templates/history.html文件用来展示历史记录!-- 头部和导航栏与 index.html 相同此处省略 -- div classcontainer mt-4 h2识别历史记录/h2 {% if history %} p共 {{ history|length }} 条记录。/p div classlist-group {% for record in history %} div classlist-group-item list-group-item-action div classd-flex w-100 justify-content-between h6 classmb-1{{ record.filename }}/h6 small{{ record.timestamp }}/small /div p classmb-1{{ record.text }}/p small a href# classtext-danger onclickdeleteRecord({{ record.id }})删除/a /small /div {% endfor %} /div div classmt-3 button classbtn btn-outline-danger btn-sm onclickclearAllHistory()清空所有历史/button /div {% else %} div classalert alert-info rolealert 暂无识别历史。 /div {% endif %} /div script function deleteRecord(recordId) { if (confirm(确定要删除这条记录吗)) { fetch(/history/delete/${recordId}, { method: DELETE }) .then(response response.json()) .then(data { if (data.success) { location.reload(); // 删除成功后刷新页面 } else { alert(删除失败: data.error); } }); } } function clearAllHistory() { if (confirm(确定要清空所有历史记录吗此操作不可撤销。)) { fetch(/history/clear, { method: POST }) .then(response response.json()) .then(data { if (data.success) { location.reload(); } else { alert(清空失败: data.error); } }); } } /script这个模板使用了Jinja2模板语法Flask默认的模板引擎。{% if history %}和{% for record in history %}用来循环渲染后端传递过来的history列表。{{ record.filename }}等用来插入变量值。第三步在app.py中实现删除和清空接口最后我们需要为前端的删除和清空操作提供后端接口。app.route(/history/delete/int:record_id, methods[DELETE]) def delete_history_record(record_id): history load_history() # 过滤掉要删除的记录 new_history [record for record in history if record[id] ! record_id] if len(new_history) ! len(history): save_history(new_history) return jsonify({success: True}) else: return jsonify({success: False, error: 记录未找到}) app.route(/history/clear, methods[POST]) def clear_all_history(): # 清空历史记录文件 save_history([]) return jsonify({success: True})现在重启应用点击导航栏的“识别历史”你就能看到所有上传识别的记录了并且可以进行删除操作。3.3 实现服务状态监控状态监控页面相对简单我们使用psutil库获取系统信息。在app.py中添加/status路由import psutil app.route(/status) def show_status(): # 获取CPU使用率间隔1秒 cpu_percent psutil.cpu_percent(interval1) # 获取内存信息 memory_info psutil.virtual_memory() # 获取磁盘信息根目录 disk_info psutil.disk_usage(/) status_data { cpu_percent: cpu_percent, memory_total: round(memory_info.total / (1024**3), 2), # 转换为GB memory_used: round(memory_info.used / (1024**3), 2), memory_percent: memory_info.percent, disk_total: round(disk_info.total / (1024**3), 2), disk_used: round(disk_info.used / (1024**3), 2), disk_percent: disk_info.percent, } return render_template(status.html, statusstatus_data)然后创建templates/status.html!-- 头部和导航栏与 index.html 相同此处省略 -- div classcontainer mt-4 h2服务状态监控/h2 p当前服务器资源使用情况。/p div classrow div classcol-md-6 div classcard mb-3 div classcard-header h5 classcard-title mb-0CPU 使用率/h5 /div div classcard-body h2 classdisplay-6{{ status.cpu_percent }}%/h2 div classprogress div classprogress-bar roleprogressbar stylewidth: {{ status.cpu_percent }}%; aria-valuenow{{ status.cpu_percent }} aria-valuemin0 aria-valuemax100 /div /div /div /div /div div classcol-md-6 div classcard mb-3 div classcard-header h5 classcard-title mb-0内存使用/h5 /div div classcard-body h2 classdisplay-6{{ status.memory_percent }}%/h2 p classcard-text 已用 {{ status.memory_used }} GB / 总共 {{ status.memory_total }} GB /p div classprogress div classprogress-bar bg-success roleprogressbar stylewidth: {{ status.memory_percent }}%; aria-valuenow{{ status.memory_percent }} aria-valuemin0 aria-valuemax100 /div /div /div /div /div /div div classcard div classcard-header h5 classcard-title mb-0磁盘使用/h5 /div div classcard-body h2 classdisplay-6{{ status.disk_percent }}%/h2 p classcard-text 已用 {{ status.disk_used }} GB / 总共 {{ status.disk_total }} GB /p div classprogress div classprogress-bar bg-info roleprogressbar stylewidth: {{ status.disk_percent }}%; aria-valuenow{{ status.disk_percent }} aria-valuemin0 aria-valuemax100 /div /div /div /div /div这个页面使用了Bootstrap的卡片和进度条组件直观地展示了CPU、内存和磁盘的使用情况。数据通过{{ status.xxx }}从后端传递过来。4. 总结与后续优化建议跟着上面的步骤走下来一个具备基本功能的SenseVoice-Small Web管理界面就搭建完成了。整个过程其实并不复杂核心就是理解Flask如何处理请求路由、如何与前端交互接收表单、返回JSON、以及如何渲染动态页面模板。现在你的应用应该具备了一个简洁的导航栏可以在三个核心页面间切换上传识别、查看历史、监控状态。对于个人使用或小团队内部测试来说这个功能已经足够实用了。当然这只是一个起点。如果你想让这个工具更强大、更健壮可以考虑从以下几个方向继续完善美化界面与交互现在的界面比较基础。你可以深入学习Bootstrap使用更多的组件如模态框、Toast提示、更漂亮的表格来提升用户体验。也可以考虑使用一些前端图表库如Chart.js来让状态监控页面更直观。增强文件管理目前我们识别完就删除了临时文件。你可以增加一个文件管理功能让用户可以选择保留某些音频文件或者提供批量上传和识别的能力。用户与权限如果需要给多人使用可以增加简单的用户登录和权限管理不同用户只能看到自己的历史记录。使用真正的数据库当历史记录变多时JSON文件会变得难以管理。可以考虑换成SQLite轻量级或MySQL/PostgreSQL使用Flask-SQLAlchemy这样的ORM库来操作会方便和安全得多。错误处理与日志增加更完善的错误处理机制比如网络超时、API返回异常格式等。同时记录操作日志方便排查问题。部署上线现在用的是Flask自带的开发服务器不适合生产环境。可以考虑使用Gunicorn Nginx 或 Docker 等方式进行部署让应用更稳定、安全。最重要的是这个项目为你提供了一个清晰的模式用Flask快速搭建一个连接AI模型与用户的桥梁。你可以把这里的SenseVoice-Small换成任何其他提供HTTP API的AI模型如图像识别、文本生成等快速构建出对应的测试或管理界面。希望这个小小的项目能成为你探索更多AI应用可能性的起点。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。