跳转到内容

护栏

护栏可以与智能体并行运行,也可以阻塞执行直至自身完成,从而让你对用户输入或智能体输出执行检查和验证。例如,你可以在调用高成本模型之前,先运行一个轻量模型作为护栏。如果护栏检测到恶意使用,它可以触发错误并阻止高成本模型运行。

护栏有两种:

  1. 输入护栏在初始用户输入上运行。
  2. 输出护栏在最终智能体输出上运行。

护栏会附加到智能体上,但它们不一定会在工作流中的每个智能体上运行:

  • 输入护栏仅在链中的第一个智能体上运行。
  • 输出护栏仅在生成最终输出的智能体上运行。
  • 工具护栏会在每次函数工具调用时运行:输入护栏在执行前运行,输出护栏在执行后运行。

如果你需要在包含管理器或交接的工作流中检查每次自定义函数工具调用,请使用工具护栏,而不是智能体级别的输入/输出护栏。

输入护栏分三步运行:

  1. 护栏接收传递给智能体的同一份输入。
  2. 护栏函数执行,并返回封装在 InputGuardrailResult 中的 GuardrailFunctionOutput
  3. 如果 tripwireTriggeredtrue,则会抛出 InputGuardrailTripwireTriggered 错误。

注意输入护栏用于用户输入,因此只有当该智能体是工作流中的第一个智能体时才会运行。护栏配置在智能体自身上,因为不同的智能体通常需要不同的护栏。

  • runInParallel: true(默认)会让护栏与 LLM/工具调用并行启动。这可以最大限度降低延迟,但如果护栏之后触发,模型可能已经消耗了 token 或运行了工具。
  • runInParallel: false 会在调用模型之前运行护栏,从而在护栏阻止请求时避免 token 消耗和工具执行。当你更重视安全与成本而非延迟时,请使用此模式。

输出护栏分 3 步运行:

  1. 护栏接收智能体生成的输出。
  2. 护栏函数执行,并返回封装在 OutputGuardrailResult 中的 GuardrailFunctionOutput
  3. 如果 tripwireTriggeredtrue,则会抛出 OutputGuardrailTripwireTriggered 错误。

注意输出护栏仅在该智能体是工作流中的最后一个智能体时运行。对于实时语音交互,请参见构建语音智能体

输出护栏函数还会接收一个可选的 details 对象,其中包含底层的 modelResponse 以及该轮生成的输出项。当仅凭最终输出不足以判断响应是否应通过时,可以使用它,例如在触发护栏之前,你想检查完整的生成项列表或提供商响应元数据。

工具护栏会包装函数工具,让你在工具调用执行前后验证或阻止调用。它们配置在工具自身上(通过 tool() 选项),并会在该工具的每次调用时运行。

实际上,这意味着你可以在自定义函数工具的 tool({...}) 上设置 inputGuardrails 和/或 outputGuardrails

  • 输入工具护栏在工具执行前运行,可以用一条消息拒绝该调用,或抛出触发线错误。
  • 输出工具护栏在工具执行后运行,可以用拒绝消息替换输出,或抛出触发线错误。

如果某个本地函数工具还需要人工审批,输入工具护栏通常会在审批通过后、执行前立即运行。若还希望这些输入护栏在发起待审批请求之前运行,请在 run()Runner 上设置 toolExecution: { preApprovalInputGuardrails: true }。审批后,在工具执行前,这些护栏仍会再次运行。

工具护栏会返回一个 behavior

  • allow——继续执行下一个护栏或工具。
  • rejectContent——用一条消息短路返回(跳过工具调用或替换输出)。
  • throwException——立即抛出触发线错误。

工具护栏适用于你使用 tool() 定义的函数工具。交接会以类似函数的工具形式呈现给模型,但它们走的是 SDK 的交接路径,而不是常规函数工具流水线,因此工具护栏不适用于交接调用本身。托管工具和内置执行工具(computerToolshellToolapplyPatchTool)也不使用这条护栏流水线,并且 agent.asTool() 目前也不会直接暴露工具护栏选项。

当护栏失败时,它会通过触发线发出信号。一旦触发线被触发,运行器就会抛出相应错误并停止执行。

护栏本质上只是一个返回 GuardrailFunctionOutput 的函数。下面是一个最小示例:它通过在底层运行另一个智能体,检查用户是否在请求数学作业帮助。

