模型准备输出“到手价 8999 元”,网络却先送来“到手价 8”,接着是“99”,最后才是“9 元”。如果网关每收到一块文本,就把当前正则匹配出的数字当完整价格,第一块中的 8 会被误判为低价承诺。
这个反例不需要测出模型的幻觉概率。只要把网络分块和业务实体混为一谈,一个原本正确的回答也可能被护栏改错。
协议边界与业务边界是两件事
WHATWG 的 SSE 规范定义事件流的编码和事件分隔,并不保证一个事件就是完整句子或完整金额。应用端还可能把一次模型文本增量拆成多次网络读取。不要用 TCP 读取次数、SSE 事件数或中文字符数直接代替业务事实数量。
对于价格、库存、优惠有效期这类高风险信息,我倾向于让模型选择可信业务字段,再由确定性模板渲染。若确实要检查自由文本,也要明确允许的语法、缓冲上限、事实版本和失败方式。封面中的拦截结构只表示管道位置,不代表能识别所有幻觉。
用一个严格的小语法展示错误所在
下面约定“一行只允许一条价格”,并且只在换行后校验。它演示实体完成前不显示,不是自然语言价格解析器。数值直接与业务字典的确定值比较,不把“最低允许价”冒充真实成交价。
import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; public class Demo { static class Gate { final StringBuilder pending = new StringBuilder(); final List<String> visible = new ArrayList<>(); final Pattern grammar = Pattern.compile("到手价 ([0-9]+) 元"); void add(String chunk) { pending.append(chunk); if (pending.length() > 256) throw new IllegalArgumentException("buffer limit"); int end; while ((end = pending.indexOf("\n")) >= 0) { String line = pending.substring(0, end); Matcher match = grammar.matcher(line); if (!match.matches() || !match.group(1).equals("8999")) { throw new IllegalArgumentException("unverified price"); } visible.add(line); pending.delete(0, end + 1); } } } public static void main(String[] args) { Gate gate = new Gate(); gate.add("到手价 8"); gate.add("99"); if (!gate.visible.isEmpty()) throw new AssertionError("partial leak"); gate.add("9 元\n"); if (!gate.visible.equals(List.of("到手价 8999 元"))) throw new AssertionError(); boolean rejected = false; try { new Gate().add("到手价 199 元\n"); } catch (IllegalArgumentException expected) { rejected = true; } if (!rejected) throw new AssertionError(); System.out.println("partial price withheld; complete price checked"); } }保存为Demo.java,使用javac --release 11 Demo.java、java Demo运行。本地样例覆盖了分块与不匹配金额,未调用模型或浏览器。生产实现不能照搬这里的有状态对象:每个会话要隔离缓冲,超时和流结束仍未闭合的内容必须拒绝或降级;校验失败后应终止本轮,不继续使用处于失败状态的对象。
校验失败,别假装什么都没发生
已经显示的文字可能被截图、被屏幕阅读器读出,或者进入客户端日志。向前端发送清空指令可以改善展示,却不能撤销已经发生的信息暴露。错误处理应说明当前回答不能确认,并给出权威的业务查询入口。
OWASP 对敏感信息披露的讨论提醒应用方不要只依赖模型提示约束。对事实校验同样如此:规则覆盖范围有限,必须保留没检查到什么、事实字典来自哪里和何时失效。这里没有依据宣称任意价格承诺都能被消除,更没有给出法律责任结论。护栏的价值,在于把一个可定义的风险窗口收窄,而不是让错误在界面上消失后就当作不存在。
本地运行环境为 Java/javac 12.0.1,使用--release 11编译并执行了文中的检查;没有安装新依赖。该结果只覆盖示例列出的逻辑,不代表真实模型、浏览器或生产链路已经验证。