项目介绍
这是一个 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 API (
SpeechRecognition/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 的浏览器自动禁用按钮
使用场景
完整的播客制作流程
- 准备阶段 :通过前端上传背景音乐到
storage/bgm/目录 - 对话生成:Agent 调用 TTS 工具生成多个角色的语音片段
- 音频拼接 :Agent 调用
concatenate_audio工具拼接对话 - BGM选择 :Agent 调用
select_background_music工具匹配背景音乐 - 混音合成 :Agent 调用
mix_audio_with_bgm工具生成最终播客成品 - 资源管理:通过前端查看、播放、删除所有音频资源
语音输入交互流程
- 用户点击聊天输入框旁的麦克风按钮
- 浏览器请求麦克风权限
- 开始录音,按钮显示脉冲动画
- 语音实时识别为文字
- 识别结果自动填入输入框
- 用户按 Enter 发送消息
升级指南
后端升级
-
安装新依赖:
bashcd backend pip install pydub -
安装 FFmpeg(如未安装):
bash# macOS brew install ffmpeg # Ubuntu sudo apt install ffmpeg -
创建新目录:
bashcd backend mkdir -p storage/bgm storage/podcasts -
准备背景音乐:
- 将
.mp3或.wav格式的背景音乐文件放入storage/bgm/目录 - 文件名建议使用场景描述(如
欢快的开场.mp3、深沉的讨论.mp3)
- 将
-
重启服务:
csspython main.py
前端升级
无需额外操作,API 调用逻辑和语音输入组件已在现有代码中集成。
技术亮点
- 专业的音频处理:支持交叉淡入淡出、音量归一化等专业特性
- 智能BGM匹配:基于文件名的语义匹配,易于扩展为向量检索
- 统一索引管理:所有音频资源统一管理,支持查询、上传、删除等操作
- 浏览器端语音识别:基于 Web Speech API,无需后端支持,零延迟
- 全链路闭环:从前端上传、语音输入到后端处理,完整的数据流转
结果展示


具体音频在podcasts里面
