项目介绍
这是一个 AI 驱动的播客创作助手,专注于从文字/视频/音频内容识别到播客制作的全流程。你可以用自然语言与它对话,也可以上传视频和音频,它会理解意图、调用相应的语音合成工具,把他们变成真实的播客音频。
关于项目的详细介绍见以下文章:
1.第一个 AI Agent 项目:从零搭建 AI 音频创作助手(入门篇): 这个是第一版本,里面主要进行初期框架搭建,包含Vue3 + ai-elements-vue 搭前端界面,LangChain 1.0 × AG-UI 协议 × Qwen TTS 驱动后端,支持定制音色,复刻音色和配音功能。
2.第一个 AI Agent 项目:从零搭建 AI 音频创作助手(进阶篇) : 这个是第二版本,新增了完整的播客后期制作能力,包括音频拼接、智能BGM选择、音轨混音三大核心功能,同时新增了音频资源管理API和语音输入功能,实现了从前端上传、语音输入到后端处理的全链路闭环。
这篇文章更多介绍高阶篇添加的功能。
更新概览
本次更新新增了音频、视频识别能力、临时-永久双层存储架构、定时清理机制和更智能的提示词系统,让播客 Agent 从单纯的"语音合成工具"升级为"全链路音频内容创作平台"。
一、后端更新
1. 音频/视频文件存储路径重构
变更文件:backend/app/tools/audio_index.py
原先音频文件统一保存在 storage/audios/ 目录下,现在改为保存到临时目录 storage/temp/,实现临时文件与永久文件的分离管理。
核心改动:新增 save_media_to_temp() 函数,支持三种输入类型:
def save_media_to_temp(media_data, filename: str) -> str:
"""将音频/视频文件保存到临时目录"""
temp_dir = STORAGE_DIR / "temp"
temp_dir.mkdir(parents=True, exist_ok=True)
file_path = temp_dir / filename
if isinstance(media_data, str):
# base64 字符串:解码保存
file_data = base64.b64decode(media_data)
file_path.write_bytes(file_data)
elif isinstance(media_data, bytes):
# bytes 对象:直接保存
file_path.write_bytes(media_data)
else:
# AudioSegment 对象:导出文件
file_format = filename.split('.')[-1]
media_data.export(str(file_path), format=file_format)
return f"storage/temp/{filename}"
设计思路:音色设计、声音克隆等工具生成的中间产物统一放在 storage/temp/,当用户确认满意后再通过 save_voice 工具将音色永久迁移到 storage/audios/。这样既避免了永久目录的膨胀,也让文件生命周期管理更清晰。
2. 定时临时文件清理任务
变更文件:backend/app/utils/temp_cleanup.py(新增)
新增定时清理机制,自动清除 storage/temp/ 目录下超过指定时间的过期文件,防止临时文件堆积占用磁盘空间。
核心实现:
def cleanup_temp_files(max_age_minutes: int = 10) -> dict:
"""清理过期的临时文件"""
cutoff_time = time.time() - (max_age_minutes * 60)
for file_path in TEMP_DIR.rglob("*"):
if not file_path.is_file():
continue
file_mtime = file_path.stat().st_mtime
if file_mtime < cutoff_time:
file_size = file_path.stat().st_size
file_path.unlink()
stats["deleted_files"] += 1
stats["freed_bytes"] += file_size
# 清理空目录
cleanup_empty_dirs(TEMP_DIR)
return stats
关键特性:
- 默认保留时间 10 分钟,可灵活配置
- 递归清理所有子目录,并自动移除空目录
- 返回详细统计信息(文件数、删除数、释放空间)
- 提供
schedule_cleanup_task()函数,方便接入 APScheduler 等定时调度框架
3. 音色保存工具
变更文件:backend/app/tools/voice_save.py(新增)
新增 save_voice 工具,将用户满意的定制音色从临时目录永久保存到 storage/audios/,并记录到 voice_index.json 索引文件。
核心逻辑:
@tool("save_voice", args_schema=VoiceSaveInput)
def save_voice_tool(audio_source: str, voice_id: str, text: str, model_name: str = "") -> str:
# 判断输入类型:文件路径 或 base64数据
if audio_source.startswith("storage/") or audio_source.startswith("/"):
# 从临时目录复制到永久目录
source_path = BASE_DIR / audio_source.lstrip("/")
dest_path = AUDIOS_DIR / source_path.name
shutil.copy2(source_path, dest_path)
local_path = f"storage/audios/{source_path.name}"
else:
# base64 数据:解码并保存
local_path = save_audio_from_base64(audio_source, text, "saved_voice")
# 记录到索引文件
record_voice_index(local_path, voice_id, model_name, path="audios")
return json.dumps({"audio_url": local_path, "voice_id": voice_id, ...})
设计亮点:
- 支持两种输入方式:临时文件路径(直接复制)和 base64 编码数据(解码保存)
- 自动生成唯一文件名,包含时间戳、UUID、音色ID和文本片段
- 使用文件锁(
fcntl.flock)保证索引文件并发写入安全 - 与播客工作流无缝衔接:音色设计 → 临时保存 → 用户确认 → 永久保存
4. 语音识别工具(ASR)
变更文件:backend/app/tools/qwen_asr.py(新增)
集成阿里云 DashScope qwen3-asr-flash 模型,提供高精度语音识别能力。
核心调用链:
@tool("qwen_asr_tool", args_schema=ASRInput)
def qwen_asr_tool(audio: str, enable_itn: bool = False) -> str:
# 1. 解析音频来源(URL / 本地路径 → data URI)
audio_url = _resolve_audio_source(audio)
# 2. 调用 ASR API
response = _call_asr_api(audio_url, enable_itn)
# 3. 提取识别文本
text = _extract_text_from_response(response)
return text
关键能力:
- 多格式支持:MP3、WAV、OGG、FLAC、M4A、AAC 等主流音频格式
- ITN 逆文本标准化:自动将口语化表达转为书面形式(如"二零二五年" → "2025年")
- 智能来源解析:支持本地路径和网络 URL,本地文件自动转为 base64 data URI
- MIME 类型自动推断:根据文件扩展名匹配正确的 MIME 类型
5. 多模态识别工具
变更文件:backend/app/tools/qwen_multimodal.py(新增)
集成阿里云 DashScope qwen3.5-omni-plus 模型,支持图片、视频、音频的统一多模态理解。
两大核心工具:
| 工具名 | 功能 | 适用场景 |
|---|---|---|
qwen_multimodal_tool | 单媒体多模态识别 | 分析单张图片/单个视频/单段音频 |
qwen_combined_multimodal_tool | 组合多模态识别 | 同时分析多个媒体(如图片+音频组合) |
大视频智能分割:当视频文件超过 21MB 时,自动使用 moviepy 分割为多个片段分别处理:
def _split_and_encode_video(video_path: Path, suffix: str) -> List[dict]:
video = VideoFileClip(str(video_path))
target_size = 10 * 1024 * 1024 # 每段 10MB
num_segments = max(1, int(file_size / target_size))
segment_duration = duration / num_segments
for i in range(num_segments):
segment = video.subclipped(start_time, end_time)
# 编码并添加到片段列表
segments.append(_build_media_content(data_uri, suffix, is_url=False))
return segments
额外能力:
- 支持流式输出(
stream=True),实时获取识别结果 - 支持音频输出模态(
enable_audio_output),可指定音色进行语音合成 - 完善的错误处理和日志追踪
6. 提示词优化
变更文件:backend/app/services/prompt.py
对播客 Agent 的系统提示词进行了全面精炼和增强:
优化要点:
- 结构重组:将原本杂乱的提示词拆分为清晰的模块化结构——核心能力定位、工作方式、工具调用规则、沟通规范、上下文理解、执行流程
- 新增音视频识别内容:在工作流中明确区分语音识别(
qwen_asr_tool)和多模态理解(qwen_multimodal_tool/qwen_combined_multimodal_tool)的使用场景 - 工具使用策略表格化:用清晰的映射关系说明各工具职责,降低 LLM 误调用概率
- 音色保存流程标准化:明确"设计 → 临时保存 → 用户确认 → 永久保存"的四步流程
- 避免重复调用:在提示词中强调"对于简单请求,调用一次工具后立即结束回复"
新增的音视频识别工作流描述:
**音视频转播客工作流**:
1. 内容识别:调用 qwen_multimodal_tool 或 qwen_asr_tool 识别音视频中的内容
2. 脚本整理:基于识别出的内容,整理为播客脚本
3. 播客制作:按照工作流步骤继续执行,完成播客制作
二、前端更新
本次更新前端无变更,主要集中在后端能力的扩展和优化。前端现有的 ChatAgent.vue 已具备音视频文件上传、播放预览、工具调用结果展示等能力,能够完整承接后端新增的 ASR 和多模态识别功能。
三、架构总结
本次更新的核心脉络:
用户上传音视频
│
▼
┌─────────────────────────────────────────────┐
│ 内容识别层 │
│ ├─ qwen_asr_tool 语音→文字 │
│ └─ qwen_multimodal_tool 音视频→理解 │
└─────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ 播客制作层 │
│ ├─ 音色设计 → 临时目录 (storage/temp/) │
│ ├─ save_voice → 永久目录 (storage/audios/) │
│ └─ 音频合成 / 混音 / 拼接 │
└─────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ 系统维护层 │
│ └─ temp_cleanup 定时清理临时文件 │
└─────────────────────────────────────────────┘
通过"临时-永久"双层存储架构 + 定时清理机制,既保证了播客制作流程的灵活性,也确保了系统资源的有效管理。
四、技术亮点
1. 临时-永久双层存储架构
本次更新最核心的架构设计。工具生成的中间产物(音色设计、声音克隆结果)统一写入 storage/temp/,只有用户明确确认满意的音色才通过 save_voice 工具迁移到 storage/audios/。这种设计:
- 避免存储膨胀:临时文件有 10 分钟生命周期,过期自动清理
- 语义清晰:
temp/= 未确认的中间产物,audios/= 用户确认的最终产物 - 用户可控:用户拥有"保存"和"丢弃"的主动权
2. 大视频智能分段策略
qwen_multimodal.py 中当视频文件超过 21MB 时,自动用 moviepy 按时间轴均匀切分为多个 ≤10MB 的片段分别上传识别。这解决了大模型 API 对单文件大小的限制问题,同时保证了识别完整性。
3. 并发安全的索引文件写入
voice_save.py 使用 fcntl.flock 对 voice_index.json 加排他锁,确保多请求并发写入时不会出现数据竞争或索引损坏。
4. 模块化提示词工程
prompt.py 将系统提示词拆分为 6 个独立模块(能力定位、工作方式、工具规则、沟通规范、上下文理解、执行流程),每个模块职责单一、边界清晰,大幅降低了 LLM 误调用工具的概率,也便于后续迭代维护。
五.结果展示