护栏
护栏可以与智能体并行运行,也可以阻止执行直至检查完成,从而对用户输入或智能体输出进行检查和验证。例如,您可以先运行一个轻量级模型作为护栏,再调用成本较高的模型。如果护栏检测到恶意使用,就可以触发错误并阻止高成本模型运行。
护栏分为三类:
- 输入护栏针对初始用户输入运行。
- 输出护栏针对智能体的最终输出运行。
- 工具护栏在每次自定义函数工具调用前后运行。
护栏附加到智能体上,但不一定会在工作流中的每个智能体上运行:
- 输入护栏仅针对链中的第一个智能体运行。
- 输出护栏仅针对生成最终输出的智能体运行。
- 工具护栏针对每次函数工具调用运行,其中输入护栏在执行前运行,输出护栏在执行后运行。
如果您需要在包含管理器或交接的工作流中检查每次自定义函数工具调用,请使用工具护栏,而不是智能体级别的输入护栏和输出护栏。
输入护栏分三步运行:
- 护栏接收传递给智能体的相同输入。
- 护栏函数执行并返回一个
GuardrailFunctionOutput,该输出封装在InputGuardrailResult中。 - 如果
tripwireTriggered为true,则抛出InputGuardrailTripwireTriggered错误。
注意输入护栏用于处理用户输入,因此仅当智能体是工作流中的第一个智能体时才会运行。护栏配置在智能体本身,因为不同的智能体通常需要不同的护栏。
runInParallel: true(默认)会让输入护栏与智能体同时开始执行。并发执行可以最大限度减少延迟,但如果护栏随后触发,模型可能已经消耗了 token 或运行了工具。runInParallel: false会在调用模型之前运行护栏。当护栏阻止请求时,可以避免消耗 token 和执行工具。如果您更重视安全性和成本而非延迟,请使用此模式。
输出护栏分 3 步运行:
- 护栏接收智能体生成的输出。
- 护栏函数执行并返回一个
GuardrailFunctionOutput,该输出封装在OutputGuardrailResult中。 - 如果
tripwireTriggered为true,则抛出OutputGuardrailTripwireTriggered错误。
注意输出护栏仅当智能体是工作流中的最后一个智能体时才会运行。有关实时语音交互的信息,请参阅构建语音智能体。
输出护栏函数还会接收一个可选的 details 对象,其中包含底层 modelResponse 和本轮生成的输出项。当仅凭最终输出不足以判断响应是否应当通过时,可以使用此对象。例如,您可能希望先检查完整的生成项列表或提供商响应元数据,再决定是否触发护栏。
被拒绝的终结工具输出
Section titled “被拒绝的终结工具输出”当 toolUseBehavior 将已完成的函数工具结果用作本次运行的最终输出时,输出护栏会在 SDK 将该结果视为可重放的最终数据之前对其进行评估。护栏会接收原始候选输出。如果护栏触发,SDK 会在由 SDK 管理的运行状态、重放数据、会话历史记录以及公开的熔断错误中,将被拒绝的终结结果替换为 Output withheld by an output guardrail.。
SDK 还会清理可能保留被拒绝输出别名的元数据。对于当前响应,OutputGuardrailResult.agentOutput 会变为相同的占位内容,并移除 OutputGuardrailResult.outputInfo。当前工具输出护栏结果会保留其判定,但省略 outputInfo;rejectContent 结果会使用该占位内容作为消息。此前已接受轮次的结果和历史记录保持不变。
对于可识别的函数调用及其结果对,SDK 会保留可安全重放的标识和状态字段,同时替换结果内容。如果 SDK 无法确认当前响应中的哪些项属于被拒绝的终结输出,就会丢弃当前响应中存在歧义的后缀,或采取封闭式失败策略,而不是进行重放。
此保护不会撤销外部工具的副作用、收回已经发送给应用程序代码的输出、删除已存储在 SDK 控制范围之外的数据,也不会修改应用程序对原始提供商数据的自有引用。
如果您的应用程序需要不同的不含数据的占位内容,请在 Runner 上或单次 run() 调用的选项中设置 outputGuardrailBlockedMessage。单次运行的值优先于 Runner 上的值。您可以提供非空字符串,也可以提供一个接收 defaultMessage、guardrailName、agent 和 runContext 的格式化函数。该格式化函数可以是异步的,但不会接收被拒绝的输出。如果格式化函数抛出错误、被拒绝,或返回空值或非字符串值,SDK 会采取封闭式失败策略并使用默认占位内容,而不会暴露格式化函数的失败。
工具护栏封装函数工具,允许您在工具调用执行前后对其进行验证或阻止。您可以通过 tool() 选项为单个工具配置护栏,也可以在本地 MCP 服务器上配置护栏,使其针对 SDK 从该服务器转换的每个工具运行。
对于自定义函数工具,请在 tool({...}) 上设置 inputGuardrails、outputGuardrails 或同时设置两者。对于 MCPServerStreamableHttp、MCPServerStdio 或 MCPServerSSE,请在服务器构造函数选项中设置 toolInputGuardrails、toolOutputGuardrails 或同时设置两者。服务器范围的 MCP 护栏使用相同的本地函数工具执行管线,并应用于从该服务器转换的每个工具。
- 工具输入护栏在工具执行前运行,可以通过消息拒绝调用或抛出熔断错误。
- 工具输出护栏在工具执行后运行,可以用拒绝消息替换输出或抛出熔断错误。
如果本地函数工具还需要人工批准,工具输入护栏通常会在批准后、执行前立即运行。若还要让这些输入护栏在发出待处理批准请求之前运行,请在 run() 或 Runner 上设置 toolExecution: { preApprovalInputGuardrails: true }。批准后,护栏仍会在工具执行前再次运行。
工具护栏会返回一个 behavior:
allow— 继续执行下一个护栏或工具。rejectContent— 使用一条消息立即终止(跳过工具调用或替换输出)。throwException— 立即抛出熔断错误。
工具护栏适用于使用 tool() 定义的函数工具,也适用于在服务器配置了相应护栏时从本地 MCP 服务器转换的工具。交接会作为类似函数的工具呈现给模型,但它们通过 SDK 的交接路径运行,而不是常规的函数工具管线,因此工具护栏不适用于交接调用本身。托管 MCP 工具、其他托管工具以及内置执行工具(computerTool、shellTool、applyPatchTool)也不使用此护栏管线,并且 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?'); throw new Error('Expected the math homework guardrail to trip.'); } catch (e) { if (e instanceof InputGuardrailTripwireTriggered) { console.log('Math homework guardrail tripped'); return; } throw e; }}
main().catch((error) => { console.error(error); process.exit(1);});输出护栏的工作方式相同。
import { Agent, run, OutputGuardrailTripwireTriggered, OutputGuardrail,} from '@openai/agents';import { z } from 'zod';
// The output by the main agentconst MessageOutput = z.object({ response: z.string() });type MessageOutput = z.infer<typeof MessageOutput>;
// The output by the math guardrail agentconst MathOutput = z.object({ reasoning: z.string(), isMath: z.boolean() });
// The guardrail agentconst guardrailAgent = new Agent({ name: 'Guardrail check', instructions: 'Check if the output includes any math.', outputType: MathOutput,});
// An output guardrail using an agent internallyconst 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); throw new Error('Expected the math output guardrail to trip.'); } catch (e) { if (e instanceof OutputGuardrailTripwireTriggered) { console.log('Math output guardrail tripped'); return; } throw e; }}
main().catch((error) => { console.error(error); process.exit(1);});工具输入护栏和工具输出护栏如下所示:
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],});guardrailAgent在护栏函数内部使用。- 护栏函数接收智能体输入或输出并返回结果。
- 护栏结果中可以包含额外信息。
agent定义实际应用护栏的工作流。