Workshop

实战工坊:用 GPT-Live-1 搭一个全双工语音 Agent,把重推理委托给后端模型

5 min read ·

上一代语音 Agent 的写法几乎是固定的:先跑一遍语音识别,把文本丢给大模型,等模型吐完再跑一遍语音合成。三段流水线串起来,架构清晰,Demo 也能跑通,但只要进了真实通话场景就会暴露三个老问题——每段交接都加一次延迟、用户一打断就得重置轮次、以及「听着像在思考」的停顿无法被自然表达。

OpenAI 在 2026 年 9 月把 GPT-Live-1 开进了 API,给出的答案是把这个级联拆掉:听和说由同一个模型联合处理,重推理则被显式外包出去。这篇工坊就按它的实际接口形态,从零搭一个能接真实通话的全双工语音 Agent。

一、架构:为什么要把推理「委托」出去

GPT-Live-1 的设计有一个很硬的前提:它本身不负责深度推理。它只做语音交互层——听、说、判断轮次边界、处理打断和反向信道。真正的工具调用和长链思考,交给一个后端文本模型,官方示例里配的是 GPT-6 Astra(指定 medium 推理强度),轻任务也可以挂 Terra 之类的低强度模型。

这样切分的好处很直接。语音延迟要求是几百毫秒量级,而深度推理动辄几秒到几十秒,两者的时间尺度差了两个数量级。如果强行让一个模型同时满足两边的约束,结果只能是语音变卡或者推理变浅。拆开之后,每一边都能按自己的节拍工作。

代价是:你必须自己维护前后端的状态一致性。这也是后面绝大多数坑的来源。

层面职责时间尺度
前端语音层(GPT-Live-1)音频理解与生成、轮次判断、打断、反向信道百毫秒级
后端推理层(你的 harness)工具调用、检索、长链推理、代码执行秒到数十秒
你的编排代码用 delegation_id 关联两者、处理过期结果——

二、第一步:建立会话并选好后端

语音层的另一个实用特性是原生支持轮次检测但并非基于轮次。也就是说模型本身是流式的,但你可以要求它在显式轮次边界上工作。对于迁移传统的 IVR 状态机,这个开关很重要,它让你不必一次性重写全部对话逻辑。

语音库这次也扩了,Quartz、Ripple、Vesper、Willow、Stone、Gleam、Meridian、Bossa、Tempo、Beacon、Delta、Cinder 覆盖不同口音、方言与语种。选语音时不要只看音色,还要实测它在你的业务词汇上的发音稳定性。

// 会话配置:前端语音 + 后端推理两段式
const session = await live.createSession({
  voice: "Meridian",
  systemPrompt: `
    你是一名餐厅预订助理。语气自然、节奏偏慢。
    遇到需要查询库存、改期、计算费用的请求时,
    调用 delegate 而不是自己猜答案。
    如果用户打断你,先承认打断,再回答新问题。
  `,
  backend: {
    model: "gpt-6-astra",
    reasoningEffort: "medium",   // 深度推理任务:medium 起步
    tools: ["check_availability", "create_booking", "modify_booking"]
  },
  turnDetection: "native",       // 可切换为显式轮次,便于迁移状态机
  keywordBiasing: ["包间", "靠窗", "素食", "无障碍通道"]
});

keywordBiasing 值得单独说一句。语音模型对字母数字和专有名词的识别天然不稳,把门店名、SKU、房型名预置进去,比事后做文本纠错便宜得多。

三、第二步:事件协议与 delegation

前后端之间靠 delegation_id 串起来。前端发起一次委托时拿到这个 ID,后端跑完之后通过事件把结果贴回会话。官方示例里用的是 session.commentary.append——「往会话里追加一段旁白」,也就是让后端产出的文本以中间结果的形式进入对话流,而不是抢过话筒。

async function answer(live, delegationId, context) {
  // 后端长跑:检索 + 推理 + 工具调用
  const result = await backendThread.run({
    input: context.transcriptTail,
    tools: context.allowedTools
  });

  // 结果回灌到前端会话,仍然挂在同一条 delegation 上
  live.send({
    type: "session.commentary.append",
    delegation_id: delegationId,
    content: finalResponse(result)
  });
}

// 前端触发委托,立刻拿到 ID,不等后端
live.on("delegation.requested", (evt) => {
  const id = evt.delegation_id;
  answer(live, id, buildContext(evt)).catch((err) => {
    live.send({
      type: "session.commentary.append",
      delegation_id: id,
      content: "这个我需要再确认一下,稍等半秒。"
    });
  });
});

注意上面那段 catch。委托失败必须有一条语音兜底,否则用户体验到的就是一段莫名其妙的沉默——比说一句「我需要确认一下」糟糕得多。

四、第三步:处理打断与过期结果

这是全双工真正区别于半双工的地方,也是我认为最容易写错的地方。

用户中途插话时,前端会立刻转向。但此时后端很可能还在跑——它不知道话题已经变了。如果你按「后端结果回来就播报」的朴素写法实现,就会出现经典事故:用户已经问到第三件事,Agent 突然念出第一件事的答案。

