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

项目介绍

这是一个 AI 驱动的音频内容创作助手。你可以用自然语言与它对话,它会理解意图、调用相应的语音合成工具,把文字变成真实的播客音频,并保存在本地。

关于项目的详细介绍见第一篇文章:第一个 AI Agent 项目:从零搭建 AI 音频创作助手(入门篇)

这篇文章更多介绍进阶篇添加的功能

更新概览

本次更新新增了完整的播客后期制作能力,包括音频拼接、智能BGM选择、音轨混音三大核心功能,同时新增了音频资源管理API和语音输入功能,实现了从前端上传、语音输入到后端处理的全链路闭环。


后端更新

1. 音频混音工具模块

新增 backend/app/tools/audio_mixing.py,提供三个核心音频处理工具:

1.1 音频拼接工具(concatenate_audio

将多个音频片段按顺序拼接成完整对话,适用于播客场景。

核心功能

  • 支持交叉淡入淡出过渡(crossfade),使音频过渡更自然
  • 可配置音频片段之间的静音时长(默认1200ms,播客推荐1000-1500ms)
  • 自动生成带时间戳的唯一文件名
  • 自动记录到音频索引系统

代码示例

python 复制代码
@tool("concatenate_audio", args_schema=ConcatenateAudioInput)
def concatenate_audio_tool(
    audio_files: List[str],       # 音频文件路径列表
    crossfade_duration: int = 200,  # 交叉淡入淡出时长(ms)
    silence_duration: int = 1200    # 静音间隔(ms)
) -> str:
    """
    拼接逻辑:
    1. 加载所有音频片段 → AudioSegment.from_file()
    2. 依次拼接,支持两种模式:
       - crossfade > 0:交叉淡入淡出过渡
       - silence > 0:静音间隔拼接
    3. 导出为 WAV 文件并记录索引
    """

使用场景:将多个角色的语音片段拼接成播客对话。

1.2 智能BGM选择工具(select_background_music

根据场景描述智能匹配合适的背景音乐。

核心功能

  • 基于文件名的语义匹配(BGM文件名即场景描述)
  • 自动循环播放或裁剪以匹配目标时长
  • 支持直接指定BGM文件路径
  • 自动添加淡出效果

代码示例

python 复制代码
@tool("select_background_music", args_schema=SelectBGMInput)
def select_bgm_tool(
    scene_description: str,           # 场景描述,如"欢快的开场"
    duration_seconds: Optional[float] = None,  # 期望时长
    bgm_audio_path: Optional[str] = None       # 指定BGM路径
) -> str:
    """
    匹配逻辑:
    1. 如果指定了 bgm_audio_path,直接使用
    2. 否则扫描 storage/bgm/ 目录下所有 .mp3/.wav 文件
    3. 基于文件名与 scene_description 的关键词匹配度打分
    4. 选择得分最高的BGM,必要时循环/裁剪匹配时长
    """

使用场景:为播客对话选择合适的背景音乐。

1.3 音频混音工具(mix_audio_with_bgm

将人声对话与背景音乐混合,生成专业的播客成品。

核心功能

  • 专业BGM开场效果

    • 前N秒(默认3秒):BGM原音量播放
    • 过渡阶段:音量渐变至背景音量
    • 后续阶段:BGM降低至约5%音量作为背景
  • 音量归一化处理,确保音质一致性

  • 自动淡出效果

  • 支持自定义BGM音量(推荐-24到-28dB)

代码示例

python 复制代码
@tool("mix_audio_with_bgm", args_schema=MixAudioWithBGMInput)
def mix_audio_with_bgm_tool(
    voice_audio: str,           # 主音频路径(人声)
    bgm_audio: str,             # BGM路径
    bgm_volume: float = -26,    # BGM背景音量(dB)
    intro_duration: float = 3.0, # BGM开场时长(秒)
    normalize: bool = True       # 是否归一化
) -> str:
    """
    混音逻辑:
    1. 加载人声和BGM音频
    2. BGM分段处理:
       - 开场段:原音量播放 intro_duration 秒
       - 过渡段:20步渐变从原音量到 bgm_volume
       - 背景段:固定 bgm_volume 音量
    3. 人声前插入静音(与BGM开场对齐)
    4. 叠加人声到BGM → bgm_processed.overlay(voice_with_intro)
    5. 可选归一化 + 淡出,导出为 MP3
    """

音量变化曲线

markdown 复制代码
BGM 音量变化:
原音量 ─────┐
            │\
            │ \  过渡时段(2秒)
            │  \
背景音量 ───┘   └────────── 背景音量
         ↑
      开场时段(3秒)

2. 音频资源管理API

新增 backend/app/routers/resources.py,提供音频资源的RESTful API。

2.1 接口列表

方法 路径 说明
GET /resources/audio 查询所有音频资源列表
POST /resources/audio/upload 上传音频文件(支持 .mp3 和 .wav)
DELETE /resources/audio/{id} 删除指定音频资源

2.2 上传接口代码示例

python 复制代码
@router.post("/audio/upload", response_model=UploadResponse)
async def upload_audio_resource(file: UploadFile = File(...)):
    """上传音频文件,存储到 BGM_DIR 并记录到索引"""
    # 1. 校验文件格式(仅支持 .mp3 和 .wav)
    ext = os.path.splitext(file.filename)[1].lower()
    if ext not in UPLOAD_ALLOWED_EXTENSIONS:
        raise HTTPException(status_code=400, detail=f"不支持的文件格式")

    # 2. 生成唯一文件名,避免冲突
    voice_id = str(uuid.uuid4())
    safe_filename = f"{name_without_ext}{voice_id}{ext}"

    # 3. 保存文件到 storage/bgm/
    with open(save_path, "wb") as f:
        shutil.copyfileobj(file.file, f)

    # 4. 记录到音频索引
    record_voice_index(local_path=save_path, voice=voice_id, model_name="", path="bgm")

    return UploadResponse(success=True, message="上传成功", voice_id=voice_id)

2.3 删除接口代码示例

python 复制代码
@router.delete("/audio/{id}", response_model=DeleteResponse)
async def delete_audio_resource(id: str):
    """删除音频资源:移除索引记录 + 删除物理文件"""
    # 1. 从 voice_index.json 中查找匹配记录
    matched = [r for r in voice_index if r.get("id") == id]

    # 2. 删除对应的本地文件
    for record in matched:
        filepath = os.path.abspath(record.get("local_path", ""))
        os.remove(filepath)

    # 3. 从索引中移除并写回
    voice_index = [r for r in voice_index if r.get("id") != id]
    with open(index_path, "w", encoding="utf-8") as f:
        json.dump(voice_index, f, ensure_ascii=False, indent=2)

    return DeleteResponse(success=True, message="删除成功")

3. 音频索引系统

新增 backend/app/tools/audio_index.py,提供统一的音频索引管理,供各工具模块共享调用。

代码示例

python 复制代码
def record_voice_index(local_path: str, voice: str, model_name: str = "", path: str = "audios") -> None:
    """将音频文件信息记录到 voice_index.json 索引文件中

    Args:
        local_path: 音频文件本地路径
        voice: 音色ID
        model_name: 模型名称
        path: 音频存储路径类型 (audios/bgm/podcasts)
    """
    # 读取已有索引 → 追加新记录 → 写回文件
    new_record = {
        "id": str(uuid.uuid4()),
        "local_path": local_path,
        "voice_id": voice,
        "model_name": model_name,
        "path": path,
        "createTime": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    }
    index.append(new_record)
    VOICE_INDEX_FILE.write_text(json.dumps(index, ensure_ascii=False, indent=2))

path 字段说明

path 值 说明 存储目录
audios TTS生成的音频 storage/audios/
bgm 上传的背景音乐 storage/bgm/
podcasts 混音生成的播客成品 storage/podcasts/

4. 新增依赖

复制代码
pip install pydub

注意 :pydub 需要系统安装 FFmpeg。macOS: brew install ffmpeg,Ubuntu: sudo apt install ffmpeg

5. 存储目录结构更新

bash 复制代码
storage/
├── audios/              # TTS生成的音频
├── bgm/                 # 背景音乐(新增)
├── podcasts/            # 播客成品(新增)
├── voice_index.json     # 音频索引(统一管理)
└── images/              # 图片文件

前端更新

1. 音频资源API对接

新增上传音频文件和删除音频资源功能,对应 fronted/src/api/resource.ts 中的两个新接口

2. 语音输入转文字

在聊天输入框中新增语音输入功能,用户可以通过麦克风将语音转换为文字发送消息。

组件位置fronted/src/components/ai-elements/prompt-input/PromptInputSpeechButton.vue

使用方式 :在 ChatAgent.vue 的输入区域中集成麦克风按钮:

xml 复制代码
<PromptInputFooter>
  <PromptInputSpeechButton />  
</PromptInputFooter>

技术实现

  • 基于 Web Speech APISpeechRecognition / webkitSpeechRecognition)实现浏览器端语音识别
  • 默认语言为中文(zh-CN),支持通过 lang 属性配置
  • 支持连续识别(continuous: true)和中间结果(interimResults: true
  • 识别完成后自动将文字填入输入框,智能处理中英文空格

核心代码

ini 复制代码
// 初始化语音识别
const sr = new SpeechRecognition()
sr.continuous = true          // 连续识别
sr.interimResults = true      // 返回中间结果
sr.lang = 'zh-CN'             // 默认中文

// 识别结果回调
sr.onresult = (event) => {
  let finalTranscript = ''
  for (let i = event.resultIndex; i < event.results.length; i++) {
    if (event.results[i].isFinal) {
      finalTranscript += event.results[i][0]?.transcript ?? ''
    }
  }
  if (finalTranscript) {
    // 智能添加空格:中文不加空格,英文前加空格
    const needsSpace = textInput.value && !/[\u4e00-\u9fa5]/.test(textInput.value.slice(-1))
    setTextInput(textInput.value + (needsSpace ? ' ' : '') + finalTranscript)
  }
}

交互效果

  • 点击麦克风按钮开始录音,按钮显示脉冲动画
  • 再次点击停止录音
  • 识别结果自动追加到输入框文本中
  • 不支持 Web Speech API 的浏览器自动禁用按钮

使用场景

完整的播客制作流程

  1. 准备阶段 :通过前端上传背景音乐到 storage/bgm/ 目录
  2. 对话生成:Agent 调用 TTS 工具生成多个角色的语音片段
  3. 音频拼接 :Agent 调用 concatenate_audio 工具拼接对话
  4. BGM选择 :Agent 调用 select_background_music 工具匹配背景音乐
  5. 混音合成 :Agent 调用 mix_audio_with_bgm 工具生成最终播客成品
  6. 资源管理:通过前端查看、播放、删除所有音频资源

语音输入交互流程

  1. 用户点击聊天输入框旁的麦克风按钮
  2. 浏览器请求麦克风权限
  3. 开始录音,按钮显示脉冲动画
  4. 语音实时识别为文字
  5. 识别结果自动填入输入框
  6. 用户按 Enter 发送消息

升级指南

后端升级

  1. 安装新依赖

    bash 复制代码
    cd backend
    pip install pydub
  2. 安装 FFmpeg(如未安装):

    bash 复制代码
    # macOS
    brew install ffmpeg
    # Ubuntu
    sudo apt install ffmpeg
  3. 创建新目录

    bash 复制代码
    cd backend
    mkdir -p storage/bgm storage/podcasts
  4. 准备背景音乐

    • .mp3.wav 格式的背景音乐文件放入 storage/bgm/ 目录
    • 文件名建议使用场景描述(如 欢快的开场.mp3深沉的讨论.mp3
  5. 重启服务

    css 复制代码
    python main.py

前端升级

无需额外操作,API 调用逻辑和语音输入组件已在现有代码中集成。


技术亮点

  1. 专业的音频处理:支持交叉淡入淡出、音量归一化等专业特性
  2. 智能BGM匹配:基于文件名的语义匹配,易于扩展为向量检索
  3. 统一索引管理:所有音频资源统一管理,支持查询、上传、删除等操作
  4. 浏览器端语音识别:基于 Web Speech API,无需后端支持,零延迟
  5. 全链路闭环:从前端上传、语音输入到后端处理,完整的数据流转

结果展示

具体音频在podcasts里面

相关推荐
满栀5853 小时前
vue3动态路由详细效果
前端·javascript·vue.js·typescript·前端框架
mCell12 小时前
DeepSeek Harness 速览:“一切皆插件”意味着什么
typescript·agent·deepseek
京东云开发者19 小时前
【全栈实践】第一个 AI Agent 项目:从零搭建 AI 音频创作助手(入门篇)
typescript·agent·vuex
云和数据.ChenGuang1 天前
fastapi项目拆分实战数据模型
java·服务器·数据库·人工智能·深度学习·fastapi·强化学习
燐妤1 天前
中老年智能健康管理项目
python·ai·pycharm·vue3·fastapi·项目开发·健康
王林不想说话1 天前
React + Ant Design 后台项目:63 个业务组件的分层实践
react.js·typescript·ant design
大家的林语冰1 天前
👍 超越 ESLint,Oxc 优先采用 TypeScript 7,Rust 和 Go 梦幻联动!
前端·javascript·typescript
circuitsosk1 天前
构建高可用AI后端服务:REST API设计、数据库交互及异步任务编排经验总结
数据库·人工智能·python·交互·fastapi·数据库连接池·rest api
embedded大铭1 天前
ai时代上站记录
typescript