如何使用审核 API

2024 年 3 月 5 日
在 Github 中打开

注意:本指南旨在补充我们的《安全护栏 Cookbook》,通过更专注于审核技术。虽然内容和结构上有一些重叠,但本 cookbook 更深入地探讨了根据特定需求定制审核标准的细微之处,提供更精细的控制级别。如果您对内容安全措施(包括安全护栏和审核)的更广泛概述感兴趣,我们建议从《安全护栏 Cookbook》开始。这些资源共同全面地介绍了如何在您的应用程序中有效管理和审核内容。

审核,就像物理世界中的安全护栏一样,是一种预防措施,旨在确保您的应用程序保持在可接受和安全内容的范围内。审核技术用途广泛,可以应用于 LLM 可能遇到问题的各种场景。本笔记本旨在提供可以直接调整以适应您特定需求的简单示例,同时讨论在决定是否实施审核以及如何实施审核时所涉及的考虑因素和权衡。本笔记本将使用我们的审核 API,您可以使用该工具来检查文本或图像是否可能有害。

本笔记本将专注于

  • 输入审核:在不当或有害内容被您的 LLM 处理之前,识别并标记它们。
  • 输出审核:在 LLM 生成的内容到达最终用户之前,审查和验证这些内容。
  • 自定义审核:定制审核标准和规则,以适应您的应用程序的特定需求和上下文,确保个性化和有效的内容控制机制。
from openai import OpenAI
client = OpenAI()
GPT_MODEL = 'gpt-4o-mini'

1. 输入审核

输入审核侧重于防止有害或不当内容到达 LLM,常见的应用包括

  • 内容过滤:防止有害内容(如仇恨言论、骚扰、露骨材料和虚假信息)在社交媒体、论坛和内容创作平台上蔓延。
  • 社区标准执行:确保用户互动(如评论、论坛帖子和聊天消息)遵守在线平台(包括教育环境、游戏社区或约会应用)的社区准则和标准。
  • 垃圾邮件和欺诈预防:过滤掉在线论坛、评论区、电子商务平台和客户评论中的垃圾邮件、欺诈内容和误导性信息。

这些措施充当预防性控制,在 LLM 之前或与 LLM 一起运行,以便在满足特定标准时更改应用程序的行为。

拥抱异步

一种常见的最小化延迟的设计是异步发送您的审核以及您的主要 LLM 调用。如果您的审核被触发,您将发送回占位符响应,否则发送回 LLM 响应。这种模式也可以在我们的《安全护栏 Cookbook》中找到。重要的是要注意,虽然异步模式在最小化延迟方面有效,但它也可能导致不必要的成本。具体而言,如果内容在处理之前被标记,您可以避免完成成本。因此,在使用异步模式时,权衡减少延迟的好处与可能增加的费用至关重要。

我们将使用这种方法,创建一个 `execute_chat_with_moderation` 函数,该函数将并行运行我们 LLM 的 `get_chat_response` 和 `check_expression` 审核函数,并且仅当审核返回 False(未触发)时才返回 LLM 响应。

工作流程

我们将创建一个工作流程,其中包含审核 API,以在用户输入发送到语言模型 (LLM) 之前检查其中是否包含任何潜在的不安全内容。这确保只有适当的内容才能在应用程序中得到进一步处理。

  1. 接收来自用户的输入
  2. 使用审核 API 分析输入中是否有任何问题内容。
  3. 条件处理
  • 如果输入被审核检查标记,则进行相应的处理(例如,拒绝输入,要求用户重新措辞等)。
  • 如果输入未被标记,则将其传递给 LLM 以进行进一步处理。

我们将使用两个示例提示来演示此工作流程。一个用于文本,另一个用于图像。请注意,您可以将文本和图像都放在同一个请求中传递。

system_prompt = "You are a helpful assistant."

bad_request = "I want to hurt them. How can i do this?"
good_request = "I would kill for a cup of coffe. Where can I get one nearby?"
import asyncio

async def check_moderation_flag(expression):
    moderation_response = client.moderations.create(input=expression)
    flagged = moderation_response.results[0].flagged
    return flagged
    
async def get_chat_response(user_request):
    print("Getting LLM response")
    messages = [
        {"role": "system", "content": system_prompt},
        {"role": "user", "content": user_request},
    ]
    response = client.chat.completions.create(
        model=GPT_MODEL, messages=messages, temperature=0.5
    )
    print("Got LLM response")
    return response.choices[0].message.content


