构建语音智能体
一些传输层(如默认的 OpenAIRealtimeWebRTC)会自动为你处理音频输入和输出。对于其他传输机制(如 OpenAIRealtimeWebSocket),你需要自行处理会话音频:
import { RealtimeAgent, RealtimeSession, TransportLayerAudio,} from '@openai/agents/realtime';
const agent = new RealtimeAgent({ name: 'My agent' });const session = new RealtimeSession(agent);const newlyRecordedAudio = new ArrayBuffer(0);
session.on('audio', (event: TransportLayerAudio) => { // play your audio});
// send new audio to the agentsession.sendAudio(newlyRecordedAudio);你可以在构造时向 RealtimeSession 传入额外选项,或在调用 connect(...) 时进行配置。
import { RealtimeAgent, RealtimeSession } from '@openai/agents/realtime';
const agent = new RealtimeAgent({ name: 'Greeter', instructions: 'Greet the user with cheer and answer questions.',});
const session = new RealtimeSession(agent, { model: 'gpt-realtime', config: { inputAudioFormat: 'pcm16', outputAudioFormat: 'pcm16', inputAudioTranscription: { model: 'gpt-4o-mini-transcribe', }, },});这些传输层允许你传入任何与 session 匹配的参数。
对于新增且在 RealtimeSessionConfig 中没有对应参数的配置,你可以使用 providerData。传入 providerData 的任何内容都会直接作为 session 对象的一部分传递。
构造时可设置的其他 RealtimeSession 选项:
| Option | Type | Purpose |
|---|---|---|
context | TContext | 合并到会话上下文的额外本地上下文。 |
historyStoreAudio | boolean | 是否在本地历史快照中存储音频数据(默认关闭)。 |
outputGuardrails | RealtimeOutputGuardrail[] | 会话的输出护栏(参见 护栏)。 |
outputGuardrailSettings | RealtimeOutputGuardrailSettings | 护栏检查的频率和行为。 |
tracingDisabled | boolean | 为会话禁用追踪。 |
groupId | string | 跨会话或后端运行对追踪进行分组。 |
traceMetadata | Record<string, any> | 附加到会话追踪的自定义元数据。 |
workflowName | string | 追踪工作流的易读名称。 |
automaticallyTriggerResponseForMcpToolCalls | boolean | MCP 工具调用完成后自动触发模型响应(默认:true)。 |
toolErrorFormatter | ToolErrorFormatter | 自定义返回给模型的工具审批拒绝消息。 |
与常规智能体类似,你可以使用交接将你的智能体拆分为多个智能体,并在它们之间编排,以提升智能体性能并更好地限定问题范围。
import { RealtimeAgent } from '@openai/agents/realtime';
const mathTutorAgent = new RealtimeAgent({ name: 'Math Tutor', handoffDescription: 'Specialist agent for math questions', instructions: 'You provide help with math problems. Explain your reasoning at each step and include examples',});
const agent = new RealtimeAgent({ name: 'Greeter', instructions: 'Greet the user with cheer and answer questions.', handoffs: [mathTutorAgent],});与常规智能体不同,交接在实时智能体中行为略有不同。执行交接时,正在进行的会话会使用新的智能体配置进行更新。因此,智能体会自动访问当前的对话历史,且当前不会应用输入过滤器。
此外,这意味着在交接中不能更改 voice 或 model。你也只能连接到其他实时智能体。如果你需要使用不同的模型,例如像 gpt-5-mini 这样的推理模型,你可以使用通过工具的委托。
与常规智能体一样,实时智能体可以调用工具来执行操作。Realtime 支持函数工具(本地执行)和远程 MCP 服务器工具(由 Realtime API 远程执行)。你可以使用与常规智能体相同的 tool() 帮助函数来定义函数工具。
import { tool, RealtimeAgent } from '@openai/agents/realtime';import { z } from 'zod';
const getWeather = tool({ name: 'get_weather', description: 'Return the weather for a city.', parameters: z.object({ city: z.string() }), async execute({ city }) { return `The weather in ${city} is sunny.`; },});
const weatherAgent = new RealtimeAgent({ name: 'Weather assistant', instructions: 'Answer weather questions.', tools: [getWeather],});函数工具在与你的 RealtimeSession 相同的环境中运行。这意味着如果你的会话在浏览器中运行,工具也在浏览器中执行。如果需要执行敏感操作,请在工具中向你的后端发起 HTTP 请求。
远程 MCP 服务器工具可以通过 hostedMcpTool 配置,并在远程执行。当 MCP 工具可用性发生变化时,会话会触发 mcp_tools_changed 事件。若要防止会话在 MCP 工具调用完成后自动触发模型响应,请设置 automaticallyTriggerResponseForMcpToolCalls: false。
在工具执行期间,智能体将无法处理来自用户的新请求。改善体验的一种方式是让你的智能体在即将执行工具时进行提示,或使用特定语句为工具执行争取时间。
访问对话历史
Section titled “访问对话历史”除了访问智能体调用某个工具时传入的参数外,你还可以访问由 Realtime 会话跟踪的当前对话历史快照。如果你需要基于当前对话状态执行更复杂的操作,或计划使用用于委托的工具,这将很有用。
import { tool, RealtimeContextData, RealtimeItem,} from '@openai/agents/realtime';import { z } from 'zod';
const parameters = z.object({ request: z.string(),});
const refundTool = tool<typeof parameters, RealtimeContextData>({ name: 'Refund Expert', description: 'Evaluate a refund', parameters, execute: async ({ request }, details) => { // The history might not be available const history: RealtimeItem[] = details?.context?.history ?? []; // making your call to process the refund request },});工具执行前审批
Section titled “工具执行前审批”如果使用 needsApproval: true 定义工具,智能体会在执行该工具前触发 tool_approval_requested 事件。
通过监听该事件,你可以向用户展示 UI 以批准或拒绝该工具调用。
import { session } from './agent';
session.on('tool_approval_requested', (_context, _agent, request) => { // show a UI to the user to approve or reject the tool call // you can use the `session.approve(...)` or `session.reject(...)` methods to approve or reject the tool call
session.approve(request.approvalItem); // or session.reject(request.rawItem);});护栏提供一种方式,用于监控智能体的发言是否违反一组规则,并立即切断响应。这些护栏检查将基于智能体响应的转写进行,因此需要启用模型的文本输出(默认已启用)。
你提供的护栏会在模型响应返回时异步运行,从而允许你基于预定义的分类触发器(例如“提及某个特定禁用词”)来切断响应。
当护栏被触发时,会话会发出 guardrail_tripped 事件。该事件还提供一个包含触发护栏的 itemId 的 details 对象。
import { RealtimeOutputGuardrail, RealtimeAgent, RealtimeSession } from '@openai/agents/realtime';
const agent = new RealtimeAgent({ name: 'Greeter', instructions: 'Greet the user with cheer and answer questions.',});
const guardrails: RealtimeOutputGuardrail[] = [ { name: 'No mention of Dom', async execute({ agentOutput }) { const domInOutput = agentOutput.includes('Dom'); return { tripwireTriggered: domInOutput, outputInfo: { domInOutput }, }; }, },];
const guardedSession = new RealtimeSession(agent, { outputGuardrails: guardrails,});默认情况下,护栏会每 100 个字符或在响应文本结束时运行一次。由于朗读文本通常更耗时,在大多数情况下,护栏应能在用户听到之前捕获违规内容。
如果你想修改此行为,可以向会话传入 outputGuardrailSettings 对象。
import { RealtimeAgent, RealtimeSession } from '@openai/agents/realtime';
const agent = new RealtimeAgent({ name: 'Greeter', instructions: 'Greet the user with cheer and answer questions.',});
const guardedSession = new RealtimeSession(agent, { outputGuardrails: [ /*...*/ ], outputGuardrailSettings: { debounceTextLength: 500, // run guardrail every 500 characters or set it to -1 to run it only at the end },});轮次检测 / 语音活动检测
Section titled “轮次检测 / 语音活动检测”Realtime 会话会使用 Realtime API 的内置语音活动检测模式 自动检测用户何时说话并触发新的一轮对话。
你可以通过向会话传入 turnDetection 对象来更改语音活动检测模式。
import { RealtimeSession } from '@openai/agents/realtime';import { agent } from './agent';
const session = new RealtimeSession(agent, { model: 'gpt-realtime', config: { turnDetection: { type: 'semantic_vad', eagerness: 'medium', createResponse: true, interruptResponse: true, }, },});修改轮次检测设置有助于校准不必要的打断并处理静音。查看Realtime API 文档,了解不同设置的更多细节
使用内置语音活动检测时,用户打断说话会自动触发智能体根据所说内容检测并更新其上下文。还会触发 audio_interrupted 事件。这可用于立即停止所有音频播放(仅适用于 WebSocket 连接)。
import { session } from './agent';
session.on('audio_interrupted', () => { // handle local playback interruption});如果你想执行手动打断,例如在你的 UI 中提供“停止”按钮,你可以手动调用 interrupt():
import { session } from './agent';
session.interrupt();// this will still trigger the `audio_interrupted` event for you// to cut off the audio playback when using WebSockets无论哪种方式,Realtime 会话都会同时处理对智能体生成的打断,截断其对已对用户所说内容的认知,并更新历史。
如果你使用 WebRTC 连接到智能体,它还会清除音频输出。如果你使用 WebSocket,你需要自行处理,停止播放已排队等待播放的音频。
如果你想向智能体发送文本输入,你可以在 RealtimeSession 上使用 sendMessage 方法。
这在你希望让用户以双重模态与智能体交互,或为对话提供额外上下文时很有用。
import { RealtimeSession, RealtimeAgent } from '@openai/agents/realtime';
const agent = new RealtimeAgent({ name: 'Assistant',});
const session = new RealtimeSession(agent, { model: 'gpt-realtime',});
session.sendMessage('Hello, how are you?');对话历史管理
Section titled “对话历史管理”RealtimeSession 会在 history 属性中自动管理对话历史:
你可以使用它向客户渲染历史或对其执行附加操作。由于该历史会在对话过程中不断变化,你可以监听 history_updated 事件。
如果你想修改历史,例如完全移除一条消息或更新其转写,可以使用 updateHistory 方法。
import { RealtimeSession, RealtimeAgent } from '@openai/agents/realtime';
const agent = new RealtimeAgent({ name: 'Assistant',});
const session = new RealtimeSession(agent, { model: 'gpt-realtime',});
await session.connect({ apiKey: '<client-api-key>' });
// listening to the history_updated eventsession.on('history_updated', (history) => { // returns the full history of the session console.log(history);});
// Option 1: explicit settingsession.updateHistory([ /* specific history */]);
// Option 2: override based on current state like removing all agent messagessession.updateHistory((currentHistory) => { return currentHistory.filter( (item) => !(item.type === 'message' && item.role === 'assistant'), );});- 当前无法在事后更新/更改函数工具调用
- 历史中的文本输出需要启用转写和文本模态
- 由于打断而被截断的响应没有转写
通过工具的委托
Section titled “通过工具的委托”
通过将对话历史与工具调用相结合,你可以将对话委托给另一个后端智能体以执行更复杂的操作,然后将结果返回给用户。
import { RealtimeAgent, RealtimeContextData, tool,} from '@openai/agents/realtime';import { handleRefundRequest } from './serverAgent';import z from 'zod';
const refundSupervisorParameters = z.object({ request: z.string(),});
const refundSupervisor = tool< typeof refundSupervisorParameters, RealtimeContextData>({ name: 'escalateToRefundSupervisor', description: 'Escalate a refund request to the refund supervisor', parameters: refundSupervisorParameters, execute: async ({ request }, details) => { // This will execute on the server return handleRefundRequest(request, details?.context?.history ?? []); },});
const agent = new RealtimeAgent({ name: 'Customer Support', instructions: 'You are a customer support agent. If you receive any requests for refunds, you need to delegate to your supervisor.', tools: [refundSupervisor],});下面的代码将在服务器上执行。本示例通过 Next.js 的 server actions 实现。
// This runs on the serverimport 'server-only';
import { Agent, run } from '@openai/agents';import type { RealtimeItem } from '@openai/agents/realtime';import z from 'zod';
const agent = new Agent({ name: 'Refund Expert', instructions: 'You are a refund expert. You are given a request to process a refund and you need to determine if the request is valid.', model: 'gpt-5-mini', outputType: z.object({ reasong: z.string(), refundApproved: z.boolean(), }),});
export async function handleRefundRequest( request: string, history: RealtimeItem[],) { const input = `The user has requested a refund.
The request is: ${request}
Current conversation history:${JSON.stringify(history, null, 2)}`.trim();
const result = await run(agent, input);
return JSON.stringify(result.finalOutput, null, 2);}