feat(loop): Top5#5 上下文溢出压缩 + 每轮 assemble(对齐 OpenClaw context-engine)——Top5 全完成
- 每轮 assemble(transformContext 等价):run.go 每 step 前 assembleContext——依次 Inject(每轮 重新 Retriever.Inject 不缓存首轮)、WorkingMemory 前置 Summarize、可插拔 TransformContext 投影/ 过滤;生成的 view 交 step,msgs 保持纯对话供工具结果追加,记忆不重复注入、不写回 checkpoint。 - Compactor:结构分支感知压缩——estimateContextTokens 启发式超 MaxContextTokens 且消息≥3 才动; findKeepStart 按 user 消息切整轮从后往前累加(至少保留最后一整轮→尾随工具结果绝不切片/不跨轮); 旧轮 user/assistant 对压成一条 RoleSystem 摘要置顶,既有摘要前缀识别合并防叠;capSummary 上限 4000 rune;ctx 即 abortSignal(取消返回 ctx.Err,压缩失败仅记日志不中断 run)。 - 选项:Options 增 MaxContextTokens/Compactor/TransformContext,均 nil-tolerant(默认=现有行为, 零值不压缩);DefaultMaxContextTokens=32768。Compactor 接口预留注入 LLM 摘要。 go build/vet/test -count=1 全绿 + windows/darwin 交叉编译。12 断言覆盖尾随工具结果保留/不跨轮切割/ 摘要上限/摘要合并/ctx 取消/每轮重检索/TransformContext 逐轮/超限压缩/默认关闭。-race 受宿主 TSan VMA 限制无法运行(与代码无关)。 **至此 Top5(1~5) 全部完成并推库。**
This commit is contained in:
1 parent
35cb2d3942
commit
648292d035
7 files changed
+709
-31
No files matched your search
@@ -9,10 +9,12 @@ import (
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/trace"
|
||||
)
|
||||
|
||||
// Run drives the agent loop: inject relevant memory into the first-turn context,
|
||||
// then run up to MaxRounds steps. Each step runs the model (streaming through
|
||||
// OnStream when configured) and any requested tool calls are executed under a
|
||||
// per-invoke timeout and fed back as RoleTool messages so the model can continue.
|
||||
// Run drives the agent loop: assemble a fresh model-visible context every round
|
||||
// (re-inject relevant memory from the current conversation, optionally compacting
|
||||
// the older turns when the context exceeds the configured budget), then run up to
|
||||
// MaxRounds steps. Each step runs the model (streaming through OnStream when
|
||||
// configured) and any requested tool calls are executed under a per-invoke
|
||||
// timeout and fed back as RoleTool messages so the model can continue.
|
||||
// A checkpoint is persisted at every step, every step is traced as a
|
||||
// state_transition span (with an llm_call child and tool_call spans for each
|
||||
// invocation), and after each step the memory Extractor runs with errors logged
|
||||
@@ -29,7 +31,7 @@ func (l *Loop) Run(ctx context.Context, sessionID string, messages []model.Messa
|
||||
return nil, fmt.Errorf("loop: run: %w", ErrNilAgent)
|
||||
}
|
||||
|
||||
msgs := l.injectMemory(ctx, messages)
|
||||
msgs := messages
|
||||
toolState := &toolLoopState{prevFP: make(map[string]bool)}
|
||||
res := &Result{}
|
||||
for step := 1; step <= l.maxRounds; step++ {
|
||||
@@ -37,8 +39,14 @@ func (l *Loop) Run(ctx context.Context, sessionID string, messages []model.Messa
|
||||
return nil, fmt.Errorf("loop: run: step %d: %w", step, err)
|
||||
}
|
||||
|
||||
// Compact older turns when the conversation exceeds the context budget,
|
||||
// then assemble the model-visible context for this round (fresh memory
|
||||
// injection + transform hook), so every step sees an up-to-date context.
|
||||
msgs = l.compactIfNeeded(ctx, msgs)
|
||||
view := l.assembleContext(ctx, msgs, step)
|
||||
|
||||
rc, roundSpan := l.span(ctx, "loop.step", trace.SpanKindStateTransition)
|
||||
outcome, err := l.step(rc, sessionID, msgs)
|
||||
outcome, err := l.step(rc, sessionID, view)
|
||||
if err != nil {
|
||||
l.spanProp(roundSpan, "error", err.Error())
|
||||
l.finishSpan(roundSpan, trace.SpanStatusError)
|
||||
|
||||
Reference in new issue
Block a user