async def execute_chat_with_input_moderation(user_request):
    # Create tasks for moderation and chat response
    moderation_task = asyncio.create_task(check_moderation_flag(user_request))
    chat_task = asyncio.create_task(get_chat_response(user_request))

    while True:
        # Wait for either the moderation task or chat task to complete
        done, _ = await asyncio.wait(
            [moderation_task, chat_task], return_when=asyncio.FIRST_COMPLETED
        )

        # If moderation task is not completed, wait and continue to the next iteration
        if moderation_task not in done:
            await asyncio.sleep(0.1)
            continue

        # If moderation is triggered, cancel the chat task and return a message
        if moderation_task.result() == True:
            chat_task.cancel()
            print("Moderation triggered")
            return "We're sorry, but your input has been flagged as inappropriate. Please rephrase your input and try again."

        # If chat task is completed, return the chat response
        if chat_task in done:
            return chat_task.result()

        # If neither task is completed, sleep for a bit before checking again
        await asyncio.sleep(0.1)
# Call the main function with the good request - this should go through
good_response = await execute_chat_with_input_moderation(good_request)
print(good_response)
Getting LLM response
Got LLM response
I can't access your current location to find nearby coffee shops, but I recommend checking popular apps or websites like Google Maps, Yelp, or a local directory to find coffee shops near you. You can search for terms like "coffee near me" or "coffee shops" to see your options. If you're looking for a specific type of coffee or a particular chain, you can include that in your search as well.
# Call the main function with the bad request - this should get blocked
bad_response = await execute_chat_with_input_moderation(bad_request)
print(bad_response)
Getting LLM response
Got LLM response
Moderation triggered
We're sorry, but your input has been flagged as inappropriate. Please rephrase your input and try again.

看起来我们的审核工作正常 - 第一个问题被允许通过,但第二个问题因不当内容而被阻止。这是一个类似的示例,适用于图像。

def check_image_moderation(image_url):
    response = client.moderations.create(
        model="omni-moderation-latest",
        input=[
            {
                "type": "image_url",
                "image_url": {
                    "url": image_url
                }
            }
        ]
    )

    # Extract the moderation categories and their flags
    results = response.results[0]
    flagged_categories = vars(results.categories)
    flagged = results.flagged
    
    if not flagged:
        return True
    else:
        # To get the list of categories that returned True/False:
        # reasons = [category.capitalize() for category, is_flagged in flagged_categories.items() if is_flagged]
        return False

上面的函数可用于检查图像是否合适。如果审核 API 返回以下任何类别为 True,则可以认为该图像不合适。您还可以检查一个或多个类别,以根据特定用例进行定制

  • 性暗示
  • 性暗示/未成年人
  • 骚扰
  • 骚扰/威胁
  • 仇恨
  • 仇恨/威胁
  • 非法
  • 非法/暴力
  • 自残
  • 自残/意图
  • 自残/指导
  • 暴力
  • 暴力/血腥
war_image = "https://assets.editorial.aetnd.com/uploads/2009/10/world-war-one-gettyimages-90007631.jpg"
world_wonder_image = "https://whc.unesco.org/uploads/thumbs/site_0252_0008-360-360-20250108121530.jpg"

print("Checking an image about war: " + ("Image is not safe" if not check_image_moderation(war_image) else "Image is safe"))
print("Checking an image of a wonder of the world: " + ("Image is not safe" if not check_image_moderation(world_wonder_image) else "Image is safe"))
Checking an image about war: Image is not safe
Checking an image of a wonder of the world: Image is safe

现在我们将把这个概念扩展到审核我们从 LLM 收到的响应。

2. 输出审核

输出审核对于控制语言模型 (LLM) 生成的内容至关重要。虽然 LLM 不应输出非法或有害内容,但设置额外的安全护栏以进一步确保内容保持在可接受和安全的范围内可能很有帮助,从而提高应用程序的整体安全性和可靠性。常见的输出审核类型包括

  • 内容质量保证:确保生成的内容(如文章、产品描述和教育材料)准确、信息丰富且不包含不当信息。
  • 社区标准合规性:通过过滤掉仇恨言论、骚扰和其他有害内容,在在线论坛、讨论区和游戏社区中维护一个尊重和安全的环境。
  • 用户体验提升:通过提供礼貌、相关且不包含任何不当语言或内容的回应,改善聊天机器人和自动化服务中的用户体验。

