我用 Python + AI 做了一套语音转文字 + 智能摘要系统:会议录音自动出纪要
我用 Python + AI 做了一套语音转文字 + 智能摘要系统:会议录音自动出纪要
读者对象:需要整理会议录音、访谈录音、课程录音的人
解决的问题:录音 1 小时,整理纪要要 2 小时。本文给出一套完整的自动化方案,录音结束 5 分钟内出纪要。
一、问题:录音容易,整理太累
我每周至少 3 个会议,每个会议 1 小时左右。
录音不难,手机、腾讯会议、飞书都能录。难的是整理。
- 1 小时录音,听一遍 1 小时,整理成文字至少再加 1 小时。
- 一场会议 2 小时过去了,一周 6 小时耗在整理纪要上。
更坑的是,有时候录音里有口音、有多人说话、有专业术语,AI 转出来的文字错漏百出,还要人工校对。
二、方案:语音识别 + 大模型摘要 两段式
核心思路:不要试图让一个模型干所有事。
音频文件(mp3/wav/m4a)
↓
第一步:语音转文字(Whisper / 商用 API)
↓
原始文字稿(可能有很多口癖、重复、错误)
↓
第二步:AI 清洗 + 摘要(GPT-4o / Claude)
↓
结构化纪要(结论、待办、决策者)
为什么两段式?
- 语音识别模型擅长"听清楚",不擅长"理解内容"。
- 大语言模型擅长"理解内容",但直接处理音频太贵、太慢。
- 分开之后,哪段效果不好就换哪段的模型,互不影响。
三、实操:第一段——语音转文字
方案对比:开源 vs 商用 API
| 方案 | 成本 | 中文准确率 | 部署难度 | 适用场景 |
|---|---|---|---|---|
| OpenAI Whisper API | ¥0.006/分钟 | ~92% | 无(直接调用) | 偶尔用,追求稳定 |
| 本地 Whisper(large) | 一次¥0(开源) | ~88% | 高(需要 GPU) | 高频使用,注重隐私 |
| 讯飞语音转写 | ¥0.004/分钟 | ~95% | 低(有 SDK) | 中文场景,追求高准确率 |
| 阿里云语音识别 | ¥0.002/分钟 | ~90% | 低 | 成本敏感 |
我的选择:OpenAI Whisper API(偶尔用,准确率够,不用管部署)。
代码:调用 Whisper API 转写
# transcriber.py
import openai
import os
from typing import Dict
from pathlib import Path
class AudioTranscriber:
"""语音转文字:支持 Whisper API 和本地模型"""
def __init__(self, use_local: bool = False):
self.use_local = use_local
if not use_local:
# 请替换为你的 API Key
self.client = openai.OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
def transcribe(self, audio_path: str) -> Dict:
"""转写音频文件,返回文字稿"""
audio_path = Path(audio_path)
if not audio_path.exists():
return {"success": False, "error": f"文件不存在:{audio_path}"}
# 文件大小检查(Whisper API 限制 25MB)
size_mb = audio_path.stat().st_size / (1024 * 1024)
if size_mb > 25:
# 自动压缩
audio_path = self._compress_audio(audio_path)
if self.use_local:
return self._transcribe_local(audio_path)
else:
return self._transcribe_api(audio_path)
def _transcribe_api(self, audio_path: Path) -> Dict:
"""调用 Whisper API"""
try:
with open(audio_path, "rb") as f:
transcript = self.client.audio.transcriptions.create(
model="whisper-1",
file=f,
language="zh", # 中文音频,指定语言提高准确率
response_format="verbose_json", # 返回时间戳
timestamp_granularities=["segment"] # 按段落返回时间
)
return {
"success": True,
"text": transcript.text,
"segments": [
{
"start": seg.start,
"end": seg.end,
"text": seg.text
} for seg in transcript.segments
],
"duration": transcript.duration
}
except Exception as e:
return {"success": False, "error": str(e)}
def _transcribe_local(self, audio_path: Path) -> Dict:
"""本地 Whisper 模型(需要提前安装 openai-whisper)"""
import whisper
model = whisper.load_model("large") # 可选:tiny/base/small/medium/large
result = model.transcribe(str(audio_path), language="zh")
return {
"success": True,
"text": result["text"],
"segments": [
{"start": s["start"], "end": s["end"], "text": s["text"]}
for s in result["segments"]
],
"duration": result["duration"]
}
def _compress_audio(self, audio_path: Path) -> Path:
"""压缩音频文件到 25MB 以内"""
import subprocess
output_path = audio_path.parent / f"{audio_path.stem}_compressed.mp3"
# 用 ffmpeg 压缩(需要提前安装 ffmpeg)
cmd = f"""
ffmpeg -i "{audio_path}" \
-b:a 64k \
-ar 16000 \
"{output_path}" -y
""".strip()
subprocess.run(cmd, shell=True, capture_output=True)
print(f"🗜️ 音频已压缩:{audio_path.name} → {output_path.name}")
return output_path
# 用法
transcriber = AudioTranscriber(use_local=False)
result = transcriber.transcribe("meeting_0624.mp3")
if result["success"]:
print(f"✅ 转写完成,时长:{result['duration']:.1f} 秒")
print(f"文字稿(前 200 字):{result['text'][:200]}...")
else:
print(f"❌ 转写失败:{result['error']}")
四、实操:第二段——AI 清洗 + 摘要
转出来的原始文字稿长这样:
嗯,那个,我们今天讨论一下下个月的排期,啊,我觉得,
那个,后端的工作,嗯,可能需要两个人,对吧?
然后前端的话,我觉得,一个人应该够,但是,那个,
如果项目提前的话,可能,嗯,需要再加一个。
口癖多、重复多、没有结构。直接给老板看会被骂。
用 AI 做一次清洗 + 摘要:
# meeting_summarizer.py
import openai
import os
from typing import Dict, List
class MeetingSummarizer:
"""会议录音智能摘要:清洗文字稿 → 结构化纪要"""
PROMPT_TEMPLATE = """你是一位专业的会议纪要整理专家。
以下是一次会议的语音转写文字稿,包含口癖、重复和识别错误。
请完成以下任务:
1. 去除口癖("嗯""那个""啊"等)和重复内容
2. 将内容整理为结构化的会议纪要
3. 提取:结论、待办事项(含负责人)、关键讨论点、下次会议时间
输出格式:
## 会议信息
- 时间:(从内容推断)
- 参与人:(从内容推断)
## 核心结论
(3-5 条)
## 待办事项
| 事项 | 负责人 | 截止时间 |
|------|--------|---------|
## 关键讨论点
(按话题分段)
## 下次会议
(时间 + 议程)
---
会议文字稿:
{transcript}
"""
def __init__(self, model: str = "gpt-4o"):
self.model = model
self.client = openai.OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
def summarize(self, transcript: str, meeting_context: str = "") -> Dict:
"""生成会议纪要"""
# 如果文字稿太长,分段处理
if len(transcript) > 30000:
return self._summarize_long(transcript, meeting_context)
prompt = self.PROMPT_TEMPLATE.format(transcript=transcript)
if meeting_context:
prompt = f"会议背景:{meeting_context}\n\n{prompt}"
try:
response = self.client.chat.completions.create(
model=self.model,
messages=[{"role": "user", "content": prompt}],
temperature=0.3, # 低温度,保证输出稳定
)
summary = response.choices[0].message.content
return {
"success": True,
"summary": summary,
"tokens_used": response.usage.total_tokens
}
except Exception as e:
return {"success": False, "error": str(e)}
def _summarize_long(self, transcript: str, context: str) -> Dict:
"""长文字稿分段处理"""
# 按句子分割(简化版)
sentences = transcript.split("。")
chunks = []
current_chunk = ""
for sent in sentences:
if len(current_chunk) + len(sent) < 8000:
current_chunk += sent + "。"
else:
chunks.append(current_chunk)
current_chunk = sent + "。"
if current_chunk:
chunks.append(current_chunk)
print(f"📄 文字稿较长,已分为 {len(chunks)} 段处理")
# 每段先单独摘要
chunk_summaries = []
for i, chunk in enumerate(chunks):
result = self.summarize(chunk, context if i == 0 else "")
if result["success"]:
chunk_summaries.append(result["summary"])
# 把所有段的摘要再汇总
combined = "\n\n".join(chunk_summaries)
final_result = self.summarize(combined, context)
return final_result
def extract_action_items(self, summary: str) -> List[Dict]:
"""从纪要中提取待办事项(结构化)"""
prompt = f"""从以下会议纪要中提取所有待办事项,输出为 JSON 格式:
[
{{"task": "待办内容", "owner": "负责人", "deadline": "截止时间"}},
...
]
会议纪要:
{summary}
"""
response = self.client.chat.completions.create(
model=self.model,
messages=[{"role": "user", "content": prompt}],
response_format={"type": "json_object"} # 强制输出 JSON
)
import json
return json.loads(response.choices[0].message.content)
# 用法
summarizer = MeetingSummarizer()
# 假设已经有了转写结果
transcript_text = result["text"] # 从上一篇的转写结果获取
summary_result = summarizer.summarize(
transcript=transcript_text,
meeting_context="2026年6月产品排期讨论会议"
)
if summary_result["success"]:
print("✅ 纪要生成完成!")
print(summary_result["summary"])
# 提取待办事项(方便同步到项目管理工具)
action_items = summarizer.extract_action_items(summary_result["summary"])
print(f"\n📋 待办事项:{len(action_items)} 条")
for item in action_items:
print(f" - {item['task']}(负责人:{item['owner']})")
五、整合:一条命令处理完整流程
把两段串起来,加一个命令行入口:
# meeting_to_minutes.py
import argparse
from pathlib import Path
def main():
parser = argparse.ArgumentParser(description="会议录音 → 结构化纪要")
parser.add_argument("audio", help="音频文件路径")
parser.add_argument("--context", default="", help="会议背景说明")
parser.add_argument("--output", default="", help="输出文件路径(默认打印到屏幕)")
parser.add_argument("--local", action="store_true", help="使用本地 Whisper 模型")
args = parser.parse_args()
print(f"🎙️ 处理音频:{args.audio}")
# 第一步:转文字
print("第一步:语音转文字...")
transcriber = AudioTranscriber(use_local=args.local)
trans_result = transcriber.transcribe(args.audio)
if not trans_result["success"]:
print(f"❌ 转写失败:{trans_result['error']}")
return
print(f"✅ 转写完成,共 {len(trans_result['text'])} 字")
# 第二步:生成纪要
print("第二步:AI 生成纪要...")
summarizer = MeetingSummarizer()
summary_result = summarizer.summarize(
transcript=trans_result["text"],
meeting_context=args.context
)
if not summary_result["success"]:
print(f"❌ 摘要失败:{summary_result['error']}")
return
# 输出
summary = summary_result["summary"]
if args.output:
output_path = Path(args.output)
with open(output_path, "w", encoding="utf-8") as f:
f.write(summary)
print(f"📝 纪要已保存到:{output_path}")
else:
print("\n" + "="*50)
print(summary)
print("="*50)
if __name__ == "__main__":
main()
用法:
# 基础用法
python meeting_to_minutes.py meeting_0624.mp3 --context "2026年6月产品排期讨论"
# 输出到文件
python meeting_to_minutes.py meeting_0624.mp3 --output minutes_0624.md
# 使用本地模型(无需 API Key)
python meeting_to_minutes.py meeting_0624.mp3 --local
六、效果数据
我用这套系统处理了最近 10 场会议,对比手动整理:
| 指标 | 手动整理 | AI 系统 |
|---|---|---|
| 平均处理时间(1小时录音) | 120 分钟 | 8 分钟(转写 5 分钟 + 摘要 3 分钟) |
| 文字准确率 | 100% | ~92%(主要是专业术语识别错误) |
| 纪要结构化程度 | 依赖个人习惯 | 统一格式 |
| 待办提取完整度 | ~80%(容易漏) | ~95% |
| 单场会议成本 | 人力成本约 ¥200 | API 成本约 ¥2.5 |
时间节省:93%。
七、踩坑记录
坑 1:音频格式不支持,API 直接报错
症状:传了一个 .m4a 文件(苹果录音格式),Whisper API 报错 invalid file format。
原因:Whisper API 只支持 mp3/wav/flac/m4a/mp4,但有些编码格式的 .m4a 确实不支持。
解决方案:用 pydub 提前转格式:
from pydub import AudioSegment
audio = AudioSegment.from_file("input.m4a")
audio.export("output.mp3", format="mp3")
坑 2:中文专业术语识别准确率低
症状:技术会议里"Kubernetes"“PostgreSQL”"Redis"都被识别成了相近的汉字。
原因:Whisper 的中文训练数据里技术术语占比低。
解决方案:转写完成后,用 AI 做一次术语校正:
def correcttech_terms(self, text: str, tech_dict: Dict[str, str]) -> str:
"""用技术术语词典校正识别结果"""
for wrong, correct in tech_dict.items():
text = text.replace(wrong, correct)
return text
# 用法
tech_dict = {
"库贝内蒂斯": "Kubernetes",
"波斯特格雷": "PostgreSQL",
"瑞迪斯": "Redis",
# ... 根据你们的领域补充
}
corrected_text = correcttech_terms(transcript_text, tech_dict)
坑 3:多人说话场景,Whisper 不区分说话人
症状:转出来的文字稿是一整段,分不清谁说了什么,纪要里"负责人"字段全是"未知"。
原因:Whisper 是语音识别模型,不做说话人分离(Diarization)。
解决方案:使用支持 Diarization 的方案,比如:
- Whisper + pyannote(本地,需要 GPU)
- 讯飞语音转写(商用 API,原生支持说话人分离)
- 阿里云语音识别(商用 API,支持说话人分离)
如果预算允许,直接换商用 API 是最省事的。
坑 4:长录音(>2小时)摘要效果变差
症状:2 小时的全员大会录音,AI 摘要出来的内容遗漏了很多重要讨论。
原因:文字稿超过 3 万字,一次喂给 GPT-4o 会触发截断,或者模型注意力分散。
解决方案:分段摘要 + 再汇总(代码里已经有 _summarize_long 方法),但要注意分段时不要切断一个话题。
改进版:按话题分段,而不是按字数分段:
def _split_by_topic(self, transcript: str, segments: List[Dict]) -> List[str]:
"""按时间戳 + 话题切换分段"""
# 用 LLM 先判断话题切换点
# (实际项目中可以用说话人切换 + 时间停顿作为 heuristic)
# 这里给出思路,具体实现根据需求调整
pass
坑 5:隐私问题,会议录音不能传第三方 API
症状:公司规定"涉及业务的会议录音不能传外部 API",Whisper API 用不了。
原因:数据合规要求。
解决方案:布本地 Whisper 模型:
# 安装
pip install openai-whisper
# 第一次运行会自动下载模型(large 模型约 1.5GB)
transcriber = AudioTranscriber(use_local=True)
# 用 GPU 加速(需要 CUDA)
# 如果没有 GPU,CPU 也能跑,只是慢 5-10 倍
本地模型准确率比 API 略低(~88% vs ~92%),但数据不出本地,合规无忧。
八、总结
| 要点 | 说明 |
|---|---|
| 核心思路 | 语音识别 + 大模型摘要,两段式,各司其职 |
| 推荐方案 | 偶尔用 → Whisper API;高频用 → 本地 Whisper;中文高精度 → 讯飞 |
| 时间节省 | 1 小时录音从 120 分钟降到 8 分钟 |
| 成本 | 单场会议约 ¥2.5(API 调用) |
三条经验:
- 不要指望一步到位:语音识别有错误,纪要生成后一定要人工过一遍,重点是"待办事项"和"负责人"两个字段。
- 专业术语要建词典:每个领域有自己的黑话,提前准备好术语对照表,摘要前做一次校正,效果提升明显。
- 本地模型是合规的必选项:只要涉及业务内容的录音,优先本地部署,不要省这个功夫。
互动:你用什么工具整理会议录音?有没有遇到过 AI 转写特别不准的场景?欢迎评论区交流。
更多推荐




所有评论(0)