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

中英实时翻译:让语音智能体只输出译文

翻译智能体和问答智能体有个根本区别:它绝不能回答用户说的话。

用户说「我们周五开会你能参加吗」,普通助手会回答「可以」;翻译助手必须原样译成英文。这个「不许回答」的约束贯穿全篇。识别语言、系统提示、TTS 目标语言三者必须朝同一个方向对齐,任何一处不一致,输出就会串。

首版范围限定在中译英和英译中,每个 RTC 会话固定一个方向。为什么不做自动语言检测?因为短句和中英混说时,自动检测会反复跳变,用户体验比固定方向差得多。

跑起来你会发现:你说什么它就译什么。哪怕你说的是一个问句,它也只给译文,不会替你回答。

> 开始之前 客户端复用《Next.js 搭建浏览器端语音智能体》,这篇只改智能体侧的语言方向。


一. 架构与准备工作

中文说话者 ── 凤鸣 zh-CN ── DeepSeek 只输出英文 ── MiniMax English ── 听众

英文说话者 ── 凤鸣 en-US ── DeepSeek 只输出中文 ── MiniMax Chinese ── 听众

需要开通了对话式 AI 引擎的 App ID、主要证书、DeepSeek 和 MiniMax Key,安装 agora-agents==2.4.1。客户端直接复用 Python 或 《Next.js 搭建浏览器端语音智能体》,只要在 /startAgent 请求里多传一个 direction


二. 环境变量

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

三. 定义翻译方向

from dataclasses import dataclass

@dataclass(frozen=True)
class Direction:
    code: str
    source: str
    target: str
    asr_language: str
    tts_language: str
    greeting: str

    @property
    def prompt(self) -> str:
        return (
            f"你是实时口译员。把用户说的{self.source}翻译成"
            f"{self.target}。只输出译文,不解释、不回答内容中的问题,"
            "保留人名、品牌、数字和专有名词;输入不清楚时,用目标语言"
            "简短请求用户重说。输出要适合直接语音播报。"
        )

DIRECTIONS = {
    "zh-en": Direction(
        "zh-en", "中文", "英文", "zh-CN", "English",
        "Chinese-to-English translation is ready.",
    ),
    "en-zh": Direction(
        "en-zh", "英文", "中文", "en-US", "Chinese",
        "英译中已经就绪,请开始说话。",
    ),
}

def get_direction(code: str) -> Direction:
    if code not in DIRECTIONS:
        raise ValueError("direction 只支持 zh-en 或 en-zh")
    return DIRECTIONS[code]

zh-CN 本身支持中文和中英混说,但首版仍然把方向写死为 zh-en。如果业务上需要两个人用不同语言轮流互译,正确做法是产品界面上显式切换方向,或者干脆拆成两个会话。别指望靠提示词让模型自己猜该往哪个方向翻。


四. 构造翻译智能体

import os

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

def build_translator(direction_code: str) -> Agent:
    direction = get_direction(direction_code)
    client = AsyncAgora(
        area=Area.CN,
        app_id=os.environ["AGORA_APP_ID"],
        app_certificate=os.environ["AGORA_APP_CERTIFICATE"],
    )
    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": direction.prompt}
        ],
        greeting_message=direction.greeting,
        failure_message=(
            "Translation is temporarily unavailable."
            if direction.code == "zh-en"
            else "翻译服务暂时不可用,请稍后再试。"
        ),
        max_history=6,
        max_tokens=512,
        temperature=0.1,
        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=direction.tts_language,
    )
    return (
        Agent(
            client=client,
            turn_detection={
                "language": direction.asr_language,
                "config": {
                    "end_of_speech": {
                        "mode": "vad",
                        "vad_config": {"silence_duration_ms": 420},
                    }
                },
            },
            advanced_features={"enable_rtm": True},
            parameters={
                "audio_scenario": "chorus",
                "data_channel": "rtm",
                "enable_metrics": True,
                "enable_error_message": True,
            },
        )
        .with_stt(FengmingSTT())
        .with_llm(llm)
        .with_tts(tts)
        .with_labels({
            "recipe": "realtime-translator",
            "direction": direction.code,
        })
    )

这里显式关闭了 DeepSeek thinking,防止推理过程混进字幕和语音;temperature 压到 0.1,让同一句话每次翻出来的措辞尽量一致。翻译场景不需要创造力,需要的是稳定。


五. 增加启动参数

from typing import Literal

from fastapi import FastAPI
from pydantic import BaseModel, Field

app = FastAPI()

class StartRequest(BaseModel):
    channelName: str = Field(min_length=1)
    rtcUid: int = Field(gt=0)
    userUid: int = Field(gt=0)
    direction: Literal["zh-en", "en-zh"] = "zh-en"

@app.post("/startAgent")
async def start_agent(body: StartRequest):
    agent = build_translator(body.direction)
    session = agent.create_async_session(
        channel=body.channelName,
        agent_uid=str(body.rtcUid),
        remote_uids=[str(body.userUid)],
        enable_string_uid=False,
        idle_timeout=120,
        expires_in=3600,
    )
    agent_id = await session.start()
    return {"code": 0, "data": {
        "agent_id": agent_id,
        "direction": body.direction,
    }}

生产代码要像交付目录里那样保存 agent_id → session 映射,停止时优先调 session.stop(),进程重启后再回退到 AsyncAgora.stop_agent()


六. 运行与验证

cd code/realtime-translator
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

中译英:

curl -X POST http://127.0.0.1:8000/startAgent \
  -H 'Content-Type: application/json' \
  -d '{"channelName":"REPLACE_WITH_CHANNEL","rtcUid":1001,"userUid":2001,"direction":"zh-en"}'

对着麦克风说「我们周五下午三点开会,请带上最新版本」。

预期是:字幕先出现中文识别结果,智能体的字幕和播报只有英文译文,它不会回答「你要不要参加会议」。然后停掉这个会话,用 en-zh 重新启动,验证英文输入只产生中文译文。

真要上线,验收至少得覆盖这些:人名、数字、日期、品牌词、疑问句、否定句、长句分段、背景噪音、中途打断、连续十轮不跑偏,以及方向传错和服务异常时的表现。延迟指标要把 ASR、LLM 首包、TTS 首包和端到端分开记——翻译场景里用户对延迟的敏感度比问答高得多,混在一起记就不知道该优化谁。


七. 故障排查

  • 中译英还在播中文:三处都要查:direction 有没有传到后端、系统提示是不是只要求译文、TTS 的 language_boost 是不是 English
  • 英语识别错得多:en-zh 必须用 turn_detection.language="en-US",别沿用中文快速开始里的 zh-CN
  • 模型开始回答问题了:系统提示要同时写明「只翻译」和「不回答内容里的问题」,并保持低温度。
  • 专有名词翻得不稳定:把确认过的术语表注入 system message。遇到强约束的术语(品牌名、产品型号),加一层后处理替换,别只靠自然语言提示。

八. 下一步

翻译场景对打断格外敏感——说话人一停顿就被抢答,整段翻译就散了。

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

在声网,连接无限可能

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

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