【全栈实践】第一个 AI Agent 项目:从零搭建 AI 音频创作助手(高级篇)

0 阅读8分钟

项目介绍

这是一个 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 误调用工具的概率,也便于后续迭代维护。


五.结果展示

image.png
image.png
image.png