ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Flask+Rasa中文任务型对话机器人实战:从源码部署到业务改造

Flask+Rasa中文任务型对话机器人实战:从源码部署到业务改造 简介这份资源是面向Python初学者与对话系统爱好者的中文任务型对话机器人完整项目基于Flask与Rasa框架搭建可直接替换数据使用适合想快速上手任务型对话开发、完成课程设计或毕业设计的读者。压缩包共109个文件约7.51MB涵盖7个py源码文件、6个yml配置文件、13个json数据文件、5个md与5个pdf部署文档以及css、html、js等前端界面资源另含pkl模型、dat实体提取数据与dot对话流程图结构完整。项目已积累117人学习下载配套部署文档详细说明环境搭建与运行流程读者可据此理解Rasa意图识别、实体抽取与Flask接口联调的整体链路掌握对话管理、前端交互与模型训练的关键环节并借助流程图与配置文件快速定位模块职责为二次开发或定制中文对话机器人提供可复用的工程模板。1. 从一份 FlaskRasa 源码包说起中文任务型对话机器人到底怎么跑起来拿到「基于 Flaskrasa 的中文任务型对话机器人源码部署文档全部数据资料.zip」这类项目包多数人的第一反应是解压、找 README、pip install -r requirements.txt然后卡在 Rasa 版本和 Python 版本对不上。这不是你操作有问题而是任务型对话系统本身就横跨两个技术栈Rasa 负责意图识别、槽位填充、对话策略Flask 负责把训练好的模型包装成 HTTP 接口再对接前端或第三方渠道。中文场景又多一层分词、同义词和语料标注的坑。这篇笔记按「先跑通最小闭环再拆解每个模块最后处理部署和踩坑」的顺序展开适合手里已经有一份类似源码包、想把它真正跑起来并改造成自己业务的工程师。读完你能判断这套方案值不值得投入、哪些参数必须调、哪些地方最容易翻车。2. 拆开压缩包先看什么目录结构与运行链路2.1 一个典型 FlaskRasa 项目的目录长什么样不同作者打包习惯不同但中文任务型对话机器人的源码包通常包含这几类内容Rasa 项目目录data/、config.yml、domain.yml、credentials.yml、Flask 应用目录app.py或server/、训练好的模型文件models/下的.tar.gz、前端静态页面templates/、static/、以及数据资料nlu.yml、stories.yml、rules.yml、自定义 action 的actions.py。先别急着装依赖用tree -L 2或文件管理器把层级看清楚重点确认三件事Rasa 版本写在哪个文件里、Flask 入口文件是哪个、模型文件是否已经训练好。# 查看项目根目录结构排除虚拟环境和缓存 find . -maxdepth 2 -not -path ./venv/* -not -path ./.git/* -not -path ./__pycache__/* | sort这条命令帮你快速定位关键文件。如果看到config.yml里有recipe: default.v1和language: zh说明这是 Rasa 3.x 的项目如果看到policies里还有MemoizationPolicy和TEDPolicy的旧写法可能是 Rasa 2.x。版本判断错了后面所有命令都会报错。2.2 运行链路从用户一句话到机器人回复中文任务型对话机器人的完整链路是用户输入中文文本 → Flask 接收请求 → 调用 Rasa 的 HTTP API通常是/model/parse做意图识别/webhooks/rest/webhook做完整对话→ Rasa 加载 NLU 模型解析意图和实体 → 根据 domain 和 stories 决定下一步 action → 如果是自定义 action通过 action server 执行 Python 代码 → 返回响应给 Flask → Flask 再返回给前端。这条链路里Flask 和 Rasa 是两个独立进程中间靠 HTTP 通信。很多人把 Flask 和 Rasa 写在同一个进程里结果调试时互相阻塞这是第一个要避开的架构坑。# Flask 端调用 Rasa REST API 的最小示例 import requests from flask import Flask, request, jsonify app Flask(__name__) RASA_API http://localhost:5005/webhooks/rest/webhook app.route(/chat, methods[POST]) def chat(): user_msg request.json.get(message, ) # 发送给 Rasasender 用于区分不同会话 resp requests.post(RASA_API, json{sender: user1, message: user_msg}) # Rasa 返回的是列表每项包含 text 字段 replies [r.get(text) for r in resp.json() if text in r] return jsonify({reply: replies}) if __name__ __main__: app.run(host0.0.0.0, port5000, debugTrue)这段代码的关键参数是sender它决定 Rasa 用哪个 tracker 来维护对话状态。如果所有用户都用同一个 sender对话历史会串。RASA_API指向 Rasa 服务的默认端口 5005生产环境要改成内网地址并加超时和重试。3. 把 Rasa 中文 NLU 跑通语料、配置与训练命令3.1 中文语料标注的四个硬性要求Rasa 的 NLU 训练数据放在data/nlu.yml中文场景下最容易出问题的是标注格式。每条样本必须包含intent和examples实体用[实体值](实体名)标注。中文不需要额外分词Rasa 3.x 默认用JiebaTokenizer或WhitespaceTokenizer但中文必须显式配置 tokenizer否则按空格切分会导致整句变成一个 token。同义词用synonym定义比如「北京」和「帝都」映射到同一个实体值。语料量方面每个意图至少 1520 条不同表达否则模型在真实输入上会频繁 fallback。# data/nlu.yml 片段中文意图与实体标注 version: 3.1 nlu: - intent: query_weather examples: | - 今天[北京](city)天气怎么样 - 帮我查一下[上海](city)明天天气 - [广州](city)会下雨吗 - synonym: 北京 examples: | - 帝都 - 首都注意version字段必须和 Rasa 版本匹配3.1 和 3.0 的格式有差异。synonym要写在顶层不能嵌在 intent 里。3.2 config.yml 里必须改的三个参数Rasa 的config.yml决定 pipeline 和 policies。中文任务型对话最常改的三个地方tokenizer 换成JiebaTokenizerfeaturizer 用CountVectorsFeaturizer并开启char_wb分析器来捕捉中文字符特征policies 里RulePolicy的core_fallback_threshold根据业务容忍度调整。默认 pipeline 对中文支持一般不改这三个参数意图识别准确率可能只有 60% 左右。# config.yml 中文优化片段 language: zh pipeline: - name: JiebaTokenizer - name: CountVectorsFeaturizer analyzer: char_wb min_ngram: 1 max_ngram: 4 - name: DIETClassifier epochs: 100 policies: - name: RulePolicy core_fallback_threshold: 0.4 core_fallback_action_name: action_default_fallbackchar_wb分析器对中文短文本效果明显min_ngram和max_ngram控制字符组合范围。DIETClassifier的epochs设 100 是常见起点语料少可以降到 50语料多可以加到 200但要注意过拟合。3.3 训练与启动两条命令的顺序不能反Rasa 的训练和启动是分开的。先rasa train生成模型到models/再rasa run加载模型启动服务。如果直接rasa run而没有模型文件会报错。自定义 action 需要单独启动rasa run actions端口默认 5055。Flask 端要等 Rasa 和 action server 都起来之后再启动否则第一次请求会超时。# 训练 NLU 和 Core 模型 rasa train --data data --config config.yml --domain domain.yml --out models # 启动 Rasa 服务加载最新模型 rasa run --model models --enable-api --cors * --debug # 另开终端启动 action server rasa run actions --actions actions--enable-api是必须的否则 Flask 无法通过 HTTP 调用。--cors *只在开发环境用生产环境要限制来源。--debug会打印详细日志排查意图识别问题时很有用但生产环境要关掉。4. Flask 与 Rasa 对接接口封装、会话管理与并发处理4.1 用 Flask 封装 Rasa 的两种模式Flask 对接 Rasa 有两种常见模式一种是透传模式Flask 只做请求转发和鉴权所有对话逻辑交给 Rasa另一种是增强模式Flask 在 Rasa 返回结果上做二次处理比如查数据库、调外部 API、拼接富文本。任务型对话机器人通常用增强模式因为很多业务查询需要实时数据不能全写在 Rasa action 里。增强模式的关键是把 Rasa 的 action 和 Flask 的业务接口分开Rasa 负责对话流程Flask 负责数据聚合。# 增强模式Flask 在 Rasa 回复后补充业务数据 app.route(/chat, methods[POST]) def chat(): user_msg request.json.get(message, ) sender request.json.get(sender, default) resp requests.post(RASA_API, json{sender: sender, message: user_msg}, timeout5) replies resp.json() # 如果 Rasa 返回了自定义字段Flask 可以继续处理 for r in replies: if r.get(custom, {}).get(need_order): order query_order_from_db(sender) r[text] f{r[text]}您的订单状态是{order[status]} return jsonify({reply: replies})timeout5是必须的Rasa 在加载大模型时首次响应可能超过 3 秒。custom字段需要在 Rasa 的responses.yml或自定义 action 里返回Flask 才能识别。4.2 会话管理sender 怎么设计才不串Rasa 用sender_id区分会话Flask 端要保证每个真实用户有唯一且稳定的 sender。常见做法是用前端生成的 UUID 存在 localStorage每次请求带上。如果用用户 ID 做 sender要注意多设备登录时的会话隔离。Rasa 的 tracker store 默认是内存重启后对话历史丢失生产环境要换成 Redis 或 SQL 数据库。# endpoints.yml 配置 Redis tracker store tracker_store: type: redis url: localhost port: 6379 db: 0 key_prefix: rasa:Redis 的key_prefix用于区分不同环境避免开发和生产数据混在一起。如果并发量大还要考虑 Redis 连接池和持久化策略。4.3 并发处理Flask 和 Rasa 各自能扛多少Flask 默认单线程用gunicorn或uwsgi可以多 worker。Rasa 的rasa run默认也是单进程可以用--workers参数开多进程但要注意模型加载会占内存每个 worker 都会加载一份模型。中文任务型对话机器人如果只是内部使用Flask 开 4 个 worker、Rasa 开 2 个 worker 通常够用。如果 QPS 超过 50要考虑把 Rasa 的 NLU 和 Core 分开部署或者用 Rasa 的rasa run --enable-api --response-timeout调整超时。# Flask 用 gunicorn 启动4 个 worker gunicorn -w 4 -b 0.0.0.0:5000 app:app --timeout 60 # Rasa 开 2 个 worker rasa run --model models --enable-api --workers 2 --port 5005--timeout 60要大于 Rasa 的最长响应时间否则 Flask 会先断开。--workers不是越多越好内存不够时反而会频繁 OOM。5. 部署与避坑从本地到服务器的常见翻车现场5.1 避坑Rasa 版本与 Python 版本的兼容矩阵现象pip install rasa后运行rasa train报ImportError: cannot import name XX from rasa。原因Rasa 3.x 要求 Python 3.83.10Rasa 2.x 支持 Python 3.63.8装错版本会导致依赖冲突。解决先看源码包里的requirements.txt或setup.py确定 Rasa 版本再用conda create -n rasa python3.9建独立环境最后pip install rasa3.x.x。不要用系统 Python 直接装。5.2 避坑中文模型训练时 loss 不下降现象rasa train跑完DIETClassifier的 loss 一直在 0.6 以上意图识别准确率低于 70%。原因中文语料太少或者CountVectorsFeaturizer没开char_wb导致中文被当成一个整体 token。解决每个意图补到 20 条以上config.yml里加analyzer: char_wbmin_ngram: 1max_ngram: 4。如果还是不行换LanguageModelFeaturizer加载bert-base-chinese但训练时间会翻倍。5.3 避坑Flask 调用 Rasa 超时现象前端请求/chat超过 10 秒无响应Flask 日志显示requests.exceptions.ReadTimeout。原因Rasa 首次加载模型或 action server 没启动导致请求阻塞。解决先确认rasa run和rasa run actions都在运行Flask 的requests.post加timeout10并在 Rasa 启动参数里加--response-timeout 10。如果 action 里有慢查询把查询逻辑移到 Flask 端异步处理。5.4 避坑Docker 部署时端口映射错乱现象Docker 容器里 Rasa 和 Flask 都起来了但 Flask 访问localhost:5005报连接拒绝。原因容器内localhost指向容器本身不是宿主机。解决用 Docker Compose 把 Rasa、action server、Flask 放在同一个 network 里Flask 里RASA_API改成http://rasa:5005/webhooks/rest/webhook。端口映射只暴露 Flask 的 5000 给外部。# docker-compose.yml 片段 services: rasa: image: rasa/rasa:3.x.x ports: - 5005:5005 volumes: - ./:/app command: run --model models --enable-api --cors * action: image: rasa/rasa-sdk:3.x.x ports: - 5055:5055 volumes: - ./actions:/app/actions flask: build: . ports: - 5000:5000 environment: - RASA_APIhttp://rasa:5005/webhooks/rest/webhookrasa/rasa和rasa/rasa-sdk的版本要一致否则 action 调用会报协议不匹配。5.5 避坑中文编码导致响应乱码现象Flask 返回的 JSON 里中文显示为\uXXXX前端解析后乱码。原因Flask 默认JSON_AS_ASCIITrue会把中文转义。解决在 Flask 配置里设app.config[JSON_AS_ASCII] False或者用jsonify时加ensure_asciiFalse。Rasa 端返回的中文一般没问题但 Flask 二次处理时要注意编码。6. 进阶把通用对话机器人改成业务查询机器人的三个技巧6.1 用 custom action 对接业务数据库Rasa 的 custom action 是 Python 类继承Action在run方法里查数据库并返回SlotSet和FollowupAction。中文任务型对话机器人最常见的业务是查订单、查物流、查余额。关键是把数据库查询封装成独立函数action 里只做参数校验和结果拼接。不要在 action 里写复杂 SQL否则调试困难。# actions/actions.py 查询订单状态 from rasa_sdk import Action, Tracker from rasa_sdk.executor import CollectingDispatcher class ActionQueryOrder(Action): def name(self): return action_query_order def run(self, dispatcher, tracker, domain): order_id tracker.get_slot(order_id) if not order_id: dispatcher.utter_message(text请提供订单号) return [] # 调用业务查询函数 status query_order_status(order_id) dispatcher.utter_message(textf订单 {order_id} 的状态是{status}) return []tracker.get_slot获取槽位值dispatcher.utter_message返回文本。如果查询失败要返回友好的 fallback 话术而不是抛异常。6.2 用 rules 和 stories 控制对话分支Rasa 的对话逻辑靠rules.yml和stories.yml。规则适合固定流程比如「用户问天气 → 机器人问城市 → 用户回答城市 → 机器人返回天气」。故事适合多轮分支比如用户中途换意图。中文场景下规则要写清楚condition避免和故事冲突。常见做法是简单流程用规则复杂流程用故事两者不要重叠。# rules.yml 天气查询规则 version: 3.1 rules: - rule: 查询天气 steps: - intent: query_weather - action: action_query_weather - slot_was_set: - city: null - action: utter_ask_city - intent: inform_city - action: action_query_weatherslot_was_set判断槽位是否为空决定是否追问。如果城市槽位已经有值直接查天气不追问。6.3 用 Rasa X 或日志做持续优化Rasa X 可以可视化对话历史、标注错误意图、一键加入训练数据。如果不用 Rasa X至少要把 Rasa 的日志存下来定期分析 fallback 的句子补充到nlu.yml。中文任务型对话机器人的准确率不是一次训练就能到 90% 的通常要经过 35 轮迭代。我一般会在 Flask 端加一个/feedback接口让用户对回复点赞或点踩把点踩的对话存到数据库每周导出一次做语料补充。# Flask 端记录用户反馈 app.route(/feedback, methods[POST]) def feedback(): data request.json # 存到数据库或日志文件 with open(feedback.log, a, encodingutf-8) as f: f.write(f{data[sender]}\t{data[message]}\t{data[reply]}\t{data[rating]}\n) return jsonify({status: ok})rating用 1 和 -1 表示赞和踩日志按天切割方便后续分析。这个习惯坚持三个月意图识别准确率能从 70% 提到 85% 以上。这套 FlaskRasa 的中文任务型对话机器人方案适合中小型业务快速验证对话交互不适合超大规模并发或需要复杂多轮推理的场景。如果你手里有源码包先按第 2 章的目录结构确认版本再按第 3 章训练第 4 章对接第 5 章避坑基本能跑通。后续优化靠的是持续补语料和调参数没有一劳永逸的配置。希望帮到你。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进