Files
Cyrene_v2/internal/runtime/loop/options.go
T
Aska 648292d035
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
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) 全部完成并推库。**
2026-09-06 18:08:55 +08:00

90 lines
4.6 KiB
Go

package loop
import (
"context"
"log/slog"
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/agent"
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/guardrail"
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/memory"
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/model"
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/persist"
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/tool"
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/trace"
)
// Options configures a Loop. All fields are optional and nil is tolerated until
// the step that actually needs the field: the agent is required for Run (Run
// returns ErrNilAgent otherwise); a missing tool dispatcher causes a requested
// call to be fed back as an error; a missing retriever/persona skips memory
// injection; a missing extractor/store skips the memory hook; a missing
// checkpoint skips persistence; a missing tracer skips tracing.
type Options struct {
// Agent is the loop's execution unit: an instruction string plus a backend
// adapter, an optional tool dispatcher and optional guardrails.
Agent *agent.Agent
// Tools is the tool dispatcher used to execute model-requested calls. When
// nil the loop falls back to the agent's own dispatcher (if any).
Tools *tool.Dispatcher
// Memory is the long-term memory store used to persist extracted items.
Memory *memory.Store
// Retriever injects relevant memories into the context, re-evaluated every
// round (see assembleContext) so retrieval tracks the latest conversation.
Retriever *memory.Retriever
// WorkingMemory is the per-session sliding window, provided for the
// orchestrator to inspect; the loop does not mutate it.
WorkingMemory *memory.WorkingMemory
// Extractor derives memory items to persist after every step.
Extractor memory.Extractor
// Checkpoint persists a durable snapshot of the loop state at every step.
Checkpoint *persist.Store
// Tracer emits a structured span for every step, LLM call and tool call.
Tracer *trace.Tracer
// Guards validates request/response payloads on the streaming path, where
// RunStep is bypassed. On the non-streaming path the agent runs its own
// guardrails inside RunStep.
Guards *guardrail.Pipeline
// PersonaID scopes memory retrieval and extraction. Blank skips both.
PersonaID string
// MaxRounds is the tool-loop upper bound. Non-positive selects
// DefaultMaxRounds (8).
MaxRounds int
// OnStream, when non-nil, switches the loop to the streaming path: the model
// adapter's ChatStream is driven directly and each structured stream event is
// forwarded to this callback (model.StreamEvent), so the frontend can render
// body text, tool calls and (eventually) reasoning segments independently.
OnStream func(model.StreamEvent)
// ToolExecution selects how a batch of tool calls is run: "parallel" (the
// default) or "sequential". Empty means parallel.
ToolExecution string
// ToolParallelism caps how many tool calls run concurrently when ToolExecution
// is "parallel". Non-positive selects DefaultToolParallelism (4).
ToolParallelism int
// Hooks installs optional per-batch/per-call tool-loop hooks. Every field is
// nil-tolerant; these are reserved for "敏感操作代审" (design/11) and the
// future asynchronous-tool path. A zero Hooks value is a no-op.
Hooks ToolLoopHooks
// MaxContextTokens is the context budget that triggers overflow compaction.
// Non-positive (the zero value) disables compaction, preserving the loop's
// previous behavior. Setting it to a positive value (e.g.
// DefaultMaxContextTokens) enables compaction: when the loop's estimated
// context exceeds the budget it summarizes older turns into a leading summary
// message while keeping the recent tail (including trailing tool results)
// intact (design §1.3). Nil-tolerant: leaving it unset changes nothing.
MaxContextTokens int
// Compactor compacts an over-budget conversation. Nil selects the built-in
// structural compactor (no model call); a non-nil value lets a caller inject
// an LLM-backed or custom summarizer. It is only invoked when
// MaxContextTokens is positive.
Compactor Compactor
// TransformContext, when non-nil, is called immediately before every model
// step with the assembled message list (after memory injection and any
// compaction) and the current round number (1-based). It may project, filter
// or re-order the messages the model sees. It is the loop's equivalent of
// OpenClaw's transformContext (design §1.3). Nil = no projection.
TransformContext func(ctx context.Context, messages []model.Message, round int) []model.Message
// Log is the base logger. Nil is replaced with a discard logger tagged with
// source "runtime/loop".
Log *slog.Logger
}