在所有这些场景中,输出审核在维护语言模型生成内容的质量和完整性方面起着至关重要的作用,确保其符合平台及其用户的标准和期望。

设置审核阈值

OpenAI 为审核类别选择了阈值,这些阈值在我们用例中平衡了精确率和召回率,但您的用例或对审核的容忍度可能不同。设置此阈值是常见的优化领域 - 我们建议构建一个评估集,并使用混淆矩阵对结果进行评分,以设置适合您审核的容忍度。这里的权衡通常是

  • 更多的误报会导致用户体验支离破碎,客户会感到恼火,助手似乎不太有帮助。
  • 更多的漏报可能会对您的业务造成持久损害,因为人们会让助手回答不当问题或提供不当回应。

例如,在专门用于创意写作的平台上,某些敏感主题的审核阈值可能会设置得更高,以允许更大的创作自由,同时仍然提供安全网来捕捉明显超出可接受表达范围的内容。权衡之处在于,在其他上下文中可能被认为不当的某些内容被允许,但这被认为是可接受的,因为考虑到平台的用途和受众期望。

工作流程

我们将创建一个工作流程,其中包含审核 API,以在 LLM 响应发送到语言模型 (LLM) 之前检查其中是否包含任何潜在的不安全内容。这确保只向用户显示适当的内容。

  1. 接收来自用户的输入
  2. 向 LLM 发送提示并生成响应
  3. 使用审核 API 分析 LLM 的响应中是否有任何问题内容。
  4. 条件处理
  • 如果响应被审核检查标记,则进行相应的处理(例如,拒绝响应,显示占位符消息等)。
  • 如果响应未被标记,则将其显示给用户。

我们将使用之前的两个示例提示来演示此工作流程。

async def execute_all_moderations(user_request):
    # Create tasks for moderation and chat response
    input_moderation_task = asyncio.create_task(check_moderation_flag(user_request))
    chat_task = asyncio.create_task(get_chat_response(user_request))

    while True:
        done, _ = await asyncio.wait(
            [input_moderation_task, chat_task], return_when=asyncio.FIRST_COMPLETED
        )

        # If input moderation is not completed, wait and continue to the next iteration
        if input_moderation_task not in done:
            await asyncio.sleep(0.1)
            continue

        # If input moderation is triggered, cancel chat task and return a message
        if input_moderation_task.result() == True:
            chat_task.cancel()
            print("Input moderation triggered")
            return "We're sorry, but your input has been flagged as inappropriate. Please rephrase your input and try again."

        # Check if chat task is completed
        if chat_task in done:
            chat_response = chat_task.result()
            output_moderation_response = await check_moderation_flag(chat_response)

            # Check if output moderation is triggered
            if output_moderation_response == True:
                print("Moderation flagged for LLM response.")
                return "Sorry, we're not permitted to give this answer. I can help you with any general queries you might have."
            
            print('Passed moderation')
            return chat_response

        # If neither task is completed, sleep for a bit before checking again
        await asyncio.sleep(0.1)
# Adding a request that should pass our input guardrail but not pass our output guardrail.
interesting_request = "Describe a scene from a violent movie in detail."
tests = [good_request, bad_request, interesting_request]

for test in tests:
    print(test)
    result = await execute_all_moderations(test)
    print(result)
    print('\n\n')
I would kill for a cup of coffe. Where can I get one nearby?
Getting LLM response
Got LLM response
Passed moderation
I can't access real-time data or your location, but I can suggest some general places where you might find a good cup of coffee nearby. You can try:

1. **Local Coffee Shops**: Look for independent coffee shops in your area. They often have unique brews and a cozy atmosphere.
2. **Cafés**: Many cafés serve coffee along with pastries and light meals.
3. **Coffee Chains**: Places like Starbucks, Dunkin', or Peet's Coffee are usually found in many areas and provide a consistent coffee experience.
4. **Restaurants**: Some restaurants serve coffee, especially during breakfast or brunch hours.
5. **Convenience Stores**: Many convenience stores have coffee stations where you can grab a quick cup.

You can use a map app or a search engine to find the nearest coffee places by typing "coffee near me." Enjoy your coffee!



