
简介基于Python开发的Rasa中文聊天机器人项目整合源码、开发文档、项目代码解析与模型训练流程主要面向毕业设计、课程设计和实际项目开发场景。项目经过严格测试可在Rasa 1.9.5环境下运行包含NLU意图识别、实体提取、Core对话管理及交互式学习样本并对接图灵闲聊、心知天气API便于快速验证功能。压缩包共24个文件以7个Markdown开发指南、6个YAML配置、5个Python脚本为主辅以3个文本说明、2个Bash启动脚本和1个模型压缩包整体仅4.42MB结构清晰适合按文档逐步复现。目前已有249人学习下载。读者可借助分阶段更新日志与开发文档从NLU样本优化、同义词/正则/查找表、MITIE管道到身份查询案例逐层深入既能用于理解Rasa中文聊天机器人实现原理也能作为二次开发的基础模板。1. Rasa 中文聊天机器人从课程设计到能演示的完整路径把 Rasa 中文聊天机器人当毕业设计或课程设计题目是这几年很稳的选择它不像自建规则引擎那样没有技术含量也不像直接调大模型 API 那样没法写论文。Rasa 是开源对话管理框架自带 NLU 意图识别、实体抽取、对话策略和自定义动作四层结构Python 编写源码、开发文档、模型训练三个环节都能成为答辩亮点。你可以用它做一个查天气、查课表、查图书的小助手最后rasa shell一跑效果立刻看得见。刚拿到这个题目的人通常卡住的地方不是写代码而是没搞懂config.yml、domain.yml、data目录这三者之间的关系导致训练出来的模型行为跟脑子里想的不一致翻车了也不知道看哪。这篇笔记按「环境 → 源码 → 训练 → 排错 → 验证」的顺序把一条能复现、能演示、能写进报告的中文 Rasa 落地方案拆开讲新手能跟着跑通熟手可以直接跳去抄参数和避坑清单。2. 从零跑通中文 RasaPython 环境、三件套配置与第一次模型训练2.1 版本选择为什么我锁 Python 3.9 Rasa 3.5做 Rasa 中文项目版本组合是第一道坎。Rasa 3.x 是当前用得最多的稳定大版本它对 Python 的要求是 3.7 到 3.9 之间。Python 3.10 以后部分依赖包在 Windows 上编译容易报错Python 3.7 官方停止维护所以 3.9 是最省事的选择。如果你新装 Python直接从官网下载 3.9 系列安装包勾选 Add to PATH这一步不用纠结。我一般用 conda 单独建一个虚拟环境避免把系统 Python 环境搞乱conda create -n rasa python3.9 -y conda activate rasa pip install --upgrade pip pip install rasa[zh]3.5.2这里rasa[zh]不是某个魔改版本而是安装 Rasa 的同时带上中文分词依赖Jieba、pkuseg 等省得之后再补装。如果你的机器已经装了别的版本或者网络下载一半断了先pip uninstall rasa再重装残留的旧版本会让你在训练时报一些莫名其妙的错。装完之后用python -m rasa --version确认版本号下面所有命令都以 Rasa 3.5 为准。2.2 三件套第一件config.yml 里决定中文效果的 pipelineRasa 项目的最小骨架只需要三个文件config.yml、domain.yml、data/nlu.yml。config.yml是最先要写对的因为它定义了用什么模型做中文意图识别和实体抽取。一个能跑通中文的最简配置如下recipe: default.v1 language: zh pipeline: - name: JiebaTokenizer - name: LanguageModelFeaturizer model_name: bert-base-chinese cache_dir: /path/to/model_cache - name: DIETClassifier epochs: 100 learning_rate: 0.001 policies: - name: MemoizationPolicy epochs: 1 - name: RulePolicy - name: TEDPolicy epochs: 200JiebaTokenizer负责把中文句子切词LanguageModelFeaturizer用中文 BERT 预训练模型把词语转成向量这一步决定了机器人对中文语义的理解上限DIETClassifier是联合完成意图分类和实体识别的核心组件。policies里的MemoizationPolicy负责精确命中训练故事RulePolicy处理固定规则TEDPolicy负责没见过的对话路径泛化。提示cache_dir不是必须项但建议显式写出来后面下载模型的坑至少少一半。2.3 三件套的另外两件domain 声明能力nlu 给训练素材domain.yml声明机器人能理解什么、能说什么、能记住什么。最小版长这样version: 3.1 intents: - greet - ask_weather entities: - city slots: city: type: text mappings: - type: from_entity entity: city responses: utter_greet: - text: 你好有什么可以帮你 utter_ask_city: - text: 你想查哪个城市的天气intents列出所有意图entities声明要抽取的实体slots是机器人记忆上下文的地方responses是回复话术。注意utter_greet这类回复名必须带utter_前缀这是 Rasa 的硬性规定写错训练不会报错但对话时就是找不到回复。data/nlu.yml给 NLU 模型提供中文训练样本实体标注用[北京](city)这种语法version: 3.1 nlu: - intent: greet examples: | - 你好 - 嗨 - 早上好 - 在吗 - intent: ask_weather examples: | - [北京](city)天气怎么样 - 今天[上海](city)有雨吗 - 帮我看看[广州](city)明天会不会降温每个意图建议至少给 10 条以上样本并且句子首尾和用词要尽量散开不要全部写成同一种句式。实体标注只标最小语义单位比如「北京市」标成「北京」否则槽位回填会把「市」也带进去后面查天气接口就会带错参数。2.4 跑起第一次训练rasa train 与模型产物检查目录结构搭好之后环境激活状态下直接跑cd rasa_project python -m rasa train第一次训练会比预想中慢因为要下载 BERT 中文模型权重几百 MB 的东西取决于你的网络情况。训练过程中会看到类似Processed messages: 20、DIET: epochs done的日志最后出现Your model is trained and saved to models/20250101-123456.tar.gz就成功了。模型压缩包里有完整的 NLU 和对话策略之后启动对话直接加载它。models/目录下的 tar.gz 包是按时间戳命名的这是 Rasa 给你留的后悔药旧模型不会自动删。训练完想要看训练数据里的故事覆盖情况可以在data/stories.yml里补齐流程后再次rasa trainRasa 会把新旧数据合并训练。2.5 用 rasa shell 手动聊天第一次验收效果模型训练完成后启动交互式对话验证行为python -m rasa shell --model models/*.tar.gz输入「你好」机器人应该回 greeting 话术输入「北京天气怎么样」如果配置正确它会接着问「你想查哪个城市的天气」因为ask_weather这个意图还需要city槽位而你还没写填槽逻辑所以它只能先追问。这一步能跑通说明三件套和模型训练链路已经通了。如果这里出现识别成别的意图、中文乱码、回复空白先不要急着改训练数据大概率是 2.2 的 pipeline 里LanguageModelFeaturizer配置有问题或者终端编码不对具体情况看第 5 章。3. 源码结构拆解可交付 Rasa 项目的目录、动作与故事3.1 标准目录树每个目录负责什么一个正经可交付的 Rasa 项目不建议把所有文件堆在根目录。用rasa init --no-prompt生成的骨架再手动改造目录结构最终长这样rasa_project/ ├── actions/ │ ├── __init__.py │ └── actions.py ├── data/ │ ├── nlu.yml │ ├── stories.yml │ └── rules.yml ├── tests/ │ └── test_stories.yml ├── docs/ │ ├── 需求说明.md │ ├── 安装部署.md │ └── 模型训练说明.md ├── config.yml ├── domain.yml ├── credentials.yml ├── endpoints.yml └── models/ └── 20250101-123456.tar.gzactions目录放自定义动作的 Python 源码data放训练数据tests放回归测试故事docs放开发文档models放训练产物。这个结构本身就能当成软件开发能力来答辩数据、代码、文档、产物四层分离。唯一要提醒的是credentials.yml和endpoints.yml是接外部渠道和服务用的不上线聊天渠道的项目里可以保持注释状态不影响训练。3.2 actions/actions.py写一个真正干活的 Python 动作模板自带的actions.py只有两个空壳类实际项目里第一个要写的动作通常是查天气。代码如下逻辑不复杂但它是让你从「玩具对话」变成「能接业务系统」的关键一步from typing import Any, Text, Dict, List from rasa_sdk import Action, Tracker from rasa_sdk.executor import CollectingDispatcher class ActionWeather(Action): def name(self) - Text: return action_weather def run( self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict[Text, Any], ) - List[Dict[Text, Any]]: city tracker.get_slot(city) # 实际项目里这里换成天气 API 或数据库查询 weather 晴25 度 if city else 未知 dispatcher.utter_message(textf{city}今天{weather}) return []name()返回值是动作的唯一标识必须和domain.yml里actions列表中的名字一致。run()的三个参数里tracker是读取上下文的关键tracker.get_slot(city)拿之前填的槽位值dispatcher.utter_message()把回复内容发给用户。如果一个动作要做多个步骤比如先查库再拼接话术就在run()里多写几行最后统一utter_message一次。对应的domain.yml需要在actions下注册这个动作actions: - action_weather不注册的话训练不报错但对话一走到 story 里引用这个动作的步骤Rasa 会直接跳过它对话流程就断了这是新手最容易踩的坑。3.3 stories.yml 与 rules.yml对话流程怎么编排stories.yml描述「用户说了什么 → 机器人该做什么」的多轮路径是 Rasa 从训练样本里学习对话策略的依据。查天气的完整故事如下version: 3.1 stories: - story: 查天气完整流程 steps: - intent: ask_weather - action: utter_ask_city - intent: inform entities: - city: 北京 - action: action_weather这里有一个容易被忽略的点用户第二次回答「北京」时inform意图并不在domain.yml里声明过需要补上否则故事训练时 Rasa 找不到inform这个 intent 的定义整个故事会被丢弃。Rasa 对「训练数据里的意图/实体/槽位必须在 domain 里有声明」这件事查得很严日志里看到Failed to validate story就说明有引用未声明的名字。rules.yml和 stories 的区别是rules 是无条件或按固定状态触发不参与对话策略学习。典型的用法是处理 fallback 和打招呼version: 3.1 rules: - rule: 用户打招呼 steps: - intent: greet - action: utter_greet - rule: 助手不理解时 steps: - intent: nlu_fallback - action: utter_default规则文件里出现的nlu_fallback是内置意图不需要自己在 domain 里声明但utter_default要写在responses里。3.4 槽位与表格让机器人记住上下文槽位是中文聊天机器人能不能做多轮对话的关键。上面的ask_weather场景用户第一次只说「查天气」没有城市名槽位city是空的机器人应该追问而不是直接查。Rasa 的FormAction可以简化这个流程在domain.yml里定义一个简单表单forms: weather_form: required_slots: - city slots: city: type: text mappings: - type: from_entity entity: city conditions: - active_loop: weather_form配合 story对话进入weather_form后Rasa 会检查required_slots发现city为空就自动触发追问槽位的话术用户再说「北京」时实体抽取把槽位填上表单才结束。这套机制能让你少写大量 if-else代码量小的项目基本不需要手动管理槽位流。3.5 开发文档怎么组织给评审看的不是 README 流水账毕业设计和课程设计最吃亏的地方是代码写好了但文档不像开发文档。Rasa 项目交付时建议按表格里的结构组织docs目录每个文件控制在 3 到 5 页文档必写内容常见误区需求说明用户角色、意图清单、对话流程示例写产品故事不写需求条目安装部署Python 版本、pip 命令、模型下载漏写国内镜像配置源码结构目录树、action 清单、设计理由贴全部代码不做说明模型训练pipeline 选择、参数表、评估结果只写命令不写结果测试用例故事路径、预期行为、实测结果没有预期直接截图开发文档不需要写得多华丽重点是让答辩老师能按文档把环境搭起来、把模型重新训练一遍。你自己一个月后回来看也能少花两小时回忆当时怎么配置的。4. 中文模型训练意图识别、实体抽取与参数调优4.1 中文 NLU 与英文项目的三个本质差别Rasa 的官方示例大多是英文直接套到中文上会出问题差别集中在三处。第一是分词中文句子没有空格边界JiebaTokenizer是 pipeline 里处理这个问题的最常用组件它对「北京天气怎么样」会切成「北京 / 天气 / 怎么样」。第二是预训练模型LanguageModelFeaturizer里model_name默认是bert-base-uncased那是英文模型中文项目必须换成bert-base-chinese如果你的项目领域比较专比如医疗、法律可以考虑换roberta中文预训练模型语义特征会比通用 BERT 更好。第三是样本量策略。中文意图识别在小样本下很容易过拟合一段 1000 字的训练文本就能让模型记住字面而不是语义。所以中文 Rasa 项目的训练数据贵在「句式分散」同一个意图不要连续写 10 条结构相似的句子交错着写疑问句、陈述句、带语气词的句子这比单纯增加条数更管用。4.2 训练数据标注实体边界和语义多样性是生命线标注是模型训练里最玄学也最关键的一环。实体标注的错误很容易犯把「北京大学」整体标成city把「北京今天下雨吗」里的「今天」标进实体这些都会被模型当成规律学到。标注规则建议统一成「最小完整实体」城市名绝不带「市」字时间实体只标「明天」不标「明天天气」。语义多样性比条数更重要。同样是查天气一个意图里至少要包含这些变化- intent: ask_weather examples: | - [北京](city)天气怎么样 - 今天[上海](city)有雨吗 - [广州](city)明天多少度 - 帮我查一下[成都](city)的空气质量 - 后天[武汉](city)会降温吗每条样本至少引入一个变化维度加疑问词、换地点、换时间词、换语气。如果只写「北京天气怎么样、上海天气怎么样、广州天气怎么样」这种同一模板复制出来的样本模型训练完只会认「XX 天气怎么样」别人换一种说法就一脸蒙。这是我在多个项目里踩过的坑模板化数据是中文 Rasa 意图识别不准的第一原因。4.3 pipeline 与训练超参epochs 不是越大越好不少教程默认配置里DIETClassifier的epochs是 300对中文小数据集来说这是灾难。300 个 epoch 在几百条样本的项目里要跑很久而且大概率过拟合。我一般在小项目里这样设置- name: DIETClassifier epochs: 100 learning_rate: 0.001 batch_size: 32learning_rate从 0.001 开始训练报 NaN 的话先把它降到 0.0005 重试。batch_size在数据量小于 1000 条时不用动默认值就行。实体抽取对参数更敏感如果rasa test nlu里实体 F1 明显低于意图准确率优先回去检查标注边界而不是调参。注意训练报 NaN 通常不是参数问题而是训练数据里出现了空行、特殊字符或未转义的括号。先把数据文件用 YAML 校验工具跑一遍再改学习率顺序不要反。4.4 rasa test 评估用数字判断模型能不能用手工rasa shell聊天只能定性判断答辩要有数据支撑得跑评估python -m rasa test nlu --nlu data/nlu.yml --config config.yml这个命令会交叉验证 NLU 模型输出意图分类的准确率、精确率、召回率和 F1 分数同时生成一个混淆矩阵图片存放在results/目录下。中文小项目里意图准确率达到 0.85 以上算合格0.95 以上算优秀实体抽取 F1 到 0.9 已经不错。混淆矩阵里那些「其他意图被识别成 ask_weather」的格子直接告诉你要往哪个意图补样本。对话策略的评估另外用故事测试python -m rasa test --stories tests/test_stories.yml它按你预设的故事路径跑一遍完整对话输出有多少条路径成功走完。这个数字是毕业设计里很有说服力的指标比十几张截图管用。4.5 模型产物管理旧模型别急着删models/目录下的 tar.gz 包按时间命名每次rasa train都会新增一个不会覆盖旧文件。这里我养成了一个习惯每次调参训练后先不删旧模型跑一遍rasa test对比新旧模型的意图准确率确认新模型没有变差再把旧包移走。因为中文数据小改版偶尔会让模型整体变笨有旧的 tar.gz 在随时能回到上一版。如果项目多人协作建议把模型文件名改成model-20250101-cityweather.tar.gz这种含意图描述的名字比纯时间戳好认。5. 常见问题排查中文乱码、训练慢、对话乱跳的避坑清单5.1 LanguageModelFeaturizer 下载模型失败训练卡在下载阶段现象执行rasa train后长时间停在类似Downloading...的日志或者直接报ConnectionError退出翻日志能看到 HuggingFace 模型权重相关的报错。原因LanguageModelFeaturizer在第一次训练时要拉取 BERT 中文模型权重网络无法直连模型托管站时就会卡死。解决先把模型权重单独拉下来再让 Rasa 用本地缓存。常见做法是配置环境变量让模型工具走国内镜像站export HF_ENDPOINThttps://hf-mirror.com之后重新rasa train。如果你的网络环境还是拉不动就把 pipeline 里的LanguageModelFeaturizer先注释掉用MitieFeaturizer或纯词典特征先跑通流程之后再补预训练模型代码逻辑不用改。这个问题不影响模型质量只影响能不能跑起来是最多人在开局阶段翻车的地方。5.2 中文乱码、识别不出任何意图现象rasa shell里输入「你好」机器人回一句英文报错或直接没反应终端里中文显示成\u4f60\u597d或。原因两个层面。一是文件编码不对Windows 下用记事本编辑 YAML 文件默认保存成 GBKRasa 强制要求 UTF-8二是终端自身编码不匹配Windows 命令行默认代码页经常不是 UTF-8。解决所有.yml和.py文件统一用 UTF-8 保存Windows 下推荐在 VS Code 里确认右下角编码显示的是 UTF-8。终端先执行chcp 65001切到 UTF-8 代码页再跑rasa shell。已经乱掉的模型不要硬找补改完编码后重新rasa train一次几分钟的事。5.3 对话进行中突然 fallback明明写了故事却不走现象用户的句子进了ask_weather故事里下一步是追问城市但机器人直接回utter_default说听不懂。原因RulePolicy的 fallback 机制被触发说明模型对当前输入的置信度低于阈值或者故事路径里有一步没匹配上。常见原因是训练故事和实际对话之间多了一个你没预料的意图。解决先用rasa shell --debug跑一遍观察日志里每个意图的置信度和选中策略然后把 fallback 阈值调低一点policy: - name: RulePolicy core_fallback_threshold: 0.3同时检查 story 里intent: inform后带的实体是否在 domain 里声明了。这个坑花了我一个晚上排查最后发现只是 story 里写了个没注册的实体Rasa 静默跳过整个故事。5.4 模型训练时间过长CPU 上 BERT 跑不动现象训练持续几个小时DIETClassifier的进度条半天不往前走一步。原因LanguageModelFeaturizer加 BERT 中文模型是小算力机器的重负担再加上epochs默认 300两者叠加就是灾难。解决两个方向并行。把DIETClassifier的epochs降到 80 到 100小数据集 100 个 epoch 完全足够训练时确认是否真用上了 GPUpip list | grep cuda或看日志里有没有use_gpu: True。没有 GPU 的机器可以租用云厂商的免费算力额度跑训练产出的 tar.gz 模型包拷回本地models/目录直接rasa shell加载训练和推理环境不需要一致。这个做法在课程设计里特别实用演示时用本地小机器训练放到云端。5.5 实体抽取出边界错误「北京市」识别成「北京」现象rasa test nlu里实体 F1 不高rasa shell里查「北京天气怎么样」槽位city的值变成「北京市」或者「北京天」。原因分词和标注不一致。JiebaTokenizer默认把「北京市」切成「北京 / 市」但你训练样本里标注的是「北京」模型学到的边界就乱了。解决先检查训练数据里的实体标注是否统一[北京市](city)这类带后缀的标注建议全部改成[北京](city)。如果项目里大量出现专业地名给 Jieba 配自定义词典- name: JiebaTokenizer dict_path: resources/jieba_custom_dict.txt词典文件每行一个词比如「北京市」占一行分词时 Jieba 会优先按整词切。改完词典需要重训实体边界问题八成能缓解。这个问题属于「数据标注压过模型参数」的典型调DIETClassifier的参数学什么的是没用的。5.6 重新训练后行为漂移模型越训越笨现象给某个意图加了五条样本重新训练完原来识别对的句子现在识别成别的意图整体准确率反而下降。原因Rasa 训练过程有随机性数据量小的时候尤其明显另外新增样本和已有样本句式冲突模型学到了旧数据里不存在的模式。解决在config.yml里固定随机种子训练前设置环境变量或直接在配置里加random_seed: 42保证两次训练初始条件一致。数据层面新增样本后用rasa test nlu对比前后两版模型的准确率如果变差就回滚到上一版模型包然后检查新样本里有没有和旧样本语义重叠但标签不同的写法。模型培训是玄学也是工程固定种子、小步增量、对比评估这三步能保住底裤。6. 验证与进阶几招让中文机器人从「能跑」到「可靠」6.1 用测试故事代替手工点对话手工rasa shell聊几十轮既费时间又没法回归验证。Rasa 支持把测试路径写进tests/test_stories.yml格式和 stories 一样但专门用于验证。写法示例version: 3.1 stories: - story: 查天气主流程 steps: - user: 北京天气怎么样 intent: ask_weather - action: utter_ask_city - user: 北京 intent: inform - action: action_weather跑python -m rasa test --stories tests/test_stories.yml --e2eRasa 会模拟真实用户输入走完流程输出通过率。我现在的习惯是每改一次训练数据或 action先跑这个命令再考虑上线演示两分钟的事能挡掉大多数低级翻车。6.2 用置信度分布判断模型自信程度rasa shell --debug会打印每个意图的置信度分布这是调优时最有价值的输出。如果发现「你好」和「在吗」两个意图的置信度都徘徊在 0.5 左右说明训练数据里这两个意图的句子太像需要人为拉开差异比如给 greet 加更多带语气词的口语样本给另一个意图加更多疑问句。模型自信度提上来之后对话里的 fallback 次数会明显下降。这个技巧不需要改任何代码只动训练数据却是让机器人显得「聪明」的捷径。我第一个中文 Rasa 项目就是栽在只做手工验证上答辩前一晚跑rasa test才发现三条主流程里两条因为实体没注册直接跳过。后来不管项目多小我都会把测试故事写在前头训练完立刻跑回归。这半年再做中文聊天机器人模型质量稳定多了你按这套流程走坑大概率能躲过去希望帮到你。本文还有配套的精品资源点击获取