会议记录整理工具(Python Qt5 + 讯飞API)
实现一个完整的会议记录整理工具,包含语音转文字、要点提取、待办事项识别和结构化文档生成功能。
前置准备
- 注册讯飞开放平台账号,创建应用并获取
APPID、APISecret、APIKey(语音转写API):https://www.xfyun.cn/
pip install PyQt5 requests python-docx jieba
完整代码实现
import sysimport jsonimport timeimport hashlibimport base64import hmacfrom datetime import datetimefrom urllib.parse import urlencodeimport requestsfrom PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QTextEdit, QFileDialog, QLabel, QProgressBar, QMessageBox, QTabWidget)from PyQt5.QtCore import Qt, QThread, pyqtSignalimport jiebaimport jieba.analysefrom docx import Document# 讯飞API配置(请替换为你的实际信息)XF_APPID = "你的讯飞APPID"XF_APISecret = "你的讯飞APISecret"XF_APIKey = "你的讯飞APIKey"# 语音转写线程classXfSpeechToTextThread(QThread): progress_signal = pyqtSignal(int) # 进度信号 result_signal = pyqtSignal(str) # 结果信号 error_signal = pyqtSignal(str) # 错误信号def__init__(self, audio_path): super().__init__() self.audio_path = audio_pathdefget_header(self):"""生成讯飞API请求头""" cur_time = datetime.now().strftime('%Y%m%d%H%M%S')# 拼接字符串 content = f"host=ws-api.xfyun.cn&date={cur_time}&request-line=GET /v2/iat HTTP/1.1"# HMAC-SHA256加密 signature = hmac.new(XF_APISecret.encode('utf-8'), content.encode('utf-8'), hashlib.sha256).digest() signature = base64.b64encode(signature).decode('utf-8') headers = {"Content-Type": "application/json","X-CurTime": cur_time,"X-Appid": XF_APPID,"X-Signature": signature,"X-ApiKey": XF_APIKey }return headersdefaudio_to_text(self):"""音频转文字核心逻辑"""# 读取音频文件with open(self.audio_path, 'rb') as f: audio_data = f.read() audio_base64 = base64.b64encode(audio_data).decode('utf-8')# 构建请求参数 data = {"common": {"app_id": XF_APPID},"business": {"language": "zh_cn","domain": "meeting","accent": "mandarin","vad_eos": 5000 },"data": {"status": 2,"format": "wav","encoding": "raw","audio": audio_base64 } }# 发送请求 url = "https://ws-api.xfyun.cn/v2/iat" headers = self.get_header() self.progress_signal.emit(30)try: response = requests.post(url, json=data, headers=headers, timeout=60) self.progress_signal.emit(70) result = json.loads(response.text)if result["code"] == 0: text = result["data"]["result"]["ws"][0]["cw"][0]["w"]for ws in result["data"]["result"]["ws"][1:]: text += ws["cw"][0]["w"] self.result_signal.emit(text)else: self.error_signal.emit(f"讯飞API错误:{result['desc']}")except Exception as e: self.error_signal.emit(f"转写失败:{str(e)}") self.progress_signal.emit(100)defrun(self): self.audio_to_text()# 会议内容分析工具classMeetingAnalyzer: @staticmethoddefextract_key_points(text):"""提取会议要点(基于TF-IDF算法)""" jieba.analyse.set_stop_words("stopwords.txt") # 可自行添加停用词文件 keywords = jieba.analyse.extract_tags(text, topK=10, withWeight=False) key_points = []# 分句并匹配关键词 sentences = text.replace('。', '。\n').split('\n')for sentence in sentences:if any(keyword in sentence for keyword in keywords) and len(sentence) > 10: key_points.append(sentence.strip())return key_points[:5] # 返回前5个核心要点 @staticmethoddefextract_todo_items(text):"""提取待办事项(基于关键词匹配)""" todo_keywords = ["待办", "需要", "必须", "应该", "负责", "完成", "处理", "跟进", "安排"] todo_items = [] sentences = text.replace('。', '。\n').split('\n')for sentence in sentences:if any(keyword in sentence for keyword in todo_keywords): todo_items.append(sentence.strip())return todo_items @staticmethoddefgenerate_doc(text, key_points, todo_items, save_path):"""生成结构化文档""" doc = Document() doc.add_heading('会议记录整理', 0)# 添加基本信息 doc.add_heading('会议时间', level=2) doc.add_paragraph(datetime.now().strftime('%Y年%m月%d日 %H:%M:%S'))# 添加完整文本 doc.add_heading('完整会议内容', level=2) doc.add_paragraph(text)# 添加会议要点 doc.add_heading('会议核心要点', level=2)for i, point in enumerate(key_points, 1): doc.add_paragraph(f"{i}. {point}", style='List Number')# 添加待办事项 doc.add_heading('待办事项', level=2)if todo_items:for i, todo in enumerate(todo_items, 1): doc.add_paragraph(f"{i}. {todo}", style='List Number')else: doc.add_paragraph('无待办事项') doc.save(save_path)# 主窗口classMeetingToolWindow(QMainWindow):def__init__(self): super().__init__() self.init_ui() self.audio_path = "" self.transcribed_text = ""definit_ui(self): self.setWindowTitle("会议记录整理工具") self.setGeometry(100, 100, 800, 600)# 中心部件 central_widget = QWidget() self.setCentralWidget(central_widget)# 主布局 main_layout = QVBoxLayout(central_widget)# 标签页 tab_widget = QTabWidget() main_layout.addWidget(tab_widget)# 1. 语音转文字标签页 stt_tab = QWidget() stt_layout = QVBoxLayout(stt_tab)# 选择文件按钮 btn_layout = QHBoxLayout() self.select_btn = QPushButton("选择音频文件(WAV)") self.select_btn.clicked.connect(self.select_audio) btn_layout.addWidget(self.select_btn) self.convert_btn = QPushButton("开始转写") self.convert_btn.clicked.connect(self.start_convert) self.convert_btn.setEnabled(False) btn_layout.addWidget(self.convert_btn) stt_layout.addLayout(btn_layout)# 进度条 self.progress_bar = QProgressBar() self.progress_bar.setValue(0) stt_layout.addWidget(self.progress_bar)# 转写结果 stt_layout.addWidget(QLabel("转写结果:")) self.result_text = QTextEdit() self.result_text.setReadOnly(True) stt_layout.addWidget(self.result_text)# 2. 内容分析标签页 analysis_tab = QWidget() analysis_layout = QVBoxLayout(analysis_tab) analysis_layout.addWidget(QLabel("会议要点:")) self.key_points_text = QTextEdit() self.key_points_text.setReadOnly(True) analysis_layout.addWidget(self.key_points_text) analysis_layout.addWidget(QLabel("待办事项:")) self.todo_text = QTextEdit() self.todo_text.setReadOnly(True) analysis_layout.addWidget(self.todo_text)# 分析按钮 self.analyze_btn = QPushButton("分析会议内容") self.analyze_btn.clicked.connect(self.analyze_content) self.analyze_btn.setEnabled(False) analysis_layout.addWidget(self.analyze_btn)# 3. 文档生成标签页 doc_tab = QWidget() doc_layout = QVBoxLayout(doc_tab) self.generate_btn = QPushButton("生成Word文档") self.generate_btn.clicked.connect(self.generate_document) self.generate_btn.setEnabled(False) doc_layout.addWidget(self.generate_btn) self.status_label = QLabel("状态:等待生成文档") doc_layout.addWidget(self.status_label)# 添加标签页 tab_widget.addTab(stt_tab, "语音转文字") tab_widget.addTab(analysis_tab, "内容分析") tab_widget.addTab(doc_tab, "文档生成")defselect_audio(self):"""选择音频文件""" file_path, _ = QFileDialog.getOpenFileName( self, "选择音频文件", "", "WAV文件 (*.wav)" )if file_path: self.audio_path = file_path self.select_btn.setText(f"已选择:{file_path.split('/')[-1]}") self.convert_btn.setEnabled(True) self.progress_bar.setValue(0)defstart_convert(self):"""开始语音转写""" self.convert_btn.setEnabled(False) self.progress_bar.setValue(0)# 创建并启动线程 self.stt_thread = XfSpeechToTextThread(self.audio_path) self.stt_thread.progress_signal.connect(self.update_progress) self.stt_thread.result_signal.connect(self.show_result) self.stt_thread.error_signal.connect(self.show_error) self.stt_thread.start()defupdate_progress(self, value):"""更新进度条""" self.progress_bar.setValue(value)defshow_result(self, text):"""显示转写结果""" self.transcribed_text = text self.result_text.setText(text) self.analyze_btn.setEnabled(True) QMessageBox.information(self, "成功", "语音转写完成!")defshow_error(self, error):"""显示错误信息""" QMessageBox.critical(self, "错误", error) self.convert_btn.setEnabled(True)defanalyze_content(self):"""分析会议内容"""ifnot self.transcribed_text: QMessageBox.warning(self, "警告", "请先完成语音转写!")return# 提取要点和待办 key_points = MeetingAnalyzer.extract_key_points(self.transcribed_text) todo_items = MeetingAnalyzer.extract_todo_items(self.transcribed_text)# 显示结果 self.key_points_text.setText("\n".join(key_points)) self.todo_text.setText("\n".join(todo_items) if todo_items else"无待办事项") self.generate_btn.setEnabled(True) QMessageBox.information(self, "成功", "内容分析完成!")defgenerate_document(self):"""生成Word文档""" save_path, _ = QFileDialog.getSaveFileName( self, "保存文档", f"会议记录_{datetime.now().strftime('%Y%m%d')}.docx", "Word文档 (*.docx)" )if save_path:try: key_points = self.key_points_text.toPlainText().split('\n') todo_items = self.todo_text.toPlainText().split('\n') MeetingAnalyzer.generate_doc( self.transcribed_text, key_points, todo_items, save_path ) self.status_label.setText(f"状态:文档已生成 → {save_path}") QMessageBox.information(self, "成功", "文档生成完成!")except Exception as e: QMessageBox.critical(self, "错误", f"生成失败:{str(e)}")if __name__ == "__main__": app = QApplication(sys.argv) window = MeetingToolWindow() window.show() sys.exit(app.exec_())
代码关键部分解释
XfSpeechToTextThread 类:单独线程处理语音转写,避免界面卡顿get_header() 方法:生成讯飞API所需的加密请求头audio_to_text() 方法:读取音频文件并发送转写请求
MeetingAnalyzer 类:提供要点提取(TF-IDF算法)和待办事项识别(关键词匹配)- 支持中文分词(jieba库),可通过停用词文件优化结果
- 使用python-docx库创建结构化Word文档
使用说明
- 替换代码中的
XF_APPID、XF_APISecret、XF_APIKey 为你的讯飞平台信息
总结
- 该工具基于Qt5实现可视化界面,通过讯飞API完成语音转文字,利用jieba分词提取会议要点和待办事项,最终生成结构化Word文档。
- 核心功能分为三部分:语音转写(多线程处理避免卡顿)、内容分析(TF-IDF+关键词匹配)、文档生成(python-docx)。
- 使用前需配置讯飞API密钥,仅支持WAV格式音频,界面交互逻辑完整,包含进度显示和错误提示。