PII、越狱和真正的护栏

在这篇文章中,我探讨了护栏实际上应该做什么——以及不同的框架如何处理这个问题。

PII、越狱和真正的护栏
AI编程/Vibe Coding 遇到问题需要帮助的,联系微信 ezpoda,免费咨询。

我们正在构建越来越多的 AI 智能体来处理真实的公司数据。这些数据通常包含 PII(个人身份信息)——电子邮件地址、电话号码、出生日期和客户案例描述。即使拥有企业 LLM 许可证,一个令人不安的问题仍然存在:

我们在法律上和技术上是否允许将那些数据发送到外部 LLM 提供商?

许多团队假设"企业许可证 = 安全"。但合规性、日志记录、痕迹存储和数据驻留权讲述了一个更复杂的故事。

一周前,我观看了 Aimable 的发布会演示——这是一个承诺提供安全 AI 工作空间的产品。这触发了我一直推迟的一个问题:

我们应该如何在我们自己构建的应用程序中正确实现护栏?

不仅仅是审核。不仅仅是越狱保护。真正的护栏包括 PII 掩码、幻觉检测、工具安全和内容过滤。

在这篇文章中,我探讨了护栏实际上应该做什么——以及不同的框架如何处理这个问题。

1、护栏做什么?

并非每个框架都在同一水平上。我讨论了我认为护栏应该做的事情。最初的职责是与 LLM 安全地工作。这意味着用户不能尝试破解提示词,使系统执行智能体开发者不希望它做的事情。护栏确保我们使用礼貌的语言,避免向 LLM 询问错误的事情,以及 LLM 的回答是礼貌的。

护栏有不同的形式。有些充当应用程序和 LLM 之间的代理。不是指向 OpenAI,而是指向代理。在开发过程中,这可以是一个 Docker 镜像;对于生产环境,您可以在 Kubernetes 中创建服务或使用 SAAS 版本。支持开源产品的公司通常提供具有更多功能的付费 SAAS 版本。特别是对于 PII 掩码,我更喜欢本地解决方案;将数据发送到远程服务进行掩码,然后让 LLM 搜索该 PII 感觉很奇怪。使用代理很容易;但是,它给您更少的控制权。这些产品的示例是 LLM GuardGuardrails AI

另一种方法是使用 API 来验证或掩码内容。在将内容提供给智能体或 LLM 之前,您将其通过审核和掩码服务。在收到智能体的响应后,您执行相同的操作。这给您更多的控制权,而通过这种控制权,您承担更多的责任。您必须为每次与智能体的交互自己安排这种保护行为。

第三种方法是使用可用的框架。大多数框架,如 SpringAI、Langchain4j 和 Embabel,都包含护栏的实现。它们旨在在不必自己完成所有事情的情况下给您相同的控制水平。它们通常使用通用接口包装特定的实现。命名不同的功能并不一致。有些使用护栏,其他使用审核,还有其他的为 PII 掩码等功能提供了回调机制。

2、阻止或不阻止

护栏可以阻止进行到下一步,从而防止调用 LLM。典型的用例是越狱护栏。这个护栏的目的是防止用户绕过提示词的规则,使智能体做它不被允许做的事情。另一个例子是审核护栏,它检测可能是骚扰、仇恨、自残或暴力的内容。这种类型的护栏用于用户请求以及助手响应。幻觉护栏就是一个例子。这个护栏检查 LLM 返回的内容,以确保它来自我们提供的上下文,而不是 LLM 刚刚编造的东西。

护栏也可以具有非阻止功能。PII 检测就是这里最好的例子。如果内容包含诸如电子邮件地址或出生日期之类的信息,我们要在将其发送到 LLM 之前掩码它。最好的方法是用占位符替换内容。您可以添加功能以保留占位符值,并在返回响应时用该值替换它。

3、工具参数和响应

用户和助手是对话的主要贡献者;然而,工具和组件(如技能和 MCP)也向提示词添加内容。对进入工具的信息以及工具的响应值应用护栏很重要。一个问题是一个恶意的 MCP 服务器试图诱骗您的智能体透露它不应该透露的信息。

4、一个还是两个端点?

我个人的偏好是使用两种机制:一种来审核内容并返回 true 或 false 值。不更改内容。另一种是执行诸如 PII 掩码、翻译或修剪大提示词之类的操作。

是时候看一些例子了。

5、Guardrails AI

我专注于 Guardrails AI 的开源版本。在跳转到代码之前,请访问 Guardrails AI Hub 以查看有哪些护栏可用。在第一部分中,我构建 Docker 镜像;在第二部分中,我使用它来实验护栏。

6、与 API 交互

我采取的大多数步骤来自 GuardrailsAI 部署页面。一切都从创建账户并获取密钥开始。

https://guardrailsai.com/hub/keys

