[polish] 对话感知润色在本地小参数模型上会将上一轮历史内容输出为当前结果
Maintainer thường phản hồi trong vòng 1 ngày
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 5/5
- Thời gian dự kiến
- Hơn một tuần
- Mức phù hợp với người mới
- 35/100
Hướng nghiên cứu
Start with openless-all/app/src-tauri/src/coordinator/dictation.rs and openless-all/app/src-tauri/src/polish.rs, especially build_polish_history_messages; the issue also identifies build_polish_history_contents as a related Gemini path. Reproduce with the described system + history + current-user request using lfm2.5-voice-8k, then assess the proposed provider-specific controls and result guard. Done means the stated 10 history-bearing tests produce no historical content and cloud-channel behavior still works.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
现象
相关背景:#678 解决了历史上下文无上限导致的润色延迟问题,#693 合并后当前
beta代码最多保留 2 轮历史。本 issue 关注的是:即使只有 1 轮历史,部分本地小参数模型仍会把历史内容输出为当前结果,属于结果正确性问题。
环境与测试模型
- OpenLess 1.3.18-Beta.7(Windows),Ollama 0.33.2(localhost,模型常驻)
- 本地前后共测试 9 个模型,串线现象跨模型出现,形态随模型不同:
| 模型 | 参数量 | 证据来源 | 观察到的形态 |
|---|---|---|---|
lfm2.5-voice-8k:latest |
1.2B | 受控复现 3/3 + 生产日志 | 复读上一轮结果 |
LiquidAI/lfm2.5-1.2b-instruct:q8_0 |
1.2B | 受控对照 0/11 | 未触发(对照组) |
qwen2.5:1.5b |
1.5B | 生产日志 | 把转写当聊天回答 |
qwen2.5:3b |
3B | 生产日志 | 对话式跑题 / 评论润色任务本身 |
hf.co/LiquidAI/LFM2.5-2.6B-GGUF:Q4_K_M |
2.6B | 日常使用观察 | 串线/混合 |
hf.co/tencent/Youtu-LLM-2B-GGUF:F16 |
2B | 日常使用观察 | 串线/混合 |
qwen3.5:2b-q8_0 |
2.3B | 受控测试 | 非流式响应 content 为空(独立问题,见 TODO) |
qwen3.5:4b |
4.7B | 日常使用观察 | 串线/混合 |
gemma4:12b |
11.9B | 日常使用观察 | 偶发,程度较轻 |
受控复现数据目前覆盖前 4 行;其余为日常使用观察,未逐一做受控量化。
触发条件
默认配置即可满足,共三个:
- 润色走 custom 渠道本地模型(Ollama 等);
polishContextWindowMinutes> 0(默认值 5);- 窗口内存在上一条成功润色的会话(日志字段
prior_turns >= 1)。
当前表现
当前这段转写的润色结果被上一轮会话内容替换,三种形态:
- 复读上一轮:当前输入 104 字(直播带货内容),返回的是上一条 16 字录音(买机票)的润色结果;
- 把转写当聊天回答:输出「你去搜索」「帮我解决」等对话式内容,完全脱离润色任务;
- A/B 混合:当前内容与上一轮内容拼合输出。
同一文本点击「重新润色」立即恢复正常——重润色路径不携带历史,可稳定反证。
证据链(排查全程)
-
排除 Ollama 本体:绕过客户端直连
localhost:11434(/api/chat与/v1/chat/completions双端点,串行 24 轮 + 最高 8 路并发,45+ 次请求),messages仅 system + 当前文本时零串线。keep-alive、模型常驻、KV 缓存、并发请求映射全部排除。 -
定位客户端机制:源码确认润色请求默认携带历史——
openless-all/app/src-tauri/src/coordinator/dictation.rs拉取最近 N 分钟会话(当前上限 2 轮),openless-all/app/src-tauri/src/polish.rs的build_polish_history_messages将历史展开为真实 user/assistant 消息对。polish.rs中虽有「不要复读历史」的 system 指令,但小参数模型服从不了。 -
API 层稳定复现:按源码 1:1 模拟请求形态(system + 历史对 + 当前 user),
lfm2.5-voice-8k3/3 稳定输出历史轮内容、完全无视当前输入;其母模型 0/11 未触发。证实触发概率取决于「模型权重 × 历史内容组合」——与「跨模型出现、时有时无」的实际体验一致。 -
生产日志实锤(
%LOCALAPPDATA%\OpenLess\Logs\openless.log):时间 (UTC) 场景 raw_chars prior_turns 响应开头 判定 09-04 10:19:16 记录 B 自动润色 16 0 正常 B 本身是该主题 09-04 10:20:41 记录 A 自动润色 104 1 「去…」 输出上一条 B 的结果 09-04 10:33:47 记录 A 手动重润色 104 0 「有…」 恢复正常(A 原文开头) 三行中同模型、同模式、A 两行同输入文本,唯一实质差异是
prior_turns;prompt 长度差4028-3840=188字符,与一轮历史(原文 16 字 + 润色结果 + 信封包装)体量吻合。 -
污染面量化:该日志共 362 次 LLM 润色调用,其中 76 次(21%)
prior_turns>=1,观察到的串线事故全部落在其中。
临时解决(已执行,已止血)
已在「设置 → 隐私 → 数据存储 → 对话上下文窗口(分钟)」将默认 5 改为 0。改后所有润色请求恢复 stateless(messages 仅 system + 当前转写),经连续口述验证串线不再出现。
这是配置层面的规避:历史面板、手动重润色等本地功能不受影响,代价是连续口述时代词指代无法看到前文。该参数无渠道粒度,对所有 provider(含云端)一刀切关闭,这正是希望官方改进的部分。
相关 issue
- #678:历史上下文无上限导致润色延迟,已由 #693 修复为有限轮数;本 issue 是该机制的结果正确性后续问题。
- #1011:润色后相邻近似重复文本,现象相近但根因和复现路径不同。
影响
- 数据正确性:默认配置下,本地小参数模型用户会静默拿到错误的润色结果——当前记录被写入上一条的内容,无任何提示或标记。对以转写正确性为核心卖点的语音输入产品,属于核心功能产出错误数据。
- 排查成本极高:现象酷似「模型幻觉 / 服务串线」,用户第一反应是换模型、查 Ollama(本次排查先后测试 9 个模型、排查过 keep-alive 与 KV 缓存后才锁定客户端),且该场景发生在产品明确支持的 custom + Ollama 主路径上。
- 风险不对称无提示:携带历史对强云端模型是正向功能,对小参数本地模型是污染源;设置项描述(「把最近 N 分钟内已润色的转写作为多轮上下文,0 = 关闭」)无任何风险说明,用户无从判断该不该开。
建议接受标准
运维侧目前用「全局窗口 = 0」规避,但这是临时方案——一刀切放弃了所有渠道的上下文能力。希望官方针对**自定义模型渠道(尤其 Ollama 这类本地推理)**做系统性优化,按优先级:
- 渠道粒度上下文开关:
polishContextWindowMinutes支持按 provider / 渠道维度配置。本地 custom 渠道(Ollama 等)可独立关闭或调小,不影响内置云渠道;UI 上对本地渠道给出风险提示(「小参数模型可能将历史内容带入当前结果」)。 - 默认值调整:默认窗口 5 → 0(上下文显式 opt-in),或至少 custom 渠道默认 0、内置云渠道维持 5。
- 结果守卫:润色返回后校验——若输出与任一历史消息(assistant 润色结果或 user 原始转写,两者实测均可被复读)高度相似、而与当前输入差异大,自动去历史重发一次单轮请求。仅坏 case 触发,正常路径零额外延迟。
- 结构性改写(治本,可选):历史不再展开为 user/assistant 消息对,改为前文纯文本参考块并入 system prompt,chat 结构只保留一条 user 消息——消除「上一条 assistant 消息」这一复读目标,同时保留语境能力。
验证方式:lfm2.5-voice-8k + 受控复现请求(历史对 + 当前转写),连续 10 次含历史润色,0 次输出历史内容;同时云渠道功能回归正常。
TODO / 不确定项
- 触发率与「模型 × 历史内容」组合强相关:同一模型对特定历史组 3/3 稳定复现,另一模型 0/11 未触发;受控量化目前覆盖 1.2B~3B 四个模型,其余为日常观察,未逐一量化。
- 复读对象有两种形态:历史 assistant 润色结果、历史 user 原始转写(受控复现中均实测到),守卫比对需同时覆盖。
- 划词语音问答的多轮历史为独立机制,未纳入本次排查。
- Gemini 路径存在同款历史注入逻辑(
build_polish_history_contents),未实测。 - thinking 类模型非流式响应
content为空(正文在 reasoning 字段)是另一个独立问题,建议另开 issue。 - 复现脚本、raw 请求/响应日志、完整排查报告可按需提供。
- Ngôn ngữ chính
- Rust
- Star
- 3.7k
- Fork
- 349
- Merge trung bình
- 2 ngày 14 giờ
- Pull request đã merge (30 ngày)
- 97
Chuẩn bị môi trường
Dự án này không cung cấp dev container, Dockerfile hay hướng dẫn đóng góp, nên bạn cần tự thiết lập môi trường: hãy bắt đầu từ README và xem hướng dẫn đóng góp lần đầu của chúng tôi để biết các bước chung.
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của Open-Less/openless
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 62/100
Maintainer thường phản hồi trong vòng 1 ngày
-
[windows] 录音快捷键还不支持设置为ScrllLock键Có thể đã có người làm @HKLHaoBin đã nhận 9 ngày trước. Đang mởbug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
Open-Less/openless#1241 · 5 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 18/100
Maintainer thường phản hồi trong vòng 1 ngày
-
[omni] 多模态模式开启翻译后,译文时有时无,或混在 JSON 字段里Có thể đã có người làm @Breadswim đã nhận 2 ngày trước. Đang mở
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 52/100
Maintainer thường phản hồi trong vòng 1 ngày
-
[omni] 多模态「验证」对 OpenAI gpt-audio-1.5 必然返回 400Có thể đã có người làm @Breadswim đã nhận 2 ngày trước. Đang mở
Độ khó 3/5 1-2 ngày Mức phù hợp với người mới 58/100
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của Open-Less/openless
Issue tương tự
-
example
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
rust-lang-nursery/rust-cookbook#954 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 1/5 1-3 giờ Mức phù hợp với người mới 70/100
OpenZeppelin/openzeppelin-relayer#967 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 62/100
Maintainer thường phản hồi trong vòng 1 ngày
-
DecisionOutcome::label() / n_dosed(): engine-side mapping so downstream needs no wildcard armĐang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
Maintainer thường phản hồi trong vòng 1 ngày
-
src/runs.rs:1987: a missing --source-run-id now answers 400 "Source run ... does not exist", not 404Đang mởnightly-audit
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 62/100
antithesishq/snouty#602 ·
Maintainer thường phản hồi trong vòng 1 ngày