- df-ai sanitize 新增 step 0.5 reorder_interlopers_after_tool_run: 楔入 head 与 tool 响应间的异类消息(压缩摘要/用户插话/警示)移到该组响应之后,只重排不删内容,工具轮不再被剥除 - 幂等;仅处理单头+夹层+紧随连续 tool 段,其余畸形仍走剥除兜底 - 单测: 旧剥除用例改为归位保轮 + 新增幂等用例;cargo test -p df-ai --lib context 156 passed - 实证更正: 2026-08-04 报告「实证4」被推翻——已定位原始会话 b4d6b4e0,首轮裸问候即发两个 tool_calls;同步更新看板 909 状态
98 lines
6.1 KiB
Markdown
98 lines
6.1 KiB
Markdown
# aichat 历史会话实证诊断 —— 弱模型工具行为失控
|
||
|
||
> 日期:2026-08-04 | 类型:会话实证分析(走查,未实施代码) | 关联:[待办 AC-1~5](../todo.md)
|
||
|
||
## 触发背景
|
||
|
||
用户在 DevFlow aichat 发"你好",模型(sensenova-6.7-flash-lite)却调用:
|
||
- `list_project_modules({"project_id": "DevFlow"})` → 空
|
||
- `search_files({"pattern": "scan", "path": "E:/wk-lab/devflow/src-tauri", "recursive": true})`
|
||
|
||
## 数据来源与方法
|
||
|
||
- prod 库:`Roaming/top.1216.devflow/devflow.db`(92MB,76 会话/9928 消息/6392 工具执行)
|
||
- dev 库:`Roaming/top.1216.devflow/devflow-dev.db`(59MB,98 会话/7853 消息,trace 日志确认当前实例所用)
|
||
- 方法:复制副本 + node:sqlite 只读查询,按会话 dump 消息序列 + 工具执行记录反推行为
|
||
|
||
> 注:用户贴的"你好"会话原始调用(`"project_id":"DevFlow"` + `pattern:"scan"`)在三个库中均**未检索到**,
|
||
> 疑未落库或已清理。但同类行为在库中有大量实证,结论不依赖该单例。
|
||
>
|
||
> **✅ 2026-10-10 定位恢复(推翻上文"未检索到")**:该会话已实证定位——`devflow-dev.db` 会话
|
||
> `b4d6b4e0-6901-4937-af47-d3202678406b`(标题「打招呼」,模型 `sensenova-6.7-flash-lite`,创建
|
||
> 2026-08-04 04:00:46 UTC)。`ai_conversations.messages` blob 恰为该轮:`[user] "你好"` +
|
||
> `[assistant] 空文本 + tool_calls=[list_project_modules, search_files]`;`ai_tool_executions`
|
||
> 两行逐字对上截图(`{"project_id": "DevFlow"}`→`{"modules":[],"project_id":"DevFlow","total":0}`;
|
||
> `{"pattern":"scan","path":"E:/wk-lab/devflow/src-tauri","recursive":true}`→`{"has_more":false,...,"total":0}`),
|
||
> tool_call_id 为弱模型特征 `gen_stream_4`/`gen_stream_23`,message_id `msg_1785816059122_5`。
|
||
> 原检索为空的可能原因:该轮未落 `ai_messages`(该会话 ai_messages 最早为 2026-08-06),仅存于 blob+executions 表。
|
||
|
||
## 实证 1:重复探索是系统性现象(非偶发)
|
||
|
||
**同会话同工具同参数 ≥3 次的重复组合:83 个**(6392 次执行中)。
|
||
|
||
典型实例:
|
||
|
||
| 会话 | 重复 | 内容 |
|
||
|------|------|------|
|
||
| `09e7abfa` | 目录列 3 次 + 读同文件 6 次 | `list_directory(devflow)` → `(devflow,max_depth:2)` → `(devflow,recursive:false)` → `run_command(Get-ChildItem)`;`PROGRESS.md` 被 read_file(3 种 limit)+ run_command Get-Content 读 6 次 |
|
||
| `f89d129a` | read_file 同文件 6 次 | `docs/待决策.md` |
|
||
| `e0b5d8fc` | update_task 同参 8 次 | 同 field/id/value 反复提交 |
|
||
| `b505d7c7` | patch_file / grep / list_directory 各 5-6 次 | 同参数反复 |
|
||
| `f64dee94` | file_info × 3 文件各 4 次 + list_projects 4 次 | 重复状态查询 |
|
||
|
||
机制佐证:`prompt.rs:107` 已写"重复调用检测"规则,但**对弱模型纯说教无效** —— 需要机制层拦截。
|
||
|
||
## 实证 2:传 name 当 id,系统清单不给 UUID
|
||
|
||
- `list_tasks(project_id:"DevFlow")` 出现于 8+ 个不同会话
|
||
- `"meta-kit"` / `"devflow"` / `"HaoGamePlatfProject"` / `"u-ask"` / `"planning"` 等 name 全被当 id 传
|
||
- **全部返回空**(`list_tasks` 需 UUID),模型不理解"为什么空",继续换工具瞎探
|
||
|
||
根因:`prompt.rs:304` 注入项目清单格式为 `- {name} ({status}): {description}`,**无 UUID**。模型只能拿 name 猜。
|
||
|
||
## 实证 3:read_symbol 被弃用,AST 代码智能落地空转
|
||
|
||
- read_file **2582** 次 vs read_symbol **36** 次(采用率 ~1.4%)
|
||
- 近 15 个 read_file 会话中 read_symbol 几乎全为 0(276:0, 166:0, 161:3, 139:3...)
|
||
- `prompt.rs:98` 大段引导"优先 read_symbol",弱模型基本不用 → AST 代码智能(7c2e3b2,目标降 token 24.4x)未生效,全文回灌照旧
|
||
|
||
## 实证 4:问候本身不触发工具,但弱模型任务中失控
|
||
|
||
- 修正:纯"你好"第一轮模型会正常回复能力介绍(09e7abfa/1982ff08 均如此),**0 例**"问候后紧邻调工具"
|
||
|
||
> **⚠️ 2026-10-10 更正(实证推翻本条)**:会话 `b4d6b4e0`(标题「打招呼」)的**首轮**即是裸问候
|
||
> 「你好」→ assistant **空文本 + 直接发 2 个 tool_calls**,无任何前置上下文。故"问候本身不触发工具"
|
||
> 不成立——至少对 `sensenova-6.7-flash-lite` 存在**首轮问候即开工具**的实例。原"0 例"结论源于当时未
|
||
> 检索到该会话(其轮次未落 ai_messages),属样本缺失导致的误判。据此,「工具全量暴露 + 弱模型自控弱」
|
||
> 的诱因比原判更严重:**无需任务上下文,裸问候即可触发工具探索**。
|
||
- 用户贴的"你好犯傻"更可能是:会话有历史上下文(scan/工程模块任务残留),模型把"你好"当继续信号,或 system prompt 全量 55 工具 + "优先使用工具"引导诱使弱模型"准备上下文"
|
||
- 一旦进入实质任务,弱模型即进入重复探索/绕行/参数错误循环
|
||
|
||
## 实证 5:失败工具画像(浪费的往返)
|
||
|
||
| 工具 | 失败次数 | 类别 |
|
||
|------|---------|------|
|
||
| run_command | 67 | 命令执行失败/超时 |
|
||
| read_file | 55 | 路径错/授权 |
|
||
| search_files | 33 | 路径/参数 |
|
||
| advance_task | 31 | 状态机拒绝(非法迁移) |
|
||
| patch_file | 27 | 精确匹配失败 |
|
||
| delete_file | 22 | 授权/路径 |
|
||
|
||
## 根因归纳
|
||
|
||
1. **工具全量暴露**:`Intent::Chat → &[]`(intent.rs:462)→ `filter_tool_defs` 回全量 55 工具(intent.rs:520)。弱模型面对全量工具无法自控。
|
||
2. **弱模型能力天花板**:sensenova-6.7-flash-lite 判别"结果已足够/该停手"的能力弱,参数映射(name→id)能力弱。
|
||
3. **机制缺位**:重复调用只靠 prompt 说教,无机制拦截;探索无预算上限。
|
||
4. **上下文注入缺陷**:清单无 UUID;超大会话(504msgs/363tools)漂移放大。
|
||
|
||
## 待办映射
|
||
|
||
| 待办 | 内容 | 机制杠杆 |
|
||
|------|------|---------|
|
||
| AC-1 | 同参数重复调用缓存/拦截 | `agentic/mod.rs` 工具执行前查重,≥2 次返缓存结果 |
|
||
| AC-2 | 探索预算上限 + 目录列举去重 | loop 内轮次工具计数 + list_directory 结果快照 |
|
||
| AC-3 | 清单注入补 UUID | `prompt.rs:304` 项目行加 id(或注入 name→id 映射表) |
|
||
| AC-4 | read_symbol 机制化采用 | read_file 命中已注册符号时追加引导提示/自动换 read_symbol |
|
||
| AC-5 | 失败工具逐类复盘 | run_command 超时/advance_task 状态机提示前置 |
|