在线咨询
专属客服在线解答,提供专业解决方案
工单支持
专业技术支持团队,随时响应服务需求

语音智能体的填充语与优雅退出:消除对话空白

网页加载两秒,用户会等;语音对话沉默两秒,用户会以为断线了,然后开始「喂?喂?」。

这是语音场景特有的问题,解法是两个小配置:填充语在模型思考期间说一句「稍等,我看一下」,告诉用户系统还在干活;优雅退出让智能体把告别说完再离开频道,而不是话说一半突然消失。

两个功能加起来代码没几行,但对「像不像人」的影响,比换个更贵的模型明显得多。

配好之后你会注意到:模型思考的那一两秒不再是空白,而挂断前那句告别也能完整说完。

> 开始之前 浏览器端直接用《Next.js 搭建浏览器端语音智能体》里的客户端,先备好它。


一. 架构与准备工作

用户说完 ─→ 凤鸣 ASR ─→ DeepSeek 生成
                    └──── 等待期间播放中文填充语
DeepSeek 回复 ─→ MiniMax TTS ─→ 正常答案
停止会话 ─→ 等待告别播报 ─→ 智能体离开频道

二. 环境变量

AGORA_APP_ID=REPLACE_WITH_SHENGWANG_APP_ID
AGORA_APP_CERTIFICATE=REPLACE_WITH_PRIMARY_CERTIFICATE
DEEPSEEK_API_KEY=REPLACE_WITH_DEEPSEEK_KEY
MINIMAX_API_KEY=REPLACE_WITH_MINIMAX_KEY
FILLER_PHRASES=这个问题很好,我想一下。|好的,稍等我一下。|明白,我正在整理。
AGENT_FAREWELL=感谢你的交流,我们下次再见。
GRACEFUL_EXIT_TIMEOUT_SECONDS=8

三. 配置中文填充语与退出

import os

DEFAULT_PHRASES = [
    "这个问题很好,我想一下。",
    "好的,稍等我一下。",
    "明白,我正在整理。",
    "让我确认一下。",
]

def phrases_from_env() -> list[str]:
    raw = os.getenv("FILLER_PHRASES", "")
    phrases = [item.strip() for item in raw.split("|") if item.strip()]
    return phrases or DEFAULT_PHRASES

def filler_words() -> dict:
    return {
        "enable": True,
        "content": {
            "mode": "static",
            "static_config": {
                "phrases": phrases_from_env(),
                "selection_rule": "shuffle",
            },
        },
    }

def farewell_config() -> dict:
    timeout = int(os.getenv("GRACEFUL_EXIT_TIMEOUT_SECONDS", "8"))
    return {
        "graceful_enabled": True,
        "graceful_timeout_seconds": max(1, min(timeout, 30)),
    }

当前版本用的是静态填充语,通过 shuffle 避免每次都说同一句——同一句话听三遍,用户就会开始注意到这是个机器人。

填充语要短、要自然,而且绝不能伪造进度。「稍等一下」「我想想」都可以,但「已经查到了」「订单已确认」这种绝对不行——话说出口的时候你根本还没查,用户后面听到不一致的答案会直接失去信任。


四. 接入智能体

import os

from agora_agent import Area, AsyncAgora
from agora_agent.agentkit import Agent
from agora_agent.cn import DeepSeekLLM, FengmingSTT, MiniMaxTTS

client = AsyncAgora(
    area=Area.CN,
    app_id=os.environ["AGORA_APP_ID"],
    app_certificate=os.environ["AGORA_APP_CERTIFICATE"],
)
farewell = os.getenv("AGENT_FAREWELL", "感谢你的交流,我们下次再见。")
llm = DeepSeekLLM(
    api_key=os.environ["DEEPSEEK_API_KEY"],
    base_url="https://api.deepseek.com/chat/completions",
    model="deepseek-v4-flash",
    system_messages=[{"role": "system", "content": (
        "你是简洁自然的中文语音助手。回答控制在两句话内。"
        f"用户告别时请回复:{farewell}"
    )}],
    greeting_message="你好!今天有什么可以帮你?",
    failure_message="抱歉,服务暂时繁忙,请稍后再试。",
    max_history=20,
    max_tokens=256,
    params={"thinking": {"type": "disabled"}},
)
tts = MiniMaxTTS(
    key=os.environ["MINIMAX_API_KEY"], model="speech-01-turbo",
    voice_id="female-shaonv", sample_rate=16000, language_boost="Chinese",
)
agent = (Agent(
    client=client,
    filler_words=filler_words(),
    turn_detection={"language": "zh-CN"},
    advanced_features={"enable_rtm": True},
    parameters={
        "audio_scenario": "chorus",
        "data_channel": "rtm",
        "enable_metrics": True,
        "enable_error_message": True,
        "farewell_config": farewell_config(),
    },
).with_stt(FengmingSTT()).with_llm(llm).with_tts(tts))

正常用 session.start()session.stop() 即可。

客户端这边要配合一下。停止时别急着销毁浏览器的 RTC/RTM,要等后端 /stopAgent 返回之后再释放。抢在前面关掉,告别语就播不完——那优雅退出也就白配了。


五. 运行与验证

cd code/filler-exit
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env.local
uvicorn app:app --host 0.0.0.0 --port 8000

用 《Next.js 搭建浏览器端语音智能体》的客户端连上这个后端,问一个需要模型组织一下的问题,比如「用三点总结一次线上活动的复盘方法」。

预期表现:

  1. 模型首包到达前,可能会播一条中文填充语。
  2. 填充语不会和正式回答重复或叠在一起。
  3. 你说「再见」时,智能体会说告别语。
  4. 点结束后,服务端会等优雅退出走完再释放会话。

六. 故障排查

  • 没听到填充语:模型响应快的时候系统会判断不需要播,这不算失败。想验证触发逻辑,用一个故意加延迟的测试服务。
  • 每轮都播,听着烦:短问题场景下少用填充语,单条控制在一秒左右,并监控实际触发率。填充语的价值在于「偶尔出现」,一直出现就变成噪音了。
  • 告别语被截断:客户端别在后端 stop 返回前就关掉远端音轨;也可以适当调高 graceful timeout,但别超过业务能接受的等待时间。

七. 下一步

填充语和打断是同一件事的两面:一个填空白,一个管抢话。

《语音智能体打断实战:VAD、关键词与不可打断》

在声网,连接无限可能

想进一步了解「对话式 AI 与 实时互动」?欢迎注册,开启探索之旅。

本博客为技术交流与平台行业信息分享平台,内容仅供交流参考,文章内容不代表本公司立场和观点,亦不构成任何出版或销售行为。