Skip to content

fix(dfc): 修复 default_chatter 提示词重复注入导致的上下文污染#74

Open
tt-P607 wants to merge 1 commit into
mainfrom
fix/dfc_prompt_injection_duplication
Open

fix(dfc): 修复 default_chatter 提示词重复注入导致的上下文污染#74
tt-P607 wants to merge 1 commit into
mainfrom
fix/dfc_prompt_injection_duplication

Conversation

@tt-P607

@tt-P607 tt-P607 commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

描述 / Description

修复了 default_chatter 中因提示词重复注入导致的严重上下文污染问题。

在此之前,各种插件(如 booku_memoryfeeling 等)通过 on_prompt_build 注入的 system_reminder,会随着完整的 user prompt 被持久化写入对话历史(保存在 state.response.payloads 中)。这导致在多轮对话中,每一轮都会叠加前一轮所有的动态注入内容,引发上下文体积爆炸,浪费大量 Token,并导致 LLM 逻辑降级或对旧设定的过度反应。

解决方案 / Solution

引入了 请求后净化 (Post-request Sanitization) 机制,不改变底层架构而优雅地解决问题:

  1. 新增清理模式:在 PromptAdapter 的 _build_user_prompt 方法中新增了 clean_mode 标识。
  2. 绕过事件注入:在 prompt_builder.py 中,当 clean_modeTrue 时,通过克隆模板并重命名为 default_chatter_user_prompt_clean,成功绕过所有的 on_prompt_build 事件订阅者,生成了一份仅包含纯净对话文本的 Prompt。
  3. 延迟历史替换:在 session.py 的处理流中,带有完整注入信息的 Payload 正常被发送给 LLM。而在发送请求返回后,立即调用 clean_mode 生成纯净文本,并覆盖刚才发送出去的那个 Payload 的内容。

这就使得那些动态注入的 Prompt 仅作为“瞬态载荷(Transient Payload)”在本次请求中对 LLM 可见,但绝对不会被作为历史记忆进入下一轮。

测试 / Testing

已通过实际抓包确认:

  • 第一轮请求发送时,尾部的 User Payload 成功携带了所有需要的 <system_reminder> 信息。
  • 第二轮请求发送时,第一轮的那条 User Payload 被完美替换为无注入的纯净文本,且本次请求的末尾也只包含当次新增的注入内容。没有发生任何重复叠加现象。

Summary by Sourcery

Prevent prompt injection reminders from polluting persisted conversation history in default_chatter.

Bug Fixes:

  • Ensure dynamically injected system_reminder content is treated as transient and not persisted into future conversation turns.

Enhancements:

  • Add a clean_mode to user prompt construction to generate a version of the user prompt without plugin on_prompt_build injections for history storage.

…tions

This commit implements a post-request sanitization mechanism in the Default Chatter session to prevent dynamic extra prompts (injected via `on_prompt_build`) from being permanently added to the dialogue history. By introducing a `clean_mode` flag, the `unread_user_prompt` is cleansed immediately after the LLM request returns, effectively preventing duplicate injections in subsequent dialogue turns.
@sourcery-ai

sourcery-ai Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Implements a post-request sanitization mechanism for default_chatter so that system_reminder-style dynamic injections affect only the current LLM call but are not persisted into conversation history, by adding a clean_mode to user prompt building and rewriting the last user payload after the request returns.

Sequence diagram for post-request sanitization in default_chatter

sequenceDiagram
    participant Session
    participant State
    participant LLM
    participant PromptAdapter
    participant PromptBuilder

    Session->>State: execute_with_stream
    State->>State: detect last_user_payload (ROLE.USER)
    State->>LLM: response.send(stream=use_stream)
    LLMI-->>State: LLM reply

    alt last_user_payload exists
        Session->>PromptAdapter: _build_user_prompt(chat_stream, history_text, unread_lines, extra, clean_mode=True)
        PromptAdapter->>PromptBuilder: build_user_prompt(stream_name, history_text, unread_lines, extra, clean_mode=True)
        PromptBuilder->>PromptBuilder: tmpl.clone() and rename to default_chatter_user_prompt_clean
        PromptBuilder-->>PromptAdapter: clean_text
        PromptAdapter-->>Session: clean_text
        Session->>State: last_user_payload.content = [Text(clean_text)]
    end
Loading

File-Level Changes

Change Details Files
Add a clean-mode user prompt build path that bypasses on_prompt_build injections by using a cloned template.
  • Extend build_user_prompt/_build_user_prompt/type_defs signatures to accept a clean_mode flag with default False.
  • When clean_mode is True, clone the default_chatter_user_prompt template, rename it to a clean variant, and render this instead.
plugins/default_chatter/prompt_builder.py
plugins/default_chatter/plugin.py
plugins/default_chatter/type_defs.py
Sanitize persisted conversation history after sending an LLM request by rebuilding the last user payload with a clean prompt.
  • Capture the last user-role payload from state.response.payloads before sending when there are unread messages to flush.
  • After state.response.send, rebuild a clean user prompt (with history optionally omitted when merged) using clean_mode=True and overwrite the last user payload content with the sanitized text.
  • Keep existing streaming observer flow intact after sanitization.
plugins/default_chatter/session.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • The new clean_mode boolean is threaded through multiple layers and toggles fairly different behavior; consider extracting a dedicated "build_clean_user_prompt" helper to keep the main adapter API simpler and avoid flag-based branching.
  • In session.execute_with_stream, the logic that finds last_user_payload assumes the last payload is always the user prompt you want to sanitize; it may be safer to identify the specific payload more robustly (e.g. by type or metadata) to avoid unintended replacement if the payload ordering changes.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new `clean_mode` boolean is threaded through multiple layers and toggles fairly different behavior; consider extracting a dedicated "build_clean_user_prompt" helper to keep the main adapter API simpler and avoid flag-based branching.
- In `session.execute_with_stream`, the logic that finds `last_user_payload` assumes the last payload is always the user prompt you want to sanitize; it may be safer to identify the specific payload more robustly (e.g. by type or metadata) to avoid unintended replacement if the payload ordering changes.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

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