stop-sequence-multibyte · ZH · 2026-11-04

把中文句子从中间截断的 stop 序列:在一把 key 上调 stop,别让多字节文本被切在半路

本文对比不同 LLM 后端在匹配 stop 序列时是按字节还是按 token 处理,解释这对中文输出截断的影响,并给出在聚合 API 上安全设置 stop 的写法,避免多字节字符被切半导致乱码。

为什么 stop 序列会切坏中文

stop 序列告诉模型“遇到这个字符串就停”。但不同后端判断“遇到”的方式不一样:有的按解码后的字符串匹配,有的在 token 层面提前截断,还有的在字节流上做匹配。中文一个汉字通常占多个字节,如果 stop 匹配发生在字节层面,就可能正好切在一个汉字的中间,解码后出现乱码或替换字符。

把同一段中文请求发到聚合站的不同模型后端时,你可能观察到:英文 stop 很少出问题,中文 stop 偶尔在结尾多出半个字或问号方块。这不是模型“写错了”,而是 stop 的匹配粒度不同。

三种常见的 stop 匹配方式

  • 按解码后字符串匹配:模型先生成文本,再在文本里找 stop。最安全,但停止可能晚一个 token。
  • 按 token 匹配:把 stop 预先切成 token 序列,在生成过程中比对。stop 本身必须能被目标模型的分词器完整切出;中文 stop 如果被切碎,可能匹配不上或提前停。
  • 按字节匹配:在字节流里找 stop 的字节序列。速度直接,但中文 stop 和中文输出都可能落在多字节字符中间,造成截断。

聚合站一个 API key 调多个模型,这些差异对你来说是隐藏的。你写的 stop 在 A 模型上正常,在 B 模型上可能就切出乱码。

中文 stop 的安全写法

优先选边界清晰的 stop

避免用单个汉字或半个词做 stop。用完整词、标点或换行组合,例如 \n\n、。\n、【结束】。这些边界在多数分词器里更容易被整体识别,减少半字截断。

不要在 stop 里混用中英文边界

像 答: 这种 stop 本身没问题,但如果你同时要求输出严格 JSON,stop 又设在 JSON 结构中间,不同后端可能一个在冒号后停、一个在引号前停。尽量让 stop 落在结构之外,比如整个 JSON 结束后的换行。

用后处理兜底

即便 stop 设置得当,也建议在客户端对返回文本做一次清理:去掉末尾不完整的多字节字符、非法代理对和替换字符。聚合站返回的是统一格式,但底层截断点不同,兜底能保证展示层不出现乱码。

需要精确截断时,改用 max_tokens + 解析

如果业务要求“输出到某个标记为止”,与其依赖 stop 的字节级行为,不如让模型正常生成,再用字符串查找定位标记并截取。这样截断发生在完整解码后的文本上,不会切到字符中间。代价是多花一点输出 token,但可控性更高。

在聚合 API 上验证 stop 行为

同一把 key 可以调多个模型,这正好用来做对比测试:用同一段中文 prompt 和同一个中文 stop,分别请求 Claude、GPT、DeepSeek、Qwen、GLM、Kimi,检查返回文本末尾是否完整。如果某个后端出现半字,就把 stop 改成更长的边界串,或改用后处理截断。

注意计费按官方价 ×1.3 扣费,测试时控制输出长度即可,不必担心不同后端计费口径差异带来的额外复杂度。

小结

stop 截断中文的根因是匹配粒度:字节级匹配可能切在多字节字符中间,token 级匹配依赖分词器对 stop 的切分,字符串级匹配最稳但停止稍晚。写 stop 时选完整词或标点边界,避免单字 stop,客户端做末尾清理,需要精确位置时改用生成后解析。用一把 key 跨模型对比,能快速发现哪个后端的 stop 行为需要额外处理。