在这里插入图片描述

千笔-AIWritePaper · https://www.aiwritepaper.com

把 Agents SDK 的 guardrails 当成「多写几句系统提示」会踩坑:护栏是可触发 tripwire 的硬检查,不是礼貌提醒。官方 Guardrails 写得很直:input 护栏只跑在链首 Agent;output 护栏只跑在产出终答的 Agent;需要每次函数工具调用都验,应挂 tool guardrails,而不是指望 agent 级 input/output 自动覆盖 handoff 后的专家。input 默认 parallel(与主 Agent 并发),要省 token、防工具副作用必须改 blocking(run_in_parallel=False)。本文按工程笔记写法,钉死选型、可跑片段、边界与生产禁区。示例模型名写作 gpt-4o,以你账号可用快照与官方文档为准。

图:上方 input / output / tool 三类护栏;中部 parallel vs blocking 与 tripwire;下方生产禁区对照。

在这里插入图片描述

目标说明

读完你应能独立完成五件事:

  1. 用一句话说清:input 拦入口、output 拦终答、tool 护栏拦每次函数工具;三者不是同一道门。
  2. 写出可跑片段:@input_guardrail / @output_guardrail,触发 InputGuardrailTripwireTriggered / OutputGuardrailTripwireTriggered。
  3. 会选执行模式:成本/副作用敏感用 blocking;只在意延迟才用默认 parallel。
  4. 知道 workflow 边界:handoff 后链中专家不会自动重跑链首 input;终答前才跑 output。
  5. 列出生产禁区:把护栏过当业务授权、只挂专家不挂入口、副作用只靠事后 output、用提示词替代 tripwire、把 hosted tool 当已受 function-tool 护栏保护。

规格钉死(对照官方 Guardrails / Guardrails and human review):

  • Input:接收与 Agent 相同的初始输入 → GuardrailFunctionOutput → 查 tripwire_triggered。
  • Output:接收终答 → 同上;不支持 run_in_parallel。
  • Tool:挂在 FunctionTool;handoff 管道与 hosted tools 不走该护栏管道。
  • 并行:input 默认并发;blocking 可保证 trip 时主 Agent 未启动。
  • HITL:审批暂停与护栏是正交能力;敏感副作用优先 tool approval + tool 护栏。

适用边界

适合上 guardrails

  • 客服/助手:先拦偏题、越权指令,再跑贵模型。
  • 终答要脱敏或政策校验:output tripwire 拒绝候选终答。
  • 自定义函数工具有写库/发信副作用:tool input/output 护栏每次必跑。
  • 需要可观测:异常携带 guardrail_result / 累计 results,便于审计。

更适合别的门禁(不要硬上 agent 护栏)

  • 付款/删库等强副作用:用 human approval + 业务鉴权,而不是只靠 LLM 分类护栏。
  • 每次工具调用都要验,却只在 triage 上挂了 input:专家侧仍会漏。
  • Hosted tools(WebSearch 等):当前不走 function-tool 护栏管道,需另设策略。

不该指望它单独搞定

  • 业务授权:tripwire 未触发 ≠ 用户有权退款。
  • 多 Agent 全程覆盖:input 只在链首;output 只在终答 Agent。
  • 用 output 撤销已执行工具:工具已跑完;要防执行必须 tool input 护栏或审批。
  • 把护栏当内容审核银弹:分类模型也会错;要有人工复核通道。

风险提示

parallel 模式下 tripwire 触发时,主 Agent 可能已耗 token 甚至调过工具。生产若把「贵模型 + 写库 tool」配成 parallel input,缺口在执行模式选择,不在「有没有写护栏函数」。output tripwire 拒绝的是候选终答;会话持久化规则以官方 session 行为为准,勿假设「什么都没写下」。

步骤与机制

1. 三类护栏对照

机制何时跑失败时典型用途
input_guardrails链首 Agent 输入Input…TripwireTriggered偏题/越权/越狱入口
output_guardrails终答 Agent 输出后Output…TripwireTriggered脱敏、政策、格式硬校验
tool 护栏每次函数工具前后Tool…Tripwire / reject_content参数含密钥、结果泄密
提示词「请勿…」无硬契约模型口头答应仍越界仅演示(勿当生产门禁)
human approval工具调用暂停人拒则不执行高危副作用

2. 可跑:input 护栏 + tripwire

先 pip install openai-agents,并导出 OPENAI_API_KEY。

