Skip to content

Stop dialog replies from repeating their own openings - #257

Merged
iamwavecut merged 2 commits into
mainfrom
feat/dialog-reply-shape-guard
Oct 13, 2026
Merged

iamwavecut merged 2 commits into
mainfrom
feat/dialog-reply-shape-guard

Conversation

@iamwavecut

Copy link
Copy Markdown
Owner

Problem

Since the dialog moved to NInfer Gemma (2026-10-11), replies collapse into one template in every chat: an opener that praises the person («Имя, ну ты и мастер…! 😂»), a paraphrase of their message («это ты так намекаешь…?»), a recap and a promise with an emoji tail. In 48 h of production replies, 38% of Gemma replies open with such a reaction and 9.1% open like one of the bot's last three replies in the same chat (Qwen on the same contexts: 2.6%).

Causes:

  • the Gemma model prior;
  • NInfer ignores the dry_* and repeat_penalty fields the bot sends, and its frequency/presence penalties count only the current reply, so nothing penalises a template repeated across replies;
  • the bot's own replies in the history act as examples.

Changes

  • prompts/chat/_shared_core.prompt: a reply has no intro or outro; no praise or evaluation of the person; no reading their message aloud; plain speech without literary padding; length that fits the message; variety also covers openings and endings.
  • openplotva-dialog::reply_shape + aifarm.rs: a final reply that opens like one of the bot's last three replies (repeated_opening), or announces a drawing or generation that no call of the turn started (unexecuted_action), is rejected once per turn and re-sampled with a note. A re-sample is delivered as it is.
  • openplotva-telegram::telegram_html_from_stray_markdown, called from prepare_dialog_chat_response: Markdown left in a dialog reply (**bold**, *italic* at word edges, `code`, fences, headings, links, * bullets, *** rules) becomes Telegram HTML. Text inside <code>/<pre> and tag markup stays as it is; censored words like бл**ь stay literal.
  • DEFAULT_DIALOG_MODEL_OUTPUT_RETRIES 1 → 5.

Measurement

Replay of 128 captured production dialog requests through the production Gemma (temperature 0.2; two baseline runs matched almost exactly):

metric baseline this PR
reaction/praise intro 38.6% 33.3%
praise of the person 7.9% 5.2%
quote echo of the message 7.9% 3.1%
Markdown written by the model 14.9% 8.3%
reply length p50 (chars) 246 154
native tool calls where production called a tool 13/18 10/18
transcript envelope copies (unwrapped downstream) 3/128 8/128

On 2000 production replies with Markdown, the converter leaves visible Markdown in 5.

An earlier prompt draft with literal HTML tag examples, a new final_check and a new identity made Gemma wrap replies in tags, invent links, copy the transcript envelope and drop native tool calls; an ablation found these blocks, and they stay as they were.

Deploy note

Production .env.production had DIALOG_MODEL_OUTPUT_RETRIES=0; it is set to 5 on the server. With 0, a rejected reply would go to the Qwen/Luna fallbacks instead of a Gemma re-sample.

Checks

  • cargo fmt --all --check
  • cargo clippy --workspace --all-targets -- -D warnings
  • cargo test -p openplotva-dialog, -p openplotva-llm, -p openplotva-prompts, -p openplotva-config, -p openplotva-telegram, -p openplotva-app --lib dialog_jobs

Since the dialog moved to NInfer Gemma, replies collapse into one template:
the same opener with praise of the person, a paraphrase of their message,
a promise and an emoji tail. NInfer ignores the DRY and repeat penalties
the bot sends, and the bot's own replies in the history act as examples.

- Rewrite the dialog style rules: no intro or outro, no praise or reading
  of the interlocutor, plain speech, length that fits the message.
- Re-sample once a reply that opens like one of the bot's last three
  replies, or that announces a generation no tool call started.
- Convert Markdown left in dialog replies to Telegram HTML at the send
  boundary.
- Raise the default dialog model output retries from 1 to 5.
@github-actions

Copy link
Copy Markdown
Warnings
⚠️ Prompt files are cache-sensitive contracts; confirm prompt shape and role markers were preserved intentionally.
Messages
📖 Danger checked 11 changed file(s).

Generated by 🚫 dangerJS against 924f7bf

@iamwavecut

Copy link
Copy Markdown
Owner Author

Danger prompt warning: the change is intentional. Partial names (shared_core), role markers, element tags and template data shape are unchanged; element texts in _shared_core.prompt changed, one element (human_speech) was added, and resample_note.prompt gained two verdict lines. The system prefix cache resets once per chat after deploy. The prompt was measured on 128 replayed production requests (see the PR description).

@iamwavecut
iamwavecut merged commit 5ec05c2 into main Oct 13, 2026
12 checks passed
@iamwavecut
iamwavecut deleted the feat/dialog-reply-shape-guard branch October 13, 2026 22:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant