1. 先别急着写代码,先看清“chatbot生成不了”到底长啥样
我第一次把对话系统跑起来时,兴冲冲地对着麦克风喊“今天天气怎么样?”,结果前端直接弹了个空白的聊天气泡——后台日志里只有一句response: null。那一刻我才体会到什么叫“chatbot生成不了”。
常见翻车现场大概如下:
- 用户说完一句话,ASR 把“帮我订张票”识别成“帮我订张表”,NLU 直接放弃,返回空 intent,后面所有模块都躺平。
- 多轮对话里,用户先问“北京呢?”再补一句“天气怎么样”,由于历史记录没带上,DST(对话状态跟踪)以为这是新话题,上下文断裂,答非所问。
- Transformer 生成模块调用超时,超过 5 秒没返回,前端因为没收到数据直接报“服务繁忙”,用户以为机器人宕机。
- 意图识别置信度阈值设得过高(0.9),导致稍微口语化一点就被判为“unknown”,最终触发不了任何业务逻辑。
把这些坑列出来,你会发现:问题往往不是某个算法不行,而是整条链路“断”了一环。下面我就用一套“Rasa + 轻量 Transformer”混合架构,带你亲手把断掉的地方接回去。整套方案已在内部客服机器人上线,日均 3k 轮对话,稳定跑三个月。
2. 技术选型:为什么不用纯 Dialogflow,也不迷信大模型?
Dialogflow(或同类 SaaS)
优点:上手快、可视化、中文支持好。
缺点:自定义策略受限、数据出境、QPS 高时费用飙升;一旦“生成不了”只能提工单,自己调不了底层。纯 Transformer 生成式
优点:答案自然、无需写模板。
缺点:容易胡编、延迟高(2~4 s)、需要大量 GPU;对垂直场景没有 fine-tune 时,答非所问概率高。Rasa + 自托管 Transformer(2B 级小模型)
- Rasa 负责 NLU 与对话状态机,规则可控、可本地部署,数据安全。
- 小 Transformer 只在“需要创造力”时启用,例如开放式闲聊、摘要、欢迎语生成。
- 规则兜底 + 生成增强,既保证准确率,又降低硬件成本。
一句话:用规则保证“不翻车”,用生成让回答“像人话”。
3. 核心实现:三步搭好“能说话”的骨架
下面代码可直接拷到项目里跑,注释占篇幅 30% 以上,方便二次开发。
3.1 Rasa Core 多轮状态机(YAML 版,配状态转移图)
先画一张极简状态图,帮助理解:
[用户问好] → greet → bot_hello ↓ [用户问天气] → weather → bot_weather ↓ [用户说“谢谢”] → goodbye → bot_byedomain.yml 片段(只保留关键槽位与意图):
intents: - greet - weather - goodbye entities: [] slots: {} responses: utter_greet: - text: "嗨,我是小助手,有什么可以帮您?" utter_weather: - text: "请问您想查询哪个城市?" # 先反问,把槽位 city 填上 utter_goodbye: - text: "再见,祝您生活愉快!" actions: - utter_greet - utter_weather - utter_goodbye - action_weather_api # 自定义 action,调后端天气接口rules.yml 里写死“单轮”逻辑:
rules: - rule: 打招呼 steps: - intent: greet - action: utter_greet - rule: 说再见 steps: - intent: goodbye - action: utter_goodbyestories.yml 负责“多轮”填槽:
stories: - story: 查天气交互 steps: - intent: weather - action: utter_weather - intent: inform # 用户回答“北京” entities: - city: "北京" - action: action_weather_apiRasa 会自动把city注入 tracker,DST 完成上下文跟踪;如果用户跳话题,tracker 会保留历史,直到达到max_history上限(默认 5 轮)。
3.2 Transformer 生成模块 + 异常兜底
当意图置信度 < 0.4 或 NLU 返回 “unknown” 时,调用生成模型做开放回复。下面代码用 HuggingFace pipeline,CPU 可跑 distil 版本。
# fallback_or_generate.py import logging from transformers import pipeline, set_seed import re set_seed(42) # 复现性好 gen = pipeline("text2text-generation", model="google/flan-t5-small", device=-1) # -1=CPU # 1. 敏感词正则,可自己加词库 SENSITIVE_PAT = re.compile(r"(badword1|badword2)", re.I) def safe_generate(user_text: str, max_len=64) -> str: """ 生成式兜底,返回 str;任何异常都回落到规则模板。 """ try: # 1. 敏感词直接拒绝 if SENSITIVE_PAT.search(user_text): return "抱歉,我无法回答这个问题。" # 2. 构造 prompt,让模型知道它是客服 prompt = f"User: {user_text}\nAssistant:" # 3. 生成 out = gen(prompt, max_length=max_len, do_sample=True, top_p=0.9)[0]["generated_text"] # 4. 二次校验,若还是敏感 if SENSITIVE_PAT.search(out): return "抱歉,我无法回答这个问题。" # 5. 空结果保护 if not out or out.isspace(): raise ValueError("empty generation") return out.strip() except Exception as e: # 记录 + 回落 logging.warning("Gen error: %s", e) return "我还在学习中,暂时无法回答,换个说法试试?" # 在 Rasa custom action 里调用: # reply = safe_generate(tracker.latest_message.get("text")) # dispatcher.utter_message(text=reply)要点:
- try-catch 包住所有可能超时、OOM、敏感词命中。
- 返回字符串永远非空,前端就不会“白板”。
- 日志落盘,方便后续统计“生成触发率”。
3.3 对话质量监控指标设计
指标 = 可观测 + 可告警。把下面四个埋点写进 ELK / Prometheus,就能量化“chatbot 到底行不行”。
响应延迟
t_first_byte = 收到用户句 → 首包返回毫秒数
阈值:P95 < 1500 ms;超过即触发扩容或降级到纯规则。意图识别准确率
acc = 正确意图数 / 总句数
随机抽 5% 会话做人工标注,目标 ≥ 85%。生成触发率
gen_rate = 生成模块调用 / 总句数
若 > 30%,说明规则覆盖不足,要补意图或模板。** fallback 成功率**
fallback_ok = 兜底回复后用户继续对话 / 兜底总次数
过低意味着兜底语太生硬,需优化文案。
4. 避坑指南:让线上环境不再“生成不了”
对话历史缓存策略
- 用 Redis List 存最近 10 轮
{"role":"user"/"bot", "text":"..."},设置 TTL=900 s。 - Rasa Tracker 默认是内存 + SQLite,重启即丢;生产环境请接 Postgres,并开启
tracker_store持久化。
- 用 Redis List 存最近 10 轮
冷启动默认响应
首次部署模型文件还没加载完,前端若立刻访问会 500。做法:- 启动时先
rasa train→rasa run --model model.tar.gz完全就绪后再注册到 Nginx upstream。 - 加一条
/health接口,返回{"status":"ok"},配合 K8s readinessProbe,防止流量提前打入。
- 启动时先
敏感词过滤正则示例
上面代码已给出SENSITIVE_PAT,再补充一条“数字+字母”变体干扰:
# 把“b@d w0rd”还原成“badword” def normalize(text: str) -> str: text = re.sub(r"[@0]", "a", text) text = re.sub(r"[1!]", "i", text) text = re.sub(r"[3]", "e", text) return text先归一化再跑正则,可提升 15% 拦截率。
5. 把规则与生成混到一起后,你还需思考的问题
“生成式”让回答更灵活,却带来不可控;“规则式”保证精准,却显得死板。如何拿捏两者的比例?
- 垂直客服场景:建议 80% 规则 + 20% 生成,先生存再谈体验。
- 闲聊陪伴场景:可调至 50% : 50%,甚至更高生成比例,但一定加“事实核查”层。
- 动态比例:根据用户满意度实时调整,当 fallback_ok 持续低于阈值,就自动收紧生成触发条件。
开放式问题留给你:如果业务既要“安全”又要“有趣”,你会把生成式与规则式对话的混合比例设在多少?欢迎留言交流。
6. 动手才是硬道理:从零打造可语音对话的 AI
写完这篇,我最大的感受是——别让“chatbot 生成不了”吓退你。把规则做硬、把异常兜住、再让生成模型锦上添花,你就能拥有一个既稳又会“聊天”的系统。如果你想体验更“实时”、更“像人”的语音交互,不妨试试官方提供的动手实验:从0打造个人豆包实时通话AI。里面有现成的 ASR→LLM→TTS 链路,还能在浏览器里直接麦克风对话,我这种小白也能半小时跑通。祝你玩得开心,早日让自己的 AI 开口说话!