Skip to content

perf(ondevice): 截断助手侧历史,缓解「被自己上一轮的错误答案锚死」 - #121

Merged
ReSerendipity merged 2 commits into
mainfrom
perf/ondevice-history-debias
Oct 16, 2026
Merged

ReSerendipity merged 2 commits into
mainfrom
perf/ondevice-history-debias

Conversation

@ReSerendipity

Copy link
Copy Markdown
Owner

动机

真机实测(GOTCHAS #184)暴露了一个不是模型能力问题的答错来源。4B 开启思维链时的推理原文:

Wait, checking math again. 9.9 vs 9.11. ... 9.9 is bigger.
The conversation history established WRONG MATH (9.11 > 9.9).
If I repeat it, I look dumb. If I correct it, I break the established fiction
(since the previous turns accepted it as truth).

它自己算对了,却被「自己上一轮的错误回答」绑住 —— 在两难里空转 3592 token / 254 秒仍未收敛。
所以小模型端侧答错有两个可修来源:

  1. 上下文膨胀(prefill 4359 token)→ 已修(perf(ondevice): 桌面端 on-device 上下文预算 + 4B/9B 模型下载 2GiB 缺陷修复 #119)
  2. 把助手自己的历史输出原样喂回去 → 本 PR

改动

新增 src/lib/ai/onDeviceContext.ts 的 truncateAssistantHistory():对端侧把
助手侧历史截断到 120 字符。

  • 保留开场语气,维持对话连贯与角色口吻
  • 截掉长篇断言(正是把模型锚死在错误结论上的部分)
  • 用户侧与 system 侧永不截断 —— 那才是真正的上下文,截断会丢信息
  • 不改角色设定:改提示词属产品决策(「像一张白纸 / 情绪一致性」与之有张力),未做

接线:MobileChatView(发送路径)、ChatWindow(handleSend 的 prevMessages +
重新生成路径的历史组装 —— 后者原本是逐条 push 的循环,一并改为先组装再截断)。

验证

  • 新增 onDeviceContext.test.ts 6 用例:enabled=false 原样返回 / 超长截断并加省略号 /
    不超长不动 / 用户与 system 侧永不截断 / 不原地修改入参 / limit 可覆盖
  • tsc 0 error、eslint 0、prettier OK
  • 全量 vitest 233 文件 / 2919 用例 0 失败;check_spec_refs PASS

风险

低。仅影响 provider === 'ondevice' 的 prompt 组装,云端逐字未动;
截断上限是导出常量,便于按设备/模型调参。这是降低锚定强度,不是删历史。

GOTCHAS #184

承接 GOTCHAS #185 的第四层(硬伤)。前三层(setup-android action / SIGPIPE /
Windows gradle tmpdir)已在前序 PR 修掉,构建推进到 Gradle 后又卡在:

    Could not read script '.../src-tauri/gen/android/tauri.settings.gradle' as it does not exist.
    BUILD FAILED in 5s

根因:`settings.gradle` 第 3 行 `apply from: 'tauri.settings.gradle'`,而该文件
**被 `src-tauri/gen/android/.gitignore` 忽略**;本地那份还是「**手工重建 + 写死本机
cargo 注册表路径**」的(含 `C:/Users/Doro/.cargo/registry/src/mirrors.ustc.edu.cn-...`)。
同理 `.tauri/tauri-android` 也是被忽略的生成物。→ **Android 构建在 CI 上不可能跑通**,
与业务代码无关。

修法:新增 `Generate Tauri Android glue (CI-portable)` 步骤,**按 runner 自己的
cargo 注册表路径生成**,不再指望它们在仓库里:
1. `.tauri/tauri-android` ← 从 `$REG/tauri-2.*/mobile/android` 复制(tauri crate 自带);
2. `tauri.settings.gradle` ← include 上面的子工程 + 本应用**实际声明**的
   `tauri-plugin-*/android`(插件列表从 `src-tauri/Cargo.toml` 推导,**不遍历整个
   registry**,避免纳入无关插件);找不到 android 子工程的插件只告警跳过。

**本地验证**:用同一段脚本在临时目录跑,生成的 include 项与本地手工版
**逐项一致**(`tauri-android` + deep-link / dialog / fs / notification 共 5 项);
Cargo.toml 声明的 11 个插件里恰好 4 个有 android 子工程,其余 7 个正确跳过。

未改动构建命令本身:本仓走 `gradle → BuildTask.kt → cargo ndk`,`./gradlew
assembleArm64Release` 是正确入口(`tauri android init` 对本仓不可用 —— 实测会改写
已跟踪的 `BuildTask.kt` 47+/82- 并生成冲突的 `build.gradle.kts`)。

签名材料(`keystore.properties` / `.jks`)**不需要**:`app/build.gradle` 已对缺失做
优雅降级(只 warn、不产出可分发产物),CI 只做构建验证。

GOTCHAS #185

Signed-off-by: ReSerendipity <259081123+ReSerendipity@users.noreply.github.com>
真机实测(GOTCHAS #184)暴露了一个**不是模型能力问题**的答错来源。4B 开启思维链时
的推理原文:

> `Wait, checking math again. 9.9 vs 9.11. ... 9.9 is bigger.`
> `The conversation history established WRONG MATH (9.11 > 9.9).`
> `If I repeat it, I look dumb. If I correct it, I break the established fiction`
> `(since the previous turns accepted it as truth).`

**它自己算对了**,却被「自己上一轮的错误回答」绑住 —— 在两难里空转 3592 token /
254 秒仍未收敛。所以小模型答错有两个可修来源:① 上下文膨胀(已修);② 把助手
自己的历史输出原样喂回去(本提交)。

## 改动

新增 `src/lib/ai/onDeviceContext.ts`:`truncateAssistantHistory()` —— 对端侧把
**助手侧历史截断**到 120 字符:

- 保留开场语气,维持对话连贯与角色口吻;
- 截掉长篇断言(正是把模型锚死在错误结论上的部分);
- **用户侧与 system 侧永不截断** —— 那才是真正的上下文;
- 不改角色设定,避免与「像一张白纸 / 情绪一致性」的产品口径冲突
  (改提示词属产品决策,未做)。

接线:`MobileChatView`(发送路径)、`ChatWindow`(handleSend 的 `prevMessages` +
重新生成路径的历史组装 —— 后者原本是逐条 push 的循环,一并改为先组装再截断)。

## 验证

- 新增 `onDeviceContext.test.ts` 6 用例:`enabled=false` 原样返回 / 超长截断并加省略号 /
  不超长不动 / **用户与 system 侧永不截断** / 不原地修改入参 / `limit` 可覆盖
- `tsc` 0 error、`eslint` 0、`prettier` OK
- 全量 vitest **233 文件 / 2919 用例 0 失败**;`check_spec_refs` PASS

## 风险

低。仅影响 `provider === 'ondevice'` 的 prompt 组装,云端逐字未动;
截断上限是导出常量,便于按设备/模型调参。

GOTCHAS #184

Signed-off-by: ReSerendipity <259081123+ReSerendipity@users.noreply.github.com>
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