From 7e0fce41672db3b69bac319d63577aec3b89c742 Mon Sep 17 00:00:00 2001 From: davegilligan Date: Fri, 15 May 2026 22:05:49 +0200 Subject: [PATCH] fix: rein in dbn-legal-agent feedback-loop contamination (stop seqs + JSON extract + system prompt) --- includes/BvjAnalyzerAgent.php | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/includes/BvjAnalyzerAgent.php b/includes/BvjAnalyzerAgent.php index 568908e..3635bf6 100644 --- a/includes/BvjAnalyzerAgent.php +++ b/includes/BvjAnalyzerAgent.php @@ -842,8 +842,16 @@ Rules: - Respond in {$locale}. PROMPT; + $sysPrompt = $engine === 'dbn_legal' + // dbn-legal-agent has a fine-tune that appends self-rating feedback loops after the + // JSON. Explicitly forbid that pattern and stop before it can start. + ? 'You output valid JSON only. Output the JSON object, then stop immediately. ' + . 'Do not add any self-assessment, rating, "END OF MESSAGE", feedback loop, ' + . 'USER/SYSTEM turns, or any text after the closing brace of the JSON object.' + : 'You return valid JSON only. No markdown fences.'; + $messages = [ - ['role' => 'system', 'content' => 'You return valid JSON only. No markdown fences.'], + ['role' => 'system', 'content' => $sysPrompt], ['role' => 'user', 'content' => $prompt], ]; $opts = ['json' => true, 'temperature' => $temperature, 'max_tokens' => 3000, 'timeout' => 200]; @@ -865,9 +873,19 @@ PROMPT; 'temperature' => $temperature, 'max_tokens' => 2800, 'timeout' => 660, + // Stop sequences cut generation the moment the feedback loop tries to start. + 'stop' => ["\nEND OF MESSAGE", "\nPlease rate", "\nUSER:", "지금 번역하기"], ], $emit ? static function () use ($emit): void { $emit('progress', ['detail' => 'dbn-legal-agent generating…']); } : null); + + // Belt-and-suspenders: even with stop sequences the model may still include + // preamble or trailing junk. Extract only the first complete {...} object. + $jsonStart = strpos($raw, '{'); + $jsonEnd = strrpos($raw, '}'); + if ($jsonStart !== false && $jsonEnd !== false && $jsonEnd > $jsonStart) { + $raw = substr($raw, $jsonStart, $jsonEnd - $jsonStart + 1); + } } elseif ($engine === 'gpu') { $response = dbnToolsCallGpuLlm($messages, $opts); $raw = (string)($response['choices'][0]['message']['content'] ?? ''); @@ -915,6 +933,9 @@ PROMPT; 'max_tokens' => $options['max_tokens'] ?? 2800, 'stream' => true, ]; + if (!empty($options['stop']) && is_array($options['stop'])) { + $payload['stop'] = $options['stop']; + } $body = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); $headers = [ 'Content-Type: application/json',