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

- 只翻注释/文档字符串:`go/scanner` 剥注释后按 token 序列+literal 逐 token 比对,**54/54 文件 SAME**;
  改动文件总行数与 HEAD 漂移 = 0(严格 1:1,无合并/拆行)
- 裁决编号与 design 节号逐字保留(03.4.1/03.4.2/03.4.3/14.4.1/14.4.5/07.4.2-4、design/04 §3.1/§3.2 等);
  `chat_template_kwargs` / `enable_thinking` / `reasoning_effort` / `thinking:{type}` 字段名与形状说明未动
- 存疑译法(已登记):`persona` 在叙事语境用「角色」、标识符语境保留 `persona`(仓库既有模块本身混用);
  `rung`→「级」;`provider` 存在「提供商/提供者」两种落笔(可按需一键统一)
- 复核:build/vet 绿 + 两包测试绿;gofmt 干净;残留英文注释 0 条
- 既有存量(未修,只报告):`model/anthropic_test.go` 612 行 >500;`applyAnthropicEvent` 115 行 >80
This commit is contained in:
AskaEth committed 2026-10-04 16:27:54 +08:00
1 parent f5cf117482
commit 3dc1232bc2
54 files changed
+1923 -1923

No files matched your search

+57 -57
View File
@@ -6,63 +6,63 @@ import (
"unicode"
)
// The autonomous think is guided (guidancePrompt in context.go) to conclude with
// a compact, machine-parseable decision so the service no longer has to infer
// whether to push from the trigger's importance alone (design/20 §2 P2):
// 自主思考被引导(context.go 的 guidancePrompt)以如下紧凑、
// 机器可解析的决策收尾,好让服务不必再从触发的重要度
// 单独推断是否推送(design/20 §2 P2):
//
// [我决定:push] (or no_push)
// [我决定:push] (或 no_push)
// [渠道:web|im|kook|telegram|...|auto]
// <the warm message body>
// <温暖的正文内容>
//
// The markers are parsed as plain text so a model that formats them slightly
// differently still yields a usable decision; a malformed/ambiguous decision
// marker fails safe to "no push".
// 这些标记按纯文本解析,因此模型稍有不同地
// 格式化它们时仍能得出可用的决策;格式错乱/含糊的决策
// 标记失败安全为「不推送」。
var (
// decideMarker matches the push/no_push decision token, tolerating a colon in
// either width and optional spaces. It is used to recover the decision, so it
// stays strict on the bracketed form: a bare "我决定:push" is treated as prose,
// not as a decision (fail safe).
// decideMarker 匹配 push/no_push 决策词,容忍两种宽度的
// 冒号与可选空格。它用于恢复决策,所以对
// 带括号的形式保持严格:裸的 "我决定:push" 被当作散文,
// 而不是决策(失败安全)。
decideMarker = regexp.MustCompile(`(?i)\[\s*我决定\s*[::]\s*(no_push|push)\s*\]`)
// channelMarker matches the target-channel token on the decision line.
// channelMarker 匹配决策行上的目标渠道词。
channelMarker = regexp.MustCompile(`(?i)\[\s*渠道\s*[::]\s*([a-z0-9_\-]+)\s*\]`)
// decidePrefixes are the marker shapes that signal a decision was attempted
// but could not be parsed, so the pass is treated as ambiguous (fail safe). It
// is deliberately the bracketed form only, so a message that merely contains
// "我决定:" in prose (common in Chinese) is not misread as a malformed marker.
// decidePrefixes 是「尝试了决策但无法解析」的标记形状,
// 这类轮次被视为含糊(失败安全)。它
// 刻意只含带括号形式,因此仅仅在散文中出现
// "我决定:"(中文里很常见)的消息不会被误读为格式错乱的标记。
decidePrefixes = []string{"[我决定"}
// stripDecideRe / stripChannelRe recognize the decision/channel markers as a
// token to delete from the user-facing body, in their intended bracketed form
// and with full-width brackets, so a well-formatted decision never leaks.
// stripDecideRe / stripChannelRe 把决策/渠道标记识别为
// 需要从用户可见正文中删除的词,按它们预期的带括号形式
// 与全角括号形式处理,因此格式良好的决策永不泄漏。
stripDecideRe = regexp.MustCompile(`(?i)[【\[]\s*我决定\s*[::]\s*(no_push|push)\s*[】\]]`)
stripChannelRe = regexp.MustCompile(`(?i)[【\[]\s*渠道\s*[::]\s*[a-z0-9_\-]+\s*[】\]]`)
bareDecideRe = regexp.MustCompile(`(?i)我决定\s*[::]\s*(no_push|push)`)
bareChannelRe = regexp.MustCompile(`(?i)渠道\s*[::]\s*[a-z0-9_\-]+`)
)
// nonePhrases are conclusions that mean "nothing to report" when no marker is
// present. The body is normalized (case-folded, punctuation/spaces stripped)
// before comparison so "无", "无。" and "Nothing to report." all collapse to the
// same token.
// nonePhrases 是没有标记时表示「无可汇报」的结论。
// 正文在比较前先归一化(折叠大小写、去掉标点/空格),
// 因此 "无"、"无。" 与 "Nothing to report." 都收敛到
// 同一个词。
var nonePhrases = []string{
"无", "没什么", "没有", "无物可报", "暂无", "没有值得说的",
"nothing", "none", "nothingtoreport", "nothingworthreporting", "nothingnew",
}
// parseProactive recovers the push decision, the target-channel hint and the
// clean message body from a think conclusion. It is a pure function: it reads no
// state and performs no I/O, so it is safe to call concurrently and cheap to test.
// parseProactive 从思考结论中恢复推送决策、目标渠道提示与
// 干净的正文消息。它是纯函数:不读状态、不做 I/O,
// 因此可安全并发调用、测试成本低。
//
// Semantics (design/20 §2 P2):
// - A well-formed [我决定:push|no_push] marker is authoritative.
// - A malformed/ambiguous decision marker → no push (fail safe).
// - No marker: a body that reads as "无"/"nothing to report" → no push;
// otherwise the body is treated as a proactive message (push=true), matching
// the pre-P2 default.
// - The channel hint is the [渠道:...] token, lowercased; empty/auto keeps the
// default active-client-first, IM-fallback route.
// - A push decision whose body is empty or still reads as "nothing" → no push
// (fail safe), so a lone marker never pushes an empty message.
// 语义(design/20 §2 P2):
// - 格式良好的 [我决定:push|no_push] 标记具有权威性。
// - 格式错乱/含糊的决策标记 → 不推送(失败安全)。
// - 无标记:正文读作 "无"/"nothing to report" → 不推送;
// 否则正文被当作主动消息(push=true),与
// P2 之前的默认一致。
// - 渠道提示是 [渠道:...] 词,转小写;为空/auto 则保持
// 默认的活跃客户端优先、IM 降级路由。
// - 决策为推送但正文为空或仍读作「无」 → 不推送
// (失败安全),因此单独一个标记绝不会推出空消息。
func parseProactive(conclusion string) (message, channelHint string, push bool) {
text := strings.TrimSpace(conclusion)
@@ -83,8 +83,8 @@ func parseProactive(conclusion string) (message, channelHint string, push bool)
return message, channelHint, push
}
// matchDecide returns the matched decision token (lowercased) and whether a
// well-formed decision marker was present.
// matchDecide 返回匹配到的决策词(已转小写)以及
// 是否存在格式良好的决策标记。
func matchDecide(text string) (string, bool) {
m := decideMarker.FindStringSubmatch(text)
if m == nil {
@@ -93,8 +93,8 @@ func matchDecide(text string) (string, bool) {
return strings.ToLower(m[1]), true
}
// hasAmbiguousDecide reports whether a decision marker was attempted but could
// not be parsed, which the caller treats as "do not push" (fail safe).
// hasAmbiguousDecide 报告是否尝试了决策标记但
// 无法解析,调用方把它当作「不推送」(失败安全)。
func hasAmbiguousDecide(text string) bool {
if decideMarker.MatchString(text) {
return false
@@ -107,9 +107,9 @@ func hasAmbiguousDecide(text string) bool {
return false
}
// matchChannel returns the lowercased target-channel token, or "" when the
// conclusion carries no (or a malformed) channel marker. A malformed channel
// marker degrades to the default route rather than failing the whole pass.
// matchChannel 返回已转小写的目标渠道词;结论未携带
// (或携带格式错乱的)渠道标记时返回 ""。格式错乱的渠道
// 标记降级为默认路由,而不是让整轮失败。
func matchChannel(text string) string {
m := channelMarker.FindStringSubmatch(text)
if m == nil {
@@ -118,15 +118,15 @@ func matchChannel(text string) string {
return strings.ToLower(m[1])
}
// stripInternalMarkers removes the structured decision/channel markers so a
// proactive message body never carries them to the user. It handles the intended
// bracketed form ([我决定:push][渠道:kook]) plus the drift forms a model may emit —
// bare "我决定:push" / "渠道:kook" (no brackets) and full-width 【我决定:push】 — then
// drops any line that becomes empty and collapses the remainder. It is applied
// both inside parseProactive (so the message and record are clean) and again as a
// final guard in buildProactiveMessage, so a marker cannot survive into the
// outbound body no matter how it was formatted. Only our logs/records may carry
// the marker; the user-facing message must be pure warm content.
// stripInternalMarkers 移除结构化的决策/渠道标记,因此
// 主动消息正文绝不会把它们带给用户。它处理预期的
// 带括号形式([我决定:push][渠道:kook])以及模型可能发出的漂移形式——
// 裸的 "我决定:push" / "渠道:kook"(无括号)与全角 【我决定:push】——然后
// 丢弃因此变空的行并压缩剩余部分。它既在
// parseProactive 内部应用(使消息与记录干净),又作为
// buildProactiveMessage 里的最后一道守护,因此无论标记如何
// 被格式化,都无法存活到出站正文里。只有我们的日志/记录可以携带
// 该标记;面向用户的消息必须是纯温暖内容。
func stripInternalMarkers(text string) string {
lines := strings.Split(text, "\n")
out := make([]string, 0, len(lines))
@@ -144,8 +144,8 @@ func stripInternalMarkers(text string) string {
return strings.Join(out, "\n")
}
// looksNone reports whether the body says "nothing worth saying". It is used both
// to drive the no-marker default and to fail-safe a contradictory push marker.
// looksNone 报告正文是否表示「没什么值得说的」。它既用于
// 驱动无标记时的默认,也用于对矛盾的 push 标记失败安全。
func looksNone(text string) bool {
t := normalizeNone(text)
if t == "" {
@@ -159,8 +159,8 @@ func looksNone(text string) bool {
return false
}
// normalizeNone lowercases the body and strips spaces and punctuation so the
// nonePhrases compare cleanly across "无", "无。", "Nothing to report." and similar.
// normalizeNone 把正文转小写并去掉空格与标点,好让
// nonePhrases 在 "无"、"无。"、"Nothing to report." 之类之间干净比较。
func normalizeNone(text string) string {
var b strings.Builder
for _, r := range strings.ToLower(strings.TrimSpace(text)) {