La IA supera aleatoria e impredeciblemente los umbrales de tokens de los LLM

He configurado el LLM a un máximo de 8000 tokens de salida, pero Discourse AI envía regularmente solicitudes que superan ese umbral.

Con el tiempo, mediante prueba y error y revisando los registros (lo cual en sí mismo es un problema, ya que nada se indica en el panel de control), reduje los números a 7800, 7500, 7200 y finalmente a 7000 tras días de prueba y error.

De repente, después de meses de funcionamiento, dejó de funcionar. Tras depurar el problema, me di cuenta de que, en algunos casos extremos, Discourse vuelve a solicitar más de 8000 tokens de salida, incluso aunque el LLM esté configurado con un máximo de 7000 tokens.

Mensaje (3 copias reportadas)

DiscourseAi::Completions::Endpoints::OpenAi: estado: 413 - cuerpo: {"error":{"message":"Solicitud demasiado grande para el modelo `openai/gpt-oss-120b` en la organización `org_01kccx1be8f` nivel de servicio `on_demand` en tokens por minuto (TPM): Límite 8000, Solicitado 8102, por favor reduzca el tamaño de su mensaje e inténtelo de nuevo. ¿Necesita más tokens? Actualice al nivel de desarrollador hoy en https://console.groq.com/settings/billing","type":"tokens","code":"rate_limit_exceeded"}}


Pila de seguimiento

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'

Esto es muy problemático y genera una mala experiencia al usar IA con Discourse. Los problemas de configuración del LLM no aparecen en el panel de control y, cuando Discourse ignora la configuración del LLM, se producen problemas impredecibles y frustraciones.

Discourse necesita encontrar una manera de limitarse a los parámetros de configuración del LLM y mostrar los problemas en el panel de administración.

¿Estás confundiendo los tokens de solicitud con los tokens de respuesta?

El código 413 indica que tu solicitud era demasiado grande, no la respuesta solicitada.

Para solucionarlo, debes ajustar la configuración del LLM llamada Context window. Sin embargo, te advertiría que 8k tokens es demasiado pequeño en la actualidad. Funcionará para algunas funciones, pero no es algo que se utilice mucho hoy en día, cuando los LLM manejan contextos de hasta 1 millón de tokens. Puedo ejecutar un contexto de 256k en mi PC de escritorio usando un modelo mucho mejor que el que estás utilizando.

El contexto está configurado en 130k

Pero eso me lleva de nuevo al mismo problema. El límite del modelo en Groq es de 131.072; ya lo he ajustado a 130.000. No debería tener que experimentar con los límites para averiguar cuánto envía Discourse. Discourse debería poder operar dentro de los límites proporcionados por la configuración del LLM.

Lo que no entiendo es por qué reducir los tokens de salida máximos parece solucionar el problema. No he realizado ningún cambio en la ventana de contexto, solo he reducido aún más los tokens de salida máximos y ha empezado a funcionar, retomando donde lo dejó.

Solo para que lo sepas, el problema comenzó cuando el servicio de traducción se quedó bloqueado y se agotaron los tokens:

DiscourseAi::Completions::Endpoints::OpenAi: status: 429 - body: {“error”:{“message”:“Límite de tasa alcanzado para el modelo openai/gpt-oss-120b en la organización org_01kccx1be8fffaz5sbe17, nivel de servicio on_demand, en tokens por día (TPD): Límite 200000, Usados 193487, Solicitados 7464. Inténtalo de nuevo en 6m50.832s. ¿Necesitas más tokens? Actualiza al nivel Dev hoy en https://console.groq.com/settings/billing",“type”:“tokens”,“code”:"rate_limit_exceeded”}}

Luego pausé el servicio durante 24 horas para que se restablecieran los límites diarios. Al reiniciarlo, noté este error:

DiscourseAi::Completions::Endpoints::OpenAi: status: 413 - body: {“error”:{“message”:“Solicitud demasiado grande para el modelo openai/gpt-oss-120b en la organización org_01kccx1be8fffaz5sbe17, nivel de servicio on_demand, en tokens por minuto (TPM): Límite 8000, Solicitados 8102, reduce el tamaño de tu mensaje e inténtalo de nuevo. ¿Necesitas más tokens? Actualiza al nivel Dev hoy en https://console.groq.com/settings/billing",“type”:“tokens”,“code”:"rate_limit_exceeded”}}

Después reduje los tokens de salida máximos de 7000 a 6800 en la configuración del LLM y volvió a funcionar.

¿Qué estoy pasando por alto? ¿Estás sugiriendo que está relacionado con la ventana de contexto y no tiene nada que ver con los tokens de salida máximos? Solo trato de entender cómo igualar los números de configuración de Groq / límites del modelo con las configuraciones de LLM de Discourse.