Plano 提示词护栏(Prompt Guardrails)实战:基于 Filter Chain 构建输入安全与合规检查层
【免费下载链接】planoPlano is an AI-native proxy server and data plane for agentic apps. Smart LLM routing, observability, agent orchestration, and guardrails so you stay focused on your agents core logic.项目地址: https://gitcode.com/GitHub_Trending/ar/plano
Guardrails(护栏)是 Plano 数据平面中在提示词进入应用逻辑之前施加安全检查与验证的一层机制,通常以 Filter Chain 中可复用的过滤器形式挂载到 Agent 上,使每个请求都流经一致的校验管线。本文以 TechCorp 客服场景的领域限定输入护栏为例,完整讲解护栏的动机、MCP/HTTP 两种过滤器实现方式、Plano 配置接线方法、拒绝响应语义与测试验证,并结合仓库源码揭示内置prompt_guards的 jailbreak 检测实现与过滤器编排最佳实践,帮助读者为生产级 Agent 构建可插拔、可观测、可审计的安全边界。
为什么需要护栏(Why Guardrails)
护栏是维持 AI 应用可控性的关键设施。它们帮助企业强制执行组织策略、满足 GDPR 或 HIPAA 等合规要求,并保护用户免受有害或不恰当内容的影响。在提示词会触发响应或动作的应用中,护栏将恶意输入、离题查询或不一致输出的风险降到最低——为交互增加一层一致的输入审查,使交互更安全、更可靠、更易于推理。
从仓库中的内置能力与工程实践看,Plano 护栏主要解决三类问题:
- Jailbreak 预防(Jailbreak Prevention):检测并过滤试图改变 LLM 行为、泄露系统提示词或绕过安全策略的输入。Plano 为此提供了内置的
prompt_guards配置,其模式定义在 配置 Schema 中,对应请求/响应数据结构实现于 prompt_guard.rs。 - 领域与主题限制(Domain and Topicality Enforcement):确保 Agent 只响应批准领域内的提示词(例如仅限金融或仅限医疗场景),拒绝无关查询——这正是本文 TechCorp 示例的核心场景。
- 动态错误处理(Dynamic Error Handling):请求违反策略时给出清晰、可操作的错误消息,帮助用户修正输入,而非返回笼统的 4xx。
护栏的工作原理:MCP 过滤器与 HTTP 过滤器
护栏既可以实现为进程内(in-process)的 MCP 过滤器,也可以实现为基于 HTTP 的过滤器。HTTP 过滤器是外部服务,通过 HTTP 接收请求、执行校验,并返回允许或拒绝请求的响应——这让过滤器可以用任何语言编写,或以独立服务的形式运行,天然支持多语言团队与独立部署。
每个过滤器接收聊天消息(chat messages),按策略进行评估,然后选择放行请求,或通过抛出ToolError(或返回错误响应)来拒绝请求并附带有用的错误信息。请求在到达 Agent 或上游 LLM 之前,会按顺序流经整条 Filter Chain,每一个过滤器都可能:
- 检查传入的提示词、元数据与对话状态;
- 变更或丰富请求(如重写查询、构建上下文);
- 短路流程、提前返回响应(如合规失败时阻断请求);
- 输出结构化日志与 trace,便于调试与持续改进。
正如 Filter Chain 概念文档 所描述的,过滤器通过 HTTP 状态码表达处理结果:
- HTTP 200(成功):过滤器成功处理请求;若过滤器变更了请求(如重写查询、丰富上下文),变更会向下游传递。
- HTTP 4xx(用户错误):请求违反过滤器规则(如内容审核策略、合规检查)。请求被终止,错误返回给调用方。这不是致命错误,而是预期的策略执行。
- HTTP 5xx(致命错误):过滤器自身出现意外故障(崩溃、配置错误等)。Plano 将错误反馈给调用方,并记录到日志与 trace 中。
这套语义让护栏可以执行策略(4xx)而不拖垮整个系统,同时将关键故障(5xx)暴露出来供排查。数据平面执行层的核心实现在 llm_gateway 的 filter_context.rs,它基于 Envoy WASM 在请求处理上下文中维护过滤器调用(callouts)映射,异步接收各过滤器的 HTTP 响应并继续流水线。
编写第一个护栏:FastMCP 领域校验过滤器
下面以 TechCorp 客服系统的输入护栏为例,它校验查询是否落在公司领域内。使用 FastMCP 将护栏暴露为 MCP 工具:
from typing import List from fastmcp.exceptions import ToolError from . import mcp @mcp.tool async def input_guards(messages: List[ChatMessage]) -> List[ChatMessage]: """Validates queries are within TechCorp's domain.""" # Get the user's query user_query = next( (msg.content for msg in reversed(messages) if msg.role == "user"), "" ) # Use an LLM to validate the query scope (simplified) is_valid = await validate_with_llm(user_query) if not is_valid: raise ToolError( "I can only assist with questions related to TechCorp and its services. " "Please ask about TechCorp's products, pricing, SLAs, or technical support." ) return messages代码要点:
- 从消息列表中逆序找到最后一条
user消息作为待校验查询,这保证评估的是用户最近一次意图; - 校验通过则原样返回
messages,让请求继续流转; - 校验失败则抛出
ToolError,携带对用户友好、可操作的提示信息,指明可提问的范围。
将护栏接入 Plano:Filter 定义与 Filter Chain 配置
要把这个护栏接入 Plano,先在配置中定义过滤器,再把它加入 Agent 的 filter chain:
filters: - id: input_guards url: http://localhost:10500 listeners: - type: agent name: agent_1 port: 8001 router: plano_orchestrator_v1 agents: - id: rag_agent description: virtual assistant for retrieval augmented generation tasks filter_chain: - input_guards当请求到达agent_1时,Plano首先调用input_guards过滤器。校验通过则请求继续流向 Agent;校验失败(抛出ToolError)则 Plano 向调用方返回错误响应,被拒绝的查询永远不会到达 Agent。
最小配置与可选字段
根据 Filter Chain 编程模型,定义过滤器时以下字段都是可选的,实际起步通常只需id与url:
type:控制过滤器运行时,mcp表示 Model Context Protocol 过滤器,http表示纯 HTTP 过滤器,默认mcp;transport:控制 Plano 与过滤器的通信方式,默认streamable-http(基于 HTTP 的高效流式交互);标准 HTTP 传输时可省略;tool:指定 Plano 调用的 MCP 工具名,默认取过滤器id;工具名与过滤器 id 一致时可省略。
仓库中的 mcp_filter 示例配置 展示了一条由input_guards、query_rewriter、context_builder组成的三段式过滤链:先做输入安全校验,再重写查询,最后构建检索上下文,并通过model_aliases(fast-llm/smart-llm)配合路由。
模型监听器上的护栏(Model Listener Filter Chain)
Filter Chain 也可以直接挂到model listener上,让直接走 LLM 代理请求(/v1/chat/completions、/v1/responses等)的流量无需 Agent 层也能先过输入护栏:
filters: - id: content_guard url: http://content-guard:10500 type: http model_providers: - model: openai/gpt-4o-mini access_key: $OPENAI_API_KEY default: true listeners: - type: model name: llm_gateway port: 12000 filter_chain: - content_guard这里filter_chain声明在监听器层级(而非按 Agent 声明)。请求到达模型监听器时,Plano 按序执行过滤器,再转发给上游 LLM 提供商;若过滤器拒绝请求(HTTP 4xx),错误直接返回给调用方,LLM 永远不会被调用——这是成本与安全双赢的设计。
仓库中的 model_listener_filter 示例 更进一步区分了input_filters(输入侧content_guard)与output_filters(输出侧output_redactor),即输入安全 + 输出脱敏的双向护栏;对应的 content_guard.py 是一个纯关键词内容安全过滤器,兼容 OpenAI/v1/chat/completions、/v1/responses与 Anthropic/v1/messages三种请求格式,无需调用 LLM,命中黑名单关键词即返回 400。
测试护栏:拒绝越界查询
下面演示护栏的实际效果——拒绝一条关于 Apple Corporation 的查询(超出 TechCorp 领域):
curl -X POST http://localhost:8001/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4", "messages": [ { "role": "user", "content": "what is sla for apple corporation?" } ], "stream": false }'护栏返回的错误响应:
{ "error": "ClientError", "agent": "input_guards", "status": 400, "agent_response": "I apologize, but I can only assist with questions related to TechCorp and its services. Your query appears to be outside this scope. The query is about SLA for Apple Corporation, which is unrelated to TechCorp.\n\nPlease ask me about TechCorp's products, services, pricing, SLAs, or technical support." }响应结构清晰可观测:error标明错误类型、agent指明是哪条过滤器拒绝(input_guards)、status为 400、agent_response携带完整的解释性消息。这样既阻止越界查询触达 Agent,又给用户明确的拒绝原因与修正方向。
纵深:内置 prompt_guards 与编排最佳实践
内置 jailbreak 检测:prompt_guards
除了自定义 Filter Chain,Plano 还提供内置的 jailbreak 检测能力,无需部署任何外部服务。配置结构定义在 配置 Schema,其底层数据结构(任务类型与请求/响应模型)位于 prompt_guard.rs:
PromptGuardTask:任务类型,可选jailbreak、toxicity、both;PromptGuardRequest:携带input(待检文本)与task;PromptGuardResponse:返回toxic_prob、jailbreak_prob概率值与对应的toxic_verdict、jailbreak_verdict布尔判定。
配置示例(来自 filter-guardrails 规则):
version: v0.3.0 prompt_guards: input_guards: jailbreak: on_exception: message: > I'm not able to help with that request. This assistant is designed to help with customer support. Please rephrase your question or contact support@yourdomain.com if you believe this is an error.当内置 jailbreak 检测被触发时,Plano 返回on_exception.message而不是转发请求。注意两点:
prompt_guards对所有 listener 全局生效;如需按 Agent 差异化策略,应使用各 Agent 上的filter_chain;- 拒绝消息必须可操作:空消息或
"Error code 403: guard triggered"这类晦涩消息会让用户困惑、无法重新表述;应说明限制原因并给出可做的替代动作,既改善体验又降低支持负担。
内置检测 + 自定义过滤器的组合
实战中推荐将内置 jailbreak 检测(快速、无需外部服务)与MCP 自定义过滤器(领域限定等附加策略)组合使用:
# Built-in jailbreak detection (fast, no external service needed) prompt_guards: input_guards: jailbreak: on_exception: message: "This request cannot be processed. Please ask about our products and services." # MCP-based custom guards for additional policy enforcement filters: - id: topic_restriction url: http://host.docker.internal:10500 type: mcp transport: streamable-http tool: topic_restriction # Custom filter for domain-specific restrictions listeners: - type: agent name: customer_support port: 8000 router: plano_orchestrator_v1 agents: - id: support_agent description: Customer support assistant for product questions and order issues. filter_chain: - topic_restriction # Additional custom topic filtering过滤器链的编排顺序:护栏优先,丰富靠后
filter_chain是有序的过滤器 id 列表,每个过滤器接收上一个过滤器的输出,顺序具有语义意义。根据 filter-ordering 规则,推荐顺序为:
- 输入护栏(Input guards)——jailbreak 检测、PII 检测、主题限制(尽早拒绝);
- 查询重写(Query rewriting)——规范化或增强用户查询;
- 上下文构建(Context building)——RAG 检索、工具查询、知识注入(开销大);
- 输出护栏(Output guards)——在返回前校验或清理 LLM 响应。
反例是先把context_builder放在input_guards之前:jailbreak 请求会在被阻断前先获得 RAG 增强的上下文——既浪费算力,又存在数据暴露风险。同一条 listener 下不同 Agent 可以有不同过滤链:对外 Agent 全量上护栏,内部管理 Agent 可以跳过部分检查。
延伸阅读
- Filter Chain 概念文档:过滤器可复用工作流步骤、HTTP/MCP 编程模型与状态码语义
- Filter Chain 示例配置 与 Model Listener 双向过滤示例
- filter-guardrails 规则:可操作拒绝消息的写法与
prompt_guards全局语义 - filter-mcp 规则:显式声明
type/transport/tool避免默认值静默误路由 - filter-ordering 规则:守卫在前、丰富在后的链式编排
- 配置 Schema 与 prompt_guard.rs:内置检测的配置结构与数据结构
- llm_gateway 执行上下文:Filter Chain 在 Envoy WASM 数据平面中的运行时实现
【免费下载链接】planoPlano is an AI-native proxy server and data plane for agentic apps. Smart LLM routing, observability, agent orchestration, and guardrails so you stay focused on your agents core logic.项目地址: https://gitcode.com/GitHub_Trending/ar/plano
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考