0
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-08-09 21:45:25 +08:00
discourse/plugins/discourse-ai/lib/agents/short_text_translator.rb
Rafael dos Santos Silva 491e246166
FIX: Steer translation agents away from ASCII quotes in structured output (#42131)
## Problem

A topic title translation was saved truncated: `Permanently Delete does
not show (even after 5 minutes)` → `"Endgültig löschen`.

The raw completion showed the model emitted `{"output": "\"Endgültig
löschen" }` with `finish_reason: stop` at 12 tokens — complete, valid
JSON that our parsing pipeline reproduced faithfully. The truncation
happened at generation time: the model opened a quoted UI label with an
escaped ASCII quote (`\"`), then sampled a bare `"` where it meant `\"`.
Under strict `json_schema` guided decoding that bare quote terminates
the JSON string value, and with `additionalProperties: false` the
grammar then only allows `}`, forcing the generation to end
mid-sentence.

Native typographic quotation marks (`„…“`, `«…»`, `「…」`) are ordinary
characters inside a JSON string — no escaping, no ambiguity — so they
can't trigger this failure mode. A parallel successful run that happened
to use `„…“` completed fine.

## Fix

`PostRawTranslator` already has a rule instructing native quotation
marks. This adds the same rule to `TopicTitleTranslator` and
`ShortTextTranslator`:

- **TopicTitleTranslator**: new guideline 6, plus a few-shot example
using the exact title from the incident, showing German `„…“` around the
UI label.
- **ShortTextTranslator**: new guideline 5, plus a `Click "Reply"` →
`Cliquez sur « Répondre »` example.
- **PostRawTranslator**: the existing rule's German example closed with
an ASCII `"` instead of `“` — fixed.

Seeded system agents re-sync `system_prompt` and `examples` from the
classes on migration, so sites pick this up automatically.
2026-07-29 18:36:34 -03:00

55 lines
2.2 KiB
Ruby
Vendored

# frozen_string_literal: true
module DiscourseAi
module Agents
class ShortTextTranslator < Agent
def self.default_enabled
false
end
def system_prompt
<<~PROMPT.strip
You are a translation service specializing in translating short pieces of text or a few words.
These words may be things like a name, description, or title. Adhere to the following guidelines:
1. Keep proper nouns (like 'Minecraft' or 'Toyota') and technical terms (like 'JSON') in their original language
2. Keep the translated content close to the original length
3. Translation maintains the original meaning
4. Preserve any Markdown, HTML elements, links, parenthesis, or newlines
5. Never use ASCII double quotes (") inside the translation. For quoted text, use the target language's native quotation marks — for example German „…“, French «…», Japanese 「…」
The text to translate will be provided in JSON format with the following structure:
{"content": "Text to translate", "target_locale": "Target language code"}
Format your response as a JSON object with a single key named "output", which has the translation as the value.
Your output should be in the following format:
{"output": "xx"}
Where "xx" is replaced by the translation.
reply with valid JSON only
PROMPT
end
def response_format
[{ "key" => "output", "type" => "string" }]
end
def examples
[
[{ content: "Japan", target_locale: "es" }.to_json, { output: "Japón" }.to_json],
[{ content: "Cats and Dogs", target_locale: "zh_CN" }.to_json, { output: "" }.to_json],
[
{ content: "Q&A", target_locale: "pt" }.to_json,
{ output: "Perguntas e Respostas" }.to_json,
],
[{ content: "Minecraft", target_locale: "fr" }.to_json, { output: "Minecraft" }.to_json],
[
{ content: %(Click "Reply"), target_locale: "fr" }.to_json,
{ output: "Cliquez sur « Répondre »" }.to_json,
],
]
end
end
end
end