数据截至 (上游 commit 2d68a10f8c15)
模型抽象、流式、追踪与兄弟包
前四章讲完了引擎主线和挂件。这一章收尾三个「横切」能力——模型怎么解耦、流式怎么走、追踪怎么记——再俯瞰两条建立在引擎之上的扩展线(sandbox agent、realtime 语音)。
1. 模型抽象:provider 无关靠两个方法
1.1 思路
整个引擎只通过一个窄接口跟 LLM 打交道。换供应商(OpenAI / Vercel AI SDK / 自研),只要实现这接口,循环代码一行不改。
1.2 真实实现
Model 接口(model.ts:573)只有三个方法,两个是核心:
// model.ts:573 Model(真实源码,节选)
export interface Model {
getResponse(request: ModelRequest): Promise<ModelResponse>; // 非流式
getStreamedResponse(request: ModelRequest): AsyncIterable<StreamEvent>; // 流式
getRetryAdvice?(args: ModelRetryAdviceRequest): ...; // 可选:重试建议
}
- 输入
ModelRequest(model.ts:439):系统指令、input items、工具/交接的序列化形式、modelSettings、tracing 开关。 - 输出
ModelResponse(model.ts:542):usage、output(消息/工具调用等)、responseId、providerData。 - 查找模型 用
ModelProvider.getModel(name)(model.ts:600)——把字符串模型名解析成Model实例。
主循环里调用点是 getResponseWithRetry(preparedCall.model, ...)(run.ts:1707-1716),preparedCall.model 就是一个 Model。引擎从不直接 import OpenAI。
1.3 默认实现在哪
默认 provider 是惰性加载的 LazyDefaultModelProvider(run.ts:481),它转调 getDefaultModelProvider()(来自 providers.ts)——具体 OpenAI 实现住在 @openai/agents-openai(openaiResponsesModel.ts、openaiChatCompletionsModel.ts)。默认模型名见 defaultModel.ts(getDefaultModel,当前默认是一个 gpt-5 系模型,见 agent.ts:328-330 注释)。
1.4 巧妙之处:gpt-5 的 modelSettings 隔离
构造 Agent 时有段防呆逻辑(agent.ts:565-581):如果默认是 gpt-5、但你显式指定了非 gpt-5 模型且没自定义 modelSettings,它会把 modelSettings 清空——因为 gpt-5 默认的 reasoning 设置喂给别的模型会报错。这是「best-effort 让换模型不炸」的细节。
2. 流式
2.1 思路
流式不是「把非流式包一层」,而是一条几乎平行的循环 Runner.#runStreamLoop(run.ts:1966)。它和非流式共用准备/决议逻辑,区别在:边收模型事件边 emit 给调用方。
2.2 真实路径
- 调
getStreamedResponseWithRetry(...)(run.ts:2664),for await逐个事件。 - 每个原始事件包成
RunRawModelStreamEvent推进结果流(run.ts:2731);response_done事件用来组装finalResponse(run.ts:2686-2722)。 - 收完后照样
processModelResponseAsync+resolveTurnAfterModelResponse(run.ts:2784、:1587)——和非流式同一套决议。 - 取消对账:用户提前退出消费 stream,或 abort 时,要跟服务端「对账」流到一半的函数调用,靠
reconcileStreamAbortIfNeeded(run.ts:2571)补一次请求把状态拉齐。这是流式特有的复杂度。
用户侧看到的事件类型在 events.ts:RunRawModelStreamEvent、RunItemStreamEvent、RunAgentUpdatedStreamEvent(index.ts:40-45 导出)。
3. 追踪 Tracing
3.1 思路
每次 run 包在一个 trace 里,run 内的每个有意义动作(一次 agent 执行、一次模型调用、一次工具调用、一次交接)是一个 span。span 形成树,导出后能在 UI 里看「这次 run 到底干了啥、哪步慢」。
3.2 真实路径
- run 入口用
withTrace(...)把整次执行包进 trace context:默认入口run.ts:969,恢复带 trace 的RunState时run.ts:951,trace 被禁用时run.ts:917;拿到外部 invocation trace context 时走withTraceContext(run.ts:966)。 - span 创建用
withFunctionSpan/withHandoffSpan等(tracing/createSpans.ts,在toolExecution.ts:70被引)。 - 默认就开:
index.ts:338在模块加载时addTraceProcessor(defaultProcessor())——defaultProcessor批量把 trace/span 导出到后端。要换行为用addTraceProcessor/setTraceProcessors(tracing/index.ts:66、:71),要关用setTracingDisabled(tracing/index.ts:84)。 - 敏感数据开关:
RunConfig.traceIncludeSensitiveData(run.ts:330-334)控制工具入参/模型生成要不要进 trace。
OpenAI 的 trace 导出实现在 @openai/agents-openai 的 openaiTracingExporter.ts。