直接回答
乱码是某个边界上的症状,不是对模型质量的诊断。应比较最早可观察表示与下一层:运行时输出、应用收到的字符串或流事件、组装后的全文、解析结果和 UI 渲染。如果运行时返回 Kotlin/Java String,先查事件顺序、重复收集、截断、规范化和界面状态,不要立刻假设是字节解码。如果原生桥接返回字节,就记录文档规定的编码;应在字符完整的边界解码,或使用增量解码器。任意流块可能切开一个多字节字符,逐块独立解码会产生替换符。 准备固定夹具,包含 ASCII、带重音符号字符、中日韩文字、emoji、标点和换行。保留预期源文本,在每个边界比较码点或转义后的内容。生产环境不要记录真实提示词或答案;合成夹具足以复现编码行为,也避免保存用户内容。测试完整响应及流式分块;若传输可能在多字节序列中间切块,就加入这种情况。输出未完成、事件重复、解析器丢字段和乱码显示,应是不同错误类别。 制品、tokenizer 和提示模板仍可能相关,但不应先动它们。如果运行时原始结果就是异常文本,再单独核对制品身份与对话格式;如果运行时文本正确而 UI 显示异常,就查转换和渲染。如果原始输出字符正确但片段重复或缺失,就检查收集器生命周期、缓冲追加、取消和结束逻辑。将模型 token 生成与文本解码分开,避免修复某一层时掩盖另一层缺陷。 只有产品文档说明了等价语义时才做 Unicode 规范化,并为其编写预期等价测试;规范化可能改变码点序列,即使显示字形看起来相似。不能把删除组合符号或标点当作通用“清理”。检查字符串截取是否遵循合理边界;若字节缓冲经过原生接口,就不要使用平台默认字符集。JNI 字符串应按官方定义的转换方式处理,不要把 Modified UTF-8 与任意 UTF-8 数据当成同一种编码。 记录故障阶段、载荷类型、编码契约、流序号、应用/运行时版本和固定夹具 ID。如果 API 没有暴露原生字节,就说明这一点。有效结果应指出文本首次发生变化的边界,并形成回归夹具。没有其他证据时,不要声称模型已修复、支持某语言或性能提高。 如果流式接口同时包含部分内容和终止信号,测试要区分“最后一个有效文本块已到达”“流正常结束”“收集器取消”和“页面观察停止”。UI 页面被销毁不一定代表底层任务停止,也可能让新页面重新收集同一条状态流。使用事件 ID 或任务 ID,确保每个块只消费一次。若页面生命周期恢复后旧事件重新播放,重复文本可能与字符编码完全无关。把序列号、观察者和任务所有权纳入诊断,才能避免通过更换模型掩盖应用状态问题。 支持排查时优先保存合成复现材料和阶段码。即使文本乱码,也不需要默认收集真实对话来调试。只有经过授权和必要性评估,才应临时查看真实内容,并限制访问与保留期限。普通日志记录应用构建、文本载荷类型、错误类别和失败阶段即可;不要额外写入提示词或整段生成内容。
日志中的中文正常,最终界面偶尔显示替换字符
某个本地模型响应在测试日志里看起来正确,但生产 UI 偶尔显示替换字符,有时末尾词语还重复。应用通过原生桥接流式发送内容,并由屏幕级观察者收集。
决策
用合成 Unicode 输出逐层比较所有可观察边界,保留流顺序和结束状态;只有桥接实际返回字节时才调查字节解码。
预期结果
团队能把症状定位到传输、收集器、解析或 UI,并添加回归夹具,而不保存用户对话。
需要收集的证据
只有将以下证据与同一模型文件和测试运行关联起来,结论才可复核:
- COVE-MODEL-02:配置中 Gemma 4 E2B 的 SHA-256 字段为空,不能声称制品哈希已验证。
- BENCHMARK-01:项目没有完整设备基准集,文本故障诊断不得变成性能或普遍模型质量结论。
发布结论只对所记录的配置和验收集有效;扩大适用范围需要更多记录。
实施流程
- 分类“乱码”的具体表现. 把替换符、错误字符、重复片段、缺失块、换行损坏和解析字段丢失分开标记。保留合成截图或转义文本,但不能把症状名称当成根因。
- 建立 Unicode 边界夹具. 使用包含 ASCII、变音符号、中日韩文字、emoji、标点与换行的合成文本。定义预期字符串和码点属性;增加可在传输边界处切块的流式用例。
- 对比运行时、应用、解析和 UI 表示. 依次保存每个安全可观察输出及其事件 ID/顺序号。运行时返回字符串时查收集器和字符串转换;返回字节时核对文档编码,并只在安全边界解码。
- 隔离切块、规范化或生命周期缺陷. 回放完整和流式响应,取消并重建观察者、重建页面,并确认事件只追加一次。单独测试规范化和子串逻辑,不用批量删除组合符号或标点的办法掩盖问题。
- 加入回归并限制诊断数据. 把合成夹具留在测试中,在首次发生变化的层断言预期转换;生产事件只保存阶段元数据。不要为排查显示问题把真实提示词、输出文本或用户标识写进常规日志。
让每个阶段的状态都可观察。失败信息应指出阶段、模型、运行时和恢复动作,同时避免记录用户私密内容。
验收标准
- 默认将问题归类为字节/字符解码、流顺序、转换、解析或渲染,而非直接说模型质量低。
- 合成 Unicode 夹具覆盖完整与流式响应,并断言文本首次变化的边界。
- 字节经过原生边界时使用显式编码;字符串事件不能又按任意字节重新解码。
- 生产诊断不会仅为排查显示问题保留用户提示词或生成文本。
决策对照表
| 维度 | 选择或证据 | 解读方式 |
|---|---|---|
| 乱码/替换字符 | 检查字节到字符串的契约与解码边界 | 仅在接口暴露字节时相关;如果各层已传递有效字符串,就不是首要方向。 |
| 流片段重复或缺失 | 检查事件顺序、观察者生命周期、追加与结束逻辑 | 通常是应用流状态症状,和 Unicode 字节解码不同。 |
| 原始输出正确而 UI 异常 | 比较解析对象与界面字符串转换 | 说明首次变化发生在运行时之后,不应优先更换模型。 |
需要避免的失败模式
- 看到乱码就先更换模型或 tokenizer。
- 把流重复、解析字段丢失和字节解码问题混为一谈。
还要避免静默回退、版本未固定、缺少取消处理,以及把不同配置的结果混在一起下结论。失败运行也应与成功记录一并保存。
最小复现记录
| 层级 | 记录内容 |
|---|---|
| 设备 | 厂商、型号、芯片、内存档位、操作系统版本 |
| 软件 | 应用版本和 Git 提交号 |
| 模型 | 系列、变体、版本、格式、文件长度、摘要、量化方式 |
| 运行时 | 名称、版本、请求后端及实际后端证据 |
| 工作负载 | 样例版本、输入摘要、提示词摘要、输出策略 |
| 结果 | 完成、失败、取消、回退及保护隐私的诊断信息 |
发布检查表
- ☐ 主要搜索问题已回答,且没有未经支持的数字。
- ☐ 所有模型文件和运行时版本均已固定。
- ☐ 代表性任务和失败处理策略均有说明。
- ☐ 生命周期、取消、清理和回退均已测试。
- ☐ 用户内容与网络边界均有说明。
- ☐ 结论只适用于被记录的配置。
- ☐ 新增测试结果时链接到原始方法或证据。
来源与相关证据
中文部署与排错专题见。奇连 AI 端侧专题。
常见问题
是否应该把每个模型流块单独按 UTF-8 解码?
只有传输契约保证每块都在完整字符边界结束时才可以。多字节字符可能横跨多个块;应使用文档规定的增量解码器,或先组装字节再解码,并用拆分边界夹具验证。
乱码是否说明 tokenizer 错了?
不一定。乱码可能出现在桥接转换、流组装、解析或 UI 渲染阶段。先比较最早可观察输出,再决定是否检查 tokenizer 或模型配置;保留原始合成夹具。
能否删除所有不常见标点让文字变正常?
不要批量清理。标点和组合符号可能有意义。应找出准确的变换边界,为受影响字符增加夹具后再修改规范化逻辑,避免改变用户原文。