VS Code断点调试Rasa自定义动作全链路指南

发布时间:2026/7/19 8:13:00

VS Code断点调试Rasa自定义动作全链路指南 1. 项目概述用 VS Code 高效开发与调试 Rasa 自定义动作Custom Actions我从 2019 年开始做对话系统落地项目最早用 Rasa 1.x 搭建银行客服和内部 IT 支持机器人那时候调试 custom actions 全靠rasa run actionsprint() 日志滚动翻页一个逻辑分支出错得重启服务、重发请求、再翻三屏日志找 trace。直到我把整个开发流迁移到 VS Code并配齐断点调试、环境隔离、实时热重载和结构化日志查看开发效率直接翻了两倍不止——现在改一行 Python 代码3 秒内就能在真实对话流里看到效果错误堆栈直接定位到.py文件第 47 行的if判断条件上。这不是“配置个插件”那么简单而是把 VS Code 变成一个可交互、可追踪、可回溯的 Rasa 动作沙盒。本文讲的不是“如何安装 Python 插件”而是我在 6 个生产级 Rasa 项目含金融、医疗、政务三类高合规场景中反复打磨出的一套完整工作流它覆盖从项目初始化、依赖隔离、动作函数编写、本地模拟调用、断点单步调试、HTTP 请求拦截分析到异常上下文还原的全链路。关键词里的 “Towards AI — Multidisciplinary Science Journal” 提示你这套方法不依赖任何商业平台或黑盒工具全部基于开源组件组合所有配置可版本化、可复现、可审计。适合两类人一是刚学 Rasa 的开发者想跳过“日志大海捞针”的原始阶段二是已有项目但调试仍靠curl手动构造 JSON 的老手想把动作开发真正纳入工程化协作流程。2. 整体设计思路与核心架构拆解2.1 为什么必须放弃“裸跑 actions server”模式先说结论Rasa 官方推荐的rasa run actions启动方式本质是把 custom actions 当作一个独立 HTTP 微服务来运行。这在部署时合理但在开发阶段它制造了三重割裂时间割裂每次改完代码必须手动CtrlC终止进程 →pip install -e .如果用了包管理→rasa run actions重启 → 等待服务就绪 → 再切到另一个终端发测试请求。实测平均耗时 22~38 秒/次一天改 50 处光等待就浪费近 20 分钟。空间割裂动作代码Python和对话逻辑YAML/Markdown物理分离VS Code 里打开actions.py时看不到当前触发该动作的stories.yml上下文更无法反向跳转到domain.yml中定义的 slot 或 form。调试割裂rasa run actions输出的是纯文本日志错误信息如Failed to execute custom action action_check_balance. Error: NoneType object has no attribute get你根本不知道None是哪个变量、来自哪次用户输入、上游tracker的slots状态是什么。它不提供变量快照、不支持断点、不记录 HTTP 请求原始 payload。我试过用pdb嵌入式调试但rasa run actions是多进程模型默认 4 workerpdb只能挂在一个进程上而你永远不知道当前请求被分发到哪个 worker。后来换成ptvsd远程调试又得配端口、防火墙、跨进程 attach复杂度远超收益。所以我的方案是让 VS Code 成为 actions server 的“控制平面”——不是去连一个外部服务而是把rasa-sdk的核心执行循环以单进程、可调试的方式嵌入到 VS Code 的 Python 运行环境中。这样所有 Python 调试能力断点、变量监视、表达式求值、调用栈展开全部可用且完全复用 VS Code 的终端、Git 集成、代码导航等成熟能力。2.2 架构选型为什么是rasa-sdkFlaskVS Code Debug Config而非其他市面上有几种替代思路我都踩过坑用rasa shell --enable-api Postman 调试看似简单但 Postman 无法设置断点只能看返回结果。当动作内部调用第三方 API如查数据库、调用风控引擎失败时你只能看到500 Internal Server Error看不到中间哪一步抛了异常、tracker的latest_message是什么内容、slots是否被意外清空。我曾为一个action_validate_form里slot_mapping的优先级问题在 Postman 里构造了 17 种不同 JSON payload耗时 3 小时才定位到是requested_slot字段名拼写错误。用 Docker Compose 启动全套 Rasa 环境适合集成测试但开发时太重。每次改actions.py都要docker build新镜像 →docker-compose up -d→ 等容器启动 →docker logs -f查日志。镜像层缓存虽能提速但requirements.txt一变整个构建链就失效。我们一个项目有 12 个自定义动作Docker 方式平均单次迭代 55 秒。用 PyCharm 专业版远程调试功能确实强但它强制要求你买订阅且团队协作时每人配一套 License 成本高。更重要的是PyCharm 的 Rasa 插件生态弱对domain.yml的语法校验、stories.yml的路径可视化支持远不如 VS Code 的 YAML 插件成熟。最终选定rasa-sdk原生方式原因很实在rasa-sdk本身就是一个标准 Flask 应用启动入口清晰rasa_sdk/__main__.py没有隐藏的 daemon 化逻辑它的ActionExecutor类设计良好run方法接收Tracker对象这个对象就是 Rasa 对话状态的完整快照包含sender_id、slots、latest_message、events等全部字段调试时可直接展开查看VS Code 的 Python 扩展对 Flask 的调试支持极佳自动识别app.run()无需额外配置 WSGI 服务器。提示不要用rasa-sdk的--enable-api参数启动。这个参数会启用一个额外的/webhook接口用于接收 Rasa Core 的请求但它会绕过 Flask 的标准调试模式导致断点失效。我们只用rasa-sdk的main模块作为普通 Python 脚本运行。2.3 核心组件协同关系一张图说清数据流向虽然不能用 Mermaid但我用文字描述清楚这个闭环VS Code 启动调试器你点击绿色三角形VS Code 执行python -m rasa_sdk --actions actions --debug注意--debug是关键它禁用多进程强制单线程运行Flask 服务监听rasa-sdk启动一个单线程 Flask 实例默认监听http://localhost:5055Rasa Core 发起请求你在另一个终端运行rasa shell当对话流触发action_check_balance时Rasa Core 会向http://localhost:5055/webhook发送 POST 请求body 是完整的TrackerJSON 序列化数据VS Code 捕获断点请求到达rasa-sdk的WebhookResource.post()方法执行到你设在actions.py中ActionCheckBalance.run()的第一行断点VS Code 立即暂停你可以查看tracker变量的所有属性实时响应与反馈你按 F10 单步执行观察dispatcher.utter_message()如何生成响应SlotSet(balance, ¥12,345.67)如何更新状态最后return []返回事件列表Rasa Core 接收后继续执行后续步骤。这个流程里VS Code 不是“客户端”而是rasa-sdk进程的宿主环境。所有日志、变量、调用栈都在同一个 IDE 里可见没有跨进程、跨终端、跨网络的盲区。3. 核心细节解析与实操要点3.1 项目结构标准化为什么必须用src/pyproject.toml而非扁平目录很多教程教你把actions.py和credentials.yml放在同一级这在 demo 阶段可行但一旦项目超过 5 个动作就会失控。我见过最乱的项目actions.py2300 行混着数据库连接、HTTP 调用、正则匹配、业务规则git blame时根本分不清谁改了哪段逻辑。我的标准结构已用于 6 个项目my_rasa_project/ ├── data/ │ ├── nlu.yml │ ├── stories.yml │ └── rules.yml ├── domain.yml ├── config.yml ├── credentials.yml ├── endpoints.yml ├── src/ # 所有 Python 代码放这里强制模块化 │ ├── __init__.py │ ├── actions/ # 动作实现 │ │ ├── __init__.py │ │ ├── base.py # 基类封装通用 DB/HTTP 工具 │ │ ├── balance.py # action_check_balance │ │ ├── transfer.py # action_initiate_transfer │ │ └── validation.py # action_validate_form_xxx │ ├── utils/ # 工具函数 │ │ ├── db.py │ │ └── api_client.py │ └── tests/ # 动作单元测试 ├── pyproject.toml # 替代 requirements.txt支持分组依赖 └── docker-compose.yml为什么用pyproject.tomlrequirements.txt是扁平列表无法区分“开发依赖”如pytest,black和“运行依赖”如rasa-sdk3.5.10。pyproject.toml支持[project.optional-dependencies]比如[project.optional-dependencies] dev [pytest7.0, black23.0, ruff0.0.280] test [pytest-cov4.0]这样pip install -e .[dev]就只装开发工具pip install -e .[test]装测试工具避免生产环境误装pytest。更重要的是pyproject.toml是 PEP 517/518 标准VS Code 的 Python 扩展能自动识别并配置解释器路径比手动指定venv/bin/python稳定得多。注意src/目录不是可选的。如果你把actions.py放在项目根目录rasa-sdk启动时会把当前目录加入sys.path导致import src.actions.balance失败。必须用src/结构并在pyproject.toml中声明[project] name my-rasa-actions version 0.1.0 [build-system] requires [setuptools45, wheel, setuptools_scm[toml]6.2] build-backend setuptools.build_meta3.2 VS Code 调试配置详解.vscode/launch.json的每一行都经过生产验证这是最核心的配置文件我把它拆解到原子级别{ version: 0.2.0, configurations: [ { name: Rasa Actions Debug, type: python, request: launch, module: rasa_sdk, args: [ --actions, src.actions, --cors, *, --debug, --enable-api ], console: integratedTerminal, justMyCode: true, env: { PYTHONPATH: ${workspaceFolder}/src, RASA_SDK_LOG_LEVEL: DEBUG } } ] }逐项说明module: rasa_sdk告诉 VS Code 不要运行某个.py文件而是以python -m rasa_sdk方式启动。这是关键因为rasa-sdk的入口在__main__.py直接运行rasa_sdk/__main__.py会报错。--actions, src.actions指定动作模块路径。注意是src.actionsPython 包路径不是src/actions/文件系统路径。rasa-sdk会自动导入src.actions.__init__.py然后扫描所有继承Action的类。--debug强制单线程模式。没有它rasa-sdk默认启 4 个 worker 进程VS Code 只能 attach 到主进程子进程的断点全部失效。--enable-api启用/webhook接口。别被名字误导这不是“开启 API 服务”而是让rasa-sdk响应 Rasa Core 的 webhook 请求。没有它rasa shell发来的请求会 404。env: { PYTHONPATH: ${workspaceFolder}/src }这是解决ImportError的终极方案。rasa-sdk启动时当前工作目录是项目根目录但src/不在sys.path里。加这一行所有from src.utils.db import get_db都能成功解析。justMyCode: true让调试器只停在你的代码里跳过rasa-sdk、Flask、requests等库的内部代码。否则你按 F11 进入dispatcher.utter_message()会掉进rasa_sdk/executor.py的 200 行源码里毫无意义。console: integratedTerminal日志输出到 VS Code 内置终端而不是弹出新窗口。这样你可以同时看调试器变量窗、终端日志、代码编辑器三者联动。3.3 动作代码编写规范从“能跑”到“可维护”的 5 条铁律我见过太多actions.py写成“瑞士军刀”一个函数里连数据库、调 API、做 NLP、发邮件、写日志。这样的代码无法单测、无法复用、无法 debug。以下是我在金融项目中强制推行的 5 条规范每个动作类必须继承AsyncActionRasa 3.x或Action2.x且run()方法只做三件事输入校验、核心业务调用、输出组装。绝不包含 I/O 操作。# ✅ 正确业务逻辑外移 class ActionCheckBalance(Action): def name(self) - Text: return action_check_balance async def run( self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict[Text, Any] ) - List[Dict[Text, Any]]: # 1. 输入校验 user_id tracker.get_slot(user_id) if not user_id: dispatcher.utter_message(responseutter_no_user_id) return [] # 2. 调用业务服务纯函数 balance_data await check_balance_service(user_id) # 3. 输出组装 if balance_data[status] success: dispatcher.utter_message( textf您的账户余额为{balance_data[amount]} ) else: dispatcher.utter_message(responseutter_balance_check_failed) return []所有外部依赖DB、API、缓存必须通过依赖注入或工厂函数获取禁止在类里import sqlite3或requests.get()。# ✅ 正确依赖抽象化 from src.utils.db import get_db_connection class ActionCheckBalance(Action): async def run(...): db get_db_connection() # 工厂函数可 mock result db.execute(SELECT balance FROM accounts WHERE user_id ?, [user_id])dispatcher.utter_message()必须用response参数引用domain.yml中的responses禁止硬编码字符串。# domain.yml responses: utter_balance_info: - text: 您的账户余额为{amount}# ✅ 正确 dispatcher.utter_message(responseutter_balance_info, amountbalance_data[amount]) # ❌ 错误硬编码破坏多语言支持 dispatcher.utter_message(textf您的账户余额为{balance_data[amount]})所有异步调用await必须包裹try/except捕获具体异常如DatabaseConnectionError并转换为dispatcher.utter_message(responseutter_db_unavailable)。绝不让未捕获异常穿透到rasa-sdk层否则整个服务会 500。每个动作类必须有对应单元测试覆盖正常流、异常流、边界值。测试用Tracker.from_dict()构造模拟状态不用启动真实 Rasa。# src/tests/test_balance.py def test_action_check_balance_success(): tracker Tracker.from_dict( { sender_id: test_user, slots: {user_id: U12345}, latest_message: {intent: {name: check_balance}}, } ) action ActionCheckBalance() events asyncio.run(action.run(CollectingDispatcher(), tracker, {})) assert len(events) 1 assert events[0][text] 您的账户余额为¥12,345.674. 实操过程与核心环节实现4.1 从零搭建5 分钟完成可调试环境附完整命令清单以下是在 macOS/Linux 下的实操步骤Windows 用户将source venv/bin/activate替换为venv\Scripts\activate.bat即可。所有命令均经 Rasa 3.5.10 Python 3.9 验证。第一步初始化项目与虚拟环境# 创建项目目录 mkdir my-banking-bot cd my-banking-bot # 初始化 Git强制调试配置需版本化 git init # 创建虚拟环境用 venv不推荐 conda因 Rasa 对 conda channel 依赖复杂 python3 -m venv venv source venv/bin/activate # 安装 Rasa SDK注意不是 rasarasa 是 Corerasa-sdk 是动作服务 pip install rasa-sdk3.5.10 # 创建标准目录结构 mkdir -p src/{actions,utils,tests} data touch src/__init__.py src/actions/__init__.py src/utils/__init__.py src/tests/__init__.py第二步创建最小可运行动作# src/actions/balance.py from typing import Any, Text, Dict, List from rasa_sdk import Action, Tracker from rasa_sdk.executor import CollectingDispatcher from rasa_sdk.events import SlotSet class ActionCheckBalance(Action): def name(self) - Text: return action_check_balance def run( self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict[Text, Any], ) - List[Dict[Text, Any]]: # 模拟查余额实际应调用 service user_id tracker.get_slot(user_id) or U12345 balance ¥12,345.67 dispatcher.utter_message( textf您好用户 {user_id}您的账户余额为{balance} ) return [SlotSet(balance, balance)]第三步配置 VS Code 调试环境# 创建 .vscode 目录 mkdir -p .vscode # 写入 launch.json复制上面的完整配置 cat .vscode/launch.json EOF { version: 0.2.0, configurations: [ { name: Rasa Actions Debug, type: python, request: launch, module: rasa_sdk, args: [ --actions, src.actions, --cors, *, --debug, --enable-api ], console: integratedTerminal, justMyCode: true, env: { PYTHONPATH: ${workspaceFolder}/src, RASA_SDK_LOG_LEVEL: DEBUG } } ] } EOF第四步启动调试并验证# 在 VS Code 中打开此文件夹File Open Folder # 确保右下角 Python 解释器选中 venv/bin/python # 在 src/actions/balance.py 的 run() 第一行设断点点击行号左侧空白处 # 按 CtrlShiftDWindows/Linux或 CmdShiftDmacOS打开调试面板 # 选择 Rasa Actions Debug点击绿色三角形 # 观察终端输出 # * INFO:werkzeug: * Running on http://localhost:5055 # * INFO:rasa_sdk: Action endpoint is up and running. # 新开终端启动 Rasa Core需先有 domain.yml 和 config.yml rasa train rasa shell --enable-api --cors * # 在 shell 中输入触发动作的句子如 我想查余额 # VS Code 会立即在断点处暂停你可以展开 tracker 变量看 slots、latest_message 等第五步添加日志与性能监控生产就绪# src/actions/base.py import logging from datetime import datetime logger logging.getLogger(__name__) class BaseAction(Action): def log_start(self, tracker: Tracker): logger.info( f[{self.name()}] START | sender_id{tracker.sender_id} | fslots{dict(tracker.slots)} | flatest_intent{tracker.latest_message.get(intent, {}).get(name)} ) def log_end(self, tracker: Tracker, duration_ms: float): logger.info( f[{self.name()}] END | duration{duration_ms:.2f}ms | fsender_id{tracker.sender_id} )然后在ActionCheckBalance.run()开头加self.log_start(tracker)结尾加self.log_end(tracker, (end_time - start_time) * 1000)。这样每次动作执行都会在 VS Code 终端打印结构化日志方便快速识别慢动作500ms。4.2 断点调试实战一次典型故障的 3 分钟定位全过程场景用户说“我要转账给张三”但机器人回复“请确认收款人姓名”然后卡住不再追问账号。预期是连续问姓名和账号。步骤 1复现问题启动rasa shell输入我要转账给张三观察回复确认卡在action_ask_recipient_name后无后续步骤 2在可疑动作设断点打开src/actions/validation.py在ActionValidateTransferForm.validate_recipient_name()的第一行设断点这是 form validation 动作步骤 3触发并观察在rasa shell中再次输入我要转账给张三VS Code 暂停展开tracker变量tracker.slots[recipient_name]是张三正确tracker.slots[requested_slot]是recipient_account预期是recipient_name说明 form 状态错乱tracker.events最后一条是ActiveLoop(transfer_form)但tracker.active_loop是None步骤 4溯源问题查看domain.yml中transfer_form的required_slotsforms: transfer_form: required_slots: - recipient_name - recipient_account但stories.yml中有一条故事recipient_name被设为null- story: transfer with missing name steps: - intent: transfer_money - action: transfer_form - slot_was_set: - recipient_name: null # ❌ 这里设了 null导致 requested_slot 跳到下一个步骤 5修复与验证删除slot_was_set中的null行或改为recipient_name: 张三保存VS Code 自动重载因--debug模式支持热重载重新训练rasa train再试问题消失。整个过程从发现问题到定位根因不到 3 分钟。没有日志 grep没有 curl 构造所有信息在 VS Code 里一目了然。4.3 HTTP 请求拦截分析看清 Rasa Core 与 Actions 的真实通信有时动作逻辑没错但 Rasa Core 发来的tracker数据不对。这时需要抓包看原始 HTTP 请求。方法用 VS Code 的 REST Client 插件推荐或 curl 手动模拟安装 VS Code 插件 “REST Client”创建test-webhook.http文件POST http://localhost:5055/webhook Content-Type: application/json { next_action: action_check_balance, sender_id: test_user_123, tracker: { sender_id: test_user_123, slots: { user_id: U12345, balance: null }, latest_message: { intent: {name: check_balance}, text: 查余额 }, events: [], paused: false, followup_action: null, active_loop: null, latest_action_name: null } }在 VS Code 中右键 “Send Request”它会发送请求到rasa-sdk并在下方显示响应。如果你想看 Rasa Core 实际发了什么启动rasa shell时加--log-file rasa-core.log然后tail -f rasa-core.log | grep webhook就能看到原始 JSON。这个技巧帮我揪出过多次问题比如tracker.slots里user_id是U12345 带空格因为 NLU 没做strip()或者latest_message.text是 查余额 导致正则匹配失败。5. 常见问题与排查技巧实录5.1 常见问题速查表问题现象根本原因解决方案验证方式断点不生效程序直接跑完--debug参数缺失rasa-sdk启动了多进程检查launch.json的args数组确认包含--debug终端输出应有INFO:rasa_sdk: Starting actions server in debug mode.ImportError: No module named src.actionsPYTHONPATH未正确设置或src/下缺少__init__.py确认.vscode/launch.json中env.PYTHONPATH值为${workspaceFolder}/src且src/__init__.py存在在调试终端执行import sys; print(sys.path)检查workspaceFolder/src是否在列表中Rasa Core 报错Failed to execute custom action xxx. Error: Connection refusedrasa-sdk未启动或端口被占用默认 5055运行lsof -i :5055查看占用进程kill -9 PID或改launch.json的args加--port 5056curl http://localhost:5055/health应返回{status:ok}动作返回后Rasa Core 无响应对话卡住动作run()方法未return []或返回了非法事件类型检查run()方法末尾是否有return []确保返回的是List[Dict]不是Dict或None在断点处查看return行的变量值或加print(type(events))中文乱码日志显示?VS Code 终端编码非 UTF-8VS Code 设置搜索terminal.integrated.env.osxmacOS或terminal.integrated.env.linux添加LANG: en_US.UTF-8在终端执行locale确认LANGen_US.UTF-85.2 独家避坑技巧那些文档里不会写的细节技巧 1用rasa-sdk的--verbose参数看详细路由日志在launch.json的args里加--verbose启动后你会看到INFO:rasa_sdk: Registered function for action_check_balance INFO:rasa_sdk: Registered function for action_initiate_transfer INFO:rasa_sdk: Received request to run action_check_balance这能确认rasa-sdk是否正确扫描到了你的动作类。如果没看到Registered function说明--actions路径写错了或类没继承Action。技巧 2调试form动作时强制requested_slot为特定值form动作的validate_xxx方法只在requested_slot匹配时触发。有时你想单独测试validate_recipient_account但requested_slot总是None。解决方案在stories.yml中写一个测试故事显式设置- story: test validate account steps: - intent: transfer_money - action: transfer_form - slot_was_set: - requested_slot: recipient_account - user: 123456789 - action: action_validate_transfer_form这样rasa train后rasa shell里输入123456789就能精准触发你要调试的方法。技巧 3当tracker的slots显示MagicMock ...时这是 pytest mock 的痕迹不是真实数据在单元测试中Tracker.from_dict()会创建 mock 对象。如果你在调试测试时看到slots是 mock说明你没在真实rasa shell环境下调试。解决不要在测试文件里设断点而是在rasa shell触发时在actions.py里设断点。技巧 4rasa-sdk的--enable-api和--cors必须同时存在单独--enable-api会导致 CORS 错误Rasa Core 无法跨域请求单独--cors *无效因为--enable-api是开关。两者缺一不可。技巧 5升级 Rasa 版本后rasa-sdk必须严格匹配Rasa 3.5.x 要求rasa-sdk3.5.10不能用3.5.0或3.6.0。不匹配会导致AttributeError: Tracker object has no attribute get_latest_entity_values等奇怪错误。解决方案pip install rasa-sdk$(rasa --version | cut -d -f2)自动取 Rasa 版本号。5.3 性能优化让调试环境快如闪电禁用 VS Code 的自动格式化在.vscode/settings.json中加{ editor.formatOnSave: false, python.formatting.provider: none }因为rasa-sdk启动时会扫描所有.py文件格式化插件会锁文件导致启动卡顿。用pyright替代Pylance做类型检查Pylance功能强但内存占用高pyright更轻量。在设置中关闭Pylance启用pyright。调试时关闭不必要的 VS Code 扩展如 Live Share、GitHub Pull Requests它们会在后台建立 WebSocket 连接拖慢调试器响应。为rasa-sdk单独建一个轻量虚拟环境不要和 Rasa Core 共用venv。Core 环境装rasa2GBActions 环境只装rasa-sdk50MB启动更快。6. 后续扩展与工程化建议这套 VS Code 调试流我已经把它沉淀为一个可复用的模板仓库rasa-actions-template。里面包含预配置的pyproject.toml含dev、test、prod三组依赖完整的.vscode/目录含launch.json、settings.json、extensions.json推荐插件列表Makefile一条命令完成make setup初始化、make test运行所有单元测试、make debug启动调试GitHub Actions CI 配置每次 PR 自动运行pytestruffblack --check。如果你的团队有多个 Rasa 项目

相关新闻