Este tópico cobre a configuração do recurso NSFW (Not Safe For Work) do plugin Discourse AI.
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.0e1.0. Defina o limite para que um upload seja considerado NSFW através da configuraçãoai_nsfw_flag_threshold_general.- nsfw_detector retorna pontuações para quatro categorias:
drawings,hentai,pornesexy. Defina o limite para cada uma através da configuraçãoai_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
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.
É 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.
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.
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.
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.