接下来,我们使用 uv 创建项目,添加一些依赖项,并激活 Python 虚拟环境以使用 guardrails CLI。

uv init guardrails-ai-tryout
cd guardrails-ai-tryout
uv add "guardrails-ai[api]"
source .venv/bin/activate

有了可用的 guardrails cli,我们配置项目,添加护栏,并配置 config.py。护栏来自我之前提到的中心。

guardrails configure
guardrails hub install hub://guardrails/guardrails_pii
# config.py
from guardrails import Guard
from guardrails.hub import GuardrailsPII

pii_check = Guard(
    name="pii",
    description="Checks if the string contains specific PII data."
).use(
    GuardrailsPII(entities=["EMAIL_ADDRESS","PERSON","DATE_TIME"], on_fail="fix")
)

这就是在本地运行服务器并检查它是否工作所需的全部内容。

guardrails start --config=./config.py

# Use the swagger-ui to send a message
http://localhost:8000/docs

如果 guardrails 应用程序的启动成功,您应该看到与此类似的内容。

🚀 Guardrails API is available at http://localhost:8000
📖 Visit http://localhost:8000/docs to see available API endpoints.

🟢 Active guards and OpenAI compatible endpoints:
- Guard: pii http://localhost:8000/guards/pii/openai/v1

========================================================================================= Server Logs ==========================================================================================
INFO:     Started server process [31365]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)

注意 OpenAI 的代理 URL。目前,我用 curl 演示验证端点。您必须传递 openai api 密钥。下一个代码块显示了带有特定标头的 curl 请求。

curl -X 'POST' \
  'http://localhost:8000/guards/pii/validate' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -H 'x-openai-api-key: $GUARDRAILS_API_KEY' \
  -d '{
  "llmOutput": "Ik ben op 1 November 2014 gestart bij Luminis. Mijn eerste taak was het ontwikkelen een PII systeem om email adressen als jettro@donotuse.eu te identificeren.",
  "numReasks": 0,
  "llmApi": "openai.Completion.create"
}'

响应是以下 JSON 消息。注意占位符。如果我们得到一个包含替换值的数组也会很好。

{
  "callId":"281469803946992",
  "rawLlmOutput":"Ik ben op 1 November 2014 gestart bij Luminis. Mijn eerste 
     taak was het ontwikkelen een PII systeem om email adressen als 
     jettro@donotuse.eu te identificeren.",
  "validatedOutput":"Ik ben op <DATE_TIME> gestart bij Luminis. Mijn eerste 
    taak was het ontwikkelen een PII systeem om email adressen als 
    <EMAIL_ADDRESS> te identificeren.",
  "validationPassed":true
}

您也可以为 OpenAI 使用代理。

curl -X 'POST' \
  'http://localhost:8000/guards/pii/openai/v1/chat/completions' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "model": "gpt-4o-mini",
  "messages": [
    {
      "role": "user",
      "content": "This is a test message for Jettro Coenradie, how are you doing?"
    }
  ],
  "max_tokens": 1000,
  "temperature": 1
}'

响应有一个问题:它不返回 ID。根据规范,此字段是必需的。实际响应未显示它。因此,目前使用 Spring AI 和代理不起作用。

{
  "choices": [
    {
      "message": {
        "content": "<PERSON>'m just a computer program, so <PERSON> don't have feelings, but <PERSON>'m here to help you! If you have any questions or need assistance, feel free to ask. How can <PERSON> help you today?"
      }
    }
  ],
  "guardrails": {
    "reask": null,
    "validation_passed": true,
    "error": null
  }
}

我创建了一个 问题,如果有什么变化,我会在这里更新。

请注意,结果包含一个字段 validation_passed。他们对两种类型的护栏都使用一个端点。

7、构建 Docker 镜像

我想拥有一个 Docker 镜像来运行 Guardrails 服务。可以下载 Docker 配置文件。

Docker 需要一个 requirements-lock.txt 文件。此文件使用 uv 生成。

uv export --format requirements-txt --hashes --output-file requirements-lock.txt

在使用此命令运行它之前构建镜像。

docker build -f Dockerfile -t "guardrails-server:latest" \
    --build-arg GUARDRAILS_TOKEN=<your-token> .

# Run the Docker container
docker run -p 8000:8000 -d guardrails-server:latest

8、OpenAI

OpenAI 提供多种形式的护栏。其中一个 API 是 审核。这是服务器端审核内容的方法。审核输入和输出以删除不良内容很有用。该服务针对有害类别(如暴力、仇恨和自残)对内容进行分类。该 API 与 SDK 很好地集成。因此,Java SDK 也可以访问该 API。

另一种形式是 Python 库 OpenAI Guardrails。该库使用开源 Microsoft 框架 Presidio。该库提供了聊天客户端的直接替代品,强制执行配置文件中定义的所有护栏。例如:PII、Jailbreak、NSFW。

