mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-11-01 14:57:38 +00:00
## 1. contextoverflow:L4 空中断(推翻三处偏移) 用户口径:**原始设计是注入 L4 空中断,目的是触发语义相关性裁剪, 不含任何消息,就是一个空中断。**此前的实现有三处偏移: ① **中断带正文** —— `Build` 生成「已裁剪 N 条低相关事件到文档记忆…」, 于是 L4 变成一条要模型回应的消息,白烧一整轮推理。 ② **裁剪在 raise 之前做完** —— 先 `pruneByQuery` 再 raise,中断沦为 「裁剪已完成」的回执,晚了一步。 ③ **上游 contextfull 后原地重跑** —— `StepPrepare` 重建后拿**同一份** 超限内容再发一次,必然再超限,快速烧光 recoverBudget。 (这条是修① ② 时暴露的连带问题。) 改动: · `VectorContextOverflow.Build` 返回空串 ⇒ 空中断;级别仍是 L4 · 删除 `overflowNotice` 与 `vectorArgs.Pruned`(中断不需要知道裁了多少) · `handleContextOverflow` 不再裁剪,只发空中断,如实返回 0 · 新增 `pruneForAirInterrupt`:裁剪由空中断任务**真正执行时**触发, query 取上下文里最后一条真实用户输入作相关性基准 · 新增 `isAirInterrupt`:只认「内核来源 + type=interrupt + 无正文」, 插件带正文的中断不受影响(否则"插件发了个空中断"会变成无声裁剪) · `prepareInputTask` 识别空中断 → 裁剪 → terminalSkipped (不进 LLM、不写上下文、不发回复) · 上游 contextfull 后改为 `outcomeSuspended`(让位等裁剪),不再原地重跑 顺带记录偏移成因(`raiseContextOverflow` 注释):当时的**观察**是对的 (L4 看不到被打断者上下文 ⇒ 模型会把"没检索到"推断成"不存在"), 但**结论**错了——它把"补偿 L4 看不到上下文"当成中断该带正文的理由。 模型需要的不是"知道有内容被移走",而是**留下来的内容与当前问题相关**。 判据(均经变异验证): · `TestVectors_OverflowIsAirInterrupt` —— 正文必须为空 + 级别 L4 变异:Build 改回非空 ⇒ 红 · `TestOverflow_RaisesAirInterruptWithoutPruning` —— 发中断那步不改上下文 变异:加回 pruneByQuery ⇒ 红 · `TestAirInterrupt_ResolvedAsValidInput` —— 空中断须被认作有效输入 · `TestAirInterrupt_RejectsOrdinaryEmptyInput` —— 普通空包/插件带正文 中断**不得**误判 · 改写 5 条断言"发中断即裁剪"的旧测试(T1/T2/T5/大事件/自适应), 保留它们原本守的东西(超限判定、恢复预算、终止保护), 只把裁剪步骤显式移到 `pruneForAirInterrupt` ## 2. clawhubadapter:Python 解释器与工作目录可配置 真机事故:`bilibili-all-in-one` 声明 requirements 含 httpx,系统 Python 没装 ⇒ 启动即 `ModuleNotFoundError`。而解释器此前是**硬编码猜**的 (先 /usr/bin/python3,再 /usr/local/bin/python3)。 「猜」没有正确解:不同部署的 Python 布局完全不同(发行版 Python、 pyenv、uv、conda、venv…)。而往系统 Python 里 pip install 是**错的**: PEP 668 明确禁止,`--break-system-packages` 会破坏 apt 包一致性。 新增两个配置项(与既有 skills_dir/simulator_dir 同一风格): · `python_bin` —— 解释器绝对路径,最高优先 · `skill_workdir` —— skill 子进程工作目录 回退顺序:配置 → `<skill>/.venv/bin/python` → 系统 python3。 **配错必须响亮**:配置的路径不存在时返回 error,不静默退回系统解释器 (否则用户以为配好了,实际跑在缺依赖的 Python 上 —— 又一次静默降级)。 判据:`TestPickPython_ConfiguredWins`(配置优先于 venv)/ `TestPickPython_VenvWhenNotConfigured` / `TestPickPython_MissingConfiguredIsLoud` `launchProcessIn` 是 `launchProcess` 的工作目录变体(不改既有 4 个调用点)。
64 lines
2.4 KiB
Go
64 lines
2.4 KiB
Go
package clawhubadapter
|
||
|
||
import (
|
||
"os"
|
||
"path/filepath"
|
||
"testing"
|
||
)
|
||
|
||
// ── Python 解释器选择优先级 ──────────────────────────────────
|
||
//
|
||
// 背景(2026-10-25 真机事故):bilibili skill 声明 requirements 含 httpx,
|
||
// 系统 Python 没装 ⇒ 启动即 ModuleNotFoundError。而此前解释器是**硬编码猜**的:
|
||
// 先 /usr/bin/python3,再 /usr/local/bin/python3。
|
||
//
|
||
// 「猜」没有正确解:不同部署的 Python 布局完全不同(发行版 Python、
|
||
// pyenv、uv、conda、venv…)。所以改为配置显式指定 + venv 探测兜底。
|
||
|
||
// 判据:配置了 python_bin 且可用 ⇒ 必须用它,优先于 venv 与系统解释器。
|
||
//
|
||
// 这是"配置生效"的唯一证据。没有它,配了却仍走系统解释器也不会被发现
|
||
// (正是本次事故的形态:静默降级)。
|
||
func TestPickPython_ConfiguredWins(t *testing.T) {
|
||
dir := t.TempDir()
|
||
// 造一个假的 venv 解释器,确保"配置优先于 venv"被真正验证
|
||
venvBin := filepath.Join(dir, ".venv", "bin")
|
||
os.MkdirAll(venvBin, 0755)
|
||
venvPy := filepath.Join(venvBin, "python")
|
||
os.WriteFile(venvPy, []byte("#!/bin/sh\n"), 0755)
|
||
|
||
cfgPy := filepath.Join(dir, "custom-python")
|
||
os.WriteFile(cfgPy, []byte("#!/bin/sh\n"), 0755)
|
||
|
||
got := pickPythonFor("bilibili", dir, cfgPy)
|
||
if got != cfgPy {
|
||
t.Fatalf("配置的 python_bin 应最高优先,期望 %s,实际 %s", cfgPy, got)
|
||
}
|
||
}
|
||
|
||
// 判据:没配 python_bin 但 skill 有 venv ⇒ 用 venv。
|
||
func TestPickPython_VenvWhenNotConfigured(t *testing.T) {
|
||
dir := t.TempDir()
|
||
venvBin := filepath.Join(dir, ".venv", "bin")
|
||
os.MkdirAll(venvBin, 0755)
|
||
venvPy := filepath.Join(venvBin, "python")
|
||
os.WriteFile(venvPy, []byte("#!/bin/sh\n"), 0755)
|
||
|
||
if got := pickPythonFor("x", dir, ""); got != venvPy {
|
||
t.Fatalf("无配置时应优先 skill 自带 venv,期望 %s,实际 %s", venvPy, got)
|
||
}
|
||
}
|
||
|
||
// 判据:配置了但**不存在** ⇒ 必须报错,不能静默退回系统解释器。
|
||
//
|
||
// 静默退回的后果:用户以为配好了,实际跑在缺依赖的 Python 上 ——
|
||
// 这正是本次要修的「静默降级」那一族。
|
||
func TestPickPython_MissingConfiguredIsLoud(t *testing.T) {
|
||
dir := t.TempDir()
|
||
bad := filepath.Join(dir, "no-such-python")
|
||
_, err := resolvePython("x", dir, bad)
|
||
if err == nil {
|
||
t.Fatal("配置的 python_bin 不存在时必须报错(静默退回会让用户以为配置生效)")
|
||
}
|
||
}
|