ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

openai-agents-python 工具护栏(Tool Guardrails)实战指南:在函数工具调用前后执行校验、拦截与脱敏

openai-agents-python 工具护栏(Tool Guardrails)实战指南:在函数工具调用前后执行校验、拦截与脱敏 openai-agents-python 工具护栏Tool Guardrails实战指南在函数工具调用前后执行校验、拦截与脱敏【免费下载链接】openai-agents-pythonA lightweight, powerful framework for multi-agent workflows项目地址: https://gitcode.com/GitHub_Trending/op/openai-agents-python工具护栏Tool Guardrails是 openai-agents-python 中直接附着在FunctionTool实例上的安全机制能够在每个自定义函数工具被调用之前与之后分别执行校验逻辑从而实现调用前拦截敏感入参、调用后替换敏感输出等能力。本文以 docs/ref/tool_guardrails.md 的 API 参考为骨架结合 docs/guardrails.md 的完整指南与 核心实现源码系统讲解工具护栏的数据结构、三种行为模式、装饰器用法、与审批Approval流程的配合方式以及底层执行管线与会话数据脱敏机制。读完本文你将能够在自己的多 Agent 工作流中为任意函数工具接入输入/输出护栏并正确区分allow、reject_content、raise_exception三种决策的语义。一、为什么需要工具护栏与输入/输出护栏的工作流边界在 openai-agents-python 中护栏分为三类它们在多 Agent 工作流中的运行时机完全不同输入护栏Input guardrails只在链路中的第一个 Agent收到用户输入时运行输出护栏Output guardrails只在产出最终输出的那个 Agent完成后运行工具护栏Tool guardrails在每一次自定义函数工具调用上运行输入护栏在工具执行前、输出护栏在工具执行后。这意味着如果工作流中包含 manager、handoff交接或委派给专家子 Agent 的场景仅依赖 Agent 级别的输入/输出护栏无法覆盖每一次工具调用的校验需求——此时应改用工具护栏。官方文档在 docs/guardrails.md 中明确建议如果你需要在包含 manager、handoffs 或委派专家的工作流中对每个自定义函数工具调用前后做检查请使用工具护栏。从实现上看工具护栏的代码路径与 Agent 级护栏是平行的Agent 级护栏定义在 src/agents/guardrail.py而工具护栏全部集中在 src/agents/tool_guardrails.py并由 src/agents/run_internal/tool_execution.py 中的执行管线驱动。二、核心 API 全景数据、结果与行为工具护栏的公开 API 全部从包顶层导出见 src/agents/init.py 的ToolGuardrailFunctionOutput、ToolInputGuardrail、ToolInputGuardrailData、ToolInputGuardrailResult、ToolOutputGuardrail、ToolOutputGuardrailData、ToolOutputGuardrailResult以及装饰器tool_input_guardrail、tool_output_guardrail。下面逐一拆解。1. 护栏入参数据ToolInputGuardrailData与ToolOutputGuardrailData护栏函数接收的第一个参数是数据对象而非裸的上下文。两个数据类都定义在 src/agents/tool_guardrails.pydataclass class ToolInputGuardrailData: context: ToolContext[Any] # 当前工具调用的上下文 agent: Agent[Any] # 正在执行该工具的 Agent dataclass class ToolOutputGuardrailData(ToolInputGuardrailData): output: Any # 工具函数实际返回的输出输入护栏拿到ToolInputGuardrailData可在工具真正执行前检查参数输出护栏拿到ToolOutputGuardrailData它继承了输入数据并额外携带output字段用于检查工具的真实返回值。其中的context是 ToolContext它继承自RunContextWrapper并额外提供以下字段字段含义tool_name被调用的工具名称tool_call_id本次工具调用的 IDtool_arguments工具调用的原始参数字符串JSON 文本可直接json.loadstool_call对应的ResponseFunctionToolCall对象若存在tool_namespaceResponses API 命名空间若存在agent当前活动的 Agentrun_config当前运行的配置输入护栏最常见的模式就是从data.context.tool_arguments解析出参数做检查。2. 护栏判定结果ToolGuardrailFunctionOutput与三种行为护栏函数必须返回一个ToolGuardrailFunctionOutput定义于 src/agents/tool_guardrails.py它包含两个字段dataclass class ToolGuardrailFunctionOutput: output_info: Any # 关于本次检查的可选信息例如执行了哪些检查及细粒度结果 behavior: RejectContentBehavior | RaiseExceptionBehavior | AllowBehavior其中behavior是核心它决定系统如何处理该护栏结果共有三种取值行为类型标识语义allow{type: allow}允许工具正常执行不加干预默认值reject_content{type: reject_content, message: str}拒绝该工具调用/输出但用一条消息告诉模型继续执行raise_exception{type: raise_exception}抛出异常终止执行ToolGuardrailFunctionOutput提供了三个类方法推荐用它们构造结果而不是手写 TypedDictToolGuardrailFunctionOutput.allow(output_infoNone)放行ToolGuardrailFunctionOutput.reject_content(message, output_infoNone)拒绝message会代替工具结果发给模型ToolGuardrailFunctionOutput.raise_exception(output_infoNone)抛出ToolGuardrailTripwireTriggered类异常终止执行。注意raise_exception行为与reject_content的关键差异前者立即终止整个运行后者只拒绝本次内容、运行继续模型会收到message作为工具结果并自行调整。3. 护栏对象ToolInputGuardrail/ToolOutputGuardrail与run()两个护栏对象都是泛型数据类Generic[TContext_co]结构一致dataclass class ToolInputGuardrail(Generic[TContext_co]): guardrail_function: Callable[[ToolInputGuardrailData], MaybeAwaitable[ToolGuardrailFunctionOutput]] name: str | None None # 未提供时使用函数名 def get_name(self) - str: return self.name or self.guardrail_function.__name__ async def run(self, data: ToolInputGuardrailData) - ToolGuardrailFunctionOutput: if not callable(self.guardrail_function): raise UserError(fGuardrail function must be callable, got {self.guardrail_function}) result self.guardrail_function(data) if inspect.isawaitable(result): return await result return resultrun()的实现src/agents/tool_guardrails.py值得注意它通过inspect.isawaitable(result)判断护栏函数是同步还是异步因此护栏函数既可以是普通函数也可以是async函数两者都能被正确 await。若传入的guardrail_function不可调用会抛出UserErrorfrom .exceptions import UserError。4. 装饰器tool_input_guardrail与tool_output_guardrailagents.decorators提供两个装饰器把一个普通函数包装成护栏对象src/agents/tool_guardrails.pytool_input_guardrail def my_input_guardrail(data: ToolInputGuardrailData) - ToolGuardrailFunctionOutput: ... tool_output_guardrail(namemy_named_guardrail) async def my_output_guardrail(data: ToolOutputGuardrailData) - ToolGuardrailFunctionOutput: ...装饰器支持两种调用方式直接tool_input_guardrail修饰函数或带关键字参数tool_input_guardrail(name...)自定义名称。包装后得到的就是ToolInputGuardrail/ToolOutputGuardrail实例可直接放进工具的tool_input_guardrails/tool_output_guardrails参数列表。三、实战示例拦截密钥入参、脱敏敏感输出下面这个示例来自官方指南 docs/guardrails.mdTool guardrails一节并补充了完整注释。它演示了工具护栏最典型的两个用途调用前禁止密钥入参、调用后拦截含敏感数据的输出。import json from agents import ( Agent, Runner, ToolGuardrailFunctionOutput, ) from agents.decorators import tool, tool_input_guardrail, tool_output_guardrail # 输入护栏在工具执行前检查参数中是否携带密钥 tool_input_guardrail def block_secrets(data): args json.loads(data.context.tool_arguments or {}) if sk- in json.dumps(args): # 拒绝本次调用把这条消息作为工具结果回传给模型 return ToolGuardrailFunctionOutput.reject_content( Remove secrets before calling this tool. ) return ToolGuardrailFunctionOutput.allow() # 输出护栏在工具执行后检查返回值是否泄漏敏感数据 tool_output_guardrail def redact_output(data): text str(data.output or ) if sk- in text: # 拒绝该输出用消息替换掉真实工具结果 return ToolGuardrailFunctionOutput.reject_content(Output contained sensitive data.) return ToolGuardrailFunctionOutput.allow() # 通过 tool 装饰器把两个护栏挂到同一个函数工具上 tool( tool_input_guardrails[block_secrets], tool_output_guardrails[redact_output], ) def classify_text(text: str) - str: Classify text for internal routing. return flength:{len(text)} agent Agent(nameClassifier, tools[classify_text]) result Runner.run_sync(agent, hello world) print(result.final_output)要点拆解block_secrets是输入护栏data.context.tool_arguments是工具调用的原始 JSON 参数文本护栏反序列化后检查是否含sk-前缀模拟 API 密钥。命中则reject_content——工具根本不会执行模型收到的工具结果就是护栏提供的提示消息否则allow()放行。redact_output是输出护栏此时工具已执行完毕护栏检查data.output即工具返回值。命中敏感内容时reject_content会用提示消息替换真实输出防止敏感数据继续流入对话上下文。护栏挂在tool装饰器上tool_input_guardrails[...]与tool_output_guardrails[...]是FunctionTool的正式配置字段见 src/agents/tool.py 的FunctionTool数据类定义随后工具被放入Agent(tools[...])正常使用。四、配置入口tool装饰器与function_tool工厂工具护栏的配置入口有两个都在 src/agents/tool.py 中tool装饰器接受tool_input_guardrails和tool_output_guardrails两个列表参数src/agents/tool.py上面的示例就是这种用法function_tool()工厂函数签名同样包含tool_input_guardrails与tool_output_guardrailssrc/agents/tool.py适合以编程方式构建工具时传入。两者最终都构造同一个FunctionTool因此传入的护栏列表会原样保存在工具的tool_input_guardrails/tool_output_guardrails字段上由执行管线统一消费。每个工具可以挂载多个护栏它们会按列表顺序依次执行详见第六节。五、与审批Approval流程的配合pre_approval_tool_input_guardrails当函数工具需要人工审批Approval时输入护栏的执行时机有一个重要细节官方文档 docs/guardrails.md 明确指出默认行为输入护栏在审批通过之后、工具执行之前运行可选行为将RunConfig.tool_execution设置为ToolExecutionConfig(pre_approval_tool_input_guardrailsTrue)即可让输入护栏在待审批中断pending approval interruption发出之前先跑一遍通过预检查的调用在审批通过后、真正执行前仍会被再次检查一次。from agents import Agent, Runner, RunConfig, ToolExecutionConfig run_config RunConfig( tool_executionToolExecutionConfig( pre_approval_tool_input_guardrailsTrue, ) ) result Runner.run_sync(agent, input, run_configrun_config)从源码看ToolExecutionConfig定义于 src/agents/run_config.pypre_approval_tool_input_guardrails默认值为False且在__post_init__中会校验其必须是布尔值。执行管线中由_should_run_pre_approval_tool_input_guardrails()读取该标志src/agents/run_internal/tool_execution.py决定是否在发出审批中断前先行执行输入护栏。同数据类还包含max_function_tool_concurrency字段用于限制一轮中本地函数工具的最大并发执行数默认None即不限制、并发启动本回合所有工具调用。六、底层执行管线从源码看输入/输出护栏的完整流程工具护栏的实际执行逻辑位于 src/agents/run_internal/tool_execution.py 的两个私有函数中。输入护栏_execute_tool_input_guardrailsasync def _execute_tool_input_guardrails( *, func_tool: FunctionTool, tool_context: ToolContext[Any], agent: Agent[Any], tool_input_guardrail_results: list[ToolInputGuardrailResult], ) - str | None: Execute input guardrails for a tool call and return a rejection message if any. if not func_tool.tool_input_guardrails: return None for guardrail in func_tool.tool_input_guardrails: gr_out await guardrail.run( ToolInputGuardrailData(contexttool_context, agentagent) ) tool_input_guardrail_results.append( ToolInputGuardrailResult(guardrailguardrail, outputgr_out) ) if gr_out.behavior[type] raise_exception: raise ToolInputGuardrailTripwireTriggered(guardrailguardrail, outputgr_out) elif gr_out.behavior[type] reject_content: return gr_out.behavior[message] return None流程src/agents/run_internal/tool_execution.py若工具未配置输入护栏直接返回None放行按列表顺序依次执行每个护栏每次执行后把ToolInputGuardrailResult(guardrail, output)追加到运行状态的结果列表遇到raise_exception行为 → 立即抛出ToolInputGuardrailTripwireTriggered携带触发它的guardrail与output遇到reject_content行为 → 立即返回message该消息将作为被拒绝的工具结果交回给模型后续护栏不再执行全部通过 → 返回None工具继续执行。输出护栏_execute_tool_output_guardrailsasync def _execute_tool_output_guardrails( *, func_tool: FunctionTool, tool_context: ToolContext[Any], agent: Agent[Any], real_result: Any, tool_output_guardrail_results: list[ToolOutputGuardrailResult], ) - _ToolOutputGuardrailExecutionResult: if not func_tool.tool_output_guardrails: return _ToolOutputGuardrailExecutionResult(real_result) final_result real_result for output_guardrail in func_tool.tool_output_guardrails: gr_out await output_guardrail.run( ToolOutputGuardrailData(contexttool_context, agentagent, outputreal_result) ) tool_output_guardrail_results.append( ToolOutputGuardrailResult(guardrailoutput_guardrail, outputgr_out) ) if gr_out.behavior[type] raise_exception: raise ToolOutputGuardrailTripwireTriggered(guardrailoutput_guardrail, outputgr_out) elif gr_out.behavior[type] reject_content: return _ToolOutputGuardrailExecutionResult( gr_out.behavior[message], is_rejectionTrue ) return _ToolOutputGuardrailExecutionResult(final_result)流程src/agents/run_internal/tool_execution.py与输入侧对称但有两个关键差异每个护栏收到的数据是ToolOutputGuardrailData额外携带outputreal_result工具的真实返回值reject_content时返回的是带is_rejectionTrue标记的执行结果其message会替换工具输出若所有护栏均放行则返回原始real_result继续作为工具结果。Tripwire 异常与结果收集当护栏触发raise_exception时抛出的异常定义在 src/agents/exceptions.pyToolInputGuardrailTripwireTriggered暴露guardrail触发它的护栏与output护栏函数的输出ToolOutputGuardrailTripwireTriggered同样的结构。try: result Runner.run_sync(agent, input) except ToolInputGuardrailTripwireTriggered as e: print(fguardrail{e.guardrail.get_name()}, output{e.output})官方文档 docs/guardrails.mdTripwires一节补充了结果收集语义触发异常时run_data.tool_input_guardrail_results与run_data.tool_output_guardrail_results列表会保留失败前已完成的轮次中积累的护栏结果而触发的那一条则通过异常的output字段获取其他由 runner 管理的失败如MaxTurnsExceeded同样会在这两个列表中保留已完成的工具护栏结果。stream_events()抛出异常后流式结果也暴露同样的累计护栏结果列表。注意run_data在异常发生于 runner 管理之外的执行路径时可能为None。七、适用范围边界哪些工具不走护栏管线工具护栏并非万能官方文档 docs/guardrails.md 明确划定了边界适用仅适用于通过function_tool/tool创建的自定义函数工具不适用Handoff 调用本身——handoff 走的是 SDK 的专用 handoff 管线而非普通函数工具管线托管工具WebSearchTool、FileSearchTool、HostedMCPTool、CodeInterpreterTool、ImageGenerationTool内置执行工具ComputerTool、ShellTool、ApplyPatchTool、LocalShellToolAgent.as_tool()目前也不直接暴露工具护栏选项。如果你的校验目标恰好落在这些工具上需要改用其他手段例如 Agent 级输入/输出护栏或在这些托管工具外层包一层自定义函数工具再挂护栏。八、会话持久化与数据脱敏输出护栏拒绝终端工具结果时发生了什么当工具输出成为最终输出即Agent.tool_use_behavior使该工具结果成为 final output且被输出护栏的 tripwire 拒绝时SDK 有一套严格的数据脱敏机制实现在 src/agents/run_internal/blocked_output.py被拒绝的工具结果在会话、RunState、流式结果状态、沙箱内存输入中都不会被保留function_call_output的有效载荷会被替换为固定文本Output withheld by an output guardrail.常量定义于 blocked_output.py但 SDK 会保留可用于重放replay的函数调用元数据包括函数参数这意味着这些元数据中可能包含在被拒输出里也出现过的数据当前响应的OutputGuardrailResult对象同样会用固定文本替换agent_output并清空output_info当前响应的ToolOutputGuardrailResult对象保留 allow/reject 的行为类型但会把承载数据的output_info与拒绝消息替换为同一固定文本对应_data_free_tool_output_guardrail_results的实现blocked_output.py如果响应中包含 reasoning 或其他无法安全净化的形态SDK 会直接丢弃整个当前响应后缀而不是保留被拒的输出载荷。会话持久化的时序规则同样重要官方文档 docs/guardrails.md 的 Output guardrails 一节Tripwire 拒绝runner 请求配置的 Session 持久化已完成执行的工具调用与工具输出条目含重放所需推理上下文但排除被拒的候选最终输出——流式与非流式运行均适用护栏函数抛异常判定结果未知runner 会先持久化已完成的最后回合条目再向上抛出护栏异常若该次会话写入也失败会话写入错误优先流式运行使用与非流式相同的持久化顺序终态异常从stream_events()抛出若在输出护栏运行期间立即调用RunResultStreaming.cancel()会取消进行中的护栏且不会启动最后回合的会话写入。这些设计保证了护栏拒绝的内容不会泄露进会话与重放历史对涉及敏感数据密钥、隐私、合规内容的生产场景至关重要。九、常见模式小结与最佳实践用类方法构造结果不要手写 TypedDictallow()/reject_content(message)/raise_exception()三个类方法src/agents/tool_guardrails.py语义清晰、可读性强入参检查优先用输入护栏在data.context.tool_arguments上做校验reject_content可让模型自纠而无需终止整个运行输出检查优先用输出护栏拦截工具返回值中的敏感数据用固定消息替换后继续对话需要硬终止时用raise_exception例如检测到恶意用法、需要立即止损的场景对应ToolInputGuardrailTripwireTriggered/ToolOutputGuardrailTripwireTriggered异常审批场景按需开启预检查RunConfig(tool_executionToolExecutionConfig(pre_approval_tool_input_guardrailsTrue))可以让高危参数在打扰审批人之前就被拦下护栏函数支持同步与异步ToolInputGuardrail.run()/ToolOutputGuardrail.run()会自动识别协程并 awaitsrc/agents/tool_guardrails.py可按需选择牢记适用范围护栏只作用于自定义函数工具托管工具、内置执行工具与 handoff 调用不在管线内。十、结语工具护栏把每次工具调用的前后校验从 Agent 级护栏中独立出来为多 Agent 工作流提供了更细粒度的安全边界。理解ToolGuardrailFunctionOutput的三种行为、ToolInputGuardrailData/ToolOutputGuardrailData的数据形态、以及pre_approval_tool_input_guardrails与审批流程的配合就能在密钥防护、输出脱敏、内容合规等场景中组合出可靠的防线。更深入的内容可继续阅读 docs/ref/tool_guardrails.mdAPI 参考、docs/guardrails.md护栏总览含 Agent 级输入/输出护栏、src/agents/tool_guardrails.py核心实现与 src/agents/run_internal/tool_execution.py执行管线。【免费下载链接】openai-agents-pythonA lightweight, powerful framework for multi-agent workflows项目地址: https://gitcode.com/GitHub_Trending/op/openai-agents-python创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表