一个缺点是它仅适用于 Python。如果您像我大多数时间一样在 JVM 领域,那就太可惜了。

让我们试试这个框架。创建一个新项目,添加所需的依赖项,生成配置并尝试一下。

uv init guardrails-openai-tryout
cd guardrails-openai-tryout
uv add openai-guardrails
uv add python_dotenv

添加一个包含 OPENAI_API_KEY 属性的 .env 文件

接下来,转到 https://guardrails.openai.com/ 并选择您要应用的护栏。我选择:

  • 输入:掩码 PII、审核、越狱
  • 输出:NSFW

接下来,您可以下载配置,您将获得一个默认的代码实现块。以下是配置、代码和输出。注意护栏是如何配置的,并检查最终结果中的掩码。

{
  "version": 1,
  "pre_flight": {
    "version": 1,
    "guardrails": [
      {
        "name": "Contains PII",
        "config": {
          "entities": [
            "CREDIT_CARD",
            "CVV",
            "CRYPTO",
            "DATE_TIME",
            "EMAIL_ADDRESS",
            "IBAN_CODE",
            "BIC_SWIFT",
            "IP_ADDRESS",
            "LOCATION",
            "MEDICAL_LICENSE",
            "NRP",
            "PERSON",
            "PHONE_NUMBER",
            "URL"
          ]
        }
      },
      {
        "name": "Moderation",
        "config": {
          "categories": [
            "sexual",
            "sexual/minors",
            "hate",
            "hate/threatening",
            "harassment",
            "harassment/threatening",
            "self-harm",
            "self-harm/intent",
            "self-harm/instructions",
            "violence",
            "violence/graphic",
            "illicit",
            "illicit/violent"
          ]
        }
      }
    ]
  },
  "input": {
    "version": 1,
    "guardrails": [
      {
        "name": "Jailbreak",
        "config": {
          "confidence_threshold": 0.7,
          "model": "gpt-4.1-mini",
          "include_reasoning": false
        }
      }
    ]
  },
  "output": {
    "version": 1,
    "guardrails": [
      {
        "name": "NSFW Text",
        "config": {
          "confidence_threshold": 0.7,
          "model": "gpt-4.1-mini",
          "include_reasoning": false
        }
      }
    ]
  }
}
import asyncio
from pathlib import Path

from dotenv import load_dotenv
from guardrails import GuardrailsAsyncOpenAI, GuardrailTripwireTriggered
from openai.types.chat import ChatCompletionUserMessageParam

async def call_llm(user_input):
    try:
        # GuardrailsAsyncOpenAI is a drop in replacement for the AsyncOpenAI client
        response = await guardrails_client.chat.completions.create(
            messages=[ChatCompletionUserMessageParam(content=user_input, role="user")],
            model="gpt-4.1-mini",
        )

        print(f"Assistant: {response.choices[0].message.content}")

    # If a guardrail is triggered, an exception will be raised
    except GuardrailTripwireTriggered as exc:
        raise

if __name__ == "__main__":
    load_dotenv()

    # Initialize GuardrailsAsyncOpenAI with the config file
    guardrails_client = GuardrailsAsyncOpenAI(config=Path("guardrails_config.json"))

    asyncio.run(call_llm("I need a bit formal message to Jettro Coenradie, I want to congratulate him for his anniversary since 1 november 2014."))
Assistant: Certainly! Here's a formal congratulatory message you can use:

---

Dear <PERSON>,

I hope this message finds you well. I would like to extend my sincere 
congratulations on your anniversary since <DATE_TIME>. Your dedication and 
commitment are truly commendable, and I wish you continued success in your 
endeavors.

Warm regards,  
[Your Name]

---

Let me know if you'd like it tailored further!

我喜欢响应包含大量关于应用的护栏的信息。只需几个简单的打印语句,就可以读取 GuardrailsResponse 以获取有关发现的 PII 和越狱护栏结果的信息。

def print_guardrail_results(results):
    for guardrail_result in results:
        print(f"Guardrail: {guardrail_result.info["guardrail_name"]}")
        if guardrail_result.info["guardrail_name"] == 'Contains PII':
            for key, value in guardrail_result.info["detected_entities"].items():
                print(f"  {key}: {value}")
        if guardrail_result.info["guardrail_name"] == 'Jailbreak':
            print(f"  Flagged: {guardrail_result.info['flagged']}")
            print(f"  Reason: {guardrail_result.info['reason']}")

print("--- Preflight Guardrails ---")
print_guardrail_results(response.guardrail_results.preflight)

print("--- Input flight Guardrails ---")
print_guardrail_results(response.guardrail_results.input)
--- Preflight Guardrails ---
Guardrail: Contains PII
  PERSON: ['Jettro Coenradie']
  DATE_TIME: ['1 november 2014']
