release_notes_v1.0.0.6.md
Rama main · réplica de lectura del repositorio original.
# v1.0.0.6
## 记忆
**fix(memory): `memory_save` 写入前查重 + 暴露 `fact_key`**
同一条偏好(「禁止使用 file 工具」)在库里并存 5 条 active。真因不是「记忆系统不去重」——
`SessionMemoryExtractor` 的去重是完整的(Jaccard 0.85 + fact_key 受控词表归一),
但 LLM 在对话中途调 `memory_save` 时**直通 `store.add_async()` 绕过全部去重**,
且工具参数 schema 根本不暴露 `fact_key` → `MemoryStore.add` 的「同 key 冲突失效」
分支永不触发。两套本该互为保险的机制在这条路径上同时失效。
修复:写入前先查重——同 fact_key 命中 → `update` 覆盖(importance 取 max);
否则内容 Jaccard ≥ 0.85 → `update`;都不命中才新增。fact_key 归一复用抽取链的
同一套逻辑,LLM 传别名也会被归一到规范键。
**fix(memory): `MemoryStore.add_async` fact_key 透传**
`add_async()` 接收 `fact_key/confidence/evidence` 却没传给 `add()`——dead 参数被
默默丢弃。会话结束抽取走同步 `add()` 所以没暴露,但任何走 `add_async` 的路径
保存的 fact_key 都没落库。这是上一条能生效的前提。
## 技能引擎
**fix(skills): frontmatter 回退解析支持嵌套**
`_parse_frontmatter` 优先用 PyYAML,缺包时回退到**扁平**行解析。打包版不带 PyYAML,
于是 `metadata.triggers` 被解析成空串、子键平铺到顶层,`_load_skill` 里的
`md_meta.get("triggers")` 恒为空——agentskills.io 风格的技能触发词在打包版**静默失效**。
改为支持一层缩进嵌套 + `[a, b]` 列表解析,源码与打包版行为一致。
**perf(skills):** 逐技能 prompt 注入封顶 3500 字符(段落边界 → 行边界两级回退),
不再为超长技能文本每轮付出全额 token。
## 打包链路
**fix(platform): Python 探测与实际执行走同一条路径**
在只装了 Microsoft Store 占位符的 Windows 上(`python` 命令存在但执行即失败),
`get_python_cmd()` 无条件返回 `"python"`,从不验证可用性;而兜底逻辑只存在于
shell 工具的**执行阶段**。结果是「探测说可用、实际不可用」的两套逻辑分裂:
`self_heal` 的 pytest 自修复直接崩、system prompt 误导 agent 生成注定失败的命令。
现在改为先探测 `python --version` 能否正常退出(覆盖未安装 / 超时 / 非 0 退出
三种形态),探测失败且处于打包态时回退到应用自带解释器 —— 与 shell 工具的
兜底同源。system prompt 也会纠正 agent 的认知。
顺带修一个同场景的既存 bug:`self_heal` 用裸 `.decode()`(默认 UTF-8)解
ruff/pytest 输出,中文 Windows 上输出是 GBK → `UnicodeDecodeError` 让自修复
功能整体崩。改用项目里早就为这件事造好的 `decode_output()`。
> 附带:`verify_build.py` 此前对 PyInstaller 6.x 恒定误报 3 条 FAILED
> (6.x 把 .py 编译进 PYZ,`_internal/` 下本就没有 .py 文件)。已改为读 PYZ
> 归档的模块清单 + 显式检查 GUI/native 依赖,现在能真正发现漏收。
**fix(desktop): GUI 降级为系统浏览器(原生窗口回归)**
双击 exe 弹出的是浏览器窗口而非原生应用窗口。打包环境从 conda base 切到
`.venv` 后,`pythonnet` 与 `pywebview` 两个 GUI 必需包未安装——原生窗口靠
「pythonnet(clr) + WinForms + 手写 WebView2 窗口」,而
`Microsoft.Web.WebView2.Core/WinForms.dll` 这两个 .NET 程序集由 `pywebview`
携带(launcher 只借它的 dll,不调它的 API)。缺失时走降级分支
`webbrowser.open(url)`,也就是你看到的「网页版」。
已补依赖声明,并让 spec 在缺包时**构建期直接报错**而非静默跳过——GUI 降级
属于发布事故,不该静默通过。launcher 的降级告警也补上了原因和修复命令。
> 如果你从旧 zip 升级:删掉旧目录再解压新 zip,别叠加覆盖。
**fix(packaging): exe 启动崩 `_ssl` ImportError**
spec 里的 OpenSSL 强制替换逻辑假设打包机的 `sys.base_prefix` 是标准 CPython
(DLL 在 `DLLs/`)。实际用 conda 派生 venv,conda env 把 OpenSSL 放在
`Library\bin/` → 强制替换的 `if not isfile: continue` 静默跳过 → `_internal/`
里残留 PyInstaller 从 PATH 抓来的**错版** libcrypto/libssl → `_ssl.pyd` 加载时
导出函数对不上,exe 一启动就崩。
**fix(packaging): pycryptodome 漏收回归**
`wecom_crypto.py` 用 `from Crypto.Cipher import AES` 做企业微信消息加解密。
1.0.0.5 含 42 个 crypto 文件(借 conda base 环境隐式收集),换 venv 后断裂成 0 个
——用户用到企业微信功能会直接 ImportError。已装包并在 spec 显式收集。
## 上下文圆环
- **fix:** 修「刚进会话显示 8k 占用、问一句反而降到 5.7k」的倒挂。根因不是上下文
被清理,而是同一会话混用了两把尺子:冷启动无 API usage,只能用
`estimate_tokens(system_prompt + tools + messages)`;提问后拿到
`usage.prompt_tokens` 就切到实测值。`estimate_tokens` 对密集工具 JSON 系统性高估
约 2 倍(133 会话统计校准系数 ≈ 0.508),于是出现"用完反而变小"。
- 保留原估算公式(CJK 0.7/字、字母数字与空格 4 字符/token、其他符号 2 字符/token)
——它同时是压缩触发依据,必须保守,不能为显示好看而调低。改为在**展示侧**用固定
前缀(system prompt + 工具 schema)的实测值校准估算值。
- 校准值落盘 `fixed_prefix_calibration.json`,临时文件 + `os.replace()` 原子替换,
重启后冷启动即可复用;缓存 key 含 system prompt 哈希与工具数量,支持动态加载工具。
- 圆环明细区分 `real` / `calibrated` / `estimate` 三种来源,只有真正 `estimate` 才带
`~` 前缀;仅对高度重复的 `tools` 项缩放,不缩放中文系统提示项。
- 回归测试 `tests/unit/test_context_meter_calibration.py`,25 条。
## 其他
- **fix(desktop):** `launcher.log` 不再依赖 `sys.stdout is None` 判断;`__main__`
增加未捕获异常堆栈落盘。exe 启动异常此前静默退出、无从排查。
- **feat:** 接入点模型目录——从 OpenAI 兼容端点 `GET /v1/models` 拉取真实可用模型
ID,替代硬编码厂商表;设置页外挂视觉模型下拉不再凭记忆手填。
- **feat:** 聊天链路异常映射为可操作中文提示(`friendly_errors`),此前把
`str(e)` 原样推给前端,用户看到 "Connection error." 无从下手。
- **feat:** 会话恢复增强、视觉探测诊断工具、`db_shrink_archive` 脚本。
- **test:** 移除 `test_context_engineering.py` 中重复粘贴两遍的前缀静态化契约测试块
——后者覆盖前者,导致第一份断言从未执行。
- **chore:** `.gitignore` 补 `tools/promo/`(8600+ 临时截图与拼装脚本)。
## 验证
```
Tests: pytest tests/ → 1034 passed, 15 skipped
Lint: ruff check → All checks passed
Build: PyInstaller → 0 hidden import errors
Verify: verify_build → ALL CHECKS PASSED
(PYZ 4474 模块含 scout.core.platform / scout.engine.self_heal /
Crypto.Cipher.AES;WebView2 三件套 + pythonnet 就位;
VERSION hash 一致)
Smoke: exe --no-gui → /chat 200, /api/version/check = 1.0.0.6
GUI: exe 原生窗口 → webview dll 就位 / clr.AddReference OK / MainForm Shown
Zip: 解压到干净临时目录实跑 → 顶层结构正确, GUI 原生窗口, /chat 200, 版本号 1.0.0.6
```
绿色版:`scout-agent-1.0.0.6-win-x64.zip`(56.0 MiB / 1523 文件)
sha256 `125d3db12747944b2a4be867b4b752f26f6813f00433042067741b1a67a4400c`
> 体积比 1.0.0.5(135MB)小 79MB,是 spec 有意排除 `onnxruntime`(−18MB)与
> `pymupdf/fitz`(−38MB,PDF 改走 pypdf)的瘦身,非漏文件。
>
> 数据目录随 exe 所在盘符变化(取 `sys.executable` 的盘符根 `\.scout`),
> 装在 D 盘就读 `D:\.scout\`,日志在同目录 `launcher.log`。