L'IA dépasse aléatoirement et de manière imprévisible les seuils de tokens des LLM

J’ai configuré le LLM avec un maximum de 8000 jetons de sortie, mais Discourse AI envoie régulièrement des requêtes qui dépassent ce seuil.

Au fil du temps, grâce à des essais-erreurs et à l’analyse des journaux (ce qui pose déjà problème car rien n’est indiqué sur le tableau de bord), j’ai réduit les nombres à 7800, 7500, 7200 et enfin 7000 après plusieurs jours de tâtonnements.

Soudainement, après des mois de bon fonctionnement, cela a cessé de fonctionner. Après avoir effectué un débogage, j’ai réalisé que dans certains cas limites, Discourse demandait à nouveau plus de 8000 jetons de sortie, même si le LLM était configuré avec une limite maximale de 7000 jetons.

Message (3 copies signalées)

DiscourseAi::Completions::Endpoints::OpenAi: status: 413 - body: {"error":{"message":"Request too large for model `openai/gpt-oss-120b` in organization `org_01kccx1be8f` service tier `on_demand` on tokens per minute (TPM): Limit 8000, Requested 8102, please reduce your message size and try again. Need more tokens? Upgrade to Dev Tier today at https://console.groq.com/settings/billing","type":"tokens","code":"rate_limit_exceeded"}}


Backtrace

