mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-11-06 17:27:31 +00:00
fix(core): contextoverflow 改回空中断(按原始设计)+ Python 解释器可配置
## 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 个调用点)。
This commit is contained in:
@ -443,6 +443,20 @@ func (a *Agent) prepareInputTask(evt *agentIO.InputEvent) (*TaskFrame, taskTermi
|
||||
}
|
||||
}
|
||||
|
||||
// ★★ 空中断(kernel/overflow):不做任何对话处理,只做语义相关性裁剪。
|
||||
//
|
||||
// 这是 contextoverflow 的**原始设计**(2026-10-25 按用户口径修正):
|
||||
// L4 中断本身不携带消息,唯一用途是把当前任务抢占下来,好在任务边界
|
||||
// 做一次语义相关性裁剪。此前实现把它反过来了(先裁剪、再发一条
|
||||
// 「已裁剪 N 条…」的通知),那等于把调度信号变成了回执。
|
||||
//
|
||||
// 因此这里:裁剪 → 结束本轮,**不进 LLM、不写上下文、不发回复**。
|
||||
// 用 terminalSkipped(不是 terminalError):这是正常完成,不是故障。
|
||||
if in.airInterrupt {
|
||||
a.pruneForAirInterrupt()
|
||||
return nil, terminalSkipped
|
||||
}
|
||||
|
||||
// 工具提醒/中断(terminal_watch、timer 等)不是用户发言:
|
||||
// 以 system 角色注入 LLM,且不写入用户对话履历。
|
||||
isInterrupt, _ := evt.Payload["interrupt"].(bool)
|
||||
@ -706,12 +720,18 @@ func (a *Agent) stepLLM(f *TaskFrame) stepOutcome {
|
||||
if f.Terminal == terminalError {
|
||||
return outcomeFailed // 恢复预算耗尽,f.Err 已写明
|
||||
}
|
||||
// 已裁剪。**必须退回 StepPrepare 重建 f.Msgs**:
|
||||
// f.Msgs 只在 stepPrepare 里由 buildMessages 装配,stepLLM 不重建;
|
||||
// 而挂起帧里那份就是刚刚超限的那份,复用它等于原样再发一次。
|
||||
f.Turn++
|
||||
f.Step = StepPrepare
|
||||
return outcomeContinue
|
||||
// ★ 已发出 L4 空中断,**裁剪尚未发生**(它在空中断任务被调度
|
||||
// 执行时才做)。所以这里不能让本帧原地重跑:
|
||||
//
|
||||
// · 原地重跑 = 拿同一份超限内容再发一次,必然再次超限
|
||||
// (这正是 recover budget 会被快速烧光的原因);
|
||||
// · 真正的裁剪发生在任务边界,也就是**本任务让位之后**。
|
||||
//
|
||||
// 语义上本帧的职责到此为止:把现场交出去(outcomeSuspended),
|
||||
// 由空中断任务抢占执行、完成裁剪,随后本帧再被恢复。
|
||||
//
|
||||
// 与"用户按停止"不同:那不是让位,是终结。两者不能混。
|
||||
return outcomeSuspended
|
||||
}
|
||||
// 未处理(如轻量内核不裁剪):落到下面按普通错误处理。
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user