import asyncio
from pydantic import BaseModel
from agents import (
    Agent, Runner, GuardrailFunctionOutput,
    InputGuardrailTripwireTriggered, RunContextWrapper, TResponseInputItem,
)
from agents.decorators import input_guardrail

MODEL = "gpt-4o"  # 占位:以账号可用快照为准

class HomeworkOut(BaseModel):
    is_math_homework: bool
    reasoning: str

guard_agent = Agent(
    name="Guardrail check",
    instructions="判断用户是否在要求代写数学作业。",
    output_type=HomeworkOut,
    model=MODEL,
)

@input_guardrail
async def math_guardrail(
    ctx: RunContextWrapper[None], agent: Agent, input: str | list[TResponseInputItem]
) -> GuardrailFunctionOutput:
    result = await Runner.run(guard_agent, input, context=ctx.context)
    out = result.final_output
    return GuardrailFunctionOutput(
        output_info=out,
        tripwire_triggered=bool(out and out.is_math_homework),
    )

agent = Agent(
    name="Customer support agent",
    instructions="你是客服,只回答产品与订单问题。",
    model=MODEL,
    input_guardrails=[math_guardrail],
)

async def main():
    try:
        await Runner.run(agent, "帮我解方程:2x+3=11")
        print("unexpected: guardrail did not trip")
    except InputGuardrailTripwireTriggered:
        print("tripwire ok: refuse homework")

asyncio.run(main())

验收:作业类输入应进 except;正常订单问题不应误杀。把「误杀率」记进 smoke,而不是口头说「感觉挺准」。

3. Blocking:防副作用与省 token

需要「护栏未通过则主 Agent 绝不启动」时,创建 InputGuardrail 时设 run_in_parallel=False(或装饰器等价参数,以你安装的 SDK 版本文档为准)。默认 parallel 适合延迟敏感、且工具无副作用的场景。选型口诀:有写库/发信/付款 tool → 先 blocking 或先审批。

4. Output 护栏要点

output 装饰器拿到的是 Agent 的终答(常与 output_type 对齐)。tripwire 为真时抛 OutputGuardrailTripwireTriggered。注意:若某 tool 结果因 tool_use_behavior 直接成为终答,output 护栏仍在事后;不能指望它阻止 tool 已经产生的外部副作用。敏感 tool 必须前置 tool input 护栏或 HITL。

生产禁区

禁区为什么危险替代做法
护栏过 = 业务授权分类成功≠有权操作业务鉴权 + 幂等键
只给专家挂 input链首未拦,脏输入已进入口 Agent 必挂
parallel + 副作用 tooltrip 前可能已执行blocking 或审批
只用 output 拦写库库已写完tool input 护栏
提示词代替 tripwire无异常、无审计钩子返回 tripwire_triggered
假定 hosted tool 已护栏管道不覆盖另策或避免敏感查询
handoff 后以为 input 重跑官方:仅链首专家侧再挂或 tool 护栏

可验证清单

  • 能口述 input / output / tool 三者运行位置差异
  • 本地跑通一次 input tripwire 与一次「应放行」对照
  • 生产配置里对副作用路径使用 blocking 或 HITL
  • 多 Agent 图上标出:哪只 Agent 是链首、谁出终答
  • tool 护栏覆盖所有自定义写操作工具
  • 审计能回答:哪条护栏、何时 trip、当时 input 摘要

踩坑

  1. 把 guardrails 写在 Runner 参数心里:官方把它挂在 Agent 上,因护栏通常与该 Agent 职责绑定。
  2. output 与 input 对称地设 parallel:output 不支持该参数。
  3. tripwire 与 raise 混用不清:函数内未捕获的异常与返回 tripwire 的会话持久化语义不同,按文档区分。
  4. handoff 后抱怨「护栏没拦」:检查是否只挂在被交接专家上,而脏数据来自链首。
  5. 用同一贵模型做护栏:失去「快/便宜预检」的成本意义;护栏模型应更便宜或规则化。

与 sessions / handoffs 的协作位

层解决什么和护栏的交接面
sessions跨轮记忆output trip 后哪些 item 入史,以官方为准
handoffs本轮说话权input 不随交接自动重跑
guardrails硬门禁不替代鉴权与审批

实践顺序:先定入口与终答是谁,再挂 input/output,最后给副作用 tool 加护栏/审批。反过来先堆专家再补门禁,演示能过、生产事故率高。

最小审计字段

建议至少落盘:ts、guardrail_name、kind=input|output|tool、tripwire、agent_name、run_id、input_fingerprint(哈希即可,勿存密钥原文)。验收:五分钟内能回答「谁在何时因何拦住」。