Guardrail: Moderation
--- Input flight Guardrails ---
Guardrail: Jailbreak
  Flagged: False
  Reason: The user is requesting assistance in crafting a formal congratulatory message for an anniversary, which is a legitimate and benign request. There are no signs of deception or manipulation in the input.

9、Presidio

Microsoft 的数据保护和去识别化 SDK 有一个名为 Presidio 的东西。大多数其他项目都引用了该项目。我在本文中讨论的两个——OpenAI 和 Guardrails AI——也不例外。Presidio 是一个用于检测和掩码 PII 数据的框架。

Presidio 有两个部分。在第一部分中,您使用分析器检测 PII 数据。其目标是检测 PII 并标记实体的位置。在第二部分中,您对发现的实体进行匿名化。您可以用 *** 替换它们,或使用命名掩码。命名掩码对于 LLM 非常完美;它仍然理解掩码文本的上下文。

Presidio 是一个 Python 库,它使用 Spacy 进行一些实体检测。如果您对 Python 方法感兴趣,请查看 Presidio 的教程

我尝试 Docker 方法。首先,我下载容器。接下来,我启动容器并通过本地端口暴露服务。

docker pull mcr.microsoft.com/presidio-analyzer
docker pull mcr.microsoft.com/presidio-anonymizer

docker run -d -p 5002:3000 mcr.microsoft.com/presidio-analyzer:latest
docker run -d -p 5001:3000 mcr.microsoft.com/presidio-anonymizer:latest

有了运行的容器,我可以发送上一节的数据。

curl -X POST http://localhost:5002/analyze \
-H "Content-Type: application/json" \
-d '{
  "text": "I need a bit formal message to Jettro Coenradie, I want to 
     congratulate him for his anniversary since 1 november 2014.",
  "language": "en"
}' | jq .

Presidio 分析器的响应是:

[
  {
    "analysis_explanation": null,
    "end": 47,
    "entity_type": "PERSON",
    "score": 0.85,
    "start": 31
  },
  {
    "analysis_explanation": null,
    "end": 117,
    "entity_type": "DATE_TIME",
    "score": 0.85,
    "start": 102
  }
]

我使用带有原始文本的响应与 Presidio 匿名化器掩码 PII 数据。

curl -X POST http://localhost:5001/anonymize \
-H "Content-Type: application/json"  \
-d '{
        "text": "I need a bit formal message to Jettro Coenradie, I want to congratulate him for his anniversary since 1 november 2014.",
        "analyzer_results": [
  {
    "analysis_explanation": null,
    "end": 47,
    "entity_type": "PERSON",
    "score": 0.85,
    "start": 31
  },
  {
    "analysis_explanation": null,
    "end": 117,
    "entity_type": "DATE_TIME",
    "score": 0.85,
    "start": 102
  }]}'

响应现在包含掩码值。

{
  "text": "I need a bit formal message to <PERSON>, I want to congratulate him for his anniversary since <DATE_TIME>.",
  "items": [
    {
      "start": 94,
      "end": 105,
      "entity_type": "DATE_TIME",
      "text": "<DATE_TIME>",
      "operator": "replace"
    },
    {
      "start": 31,
      "end": 39,
      "entity_type": "PERSON",
      "text": "<PERSON>",
      "operator": "replace"
    }
  ]
}

第二个响应并不太难自己创建。Presidio 确实提供其他掩码选项。对我来说,特别是第一个检测 PII 数据的服务很有趣。

10、结束语

我喜欢 OpenAI Guardrails 方法,它使用包含三个元素的管道:预检、输入和输出。当您停留在 OpenAI 空间中时,LLM 类和智能体的直接替代品当然非常方便。用于创建护栏配置的向导很棒,新功能即将推出。有一个在 beta 中使用的工具护栏,对我来说,绝对是一个值得关注的框架。你听到了但是吗?但它不适用于 JVM。

Presidio 仅用于 PII 检测。这不一定是个问题。Guardrails AI 通过 Presidio 提供 PII,并用许多其他护栏扩展它。他们下载现有护栏的中心是一个很好的补充。对我来说,连接到 OpenAI API 的代理不起作用,因为缺少 id 字段。而且响应不包括提取的实体的值。有了输入和输出,当然可以提取该信息。

Presidio 和 Guardrails AI 都是通过 Docker 容器暴露服务变体的 Python 环境。目前作为与 JVM 语言的集成,这很好。在后续博客中,我将在 Spring AI 和 Embabel 中查看这些护栏的集成。两个框架都在积极开发中;我预计在接下来的几周内可以轻松集成。


原文链接: Building AI agents safely: PII, jailbreaks, and real guardrails

汇智网翻译整理,转载请标明出处