activesupport-8.0.5/lib/active_support/broadcast_logger.rb:218:in 'block in ActiveSupport::BroadcastLogger#dispatch'
activesupport-8.0.5/lib/active_support/broadcast_logger.rb:217:in 'Array#map'
activesupport-8.0.5/lib/active_support/broadcast_logger.rb:217:in 'ActiveSupport::BroadcastLogger#dispatch'
activesupport-8.0.5/lib/active_support/broadcast_logger.rb:129:in 'ActiveSupport::BroadcastLogger#error'
/var/www/discourse/plugins/discourse-ai/lib/completions/endpoints/base.rb:202:in 'block (2 levels) in DiscourseAi::Completions::Endpoints::Base#perform_completion!'
net-http-0.9.1/lib/net/http.rb:2461:in 'block in Net::HTTP#transport_request'
net-http-0.9.1/lib/net/http/response.rb:321:in 'Net::HTTPResponse#reading_body'
net-http-0.9.1/lib/net/http.rb:2458:in 'Net::HTTP#transport_request'
net-http-0.9.1/lib/net/http.rb:2410:in 'Net::HTTP#request'
rack-mini-profiler-4.0.1/lib/patches/net_patches.rb:19:in 'block in Net::HTTP#request_with_mini_profiler'
rack-mini-profiler-4.0.1/lib/mini_profiler/profiling_methods.rb:51:in 'Rack::MiniProfiler::ProfilingMethods#step'
rack-mini-profiler-4.0.1/lib/patches/net_patches.rb:18:in 'Net::HTTP#request_with_mini_profiler'
/var/www/discourse/plugins/discourse-ai/lib/completions/endpoints/base.rb:198:in 'block in DiscourseAi::Completions::Endpoints::Base#perform_completion!'
net-http-0.9.1/lib/net/http.rb:1630:in 'Net::HTTP#start'
net-http-0.9.1/lib/net/http.rb:1064:in 'Net::HTTP.start'
/var/www/discourse/plugins/discourse-ai/lib/completions/endpoints/base.rb:146:in 'DiscourseAi::Completions::Endpoints::Base#perform_completion!'
/var/www/discourse/plugins/discourse-ai/lib/completions/endpoints/open_ai_shared.rb:28:in 'DiscourseAi::Completions::Endpoints::OpenAiShared#perform_completion!'
/var/www/discourse/plugins/discourse-ai/lib/completions/llm.rb:214:in 'DiscourseAi::Completions::Llm#generate'
/var/www/discourse/plugins/discourse-ai/lib/agents/bot.rb:144:in 'DiscourseAi::Agents::Bot#reply'
/var/www/discourse/plugins/discourse-ai/lib/translation/base_translator.rb:55:in 'DiscourseAi::Translation::BaseTranslator#get_translation'
/var/www/discourse/plugins/discourse-ai/lib/translation/base_translator.rb:31:in 'block in DiscourseAi::Translation::BaseTranslator#translate'
/var/www/discourse/plugins/discourse-ai/lib/translation/base_translator.rb:31:in 'Array#map'
/var/www/discourse/plugins/discourse-ai/lib/translation/base_translator.rb:31:in 'DiscourseAi::Translation::BaseTranslator#translate'
/var/www/discourse/plugins/discourse-ai/lib/translation/post_localizer.rb:17:in 'DiscourseAi::Translation::PostLocalizer.localize'
/var/www/discourse/plugins/discourse-ai/app/jobs/regular/localize_posts.rb:39:in 'block in Jobs::LocalizePosts#execute'
/var/www/discourse/plugins/discourse-ai/app/jobs/regular/localize_posts.rb:29:in 'Array#each'
/var/www/discourse/plugins/discourse-ai/app/jobs/regular/localize_posts.rb:29:in 'Jobs::LocalizePosts#execute'
/var/www/discourse/app/jobs/base.rb:318:in 'block (2 levels) in Jobs::Base#perform'
rails_multisite-7.0.0/lib/rails_multisite/connection_management/null_instance.rb:49:in 'RailsMultisite::ConnectionManagement::NullInstance#with_connection'
rails_multisite-7.0.0/lib/rails_multisite/connection_management.rb:17:in 'RailsMultisite::ConnectionManagement.with_connection'
/var/www/discourse/app/jobs/base.rb:305:in 'block in Jobs::Base#perform'
/var/www/discourse/app/jobs/base.rb:301:in 'Array#each'
/var/www/discourse/app/jobs/base.rb:301:in 'Jobs::Base#perform'
sidekiq-7.3.10/lib/sidekiq/processor.rb:220:in 'Sidekiq::Processor#execute_job'
sidekiq-7.3.10/lib/sidekiq/processor.rb:185:in 'block (4 levels) in Sidekiq::Processor#process'
sidekiq-7.3.10/lib/sidekiq/middleware/chain.rb:180:in 'Sidekiq::Middleware::Chain#traverse'
sidekiq-7.3.10/lib/sidekiq/middleware/chain.rb:183:in 'block in Sidekiq::Middleware::Chain#traverse'
/var/www/discourse/lib/sidekiq/suppress_user_email_errors.rb:6:in 'Sidekiq::SuppressUserEmailErrors#call'
sidekiq-7.3.10/lib/sidekiq/middleware/chain.rb:182:in 'Sidekiq::Middleware::Chain#traverse'
sidekiq-7.3.10/lib/sidekiq/middleware/chain.rb:183:in 'block in Sidekiq::Middleware::Chain#traverse'
/var/www/discourse/lib/sidekiq/discourse_event.rb:6:in 'Sidekiq::DiscourseEvent#call'
sidekiq-7.3.10/lib/sidekiq/middleware/chain.rb:182:in 'Sidekiq::Middleware::Chain#traverse'
sidekiq-7.3.10/lib/sidekiq/middleware/chain.rb:183:in 'block in Sidekiq::Middleware::Chain#traverse'
/var/www/discourse/lib/sidekiq/pausable.rb:131:in 'Sidekiq::Pausable#call'
sidekiq-7.3.10/lib/sidekiq/middleware/chain.rb:182:in 'Sidekiq::Middleware::Chain#traverse'
sidekiq-7.3.10/lib/sidekiq/middleware/chain.rb:183:in 'block in Sidekiq::Middleware::Chain#traverse'
sidekiq-7.3.10/lib/sidekiq/job/interrupt_handler.rb:9:in 'Sidekiq::Job::InterruptHandler#call'
sidekiq-7.3.10/lib/sidekiq/middleware/chain.rb:182:in 'Sidekiq::Middleware::Chain#traverse'
sidekiq-7.3.10/lib/sidekiq/middleware/chain.rb:183:in 'block in Sidekiq::Middleware::Chain#traverse'
sidekiq-7.3.10/lib/sidekiq/metrics/tracking.rb:26:in 'Sidekiq::Metrics::ExecutionTracker#track'
sidekiq-7.3.10/lib/sidekiq/metrics/tracking.rb:134:in 'Sidekiq::Metrics::Middleware#call'
sidekiq-7.3.10/lib/sidekiq/middleware/chain.rb:182:in 'Sidekiq::Middleware::Chain#traverse'
sidekiq-7.3.10/lib/sidekiq/middleware/chain.rb:173:in 'Sidekiq::Middleware::Chain#invoke'
sidekiq-7.3.10/lib/sidekiq/processor.rb:184:in 'block (3 levels) in Sidekiq::Processor#process'
sidekiq-7.3.10/lib/sidekiq/processor.rb:145:in 'block (6 levels) in Sidekiq::Processor#dispatch'
sidekiq-7.3.10/lib/sidekiq/job_retry.rb:118:in 'Sidekiq::JobRetry#local'
sidekiq-7.3.10/lib/sidekiq/processor.rb:144:in 'block (5 levels) in Sidekiq::Processor#dispatch'
sidekiq-7.3.10/lib/sidekiq/config.rb:39:in 'block in <class:Config>'
sidekiq-7.3.10/lib/sidekiq/processor.rb:139:in 'block (4 levels) in Sidekiq::Processor#dispatch'
sidekiq-7.3.10/lib/sidekiq/processor.rb:281:in 'Sidekiq::Processor#stats'
sidekiq-7.3.10/lib/sidekiq/processor.rb:134:in 'block (3 levels) in Sidekiq::Processor#dispatch'
sidekiq-7.3.10/lib/sidekiq/job_logger.rb:15:in 'Sidekiq::JobLogger#call'
sidekiq-7.3.10/lib/sidekiq/processor.rb:133:in 'block (2 levels) in Sidekiq::Processor#dispatch'
sidekiq-7.3.10/lib/sidekiq/job_retry.rb:85:in 'Sidekiq::JobRetry#global'
sidekiq-7.3.10/lib/sidekiq/processor.rb:132:in 'block in Sidekiq::Processor#dispatch'
sidekiq-7.3.10/lib/sidekiq/job_logger.rb:40:in 'Sidekiq::JobLogger#prepare'
sidekiq-7.3.10/lib/sidekiq/processor.rb:131:in 'Sidekiq::Processor#dispatch'
sidekiq-7.3.10/lib/sidekiq/processor.rb:183:in 'block (2 levels) in Sidekiq::Processor#process'
sidekiq-7.3.10/lib/sidekiq/processor.rb:182:in 'Thread.handle_interrupt'
sidekiq-7.3.10/lib/sidekiq/processor.rb:182:in 'block in Sidekiq::Processor#process'
sidekiq-7.3.10/lib/sidekiq/processor.rb:181:in 'Thread.handle_interrupt'
sidekiq-7.3.10/lib/sidekiq/processor.rb:181:in 'Sidekiq::Processor#process'
sidekiq-7.3.10/lib/sidekiq/processor.rb:86:in 'Sidekiq::Processor#process_one'
sidekiq-7.3.10/lib/sidekiq/processor.rb:76:in 'Sidekiq::Processor#run'
sidekiq-7.3.10/lib/sidekiq/component.rb:10:in 'Sidekiq::Component#watchdog'
sidekiq-7.3.10/lib/sidekiq/component.rb:19:in 'block in Sidekiq::Component#safe_thread'

