Discourse AI - NSFW

:bookmark: Este tema cubre la configuración de la función NSFW (Not Safe For Work) del plugin Discourse AI.

:person_raising_hand: Nivel de usuario requerido: Administrador

Los módulos NSFW pueden clasificar automáticamente la puntuación NSFW de cada nueva carga de imagen en publicaciones y mensajes de chat en tu instancia de Discourse. Este sistema ayuda a identificar y gestionar contenido que puede ser inapropiado o explícito para un entorno profesional o público.

También puedes habilitar el marcado automático de contenido que cruza un umbral.

Configuración

  • ai_nsfw_detection_enabled: Habilita o deshabilita el módulo

  • ai_nsfw_inference_service_api_endpoint: URL donde se está ejecutando la API para el módulo. Si estás utilizando alojamiento CDCK, esto se maneja automáticamente por ti. Si te alojas tú mismo, consulta la guía de autoalojamiento.

  • ai_nsfw_inference_service_api_key: Clave API para la API configurada anteriormente. Si estás utilizando alojamiento CDCK, esto se maneja automáticamente por ti. Si te alojas tú mismo, consulta la guía de autoalojamiento.

  • ai_nsfw_models: El modelo que utilizaremos para la clasificación de imágenes. Ofrecemos dos diferentes con sus umbrales:

    • opennsfw2 devuelve una sola puntuación entre 0.0 y 1.0. Establece el umbral para que una carga se considere NSFW a través de la configuración ai_nsfw_flag_threshold_general.
    • nsfw_detector devuelve puntuaciones para cuatro categorías: drawings, hentai, porn y sexy. Establece el umbral para cada una a través de la configuración respectiva ai_nsfw_flag_threshold_${category}. Si la general es menor que cualquiera de estas, consideraremos el contenido NSFW.
  • ai_nsfw_flag_automatically: Marca automáticamente las publicaciones/mensajes de chat que están por encima de los umbrales configurados. Cuando esta configuración está deshabilitada, solo almacenamos los resultados de la clasificación en la base de datos.

Recursos adicionales

11 Me gusta

Gracias.

Encontré este problema al usar el servicio nsfw, después de subir una imagen. Uso S3 para el almacenamiento de imágenes, si esto es relevante.

INFO:     10.0.2.145:23412 - "POST /api/v1/classify HTTP/1.1" 500 Internal Server Error
ERROR:    Exception in ASGI application
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/dist-packages/anyio/streams/memory.py", line 94, in receive
    return self.receive_nowait()
  File "/usr/local/lib/python3.9/dist-packages/anyio/streams/memory.py", line 89, in receive_nowait
    raise WouldBlock
anyio.WouldBlock

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.9/dist-packages/starlette/middleware/base.py", line 77, in call_next
    message = await recv_stream.receive()
  File "/usr/local/lib/python3.9/dist-packages/anyio/streams/memory.py", line 114, in receive
    raise EndOfStream
