文档: 审查回填 CR-22~27 + 跨端 AI Chat 设计 + 跑题试验记录

审查回填(均  PASS):
- CR-22 摘要不改 updated_at / CR-23 P2 批次A 读 / CR-24 批次B 写 / CR-25 跑题P0 /
  CR-26 跑题P1 / CR-27 跑题P2(6维度全过,topic 无破坏 + 双高置信保守)

跨端 AI Chat 设计(F-260620-01):
- 三层架构 df-tunnel/df-relay/df-miniapp + Rust 云后端选型(代码复用/类型一致/团队版演进)
- todo 规划跨端 P1-P4 + 跑题修复 P0-P2

跑题改进试验记录:
- db 基线(3 长对话:assistant 文本仅 4-5%,95% tool_result,47% echo)
- 5 类跑题现象与 P0-P2 五改进精准对应 + 四层测试计划 + 迭代过程
This commit is contained in:
2026-06-20 03:54:59 +08:00
parent de049704c6
commit 7911bc292c
5 changed files with 493 additions and 5 deletions

View File

@@ -0,0 +1,218 @@
# AI Chat 跑题改进试验记录
> 创建2026-06-20
> 范围AI Chat 长对话「抓不住重点/跑题」根因治 — 测试计划 + 迭代过程 + 改进前基线 + 遗留
> 关联提交P0 `102d398` / P1 `013ce21` / P2 `a2db5c7`
> 关联设计:`意图识别层论证-2026-06-19.md` / `多主题上下文管理愿景-2026-06-19.md`
> 三原则对齐:优雅 / 可靠 / 易迭代
---
## 一、测试计划
四层覆盖,从机制单测到实跑场景逐层外扩。机制层已闭环,实跑层待人工验证。
### ① 单测(机制正确性)— 189 测,全过
| 模块 | 测数 | 覆盖点 |
|------|------|--------|
| `intent.rs` 意图识别 + filter | 43 | 空 subset 命中 / 置信三重 fallback<0.7、过滤<3、空 subset→全量/ 顺序漂移 / 工具收敛 29→5-10 |
| `context_helpers.rs` 压缩+工具压缩+tokenize | 18 | `extract_keyword_summary`全停用词、全漂移、top-10/ `should_summarize_tool_result`2KB 边界、占比 40% 边界)/ `extract_key_info`(错误行 error/panic/失败、首尾 5 行、空/短直回)/ **2-gram 汉字滑窗**"压缩架构"→"压缩/缩架/架构"、不跨边界) |
| `context.rs` 上下文+主题检测 | 32 | TrackedMessage.topic 推断 / `pending_topic_marker` 双高置信置位 / 任一 None 不标防误报 / take 消费 |
| `prompt.rs` 系统提示 | 6 | 中文「聚焦准则」段 / 英文「Focus」段 / 兜底 lang 回落 / 压缩 prompt 未波及回归保护 |
**df-ai crate 共 189 测全过**commit `a2db5c7` 自验:`cargo test -p df-ai` EXIT 0
### ② 苛刻测(边界/对抗agent 在跑)
针对每条机制的极端输入,验证不崩溃、不误报、有兜底:
- 全漂移user 消息全是无关内容 → 意图 Unknown → fallback 全量工具,不卡死)
- 全停用词(`extract_keyword_summary` 返回空字符串,不 panic
- 全错误行(`extract_key_info` 命中所有 error/panic 行,不超长)
- 2KB 边界(`should_summarize_tool_result` 恰好 2000/2001 字节,阈值判定稳定)
- 连续主题切换(连续两条 user topic 不同 → `pending_topic_marker` 只标末两条,不累积)
> 状态:部分已并入单测(见 `context_helpers.rs` 对抗段),其余 agent 审查阶段补跑。
### ③ db 基线分析(改进前跑题现象)— 本文 §三
挑 3 个长对话(均 600KB 量级glm-5.2),解析 messages JSON归类跑题现象作为改进前基线。
**结论:跑题主因是「工具结果挤占 + 全量工具分散 + 压缩丢主题」三类P0-P2 改进恰好对症。**
### ④ 实跑场景(重启 dev 对话验证)— 待人工执行
改进机制已测过LLM 实际响应效果需实跑验证。建议场景:
| 场景 | 验证改进 | 预期 |
|------|---------|------|
| 单主题长对话(连续 5+ 轮同一主题) | 改进3 压缩增强保留主题锚点 | 续接时不丢主线 |
| 主题切换(话题 A→B | 改进5 主题标记软提示 | 切换后不把旧话题带进新回答 |
| 多主题交织A/B 轮番) | 改进1 聚焦准则 + 改进5 | 始终围绕当前轮核心 |
| 大工具结果(>2KB / >10KB | 改进4 工具结果压缩 | tool_result 不挤占上下文 |
| 工具并发4-12 个并行) | 改进2 意图收敛 29→5-10 | LLM 可见工具收敛,不被工具列表稀释意图 |
---
## 二、迭代过程P0 → P1 → P2
三批改进,每批:根因 → 改进 → 单测 → 审查 → 效果。常量开关均可回退(调参退回接入前行为)。
### P0`102d398`)— 系统提示聚焦 + 意图接入 loop 收敛工具
**根因**
1. 系统 prompt 把「聚焦」混进「行为准则」段被稀释LLM 无独立抓手
2. loop 每轮把全量 29 个工具定义塞给 LLM工具列表本身稀释意图LLM 在 29 个工具里挑,而非围绕 user 目标)
**改进**
- 改进1 `prompt.rs``## 聚焦准则`(中)/ `## Focus`(英)独立段 4 条,机制描述("始终围绕当前请求核心目标""切换话题以最新为准""先结论后解释""不主动展开无关上下文"),不混进行为准则
- 改进2 `intent.rs` `filter_tool_defs` 按意图收敛工具 29→5-10`agentic:487` 三重 fallback置信<0.7 / 过滤后<3 / 空 subset → 回全量执行路径不变audit/execute 走完整 registryfilter 只改 LLM 可见 `tool_defs`
**单测**prompt 4中/英/兜底 lang/压缩未波及)+ intent filter 7空 subset/命中/漂移/顺序)= 11
**审查**CR-25🟡2Debug 加 Data 域 + threshold 注释失准)→ 已修 ✅ PASS
**常量开关**`INTENT_CONF_THRESHOLD = 0.7`(调 1.0 关闭收敛)
### P1`013ce21`)— 压缩增强 + 工具结果压缩
**根因**
3. `compress_old_messages` 压缩时丢主题词(用户反复提及的实体/技术名词是续接锚点),续接时 AI 找不回主线
4. tool_result 原样塞回上下文,>2KB 的搜索/读文件结果直接挤占 token基线95% 内容是 tool_result
**改进**
- 改进3 `context_helpers.rs` 压缩 prompt 加「主题/关键词保留段」;失败兜底从「裸裁剪」改 `extract_keyword_summary`user 消息词素 top-10 去停用词),首位插摘要作锚点
- 改进4 `should_summarize_tool_result`>2KB 或占比>40% 触发)+ `extract_key_info`(错误行 error/panic/失败 + 首尾各 5 行中间省略view-only 不改持久化(原始 tool_result 仍在 DB`build_for_request` clone 视图)
**单测**compress/keyword 2 + should_summarize + extract_key_info 14 = 16
**审查**CR-26🟡1tokenize 中文锚点弱:每汉字单成词素致"压缩/架构"2 字词被拆滤)→ P2 修 ✅ PASS
**常量开关**`KEYWORD_FALLBACK_ENABLED` / `TOOL_RESULT_COMPRESS_ENABLED`
### P2`a2db5c7`)— 主题检测保守 + tokenize 2-gram 汉字
**根因**
5. 主题切换无感知,切换后旧话题被带进新回答(基线 d6614e0b用户问"写文件为什么总写错日期"AI 仍在追旧话题"授权体验"的尾巴)
6. CR-26 🟡1汉字单字词素导致 2 字中文锚点("压缩""架构""审批")被 tokenize 拆滤,关键词摘要抓不住中文主题
**改进**
- 改进5 `context.rs` TrackedMessage.topic 字段push 推断 topic意图置信>=0.7 且非 Unknown+ `pending_topic_marker`(末两条 user topic 双高置信且不同才标,任一 None 不标防误报)+ loop 保守标记(`agentic:571` 顶部读 marker → insert system 软提示,不参与裁剪/压缩)
- tokenize 2-gram 汉字滑窗(`context_helpers.rs:294`):每汉字既单成词素又产出相邻 2-gram"压缩架构"→"压缩/缩架/架构"),非汉字边界重置窗口不跨边界组词
**单测**topic 检测 6 + tokenize 2-gram含不跨边界= 测并入 context 32 + context_helpers 增量
**审查**CR-26 🟡1 已闭环
**常量开关**`TOPIC_MARKER_ENABLED = true`false 跳过标记,排障/对比用)
**保守性**topic 不参与裁剪/压缩、TrackedMessage 不 Serialize只 ChatMessage 落库)、软提示非强制
### 三原则对齐
| 原则 | 落地 |
|------|------|
| **优雅** | filter 只改 LLM 可见 `tool_defs` 不动执行路径view-only 不改持久化topic 软提示不参与裁剪;常量开关可回退 |
| **可靠** | 三重 fallback意图置信/过滤数/空 subset压缩失败兜底关键词摘要view-only clone 视图topic 双高置信才标防误报 |
| **易迭代** | 4 个常量开关(`INTENT_CONF_THRESHOLD`/`KEYWORD_FALLBACK_ENABLED`/`TOOL_RESULT_COMPRESS_ENABLED`/`TOPIC_MARKER_ENABLED`)可独立开关排障;每批独立 commit + 审查回填tokenize 2-gram 是纯函数增量无破坏 |
---
## 三、改进前基线db 对话分析)
> 数据源:`C:/Users/23780/AppData/Roaming/top.1216.devflow/devflow-dev.db``ai_conversations.messages` JSON
> 挑样raw_len>500K 的长对话 3 个(均为 glm-5.2,改进前数据)
### 量化指标3 对话均值)
| 指标 | f89d129a | a95f5d6d | d6614e0b | 均值 |
|------|---------|---------|---------|------|
| 总消息数 | 251 | 213 | 211 | 225 |
| user / assistant / tool | 22 / 111 / 118 | 22 / 88 / 103 | 22 / 96 / 93 | 22 / 98 / 105 |
| tool_calls 总数 | 118 | 103 | 93 | 105 |
| 单轮 ≥3 工具并发 | 6 | 4 | 3 | 4 |
| tool_result >2KB | 39 | 34 | 44 | 39 |
| tool_result >10KB | 24 | 14 | 14 | 17 |
| **空 assistant纯工具 echo** | **69/111 (62%)** | **32/88 (36%)** | **41/96 (43%)** | **47%** |
| user 「继续」 | 5 | 2 | 0 | 2.3 |
| user ≤12 字短问 | 14/22 | 7/22 | 13/22 | 52% |
| **asst 文本 / tool 文本 比** | **0.05** | **0.04** | **0.05** | **0.05** |
**关键信号**
- **asst 文本仅占 4-5%**95% 上下文是 tool_result → 工具结果挤占严重改进4 对症)
- **47% assistant 轮次是纯工具 echo**content 空、只发 tool_calls→ LLM 不产出围绕 user 目标的文本被工具流程推着走改进1+2 对症)
- **单轮 4-12 工具并发** → 工具列表稀释意图改进2 收敛对症)
### 跑题现象归类5 类,含实例)
#### 类型1工具结果挤占最高频3/3 对话)
- **现象**tool_result 占 95% tokenasst 文本仅 4-5%。AI 回复被大块搜索/读文件结果淹没user 要的「结论」淹没在工具回显里。
- **实例**f89d129a [58]):用户问"我做哪些决策,多角度论证"AI 先并发 12 个工具读 4 个文件([9]-[49] 占 50+ 条消息),最后才在 [58] 一次性吐 6760 字决策分析。论证正确但被工具流程拖到 50 条消息后。
- **对症改进**P1 改进4 工具结果压缩(>2KB 触发 extract_key_info 保留错误行+首尾 5 行)。
#### 类型2全量工具分散意图
- **现象**:每轮把 29 个工具全塞 LLMAI 在工具列表里挑而非围绕 user 目标,表现为"读一堆无关文件试探"。
- **实例**d6614e0b [92]-[116]):用户问"消息发送交付功能"AI 并发 3 工具读 useAiEvents/useAiStream/useAiSend/useAiConversations/ai.ts/stream_recv/commands.rs/agentic.rs/persistence 9 个文件,最后才产出分析。
- **对症改进**P0 改进2 意图 filter_tool_defs 收敛 29→5-10。
#### 类型3压缩丢主题长对话续接跑题
- **现象**`compress_old_messages` 裁掉主题词后,续接时 AI 找不回主线,绕旧话题。
- **实例**d6614e0b [174]→[181]):用户问"最新 aichat 权限"AI 先读旧路径 `2025-07-14.md`(不存在),再搜索才发现文件已被手工改名为 `07-15`,绕了一圈。主题锚点("授权方案文件")在压缩中被丢。
- **对症改进**P1 改进3 压缩增强 + extract_keyword_summary 保留主题锚点。
#### 类型4多主题交织切换不干净
- **现象**用户切换话题AI 仍带旧话题尾巴进新回答。
- **实例**d6614e0b [140]→[153]):用户从"消息交付功能不足"切到"写文件为什么写错日期"AI 在 [141]-[152] 还在读 useAiSend/ai_tools 等旧话题文件,[153] 才答到日期根因。
- **对症改进**P2 改进5 主题检测 + pending_topic_marker 软提示。
#### 类型5无聚焦系统提示无独立抓手
- **现象**:系统 prompt 把「聚焦」混在「行为准则」被稀释AI 无明确"围绕当前请求核心"的指令约束。
- **实例**3 对话通用47% assistant 轮次纯工具 echo 不产出围绕 user 目标的文本。
- **对症改进**P0 改进1 系统提示独立聚焦段(中「聚焦准则」/ 英「Focus」4 条)。
### 基线小结
5 类跑题现象与 P0-P2 五条改进**一一对应**,改进设计精准对症基线,无遗漏主因。改进后预期:
| 基线现象 | 改进 | 机制测过 | 实跑预期 |
|---------|------|---------|---------|
| 工具结果挤占 | 改进4 | ✅ 14 测 | tool_result 压缩释放 token |
| 全量工具分散 | 改进2 | ✅ 7 测 | 工具收敛 29→5-10 |
| 压缩丢主题 | 改进3 | ✅ 2 测 | 主题锚点保留 |
| 多主题交织 | 改进5 | ✅ 6 测 | 切换软提示 |
| 无聚焦 | 改进1 | ✅ 4 测 | 独立聚焦段 |
---
## 四、遗留与风险
### 已知遗留
1. **LLM 实际响应效果需实跑验证** — 单测覆盖机制正确性filter/压缩/tokenize/topic 标记),但 LLM 在改进后 prompt + 收敛工具下的实际「抓重点」表现,需重启 dev 对话按 §一④ 场景实跑。机制正确 ≠ LLM 行为改善。
2. **主题检测 F1 天花板** — topic 推断依赖意图置信>=0.7(非 Unknown意图识别本身的准确率决定 topic 检测上限。短问(基线 52% user ≤12 字)意图置信常 <0.7,这类 topic 推断为 None不参与标记保守防误报的代价
3. **中文 tokenize 2-gram 仍有边界** — 3+ 字词("状态机""工作流")能由 2-gram 组合近似,但语义级词("推进链"3 字)仍可能被切;已优于单字方案,非完美。
### 未覆盖的跑题场景(建议补改进,基于基线)
基线 5 类已被 P0-P2 覆盖,未发现遗漏的**主因**。但有两类**次生现象**改进未直接覆盖,建议观察实跑后决定是否补:
1. **「继续」疲劳**f89d129a 5 次"继续")— AI 被工具流程拖到长尾user 不得不催。根因是工具流程过长类型1+2 的次生P0-P2 收敛工具+压缩后应缓解,但若实跑仍高频出现,可考虑加「单轮工具调用数硬上限」或「长工具流程中途主动给中间结论」。
2. **空 assistant 纯工具 echo**47%)— P0 改进1 聚焦段是软约束,不强制 assistant 每轮产出文本。若实跑 echo 率仍高,可考虑 prompt 加「工具调用前用一句话说明意图与当前请求的关系」的硬约束(当前基线已有"执行操作前简要说明你的意图",但未要求与当前请求关联)。
> 两项均为**次生/可选**,不阻塞当前迭代,建议实跑验证后再定。
---
## 附:数据可复现
db 分析脚本核心逻辑python sqlite3标准库
```python
import sqlite3, json
conn = sqlite3.connect(r'C:/Users/23780/AppData/Roaming/top.1216.devflow/devflow-dev.db')
# 按长度排长对话
conn.execute("SELECT id,title,LENGTH(messages) FROM ai_conversations ORDER BY 3 DESC LIMIT 5")
# 解析单对话
msgs = json.loads(row['messages'])
# 量化:空 assistantcontent 空但 tool_calls 非空)/ asst 文本 vs tool 文本比 / 单轮工具并发数
```
复现命令:见本任务分析脚本(已离线保存于 `/tmp/analyze_chat.py` + `/tmp/metrics.py`)。