Discourse AI : la réponse de sortie structurée n'était pas un JSON valide

J’ai effectué la mise à jour hier[1] et maintenant, il y a beaucoup d’erreurs dans les journaux :

Discourse AI: la réponse de sortie structurée n'était pas un JSON valide, bascule sur une analyse au mieux (311 octets) 

J’ai inspecté la réponse HTTP et elle n’avait aucun problème. Ensuite, j’ai essayé le texte avec rails console :

...
discourse(prod)> p = JsonCompleter.new
=>
#<JsonCompleter:0x00007f48763b0270
...
discourse(prod)> p.parse text
=>
{"spam" => false,
 "reason" =>
  "..."}
discourse(prod)> p.parse DiscourseAi::Utils::BestEffortJsonParser.escape_control_characters(text)
(discourse):14:in '<main>': jeton inattendu "\\" (JsonCompleter::ParseError)

il s’avère que Discourse échappait les caractères de saut de ligne avant l’analyse[2], ce qui causait l’erreur.


  1. ↩︎

  2. code, introduit par pr#41716 ↩︎

Il s’agit d’un avertissement, pas d’une erreur.

Cela dit, je passe ce message au niveau de journalisation INFO, car les gens s’en inquiètent continuellement :face_exhaling:

Eh bien, techniquement vrai, mais cela indique un bug dans le code. La solution de contournement fonctionne donc aucune erreur n’est levée, mais il vaut mieux corriger la source plutôt que de passer au niveau INFO pour dissimuler le problème, n’est-ce pas ?

JSON autorise les caractères de saut de ligne comme espaces blancs, mais n’autorise pas les séquences d’échappement en dehors des chaînes de caractères ; la fonction d’échappement l’échappe cependant. Pour le corriger, vous pouvez modifier pour échapper uniquement les caractères ASCII de 0x0 à 0x1f inclusivement conformément à la spécification JSON. (Je ne sais pas comment l’écrire en Ruby, donc pas de PR.)

Pour illustrer, le suivant est un JSON valide :

{
  "key": "value"
}

Le suivant est une version échappée, qui est invalide et cause le problème :

{\u000a  "key": "value"\u000a}

Quel LLM utilisez-vous ?

llama-server -hf mradermacher/Qwen3-4B-Instruct-2507-GGUF:Q4_K_M

Un modèle de 4 milliards de paramètres aura du mal à se conformer, ils s’améliorent, mais celui-ci est certainement du côté des plus anciens.

Si vous optez pour 4B, je vous recommanderais probablement quelque chose de plus récent… essayez Qwen 3.5-4B ou Gemma 4 E4B.

Je pense que llama.cpp impose le schéma en ne sélectionnant que les jetons qui y sont conformes. J’ai également capturé une réponse avec tcpdump et vérifié que la réponse retournée est un JSON valide.

Cependant, elle contenait des espaces et des sauts de ligne en dehors des chaînes (c’est-à-dire qu’il s’agissait d’un JSON formaté), et ces sauts de ligne ont été échappés par Discourse, ce qui l’a rendu invalide (je l’ai vérifié dans la console Rails).

Je utilise d’ailleurs la fonctionnalité de détection des spams.

C’est légitime, je travaille sur une correction.

La correction est disponible ici :