主人 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 改动前即未格式化,按铁律保留原样未动。
179 lines
10 KiB
Go
179 lines
10 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/tool"
|
||
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/trace"
|
||
)
|
||
|
||
// Options 配置一个 Loop。所有字段都是可选的,nil 会一直被容忍到
|
||
// 真正需要该字段的那一步:Run 必须有 agent(否则
|
||
// Run 返回 ErrNilAgent);缺少工具 dispatcher 会使被请求的
|
||
// 调用以错误形式回灌;缺少 retriever/persona 会跳过记忆
|
||
// 注入;缺少 extractor/store 会跳过记忆钩子;缺少
|
||
// checkpoint 会跳过持久化;缺少 tracer 会跳过追踪。
|
||
type Options struct {
|
||
// Agent 是循环的执行单元:一个指令字符串,加上后端
|
||
// adapter、可选工具 dispatcher 与可选护栏。它是
|
||
// 默认 agent:角色在 Agents 中没有条目的每个 turn(或
|
||
// Agents 未设置时的每个 turn)都在它上面运行,
|
||
// 因此其指令应是全局默认角色的指令。
|
||
Agent *agent.Agent
|
||
// Agents 是按角色 agent 池(design/22 §4.2 方案 A):persona id → 承载
|
||
// 该角色系统提示词的 agent。循环每一步都为该
|
||
// turn 解析出的角色挑选条目,因此切换会话角色即切换
|
||
// 提示词,而无需重建循环(Agent 的指令不可变,
|
||
// 因此每角色一个 agent 是维持该不变式的方式)。所有条目通常
|
||
// 共用同一个 dispatcher/护栏管线/adapter —— 只有指令不同。
|
||
// 条目缺失或为 nil、角色未知、池为空,都会回退到
|
||
// Agent。为 nil 时保持既有的单角色行为。
|
||
Agents map[string]*agent.Agent
|
||
// Tools 是用于执行模型所请求调用的工具 dispatcher。为
|
||
// nil 时循环回退到 agent 自身的 dispatcher(若有)。
|
||
Tools *tool.Dispatcher
|
||
// Capacity 非 nil 且启用时,把每轮广告给模型的工具 schema
|
||
// 过滤为当前 capacity 状态的允许集
|
||
// (design/14 唤醒式能力渐进 §4):循环只向
|
||
// 模型暴露允许集 —— 是一个视图,不是硬边界(Invoke 不被拦截,
|
||
// 因此紧急调用仍可用)。为 nil(默认)时保持既有
|
||
// 行为:每个已注册工具都被广告。
|
||
Capacity *capacity.Decider
|
||
// OnCapacity 非 nil 时接收循环在判定一轮期间观察到的
|
||
// capacity_awaken 迁移事件(design/14 §4),使调用方能把
|
||
// 「能力点亮」的进展浮出到前端。为 nil = 空操作。
|
||
OnCapacity func(capacity.Event)
|
||
// OnAudit 非 nil 时,循环每次在受限(群聊/陌生人)环境中
|
||
// 拒绝工具调用都会收到一个 AuditEvent(design/11 §3 §8),
|
||
// 使该拒绝进入审计轨迹,且前端可以渲染
|
||
// 「xx 工具在群聊被拒」的小字。为 nil 时该拒绝仍会
|
||
// 作为软 not_allowed 结果回灌给模型,只是不记录到外部 sink。
|
||
OnAudit func(AuditEvent)
|
||
// OnTurnEnd 非 nil 时,每个已落定的 turn(返回了 Result 的
|
||
// Run)调用一次,携带该 turn 的用户/assistant 文本以及循环
|
||
// 绑定的角色,使调用方能推进紧随人格的状态 ——
|
||
// design/01 §2 的情绪机与好感度曲线。其返回值
|
||
// 记录在 Result.Mood 上并带到持久化的 assistant 消息。
|
||
// 为 nil = 循环不上报任何东西,Result.Mood 保持为空。
|
||
OnTurnEnd TurnEndHook
|
||
// Memory 是用于持久化提取出的条目的长期记忆 store。
|
||
Memory *memory.Store
|
||
// Retriever 把相关记忆注入上下文,每轮重新评估
|
||
//(见 assembleContext),使检索跟上最新对话。
|
||
Retriever *memory.Retriever
|
||
// WorkingMemory 是每会话滑动窗口,供
|
||
// orchestrator 查看;循环不改写它。
|
||
WorkingMemory *memory.WorkingMemory
|
||
// Extractor 在每一步之后导出要持久化的记忆条目。
|
||
Extractor memory.Extractor
|
||
// Checkpoint 在每一步持久化循环状态的持久快照,并在之后
|
||
// 同一 sessionID 的 Run 中据此恢复被中断的 run
|
||
//(design §1 durable execution)。*persist.Store 满足这个窄
|
||
// checkpointStore 接口;为 nil 时同时关闭持久化与恢复。
|
||
Checkpoint checkpointStore
|
||
// Tracer 为每一步、每次 LLM 调用与每次工具调用发出结构化 span。
|
||
Tracer *trace.Tracer
|
||
// Flow 非 nil 时,把 run 的 trace span 累积成每会话
|
||
// flow 图,并在每次 span 迁移时发布 ws.graph_update(design/19
|
||
// §1)。为 nil 时关闭实时 flow 可视化。
|
||
Flow *FlowPublisher
|
||
// Guards 在流式路径上校验请求/响应载荷 —— 该路径
|
||
// 绕过了 RunStep。非流式路径上由 agent 在 RunStep 内部
|
||
// 运行自己的护栏。
|
||
Guards *guardrail.Pipeline
|
||
// PersonaID 是循环配置的角色:没有显式 SessionEnv.PersonaID 的
|
||
// 每个 turn 都作为它运行(design/22 §2 裁决规则 4)。它
|
||
// 界定记忆检索与提取、系统提示词回退以及
|
||
// 情绪钩子的作用域。为空则跳过记忆注入/提取。
|
||
PersonaID string
|
||
// PersonaName 非 nil 时,为上下文的发言人归属把角色 id
|
||
// 解析为其显示名(design/22 §7-5):角色声明与
|
||
// 边界说明把发言角色称作 "「昔涟」(xilian)" 而不是裸 id,
|
||
// 使模型无需自备查表就能跟进「谁说的」。未知角色、
|
||
// 空名或 nil 解析器都回退到 id —— 归属绝不
|
||
// 因为名字无法解析而消失。
|
||
PersonaName func(personaID string) string
|
||
// 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 是用于渲染每轮「当前时间」系统消息的 IANA
|
||
// 时区(如 "Asia/Shanghai")。为空则完全关闭时间注入
|
||
// (向后兼容);加载失败的名字同样关闭它。
|
||
Timezone string
|
||
// MaxRounds 是工具循环上限。非正时选用
|
||
// DefaultMaxRounds(8)。
|
||
MaxRounds int
|
||
// OnStream 非 nil 时把循环切换到流式路径:直接驱动模型
|
||
// adapter 的 ChatStream,每个结构化流事件都转发到
|
||
// 该回调(model.StreamEvent),使前端能独立渲染
|
||
// 正文文本、工具调用以及(最终)推理分段。
|
||
OnStream func(model.StreamEvent)
|
||
// Thinking 携带注入循环构建的每个模型请求的推理旋钮
|
||
//(预算 / 显式开关),在没有按角色取值覆盖它时生效。
|
||
// nil 表示「不干预」,即既有行为。它是
|
||
// 角色没有解析出旋钮的 turn 的默认值(Options.PersonaKnobs)。
|
||
Thinking *model.Thinking
|
||
// ReasoningEffort 是注入每个模型请求的兼容 OpenAI 的
|
||
// 强度旋钮。为空表示「不干预」。
|
||
ReasoningEffort string
|
||
// ThinkingShowToUser 控制循环广播的思考帧:nil(默认)
|
||
// 保持既有行为并推送它们;显式 false
|
||
// 把 thinking_start/delta/end 挡在客户端流之外,而文本与工具
|
||
// 帧不受影响。这些帧仍被产出并仍抵达
|
||
// 模型/transcript 路径 —— 只有 UI 广播被门控,因此展示与
|
||
// 持久化保持解耦(design/04 §3.2)。
|
||
ThinkingShowToUser *bool
|
||
// ToolExecution 选择一批工具调用如何运行:"parallel"(
|
||
// 默认)或 "sequential"。为空表示并行。
|
||
ToolExecution string
|
||
// ToolParallelism 限制 ToolExecution 为 "parallel" 时并发
|
||
// 运行的工调用数。非正时选用 DefaultToolParallelism(4)。
|
||
ToolParallelism int
|
||
// Hooks 安装可选的按批次/按调用的工具循环钩子。每个字段都
|
||
// 容忍 nil;这些保留给「敏感操作代审」(design/11)以及
|
||
// 未来的异步工具路径。零值 Hooks 为空操作。
|
||
Hooks ToolLoopHooks
|
||
// MaxContextTokens 是触发溢出压缩的上下文预算。
|
||
// 非正(零值)关闭压缩,保持循环
|
||
// 此前的行为。设为正值(如
|
||
// DefaultMaxContextTokens)则启用压缩:循环的估算
|
||
// 上下文超出预算时,它把更早的轮次摘要成一条前导摘要
|
||
// 消息,同时保持近期尾部(含尾部工具结果)
|
||
// 完整(design §1.3)。容忍 nil:不设置它不改变任何行为。
|
||
MaxContextTokens int
|
||
// Compactor 压缩超预算的对话。nil 选用内置的
|
||
// 结构化 compactor(不调模型);非 nil 值让调用方注入
|
||
// LLM 支持的或自定义的摘要器。它仅在
|
||
// MaxContextTokens 为正时被调用。
|
||
Compactor Compactor
|
||
// Vision 非 nil 时,在 Run 开始时改写携带图像的用户消息,
|
||
// 使主模型能对图像进行推理(design/06 §0)。具体
|
||
// resolver(vision.MessageResolver)通过 vision Router 决定
|
||
// 是保留图像给支持视觉的主模型,还是折叠为
|
||
// vision/OCR 工具产出的文字描述("先看图再对话")。为 nil 时
|
||
// 图像内容原样保留,直接透传给支持视觉的主模型。
|
||
Vision ImageResolver
|
||
// TransformContext 非 nil 时,在每个模型步之前立即被调用,
|
||
// 传入组装好的消息列表(记忆注入与任何压缩
|
||
// 之后)以及当前轮次号(从 1 开始)。它可以投影、过滤
|
||
// 或重排模型所见的消息。它是循环中与
|
||
// OpenClaw 的 transformContext 等价之物(design §1.3)。为 nil = 不做投影。
|
||
TransformContext func(ctx context.Context, messages []model.Message, round int) []model.Message
|
||
// Log 是基础 logger。为 nil 时替换为标记来源
|
||
// "runtime/loop" 的丢弃 logger。
|
||
Log *slog.Logger
|
||
}
|