Discourse AI - NSFW

:bookmark: Ce sujet couvre la configuration de la fonctionnalité NSFW (Not Safe For Work) du plugin Discourse AI.

:person_raising_hand: Niveau d’utilisateur requis : Administrateur

Les modules NSFW peuvent classifier automatiquement le score NSFW de chaque nouvelle image téléchargée dans les publications et les messages de chat de votre instance Discourse. Ce système aide à identifier et à gérer le contenu qui pourrait être inapproprié ou explicite pour un environnement professionnel ou public.

Vous pouvez également activer le signalement automatique du contenu qui dépasse un certain seuil.

Paramètres

  • ai_nsfw_detection_enabled : Active ou désactive le module.

  • ai_nsfw_inference_service_api_endpoint : URL où l’API est exécutée pour le module. Si vous utilisez l’hébergement CDCK, cela est géré automatiquement pour vous. Si vous auto-hébergez, consultez le guide d’auto-hébergement.

  • ai_nsfw_inference_service_api_key : Clé API pour l’API configurée ci-dessus. Si vous utilisez l’hébergement CDCK, cela est géré automatiquement pour vous. Si vous auto-hébergez, consultez le guide d’auto-hébergement.

  • ai_nsfw_models : Le modèle que nous utiliserons pour la classification d’images. Nous en proposons deux avec leurs seuils :

    • opennsfw2 renvoie un score unique entre 0.0 et 1.0. Définissez le seuil pour qu’un téléchargement soit considéré comme NSFW via le paramètre ai_nsfw_flag_threshold_general.
    • nsfw_detector renvoie des scores pour quatre catégories : drawings, hentai, porn et sexy. Définissez le seuil pour chacune d’elles via le paramètre respectif ai_nsfw_flag_threshold_${category}. Si le seuil général est inférieur à l’un de ceux-ci, nous considérerons le contenu comme NSFW.
  • ai_nsfw_flag_automatically : Signale automatiquement les publications/messages de chat qui dépassent les seuils configurés. Lorsque ce paramètre est désactivé, nous stockons uniquement les résultats de classification dans la base de données.

Ressources supplémentaires

11 « J'aime »

Merci.

J’ai trouvé ce problème lors de l’utilisation du service nsfw, après avoir téléchargé une image. J’utilise S3 pour le stockage des images, si cela est pertinent.

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'

Dans Sidekiq, cela ressemble à ceci :

Qu’est-ce que je fais mal ?

Je pense que cela est peut-être lié à S3. Lors du téléchargement d’une image, les journaux d’erreurs affichent ces erreurs :
URL vers S3 expurgée…

Il semble donc que le plugin essaie de charger l’image trop tôt ou qu’il ne la trouve pas pour d’autres raisons.

Je l’ai seulement testé avec des sites utilisant un CDN pour S3 ou utilisant le stockage local. Si vous utilisez le stockage d’objets, je vous conseille d’utiliser un CDN pour le placer devant.

1 « J'aime »

C’est exactement ce que nous faisons - AWS Cloudfront. La pile est basée sur GitHub - aws-samples/aws-cdk-for-discourse: AWS CDK for Discourse. Je vais essayer de déboguer et de publier mes résultats.

2 « J'aime »

Où puis-je obtenir la clé API pour cela ?

Ça devrait être sous le panneau d’administration, non ?

Cela me semble beaucoup trop sensible, il signale les choses beaucoup trop souvent. Rien que des photos de personnes sont signalées comme NSFW.

J’ai les seuils à 60. Il n’est pas clair si je dois augmenter ou diminuer ce nombre, et quelle est la limite ? Si je le règle à 100, est-ce que cela le désactive essentiellement ? Il ne semble pas y avoir beaucoup de documentation à ce sujet.

Aussi, pourquoi n’y a-t-il aucun moyen de le désactiver complètement pour certains rôles, comme c’est le cas pour la toxicité ? Je suis à peu près certain que les membres de mon personnel ne publieront rien de grossier, pourtant ils se battent constamment avec l’IA parce qu’elle signale leurs publications. C’est devenu un peu une blague sur mon forum.

Utilisation d’opennsfw2.

1 « J'aime »

Malheureusement, ces anciens modèles ML manquent de flexibilité et, bien qu’ils soient peu exigeants en termes de ressources pour exécuter votre propre instance, nous avons constaté que leur rigidité constituait un obstacle trop important pour le divers ensemble de communautés utilisant Discourse.

Actuellement, notre plan est de déprécier toutes les fonctionnalités reposant sur ces anciens modèles et de tout migrer vers des LLM modernes, où vous pourrez ajuster l’invite pour mieux répondre à vos besoins.

1 « J'aime »

Y a-t-il eu un quelconque changement à ce sujet ? Le détecteur NSFW est toujours lamentablement inexact et se déclenche sur la plupart des images contenant des personnes.

Oui, nous avons communiqué notre plan dans What’s next for NSFW detection in Discourse AI, mais en résumé, vous pouvez utiliser Discourse AI Post Classifier - Automation rule pour une précision et une flexibilité bien meilleures.

Nous avons effectué des évaluations pour le cas d’utilisation NSFW via les LLM de vision et obtenu d’excellents résultats. Nous déployons donc actuellement cette fonctionnalité pour nos clients Entreprise et publierons nos guides à ce sujet dans les semaines à venir.

2 « J'aime »