先把当时的情况摆出来。用 openai 1.68 调 gpt-4.1-mini 做合同字段抽取,同一段提示词在编辑器里看着正常,返回的字段名却总带一个看不见的空格前缀,贴进十六进制工具才看到中间夹着 U+200B;改成 requests 直发一样复现,手工删掉那几个位置后就恢复正常。
排查方向其实有限,先把可能性排一排。网页复制粘贴时把零宽空格 U+200B 一起带了进来。也可能是富文本转纯文本的中间层没有过滤零宽字符。
不容易想到的是,prompt 存进 MySQL 时客户端插入了 BOM 头。还有一种情况,Word 粘贴带进了 U+2028 行分隔符,肉眼当成普通换行。
先把提示词逐字符打出码位看有没有异常值
python -c "s=open('prompt.txt',encoding='utf-8').read();print([hex(ord(c)) for c in s if ord(c)>0x2000][:20])"
然后按码位批量清掉零宽字符再另存一份
python -c "import re;s=open('prompt.txt',encoding='utf-8').read();open('clean.txt','w',encoding='utf-8').write(re.sub(r'[\u200b-\u200f\u2028\u2029]','',s))"
随后把这一步写进发布前的模板检查脚本
python lint_prompt.py --rules zero-width --fail-on-hit
等前面都确认过,再用清理后的文本重放同一个请求核对字段
curl https://api.openai.com/v1/chat/completions -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" -d @req_clean.json
恢复的判定其实很直接。同一段文本连续发三次,返回的字段名不再带空格前缀,字段集合完全一致,检查脚本对模板目录也不再报零宽字符。
补一句。看不见的字符不会出现在 diff 里,只能靠码位扫描,把检查放进提交流程比每次人工比对可靠。同一份配置在测试环境跑通,不代表线上没事,两边的默认值经常不一样。
