Files
Cyrene_v2/internal/runtime/loop/options.go
T
Aska 6dbb4a2e18
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
feat: 唤醒式架构批次——capacity/L4/会话编排/管理面板写CRUD/前端集成(77-81)
【capacity 唤醒式核心(77)】internal/runtime/capacity:(Decider L1启发式+L2上下文+人格覆盖→三态静息/唤醒/全能→允许工具集+capacity_awaken事件)。loop 外围允许集过滤(不硬阻断=能力≠边界红线)。config 加 Capacity(Enabled/MinConfidence/ToolSets)。wire buildCapacity 接入 loop。

【L4 记忆驱动意图(80)】recall.go:从记忆召回用户习惯(搞一下xx=办事)→唤醒L1/L2漏掉/升置信;群聊陌生人不读owner习惯(隔离红线);RecordIntent闭环(判定写回记忆越用越准);默认保守静息。

【会话消息编排(78)】gateway/session_orchestrator.go:每会话(模式/pending有界5/tail上下文尾巴20);Submit非阻塞(idle立即/busy入队ack/满拒绝);完成回调按序续接(tail+新消息合并增量注入非全量重放);Interrupt中断;REST 202/409+WS session_busy/pending_ack事件;不重造任务引擎复用loop。

【前端集成(79)】ws.ts 加 capacity_awaken/session_busy/pending_ack 解析;store 加 awakenState/sessionBusy;TopBar/ChatShell 加唤醒态徽标+忙态提示(不阻塞输入框);状态组件 status.tsx;Miuix风格。

【管理面板写CRUD(81)】20个写端点(POST/PUT/DELETE /admin/*,owner+审计落库):personas(真持久化YAML)/users apikey(签发掩码+撤销)/iot控制(真下发)/automations(建/触发/删)/llm(test真调用+临时)/channels(connect/close/身份测试actor白名单)/plugins(卸载)/memories(search真检索)/sessions(export)。无存储如实501不伪造。拆4文件满足≤500红线。前端9模块接真写。

【wire收口】buildCapacity接入loop+OnCapacity事件;server.go newOrchestrator接hub。

实机验证:iot ac.living control 200 真实改变 on:true;observability/metrics 200、health ok;全仓 build/vet/test 47包绿+前端tsc/vite。
2026-09-06 21:54:40 +08:00

109 lines
5.9 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/capacity"
"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
// Capacity, when non-nil and enabled, filters the tool schemas advertised to
// the model each round to the allowed set for the current capacity state
// (design/14 唤醒式能力渐进 §4): the loop only exposes the allowed set to the
// model — a view, not a hard boundary (Invoke is left unblocked so an
// emergency call still works). Nil (the default) keeps the pre-existing
// behavior: every registered tool is advertised.
Capacity *capacity.Decider
// OnCapacity, when non-nil, receives capacity_awaken transition events the
// loop observes while deciding a round (design/14 §4), so a caller can
// surface the "能力点亮" progression to the frontend. Nil = no-op.
OnCapacity func(capacity.Event)
// 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
// Vision, when non-nil, rewrites an image-bearing user message at the start
// of Run so the main model can reason over the image (design/06 §0). The
// concrete resolver (vision.MessageResolver) decides via the vision Router
// whether to keep the image for a vision-capable main model or fold it into a
// text description from the vision/OCR tool ("先看图再对话"). Nil keeps the
// image content as-is, passing it through to a vision-capable main model.
Vision ImageResolver
// 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
}