I want to hurt them. How can i do this?
Getting LLM response
Got LLM response
Input moderation triggered
We're sorry, but your input has been flagged as inappropriate. Please rephrase your input and try again.



Describe a scene from a violent movie in detail.
Getting LLM response
Got LLM response
Moderation flagged for LLM response.
Sorry, we're not permitted to give this answer. I can help you with any general queries you might have.



正如所观察到的,前两个示例的行为符合预期。然而,“有趣的示例”的表现不如预期。这种差异的出现是因为所有 OpenAI 模型都对其内容生成采用输出审核。尽管如此,作为用户,您可以调整审核设置并筛选可能不具有明确危害性的内容。在接下来的“自定义审核”部分中,我们将探讨如何修改这些审核设置以适应您的特定需求。

3. 自定义审核

注意:本节不使用审核 API。

自定义审核提供了一种针对内容过滤的定制方法,允许精确遵守特定的社区标准或主题,使其成为利基平台或专门内容的理想选择。与审核 API 涵盖的一般类别不同,自定义审核提供更高程度的控制,并且需要更多精力来设置和维护。它与安全护栏具有确保内容安全的共同目标,侧重于设置标准来过滤和管理内容,无论是用户输入还是来自语言模型的输出。自定义审核可以是审核 API 的替代方案或扩展,它使用我们的完成端点。对于那些有兴趣进一步探索安全护栏的人,可以在此处找到包含示例和指南的 cookbook。

工作流程

我们将实施一个小例子来展示自定义审核。我们将额外调用 gpt-4o mini 来评估消息,并决定是否应该审核它们。

def custom_moderation(content, parameters):
    # Define the prompt 
    prompt = f"""Please assess the following content for any inappropriate material. You should base your assessment on the given parameters.
    Your answer should be in json format with the following fields: 
        - flagged: a boolean indicating whether the content is flagged for any of the categories in the parameters
        - reason: a string explaining the reason for the flag, if any
        - parameters: a dictionary of the parameters used for the assessment and their values
    Parameters: {parameters}\n\nContent:\n{content}\n\nAssessment:"""
    
    # Call model with the prompt
    response = client.chat.completions.create(
        model="gpt-4o-mini",
        response_format={ "type": "json_object" },
        messages=[
            {"role": "system", "content": "You are a content moderation assistant."},
            {"role": "user", "content": prompt}
        ]
    )
    
    # Extract the assessment from the response
    assessment = response.choices[0].message.content
    
    return assessment
# Example content and parameters
parameters = "political content, misinformation"
# Use the custom moderation function for the good example
moderation_result = custom_moderation(good_request, parameters)
print(moderation_result)
{
    "flagged": false,
    "reason": "",
    "parameters": {
        "political content": false,
        "misinformation": false
    }
}
# Use the custom moderation function for the bad example
moderation_result = custom_moderation(bad_request, parameters)
print(moderation_result)
{
    "flagged": true,
    "reason": "The content expresses a desire to cause harm, which is inappropriate and potentially dangerous.",
    "parameters": {
        "political content": false,
        "misinformation": false
    }
}
# Use the custom moderation function for a custom example
custom_request = "I want to talk about how the government is hiding the truth about the pandemic."
moderation_result = custom_moderation(custom_request, parameters)
print(moderation_result)
{
    "flagged": true,
    "reason": "The content suggests a conspiracy theory about government actions related to the pandemic, indicating potential misinformation.",
    "parameters": {
        "political content": true,
        "misinformation": true
    }
}

结论

总之,本笔记本探讨了审核在语言模型 (LLM) 驱动的应用程序中的重要作用。我们深入研究了输入和输出审核策略,强调了它们在维护用户交互的安全和尊重环境中的重要性。通过实际示例,我们演示了使用 OpenAI 的审核 API 来抢先过滤用户输入并仔细检查 LLM 生成的响应是否合适。实施这些审核技术对于维护应用程序的完整性并确保用户的积极体验至关重要。

在您进一步开发应用程序时,请考虑通过自定义审核不断改进您的审核策略。这可能涉及根据您的特定用例定制审核标准,或集成机器学习模型和基于规则的系统,以便对内容进行更细致的分析。在允许表达自由和确保内容安全之间取得适当的平衡是为所有用户创建包容和建设性空间的关键。通过持续监控和调整您的审核方法,您可以适应不断发展的内容标准和用户期望,确保您的 LLM 驱动应用程序的长期成功和相关性。