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

+62 -62
View File
@@ -9,70 +9,70 @@ import (
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/model"
)
// Compaction tuning constants. These bound the compactor so it stays
// deterministic and offline: the summary is capped, the recent tail is a share
// of the budget, and very short conversations are left alone even when they
// exceed the budget (design §1.3 上下文溢出压缩).
// 压缩调优常量。它们界定 compactor,使其保持确定性与
// 离线:摘要被限额、近期尾部占预算的一个比例,
// 且极短的对话即使超出预算也原样不动
// (design §1.3 上下文溢出压缩)。
const (
// DefaultMaxContextTokens is the context budget a caller can assign to
// Options.MaxContextTokens to enable overflow compaction (design §1.3).
// DefaultMaxContextTokens 是调用方可赋给
// Options.MaxContextTokens 以启用溢出压缩的上下文预算(design §1.3)。
DefaultMaxContextTokens = 32768
// keepRecentFraction is the share of the budget reserved for the recent tail
// during compaction; the remainder is left for the summary message and prompt.
// keepRecentFraction 是压缩时为近期尾部保留的
// 预算比例;余下部分留给摘要消息与提示词。
keepRecentFraction = 0.5
// minKeepTokens is the floor for the recent-tail budget so a tiny
// MaxContextTokens does not force every turn into the summary.
// minKeepTokens 是近期尾部预算的下限,使极小的
// MaxContextTokens 不会把每个 turn 都逼进摘要。
minKeepTokens = 128
// summaryMarker tags a compaction summary system message so the compactor can
// recognize an existing summary prefix and preserve (merge into) it rather
// than re-summarize it on a later compaction.
// summaryMarker 标记压缩摘要系统消息,使 compactor 能
// 识别已存在的摘要前缀并保留(并入)它,
// 而不是在后续压缩中重新摘要。
summaryMarker = "[前文摘要 · history summary]"
// maxSummaryChars caps the compaction summary so repeated compactions cannot
// inflate the summary without bound (design §1.3 摘要容量上限).
// maxSummaryChars 限制压缩摘要,使反复压缩无法
// 无界地膨胀摘要(design §1.3 摘要容量上限)。
maxSummaryChars = 4000
// minCompactMessages is the smallest conversation the compactor will touch; a
// shorter one is left alone even when it is over budget.
// minCompactMessages 是 compactor 会触碰的最小对话;
// 更短的即使超出预算也原样不动。
minCompactMessages = 3
// messageOverheadTokens is the per-message token overhead charged for the role
// and separator tokens the provider adds around each message.
// messageOverheadTokens 是每条消息的 token 开销,对应 provider
// 在每条消息周围添加的角色与分隔符 token。
messageOverheadTokens = 4
// imageTokens is the rough token charge for a single image part.
// imageTokens 是单个图像部件的粗略 token 计数。
imageTokens = 1000
// charsPerToken is the character-to-token divisor used by the offline estimate.
// charsPerToken 是离线估算使用的字符到 token 除数。
charsPerToken = 4
// summarySampleChars bounds the per-message text sampled into a naive summary.
// summarySampleChars 限制被抽样进朴素摘要的每条消息文本长度。
summarySampleChars = 120
)
// Compactor compacts an over-budget conversation by summarizing older turns into
// a single leading summary message while preserving the recent tail — including
// the trailing tool results — unmodified (design §1.3, aligned with OpenClaw's
// context-engine). It is injected through Options.Compactor so a caller can swap
// in an LLM-backed summarizer; the loop ships a structural default that never
// calls a model.
// Compactor 通过把更早的轮次摘要成单条前导摘要消息来压缩
// 超预算的对话,同时原样保留近期尾部 —— 包括
// 尾部的工具结果(design §1.3,与 OpenClaw 的
// context-engine 对齐)。它经 Options.Compactor 注入,使调用方能
// 换成 LLM 支持的摘要器;循环自带一个绝不调用
// 模型的结构化默认实现。
type Compactor interface {
// Compact returns messages with older turns compressed into a leading summary
// when the estimated tokens exceed budget. It returns the input unchanged when
// the messages already fit within budget or there is nothing safe to
// summarize. ctx is the abort signal: a canceled context stops compaction and
// returns a wrapped context.Canceled so the caller can bail out.
// Compact 在估算 token 超过 budget 时返回把更早轮次压缩为
// 一条前导摘要的 messages。消息本来就在预算内,或
// 没有可安全摘要的内容时,它原样返回输入。
// ctx 是中止信号:被取消的 context 停止压缩并
// 返回包装过的 context.Canceled,使调用方能退出。
Compact(ctx context.Context, messages []model.Message, budget int) ([]model.Message, error)
}
// defaultCompactor is the built-in structural compactor. It is stateless and
// safe for concurrent use.
// defaultCompactor 是内置的结构化 compactor。它无状态,
// 且并发安全。
type defaultCompactor struct{}
// Compact implements Compactor.
// Compact 实现 Compactor。
func (defaultCompactor) Compact(ctx context.Context, messages []model.Message, budget int) ([]model.Message, error) {
if ctx == nil {
ctx = context.Background()
@@ -83,8 +83,8 @@ func (defaultCompactor) Compact(ctx context.Context, messages []model.Message, b
return compactMessages(ctx, messages, budget)
}
// compactMessages is the shared compaction implementation. It never mutates the
// input slices; a compacted result is always freshly allocated.
// compactMessages 是共享的压缩实现。它绝不改写
// 输入切片;压缩结果始终是新分配的。
func compactMessages(ctx context.Context, messages []model.Message, budget int) ([]model.Message, error) {
if budget <= 0 || len(messages) < minCompactMessages {
return messages, nil
@@ -97,7 +97,7 @@ func compactMessages(ctx context.Context, messages []model.Message, budget int)
keepTokens := max(int(float64(budget)*keepRecentFraction), minKeepTokens)
keepStart := findKeepStart(messages, prefixEnd, keepTokens)
if keepStart <= prefixEnd {
// Everything is being kept; there is no safe prefix to summarize.
// 全部内容都被保留;没有可安全摘要的前缀。
return messages, nil
}
@@ -125,9 +125,9 @@ func compactMessages(ctx context.Context, messages []model.Message, budget int)
return out, nil
}
// summaryPrefixEnd returns the count of leading compaction-summary messages,
// which are never re-summarized. It is 0 (no summary) or 1 (a single summary
// prefix), since compaction always emits exactly one leading summary.
// summaryPrefixEnd 返回前导压缩摘要消息的数量,
// 它们绝不再次被摘要。取值为 0(无摘要)或 1(单条摘要
// 前缀),因为压缩始终只产出一条前导摘要。
func summaryPrefixEnd(messages []model.Message) int {
if len(messages) == 0 || messages[0].Role != model.RoleSystem {
return 0
@@ -138,12 +138,12 @@ func summaryPrefixEnd(messages []model.Message) int {
return 0
}
// findKeepStart returns the index of the first message to keep given that the
// recent tail should fit within keepTokens. It always keeps whole turns: a turn
// starts at a user message and runs to the next user message. It scores turns
// from the back, so the most recent turns survive and a trailing tool result is
// never sliced apart. When no user turn exists (unusual), it keeps nothing extra
// and returns from, so nothing is summarized.
// findKeepStart 在近期尾部应装入 keepTokens 的前提下,
// 返回第一条要保留的消息索引。它始终保留完整的 turn:一个 turn
// 从用户消息开始,直到下一条用户消息。它从后往前
// 给 turn 计分,因此最近的 turn 得以存活,尾部的工具结果
// 绝不被切开。不存在用户 turn 时(少见),它不额外保留任何东西
// 并从 from 返回,因此什么也不被摘要。
func findKeepStart(messages []model.Message, from, keepTokens int) int {
n := len(messages)
turns := make([]int, 0, 8)
@@ -173,10 +173,10 @@ func findKeepStart(messages []model.Message, from, keepTokens int) int {
return keptStart
}
// estimateContextTokens returns a conservative token estimate for messages. It is
// a heuristic (chars ÷ charsPerToken with a per-message and per-image overhead)
// so it stays dependency-free and offline, but it is monotonic enough to drive
// the compact decision.
// estimateContextTokens 返回 messages 的保守 token 估算。它是
// 启发式(字符 ÷ charsPerToken,加上每条消息与每个图像的开销),
// 因此无依赖且离线,但单调性足够驱动
// 压缩决策。
func estimateContextTokens(messages []model.Message) int {
total := 0
for _, m := range messages {
@@ -193,9 +193,9 @@ func estimateContextTokens(messages []model.Message) int {
return total
}
// estimateStringTokens is the character-level token estimate for one text string,
// weighting wide (CJK) runes double and dividing by charsPerToken. An empty string
// costs nothing; a non-empty one costs at least one token.
// estimateStringTokens 是单个文本字符串的字符级 token 估算,
// 把宽字符(CJK)rune 计为双倍权重再除以 charsPerToken。空字符串
// 不花费任何 token;非空至少花费一个 token。
func estimateStringTokens(s string) int {
if s == "" {
return 0
@@ -214,10 +214,10 @@ func estimateStringTokens(s string) int {
return t
}
// buildSummary renders a compact structural summary of the given messages: a role
// distribution plus the most recent user and assistant text. It is the default
// (offline, no-model) summarizer; a caller that wants a semantic digest injects
// its own Compactor instead.
// buildSummary 渲染给定 messages 的紧凑结构化摘要:角色
// 分布,加上最近的用户与 assistant 文本。它是默认的
// (离线、不调模型)摘要器;想要语义化摘要的调用方
// 改为注入自己的 Compactor。
func buildSummary(messages []model.Message) string {
if len(messages) == 0 {
return ""
@@ -260,8 +260,8 @@ func buildSummary(messages []model.Message) string {
return b.String()
}
// capSummary truncates s to at most max runes, appending a "..." marker when it
// had to cut. It is rune-safe so it never slices a multi-byte rune in half.
// capSummary 把 s 截断到至多 max 个 rune,需要裁剪时追加 "..." 标记。
// 它是 rune 安全的,因此绝不把多字节 rune 切成两半。
func capSummary(s string, max int) string {
runes := []rune(s)
if len(runes) <= max {
@@ -273,7 +273,7 @@ func capSummary(s string, max int) string {
return string(runes[:max-3]) + "..."
}
// truncateRunes shortens s to at most n runes, Unicode-safe.
// truncateRunes 把 s 缩短到至多 n 个 rune,Unicode 安全。
func truncateRunes(s string, n int) string {
runes := []rune(s)
if len(runes) <= n {