Ceci est très problématique et nuit à l’expérience d’utilisation de l’IA avec Discourse. Les problèmes de configuration du LLM n’apparaissent pas sur le tableau de bord, et lorsque Discourse ignore la configuration du LLM, cela entraîne des problèmes imprévisibles et des frustrations.

Discourse doit trouver un moyen de se limiter aux paramètres de configuration du LLM et d’afficher les problèmes sur le tableau de bord d’administration.

Confondez-vous les jetons de requête avec les jetons de réponse ?

Le code 413 indique que votre requête était trop volumineuse, et non la réponse demandée.

Pour résoudre ce problème, vous devez ajuster la configuration du Context window de l’LLM. Cependant, je vous avertis que 8 000 jetons est bien trop faible de nos jours. Cela fonctionnera pour certaines fonctionnalités, mais ce n’est pas vraiment ce que nous utilisons couramment aujourd’hui, alors que les LLM gèrent des fenêtres de contexte allant jusqu’à 1 million de jetons. Je peux exécuter une fenêtre de contexte de 256 000 jetons sur mon PC de bureau en utilisant un modèle bien supérieur à celui que vous utilisez.

La fenêtre de contexte est définie à 130k

Mais cela me ramène au même problème. La limite du modèle sur Groq est de 131 072 ; je l’ai déjà fixée à 130 000. Je ne devrais pas avoir à expérimenter avec les limites pour déterminer combien Discourse envoie. Discourse devrait pouvoir fonctionner dans les limites fournies par la configuration du LLM.

Ce que je ne comprends pas, c’est pourquoi la réduction du nombre maximal de jetons de sortie semble résoudre le problème. Je n’ai apporté aucune modification à la fenêtre de contexte, j’ai simplement réduit davantage le nombre maximal de jetons de sortie et cela a recommencé à fonctionner, reprenant là où cela s’était arrêté.

Juste pour info, le problème a commencé lorsque le service de traduction s’est figé et a épuisé les jetons :

DiscourseAi::Completions::Endpoints::OpenAi: status: 429 - body: {“error”:{“message”:“Limite de débit atteinte pour le modèle openai/gpt-oss-120b dans l’organisation org_01kccx1be8fffaz5sbe17 au niveau de service on_demand en termes de jetons par jour (TPD) : Limite 200000, Utilisé 193487, Demandé 7464. Veuillez réessayer dans 6m50.832s. Besoin de plus de jetons ? Passez au niveau Dev dès aujourd’hui sur https://console.groq.com/settings/billing",“type”:“tokens”,“code”:"rate_limit_exceeded”}}

J’ai ensuite mis le service en pause pendant 24 heures pour permettre la réinitialisation des limites quotidiennes. Après le redémarrage, j’ai constaté cette erreur :

DiscourseAi::Completions::Endpoints::OpenAi: status: 413 - body: {“error”:{“message”:“Requête trop volumineuse pour le modèle openai/gpt-oss-120b dans l’organisation org_01kccx1be8fffaz5sbe17 au niveau de service on_demand en termes de jetons par minute (TPM) : Limite 8000, Demandé 8102, veuillez réduire la taille de votre message et réessayer. Besoin de plus de jetons ? Passez au niveau Dev dès aujourd’hui sur https://console.groq.com/settings/billing",“type”:“tokens”,“code”:"rate_limit_exceeded”}}

J’ai ensuite réduit le nombre maximal de jetons de sortie de 7000 à 6800 dans la configuration du LLM et tout a recommencé à fonctionner.

Qu’est-ce que je rate ? Suggérez-vous que cela soit lié à la fenêtre de contexte et sans rapport avec les jetons de sortie maximaux ? J’essaie simplement de comprendre comment faire correspondre les chiffres de configuration de Groq / les limites des modèles avec les configurations LLM de Discourse.