Discourse AI - NSFW

:bookmark: Este tópico cobre a configuração do recurso NSFW (Not Safe For Work) do plugin Discourse AI.

:person_raising_hand: Nível de usuário necessário: Administrador

Os módulos NSFW podem classificar automaticamente a pontuação NSFW de cada novo upload de imagem em postagens e mensagens de chat em sua instância do Discourse. Este sistema ajuda a identificar e gerenciar conteúdo que pode ser inadequado ou explícito para um ambiente profissional ou público.

Você também pode habilitar a sinalização automática de conteúdo que ultrapassa um limite.

Configurações

  • ai_nsfw_detection_enabled: Habilita ou desabilita o módulo

  • ai_nsfw_inference_service_api_endpoint: URL onde a API está em execução para o módulo. Se você estiver usando hospedagem CDCK, isso é tratado automaticamente para você. Se você estiver auto-hospedando, consulte o guia de auto-hospedagem.

  • ai_nsfw_inference_service_api_key: Chave de API para a API configurada acima. Se você estiver usando hospedagem CDCK, isso é tratado automaticamente para você. Se você estiver auto-hospedando, consulte o guia de auto-hospedagem.

  • ai_nsfw_models: O modelo que usaremos para classificação de imagens. Oferecemos dois diferentes com seus limites:

    • opennsfw2 retorna uma única pontuação entre 0.0 e 1.0. Defina o limite para que um upload seja considerado NSFW através da configuração ai_nsfw_flag_threshold_general.
    • nsfw_detector retorna pontuações para quatro categorias: drawings, hentai, porn e sexy. Defina o limite para cada uma através da configuração ai_nsfw_flag_threshold_${category} respectiva. Se o geral for menor que qualquer um destes, consideraremos o conteúdo NSFW.
  • ai_nsfw_flag_automatically: Sinaliza automaticamente postagens/mensagens de chat que estão acima dos limites configurados. Quando esta configuração está desabilitada, apenas armazenamos os resultados da classificação no banco de dados.

Recursos adicionais

11 curtidas

Obrigado.

Encontrei este problema ao usar o serviço nsfw, após o upload de uma imagem. Eu uso S3 para armazenamento de imagens, se isso for 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'

No Sidekiq, parece assim:

O que estou fazendo de errado?

Eu acho que isso pode estar relacionado ao S3. Ao carregar uma imagem, os logs de erro mostram estes erros:
URL para S3 omitida…

Portanto, parece que o plugin tenta carregar a imagem muito cedo ou não a encontra por outros motivos.

Eu testei apenas com sites que usam um CDN para S3 ou que usam armazenamento local. Se estiver usando Object Storage, aconselho a usar um CDN para ficar na frente.

1 curtida

É exatamente o que fazemos - AWS Cloudfront. A stack é baseada em GitHub - aws-samples/aws-cdk-for-discourse: AWS CDK for Discourse. Tentarei depurar e postar meus resultados.

2 curtidas

Onde consigo a chave de API para isso?

Deve ficar no painel do Administrador, não é?

Isso parece ser muito sensível para mim, sinaliza coisas com muita frequência. Apenas fotos de pessoas são sinalizadas como NSFW.

Eu tenho os limites em 60. Não está claro se devo aumentar ou diminuir este número, e qual é o limite? Se eu definir para 100, isso é essencialmente desativá-lo? Não parece haver muita documentação sobre isso.

Além disso, por que não há como desativá-lo completamente para certas funções, como acontece com a toxicidade? Tenho certeza de que os membros da minha equipe não postarão nada rude, mas eles estão constantemente lutando com a IA porque ela sinaliza as coisas deles. Tornou-se um pouco uma piada no meu fórum.

Usando opennsfw2.

1 curtida

Infelizmente, esses modelos de ML mais antigos carecem de flexibilidade e, embora sejam leves em relação aos requisitos para executar sua própria instância, descobrimos que o rigor era um obstáculo muito grande para o conjunto diversificado de comunidades que usam o Discourse.

Atualmente, nosso plano é descontinuar todos os recursos que dependem desses modelos antigos e migrar tudo para LLMs modernos, onde você poderá ajustar o prompt para melhor atender às suas necessidades.

1 curtida

Houve alguma alteração nisso? O detector NSFW ainda é terrivelmente impreciso e é acionado na maioria das imagens com pessoas.

Sim, comunicamos nosso plano em O que vem a seguir para a detecção de NSFW em Discourse AI, mas o resumo é que você pode usar Classificador de Posts do Discourse AI - Regra de Automação para uma precisão e flexibilidade muito melhores.

Executamos avaliações para o caso de uso de NSFW por meio de LLMs de Visão e obtivemos ótimos resultados, então estamos atualmente implementando isso para nossos clientes empresariais e publicaremos nossos guias sobre isso nas próximas semanas.

2 curtidas