docs(comments): 注释中文化 · internal/services/channelbridge(纯替换)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

- 只翻注释/文档字符串:剥掉注释后代码行逐字节不变(机械校验只剩允许的行尾注释形态)
- 术语对齐既有译法(owner→主人、persona→角色、engage→参与度判定、quiet hours→安静时段、backoff→退避)
- 复核:go build 绿 + channelbridge 包测试绿;gofmt 干净
This commit is contained in:
AskaEth committed 2026-10-04 13:33:06 +08:00
1 parent 61cf80d5ee
commit 093f38d986
28 files changed
+1089 -1089

No files matched your search

+35 -35
View File
@@ -12,12 +12,12 @@ import (
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/model"
)
// gateFor returns the per-account flow gate for an adapter, building it lazily
// from the adapter's Config snapshot on first use. Gates are keyed by the adapter
// name so every account (platform:confId) keeps its own gate that survives adapter
// reconnects; **the throttling counters inside a gate are per conversation**
// (adapter.ConversationKey: platform+channelID+groupID/peerID), so two groups never
// squeeze each other (design/12 §6 策略三/四, 裁决 06.4.3).
// gateFor 返回适配器的按账号流控闸门,首次使用时依据适配器的
// Config 快照惰性构建。闸门以适配器名称为键,使每个账号
// (platform:confId)保有自己、可跨适配器重连存活的闸门;
// **闸门内部的节流计数器按会话隔离**
// (adapter.ConversationKey:platform+channelID+groupID/peerID),因此两个群绝不会
// 相互挤占(design/12 §6 策略三/四,裁决 06.4.3)。
func (b *Bridge) gateFor(a adapter.PlatformAdapter) *adapter.FlowGate {
name := a.Name()
b.gateMu.Lock()
@@ -30,11 +30,11 @@ func (b *Bridge) gateFor(a adapter.PlatformAdapter) *adapter.FlowGate {
return g
}
// gateOptions resolves the per-account flow-limits out of the adapter's Config
// snapshot into a FlowGate. An absent key (single-account env adapters) leaves the
// limit off, so existing channels run unchanged; a present key with value 0 follows
// the protective default (6 group/min, 50 cap) and a negative value disables it —
// the backward-compatible contract for CHANNELS_JSON accounts (design/12 §6 实现要点).
// gateOptions 把适配器 Config 快照中的按账号流控限制解析进
// FlowGate。键缺失(单账号环境变量适配器)时限制关闭,
// 既有渠道行为不变;键存在且值为 0 时采用保护性默认值
// (每群每分钟 6 条、上限 50),负值则关闭限制——
// 这是对 CHANNELS_JSON 账号的向后兼容约定(design/12 §6 实现要点)。
func (b *Bridge) gateOptions(a adapter.PlatformAdapter) adapter.FlowGateOptions {
opts := adapter.FlowGateOptions{
Review: b.review,
@@ -54,10 +54,10 @@ func (b *Bridge) gateOptions(a adapter.PlatformAdapter) adapter.FlowGateOptions
return opts
}
// flowOrDefault maps a configured limit value to an effective limit: a negative
// value disables the limit (0), zero follows the protective default, and a positive
// value pins it. This is the "old account without the field keeps prior behavior or
// gets the safe default" contract.
// flowOrDefault 把配置的限制值映射为实际生效的限制:负值
// 关闭限制(0),零采用保护性默认值,正值则
// 固定为该值。这就是「没有该字段的旧账号保持原有行为或
// 取安全默认值」的约定。
func flowOrDefault(v, def int) int {
switch {
case v < 0:
@@ -69,8 +69,8 @@ func flowOrDefault(v, def int) int {
}
}
// cfgInt reads an integer field from an adapter config map, returning (0,false)
// when the key is absent or of an unexpected type (so an absent field stays "off").
// cfgInt 从适配器配置 map 读取整数字段,键缺失或类型不符时
// 返回 (0,false)(因此缺失的字段保持「关闭」)。
func cfgInt(m map[string]any, key string) (int, bool) {
if m == nil {
return 0, false
@@ -88,24 +88,24 @@ func cfgInt(m map[string]any, key string) (int, bool) {
}
const (
// reviewMaxTokens bounds the review model output so a noisy queue listing never
// produces a giant completion (design/12 §6 策略四 cost guard).
// reviewMaxTokens 限定审查模型的输出,使冗长的队列清单绝不会
// 产出超长补全(design/12 §6 策略四 成本护栏)。
reviewMaxTokens = 256
// reviewSnippetLen is how many bytes of each message's content are shown to the
// reviewer, enough to judge importance without leaking the whole queue.
// reviewSnippetLen 是展示给审查者的每条消息内容字节数,
// 足以判断重要性而不泄露整个队列。
reviewSnippetLen = 80
// reviewPrompt instructs the review model to pick which queued messages matter.
// It only ever decides noise/importance — never who the sender is (project red
// line: identity comes from actor metadata, not the LLM).
// reviewPrompt 指示审查模型挑选哪些队列消息重要。
// 它只判断噪声/重要性——绝不判断发送者是谁(项目
// 红线:身份来自 actor 元数据,而非 LLM)。
reviewPrompt = "你是群聊消息去噪助手。下面列出若干进入队列的消息(每条带编号)。" +
"请判断哪些消息没有保留价值(灌水、无关、低信息量),只返回你认为【有必要保留】的编号 JSON 数组。" +
"只判断信息价值,不判断发送者身份;不要编造编号;只输出 JSON 数组。"
)
// NewLLMReviewer wraps a model.Adapter into an adapter.Reviewer (design/12 §6 策略四).
// It asks the LLM, via a light single-turn prompt, which of the queued messages are
// worth keeping and routes the answer back to the gate. The gate only invokes it
// when a pending set nears its cap, so the LLM is never called per-message.
// NewLLMReviewer 把 model.Adapter 包装成 adapter.Reviewer(design/12 §6 策略四)。
// 它用一次轻量单轮提示问 LLM 哪些队列消息值得保留,
// 并把答案回送给闸门。闸门只在待处理集合接近上限时
// 调用它,因此绝不会按消息调用 LLM。
func NewLLMReviewer(llm model.Adapter, log *slog.Logger) adapter.Reviewer {
if log == nil {
log = slog.Default()
@@ -132,9 +132,9 @@ func NewLLMReviewer(llm model.Adapter, log *slog.Logger) adapter.Reviewer {
}
}
// buildReviewContent renders the queued messages as a numbered, truncated listing so
// the reviewer can judge importance cheaply. No identity or LLM judgement is made
// here — only a compact, index-labelled text summary.
// buildReviewContent 把队列消息渲染为带编号的截断清单,
// 便于审查者低成本判断重要性。这里不做身份判定或 LLM 判断
// ——只生成紧凑、带索引标签的文本摘要。
func buildReviewContent(msgs []adapter.UnifiedMessage) string {
var b strings.Builder
for i, m := range msgs {
@@ -147,7 +147,7 @@ func buildReviewContent(msgs []adapter.UnifiedMessage) string {
return b.String()
}
// truncateText shortens s to at most n bytes (by rune count) for display.
// truncateText 为展示把 s 截短到至多 n 字节(按 rune 计数)。
func truncateText(s string, n int) string {
if n <= 0 {
return ""
@@ -159,9 +159,9 @@ func truncateText(s string, n int) string {
return string(runes[:n]) + "…"
}
// parseKeepIndices decodes a JSON array of indices from the review model and keeps
// only in-range, de-duplicated values in ascending order. Out-of-range or duplicate
// indices are dropped so a hallucinated number can never evict a real message.
// parseKeepIndices 解码审查模型返回的 JSON 索引数组,并按升序
// 只保留范围内、去重后的值。越界或重复的
// 索引被丢弃,因此模型幻觉出的数字绝不会淘汰真实消息。
func parseKeepIndices(s string, bound int) ([]int, error) {
var idx []int
if err := json.Unmarshal([]byte(s), &idx); err != nil {