从一个一直没有想通的问题开始
最近一直在继续验证 A Tour of Go 多语言翻译项目中的结构保护策略。
目前正式翻译流程并不是直接把 .article 原文全部交给 GLM-5.2,而是会先保护一部分不应该被翻译或修改的结构,例如:
.play、.image等 present directive;- 链接 target;
- 行内代码的结构边界;
- emphasis 标记;
- 静态预格式化代码;
- 一些必须固定保留的技术内容。
这些内容会被转换成 protected token,翻译完成后再恢复,并进入统一的 present 解析、结构比较和页面校验。
这种方案的结构可靠性已经比较成熟,但我一直有一个疑问:
如果模型看到的不是最完整的原始页面,那么这些 protected token 会不会破坏上下文,从而降低翻译质量?
这个疑问并不是凭空产生的。
在之前的 WordPress 中文到英文 AI 翻译实践中,我已经多次确认:同一个模型下,整篇文章一次性翻译通常明显优于把文章拆成一个个孤立段落翻译。
完整上下文可以帮助模型理解前后关系、术语、指代、文章主题和整体语气。
因此最初我的直觉也是:
既然更多完整上下文通常有利于翻译,那么 A Tour of Go 中减少 protected token,让模型看到更多原始内容,理论上至少不应该让质量变差。
但后面的实验结果并没有这么简单。

