mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-11-06 17:27:31 +00:00
fix(core): 裁剪收归基础设施,contextfull 只作为触发信号
## 问题
同一件事(上下文装不下)有三套实现、三份防护差异:
task.go 发 LLM 前 maybeHandleContextOverflow(有恢复预算)
task.go 上游报错 handleUpstreamContextFull(**直调内部,绕过预算**)
resident.go 轮首 checkContextFull(只对驻留子生效)
其中 handleUpstreamContextFull 是"另起炉灶":它自己又调一遍
handleContextOverflow,绕过 f.OverflowRecover 预算——而文档 §四.1 明确
「L4 遇 L4 不能抢占,反复触发只会排队堆积」。
## 改法
裁剪成为**基础设施**,contextfull 只是「判定已被外部短路」的一种入参:
enforceContextBudget(f, query, force)
force=false 本地水位:积累量 > ratio × 窗口
force=true 外部已确认超页(provider 返回 ErrContextFull)
之后的一切(恢复预算、裁剪、raise L4)完全相同。handleUpstreamContextFull
删除,只剩 isUpstreamContextFull 这个纯判定。
## 任务恢复入口补裁剪
中断任务跑完会把它的输入/输出提交进 a.context,恢复时重建出的前缀可能
**比被挂起时更大**。不在这里裁,这份更大的前缀会被原样发出去。
恢复用的是专用入口 enforceResumeContextBudget,两处语义与通用入口不同:
1. **query 取本帧最新有效上下文**(resumeQueryFrom 从 f.Msgs 尾部往回找),
不是 f.Input。被挂起的任务可能隔了很久,f.Input 早已与当前对话脱节;
拿旧 Input 算相关性,会裁掉"对该任务重要、但字面上不匹配旧输入"的事件。
2. **保护范围是本帧自己的现场**(len(f.Msgs)-f.PrefixLen),不是全局末尾
N 条。被挂起任务的现场可能是一整批工具结果,若落在保护范围外被归档,
恢复后的任务引用的内容已不在上下文里,任务自相矛盾。
为此新增 pruneForResume + contextTopKForProtected——不能复用 contextTopK,
它内部会把 topK 抬到 effectiveProtectedCount()+1(全局保护),两套下限
打架会让恢复场景「裁不动」(实测 protected=8、20 条上下文时 topK 被抬到 20)。
## 不变的部分
声明式裁剪(业务策略)语义原样保留:
· 中断/消息注入 —— pruneOnInput,判据 payload.context_policy / ChannelDef
· 工具内容注入 —— stepToolAfter,判据 ToolDef.ContextPolicy
两者走 memoryPass → pruneByQuery,与容量保护共用底层,但职责正交。
判据:任务恢复入口必须裁、恢复查询取本帧最新、保护本帧现场、
恢复预算对所有入口生效。变异验证:恢复不裁/保护退回全局/上游绕过预算,
三种坏法均被抓。
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -153,6 +153,76 @@ func (a *Agent) pruneByQuery(query string) int {
|
||||
return a.context.PruneWithProtected(query, topK, a.docStore, a.effectiveProtectedCount())
|
||||
}
|
||||
|
||||
// pruneForResume 是按「指定保护条数」执行的裁剪,供任务恢复入口使用。
|
||||
//
|
||||
// 与 pruneByQuery 的唯一差别是 protected 的来源:
|
||||
//
|
||||
// pruneByQuery protected = a.effectiveProtectedCount()(全局末尾 N 条)
|
||||
// pruneForResume protected = 调用方给的条数(该任务帧自己的现场)
|
||||
//
|
||||
// 为什么恢复要用后者:被挂起任务的现场在 f.Msgs[PrefixLen:] 里,
|
||||
// 可能是一整批工具结果。若按全局末尾 N 条保护,那些结果可能落在保护范围外
|
||||
// 被归档——恢复后的任务引用的内容已不在上下文里,等于自相矛盾。
|
||||
func (a *Agent) pruneForResume(query string, protected int) int {
|
||||
if a == nil || a.context == nil || a.isLightKernel() {
|
||||
return 0
|
||||
}
|
||||
if protected < 0 {
|
||||
protected = 0
|
||||
}
|
||||
// ★ 不能直接复用 contextTopK():它内部会把 topK 抬到
|
||||
// effectiveProtectedCount()+1(全局末尾保护),而恢复场景的保护范围是
|
||||
// **本帧现场**,两者不是同一个数。直接用会得到「按全局保护算出来的
|
||||
// 下限 ≥ 实际条数」⇒ 裁不动(实测 protected=8、上下文 20 条时
|
||||
// topK 被抬到 20,Prune 空转)。
|
||||
//
|
||||
// 这里按**本帧保护条数**重新推导:先算 token 预算能装几条,
|
||||
// 再保证下限是 protected+1(否则 Prune 会把 protected 之外的裁光)。
|
||||
topK := a.contextTopKForProtected(protected)
|
||||
return a.context.PruneWithProtected(query, topK, a.docStore, protected)
|
||||
}
|
||||
|
||||
// contextTopKForProtected 是按**指定保护条数**推导保留条数的版本。
|
||||
//
|
||||
// 与 contextTopK 的唯一差别是下限:后者用 effectiveProtectedCount()(全局末尾
|
||||
// 保护),这里用调用方给的 protected。恢复场景必须用后者——保护范围是本帧现场。
|
||||
func (a *Agent) contextTopKForProtected(protected int) int {
|
||||
if protected < 0 {
|
||||
protected = 0
|
||||
}
|
||||
hardCap := a.maxContextSize - 1
|
||||
if hardCap < protected+1 {
|
||||
hardCap = protected + 1 // 保护范围本身超过运维上限时,保护优先
|
||||
}
|
||||
if hardCap < 1 {
|
||||
hardCap = 1
|
||||
}
|
||||
|
||||
budget := a.computeTokenBudget()
|
||||
tokens := budget.ContextTokens
|
||||
if tokens <= 0 {
|
||||
return hardCap
|
||||
}
|
||||
avg := 0
|
||||
if a.context != nil {
|
||||
if n := a.context.Len(); n > 0 {
|
||||
avg = a.accumulatedTokens() / n
|
||||
}
|
||||
}
|
||||
if avg <= 0 {
|
||||
avg = defaultAvgEventTokens
|
||||
}
|
||||
topK := tokens / avg
|
||||
if topK < protected+1 {
|
||||
// 预算装不下「保护条数 + 1」⇒ 宁可暂时超页也不破坏现场(同 contextTopK 的取舍)。
|
||||
topK = protected + 1
|
||||
}
|
||||
if topK > hardCap {
|
||||
topK = hardCap
|
||||
}
|
||||
return topK
|
||||
}
|
||||
|
||||
// contextTopK 推导裁剪后应保留的**条数**。
|
||||
//
|
||||
// ★ 为什么不能直接用 maxContextSize(2026-10-01 跑分实测出的根因):
|
||||
|
||||
Reference in New Issue
Block a user