十分钟 smoke 剧本

  1. 正常客服问:应放行。
  2. 数学作业问:input trip。
  3. 打开 blocking,确认 trip 时无主 Agent 工具日志。
  4. 故意让终答含「应拒字段」,确认 output trip。
  5. 对写库 tool 注入含 sk- 的参数,确认 tool input 拒绝。

五步记入 _w/guardrails-smoke-checklist.md。

总结

guardrails 的工程价值是可触发的硬门禁与清晰的运行位置,不是「又多了一层提示词」。选型先问:拦的是入口、终答还是每次工具?有副作用是否 blocking/审批?生产禁区的共同主题是:护栏通过 ≠ 授权成功 ≠ 副作用安全。把这三层拆开,护栏才能从演示走进可运维。

FAQ:护栏失败后用户侧怎么回?

不要把异常栈直接甩给终端用户。推荐模式:捕获 InputGuardrailTripwireTriggered 后写入审计,再返回固定拒答模板(可含工单号)。输出侧同理:用业务可读的「该回复未通过策略检查」替换原始候选。若使用 RunConfig.output_guardrail_blocked_message,保持占位符不含敏感原文。

FAQ:多轮对话里 input 护栏会每次都跑吗?

对「每次 Runner.run 的链首 Agent」会按该次输入检查。把上一轮 to_input_list() 拼进下一轮时,护栏看到的是你传入的整段输入视图——因此不要假设「第一轮过了,第二轮带毒后缀也会自动安全」。把新用户回合的脏数据追加进来后,仍依赖链首 input。

规则护栏与模型护栏怎么混用?

成本敏感路径优先正则/允许列表(例如检测到 sk-、内网 URL、明确越权动词)。模型护栏留给语义偏题与绕过尝试。混用时注意:规则护栏应短、确定、可单测;模型护栏要固定 output_type,避免自由散文当判定。两者都要能单独 trip,而不是互相「参考意见」。

预发环境必测的误杀与漏杀

至少各留 5 条金标:应放行的难例(含专业术语、投诉语气)与应拦截的攻击/偏题。记录精确率不够时,先调护栏指令与阈值,再考虑换模型。禁止上线后用真实用户当第一次误杀样本却无回滚开关。

发布前配置审一眼

  • 链首 Agent 的 input_guardrails 非空(若该入口暴露给公网用户)。
  • 所有写操作 FunctionTool 带 tool input 护栏或 approval。
  • 无「parallel input + 无审批写库」组合。
  • 文档写明:演示用模型名非生产承诺;tripwire 日志保留周期。
  • 与 handoffs 同图评审:标出链首与终答节点。

观测与回放

打开 tracing 后,回放材料应能同时看到:护栏函数名、tripwire 布尔、主 Agent 是否已产生 tool 事件。若 blocking 配置下仍出现主 Agent tool 事件,先怀疑配置未生效或跑了旧进程。把结论写进 _w/guardrails-docs-notes.md。

与官方「护栏 + 人类审批」指南对齐

Guardrails and human review 把自动检查与人工审批并列:前者决定继续/停止,后者在敏感动作上暂停。工程上不要把审批做成「另一个 LLM 护栏」却无人类出口;也不要只有人类审批却对明显越狱入口零自动拦。两条门禁都要出现在 runbook。

输出护栏与 session 持久化(工程提醒)

官方对 output tripwire 与「护栏函数抛异常」的会话写入语义做了区分:tripwire 拒绝候选终答时,可能保留已完成的工具调用回放所需信息,同时排除被拒终答;异常路径则按另一套规则处理。你的应用层不要假设「一 trip 就等于整轮从未发生」。写审计时同时记录:是否 tripwire、是否有 tool 事件、session 是否已落盘。流式跑法下,终端异常仍可能从 stream_events() 冒出——务必排空并统一捕获。

工具护栏最小示例(思路)

对自定义 @tool:入参侧拒绝含密钥模式的 JSON;出参侧发现敏感片段则 reject_content。注意:handoff 调用本身不走 function-tool 护栏;Agent.as_tool() 亦不一定暴露同等选项。把「工具名单」与「护栏覆盖名单」做成同一张表,缺失即发布阻塞。

成本账

每次 input 护栏若再跑一个 Agent,等于双倍入口成本。策略:规则预筛 → 小模型语义护栏 → 主模型。把护栏模型固定为便宜快照,并在账单里单独标签 guardrail,否则你看不见门禁税。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