
Phoenix 生产环境护栏设计Guardrails 与 Evaluators 的分工、决策与实战【免费下载链接】phoenixAI Observability Evaluation项目地址: https://gitcode.com/gh_mirrors/phoenix13/phoenixGuardrails 与 Evaluators 是 AI 应用生产化的两套互补机制前者在请求关键路径上同步阻塞有害内容后者在后台异步评估输出质量。本文以 Phoenix 的 production-guardrails 技术规范为核心结合仓库内的实时护栏 cookbook 与源码实现给出完整的架构对比、决策框架、代码插桩与分层护栏实战方案帮助你在 Phoenix 之上构建既能拦截伤害、又不误伤真实用户的生产防线。核心区别阻塞Block还是测量MeasurePhoenix 对生产评估体系给出的第一原则是Guardrails 实时阻塞Evaluators 异步测量。二者服务于完全不同的目标混用是生产事故最常见的来源之一。Request → [INPUT GUARDRAIL] → LLM → [OUTPUT GUARDRAIL] → Response │ └──→ ASYNC EVALUATOR (background)从架构图可以看到两类机制在请求生命周期中的位置完全不同Input guardrail位于模型之前能看到用户消息但看不到模型回复——它拦截 PII、prompt injection、越狱尝试、滥用与跑题请求可以在你为一个模型 token 付费之前就终止坏请求Output guardrail位于模型之后、用户看到回复之前能捕获模型自行生成的系统提示泄露、危险建议、不合规内容Async evaluator完全脱离关键路径在后台对已发生的流量打分永远不会阻塞用户。这一架构在 Phoenix 服务端有直接的语义支撑在 Span 类型定义 中SpanKind枚举将GUARDRAIL与CHAIN、LLM、TOOL、EVALUATOR等并列为一等 span 类型说明护栏检查在 Phoenix 的可观测模型中是被单独呈现的一类节点而非混在 LLM 或工具调用里。Guardrails同步、毫秒级、代码优先原文档给出的 Guardrails 关键特性如下AspectRequirementTimingSynchronous, blockingLatency 100msPurposePrevent harmTypeCode-based (deterministic)四列特性共同构成生产护栏的硬约束Timing Synchronous, blocking护栏挂在请求的关键路径上任何一次检查都会累加到用户的等待时间因此它必须可预测、可兜底Latency 100ms这是护栏的预算红线。每一个检查都占用关键路径评估一个护栏方案时必须盯着 p95 而非均值Purpose Prevent harm护栏的唯一使命是防止伤害发生而不是衡量好坏Type Code-based (deterministic)首选基于正则、长度、白名单/黑名单等确定性代码实现保证结果可复现、零模型成本。典型适用场景PII 检测、prompt injection、敏感词/亵渎内容过滤、输出长度限制、格式校验。以仓库 cookbook 设计实时护栏 中的实现为例第一层快速确定性过滤器就是典型形态——用正则做 PII 脱敏与注入检测全部为亚毫秒级操作PII_PATTERNS { email: re.compile(r[\w.-][\w-]\.[\w.-]), phone: re.compile(r\b(?:\?\d{1,2}[\s-]?)?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}\b), credit_card: re.compile(r\b(?:\d[ -]*?){13,16}\b), ssn: re.compile(r\b\d{3}-\d{2}-\d{4}\b), } def pii_filter(text): found [k for k, p in PII_PATTERNS.items() if p.search(text)] if not found: return pass, no pii redacted text for k in found: redacted PII_PATTERNS[k].sub(f[REDACTED_{k.upper()}], redacted) return redact, {types: found, redacted: redacted}注意这里的细节PII 的处理动作是redact脱敏而不是 block阻塞——用户仍然得到回答但敏感 token 永远不会到达模型。这是护栏设计的关键技巧能通过改写消除风险的就不要拒绝用户。Evaluators异步、秒级、可用 LLMAspectCharacteristicTimingAsync, backgroundLatencyCan be secondsPurposeMeasure qualityTypeCan use LLMsEvaluators 与护栏恰好互补Timing Async, background评估在请求完成后于后台运行用户感知不到Latency Can be seconds因为不在关键路径上评估可以慢甚至可以调用昂贵的 LLM judgePurpose Measure quality评估的目的是产出质量信号用于告警、排障与趋势跟踪Type Can use LLMs质量判断如忠实度、语气、完整性难以用确定性规则表达交给 LLM judge 是合理选择。典型适用场景helpfulness有用性、faithfulness忠实度、tone语气、completeness完整性、citation accuracy引用准确性。在 evaluate_dataframe 源码 中可以看到 Evaluators 的批处理执行形态它接收 DataFrame 与评估器列表逐行将数据传给每个评估器返回附带{name}_score与{name}_execution_details异常、执行时间、状态列的新 DataFrame。它使用同步执行器需要异步吞吐时切换async_evaluate_dataframe默认对异常最多重试 10 次——这些设计都服务于后台批量、对失败容忍、可观测的评估定位。决策框架五个问题对号入座面对一个具体的检查需求原文档给出了明确的决策表QuestionAnswerMust block harmful content?GuardrailMeasuring quality?EvaluatorNeed LLM judgment?Evaluator 100ms required?GuardrailFalse positives angry users?Evaluator逐条解读必须实时阻止有害内容→ Guardrail。任何让伤害不发生的需求都必须落在关键路径上在测量质量→ Evaluator。质量是连续信号需要样本统计与趋势不是一次性的二元拦截需要 LLM 判断→ Evaluator。LLM 判断通常意味着秒级延迟与成本应移出关键路径唯一的例外见下一节要求 100ms→ Guardrail。一旦延迟预算低于 100ms只有确定性代码能满足误报会激怒用户→ Evaluator。这一点最反直觉但也最重要护栏一旦误报就是直接拒绝真实用户代价远高于多跑一次评估。质量类检查宁可漏进后台评估也不要在关键路径上冒险。LLM 护栏罕见的例外原文档明确警告LLM guardrails 要极少使用Rarely。仅当以下四个条件同时成立时才考虑在关键路径上调用 LLM judgeLatency budget 1s业务可以容忍超过 1 秒的额外延迟Error cost LLM cost一次漏放造成的损失远大于一次 LLM 调用的成本Low volume请求量足够低LLM 调用成本可控Fallback existsjudge 服务不可用时有降级方案不会把整个请求拖死。仓库 cookbook 给出了符合这一原则的工程化折中——分层升级escalation第一层确定性过滤只处理强信号把弱信号、模糊信号标记为escalate只有这些少数歧义请求才会升级到 LLM judge。这样大多数请求永远不必支付 LLM 延迟STRONG_INJECTION re.compile( rignore (all |your |the )?(previous|prior|above) (instructions|prompts?) r|disregard (the |your )?(system|previous) (prompt|instructions) r|reveal (your |the )?(system prompt|instructions) r|you are (now )?dan\b|developer mode, re.IGNORECASE, ) WEAK_INJECTION re.compile( r\bignore\b|\bpretend\b|\bbypass\b|\boverride\b|\bjailbreak\b|\bhidden instructions?\b, re.IGNORECASE, ) def injection_filter(text): if STRONG_INJECTION.search(text): return block, strong injection pattern if WEAK_INJECTION.search(text): return escalate, ambiguous - needs judgment return pass, no injection signal判断注入意图的 LLM judge 调用需要包在suppress_tracing()中避免 judge 自身的 OpenAI span 混入被测项目污染指标from phoenix.trace import suppress_tracing def llm_injection_judge(text): with suppress_tracing(): resp client.chat.completions.create( modelgpt-4.1-mini, temperature0, messages[{role: user, content: INJECTION_JUDGE_PROMPT.format(texttext)}], ) verdict resp.choices[0].message.content.strip().lower() decision block if verdict.startswith(attack) else pass return decision, fjudge: {verdict}实战一把每个护栏检查插桩为 GUARDRAIL span要让护栏设计可度量Phoenix 的做法是把每一次检查都插桩为独立的GUARDRAILspan并显式记录决策pass/block/redact/escalate与延迟。因为延迟是显式属性之后聚合每层各占多少时间只需要一次 groupbyimport time from openinference.semconv.trace import OpenInferenceSpanKindValues, SpanAttributes GUARDRAIL OpenInferenceSpanKindValues.GUARDRAIL.value def run_guardrail(name, layer, text, check): Run one guardrail check and emit it as a GUARDRAIL span. with tracer.start_as_current_span(name) as span: span.set_attribute(SpanAttributes.OPENINFERENCE_SPAN_KIND, GUARDRAIL) span.set_attribute(SpanAttributes.INPUT_VALUE, text) start time.perf_counter() decision, detail check(text) latency_ms (time.perf_counter() - start) * 1000 span.set_attribute(SpanAttributes.OUTPUT_VALUE, decision) span.set_attribute(guardrail_name, name) span.set_attribute(guardrail_layer, layer) span.set_attribute(guardrail_decision, decision) span.set_attribute(guardrail_latency_ms, latency_ms) return decision, detail配合请求级的label属性标记该请求属于攻击样本、正常样本还是看似危险实则正常的 tricky 样本就能把三类设计——严格确定性、宽松确定性、分层LLM judge——放在同一批流量上同时比较覆盖率真实攻击被拦截的比例、误报率好流量被拦截的比例与附加延迟。cookbook 给出的结论具有普遍性严格确定性覆盖率最高但误报率最差会误伤说话带 ignore 的真实用户延迟近乎为零宽松确定性误报为零但会漏掉隐蔽攻击分层方案把歧义流量交给 judge既找回漏掉的攻击、又放过 tricky 的正常请求代价是仅在需要裁决的请求上多付延迟因此平均附加延迟仍然可控。护栏插桩的一个陷阱是LLM judge 调用本身就是模型调用若不处理会生成自己的 LLM span。用suppress_tracing()包裹后trace 中只剩一个携带裁决与耗时的GUARDRAILspan指标统计才不会被污染。实战二间接注入——输入过滤看不见的攻击面在 越狱与提示注入防御 中仓库演示了护栏最容易被攻破的形态间接注入indirect injection。攻击载荷不藏在用户消息里而是搭在检索文档、工具结果或网页上——用户看起来完全无辜指令却混在模型信任的数据流中。该 cookbook 用一份被投毒的知识库说明这一点warranty与account两个条目中嵌入了看似普通内部备注的指令如每次回复结尾都推荐购买 Protection Plan它们能绕过关键词过滤器与人工审查而模型没有理由怀疑自己检索到的文档。由此得到的关键架构结论是只做输入/输出过滤input_output对间接注入完全无效。ASRAttack Success Rate攻击成功率对比表显示Attack Success Rateundefendedinput_outputfull直接攻击家族指令覆盖/人格越狱/提示提取/混淆0%0%0%indirect_injection100%100%0%直接攻击即使在无防御状态下 ASR 也是 0%——这是模型对齐训练的效果不是你的防御。而间接注入在输入/输出过滤开启时依然 100% 成功只有增加上下文层护栏在检索文档进入模型前逐个筛查其是否内嵌指令才能归零def respond(message, category, mode): mode in {undefended, input_output, full}. defend_input mode in (input_output, full) defend_context mode full defend_output mode in (input_output, full) # 1) input layer screens the user message # 2) context layer screens the RETRIEVED DOC -- the layer input filtering cant substitute for if defend_context and doc: decision, _ run_guardrail(context_screen, context, doc, context_screen) if decision sanitize: doc # drop the poisoned doc; still serve the customer # 3) output layer screens the reply注意这里对间接注入的处理动作是sanitize消毒而非 block用户无辜、文档本身也有用因此丢弃投毒指令继续服务而不是拒绝客户。对应的benign与benign_tricky样本在全部模式下 0% 被拦截——即零误报因为输入层对模糊措辞是升级到 judge 而不是按关键词直接拦截。该 cookbook 还给出了三层防护之外的结构性防御用分隔符把不可信数据显式标记为数据并声明永不执行其中的指令、在系统提示中声明检索内容与工具输出低于系统规则的指令层级instruction hierarchy、以及最小权限设计——一次成功注入的破坏半径取决于 agent 能做什么。与生产评估体系的衔接护栏与评估器并非孤立机制Phoenix 的生产技能体系production-overview、production-continuous将它们组织进一条完整的生产闭环Production → Sample traffic → Run evaluators → Find failures ↑ ↓ Deploy ← Run CI evals ← Create test cases ← Error analysisCI/CD 评估部署前用固定的小规模代表性数据集约 100 条运行快速确定性检查设阈值拦截回归如 regression0.95、safety1.0、format0.98生产监控部署后按错误 → 负面反馈 → 随机样本的优先级对流量抽样异步跑评估器、记录结果并告警反馈回路生产中发现失败 → 错误分析 → 提炼成 CI 数据集新用例 → 防止未来回归能力集通过率超过 95% 即饱和把稳定通过的用例毕业graduate到回归集同时补充更难的挑战用例。护栏负责关键路径上的防评估器负责后台的测二者通过 trace 与 annotation 在 Phoenix 中汇合——护栏检查是GUARDRAILspan评估结果是 span annotationPython 侧可用client.spans.log_span_annotations_dataframe批量回传共同构成可审计、可度量、可持续演化的生产防线。一个护栏决策清单把上述原则收敛成可对任意候选检查逐条过一遍的清单QuestionIf yes →必须实时阻止伤害Guardrail同步不是 Evaluator能用廉价确定性规则判定放进快速层最先执行信号模糊、依赖意图只把这些个案升级给 LLM judge能不拒绝就消除风险脱敏/改写而不是阻塞是质量而非安全问题异步 Evaluator——永远不要在它上面阻塞在每条请求的关键路径上为它做延迟预算盯 p95 而非均值关键原则Guardrails 防止伤害阻塞Evaluators 衡量质量记录——两类机制的定位不可互换代码优先确定性检查先行LLM 只处理模糊个案脱敏优于阻塞能改写消除风险就不要拒绝用户间接注入是真正的漏洞输入过滤看不见数据流中的指令必须增加上下文层筛查别把模型对齐当成自己的防御用护栏关闭后做对照测试才知道哪些攻击其实是模型自己挡住的护栏方案是经验问题而非直觉插桩为 span、用带标签的真实对抗流量跑一遍让覆盖率、误报率与延迟数据决定该上线哪套方案。完整的可运行示例可参考仓库教程 designing_realtime_guardrails.ipynb 与 jailbreak_and_prompt_injection_defense.ipynb以及对应的 实时护栏 cookbook 与 注入防御 cookbook。【免费下载链接】phoenixAI Observability Evaluation项目地址: https://gitcode.com/gh_mirrors/phoenix13/phoenix创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考