内部测试全员点赞,演示效果惊艳全场——这样的功能上线2周后,你的用户突然问:"这些总结是不是在编东西?"

这就是几个月前我遇到的真事。一个用 LLM 做工单摘要和回复建议的功能,50次里大概出错1次。但错的那次,语气、结构、细节看起来和正确的毫无区别。付款失败被总结成"用户想取消订阅",加载慢的投诉被改写成"用户报告欧盟地区服务中断"。

打开网易新闻 查看精彩图片

模型没坏,基准测试分数漂亮,但长尾里全是这种"自信的错误答案",发现它们的是你的真实用户。

以下是 debug 过程中的关键教训,以及最终把幻觉率压到可接受范围的分层方案。

为什么难抓:流畅≠正确

首先要理解一件事:语言模型不管底层推理是否成立,都能产出流畅文本。表面输出里读不到"我不太确定"的信号。自信编造细节的模型,和自信陈述事实的模型,从你的应用视角看一模一样。

更糟的是,评估套件通常偏向典型输入。你的测试打中的是中位数场景,生产流量却撞向长尾——奇怪格式、罕见实体、矛盾上下文、模糊代词、多语言混杂。幻觉就住在长尾里。

我们的具体案例:客户提到多个无关话题时,摘要器会锚定第一个出现或情绪最强的话题,自信地把它当成整单主题。

第一层:约束输出结构

自由散文给模型留下了顺畅编造的空间。约束输出则迫使它对具体主张做出承诺,而这些主张你可以逐一验证。

不要要摘要,要结构化对象:

// 差的写法:自由散文,难验证

prompt = f"Summarize this ticket: {ticket}"

// 好的写法:结构化主张,可逐项检查

schema = {"primary_issue": "str", "customer_intent": "enum[refund, cancel, technical_help, billing, other]", "mentioned_order_ids": "list[str]", "sentiment": "enum[neutral, frustrated, angry]", "requires_human": "bool"}

JSON Schema 或各家提供的函数调用特性更好,因为它们在解码层就施加约束。核心思路是:你要离散的主张,不是段落。主张可以检查,散文不能。

第二层:加验证轮次

这是真正起作用的改动。把输出再过一遍模型,这次唯一的任务是检查每个主张是否有原文支持。