输入护栏示例
import {
Agent,
run,
InputGuardrailTripwireTriggered,
InputGuardrail,
} from '@openai/agents';
import { z } from 'zod';
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(),
}),
});
const mathGuardrail: InputGuardrail = {
name: 'Math Homework Guardrail',
// Set runInParallel to false to block the model until the guardrail completes.
runInParallel: false,
execute: async ({ input, context }) => {
const result = await run(guardrailAgent, input, { context });
return {
outputInfo: result.finalOutput,
tripwireTriggered: result.finalOutput?.isMathHomework === false,
};
},
};
const agent = new Agent({
name: 'Customer support agent',
instructions:
'You are a customer support agent. You help customers with their questions.',
inputGuardrails: [mathGuardrail],
});
async function main() {
try {
await run(agent, 'Hello, can you help me solve for x: 2x + 3 = 11?');
console.log("Guardrail didn't trip - this is unexpected");
} catch (e) {
if (e instanceof InputGuardrailTripwireTriggered) {
console.log('Math homework guardrail tripped');
}
}
}
main().catch(console.error);

输出护栏的工作方式相同。

输出护栏示例
import {
Agent,
run,
OutputGuardrailTripwireTriggered,
OutputGuardrail,
} from '@openai/agents';
import { z } from 'zod';
// The output by the main agent
const MessageOutput = z.object({ response: z.string() });
type MessageOutput = z.infer<typeof MessageOutput>;
// The output by the math guardrail agent
const MathOutput = z.object({ reasoning: z.string(), isMath: z.boolean() });
// The guardrail agent
const guardrailAgent = new Agent({
name: 'Guardrail check',
instructions: 'Check if the output includes any math.',
outputType: MathOutput,
});
// An output guardrail using an agent internally
const mathGuardrail: OutputGuardrail<typeof MessageOutput> = {
name: 'Math Guardrail',
async execute({ agentOutput, context }) {
const result = await run(guardrailAgent, agentOutput.response, {
context,
});
return {
outputInfo: result.finalOutput,
tripwireTriggered: result.finalOutput?.isMath ?? false,
};
},
};
const agent = new Agent({
name: 'Support agent',
instructions:
'You are a user support agent. You help users with their questions.',
outputGuardrails: [mathGuardrail],
outputType: MessageOutput,
});
async function main() {
try {
const input = 'Hello, can you help me solve for x: 2x + 3 = 11?';
await run(agent, input);
console.log("Guardrail didn't trip - this is unexpected");
} catch (e) {
if (e instanceof OutputGuardrailTripwireTriggered) {
console.log('Math output guardrail tripped');
}
}
}
main().catch(console.error);

工具输入/输出护栏如下所示:

工具护栏
import {
Agent,
ToolGuardrailFunctionOutputFactory,
defineToolInputGuardrail,
defineToolOutputGuardrail,
tool,
} from '@openai/agents';
import { z } from 'zod';
const blockSecrets = defineToolInputGuardrail({
name: 'block_secrets',
run: async ({ toolCall }) => {
const args = JSON.parse(toolCall.arguments) as { text?: string };
if (args.text?.includes('sk-')) {
return ToolGuardrailFunctionOutputFactory.rejectContent(
'Remove secrets before calling this tool.',
);
}
return ToolGuardrailFunctionOutputFactory.allow();
},
});
const redactOutput = defineToolOutputGuardrail({
name: 'redact_output',
run: async ({ output }) => {
const text = String(output ?? '');
if (text.includes('sk-')) {
return ToolGuardrailFunctionOutputFactory.rejectContent(
'Output contained sensitive data.',
);
}
return ToolGuardrailFunctionOutputFactory.allow();
},
});
const classifyTool = tool({
name: 'classify_text',
description: 'Classify text for internal routing.',
parameters: z.object({
text: z.string(),
}),
inputGuardrails: [blockSecrets],
outputGuardrails: [redactOutput],
execute: ({ text }) => `length:${text.length}`,
});
const agent = new Agent({
name: 'Classifier',
instructions: 'Classify incoming text.',
tools: [classifyTool],
});
  1. guardrailAgent 在护栏函数内部使用。
  2. 护栏函数接收智能体输入或输出,并返回结果。
  3. 护栏结果中可以包含额外信息。
  4. agent 定义应用护栏的实际工作流。