anyio.EndOfStream

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.9/dist-packages/uvicorn/protocols/http/h11_impl.py", line 404, in run_asgi
    result = await app(  # type: ignore[func-returns-value]
  File "/usr/local/lib/python3.9/dist-packages/uvicorn/middleware/proxy_headers.py", line 78, in __call__
    return await self.app(scope, receive, send)
  File "/usr/local/lib/python3.9/dist-packages/starlette/applications.py", line 124, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/usr/local/lib/python3.9/dist-packages/starlette/middleware/errors.py", line 184, in __call__
    raise exc
  File "/usr/local/lib/python3.9/dist-packages/starlette/middleware/errors.py", line 162, in __call__
    await self.app(scope, receive, _send)
  File "/usr/local/lib/python3.9/dist-packages/starlette_exporter/middleware.py", line 289, in __call__
    await self.app(scope, receive, wrapped_send)
  File "/usr/local/lib/python3.9/dist-packages/starlette/middleware/cors.py", line 84, in __call__
    await self.app(scope, receive, send)
  File "/usr/local/lib/python3.9/dist-packages/starlette/middleware/base.py", line 106, in __call__
    response = await self.dispatch_func(request, call_next)
  File "./app.py", line 49, in dispatch
    response = await call_next(request)
  File "/usr/local/lib/python3.9/dist-packages/starlette/middleware/base.py", line 80, in call_next
    raise app_exc
  File "/usr/local/lib/python3.9/dist-packages/starlette/middleware/base.py", line 69, in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
  File "/usr/local/lib/python3.9/dist-packages/starlette/middleware/exceptions.py", line 79, in __call__
    raise exc
  File "/usr/local/lib/python3.9/dist-packages/starlette/middleware/exceptions.py", line 68, in __call__
    await self.app(scope, receive, sender)
  File "/usr/local/lib/python3.9/dist-packages/starlette/routing.py", line 706, in __call__
    await route.handle(scope, receive, send)
  File "/usr/local/lib/python3.9/dist-packages/starlette/routing.py", line 276, in handle
    await self.app(scope, receive, send)
  File "/usr/local/lib/python3.9/dist-packages/starlette/routing.py", line 66, in app
    response = await func(request)
  File "./app.py", line 79, in classify
    results = models[parsed_params.model](tfile)
  File "/usr/local/lib/python3.9/dist-packages/opennsfw2/_inference.py", line 31, in predict_image
    pil_image = Image.open(image_path)
  File "/usr/local/lib/python3.9/dist-packages/PIL/Image.py", line 3283, in open
    raise UnidentifiedImageError(msg)
PIL.UnidentifiedImageError: cannot identify image file '/tmp/tmpbqq82655'
ERROR:uvicorn.error:Exception in ASGI application
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/dist-packages/anyio/streams/memory.py", line 94, in receive
    return self.receive_nowait()
  File "/usr/local/lib/python3.9/dist-packages/anyio/streams/memory.py", line 89, in receive_nowait
    raise WouldBlock
anyio.WouldBlock

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.9/dist-packages/starlette/middleware/base.py", line 77, in call_next
    message = await recv_stream.receive()
  File "/usr/local/lib/python3.9/dist-packages/anyio/streams/memory.py", line 114, in receive
    raise EndOfStream
anyio.EndOfStream

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.9/dist-packages/uvicorn/protocols/http/h11_impl.py", line 404, in run_asgi
    result = await app(  # type: ignore[func-returns-value]
  File "/usr/local/lib/python3.9/dist-packages/uvicorn/middleware/proxy_headers.py", line 78, in __call__
    return await self.app(scope, receive, send)
  File "/usr/local/lib/python3.9/dist-packages/starlette/applications.py", line 124, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/usr/local/lib/python3.9/dist-packages/starlette/middleware/errors.py", line 184, in __call__
    raise exc
  File "/usr/local/lib/python3.9/dist-packages/starlette/middleware/errors.py", line 162, in __call__
    await self.app(scope, receive, _send)
  File "/usr/local/lib/python3.9/dist-packages/starlette_exporter/middleware.py", line 289, in __call__
    await self.app(scope, receive, wrapped_send)
  File "/usr/local/lib/python3.9/dist-packages/starlette/middleware/cors.py", line 84, in __call__
    await self.app(scope, receive, send)
  File "/usr/local/lib/python3.9/dist-packages/starlette/middleware/base.py", line 106, in __call__
    response = await self.dispatch_func(request, call_next)
  File "./app.py", line 49, in dispatch
    response = await call_next(request)
  File "/usr/local/lib/python3.9/dist-packages/starlette/middleware/base.py", line 80, in call_next
    raise app_exc
  File "/usr/local/lib/python3.9/dist-packages/starlette/middleware/base.py", line 69, in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
  File "/usr/local/lib/python3.9/dist-packages/starlette/middleware/exceptions.py", line 79, in __call__
    raise exc
  File "/usr/local/lib/python3.9/dist-packages/starlette/middleware/exceptions.py", line 68, in __call__
    await self.app(scope, receive, sender)
  File "/usr/local/lib/python3.9/dist-packages/starlette/routing.py", line 706, in __call__
    await route.handle(scope, receive, send)
  File "/usr/local/lib/python3.9/dist-packages/starlette/routing.py", line 276, in handle
    await self.app(scope, receive, send)
  File "/usr/local/lib/python3.9/dist-packages/starlette/routing.py", line 66, in app
    response = await func(request)
  File "./app.py", line 79, in classify
    results = models[parsed_params.model](tfile)
  File "/usr/local/lib/python3.9/dist-packages/opennsfw2/_inference.py", line 31, in predict_image
    pil_image = Image.open(image_path)
  File "/usr/local/lib/python3.9/dist-packages/PIL/Image.py", line 3283, in open
    raise UnidentifiedImageError(msg)
PIL.UnidentifiedImageError: cannot identify image file '/tmp/tmpbqq82655'

En Sidekiq se ve así:

¿Qué estoy haciendo mal?

Creo que esto podría estar relacionado con S3. Al subir una imagen, los registros de errores muestran estos errores:
URL a S3 redactada…

Así que parece que el plugin intenta cargar la imagen demasiado pronto o no la encuentra por otras razones.

Solo lo probé con sitios que usan una CDN para S3 o que usan almacenamiento local. Si usa Object Storage, le recomiendo usar una CDN para colocarlo delante.

1 me gusta

Eso es exactamente lo que hacemos: AWS Cloudfront. La pila se basa en GitHub - aws-samples/aws-cdk-for-discourse: AWS CDK for Discourse. Intentaré depurar y publicaré mis resultados.

2 Me gusta

¿Dónde consigo la clave API para ello?

Debería estar en el panel de administración, ¿no?

Esto parece ser demasiado sensible para mí, marca las cosas con demasiada frecuencia. Simplemente las imágenes de personas se marcan como NSFW.

Tengo los umbrales en 60. No está claro si debo aumentar o disminuir este número, ¿y cuál es el límite? Si lo establezco en 100, ¿es eso esencialmente deshabilitarlo? No parece haber mucha documentación al respecto.

Además, ¿por qué no hay forma de deshabilitarlo por completo para ciertos roles, como ocurre con la toxicidad? Estoy bastante seguro de que los miembros de nuestro personal no publicarán nada grosero, sin embargo, están constantemente luchando con la IA porque marca sus cosas. Se ha convertido en una especie de broma en mi foro.

Usando opennsfw2.

1 me gusta

Lamentablemente, esos modelos de ML más antiguos carecen de flexibilidad y, si bien son fáciles en cuanto a los requisitos para ejecutar tu propia instancia, descubrimos que la rigidez era un obstáculo demasiado grande para el diverso conjunto de comunidades que utilizan Discourse.

Actualmente, nuestro plan es eliminar gradualmente todas las funciones que dependen de esos modelos antiguos y migrar todo a LLM modernos, donde podrás ajustar el prompt para que se adapte mejor a tus necesidades.

1 me gusta

¿Ha habido algún cambio en esto? El detector NSFW sigue siendo terriblemente impreciso y se activa en la mayoría de las imágenes con personas.

Sí, hemos comunicado nuestro plan en ¿Qué sigue para la detección de NSFW en Discourse AI?, pero en resumen, puedes usar Clasificador de Publicaciones de Discourse AI - Regla de Automatización para una precisión y flexibilidad mucho mayores.

Hemos realizado evaluaciones para el caso de uso de NSFW a través de Vision LLMs y obtuvimos excelentes resultados, por lo que actualmente lo estamos implementando para nuestros clientes empresariales y publicaremos nuestras guías al respecto en las próximas semanas.

2 Me gusta