TGI 生成中文文本时遇到重复和截断,通常要区分两个来源:重复是采样参数不合适,截断是生成长度限制或停止条件设置不当。repetition_penalty 惩罚已经生成的 token,no_repeat_ngram_size 禁止连续 n 个 token 重复。中文没有词边界,ngram 计算的是模型 token,不是汉字短语,所以一开始不要设得太小。
前置条件
检查你能否访问 TGI 服务的 /info 接口,确认模型名称和当前加载的版本。准备一组长中文提示词,提示词结尾要明确,比如“请接着写一段关于城市交通治理的建议”。调用时关闭流式,避免观察时被输出截断。
配置生成参数
用 curl 直接请求 /generate,在 parameters 中同时传入 repetition_penalty 和 no_repeat_ngram_size。以下是一个可复制的起始配置。
curl http://127.0.0.1:8080/generate -X POST \
-H 'Content-Type: application/json' \
-d '{
"inputs": "请继续写一段关于城市交通治理的中文短文,要求逻辑清晰:",
"parameters": {
"max_new_tokens": 256,
"repetition_penalty": 1.2,
"no_repeat_ngram_size": 4,
"temperature": 0.8,
"do_sample": true
}
}'
如果返回 400 或错误信息提示未知参数,说明当前 TGI 版本不支持 no_repeat_ngram_size。优先保留 repetition_penalty,把它的值降到 1.1,再次请求。这不是保险式配置,而是为了先确定重复是否由 token 级惩罚不足引起。
调整顺序与边界
- repetition_penalty:适用场景是文本中同一个字或词连续出现。操作动作是从 1.1 开始,每次增加 0.05,直到重复减少。验证方式是对比同一提示词生成结果中重复短语的出现次数。风险边界是值超过 1.5 后,句子会变得生硬,甚至漏掉标点。
- no_repeat_ngram_size:适用场景是句子没有连续重复字,但上下文里反复出现整句结构。操作动作是从 3 或 4 开始,每次增加 1。验证方式是用上面 curl 请求返回的文本,人工找出长度等于当前 n 的重复片段。风险边界是值超过 6 后,内容可能被切碎,读起来不连贯。
- max_new_tokens:不属于调参范围。当你发现每次生成都在相同长度停止,并且末尾是一个不完整句,先查看 TGI 服务端是否配置了最大输出长度。如果服务端限制是 256,客户端传 512 也不会超过。
验证与回退
验证要有固定条件。生成时加入 seed 参数,让随机采样可复现,否则无法判断是参数效果还是随机波动。
"parameters": {
"seed": 42,
"max_new_tokens": 256,
"repetition_penalty": 1.2,
"no_repeat_ngram_size": 4,
"temperature": 0.8
}
用同一组参数生成三次,每次 seed 不同,人工检查重复和截断是否每次都出现。如果加入 seed 后重复消失,说明问题来自采样随机性,不需要继续调参。如果 no_repeat_ngram_size 不可用,回退方案是只保留 repetition_penalty,并在请求中加入 stop_sequences,让生成在完整句子处主动结束,而不是被截断在重复片段上。
另外,可以写一个 20 行内的 Python 脚本,统计输出中重复的 n-gram 数量,作为验证和回退前后的对比数据。注意这个脚本只统计可见字符,不负责判断语义。
from collections import Counter
def repeated_ngrams(text, n=4):
text = ''.join(text.splitlines())
seq = list(text.replace(' ', ''))
ngrams = [tuple(seq[i:i+n]) for i in range(len(seq)-n+1)]
return {g: c for g, c in Counter(ngrams).items() if c > 1}
print(repeated_ngrams('你的生成结果文本', 4))