前言
在智能面试语音分析系统的整体架构中,前端交互层承担着连接用户与后端智能分析引擎的桥梁作用。本项目后端已基于 LangGraph 构建了完整的工作流链路(语音转写 → 内容提取 → 智能评价),并通过 FastAPI 对外暴露了标准化的 RESTful 接口,同时引入多进程并发执行机制以提升吞吐能力。然而,仅有强大的后端并不足以构成一个可用的系统——用户需要一个直观、易操作的界面来完成录音上传、任务状态监控、分析结果查阅与文件下载等全流程操作。
Streamlit 作为 Python 生态中极具代表性的快速原型开发框架,以其声明式编程范式、零前端代码依赖和丰富的组件库,成为数据应用领域的首选工具。本文将围绕该系统的前端交互模块,从整体架构、页面路由机制、核心页面实现、前后端数据交互、文件上传与下载等维度进行系统性阐述,力求为读者呈现一个完整的 Streamlit 多页面应用开发实践。
前置内容请参阅系列文章的其他篇章。
公共模块:
基于 LangGraph 的面试语音分析系统项目公共模块设计与实现-CSDN博客
工作流:
基于 LangGraph 的智能面试语音分析工作流:从零到一的完整实战指南-CSDN博客
数据库:
基于 SQLAlchemy 的面试语音数据库层设计与封装实战-CSDN博客 FastAPI:
基于 FastAPI 的面试语音分析的后端接口层设计与实现-CSDN博客
多进程:
Python 多进程并发执行 LangGraph 工作流 —— 智能面试语音分析系统的并发调度方案-CSDN博客
目录
- 一、系统前端架构总览
- 1.1 前端在整体系统中的定位
- 1.2 前端模块目录结构
- 1.3 页面路由与导航机制
- 二、应用入口——main_streamlit.py
- 2.1 侧边栏隐藏与界面定制
- 2.2 基于 session_state 的页面路由
- 2.3 完整代码与逐行解析
- 三、主页面——page_main.py
- 3.1 页面职责与功能概述
- 3.2 后端接口调用:分页数据获取
- 3.3 数据列表展示与状态映射
- 3.4 重新训练功能的交互设计
- 3.5 分页控件的实现逻辑
- 3.6 完整代码与关键逻辑解析
- 四、录音上传页——page_add.py
- 4.1 页面职责与功能概述
- 4.2 表单组件与用户输入采集
- 4.3 文件上传与 multipart/form-data 提交
- 4.4 完整代码与关键逻辑解析
- 五、详情页——page_detail.py
- 5.1 页面职责与功能概述
- 5.2 基于 record_id 的数据获取
- 5.3 Markdown 文件下载功能
- 5.4 分析结果渲染展示
- 5.5 完整代码与关键逻辑解析
- 六、公共配置与跨模块依赖
- 6.1 Config 配置类的作用
- 6.2 前后端通信地址管理
- 七、关键技术点深度解析
- 7.1 Streamlit session_state 状态管理
- 7.2 st.rerun() 与页面刷新机制
- 7.3 requests 库发起 HTTP 请求的最佳实践
- 7.4 Streamlit 按钮的 key 冲突与分页陷阱
- 八、前端模块流程总览
- 九、总结与展望
一、系统前端架构总览
1.1 前端在整体系统中的定位
智能面试语音分析系统采用 前后端分离 的架构模式。各层职责划分如下:
- 前端交互层(Streamlit):负责用户界面渲染、表单数据采集、按钮事件响应、页面路由跳转,以及通过 HTTP 请求调用后端 API。
- 后端服务层(FastAPI):负责接收前端请求、参数校验、调度 LangGraph 工作流、管理数据库读写。
- 智能分析层(LangGraph):负责语音转写(ASR)、内容提取、智能评价等核心 AI 能力。
前端不直接接触 AI 模型或数据库,所有数据交互均通过 FastAPI 提供的 RESTful 接口完成,实现了关注点分离。
1.2 前端模块目录结构
__005__streamlit_page/
├── main_streamlit.py # 应用入口,负责页面路由
├── common/
│ └── config.py # 公共配置(端口号等)
└── pages/
├── page_main.py # 主页面:面试记录列表 + 分页
├── page_add.py # 录音上传页:表单 + 文件上传
├── page_detail.py # 详情页:分析结果展示 + 下载
└── page_test.py # 测试页面(预留)
1.3 页面路由与导航机制
本项目未采用 Streamlit 原生的多页面机制(st.Page / st.navigation),而是基于 st.session_state + st.rerun() 实现了自定义的页面路由方案。其核心原理为:
通过 st.session_state[\”page\”] 存储当前页面标识符,在应用入口处根据该值渲染对应的页面函数,配合 st.rerun() 实现页面切换时的完整重载。
这种方案的优势在于:
- 完全掌控路由逻辑,便于传递页面间参数(如 record_id)
- 不依赖 Streamlit 的文件命名约定
- 适用于需要在页面间共享复杂状态的场景
页面间的导航关系如下(文字描述):
主页面 (page_main)
├── 点击「处理语音」按钮 → 跳转到录音上传页 (page_add)
├── 点击「查看详情」按钮 → 跳转到详情页 (page_detail),携带 record_id
└── 点击「重新训练」按钮 → 弹出确认对话框 → 调用后端重新训练接口
录音上传页 (page_add)
├── 点击「← 返回主界面」按钮 → 跳转回主页面
└── 点击「开始分析」按钮 → 提交表单 → 成功后自动跳转回主页面
详情页 (page_detail)
├── 点击「← 返回主界面」按钮 → 跳转回主页面
├── 点击「下载md文件」→ 下载完整面试评价
└── 点击「仅下载问题md文件」→ 下载问题摘要
二、应用入口——main_streamlit.py
2.1 侧边栏隐藏与界面定制
Streamlit 默认会在页面左侧渲染一个侧边栏(Sidebar),其中包含页面导航、设置等元素。在自定义路由方案下,该侧边栏会干扰用户体验,因此需要通过 CSS 注入的方式将其隐藏:
hide_sidebar = \”\”\”
<style>
section[data-testid=\”stSidebar\”] {
display: none;
}
</style>
\”\”\”
st.markdown(hide_sidebar, unsafe_allow_html=True)
实现原理:Streamlit 的侧边栏 DOM 节点带有 data-testid=\”stSidebar\” 属性,通过 st.markdown() 注入 CSS,将该节点的 display 设为 none 即可完成隐藏。unsafe_allow_html=True 参数允许 st.markdown() 渲染原始 HTML/CSS。
2.2 基于 session_state 的页面路由
def main():
page = st.session_state.get(\”page\”, \”page_main\”)
if page == \”page_main\”:
page_main()
elif page == \”page_detail\”:
page_detail()
elif page == \”page_add\”:
page_add()
else:
page_test()
关键逻辑解析:
- st.session_state.get(\”page\”, \”page_main\”):从 session_state 中读取 \”page\” 键的值,若不存在则默认为 \”page_main\”,即用户首次访问时默认展示主页面。
- 每个 elif 分支调用对应的页面函数,每个页面函数内部自行渲染完整的 UI。
- 当某个页面函数内部修改 st.session_state[\”page\”] 并调用 st.rerun() 时,Streamlit 会重新执行整个脚本,从而实现页面切换。
2.3 完整代码与逐行解析
import streamlit as st
from __005__streamlit_page.pages.page_add import page_add
from __005__streamlit_page.pages.page_detail import page_detail
from __005__streamlit_page.pages.page_main import page_main
from __005__streamlit_page.pages.page_test import page_test
# 隐藏 Streamlit 默认侧边栏,避免干扰自定义路由
hide_sidebar = \”\”\”
<style>
section[data-testid=\”stSidebar\”] {
display: none;
}
</style>
\”\”\”
st.markdown(hide_sidebar, unsafe_allow_html=True)
def main():
\”\”\”
应用主入口:根据 session_state 中的 page 参数路由到不同页面。
\”\”\”
# 获取当前页面标识,默认为 page_main
page = st.session_state.get(\”page\”, \”page_main\”)
# 根据页面标识分发到对应的渲染函数
if page == \”page_main\”:
page_main()
elif page == \”page_detail\”:
page_detail()
elif page == \”page_add\”:
page_add()
else:
page_test()
if __name__ == \”__main__\”:
main()
启动方式:
streamlit run main_streamlit.py –server.port 8501
三、主页面——page_main.py
3.1 页面职责与功能概述
主页面是系统的核心枢纽,承担以下职责:
- 数据展示:以表格形式分页展示面试记录列表,包含姓名、面试时间、公司名称、处理状态、状态提示、更新时间等字段。
- 页面导航:提供「处理语音」按钮跳转到录音上传页,提供「查看详情」按钮跳转到详情页。
- 状态操作:支持「重新训练」操作,点击后弹出确认对话框,确认后调用后端接口重新触发分析流程。
- 分页控制:支持上一页/下一页、指定页码跳转,且在页码较多时使用省略号进行智能折叠。
3.2 后端接口调用:分页数据获取
前端通过 requests.get() 调用后端的 /interview_records 接口获取分页数据:
def get_interview_data(page=1, page_size=10):
\”\”\”分页获取面试记录,返回 (列表, 总条数, 当前页, 每页条数)\”\”\”
interview_data = []
total = 0
try:
resp = re