正确做法是给每个委托结果打上一个语义版本号,回灌前做一次相关性判断:

const inflight = new Map(); // delegation_id -> { topicSeq, inserted }

live.on("user.turn.detected", (evt) => {
  topicSeq += 1;                       // 话题序号前进
  for (const [id, meta] of inflight) {
    if (meta.topicSeq !== topicSeq) {
      meta.inserted = false;           // 标记为可能过期
    }
  }
});

function onBackendResult(id, text) {
  const meta = inflight.get(id);
  if (meta.topicSeq !== topicSeq && !stillRelevant(text)) {
    return;                            // 丢弃,或降级写入纪要
  }
  live.send({ type: "session.commentary.append", delegation_id: id, content: text });
}

另外两个实测有效的小技巧:

  • 反向信道要显式打开。「嗯」「明白」「好的」这类短反馈以及笑声,应当被允许在模型说话期间发生而不打断它。关掉之后对话会变得很机械。
  • 背景噪声与静音要做区分。官方明确改进了这块行为,模型不会因为环境音就中断叙述,也不会把每一步操作都大声念出来。如果你的提示词里写了「每步都汇报」,反而会把这个优化抵消掉。

五、基准与成本:值不值得换

官方给的几个数字可以作为选型参考:

指标结果
Full Duplex Bench较 GPT-Realtime-2.1 提升约 30 个百分点
Tau3 端到端语音智能搭配 GPT-6 Astra(medium)排名第一
前端语音层定价每分钟 0.05 美元
客户实测(Speak 语言陪练)思考停顿期间的中断率下降近 80%
客户实测(某工程团队)相较级联实现移除约 2.3 万行代码

成本这块要算清楚结构。语音层按会话分钟计费,只要会话开着就在计费,不论谁说。客服场景里用户思考、犹豫、找资料的时间全都算钱。所以会话超时回收策略必须做,否则一个挂着不动的通话会持续烧预算。

每小时语音成本 = 60 × 0.05 = 3.00 美元(每并发会话)
总成本 = 语音时长费 + 后端推理费 + 电话接入费

按这个结构,10 路并发的客服坐席,光语音层一小时就是 30 美元。这解释了为什么官方示例里的很多设计都在强调「早结束会话」。

六、上线前的检查清单

  • 前端语音层,后端推理层,各自超时时间是否分开配置?
  • 委托失败是否有语音兜底,而不是静默?
  • 打断后,过期结果是否有丢弃逻辑?
  • 反向信道是否开启,模型说话时用户能否自然应声?
  • 会话回收策略是否存在,闲置多久强制结束?
  • keywordBiasing 是否覆盖了业务专有名词?
  • 通话录音与转写的合规声明是否就位?

结语

GPT-Live-1 的价值不在于「语音更自然」这种体感描述,而在于它把一个原本被级联架构强行耦合的问题拆成了两层:一层负责毫秒级的交互,一层负责秒级的思考。这个切分是对的,因为这两件事的工程约束本来就不同。

但它同时也把状态一致性的责任交给了你。级联架构里那些被流水线掩盖的边界情况——打断、迟到结果、话题漂移——在全双工里会全部浮到你的编排代码上。所以真正决定这套方案能不能上生产的,不是语音质量,而是你有没有认真写完那段处理过期结果的逻辑。

Frequently asked questions

GPT-Live-1 和级联的 STT 加 LLM 加 TTS 方案本质区别在哪?
级联方案在每一段交接处都会累积延迟,并且一旦用户打断,整条流水线必须重置轮次,之前算到一半的上下文容易丢失。GPT-Live-1 用单个模型对输入音频和输出音频联合推理,交接点消失,打断、确认、笑声这类反向信道可以在不重启轮次的情况下被实时吸收。
delegation 机制到底解决了什么问题?
语音层不该自己扛重推理。如果让语音模型同时做语音和深度思考,延迟会不可控。delegation 把一条会话拆成前端语音层和后端推理层,用 delegation_id 关联,前端始终能说话、能被打断,后端在后台继续跑工具调用和长思考,跑完再通过事件把结果贴回会话。
前端语音层怎么计费?
前端语音层按每分钟 0.05 美元计费,按会话开的时长算,不区分用户说话还是模型说话。后端模型按各自的价格单独结算,所以整体成本等于语音分钟费加上后端 Token 费,长会话里语音时长费往往会变成主要成本项。
用户打断时后端还在跑推理怎么办?
不要让前端等待后端。前端应该立刻用一句自然的话承认打断并调整方向,同时保留 delegation_id 不动,让后端继续跑。后端结果回来后先判断是否仍然相关,如果用户已经换话题就丢弃或降级为附录,绝不要硬塞进当前对话轮次里。
能直接接到电话网吗?
可以。官方明确支持把全双工语音 Agent 部署到真实电话呼叫场景,例如餐厅预订和客服接听。工程上你仍然需要自己处理号码接入、会话录制合规、以及通话中断后的状态恢复,这些不在语音层职责范围内。
// next.txt ›

Some outbound links in this post are affiliate links — see disclosure.