运行智能体
智能体本身不会执行任何操作——您需要使用 Runner 类或 run() 实用函数来运行它们。
当您希望执行轮次、流式传输事件或管理对话状态时,请在阅读智能体后阅读本页。如果您仍在考虑如何定义智能体,请先从智能体开始。
import { Agent, run } from '@openai/agents';
const agent = new Agent({ name: 'Assistant', instructions: 'You are a helpful assistant',});
const result = await run( agent, 'Write a haiku about recursion in programming.',);console.log(result.finalOutput);
// Code within the code,// Functions calling themselves,// Infinite loop's dance.如果不需要自定义运行器,也可以使用 run() 实用函数,它会运行一个单例的默认 Runner 实例。
您也可以创建自己的运行器实例:
import { Agent, Runner } from '@openai/agents';
const agent = new Agent({ name: 'Assistant', instructions: 'You are a helpful assistant',});
// You can pass custom configuration to the runnerconst runner = new Runner();
const result = await runner.run( agent, 'Write a haiku about recursion in programming.',);console.log(result.finalOutput);
// Code within the code,// Functions calling themselves,// Infinite loop's dance.运行智能体后,您将收到一个执行结果对象,其中包含最终输出和完整的运行历史记录。
Runner 生命周期与配置
Section titled “Runner 生命周期与配置”使用 Runner 中的 run 方法时,需要传入起始智能体和输入。输入可以是字符串(视为用户消息),也可以是输入项列表,即 OpenAI Responses API 中的项目。
然后,运行器会执行以下循环:
- 使用当前输入调用当前智能体的模型。
- 检查 LLM 响应。
- 最终输出 → 返回。
- 交接 → 切换到新智能体,保留累积的对话历史记录,然后返回第 1 步。
- 工具调用 → 执行工具,将结果追加到对话中,然后返回第 1 步。
- 达到
maxTurns后抛出MaxTurnsExceededError,除非maxTurns为null。
Runner 生命周期
Section titled “Runner 生命周期”在应用启动时创建一个 Runner,并在不同请求之间复用它。该实例存储模型提供商和追踪选项等全局配置。只有在需要完全不同的配置时,才应创建另一个 Runner。对于简单脚本,也可以调用内部使用默认运行器的 run()。
run() 方法的输入包括一个用于开始运行的初始智能体、运行输入和一组选项。
输入可以是字符串(视为用户消息)、输入项列表;如果您正在构建人机协作智能体,也可以是 RunState 对象。
其他选项包括:
| 选项 | 默认值 | 说明 |
|---|---|---|
stream | false | 如果为 true,调用将返回 StreamedRunResult,并在模型事件到达时发出这些事件。 |
context | – | 转发给每个工具、护栏和交接的上下文对象。更多信息请参阅上下文管理。 |
maxTurns | 10 | 安全限制——达到此限制时抛出 MaxTurnsExceededError。传入 null 可禁用此限制。 |
signal | – | 用于取消操作的 AbortSignal。 |
session | – | 会话持久化实现。请参阅会话。 |
sessionInputCallback | – | 用于合并会话历史记录和新输入的自定义逻辑;在调用模型之前运行。请参阅会话。 |
callModelInputFilter | – | 在调用模型之前编辑模型输入(输入项和可选 instructions)的钩子。请参阅模型调用输入过滤器。 |
toolErrorFormatter | – | 用于自定义返回给模型的工具错误消息的钩子。请参阅工具错误格式化器。 |
reasoningItemIdPolicy | – | 控制将先前的运行项重新转换为模型输入时,是保留还是省略推理项的 id。请参阅推理项 ID 策略。 |
tracing | – | 针对单次运行的追踪配置覆盖项。设置 includeTaskAndTurnSpans: false 可省略默认的任务/轮次 span 层级结构。 |
sandbox | – | 用于 SandboxAgent 运行的沙盒客户端、实时会话、会话状态、快照、清单覆盖项或并发限制。请参阅概念。 |
toolExecution | – | 本地工具调用的 SDK 端执行设置。使用 toolExecution.maxFunctionToolConcurrency 限制同时运行的函数工具数量,并使用 toolExecution.preApprovalInputGuardrails 在待处理的批准请求之前运行函数工具输入护栏。 |
toolNotFoundBehavior | 'raise_error' | 控制如何处理模型发出的未解析函数工具调用。使用 'return_error_to_model' 可返回模型可见的工具错误并继续运行。 |
errorHandlers | – | 支持的运行时错误处理器。请参阅错误处理器。 |
conversationId | – | 复用服务器端对话(仅限 OpenAI Responses API 和 Conversations API)。 |
previousResponseId | – | 从上一次 Responses API 调用继续,而不创建对话(仅限 OpenAI Responses API)。 |
流式传输让您可以在 LLM 运行时额外接收流式事件。流开始后,StreamedRunResult 将包含有关此次运行的完整信息,包括生成的所有新输出。您可以使用 for await 循环遍历流式事件。更多信息请参阅流式传输。
如果您要创建自己的 Runner 实例,可以传入 RunConfig 对象来配置运行器。
| 字段 | 类型 | 用途 |
|---|---|---|
model | string | Model | 强制此次运行中的所有智能体使用特定模型。 |
modelProvider | ModelProvider | 解析模型名称——默认为 OpenAI 提供商。 |
modelSettings | ModelSettings | 覆盖每个智能体设置的全局调优参数。有关详细信息(包括可选启用的重试配置),请参阅模型。 |
handoffInputFilter | HandoffInputFilter | 执行交接时修改输入项(前提是交接本身尚未定义输入过滤器)。 |
inputGuardrails | InputGuardrail[] | 应用于初始用户输入的护栏。 |
outputGuardrails | OutputGuardrail[] | 应用于最终输出的护栏。 |
tracingDisabled | boolean | 完全禁用 OpenAI 追踪。 |
traceIncludeSensitiveData | boolean | 从追踪中排除 LLM/工具的输入和输出,同时仍发出 span。 |
workflowName | string | 显示在 Traces 控制面板中,用于对相关运行进行分组。 |
traceId / groupId | string | 手动指定追踪或组 ID,而不是让 SDK 自动生成。 |
traceMetadata | Record<string, string> | 附加到每个 span 的任意元数据。 |
tracing | TracingConfig | 运行器级别的默认追踪配置,包括导出 API 密钥和 includeTaskAndTurnSpans。单次运行选项可以覆盖这些值。 |
sessionInputCallback | SessionInputCallback | 此运行器上所有运行的默认历史记录合并策略。 |
callModelInputFilter | CallModelInputFilter | 在每次模型调用之前编辑模型输入的全局钩子。 |
toolErrorFormatter | ToolErrorFormatter | 用于自定义返回给模型的工具错误消息的全局钩子。 |
reasoningItemIdPolicy | ReasoningItemIdPolicy | 将生成的项目重新用于后续模型调用时,保留或省略推理项 id 的默认策略。 |
sandbox | SandboxRunConfig | SandboxAgent 运行的默认沙盒运行时配置。 |
toolExecution | ToolExecutionConfig | 本地工具调用的默认 SDK 端执行设置。maxFunctionToolConcurrency 限制每个轮次中本地函数工具的并发数;未设置或设为 null 时,将启动该轮次中发出的所有函数工具调用。preApprovalInputGuardrails 用于选择在待处理的批准请求之前运行函数工具输入护栏。 |
toolNotFoundBehavior | ToolNotFoundBehavior | 未解析函数工具调用的默认行为。'raise_error' 会抛出 ModelBehaviorError;'return_error_to_model' 会返回模型可见的工具错误,并允许继续运行。 |
toolExecution.maxFunctionToolConcurrency 必须是大于或等于 1 的整数。此设置仅限制本地函数工具的 SDK 端执行,不会更改提供商端的 modelSettings.parallelToolCalls。
toolExecution.preApprovalInputGuardrails 默认处于禁用状态。设置为 true 后,需要批准的本地函数工具会在 SDK 记录待处理的批准中断之前运行其输入护栏。如果护栏返回 rejectContent,SDK 会将该拒绝消息作为工具输出发回,而不是请求批准。如果护栏允许调用,仍会发起批准请求;批准处理完成后,在工具执行前,相同的输入护栏会再次立即运行。
状态与对话管理
Section titled “状态与对话管理”记忆策略选择
Section titled “记忆策略选择”将状态带入下一轮通常有四种方式:
| 策略 | 状态存储位置 | 最适合 | 下一轮传入的内容 |
|---|---|---|---|
result.history | 应用内存 | 小型聊天循环、完全手动控制、任意提供商 | result.history |
session | 您的存储和 SDK | 持久化聊天状态、可恢复的运行、自定义存储 | 相同的 session 实例(或由存储支持的实例) |
conversationId | OpenAI Conversations API | 在不同工作进程/服务之间共享服务器端状态 | 相同的 conversationId,以及仅包含新的用户轮次 |
previousResponseId | 仅限 OpenAI Responses API | 无需创建对话即可实现最简单的服务器托管式延续 | result.lastResponseId,以及仅包含新的用户轮次 |
result.history 和 session 由客户端管理。conversationId 和 previousResponseId 由 OpenAI 管理,并且仅适用于使用 OpenAI Responses API 的情况。在大多数应用中,应为每个对话选择一种持久化策略。混用客户端管理的历史记录和服务器管理的状态可能会导致上下文重复,除非您有意协调这两个层级。
沙盒智能体还增加了另一个状态层:实时沙盒工作区。使用常规 SDK 的 session、conversationId 或 previousResponseId 管理对话历史记录,并使用 sandbox.session、sandbox.sessionState、RunState 或快照管理沙盒文件系统状态。有关工作区生命周期,请参阅概念。
对话/聊天线程
Section titled “对话/聊天线程”每次调用 runner.run()(或 run() 实用函数)都代表应用级对话中的一个轮次。您可以选择向最终用户展示多少 RunResult 内容——有时只展示 finalOutput,有时则展示生成的每个项目。
import { Agent, run } from '@openai/agents';import type { AgentInputItem } from '@openai/agents';
let thread: AgentInputItem[] = [];
const agent = new Agent({ name: 'Assistant',});
async function userSays(text: string) { const result = await run( agent, thread.concat({ role: 'user', content: text }), );
thread = result.history; // Carry over history + newly generated items return result.finalOutput;}
await userSays('What city is the Golden Gate Bridge in?');// -> "San Francisco"
await userSays('What state is it in?');// -> "California"有关交互式版本,请参阅聊天示例。
服务器管理的对话
Section titled “服务器管理的对话”您可以让 OpenAI Responses API 为您持久化对话历史记录,而不必在每个轮次中发送完整的本地对话历史记录。这在协调长对话或多个服务时非常有用。使用下述任一服务器管理方式时,每个请求都只需传入新轮次的输入。API 会为您复用之前的状态。有关详细信息,请参阅对话状态指南。
OpenAI 提供两种复用服务器端状态的方式:
1. 用于整个对话的 conversationId
Section titled “1. 用于整个对话的 conversationId”您可以使用 Conversations API 创建一次对话,然后在每个轮次中复用其 ID。SDK 会自动仅包含新生成的项目。
import { Agent, run } from '@openai/agents';import { OpenAI } from 'openai';
const agent = new Agent({ name: 'Assistant', instructions: 'Reply very concisely.',});
async function main() { // Create a server-managed conversation: const client = new OpenAI(); const { id: conversationId } = await client.conversations.create({});
const first = await run(agent, 'What city is the Golden Gate Bridge in?', { conversationId, }); console.log(first.finalOutput); // -> "San Francisco"
const second = await run(agent, 'What state is it in?', { conversationId }); console.log(second.finalOutput); // -> "California"}
main().catch(console.error);2. 用于从上一轮继续的 previousResponseId
Section titled “2. 用于从上一轮继续的 previousResponseId”如果您无论如何都只想从 Responses API 开始,可以使用上一个响应返回的 ID 串联每个请求。这样无需创建完整的对话资源,也能在不同轮次之间保留上下文。
import { Agent, run } from '@openai/agents';
const agent = new Agent({ name: 'Assistant', instructions: 'Reply very concisely.',});
async function main() { const first = await run(agent, 'What city is the Golden Gate Bridge in?'); console.log(first.finalOutput); // -> "San Francisco"
const previousResponseId = first.lastResponseId; const second = await run(agent, 'What state is it in?', { previousResponseId, }); console.log(second.finalOutput); // -> "California"}
main().catch(console.error);conversationId 和 previousResponseId 互斥。如果您需要一个可在不同系统间共享的具名对话资源,请使用 conversationId;如果只需要从一个响应延续到下一个响应的成本最低的 SDK 级基本组件,请使用 previousResponseId。
钩子与自定义
Section titled “钩子与自定义”模型调用输入过滤器
Section titled “模型调用输入过滤器”使用 callModelInputFilter 可在模型调用前一刻编辑模型输入。此钩子接收当前智能体、上下文和合并后的输入项(如果存在会话,则包括会话历史记录)。返回更新后的 input 数组和可选的 instructions,以隐藏敏感数据、移除旧消息或注入额外的系统指引。
可以在 runner.run(..., { callModelInputFilter }) 中针对单次运行设置,也可以在 Runner 配置中设为默认值(RunConfig 中的 callModelInputFilter)。
返回值必须是 ModelInputData 对象:{ input: AgentInputItem[], instructions? }。input 字段是必需的,并且必须是数组。返回任何其他结构都会抛出 UserError。
SDK 会在调用过滤器之前克隆准备好的轮次输入。如果您同时使用 session,系统将持久化过滤后的副本,因此此处执行的隐藏或截断操作也会反映在存储的会话历史记录中。
使用 conversationId 或 previousResponseId 时,该钩子会在为下一次 Responses API 调用准备的有效载荷上运行。之前由服务器管理的上下文会由 API 恢复,因此该次调用的过滤后数组可能已经仅表示新轮次的增量,而不是完整重放先前的历史记录。如果您需要在最后的过滤步骤之前更改存储的历史记录与当前轮次的合并方式,请使用 sessionInputCallback。
工具错误格式化器
Section titled “工具错误格式化器”使用 toolErrorFormatter 可自定义发回给模型的工具错误消息。这样可以返回特定于业务领域的措辞(例如合规指引),而不是 SDK 的默认消息。
可以针对单次运行设置格式化器(runner.run(..., { toolErrorFormatter })),也可以在 RunConfig 中全局设置(new Runner(...) 中的 toolErrorFormatter)。
此格式化器是批准拒绝的全局回退方案。如果使用 result.state.reject(interruption, { message: '...' }) 拒绝特定中断,该次调用的 message 优先于 toolErrorFormatter。如果两者均未提供,SDK 将回退到默认拒绝文本:Tool execution was not approved.
当 toolNotFoundBehavior: 'return_error_to_model' 将未解析的函数工具调用转换为模型可见的工具输出时,也会运行该格式化器。在这种情况下,默认消息为 Tool '<name>' not found.
格式化器接收:
kind('approval_rejected'或'tool_not_found')toolType('function'、'computer'、'shell'或'apply_patch')toolNamecallIddefaultMessage(当前错误类别的 SDK 回退消息)runContext
返回字符串可覆盖消息;返回 undefined 则保留 SDK 默认值。如果格式化器抛出异常(或返回非字符串值),SDK 会记录警告,并回退到当前错误类别的默认消息。
推理项 ID 策略
Section titled “推理项 ID 策略”使用 reasoningItemIdPolicy 可控制 SDK 将之前生成的运行项重新转换为 AgentInputItem[] 以供后续模型输入时,推理项是否保留其 id 字段。
这会影响 SDK 将已生成的模型项目重新用作输入的场景,例如:
- 同一次运行中的后续模型调用(例如工具执行之后);
- 将生成的项目复用为输入/历史记录的后续轮次;
- 从保存的
RunState恢复的运行; result.history/result.output等派生结果视图(它们是模型输入形式的数组)。'preserve'(默认)保留推理项 ID。'omit'在将推理项发回作为输入之前移除其id字段。- 非推理项不受影响。
此策略不会更改:
- 原始模型响应(
result.rawResponses); - 运行项(
result.newItems); - 提供商返回的模型当前轮次输出。
换句话说,当 SDK 根据之前生成的项目构建下一次输入时,该策略才会生效。
您可以针对单次运行设置该策略(runner.run(..., { reasoningItemIdPolicy: 'omit' })),也可以将其设为运行器默认值(new Runner({ reasoningItemIdPolicy: 'omit', ... }))。从保存的 RunState 恢复时,除非您覆盖该策略,否则将复用之前确定的策略。
与 callModelInputFilter 的交互
Section titled “与 callModelInputFilter 的交互”reasoningItemIdPolicy 会在 callModelInputFilter 之前应用。如果需要自定义行为,callModelInputFilter 仍可检查准备好的输入,并在调用模型之前手动重新添加或移除推理 ID。
'omit' 的适用场景
Section titled “'omit' 的适用场景”当您希望在不包含 ID 的情况下规范化重新使用的推理项时,请使用 'omit'(例如,让转发或重新使用的模型输入更简单,或满足应用流水线中的集成要求)。
如果后端或提供商因请求验证错误而拒绝重新使用的推理项,这也是一个实用的故障排除选项(例如,后续输入中的推理项 ID 导致 HTTP 400 错误)。在这些情况下,使用 'omit' 移除重新使用的推理 ID,可以避免发送后端认为对新请求无效的 ID。
如果您希望 SDK 在重新使用的输入中携带推理项 ID,并且您的集成接受这些 ID,请保留 'preserve'。
使用 errorHandlers 可将支持的运行时错误转换为最终输出,而不是抛出异常。支持的键包括 maxTurns、modelRefusal 和 invalidFinalOutput。
errorHandlers.maxTurns仅处理达到最大轮次的错误。errorHandlers.modelRefusal处理以ModelRefusalError形式公开的模型拒绝。errorHandlers.invalidFinalOutput处理缺少结构化最终输出或输出模式验证失败时抛出的ModelBehaviorError实例。处理器会返回经过验证的回退值,而不会重试模型或重新执行工具的副作用。errorHandlers.default用作支持错误类别的回退方案。- 处理器接收
{ error, context, runData },并可返回{ finalOutput, includeInHistory? }。返回的finalOutput必须匹配当前智能体的outputType。对于 structured outputs,SDK 会在完成运行前验证回退值。返回undefined将保留该错误的默认行为。
SDK 会抛出一小组可捕获的错误:
MaxTurnsExceededError——达到maxTurns。ModelBehaviorError——模型生成了无效输出(例如格式错误的 JSON、未知工具)。ModelRefusalError——模型拒绝生成请求的输出。InputGuardrailTripwireTriggered/OutputGuardrailTripwireTriggered——护栏违规。ToolInputGuardrailTripwireTriggered/ToolOutputGuardrailTripwireTriggered——工具护栏违规。GuardrailExecutionError——护栏未能完成执行。ToolTimeoutError——函数工具超过timeoutMs,并使用了timeoutBehavior: 'raise_exception'。ToolCallError——函数工具执行因非超时错误而失败。UserError——因配置或用户输入而抛出的任何错误。
它们都扩展自基础 AgentsError 类,该类可能提供 state 属性,用于访问当前运行状态。
以下代码示例演示了如何处理 GuardrailExecutionError。由于输入护栏仅针对第一个用户输入运行,因此该示例使用原始输入和上下文重新开始运行。它还演示了如何复用保存的状态来重试输出护栏,而无需再次调用模型:
import { Agent, GuardrailExecutionError, InputGuardrail, InputGuardrailTripwireTriggered, OutputGuardrail, OutputGuardrailTripwireTriggered, run,} from '@openai/agents';import { z } from 'zod';
// Shared guardrail agent to avoid re-creating it on every fallback run.const guardrailAgent = new Agent({ name: 'Guardrail check', instructions: 'Check if the user is asking you to do their math homework.', outputType: z.object({ isMathHomework: z.boolean(), reasoning: z.string(), }),});
async function main() { const input = 'Hello, can you help me solve for x: 2x + 3 = 11?'; const context = { customerId: '12345' };
// Input guardrail example
const unstableInputGuardrail: InputGuardrail = { name: 'Math Homework Guardrail (unstable)', execute: async () => { throw new Error('Something is wrong!'); }, };
const fallbackInputGuardrail: InputGuardrail = { name: 'Math Homework Guardrail (fallback)', execute: async ({ input, context }) => { const result = await run(guardrailAgent, input, { context }); const isMathHomework = result.finalOutput?.isMathHomework ?? /solve for x|math homework/i.test(JSON.stringify(input)); return { outputInfo: result.finalOutput, tripwireTriggered: isMathHomework, }; }, };
const agent = new Agent({ name: 'Customer support agent', instructions: 'You are a customer support agent. You help customers with their questions.', inputGuardrails: [unstableInputGuardrail], });
try { // Input guardrails only run on the first turn of a run, so retries must start a fresh run. await run(agent, input, { context }); } catch (e) { if (e instanceof GuardrailExecutionError) { console.error(`Guardrail execution failed (input): ${e}`); try { agent.inputGuardrails = [fallbackInputGuardrail]; // Retry from scratch with the original input and context. await run(agent, input, { context }); } catch (ee) { if (ee instanceof InputGuardrailTripwireTriggered) { console.log('Math homework input guardrail tripped on retry'); } else { throw ee; } } } else { throw e; } }
// Output guardrail example
const replyOutputSchema = z.object({ reply: z.string() });
const unstableOutputGuardrail: OutputGuardrail<typeof replyOutputSchema> = { name: 'Answer review (unstable)', execute: async () => { throw new Error('Output guardrail crashed.'); }, };
const fallbackOutputGuardrail: OutputGuardrail<typeof replyOutputSchema> = { name: 'Answer review (fallback)', execute: async ({ agentOutput }) => { const outputText = typeof agentOutput === 'string' ? agentOutput : (agentOutput?.reply ?? JSON.stringify(agentOutput)); const flagged = /math homework|solve for x|x =/i.test(outputText); return { outputInfo: { flaggedOutput: outputText }, tripwireTriggered: flagged, }; }, };
const agent2 = new Agent<unknown, typeof replyOutputSchema>({ name: 'Customer support agent (output check)', instructions: 'You are a customer support agent. Answer briefly.', outputType: replyOutputSchema, outputGuardrails: [unstableOutputGuardrail], });
try { await run(agent2, input, { context }); } catch (e) { if (e instanceof GuardrailExecutionError && e.state) { console.error(`Guardrail execution failed (output): ${e}`); try { agent2.outputGuardrails = [fallbackOutputGuardrail]; // Output guardrails can be retried using the saved state without another model call. await run(agent2, e.state); } catch (ee) { if (ee instanceof OutputGuardrailTripwireTriggered) { console.log('Output guardrail tripped after retry with saved state'); } else { throw ee; } } } else { throw e; } }}
main().catch(console.error);输入与输出重试:
- 输入护栏仅针对一次运行中的第一个用户输入运行,因此必须使用相同的输入/上下文开始新的运行才能重试——传入保存的
state不会重新触发输入护栏。 - 输出护栏在模型响应后运行,因此可以复用
GuardrailExecutionError中保存的state来重新运行输出护栏,而无需再次调用模型。
运行上述示例时,您将看到以下输出:
Guardrail execution failed (input): Error: Input guardrail failed to complete: Error: Something is wrong!Math homework input guardrail tripped on retryGuardrail execution failed (output): Error: Output guardrail failed to complete: Error: Output guardrail crashed.Output guardrail tripped after retry with saved state