运行智能体
智能体本身不会执行任何操作——你需要使用 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.当你不需要自定义 runner 时,也可以使用 run() 工具,它会运行一个单例的默认 Runner 实例。
或者,你也可以创建自己的 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 中使用 run 方法时,需要传入一个起始智能体和输入。输入可以是字符串(视为用户消息),也可以是输入项列表,这些输入项与 OpenAI Responses API 的项目一致。
随后 runner 会运行一个循环:
- 使用当前输入调用当前智能体的模型。
- 检查 LLM 响应。
- 最终输出 → 返回。
- 交接 → 切换到新智能体,保留累积的对话历史,回到步骤 1。
- 工具调用 → 执行工具,将其结果追加到对话中,回到步骤 1。
- 当达到
maxTurns时抛出MaxTurnsExceededError。
Runner 生命周期
Section titled “Runner 生命周期”在应用启动时创建一个 Runner 并在各个请求之间复用。该实例会存储全局配置,如模型提供方和追踪选项。仅在需要完全不同的设置时才创建新的 Runner。对于简单脚本,你也可以直接调用 run(),它会在内部使用一个默认 runner。
传给 run() 方法的输入包括:启动运行所需的初始智能体、运行的输入以及一组选项。
输入可以是字符串(视为用户消息)、输入项列表,或在构建人机协作智能体时使用的 RunState 对象。
其他可选项包括:
| Option | Default | Description |
|---|---|---|
stream | false | 若为 true,调用将返回 StreamedRunResult 并在模型产生事件时实时发出。 |
context | – | 转发到每个 tool / guardrail / handoff 的上下文对象。详见上下文管理。 |
maxTurns | 10 | 安全限制——达到时抛出 MaxTurnsExceededError。 |
signal | – | 用于取消的 AbortSignal。 |
session | – | 会话持久化实现。参见会话。 |
sessionInputCallback | – | 自定义会话历史与新输入的合并逻辑;在模型调用前运行。参见会话。 |
callModelInputFilter | – | 在调用模型前编辑模型输入(items + 可选 instructions)的钩子。参见Call model input filter。 |
toolErrorFormatter | – | 自定义在工具调用被拒绝时返回给模型的消息。参见Tool error formatter。 |
tracing | – | 覆盖本次运行的追踪配置(例如导出 API key)。 |
errorHandlers | – | 处理受支持的运行时错误(当前为 maxTurns)。参见Error handlers。 |
conversationId | – | 复用服务器端会话(仅限 OpenAI Responses API + Conversations API)。 |
previousResponseId | – | 从上一次 Responses API 调用继续,而不创建会话(仅限 OpenAI Responses API)。 |
流式传输允许你在 LLM 运行过程中额外接收事件。一旦启动流,StreamedRunResult 将包含关于本次运行的完整信息,包括所有新产生的输出。你可以使用 for await 循环遍历流式事件。详见流式传输。
如果你要创建自己的 Runner 实例,可以传入 RunConfig 对象来配置 runner。
| Field | Type | Purpose |
|---|---|---|
model | string | Model | 为运行中的所有智能体强制指定一个模型。 |
modelProvider | ModelProvider | 解析模型名称——默认为 OpenAI 提供方。 |
modelSettings | ModelSettings | 全局调参,覆盖每个智能体的设置。 |
handoffInputFilter | HandoffInputFilter | 在执行交接时修改输入项(如果交接本身未定义该过滤器)。 |
inputGuardrails | InputGuardrail[] | 应用于初始用户输入的护栏。 |
outputGuardrails | OutputGuardrail[] | 应用于最终输出的护栏。 |
tracingDisabled | boolean | 完全禁用 OpenAI 追踪。 |
traceIncludeSensitiveData | boolean | 在仍然发出 span 的同时,从追踪中排除 LLM/工具的输入与输出。 |
workflowName | string | 显示在 Traces 仪表盘中——用于聚合相关运行。 |
traceId / groupId | string | 手动指定 trace 或 group ID,而不是让 SDK 生成。 |
traceMetadata | Record<string, string> | 附加到每个 span 的任意元数据。 |
tracing | TracingConfig | 每次运行的追踪覆盖(例如导出 API key)。 |
sessionInputCallback | SessionInputCallback | 此 runner 上所有运行的默认历史合并策略。 |
callModelInputFilter | CallModelInputFilter | 在每次模型调用前编辑模型输入的全局钩子。 |
toolErrorFormatter | ToolErrorFormatter | 自定义在工具审批被拒时返回给模型的消息的全局钩子。 |
会话/聊天线程
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 为你持久化会话历史,而不是在每个轮次都发送完整的本地转录。这在协调长对话或多个服务时非常有用。详情参见会话状态指南。
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);Call model input filter
Section titled “Call model input filter”使用 callModelInputFilter 在调用模型的正前方编辑模型输入。该钩子会接收当前智能体、context,以及合并后的输入项(若存在,会包含会话历史)。返回更新后的 input 数组和可选的 instructions,用于编辑敏感数据、丢弃旧消息或注入额外的系统提示。
可在单次运行中设置(runner.run(..., { callModelInputFilter })),也可在 Runner 配置中设为默认(RunConfig 内的 callModelInputFilter)。
Tool error formatter
Section titled “Tool error formatter”使用 toolErrorFormatter 来自定义当工具调用被拒绝时发送回模型的消息。这样你可以提供特定领域的措辞(例如合规指引),而不是使用 SDK 的默认消息。
可在每次运行中设置(runner.run(..., { toolErrorFormatter })),也可在 RunConfig 中全局设置(new Runner(...) 的 toolErrorFormatter)。
使用 errorHandlers 将受支持的运行时错误转换为最终输出,而不是抛出异常。目前仅支持 maxTurns。
errorHandlers.maxTurns仅处理最大轮次错误。errorHandlers.default作为受支持类型的回退。- 处理器接收
{ error, context, runData },并可返回{ finalOutput, includeInHistory? }。
SDK 会抛出一小组可捕获的错误:
MaxTurnsExceededError——达到maxTurns。ModelBehaviorError——模型产生无效输出(例如 JSON 格式错误、未知工具)。InputGuardrailTripwireTriggered/OutputGuardrailTripwireTriggered——护栏违规。GuardrailExecutionError——护栏未能完成。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