跳转到内容

运行智能体

智能体本身不会执行任何操作——你需要使用 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 runner
const 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 会运行一个循环:

  1. 使用当前输入调用当前智能体的模型。
  2. 检查 LLM 的响应。
    • 最终输出 → 返回。
    • 交接 → 切换到新智能体,保留累积的对话历史,回到 1。
    • 工具调用 → 执行工具,将其结果追加到对话中,回到 1。
  3. 一旦达到 maxTurns,抛出 MaxTurnsExceededError

在应用启动时创建一个 Runner 并在请求之间复用。该实例存储全局配置,如模型提供方和追踪选项。仅当你需要完全不同的设置时才创建另一个 Runner。对于简单脚本,你也可以调用 run(),它会在内部使用默认 runner。

run() 方法的输入包括用于启动运行的初始智能体、此次运行的输入以及一组选项。

输入可以是字符串(视为用户消息)、输入项列表,或在你构建人机协作智能体时使用的 RunState 对象。

附加选项包括:

OptionDefaultDescription
streamfalse若为 true,调用将返回 StreamedRunResult,并在模型产生事件时进行推送。
context转发到每个工具 / 护栏 / 交接的上下文对象。详见上下文管理
maxTurns10安全上限——达到后抛出 MaxTurnsExceededError
signal用于取消的 AbortSignal
session会话持久化实现。参见会话
sessionInputCallback会话历史与新输入的自定义合并逻辑;在模型调用前运行。参见会话
callModelInputFilter在调用模型前编辑模型输入(items + 可选 instructions)的钩子。参见调用模型输入过滤器
tracing按运行设置的追踪配置覆盖(例如导出 API key)。
conversationId复用服务器端会话(仅适用于 OpenAI Responses API + Conversations API)。
previousResponseId从上一次 Responses API 调用继续,而不创建会话(仅适用于 OpenAI Responses API)。

流式传输允许你在 LLM 运行时接收事件。一旦流开始,StreamedRunResult 将包含关于此次运行的完整信息,包括所有新产生的输出。你可以使用 for await 循环迭代流事件。更多信息参见流式传输

如果你创建自己的 Runner 实例,可以传入一个 RunConfig 对象来配置 runner。

FieldTypePurpose
modelstring | Model为运行中的所有智能体强制指定特定模型。
modelProviderModelProvider解析模型名称——默认为 OpenAI 提供方。
modelSettingsModelSettings全局调参,覆盖每个智能体的设置。
handoffInputFilterHandoffInputFilter执行交接时变更输入项(如果交接本身未定义该过滤器)。
inputGuardrailsInputGuardrail[]应用于初始用户输入的护栏。
outputGuardrailsOutputGuardrail[]应用于最终输出的护栏。
tracingDisabledboolean完全禁用 OpenAI 追踪。
traceIncludeSensitiveDataboolean在仍然发出 span 的情况下,从追踪中排除 LLM/工具的输入与输出。
workflowNamestring显示在 Traces 仪表盘——有助于分组相关运行。
traceId / groupIdstring手动指定 trace 或 group ID,而不是让 SDK 生成。
traceMetadataRecord<string, string>附加到每个 span 的任意元数据。
tracingTracingConfig按运行设置的追踪覆盖(例如导出 API key)。
sessionInputCallbackSessionInputCallback在该 runner 上所有运行的默认历史合并策略。
callModelInputFilterCallModelInputFilter在每次模型调用前编辑模型输入的全局钩子。

每次调用 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"

参见聊天示例获取交互式版本。

你可以让 OpenAI Responses API 为你持久化会话历史,从而无需在每个轮次发送完整的本地记录。这在协调长对话或多个服务时很有用。详见会话状态指南

OpenAI 提供两种复用服务器端状态的方式:

你可以使用 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 串联每个请求。这样无需创建完整的会话资源即可在轮次之间保持上下文。

使用 previousResponseId 串联
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);

使用 callModelInputFilter 在调用模型之前编辑模型输入。该钩子接收当前智能体、上下文以及合并后的输入项(在存在会话历史时也包含历史)。返回更新的 input 数组和可选的 instructions,以便脱敏、丢弃旧消息或注入额外系统指导。

你可以在 runner.run(..., { callModelInputFilter }) 中为某次运行设置,或在 Runner 配置(RunConfig 中的 callModelInputFilter)里作为默认值设置。

SDK 会抛出一小组可捕获的错误:

以上错误均继承自基础 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 retry
Guardrail execution failed (output): Error: Output guardrail failed to complete: Error: Output guardrail crashed.
Output guardrail tripped after retry with saved state