docs(runtime): internal/runtime/loop 44 个文件注释改中文(逐行 1:1,代码零字节改动)

主人 2026-10-03 拍板「注释一律中文」;本模块按长期分批协议(一个模块一次派工)执行。
注释行数 1329 → 1329 逐行 1:1(含中文 33 → 1304);行尾注释 5 条一并译。
校验:机械校验(非注释改动行)= 空;独立 Go AST 程序(剥注释复渲)证明 44/44 文件代码全等;
全树 go test ./... 58 包 ok / 0 FAIL;交叉编译绿。
gofmt:capacity_loop_test.go / flow.go 改动前即未格式化,按铁律保留原样未动。
This commit is contained in:
AskaEth committed 2026-10-03 22:28:21 +08:00
1 parent 355f81d04a
commit 91e8d7a029
44 files changed
+1315 -1315

No files matched your search

+129 -129
View File
@@ -13,166 +13,166 @@ import (
"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.
// Options 配置一个 Loop。所有字段都是可选的,nil 会一直被容忍到
// 真正需要该字段的那一步:Run 必须有 agent(否则
// Run 返回 ErrNilAgent);缺少工具 dispatcher 会使被请求的
// 调用以错误形式回灌;缺少 retriever/persona 会跳过记忆
// 注入;缺少 extractor/store 会跳过记忆钩子;缺少
// checkpoint 会跳过持久化;缺少 tracer 会跳过追踪。
type Options struct {
// Agent is the loop's execution unit: an instruction string plus a backend
// adapter, an optional tool dispatcher and optional guardrails. It is the
// DEFAULT agent: every turn whose role has no entry in Agents (or every turn
// when Agents is unset) runs on it, so its instructions should be the global
// default role's.
// Agent 是循环的执行单元:一个指令字符串,加上后端
// adapter、可选工具 dispatcher 与可选护栏。它是
// 默认 agent:角色在 Agents 中没有条目的每个 turn(或
// Agents 未设置时的每个 turn)都在它上面运行,
// 因此其指令应是全局默认角色的指令。
Agent *agent.Agent
// Agents is the per-role agent pool (design/22 §4.2 方案 A): persona id → the
// agent carrying that role's system prompt. The loop picks the entry for the
// turn's resolved role on every step, so switching a session's role switches the
// prompt without rebuilding the loop (an Agent's instructions are immutable, so
// one agent per role is the way to keep that invariant). All entries normally
// share one dispatcher/guardrail pipeline/adapter — only the instructions differ.
// A missing or nil entry, an unknown role, or an empty pool all fall back to
// Agent. Nil keeps the pre-existing single-role behavior.
// Agents 是按角色 agent 池(design/22 §4.2 方案 A):persona id → 承载
// 该角色系统提示词的 agent。循环每一步都为该
// turn 解析出的角色挑选条目,因此切换会话角色即切换
// 提示词,而无需重建循环(Agent 的指令不可变,
// 因此每角色一个 agent 是维持该不变式的方式)。所有条目通常
// 共用同一个 dispatcher/护栏管线/adapter —— 只有指令不同。
// 条目缺失或为 nil、角色未知、池为空,都会回退到
// Agent。为 nil 时保持既有的单角色行为。
Agents map[string]*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 是用于执行模型所请求调用的工具 dispatcher。为
// nil 时循环回退到 agent 自身的 dispatcher(若有)。
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 非 nil 且启用时,把每轮广告给模型的工具 schema
// 过滤为当前 capacity 状态的允许集
// (design/14 唤醒式能力渐进 §4):循环只向
// 模型暴露允许集 —— 是一个视图,不是硬边界(Invoke 不被拦截,
// 因此紧急调用仍可用)。为 nil(默认)时保持既有
// 行为:每个已注册工具都被广告。
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 非 nil 时接收循环在判定一轮期间观察到的
// capacity_awaken 迁移事件(design/14 §4),使调用方能把
// 「能力点亮」的进展浮出到前端。为 nil = 空操作。
OnCapacity func(capacity.Event)
// OnAudit, when non-nil, receives an AuditEvent every time the loop denies a
// tool call in a restricted (group/stranger) environment (design/11 §3 §8),
// so the rejection lands in the audit trail and the frontend can render the
// small "xx 工具在群聊被拒" note. Nil = the rejection is still fed back to the
// model as a soft not_allowed result, just not recorded to an external sink.
// OnAudit 非 nil 时,循环每次在受限(群聊/陌生人)环境中
// 拒绝工具调用都会收到一个 AuditEvent(design/11 §3 §8),
// 使该拒绝进入审计轨迹,且前端可以渲染
// 「xx 工具在群聊被拒」的小字。为 nil 时该拒绝仍会
// 作为软 not_allowed 结果回灌给模型,只是不记录到外部 sink。
OnAudit func(AuditEvent)
// OnTurnEnd, when non-nil, is called once per settled turn (a Run that
// returned a Result) with the turn's user/assistant text and the persona the
// loop is bound to, so a caller can advance state that follows the persona —
// the emotion machine and affection curve of design/01 §2. Its return value
// is recorded on Result.Mood and carried to the persisted assistant message.
// Nil = the loop reports nothing and Result.Mood stays empty.
// OnTurnEnd 非 nil 时,每个已落定的 turn(返回了 Result 的
// Run)调用一次,携带该 turn 的用户/assistant 文本以及循环
// 绑定的角色,使调用方能推进紧随人格的状态 ——
// design/01 §2 的情绪机与好感度曲线。其返回值
// 记录在 Result.Mood 上并带到持久化的 assistant 消息。
// 为 nil = 循环不上报任何东西,Result.Mood 保持为空。
OnTurnEnd TurnEndHook
// Memory is the long-term memory store used to persist extracted items.
// Memory 是用于持久化提取出的条目的长期记忆 store。
Memory *memory.Store
// Retriever injects relevant memories into the context, re-evaluated every
// round (see assembleContext) so retrieval tracks the latest conversation.
// Retriever 把相关记忆注入上下文,每轮重新评估
//(见 assembleContext),使检索跟上最新对话。
Retriever *memory.Retriever
// WorkingMemory is the per-session sliding window, provided for the
// orchestrator to inspect; the loop does not mutate it.
// WorkingMemory 是每会话滑动窗口,供
// orchestrator 查看;循环不改写它。
WorkingMemory *memory.WorkingMemory
// Extractor derives memory items to persist after every step.
// Extractor 在每一步之后导出要持久化的记忆条目。
Extractor memory.Extractor
// Checkpoint persists a durable snapshot of the loop state at every step and,
// on a later Run with the same sessionID, resumes an interrupted run from it
// (design §1 durable execution). *persist.Store satisfies the narrow
// checkpointStore interface; nil disables both persistence and resume.
// Checkpoint 在每一步持久化循环状态的持久快照,并在之后
// 同一 sessionID 的 Run 中据此恢复被中断的 run
//(design §1 durable execution)。*persist.Store 满足这个窄
// checkpointStore 接口;为 nil 时同时关闭持久化与恢复。
Checkpoint checkpointStore
// Tracer emits a structured span for every step, LLM call and tool call.
// Tracer 为每一步、每次 LLM 调用与每次工具调用发出结构化 span。
Tracer *trace.Tracer
// Flow, when non-nil, accumulates the run's trace spans into a per-session
// flow graph and publishes ws.graph_update on each span transition (design/19
// §1). Nil disables the realtime flow visualization.
// Flow 非 nil 时,把 run 的 trace span 累积成每会话
// flow 图,并在每次 span 迁移时发布 ws.graph_update(design/19
// §1)。为 nil 时关闭实时 flow 可视化。
Flow *FlowPublisher
// 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 在流式路径上校验请求/响应载荷 —— 该路径
// 绕过了 RunStep。非流式路径上由 agent 在 RunStep 内部
// 运行自己的护栏。
Guards *guardrail.Pipeline
// PersonaID is the loop's configured role: the global default that every turn
// without an explicit SessionEnv.PersonaID runs as (design/22 §2 裁决规则 4). It
// scopes memory retrieval and extraction, the system prompt fallback and the
// emotion hook. Blank skips memory injection/extraction.
// PersonaID 是循环配置的角色:没有显式 SessionEnv.PersonaID 的
// 每个 turn 都作为它运行(design/22 §2 裁决规则 4)。它
// 界定记忆检索与提取、系统提示词回退以及
// 情绪钩子的作用域。为空则跳过记忆注入/提取。
PersonaID string
// PersonaName, when non-nil, resolves a role id to its display name for the
// context's speaker attribution (design/22 §7-5): the role declaration and the
// boundary notes name a speaking role as "「昔涟」(xilian)" instead of a bare id,
// so the model can follow "who said this" without a lookup table of its own. An
// unknown role, an empty name or a nil resolver all fall back to the id — the
// attribution never disappears because a name could not be resolved.
// PersonaName 非 nil 时,为上下文的发言人归属把角色 id
// 解析为其显示名(design/22 §7-5):角色声明与
// 边界说明把发言角色称作 "「昔涟」(xilian)" 而不是裸 id,
// 使模型无需自备查表就能跟进「谁说的」。未知角色、
// 空名或 nil 解析器都回退到 id —— 归属绝不
// 因为名字无法解析而消失。
PersonaName func(personaID string) string
// PersonaKnobs, when non-nil, resolves the reasoning knobs for a role at the
// start of every request build (design/22 §4.2), so a session bound to role Y
// uses Y's thinking budget / effort / broadcast switch rather than the default
// role's. The returned ok=false means "role unknown" and keeps the loop's
// configured defaults (Options.Thinking / ReasoningEffort / ThinkingShowToUser).
// Resolving through the role id — rather than a caller-supplied per-turn value —
// is what keeps every path (gateway, channelbridge, backthink, sub-agents) on the
// right role's knobs from a single input. Nil keeps the configured defaults for
// every turn, i.e. the pre-multi-character behavior.
// PersonaKnobs 非 nil 时,在每次请求构建开始时为角色
// 解析推理旋钮(design/22 §4.2),使绑定到角色 Y 的会话
// 使用 Y 的思考预算 / 强度 / 广播开关,而不是默认
// 角色的。返回的 ok=false 表示「角色未知」并保持循环
// 配置的默认值(Options.Thinking / ReasoningEffort / ThinkingShowToUser)。
// 经角色 id 解析 —— 而非由调用方按 turn 传值 ——
// 正是让所有路径(gateway、channelbridge、backthink、子代理)都由单一
// 输入落到正确角色旋钮的原因。为 nil 时每个 turn 都保持配置的
// 默认值,即多角色之前的行为。
PersonaKnobs func(personaID string) (LoopKnobs, bool)
// Timezone is the IANA zone used to render the per-round "current time"
// system message (e.g. "Asia/Shanghai"). Empty disables time injection
// entirely (backward compatible); a name that fails to load also disables it.
// Timezone 是用于渲染每轮「当前时间」系统消息的 IANA
// 时区(如 "Asia/Shanghai")。为空则完全关闭时间注入
// (向后兼容);加载失败的名字同样关闭它。
Timezone string
// MaxRounds is the tool-loop upper bound. Non-positive selects
// DefaultMaxRounds (8).
// MaxRounds 是工具循环上限。非正时选用
// 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 非 nil 时把循环切换到流式路径:直接驱动模型
// adapter 的 ChatStream,每个结构化流事件都转发到
// 该回调(model.StreamEvent),使前端能独立渲染
// 正文文本、工具调用以及(最终)推理分段。
OnStream func(model.StreamEvent)
// Thinking carries the reasoning knobs (budget / explicit switch) injected
// into every model request the loop builds when no per-role value overrides it.
// nil means "do not intervene", which is the pre-existing behavior. It is the
// DEFAULT for turns whose role has no resolved knobs (Options.PersonaKnobs).
// Thinking 携带注入循环构建的每个模型请求的推理旋钮
//(预算 / 显式开关),在没有按角色取值覆盖它时生效。
// nil 表示「不干预」,即既有行为。它是
// 角色没有解析出旋钮的 turn 的默认值(Options.PersonaKnobs)。
Thinking *model.Thinking
// ReasoningEffort is the OpenAI-compatible strength knob injected into every
// model request. Empty means "do not intervene".
// ReasoningEffort 是注入每个模型请求的兼容 OpenAI 的
// 强度旋钮。为空表示「不干预」。
ReasoningEffort string
// ThinkingShowToUser gates the thinking frames the loop broadcasts: nil (the
// default) keeps the pre-existing behavior and pushes them; an explicit false
// keeps thinking_start/delta/end out of the client stream while text and tool
// frames are unaffected. The frames are still produced and still reach the
// model/transcript paths — only the UI broadcast is gated, so display and
// persistence stay decoupled (design/04 §3.2).
// ThinkingShowToUser 控制循环广播的思考帧:nil(默认)
// 保持既有行为并推送它们;显式 false
// 把 thinking_start/delta/end 挡在客户端流之外,而文本与工具
// 帧不受影响。这些帧仍被产出并仍抵达
// 模型/transcript 路径 —— 只有 UI 广播被门控,因此展示与
// 持久化保持解耦(design/04 §3.2)。
ThinkingShowToUser *bool
// ToolExecution selects how a batch of tool calls is run: "parallel" (the
// default) or "sequential". Empty means parallel.
// ToolExecution 选择一批工具调用如何运行:"parallel"(
// 默认)或 "sequential"。为空表示并行。
ToolExecution string
// ToolParallelism caps how many tool calls run concurrently when ToolExecution
// is "parallel". Non-positive selects DefaultToolParallelism (4).
// ToolParallelism 限制 ToolExecution 为 "parallel" 时并发
// 运行的工调用数。非正时选用 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 安装可选的按批次/按调用的工具循环钩子。每个字段都
// 容忍 nil;这些保留给「敏感操作代审」(design/11)以及
// 未来的异步工具路径。零值 Hooks 为空操作。
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 是触发溢出压缩的上下文预算。
// 非正(零值)关闭压缩,保持循环
// 此前的行为。设为正值(如
// DefaultMaxContextTokens)则启用压缩:循环的估算
// 上下文超出预算时,它把更早的轮次摘要成一条前导摘要
// 消息,同时保持近期尾部(含尾部工具结果)
// 完整(design §1.3)。容忍 nil:不设置它不改变任何行为。
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 压缩超预算的对话。nil 选用内置的
// 结构化 compactor(不调模型);非 nil 值让调用方注入
// LLM 支持的或自定义的摘要器。它仅在
// MaxContextTokens 为正时被调用。
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 非 nil 时,在 Run 开始时改写携带图像的用户消息,
// 使主模型能对图像进行推理(design/06 §0)。具体
// resolver(vision.MessageResolver)通过 vision Router 决定
// 是保留图像给支持视觉的主模型,还是折叠为
// vision/OCR 工具产出的文字描述("先看图再对话")。为 nil 时
// 图像内容原样保留,直接透传给支持视觉的主模型。
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 非 nil 时,在每个模型步之前立即被调用,
// 传入组装好的消息列表(记忆注入与任何压缩
// 之后)以及当前轮次号(从 1 开始)。它可以投影、过滤
// 或重排模型所见的消息。它是循环中与
// OpenClaw 的 transformContext 等价之物(design §1.3)。为 nil = 不做投影。
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 是基础 logger。为 nil 时替换为标记来源
// "runtime/loop" 的丢弃 logger。
Log *slog.Logger
}