一次从麦克风、模型到 MCP 工具调用的真实实操

我最近试了个东西:让 AI 当一回能听、能说、还能自己掷骰子的地下城主持人。

我不想再做一个输入文字、等待回复的聊天页面,而是想跑通一条完整的语音智能体链路:我直接对着麦克风说中文,AI 听懂后判断要不要调用游戏工具,再把执行结果用中文说回来。我搭出的组合是 Agora Conversational AI + Fengming STT + Qwen LLM + CosyVoice TTS + MCP,最终我跑通了从中文语音识别、大模型决策、MCP 工具调用到语音合成播放的完整链路。

一、我先定下这次实操的目标

1、我要的不是语音版聊天框

我给这个项目定了三个验收条件。第一,我说中文后,页面要显示正确的中文转写;第二,AI 要用中文语音回答,而不是只返回文字;第三,当我说“创建角色”或“攻击”时,它要调用真实的游戏工具,不能靠模型随口编造生命值和伤害。

2、最终要看到什么结果

页面连接成功后,顶部会显示 STT、LLM 和 TTS 三段处理链,状态面板显示 RTC connected。对话区同时记录我说的话和 AI 的回答。我还可以在 AI 播报时再次开口,用实际交互观察它是否能处理新的语音轮次。

二、我搭出的语音智能体怎么工作

1、一句话经过了哪些环节

浏览器负责采集麦克风、加入 RTC 频道和播放远端音频;FastAPI 后端负责生成 Token、启动和停止 Agent;MCP 服务负责执行游戏动作。一次中文对话会走过下面这条链路:

我的中文语音:  
-> Agora RTC
  -> Fengming STT
  -> Qwen LLM
  -> MCP 游戏工具
  -> CosyVoice TTS
  -> Agora RTC 播放

我觉得这里最关键的是职责分开了:Fengming 负责把声音变成文字,Qwen 负责理解意图,MCP 负责执行动作,CosyVoice 再把最终结果变回声音。某一层的工作不会全部堆到大模型上。

2、为什么它算一个 Agent

项目里有 6 个 MCP 工具:创建角色、查询角色、开始遭遇、攻击、施法和逃跑。比如我说“我要攻击”,Qwen 应该调用 attack;掷骰、伤害、敌人反击和战利品结算都在 game.py 中完成,不让模型临场编数字。

@mcp.tool()
def create_character(char_class: str) -> str:
    return _run(game.create_character, char_class)

@mcp.tool()
def attack() -> str:
    return _run(game.attack)

三、我怎样完成本地配置

1、准备凭证和公网地址

我先安装前后端依赖,再在 server/.env.local 中配置 Agora App ID、App Certificate、百炼 API Key 和 MCP 公网地址。密钥只保存在本地环境文件,没有写入前端代码。

MCP_ENDPOINT 需要是公网可访问地址,因为 MCP 工具由云端调用。我使用 ngrok 把后端的 /mcp 暴露出去,并确认该地址能够访问工具服务。

2、接入中文 STT、LLM 和 TTS

考虑到这次实操主要面向中文语音场景,我将 Agent 服务区域设置为中国区:语音识别使用 Fengming,大模型通过 OpenAI 兼容接口接入百炼 Qwen,语音合成使用 CosyVoice:

llm = AliyunLLM(
    api_key=self.dashscope_api_key,
    model="qwen-plus",
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions",
    mcp_servers=build_mcp_servers(self.mcp_endpoint),
)

stt = FengmingSTT()
tts = CosyVoiceTTS(
    api_key=self.dashscope_api_key,
    model="cosyvoice-v1",
    sample_rate=24000,
    voice="longxiaochun",
)

3、固定中文对话和轮次语言

我在系统提示词中明确要求始终使用简体中文回答,并要求涉及角色、战斗、生命值、金币时必须调用工具。语音识别语言则写在轮次检测配置里:

turn_detection={
    "language": "zh-CN",
    "config": {
        "start_of_speech": {"mode": "vad"},
        "end_of_speech": {"mode": "vad"},
    },
}

这里的 VAD 用于检测我什么时候开始说话、什么时候结束一轮。它也是实现自然轮次衔接和插话体验的重要配置。

四、我如何启动并确认服务

1、先启动后端和 MCP

我先启动 FastAPI 后端。终端实际输出如下:

StreamableHTTP session manager started
Application startup complete.
Uvicorn running on http://0.0.0.0:8007

看到 Application startup complete 后,我再启动 ngrok,让 Agora 云端能够访问 /mcp。这一阶段我还会访问一次 /get_config,确认 HTTP 状态为 200,返回数据中存在 Token 和随机频道名。

2、再启动前端

Next.js 前端启动成功时,终端显示:

Local: http://localhost:3110
Ready in 394ms

我在浏览器打开这个地址,点击 Start Conversation 并允许麦克风权限。

新会话建立后,页面右上角出现绿色连接状态指示,中文欢迎语也会显示在 Transcript 中。实测页面记录到的 TTS 首包时间约为 579 ms,这只是我这次本地运行看到的结果。

五、我用四句话做功能验收

1、验收角色和战斗工具

我准备按顺序说下面四句话:

“我选择战士。”

“开始一场战斗。”

“我要攻击。”

“我现在还有多少生命值?”

这四句分别对应角色创建、遭遇生成、战斗结算和状态查询。页面必须显示中文用户转写,AI 回复中还要出现具体职业、生命值或战斗结果,这样才能证明 MCP 工具确实参与了对话。

2、验收语音轮次和插话

普通对话通过后,我会在 AI 说到一半时再次开口。我的观察重点不是动画有没有变化,而是当前播报是否停止、我的新一句话是否进入 Transcript,以及 AI 是否根据新内容继续回答。

在测试中我在 AI 还未说完时直接插话:“停一下,告诉我现在还有多少生命值。”当前语音随即停止,Transcript 中出现了我的新指令,AI 随后转而回答角色状态。这次测试说明轮次检测和语音打断已经生效,交互不需要等上一段回复完整播放结束。

六、跑完以后,我的实际感受

实际跑完以后,我对 Agora Conversational AI 最直接的感受是,它把 RTC 实时传输、语音轮次、模型调用和语音播放组织在了一起。

依托全球 RTC 网络,我的本地实测延迟仅 579ms;精准中文 VAD 轮次检测实现自然插话打断;BYOK 架构自由搭配凤鸣、通义千问、CosyVoice 自研 MCP 工具,分层分担算力。作为 OpenAI Realtime 官方合作伙伴,它避开了 WebSocket 网络短板,能让开发者低成本搭建可实时交互、具备工具执行能力的语音智能体,落地效率与交互体验都很不错。

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