
简介面向毕业设计、课程设计与项目开发场景提供一套基于 Python 的 Rasa 中文聊天机器人完整方案包含可运行源码、开发文档、代码解析与模型训练成果。整个压缩包共 24 个文件、4.42MB主要类型包括 Markdown 文档、YAML 配置、Python 脚本、bash 启动脚本以及训练好的模型文件结构清晰便于按文档顺序完成意图识别、实体提取、对话管理和 API 对接等环节的学习与实践。项目更新日志展示了完整迭代过程从初始模型训练成功到优化 NLU 样本、引入同义词/正则/查找表再到使用 Interactive Learning 构建样本、新增 MITIE 管道及身份查询案例并将 Rasa 升级至 1.9.5 解决 Win10 下 TensorFlow 异常不仅有代码还有对应开发指南与排错思路。目前已有 249 人学习下载可直接在本地启动测试并可在此基础上继续扩展图灵闲聊、心知天气查询等功能。1. 毕设选题撞上“Rasa中文聊天机器人”这是最不容易翻车的方向做毕业设计或者课程设计只要选了“智能问答”“对话系统”这类方向绕不开的就是“Rasa中文聊天机器人”。Rasa 是当前开源对话框架里文档最完整、组件可替换性最好的一档它把聊天机器人拆成两个清晰层次自然语言理解NLU负责“听懂人话”对话管理Core负责“决定怎么接话”。这套架构的妙处是你既能在答辩时讲清楚原理又能实打实跑通一个可交互的中文对话系统还能把项目代码解析、模型训练、开发文档整理成一套完整的交付物。这篇文章按我自己做项目的顺序从 Python 环境讲起一路到模型训练、坑位排查和答辩验证全程可复现。2. 先把环境和源码结构跑通从 Python 安装到 rasa init2.1 Python 环境怎么选3.8 这条线最稳Rasa 对 Python 版本比较挑剔。我经手过的项目里Python 3.8 是兼容性最稳的版本3.9 勉强可用3.10 以上容易遇到部分依赖包编译失败。做这个项目第一步不是急着写代码而是把 Python 版本和环境变量配置好否则后面装依赖时会成片报错。python -m venv rasa_venv # Windows 下激活 rasa_venv\Scripts\activate # Linux / macOS 下激活 source rasa_venv/bin/activate pip install rasa --default-timeout60 -i https://pypi.tuna.tsinghua.edu.cn/simplepython -m venv rasa_venv创建一个独立的虚拟环境这一步不能省。装依赖时如果直接用默认源Rasa 依赖的包数量大、体积大很容易超时中断--default-timeout60把超时时间拉长到 60 秒-i指定国内镜像源这两个参数同时用能省掉大半网络玄学问题。装完后用rasa --version确认版本看到 3.x 就说明环境没有问题了。2.2 用 rasa init 把官方脚手架拉下来环境就绪后我最常用的做法是先不写任何代码直接让 Rasa 自己生成一套可运行的项目骨架这是理解“源码”最快的一条路。cd my_chatbot_project rasa init --no-prompt--no-prompt表示跳过交互式问答全部选默认项。生成完后项目目录长这样my_chatbot_project/ ├── actions/ │ ├── __init__.py │ └── actions.py ├── config.yml ├── credentials.yml ├── data/ │ ├── nlu.yml │ ├── rules.yml │ └── stories.yml ├── domain.yml ├── endpoints.yml ├── models/ │ └── ... └── tests/ └── test_stories.yml这个目录就是整套“源码开发文档项目代码解析”的入口。config.yml是训练管线的总配置决定用什么算法做分词、意图识别、实体提取domain.yml是“角色表”注册意图、实体、槽位、回复文案data/下三个文件分别存放训练语料和对话流actions/actions.py是自定义动作的入口。把这几个文件读懂整个项目就拆完了一大半。2.3 源码里真正要动的是哪几个文件拿到骨架后按优先级去改动而不是每个文件都翻一遍。我一般按下面这个顺序文件作用必改程度config.yml设定 NLU 和 Core 的算法组件必改中文必须换分词器data/nlu.yml标注意图和实体样本必改换成你自己的语料domain.yml注册意图、实体、槽位和回复必改新增内容都要同步到这里data/stories.yml写多轮对话的“剧本”必改这是对话逻辑的核心data/rules.yml写不依赖上下文的固定规则按需兜底回复用actions/actions.py自定义动作调用外部接口或查数据按需做毕业设计通常要写credentials.yml/endpoints.yml配置渠道和外部服务按需本地跑可以不动改动任何语料之后都必须重新训练模型才会生效。后面所有训练和排错都是围绕这几个文件的修改展开的。3. 中文意图与实体把 NLU 部分做成能答辩的样子3.1 中文分词器Jieba 与默认 Whitespace 的差别Rasa 默认的WhitespaceTokenizer按空格分词对英文很自然对中文就是灾难。中文句子没有天然空格必须换用JiebaTokenizer。这是中文聊天机器人和英文项目在配置层面最核心的差异。language: zh pipeline: - name: JiebaTokenizer dictionary_path: data/dict/jieba_dict.txt - name: LanguageModelFeaturizer model_name: bert-base-chinese - name: DIETClassifier epochs: 100 learning_rate: 0.001 - name: EntitySynonymMapperdictionary_path指向自定义词典文件词典格式是“词语 频数”每行一个词。加自定义词典的目的是让 Jieba 正确切分你业务里的专有名词比如学校名、课程名、人名。LanguageModelFeaturizer用 BERT 系列中文预训练模型做句子向量化你也能替换成规模更大的 RoBERTa 中文预训练模型但推理速度会明显变慢毕设本地跑的话bert-base-chinese是性价比最好的选择。DIETClassifier是意图分类和实体提取的联合模型epochs和learning_rate决定训练收敛情况数据量少于几百条时不要把epochs拉太高。分词器对中文效果自定义词典适用场景WhitespaceTokenizer整句被当成一个词意图识别基本失效不支持英文项目JiebaTokenizer按中文语义切分配合自定义词典可调优支持中文项目首选3.2 意图、实体与同义词一份能进答辩的 nlu.ymldata/nlu.yml是整个项目里最值得反复打磨的文件。答辩时老师最常问的问题就是“你的训练数据怎么来的、怎么标注的”所以这里要做出规范感。version: 3.1 nlu: - intent: query_weather examples: | - 今天[北京](city)天气怎么样 - [上海](city)明天会下雨吗 - 帮我查一下[广州](city)的天气 - 我想知道[深圳](city)气温多少度 - intent: query_course examples: | - [数据结构](course)这门课什么时候上 - [操作系统](course)的考试范围是什么 - 我们[人工智能](course)课的作业在哪交意图命名用query_前缀区分“查天气”和“查课程”这两类动作方括号里是实体文本圆括号里是实体类型。每条样本都要覆盖一种真实表达方式用词不能太相似否则模型会偷懒只认其中一两个关键词。每个意图至少给 15 到 30 条不重复的中文表达样本越接近真人说话的口吻训练出来的模型越准。同义词在处理中文别称时非常有用。比如用户说“首都”指的就是“北京”在nlu.yml里加一段synonym映射就能让模型把“首都”和“北京”归到同一个实体值上这也是中文场景里最常用的技巧之一。- synonym: 北京 examples: | - 首都 - 帝都3.3 实体提取的边界“北京天气”能过“北京今天的天气”开始翻车中文实体提取最容易出问题的地方不是意图而是实体的边界切分。Jieba 在“北京天气”这种短语上表现很好但遇到“北京今天的天气怎么样”这种带修饰词的句子实体边界就开始抖动。- intent: query_weather examples: | - 北京[今天](date)天气怎么样 - 帮我查下[明天](date)上海的天气这里把“今天”“明天”也标注成date实体模型才能学会区分“查询时间”和“查询城市”。如果漏标这类实体DIETClassifier会把“今天”和“北京”粘连在一起导致实体识别结果变成一串整词。除了在样本里标注还可以用lookup列表补充常见实体名单——但要注意lookup只影响实体提取的候选范围不会自动把词加入分词词典两者需要配合使用。4. 对话流程编排stories、rules 与自定义 action 的配合4.1 写故事先写“经历”stories 的作用NLU 解决“听懂话”Core 解决“怎么接话”。data/stories.yml里记录的是完整的对话经历从用户第一句话开始到机器人回复结束每一步都写清楚。Rasa 用这些故事训练对话策略模型学会在不同情境下该选择哪个动作。version: 3.1 stories: - story: 查询天气完整流程 steps: - intent: query_weather entities: - city: 北京 slot_was_set: - city: 北京 - action: action_query_weather - slot_was_set: - city: 北京 - action: utter_weather_resultintent和entities描述用户这句话表达了什么slot_was_set记录槽位的变化槽位相当于对话里的临时变量存住用户说过的城市名action是机器人执行的下一步动作。注意action_query_weather这个动作前缀带了action_意味着它是自定义动作需要在actions.py里写代码反过来utter_weather_result是纯文本回复动作直接在domain.yml的responses里写文案就行。4.2 domain.yml 把“角色表”注册好domain.yml是整个项目的注册中心。意图、实体、槽位、回复文案全都必须在这里声明漏掉任何一个训练时都会报错。version: 3.1 intents: - query_weather - query_course entities: - city - course - date slots: city: type: text influence_conversation: true mappings: - type: from_entity entity: city responses: utter_weather_result: - text: 好的{city}今天的天气是晴天气温 22 到 28 度。 utter_default: - text: 抱歉这个问题我还没有学会换个说法试试slots里type: text表示槽位存的是普通文本influence_conversation: true表示槽位值会参与对话决策这个参数在同一个意图对应不同城市、不同课程时非常关键如果不设成true模型不会根据槽位内容区分后续回复。mappings指明槽位从哪里取值from_entity的意思是当识别到city实体时自动把这个实体值存入city槽位。responses里的文案支持模板语法{city}会被槽位值替换成具体城市名——这是做毕业设计时显得项目很“完整”的一个细节。4.3 非要写代码时自定义 action 怎么落地纯文本回复撑不起一个聊天机器人的门面。查天气要调接口、查课程要去数据库这些都得靠自定义 action 写代码。actions/actions.py是 Rasa 项目里唯一真正写 Python 业务逻辑的地方。from typing import Any, Dict, List, Text from rasa_sdk import Action, Tracker from rasa_sdk.executor import CollectingDispatcher class ActionQueryWeather(Action): def name(self) - Text: return action_query_weather def run( self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict[Text, Any], ) - List[Dict[Text, Any]]: city tracker.get_slot(city) # 这里写真实的天气接口调用逻辑 weather_info f{city}今天晴22~28 度 dispatcher.utter_message(textweather_info) return []name()方法返回的动作名必须和stories.yml里写的action_query_weather完全一致不区分大小写但拼写不能错。run()方法里tracker.get_slot(city)取出用户之前说过的城市名dispatcher.utter_message(text...)把结果发给用户。返回一个空列表代表动作执行完毕。完成后还要在endpoints.yml里确认 action 服务地址本地运行默认是http://localhost:5055用rasa run actions启动。5. 模型训练与避坑排查从 rasa train 到 rasa test5.1 训练命令与模型产出所有语料和配置改完就可以训练模型了。训练是整个流程里最吃耐心的一环也是“模型训练”相关搜索里问题最多的地方。rasa train rasa train nlu --fixed-model-name my_nlu_model rasa train core --fixed-model-name my_core_modelrasa train同时训练 NLU 和 Core 两套模型适合每次完整改动后执行如果想只调意图和实体rasa train nlu更快不用重新跑对话策略。--fixed-model-name给模型指定固定名字避免每次训练生成带时间戳的新文件写自动化测试脚本时非常有用。训练完成后模型打包成 tar.gz 文件放在models/目录下里面同时包含 NLU 模型和 Core 模型后续rasa shell交互时直接加载这个包。训练日志里如果出现 loss 为nan通常不是命中的玄学问题而是学习率偏高或者某个意图的样本数量太少先调低learning_rate再补语料。5.2 先用 rasa test 测一轮再谈调优训完不能直接rasa shell就完事应该先用rasa test跑一遍自动评测用指标说话。rasa test --nlu --model models/my_nlu_model.tar.gz--nlu表示只评测 NLU 部分。Rasa 会拿训练数据里留出的测试集做交叉验证输出结果在results/目录下。重点看intent_report.json里的precision、recall、f1-score以及intent_confusion_matrix.png混淆矩阵图。如果某个意图的召回率明显低于其他意图说明这个意图的样本表达太单一回去补几条不同说法的句子再重新训练。数据总量不到几百条时F1 值在 0.8 左右已经算健康不必盲目追求 0.95 以上——那是大厂用几万条标注数据才能堆出来的数字。5.3 避坑Rasa 中文项目最常见的五个坑以下是做 Rasa 中文项目最高频的五个踩坑位置每一条都是“现象 → 原因 → 解决”的真实路径。坑一rasa init后启动就报错提示缺依赖或版本冲突。现象是安装过程顺利但运行时报module not found或protobuf相关错误。原因是 Python 版本过高部分 Rasa 依赖编译不通过。解决方法是把 Python 降到 3.8重新创建虚拟环境安装不要尝试逐个手修依赖版本。坑二中文句子被当成一个整词意图识别完全失效。现象是用户说什么都命中同一个意图训练报告里特征很稀。原因是config.yml里没换分词器还在用默认的WhitespaceTokenizer。解决方法是把 pipeline 换成JiebaTokenizer并确认language: zh已设置。坑三训练过程 loss 变成nan模型无法收敛。现象是训练到某一轮后损失值直接变nan之后模型完全不能用。原因是学习率偏高或者某个意图/实体的样本量极少模型在稀疏数据上梯度爆炸。解决方法是把DIETClassifier的learning_rate调到0.0005一档再给每个意图补充至少 15 条样本两者同时做才最有效。坑四实体提取结果总是一长串或者丢掉后半部分。现象是“北京今天的天气”被整体识别成一个city实体或只提取出“北京”。原因是语料里没有标注date这类伴随实体模型无法区分边界。解决方法是在nlu.yml里把“今天”“明天”等词也标注为对应实体用lookup列表补充候选词。坑五对话总是落到utter_default不走预设的 story。现象是测试对话时用户问什么都被默认回复兜底预设的查询流程一次都没触发。原因是stories.yml里的样本与用户实际表达差距大或者 NLU 置信度阈值太高。解决方法是先看rasa shell --debug输出的意图识别置信度如果置信度普遍在 0.5 以下就把config.yml里的intent阈值调低或者补语料同时确认rules.yml里没有冲突的规则抢占路由。6. 把项目从“能跑”推到“能答辩”自测与验收清单做到这里项目已经是一个能交互的中文聊天机器人了。但毕业设计和课程设计通关的最后一公里是验证方法是否专业。我会在交稿前跑一遍完整的自测清单每一栏都有明确的验收物验证项操作通过标准NLU 意图评测rasa test --nlu每个意图 F1 0.8实体抽取抽查rasa shell手动输入 20 条变体表达城市、日期实体无边界错误多轮对话回放rasa test core --stories tests/test_stories.yml故事完成率 100%兜底回复故意输入无关内容落到utter_default且不报错模型体积查看models/下 tar.gz 文件控制在 200MB 内便于拷贝演示rasa test core --stories这条命令是多数人忽略的它能把tests/test_stories.yml里写好的对话剧本逐条回放检验对话流程是否稳定是答辩时最有说服力的“自动化测试证据”。如果测试故事通过率不是 100%回到 4.1 节的 stories 写法重新核对slot_was_set和action的顺序。我经手的每个 Rasa 中文项目上线前都会强迫自己用真人语气多聊二十轮而不是只测预设样本。曾经有一次我以为模型训练一切正常结果用户说“北京呢”三个字系统完全没接住——因为语料里全是完整句子没有人说过省略句。后来我把这类省略表达补进nlu.yml再训练后效果立刻不一样。做对话系统的项目多花时间在“收集真实表达”上永远比调参数划算。希望帮到你。本文还有配套的精品资源点击获取