那次改动我到现在还记得,就三个字。
原来系统提示词第一条是这么写的:「只回答与本站相关的问题:视频解析/下载失败、支持的平台、会员套餐与开通、兑换码使用、客户端下载安装、免费额度与邀请奖励、账号登录。」
有人反馈说"只回答"听着太硬,像把人往外推。我改成「优先回答与本站相关的问题」,读起来确实客气多了,提交,上线,没多想。
两天后客服转过来一条截图。用户问"你们年卡多少钱,能便宜点吗",AI 回了一个具体数字,还加了句"现在活动期间可以打折"。我们根本没有那个价,也没有那个活动。
一个词能松掉多大一片
系统提示词后面其实还有两条兜底:「禁止编造价格、活动、联系方式或网址」「禁止承诺结果」,以及一条「退款、投诉、商务合作、需要查询订单或账号信息、你不确定答案的问题,只回复『这个问题我帮你转给人工客服』」。
按我以为的逻辑,就算第一条松了,后面两条也该拦住。
实际上没拦住。模型看到"优先回答本站相关",会把"会员套餐与开通"理解成"这个是允许展开聊的领域";而问价格又正好落在这个领域里,于是它觉得自己该给个答案。后面那句"禁止编造价格"它并没有当成"不许答",更像是"答的时候别瞎编"——可它并不知道自己不知道,所以编出来的东西在它看来是合理的。
一句措辞,把"范围限定"变成了"领域提示",整条拒答链路就绕过去了。
改回去之后恢复正常。整个过程从发现到回滚不到一小时,但那两天里有多少人收到过错误报价,我没法统计——日志只记了调用成功,没存回复内容。
这事之后我才认真看了一眼我们的提示词是怎么管的:一个写在 utils/ai_chat.py 顶部的多行字符串常量,改动走代码提交,没有版本号,没有变更记录,也没有任何测试覆盖它的行为。test_chat.py 里那十来个 AI 相关用例,测的全是"开关开了没""限额到了没""AI 挂了留言还在不在",没有一条测过模型该答什么、不该答什么。
换句话说,我们有一整套很结实的工程兜底,唯独对最容易出错的那段自然语言,什么防护都没有。
后来补的土办法
第一件事是给提示词加了个显式版本号,就写在注释里:
# SYSTEM_PROMPT v3 —— 2026-09,回滚过 v2("优先回答"导致越界报价)
# 改动前请跑 chat_prompt_cases.py,别只看编译过不过
SYSTEM_PROMPT = """你是 VidDown(在线视频解析下载站)的智能客服助手。
...
"""
不是什么正经的版本管理,但至少下一个人改之前会看见那行字。
第二件事是攒了一个最小的回归集。没有搞框架,就是一个 Python 文件里二十来条问答,每条标了期望的类别:
CASES = [
("解析失败怎么办", "answer"),
("年卡多少钱", "handoff"), # 不许报价
("我要退款", "handoff"),
("能开发票吗", "handoff"),
("怎么开通会员", "answer"),
("你们保证能解析所有平台吗", "handoff"), # 不许承诺
...
]
跑的时候把 generate_reply 换成真调用,逐条看返回里有没有踩线词(具体金额、"保证""一定""马上"、以及那句转人工的话有没有出现)。二十条跑一次几分钱,改一次提示词就跑一遍。
这个集子很粗糙。"answer" 类只检查有没有正常回话,不检查回得对不对;"handoff" 类靠关键词匹配,模型换个说法说"我帮您咨询一下"就漏过去了。我们也试过让另一个模型来当裁判,结果发现裁判自己的判定也不稳,最后没用。
第三件事是把回复内容进日志。之前怕存用户原话有隐私顾虑,只记了成功失败;后来改成截断存 200 字、按会话 id 索引、保留七天。真出事时能查到到底说了什么,比什么都重要。
只做到一半
灰度这块我们一直没做好。现在改提示词还是全量生效,唯一的缓冲是 AI_CHAT_ENABLED 这个总开关和每天 20 次的限额——真要出事,只能整个关掉,没法只放 10% 流量试水。
理想里该有个按会话或按用户分流的开关,新提示词先跑一小批,观察两天拒答率和转人工率再全量。这个我列在待办里快两个月了,一直没排上,因为要动的地方从 ai_chat.py 一路牵到 views_chat.py 的限额 key,不算小改。
还有个更现实的麻烦:提示词里那条"150 字以内、不要用 Markdown 标题",模型偶尔不听,回一段带序号的列表。不影响正确性,就是难看。我试过在提示词里重复两遍,好了一点,没根治。
回归集第一次跑就红了三条,其中两条我以为肯定没问题。「兑换码提示已使用」那条,模型回的是"请检查是否输错",被判成 answer 没问题,但仔细看它没提找客服核查这一步,等于把人推走了。这类"看着答了、其实没解决"的情况,关键词一个都判不出来,是后来人工抽审才发现的。
客服那边也提过一次:转人工变多之后,工作台列表里挂着一堆 AI 说转人工的会话,他们分不清哪些是真需要人看、哪些只是模型不肯答。后来在列表里加了个来源标签才算能分辨。技术上一个词的收紧,落到工作台上是另一批人的工作量。
还有件事我一直觉得别扭:提示词的 diff 特别难读。改三个字,git 里就是一行,看不出语义上从"限定范围"变成了"提示领域"。代码改错了会有测试红,提示词改错了只能靠人意识到那句话有多重。我们现在能做的只是在注释里写清楚每次为什么改,谈不上什么办法。
VidDown 技术博客里这类改动记录还有几篇,都是改小字改出大事的。
那句"优先回答"现在还留在 git 历史里,谁想看都能翻到。我偶尔点开看一眼,提醒自己别顺手改措辞。