1e8bd12 feat: 增加静态代码上下文翻译实验 和 197b57d refactor: 完善最小保护策略与精确重试】先从 Default 与 minimal-v1 开始
前面的实验中,我先设计了一个 minimal protection 模式。
最初只保护完整 .play directive,后续根据真实失败补充了 emphasis delimiter,并加入更精确的 retry。
最终形成了 minimal-v1。
在 7 个代表页面的一轮实验中:
Default:
首次通过 7/7
最终通过 7/7
minimal-v1:
首次通过 5/7
最终通过 7/7随后还做了一次匿名翻译质量比较。
单次样本的结果是:
Default:4/7
minimal-v1:3/7当时这个结果让我更加疑惑。
minimal-v1 明明向模型暴露了更多原始内容,为什么不仅没有表现出稳定质量优势,有些页面反而明显比 Default 差?
如果简单按照“上下文越完整,翻译越好”的经验,这并不太容易解释。
于是我决定暂时不继续改 protection policy,而是先把一个更基础的问题搞清楚:
Default 的 protected token,到底真正隐藏了什么?
157 个 protected token,到底遮住了多少自然语言?
我针对固定的 7 个代表页面重新做了一次 Default protection 信息损失审计。
结果是:
Pages: 7
Protected tokens: 157
Replaced source bytes: 1,206
A machine structure: 761 bytes
B code / technical content: 419 bytes
C fixed natural language: 26 bytes
D hidden translatable English: 0 bytes
Hidden translatable English items: 0
Hidden translatable English ratio: 0.00%
这个结果非常关键。
原来我之前把“原始字节完整性”和“自然语言上下文完整性”混在了一起。
例如:
`comparable`Default 并不是把整个 comparable 隐藏掉。
实际上主要被替换的是两侧的反引号,而中间真正具有技术语义的:
comparable仍然可以被模型看到。
类似地:
*Note:*主要隐藏的是 emphasis delimiter,而 Note: 本身仍然存在于模型输入中。
链接:
[[/pkg/image/#Image][Package image]]主要隐藏的是:
/pkg/image/#Image而真正需要理解和翻译的:
Package image仍然完整可见。
因此,Default 和 minimal-v1 实际上并不是:
完整上下文
VS
残缺上下文更准确的区别是:
Default:
完整的页面级自然语言上下文
+ 较强的机器结构抽象
minimal-v1:
完整的页面级自然语言上下文
+ 较少的机器结构抽象
+ 更多原始代码和机器语法这和 WordPress 的“整篇翻译 vs 分段翻译”其实是两个不同的问题。
WordPress 分段翻译会真正丢掉前后自然语言上下文。
而 A Tour of Go 的 Default protection 并没有把完整课程页面拆成多个孤立翻译单元。
真正可能损失语义的,是静态代码
虽然 7 个页面中没有任何可翻译英文自然语言被完全隐藏,但 Default 确实会隐藏一些完整静态代码。
例如 generics/1 中:
func Index[T comparable](s []T, x T) int以及 methods/24 中:
package image
type Image interface {
ColorModel() color.Model
Bounds() Rectangle
At(x, y int) color.Color
}这些代码不需要翻译,但确实包含技术语义。
于是问题进一步收敛成:
如果 Default 的自然语言上下文本来就已经完整,那么仅仅让模型额外看到这些静态代码,是否会改善翻译?
为了验证这一点,我没有取消现有 static code protection,而是新增了一个只用于开发实验的:
--dev-static-context这个模式仍然使用完全相同的 Default protected page。
区别只有一个:
在 user message 中额外加入一份只读 static code reference。
代码只用于帮助模型理解技术关系,不允许复制、修改或重新输出。
这样就能够尽量把实验变量压缩为:
模型是否能够看到 static code 的技术语义但实验过程中又发现了一个更大的变量
第一轮 Static Context 实验还没来得及得出质量结论,就出现了一个更值得追查的问题。
我发现 generics/1 的普通 Default,在之前的一次 fresh 实验中能够首次通过 validator,而新一轮完全相同的 Default 请求,却出现了 protected token 重复。
于是我直接拿两轮已有实验的 request.json 和 response.json 做对比。
比较页面包括:
generics/1
flowcontrol/8
methods/20
concurrency/7
methods/24结果非常一致:
REQUEST_IDENTICAL : true
RESPONSE_IDENTICAL: false5 个页面全部如此。
最终结果:
identical request -> different assistant content = 5/5
这里比较的不只是 Prompt 大致相同。
实际确认了:
- source 完全一致;
- model 一致;
- system message 字节一致;
- user message 字节一致;
thinking一致;do_sample一致;max_tokens一致;- 实际 API payload 一致。
但 5/5 页面得到的 assistant content 都不同。
而且差异不仅是措辞不同。
generics/1 的一轮 Default 可以通过 validator,另一轮完全相同的 Request 却复制了同一组 protected token:
protected token count = 21, want 19
token 5 occurrence count = 2, want 1
token 6 occurrence count = 2, want 1这意味着运行间波动甚至能够影响:
validator pass
VS
validator fail至于 GLM-5.2 服务端为什么会出现这种行为,仅凭当前项目保存的 artifacts 无法判断,我也没有继续猜测服务端路由、模型部署或计算层面的具体原因。
项目真正需要面对的事实只有一个:
相同 Request,在当前实际 API 调用中并没有表现出字节级可复现性。
之前的 4:3,不能再当成模式结论
这也直接改变了我对前面 Default 与 minimal-v1 匿名评审的理解。
原来的结果:
Default:4
minimal-v1:3只能表示:
这一轮各自抽取一个 candidate 时,刚好得到这样的结果。
它不能证明:
Default 的翻译质量稳定高于 minimal-v1也不能证明:
minimal-v1 没有质量价值因为现在已经确认,同一个 Default 请求自己的多次独立运行,就足以产生明显不同的译文。
因此,如果每种模式每页只生成一次,就很容易把模型自身的运行波动误认为 protection policy 的效果。
这也是这轮实验中最重要的方法论修正之一。
把 Static Context 实验升级为 3 次独立重复
为了降低单次输出波动的影响,我没有继续做“一页一个 Default 对一个 Static Context”的实验。
而是固定 5 个页面:
generics/1
flowcontrol/8
methods/20
concurrency/7
methods/24两种模式:
Default
Static Context每页每种模式运行 3 次独立请求:
5 pages × 2 modes × 3 repeats
= 30 planned samplesR2 和 R3 还专门采用了交错执行:
同一个 page + repeat 的 Default 与 Static Context 尽量连续运行,同时随机决定哪种模式先执行。
网络失败、validator failure 都原样保留,不补跑,不重新选择“更好看的结果”。
最终结构结果为:
Default Static Context
generics/1 V / P / V V / V / V
flowcontrol/8 P / P / P P / P / P
methods/20 P / P / P N / P / P
concurrency/7 P / P / N V / P / N
methods/24 P / P / P P / P / P
其中:
P = validator pass
V = API success, validator fail
N = network/API failure总体结果:
Default:
planned 15
API success 14
network failure 1
validator pass 12
validator fail 2
usable/planned 12/15
Static Context:
planned 15
API success 13
network failure 2
validator pass 9
validator fail 4
usable/planned 9/15
这个样本量仍然不足以证明 Static Context 一定降低结构可靠性。
尤其存在网络失败,而且前面的可复现性实验已经确认相同请求自己的 validator 结果也可能发生变化。
但至少目前可以确认:
Static Context 没有表现出明显的结构可靠性优势。
接下来更重要的是看语言质量。
不再挑一个“最好结果”,而是把所有有效译文都拿来盲评
这一次我没有从每种模式里挑一份“代表译文”。
所有通过 validator 的 candidate 全部进入匿名质量材料。
最终进入盲评的页面是:
flowcontrol/8 6 个候选
methods/20 5 个候选
concurrency/7 3 个候选
methods/24 6 个候选generics/1 只有一个有效 candidate,因此没有参与两种模式的语言质量比较。
所有候选在每个页面内部随机打乱,只显示:
CANDIDATE A
CANDIDATE B
CANDIDATE C
...完全隐藏:
- Default / Static Context;
- R1 / R2 / R3;
- attempt;
- worktree;
- token usage。
评审时先锁定完整排名,之后才打开单独保存的 mapping。
methods/24 展示出了非常明显的运行波动
methods/24 一共有 6 个有效候选。
其中 Candidate D 的表达是:
但我们将使用预定义的实现 `color.RGBA` 和 `color.RGBAModel`,
暂不深究这些接口。
这些接口和类型由 image/color 包定义。Candidate C 则是:
但我们将使用预定义的实现 `color.RGBA` 和 `color.RGBAModel`
来忽略这一点。
这些接口和类型由 image/color 包指定。在完全不知道身份的情况下,最终锁定的排名是:
D > B > A > F > E > CD 排第一,C 排最后。

methods/24 Candidate D 与 Candidate C 匿名质量比较】如果这时只看译文,很容易猜测:
也许 D 来自某种更好的 Prompt 或 Context 设计,而 C 来自另一种较差的策略。
但揭晓身份以后,结果完全不是这样。
D = Default R3
C = Default R1也就是说:
Default R3 → 第 1 名
Default R1 → 第 6 名两份译文来自同一个页面、同一种 Default 模式。

methods/24 身份揭晓,第一名和最后一名都是 Default】这张结果几乎浓缩了整轮实验最重要的发现:
同一种翻译策略的一次输出,并不能稳定代表这种策略的语言质量。
Static Context 有没有让译文更好?
揭晓全部匿名映射以后,几个页面的排名分布如下。
flowcontrol/8:
Default:1、4、5
Static Context:2、3、6methods/20:
Default:1、3、4
Static Context:2、5concurrency/7:
Default:2、3
Static Context:1methods/24:
Default:1、3、6
Static Context:2、4、5最明显的特征不是哪一组全面领先,而是:
两种模式大量交错。
没有出现类似:
Static Context:1、2、3
Default:4、5、6这种明显整体上移。
例如 methods/24 中,Default 自己就同时占据:
第 1
第 3
第 6Static Context 则是:
第 2
第 4
第 5因此目前没有观察到:
额外暴露 static code 可以稳定提高 GLM-5.2 的中文翻译质量。
“更多上下文没有更好”其实是个错误的问题
走到这里以后,我觉得最初的问题本身需要重新描述。
一开始我问的是:
为什么给模型更完整的内容,翻译质量没有更高?
现在更准确的问题应该是:
额外提供的究竟是不是新的“有用语义上下文”?
Default protection 虽然用了 157 个 protected token,但 7 个代表页中:
hidden translatable English = 0真正需要翻译的页面级自然语言上下文本来就完整存在。
minimal-v1 或 Static Context 新增的主要不是前后叙事、句子关系或页面主题,而是:
- 更多机器语法;
- link target;
- directive;
- static code;
- 技术结构。
其中 static code 确实可能提供额外技术语义,所以又专门做了 Static Context 单变量实验。
但在目前的重复实验中,这种额外技术语义没有表现出稳定的翻译质量优势。
因此:
更多原始字节不能简单等同于:
更多有助于自然语言翻译的上下文这和 WordPress 整篇翻译优于分段翻译并不矛盾。
WordPress 分段会真正丢失自然语言上下文。
而这里两种模式从一开始就都在翻译一个完整的 present.Section。
当前工程决策
经过这一轮实验,我暂时不会继续为了减少 protection 而修改正式翻译流程。
当前正式方案继续使用:
Default protected-token原因并不是实验已经证明:
Default 翻译质量更高目前没有这样的证据。
更准确的理由是:
- Default 已经具有成熟的结构保护和恢复能力;
- 它没有隐藏这 7 个代表页中的可翻译自然语言;
- minimal-v1 没有显示稳定的语言质量优势;
- 单独补回 static code 也没有显示稳定质量优势;
- GLM-5.2 自身的运行间质量波动明显,单次输出不足以评价 protection policy;
- 在没有明确质量收益的情况下,没有必要为了“原始内容看起来更完整”而削弱已经验证过的结构保护。
minimal-v1 和:
--dev-static-context则继续保留为开发实验能力。
它们并不是无用的分支。
恰恰是这些实验模式帮助我确认了:
protected token 到底遮挡了什么,以及哪些原先看起来像 protection policy 导致的质量差异,其实可能只是模型自身的运行间波动。
最后的一个重要变化:以后不能再用“一次翻译”评价翻译策略
这轮实验对项目后续测试方法的影响,可能比 Static Context 本身还大。
以后如果需要比较两个 Prompt、两个 protection policy 或两个翻译工作流,我不会再简单采用:
每页每模式调用一次
→ 两份译文 A/B
→ 看谁更好因为现在已经实际验证:
相同 Request
→ 5/5 页面 Response 不同甚至同一个 Default,在 methods/24 中可以分别产生盲评第一名和最后一名。
更可靠的方式应该是:
固定页面
×
固定模式
×
多次独立运行
→
分别统计结构可靠性
→
所有 validator-passed candidate 全量匿名评审
→
最后再揭晓模式这种方法成本更高,但至少不会轻易把一次偶然生成结果误认为架构本身的优势。
这也算是这次 A Tour of Go 多语言翻译实验中,一个比“到底要不要少保护几个 token”更重要的收获。
A Tour of Go 多语言翻译项目
本系列完整记录 A Tour of Go 多语言翻译项目从架构设计、整页翻译、结构保护、自动校验,到生产发布与后续维护的实际开发过程。
项目入口:
✅ 在线学习:A Tour of Go 简体中文版
✅ 项目源码:GitHub:shuijingwan/go-tour-i18n
当前第一阶段已完成简体中文版本。项目属于非官方社区多语言翻译项目,与 Go 官方无隶属或授权关系。

发表回复