1. 项目概述用 VS Code 打造 Rasa 自定义动作开发与调试的“所见即所得”工作流我每天和 Rasa 打交道从写 domain.yml 的 intent 到调通一个带数据库查询的 custom action中间最耗神的环节从来不是逻辑设计而是“改完代码重启服务器curl 测试看日志再改……”这个循环。直到我把整个 custom action 开发流程迁进 VS Code——不是简单地用它写 Python 文件而是真正把它变成一个可断点、可变量监视、可单步执行、可复现错误上下文的完整调试环境。标题里说的 “How I Use VS Code To Develop And Debug Custom Actions In Rasa”核心就在这儿VS Code 不是编辑器而是 Rasa 动作服务的“驾驶舱”。它解决的不是“能不能跑”的问题而是“为什么没按预期跑”“变量在第几行变成了 None”“API 调用返回的 JSON 结构到底长什么样”这些真正在现场卡住你三小时的细节问题。如果你正被ActionExecutionRejection报错折磨或者每次改完actions.py都得手动 curl 七八个测试用例又或者你的 custom action 里嵌套了 requests、SQLAlchemy、甚至调用了另一个微服务那你就是这个工作流的精准目标用户。它不依赖 Docker Compose 的复杂编排虽然可以兼容也不要求你去啃 Rasa SDK 的源码调试器而是一套基于 VS Code 原生能力、Rasa 官方 SDK 接口规范和 Python 标准调试协议的轻量级组合方案。整套流程下来我平均把单个 custom action 的调试周期从 45 分钟压缩到 12 分钟以内关键在于——你能在函数刚被 Rasa 调用的那一刻就停在run()方法的第一行看着tracker里的latest_message是什么slots当前状态如何followup_action是否已被设置。这不是魔法是把本该透明的执行过程真正拉到你眼皮底下。2. 整体架构设计与工具链选型逻辑2.1 为什么放弃 Rasa X / Rasa Studio 的内置调试Rasa X 提供的 Web UI 确实能让你点几下就触发一个 action但它本质上是个黑盒你只能看到最终返回的events和responses看不到中间变量、无法设断点、不能 step into 你自己的数据库查询函数。更关键的是它的调试环境和你本地开发环境存在天然隔离——你在 X 里改的actions.py并不会实时同步到后端服务进程你得手动重启rasa run actions这直接破坏了“改-测-验”的节奏感。而 VS Code 的优势在于它完全运行在你的本地开发机上所有文件、依赖、环境变量都由你一手掌控调试器直接 attach 到你启动的 Python 进程变量栈帧一目了然。这不是“更高级”而是“更贴近真实执行路径”。2.2 为什么选择rasa run actions而非rasa shell --enable-api这是很多人踩的第一个坑。rasa shell启动的是一个集成了 NLU、Core 和 Action Server 的全功能服务它内部会 fork 出一个子进程来运行 action server但这个子进程的调试端口是随机分配且不对外暴露的。你根本没法让 VS Code 的 debugger attach 上去。而rasa run actions是 Rasa SDK 提供的专用动作服务启动命令它只做一件事监听http://localhost:5055/webhook接收 Rasa Core 发来的 action 请求并调用你actions.py中定义的类方法。更重要的是它支持--enable-api参数允许你通过 HTTP 接口手动触发 action这为单元测试和边界条件验证提供了极大便利它还支持--debug模式输出详细的请求/响应日志。最关键的是它是一个标准的 Python 主进程VS Code 的 Python Debugger 可以毫无障碍地 attach 到它身上——这才是整个调试链路成立的前提。2.3 为什么坚持使用venv而非 conda 或全局 PythonRasa 对 Python 版本和依赖库版本极其敏感。我在一个项目中用 conda 创建了python3.9环境安装了rasa3.5.0结果发现rasa-sdk的某个底层依赖aiohttp在 conda channel 里的版本和 pip 官方源不一致导致 action server 启动时抛出AttributeError: module aiohttp has no attribute TCPConnector。这种问题排查起来极其痛苦。而venv是 Python 官方标准pip install的行为完全可预测。我现在的标准操作是在项目根目录下执行python -m venv .venv然后source .venv/bin/activatemacOS/Linux或.venv\Scripts\activate.batWindows最后pip install rasa rasa-sdk。这样做的好处是.venv目录和requirements.txt文件一起提交到 Git任何新加入的同事拉下代码执行source .venv/bin/activate pip install -r requirements.txt就能获得和我完全一致的运行环境。VS Code 会自动识别项目根目录下的.venv并在右下角 Python 解释器选择器里显示它这是后续所有调试配置生效的基础。2.4 为什么调试配置必须用attach模式而非launchVS Code 的 Python 调试有两种主流模式launch启动一个新进程并调试和attach连接到一个已运行的进程并调试。对于 Rasa action server我们必须用attach。原因很简单rasa run actions启动的服务需要绑定到5055端口如果 VS Code 用launch模式去启动它那么每次调试都要重新启动服务而 Rasa Core运行在另一个终端会因为连接中断而报错Connection refused。更糟的是launch模式无法精确控制启动参数比如你无法在launch.json里优雅地指定--cors *或--enable-api。而attach模式则完美规避了这些问题你先在终端里用你喜欢的方式bash脚本、Makefile 或直接敲命令启动rasa run actions确保它稳定运行然后在 VS Code 里点击“运行并调试”选择Python: Attach配置Debugger 就会连接到那个已经存在的进程。此时你可以在任意actions.py的代码行设断点只要 Rasa Core 触发了对应 action执行流就会立刻停在那里。这是一种“服务常驻、调试按需”的生产级思路而不是“每次调试都重启服务”的开发级思路。2.5 为什么推荐rasa-sdk的0.37.x系列而非最新版Rasa SDK 的版本迭代非常快但并非每个新版都向后兼容。我在升级到rasa-sdk3.0.0后发现自定义 action 类的name()方法签名发生了变化从def name(self) - Text:变成了def name(self) - Text: ...但 Rasa Core 的调用逻辑还没跟上导致ActionNotFound错误。经过比对官方文档的变更日志我发现0.37.x系列如0.37.2是最后一个与rasa3.5.x系列完全匹配的 SDK 版本。它的 API 稳定文档齐全社区案例丰富。更重要的是它的ActionRunner内部实现清晰当你在 VS Code 里打断点时能看到完整的调用栈ActionRunner.run()→YourCustomAction.run()→your_database_query()每一层都干净利落。而新版 SDK 引入了异步async def run()虽然性能更好但对初学者来说调试器在await处的跳转逻辑会变得复杂容易迷失在事件循环里。所以我的经验是除非你明确需要新版的某项特性比如对 FastAPI 的原生支持否则 stick withrasa-sdk0.37.2rasa3.5.10这个黄金组合稳定性远胜于盲目追新。3. 核心细节解析与实操要点3.1 项目结构标准化让 VS Code 自动识别一切一个混乱的项目结构是调试失败的温床。我强制要求所有 Rasa 项目遵循以下根目录结构my_rasa_project/ ├── .vscode/ # VS Code 专属配置 │ ├── settings.json # 编辑器偏好设置 │ └── launch.json # 调试配置 ├── actions/ # custom action 代码存放处关键 │ ├── __init__.py │ └── actions.py # 所有自定义 action 类的定义 ├── data/ # NLU 和 stories 数据 ├── domain.yml # domain 定义 ├── config.yml # pipeline 配置 ├── credentials.yml # 连接外部服务的凭证 ├── endpoints.yml # action server 地址配置 ├── models/ # 训练好的模型 ├── .venv/ # Python 虚拟环境 └── requirements.txt # 依赖清单为什么actions/目录必须独立因为rasa run actions命令默认会在当前工作目录下查找actions/子目录。如果你把actions.py放在项目根目录它会找不到。更重要的是VS Code 的 Python 扩展会扫描项目根目录下的actions/并将其识别为一个可导入的 Python 包。这意味着当你在actions.py里写from actions.utils import db_connect时VS Code 不会报Import actions.utils could not be resolved的红色波浪线。而endpoints.yml的内容必须严格如下action_endpoint: url: http://localhost:5055/webhook这是 Rasa Core 和 Action Server 之间的“握手协议”。如果这里写成http://127.0.0.1:5055/webhook在某些网络环境下尤其是 Docker 网络Core 可能无法解析localhost导致Connection refused。localhost是唯一被所有环境一致认可的回环地址。3.2 VS Code Python 扩展配置不只是装个插件那么简单仅仅安装 Python 扩展是远远不够的。你需要在.vscode/settings.json中进行精细化配置{ python.defaultInterpreterPath: ./.venv/bin/python, python.testing.pytestArgs: [ tests/ ], python.testing.pytestEnabled: true, python.formatting.provider: black, python.linting.enabled: true, python.linting.pylintEnabled: true, python.analysis.extraPaths: [actions] }其中python.defaultInterpreterPath指向你的虚拟环境解释器这是 VS Code 能正确解析import语句和提供代码补全的前提。python.analysis.extraPaths是关键中的关键它告诉 VS Code 的语言服务器“actions” 这个包不在标准路径下而是在项目根目录的actions子目录里请把它加入分析路径。没有这一行你在actions.py里写的from actions.db import get_user_by_idVS Code 会认为actions.db是一个不存在的模块所有补全和跳转功能全部失效。python.testing.pytestArgs则为你后续编写 action 单元测试铺平了道路——你可以直接在 VS Code 的测试侧边栏里运行单个测试用例而无需切到终端。3.3actions.py的编写规范让调试器“看得懂”你的代码一个写得“调试友好”的actions.py其核心是显式化所有依赖和状态。我坚决反对在run()方法里直接写requests.get(https://api.example.com)。正确的做法是# actions/actions.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 import logging logger logging.getLogger(__name__) class ActionFetchUserProfile(Action): def name(self) - Text: return action_fetch_user_profile def run( self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict[Text, Any], ) - List[Dict[Text, Any]]: # 1. 显式提取关键输入 user_id tracker.get_slot(user_id) logger.debug(fFetching profile for user_id: {user_id}) # 2. 显式调用封装好的服务函数 try: profile_data self._fetch_from_api(user_id) except Exception as e: logger.error(fAPI call failed for user_id {user_id}: {e}) dispatcher.utter_message(text抱歉获取用户信息时出了点问题。) return [] # 3. 显式构造返回事件 events [] if profile_data: events.append(SlotSet(user_name, profile_data.get(name, ))) events.append(SlotSet(user_email, profile_data.get(email, ))) dispatcher.utter_message(textf你好{profile_data.get(name, 朋友)}) else: dispatcher.utter_message(text未找到该用户信息。) return events def _fetch_from_api(self, user_id: str) - Dict[Text, Any]: # 这里才是真正的 API 调用逻辑 # 你可以在这里轻松设断点查看 request URL、headers、response status code import requests response requests.get(fhttps://api.example.com/users/{user_id}) response.raise_for_status() return response.json()为什么这样写因为当你在_fetch_from_api方法的第一行设断点时调试器会停在那里你可以鼠标悬停在user_id上看到它的值是U12345你可以展开requests模块看到它的__version__你甚至可以打开调试控制台手动执行response requests.get(...)观察返回的response对象。而如果把所有逻辑都塞进run()断点一设你面对的就是一个巨大的、混杂着业务逻辑、NLU 解析、事件构造的“大泥球”根本分不清哪一行在处理什么。3.4endpoints.yml与credentials.yml的安全实践别把密钥写死在代码里credentials.yml是 Rasa 连接 Slack、Telegram 等渠道的凭证文件但它同样可以用来存放 action server 的内部配置。我习惯在credentials.yml里添加一个action_serversection# credentials.yml action_server: api_key: ${ACTION_SERVER_API_KEY} database_url: ${DATABASE_URL}然后在actions.py中这样读取import os from rasa_sdk import Action from rasa_sdk.events import SlotSet class ActionQueryDatabase(Action): def name(self) - Text: return action_query_database def run(self, dispatcher, tracker, domain): # 从环境变量读取而非硬编码 db_url os.getenv(DATABASE_URL) or domain.get(config, {}).get(database_url) # ... 后续数据库操作这样做的好处是双重的第一ACTION_SERVER_API_KEY和DATABASE_URL这些敏感信息可以放在.env文件里VS Code 的 Python 扩展会自动加载.env永远不会进入 Git第二domain.get(config, {})提供了一个 fallback 机制你可以在domain.yml的config下添加测试用的 mock 数据库 URL方便在 CI 环境中运行测试。endpoints.yml则永远只包含url绝不包含任何认证信息因为它本质上只是一个路由配置不是凭证存储。3.5 日志级别与格式化让调试信息成为你的“第三只眼”Rasa 默认的日志级别是INFO这对于调试 custom action 来说信息量严重不足。你需要在actions.py的顶部添加import logging logging.basicConfig( levellogging.DEBUG, format%(asctime)s - %(name)s - %(levelname)s - %(message)s, handlers[ logging.StreamHandler(), # 输出到终端 logging.FileHandler(actions_debug.log) # 同时写入文件 ] ) logger logging.getLogger(__name__)然后在run()方法的关键节点插入logger.debug()logger.debug(fTracker state: {tracker.current_state()})logger.debug(fSlots before action: {tracker.slots})logger.debug(fLatest message entities: {tracker.latest_message.get(entities, [])})这些日志不会污染你的rasa shell终端输出因为它们属于actions进程但会清晰地打印在你启动rasa run actions的那个终端里。更重要的是当你在 VS Code 里 attach 调试器时这些logger.debug的输出会实时出现在 VS Code 的“调试控制台”Debug Console里和你的断点调试形成互补断点告诉你“此刻变量是什么”日志告诉你“之前发生了什么之后将要发生什么”。这是一种立体化的调试视角。4. 实操过程与核心环节实现4.1 第一步初始化项目与虚拟环境5分钟打开终端导航到你的工作目录执行以下命令# 1. 创建项目目录并进入 mkdir my_rasa_bot cd my_rasa_bot # 2. 初始化 Rasa 项目会生成 data/, domain.yml 等基础文件 rasa init # 3. 创建虚拟环境注意必须用 python3.8Rasa 3.x 不支持 3.11 python3.9 -m venv .venv # 4. 激活虚拟环境 source .venv/bin/activate # macOS/Linux # 或 .venv\Scripts\activate.bat # Windows # 5. 安装 Rasa 和 SDK指定稳定版本 pip install rasa3.5.10 rasa-sdk0.37.2 # 6. 创建 actions 目录结构 mkdir -p actions touch actions/__init__.py touch actions/actions.py # 7. 创建 VS Code 配置目录 mkdir .vscode提示rasa init会自动创建一个简单的domain.yml和stories.yml里面包含了greet和goodbye的示例。这很好我们不需要从零开始。关键是它帮你建立了 Rasa 项目的最小可行骨架避免了手动创建几十个空文件的繁琐。4.2 第二步配置 VS Code 的 Python 解释器与调试器3分钟打开 VS Code用File Open Folder...打开my_rasa_bot目录。此时VS Code 右下角会弹出一个提示“Select Python Interpreter”。点击它然后在弹出的列表中选择你刚刚创建的.venv/bin/pythonmacOS/Linux或.venv\Scripts\python.exeWindows。VS Code 会自动在.vscode/settings.json中写入python.defaultInterpreterPath。接下来创建.vscode/launch.json{ version: 0.2.0, configurations: [ { name: Python: Attach to Rasa Actions, type: python, request: attach, connect: { host: localhost, port: 5678 }, pathMappings: [ { localRoot: ${workspaceFolder}, remoteRoot: ${workspaceFolder} } ], justMyCode: true } ] }这里的关键是port: 5678。这是 VS Code 调试器默认的 attach 端口。但rasa run actions默认并不开启调试端口。所以我们需要一个“桥梁”——ptvsd或debugpy。由于debugpy是微软官方维护、VS Code 原生支持的调试器我们选用它# 在已激活的 .venv 环境中安装 debugpy pip install debugpy现在debugpy已经安装好了但它还不会自动启动。我们需要修改actions.py的入口点让它在启动时主动监听调试端口。4.3 第三步修改actions.py以支持调试器 attach2分钟在actions/actions.py的最顶部import语句之前添加以下代码import os import debugpy # 如果环境变量 DEBUG_MODE 被设置则启动 debugpy 服务器 if os.getenv(DEBUG_MODE): debugpy.listen((localhost, 5678)) print(⏳ debugpy is listening on localhost:5678. Attach your debugger now!) # 可选让程序在此处暂停等待调试器连接 # debugpy.wait_for_client() # print( Debugger attached!)这段代码的意思是只有当系统环境变量DEBUG_MODE被设置时debugpy才会启动一个监听5678端口的调试服务器。这给了你完全的控制权平时开发你不用管它需要调试时你只需在启动rasa run actions前设置这个环境变量即可。debugpy.wait_for_client()是一个可选的阻塞调用它会让rasa run actions进程暂停直到你点击 VS Code 的“运行并调试”按钮并成功连接。这对于确保调试器一定在代码执行前就位非常有用但会稍微拖慢启动速度。我通常在首次调试时启用它确认流程无误后再注释掉。4.4 第四步启动 Rasa Action Server 并 attach 调试器3分钟现在打开一个新的终端窗口不要关闭之前的确保它也处于my_rasa_bot目录下并且.venv已激活。执行# 设置 DEBUG_MODE 环境变量 export DEBUG_MODE1 # macOS/Linux # 或 set DEBUG_MODE1 # Windows (cmd) # 或 $env:DEBUG_MODE1 # Windows (PowerShell) # 启动 Rasa Action Server rasa run actions --enable-api --cors * --debug你会看到终端输出⏳ debugpy is listening on localhost:5678. Attach your debugger now! ... Action endpoint is up and running on http://localhost:5055此时rasa run actions进程已经启动并且debugpy正在监听5678端口。现在回到 VS Code按下CtrlShiftDWindows/Linux或CmdShiftDmacOS打开“运行和调试”视图。在左上角的下拉菜单中选择Python: Attach to Rasa Actions然后点击绿色的“运行”三角形按钮。几秒钟后VS Code 底部状态栏会显示Debugging并且右上角会出现一个红色的“停止”按钮。恭喜你已经成功 attach 到了 action server 进程4.5 第五步实战调试一个自定义 Action10分钟让我们创建一个真实的、会出错的 action 来练习。编辑actions/actions.py添加一个故意会出错的类class ActionDivideNumbers(Action): def name(self) - Text: return action_divide_numbers def run( self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict[Text, Any], ) - List[Dict[Text, Any]]: # 从槽位提取两个数字 num1 tracker.get_slot(number_a) num2 tracker.get_slot(number_b) logger.debug(fAttempting to divide {num1} by {num2}) # 故意不处理除零错误 result num1 / num2 dispatcher.utter_message(textf计算结果是{result}) return [SlotSet(calculation_result, result)]然后在domain.yml的actions列表里添加action_divide_numbers并在slots里添加number_a和number_b。现在打开第三个终端启动 Rasa Corerasa shell在rasa shell的交互界面里输入hi What is 10 divided by 0?Rasa Core 会尝试调用action_divide_numbers但num2是0Python 会抛出ZeroDivisionError。此时回到 VS Code你会看到调试器已经自动停在了result num1 / num2这一行左侧的“变量”面板Variables里num1的值是10.0num2的值是0.0一目了然。你甚至可以把鼠标悬停在/运算符上VS Code 会提示“Division by zero”。这就是调试的魔力错误不再是一个模糊的ActionExecutionRejection日志而是一个精确到字符的、可交互的现场。4.6 第六步利用 VS Code 的高级调试功能8分钟VS Code 的调试器远不止“设断点”这么简单。在刚才的ZeroDivisionError断点处试试这些操作Watch 窗口在“调试”侧边栏的WATCH区域点击号输入type(num1)你会看到它返回class float再输入tracker.latest_message.get(intent, {}).get(name)你会看到当前意图是calculate。这是动态检查 tracker 状态的最快方式。调试控制台Debug Console在“调试”侧边栏底部切换到DEBUG CONSOLE标签页。在这里你可以像在 Python REPL 里一样直接执行任意 Python 表达式。例如输入10 / 0.1它会立刻返回100.0输入dir(tracker)它会列出 tracker 对象的所有可用方法。这比反复修改代码、重启服务要高效得多。条件断点右键点击断点左侧的红点选择Edit Breakpoint然后输入num2 0。这样断点只会在num2确实为0时才触发避免了在正常情况下被频繁打断。断点禁用/启用在“断点”侧边栏BREAKPOINTS你可以勾选或取消勾选某个断点而无需删除它。这对于临时屏蔽某个调试点非常有用。Step Over / Step Into当你的run()方法里调用了另一个函数比如self._fetch_from_api()按F10Step Over会执行完这一行跳到下一行按F11Step Into则会进入那个函数的内部让你可以逐行跟踪它的执行。这是理解第三方库或复杂逻辑的利器。4.7 第七步自动化调试流程5分钟手动设置环境变量、启动两个终端、再 attach 调试器重复多了也会累。我用 VS Code 的任务Tasks功能把它自动化。在.vscode/tasks.json中添加{ version: 2.0.0, tasks: [ { label: Start Rasa Actions (Debug), type: shell, command: DEBUG_MODE1 rasa run actions --enable-api --cors \*\ --debug, isBackground: true, problemMatcher: [], group: build } ] }然后在 VS Code 里按CtrlShiftPWindows/Linux或CmdShiftPmacOS输入Tasks: Run Task选择Start Rasa Actions (Debug)。VS Code 会自动在一个集成终端里启动 action server。你只需要再点击一次“运行并调试”按钮整个流程就完成了。更进一步你可以创建一个launch.json的复合配置Compound Configuration让 VS Code 一键启动 Rasa Core、Rasa Actions并 attach 调试器但这需要更复杂的进程管理对于大多数场景上面的两步法已经足够高效。5. 常见问题与排查技巧实录5.1 问题速查表那些让你抓狂的“Connection refused”现象最可能原因排查步骤解决方案rasa shell报错Connection refusedrasa run actions进程未启动或端口不匹配1. 在终端执行lsof -i :5055macOS/Linux或netstat -ano | findstr :5055Windows2. 检查endpoints.yml中的url是否为http://localhost:5055/webhook确保rasa run actions已成功启动检查endpoints.yml确保url字段拼写正确且协议为http不是httpsVS Code 显示Could not connect to debug adapterdebugpy未监听或端口被占用1. 检查启动rasa run actions的终端是否有⏳ debugpy is listening...输出2. 执行lsof -i :5678查看端口占用情况确认DEBUG_MODE环境变量已设置如果端口被占修改launch.json中的port为5679并在actions.py中同步修改debugpy.listen((localhost, 5679))断点变成空心圆Unverified breakpointVS Code 无法将源代码映射到正在运行的进程1. 检查launch.json中的pathMappings2. 确认 VS Code 当前打开的是项目根目录而非actions/子目录确保localRoot和remoteRoot都指向${workspaceFolder}绝对不要用相对路径如./actionsrasa run actions启动后立即退出无任何错误日志actions.py语法错误或rasa-sdk版本不兼容1. 在终端直接执行python actions/actions.py2. 检查pip list | grep rasa-sdk修复actions.py中的语法错误降级rasa-sdk到0.37.2调试器能 attach但断点不生效justMyCode设置为false或断点设在了import语句上1. 在launch.json中将justMyCode设为true2. 确保断点设在run()方法内部而非类定义或import行justMyCode: true是必须的它告诉调试器只关注你自己的代码忽略rasa-sdk的内部逻辑5.2 我踩过的三个深坑与独家避坑技巧坑一rasa run actions的--enable-api参数必须加这个参数的作用是启用一个额外的 HTTP API允许你通过curl手动触发 action例如curl -X POST http://localhost:5055/webhook -H Content-Type: application/json -d {sender: test, message: hi}。但更重要的是它是debugpy能够稳定工作的前提。我曾经在没有加--enable-api的情况下rasa run actions启动后debugpy的监听会很快超时并自动关闭。加上这个参数后action server 进入一个长连接的 HTTP 服务模式debugpy的监听才能持久化。这是一个 Rasa SDK 的内部机制官方文档并未明说但实测下来这是保证调试器长期在线的铁律。坑二Windows 用户的路径分隔符陷阱在 Windows 上rasa run actions默认会尝试在C:\Users\YourName\actions目录下查找actions.py而不是你项目里的.\actions\actions.py。这是因为 Windows 的环境变量路径分隔符是;而 Unix 是:rasa-sdk的路径解析逻辑在跨平台时有细微差异。解决方案是在启动命令前显式设置PYTHONPATH# Windows cmd set PYTHONPATH%cd%\actions rasa run actions --enable-api --cors * --debug这样rasa-sdk就会优先在你指定的actions目录下查找模块而不是去系统路径里瞎找。坑三tracker对象的“惰性求值”特性tracker对象里的很多属性比如tracker.latest_message、tracker.slots并不是在对象创建时就全部加载进内存的而是采用“惰性求值”Lazy Evaluation策略只有当你第一次访问它时才会去解析和构建。这意味着如果你在断点处把鼠标悬停在tracker上VS Code 可能只显示一个空的{}让你误以为 tracker 是空的。真正的技巧是在“调试控制台”里手动输入tracker.latest_message然后按回车。这时tracker才会真正执行它的propertygetter 方法把完整的latest_message字典打印出来。同理tracker.slots、tracker.events都适用此法。这是理解 Rasa 内部数据结构的关键心法。5.3 性能优化让调试不拖慢你的
VS Code 调试 Rasa 自定义动作:断点调试与变量监视实战
1. 项目概述用 VS Code 打造 Rasa 自定义动作开发与调试的“所见即所得”工作流我每天和 Rasa 打交道从写 domain.yml 的 intent 到调通一个带数据库查询的 custom action中间最耗神的环节从来不是逻辑设计而是“改完代码重启服务器curl 测试看日志再改……”这个循环。直到我把整个 custom action 开发流程迁进 VS Code——不是简单地用它写 Python 文件而是真正把它变成一个可断点、可变量监视、可单步执行、可复现错误上下文的完整调试环境。标题里说的 “How I Use VS Code To Develop And Debug Custom Actions In Rasa”核心就在这儿VS Code 不是编辑器而是 Rasa 动作服务的“驾驶舱”。它解决的不是“能不能跑”的问题而是“为什么没按预期跑”“变量在第几行变成了 None”“API 调用返回的 JSON 结构到底长什么样”这些真正在现场卡住你三小时的细节问题。如果你正被ActionExecutionRejection报错折磨或者每次改完actions.py都得手动 curl 七八个测试用例又或者你的 custom action 里嵌套了 requests、SQLAlchemy、甚至调用了另一个微服务那你就是这个工作流的精准目标用户。它不依赖 Docker Compose 的复杂编排虽然可以兼容也不要求你去啃 Rasa SDK 的源码调试器而是一套基于 VS Code 原生能力、Rasa 官方 SDK 接口规范和 Python 标准调试协议的轻量级组合方案。整套流程下来我平均把单个 custom action 的调试周期从 45 分钟压缩到 12 分钟以内关键在于——你能在函数刚被 Rasa 调用的那一刻就停在run()方法的第一行看着tracker里的latest_message是什么slots当前状态如何followup_action是否已被设置。这不是魔法是把本该透明的执行过程真正拉到你眼皮底下。2. 整体架构设计与工具链选型逻辑2.1 为什么放弃 Rasa X / Rasa Studio 的内置调试Rasa X 提供的 Web UI 确实能让你点几下就触发一个 action但它本质上是个黑盒你只能看到最终返回的events和responses看不到中间变量、无法设断点、不能 step into 你自己的数据库查询函数。更关键的是它的调试环境和你本地开发环境存在天然隔离——你在 X 里改的actions.py并不会实时同步到后端服务进程你得手动重启rasa run actions这直接破坏了“改-测-验”的节奏感。而 VS Code 的优势在于它完全运行在你的本地开发机上所有文件、依赖、环境变量都由你一手掌控调试器直接 attach 到你启动的 Python 进程变量栈帧一目了然。这不是“更高级”而是“更贴近真实执行路径”。2.2 为什么选择rasa run actions而非rasa shell --enable-api这是很多人踩的第一个坑。rasa shell启动的是一个集成了 NLU、Core 和 Action Server 的全功能服务它内部会 fork 出一个子进程来运行 action server但这个子进程的调试端口是随机分配且不对外暴露的。你根本没法让 VS Code 的 debugger attach 上去。而rasa run actions是 Rasa SDK 提供的专用动作服务启动命令它只做一件事监听http://localhost:5055/webhook接收 Rasa Core 发来的 action 请求并调用你actions.py中定义的类方法。更重要的是它支持--enable-api参数允许你通过 HTTP 接口手动触发 action这为单元测试和边界条件验证提供了极大便利它还支持--debug模式输出详细的请求/响应日志。最关键的是它是一个标准的 Python 主进程VS Code 的 Python Debugger 可以毫无障碍地 attach 到它身上——这才是整个调试链路成立的前提。2.3 为什么坚持使用venv而非 conda 或全局 PythonRasa 对 Python 版本和依赖库版本极其敏感。我在一个项目中用 conda 创建了python3.9环境安装了rasa3.5.0结果发现rasa-sdk的某个底层依赖aiohttp在 conda channel 里的版本和 pip 官方源不一致导致 action server 启动时抛出AttributeError: module aiohttp has no attribute TCPConnector。这种问题排查起来极其痛苦。而venv是 Python 官方标准pip install的行为完全可预测。我现在的标准操作是在项目根目录下执行python -m venv .venv然后source .venv/bin/activatemacOS/Linux或.venv\Scripts\activate.batWindows最后pip install rasa rasa-sdk。这样做的好处是.venv目录和requirements.txt文件一起提交到 Git任何新加入的同事拉下代码执行source .venv/bin/activate pip install -r requirements.txt就能获得和我完全一致的运行环境。VS Code 会自动识别项目根目录下的.venv并在右下角 Python 解释器选择器里显示它这是后续所有调试配置生效的基础。2.4 为什么调试配置必须用attach模式而非launchVS Code 的 Python 调试有两种主流模式launch启动一个新进程并调试和attach连接到一个已运行的进程并调试。对于 Rasa action server我们必须用attach。原因很简单rasa run actions启动的服务需要绑定到5055端口如果 VS Code 用launch模式去启动它那么每次调试都要重新启动服务而 Rasa Core运行在另一个终端会因为连接中断而报错Connection refused。更糟的是launch模式无法精确控制启动参数比如你无法在launch.json里优雅地指定--cors *或--enable-api。而attach模式则完美规避了这些问题你先在终端里用你喜欢的方式bash脚本、Makefile 或直接敲命令启动rasa run actions确保它稳定运行然后在 VS Code 里点击“运行并调试”选择Python: Attach配置Debugger 就会连接到那个已经存在的进程。此时你可以在任意actions.py的代码行设断点只要 Rasa Core 触发了对应 action执行流就会立刻停在那里。这是一种“服务常驻、调试按需”的生产级思路而不是“每次调试都重启服务”的开发级思路。2.5 为什么推荐rasa-sdk的0.37.x系列而非最新版Rasa SDK 的版本迭代非常快但并非每个新版都向后兼容。我在升级到rasa-sdk3.0.0后发现自定义 action 类的name()方法签名发生了变化从def name(self) - Text:变成了def name(self) - Text: ...但 Rasa Core 的调用逻辑还没跟上导致ActionNotFound错误。经过比对官方文档的变更日志我发现0.37.x系列如0.37.2是最后一个与rasa3.5.x系列完全匹配的 SDK 版本。它的 API 稳定文档齐全社区案例丰富。更重要的是它的ActionRunner内部实现清晰当你在 VS Code 里打断点时能看到完整的调用栈ActionRunner.run()→YourCustomAction.run()→your_database_query()每一层都干净利落。而新版 SDK 引入了异步async def run()虽然性能更好但对初学者来说调试器在await处的跳转逻辑会变得复杂容易迷失在事件循环里。所以我的经验是除非你明确需要新版的某项特性比如对 FastAPI 的原生支持否则 stick withrasa-sdk0.37.2rasa3.5.10这个黄金组合稳定性远胜于盲目追新。3. 核心细节解析与实操要点3.1 项目结构标准化让 VS Code 自动识别一切一个混乱的项目结构是调试失败的温床。我强制要求所有 Rasa 项目遵循以下根目录结构my_rasa_project/ ├── .vscode/ # VS Code 专属配置 │ ├── settings.json # 编辑器偏好设置 │ └── launch.json # 调试配置 ├── actions/ # custom action 代码存放处关键 │ ├── __init__.py │ └── actions.py # 所有自定义 action 类的定义 ├── data/ # NLU 和 stories 数据 ├── domain.yml # domain 定义 ├── config.yml # pipeline 配置 ├── credentials.yml # 连接外部服务的凭证 ├── endpoints.yml # action server 地址配置 ├── models/ # 训练好的模型 ├── .venv/ # Python 虚拟环境 └── requirements.txt # 依赖清单为什么actions/目录必须独立因为rasa run actions命令默认会在当前工作目录下查找actions/子目录。如果你把actions.py放在项目根目录它会找不到。更重要的是VS Code 的 Python 扩展会扫描项目根目录下的actions/并将其识别为一个可导入的 Python 包。这意味着当你在actions.py里写from actions.utils import db_connect时VS Code 不会报Import actions.utils could not be resolved的红色波浪线。而endpoints.yml的内容必须严格如下action_endpoint: url: http://localhost:5055/webhook这是 Rasa Core 和 Action Server 之间的“握手协议”。如果这里写成http://127.0.0.1:5055/webhook在某些网络环境下尤其是 Docker 网络Core 可能无法解析localhost导致Connection refused。localhost是唯一被所有环境一致认可的回环地址。3.2 VS Code Python 扩展配置不只是装个插件那么简单仅仅安装 Python 扩展是远远不够的。你需要在.vscode/settings.json中进行精细化配置{ python.defaultInterpreterPath: ./.venv/bin/python, python.testing.pytestArgs: [ tests/ ], python.testing.pytestEnabled: true, python.formatting.provider: black, python.linting.enabled: true, python.linting.pylintEnabled: true, python.analysis.extraPaths: [actions] }其中python.defaultInterpreterPath指向你的虚拟环境解释器这是 VS Code 能正确解析import语句和提供代码补全的前提。python.analysis.extraPaths是关键中的关键它告诉 VS Code 的语言服务器“actions” 这个包不在标准路径下而是在项目根目录的actions子目录里请把它加入分析路径。没有这一行你在actions.py里写的from actions.db import get_user_by_idVS Code 会认为actions.db是一个不存在的模块所有补全和跳转功能全部失效。python.testing.pytestArgs则为你后续编写 action 单元测试铺平了道路——你可以直接在 VS Code 的测试侧边栏里运行单个测试用例而无需切到终端。3.3actions.py的编写规范让调试器“看得懂”你的代码一个写得“调试友好”的actions.py其核心是显式化所有依赖和状态。我坚决反对在run()方法里直接写requests.get(https://api.example.com)。正确的做法是# actions/actions.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 import logging logger logging.getLogger(__name__) class ActionFetchUserProfile(Action): def name(self) - Text: return action_fetch_user_profile def run( self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict[Text, Any], ) - List[Dict[Text, Any]]: # 1. 显式提取关键输入 user_id tracker.get_slot(user_id) logger.debug(fFetching profile for user_id: {user_id}) # 2. 显式调用封装好的服务函数 try: profile_data self._fetch_from_api(user_id) except Exception as e: logger.error(fAPI call failed for user_id {user_id}: {e}) dispatcher.utter_message(text抱歉获取用户信息时出了点问题。) return [] # 3. 显式构造返回事件 events [] if profile_data: events.append(SlotSet(user_name, profile_data.get(name, ))) events.append(SlotSet(user_email, profile_data.get(email, ))) dispatcher.utter_message(textf你好{profile_data.get(name, 朋友)}) else: dispatcher.utter_message(text未找到该用户信息。) return events def _fetch_from_api(self, user_id: str) - Dict[Text, Any]: # 这里才是真正的 API 调用逻辑 # 你可以在这里轻松设断点查看 request URL、headers、response status code import requests response requests.get(fhttps://api.example.com/users/{user_id}) response.raise_for_status() return response.json()为什么这样写因为当你在_fetch_from_api方法的第一行设断点时调试器会停在那里你可以鼠标悬停在user_id上看到它的值是U12345你可以展开requests模块看到它的__version__你甚至可以打开调试控制台手动执行response requests.get(...)观察返回的response对象。而如果把所有逻辑都塞进run()断点一设你面对的就是一个巨大的、混杂着业务逻辑、NLU 解析、事件构造的“大泥球”根本分不清哪一行在处理什么。3.4endpoints.yml与credentials.yml的安全实践别把密钥写死在代码里credentials.yml是 Rasa 连接 Slack、Telegram 等渠道的凭证文件但它同样可以用来存放 action server 的内部配置。我习惯在credentials.yml里添加一个action_serversection# credentials.yml action_server: api_key: ${ACTION_SERVER_API_KEY} database_url: ${DATABASE_URL}然后在actions.py中这样读取import os from rasa_sdk import Action from rasa_sdk.events import SlotSet class ActionQueryDatabase(Action): def name(self) - Text: return action_query_database def run(self, dispatcher, tracker, domain): # 从环境变量读取而非硬编码 db_url os.getenv(DATABASE_URL) or domain.get(config, {}).get(database_url) # ... 后续数据库操作这样做的好处是双重的第一ACTION_SERVER_API_KEY和DATABASE_URL这些敏感信息可以放在.env文件里VS Code 的 Python 扩展会自动加载.env永远不会进入 Git第二domain.get(config, {})提供了一个 fallback 机制你可以在domain.yml的config下添加测试用的 mock 数据库 URL方便在 CI 环境中运行测试。endpoints.yml则永远只包含url绝不包含任何认证信息因为它本质上只是一个路由配置不是凭证存储。3.5 日志级别与格式化让调试信息成为你的“第三只眼”Rasa 默认的日志级别是INFO这对于调试 custom action 来说信息量严重不足。你需要在actions.py的顶部添加import logging logging.basicConfig( levellogging.DEBUG, format%(asctime)s - %(name)s - %(levelname)s - %(message)s, handlers[ logging.StreamHandler(), # 输出到终端 logging.FileHandler(actions_debug.log) # 同时写入文件 ] ) logger logging.getLogger(__name__)然后在run()方法的关键节点插入logger.debug()logger.debug(fTracker state: {tracker.current_state()})logger.debug(fSlots before action: {tracker.slots})logger.debug(fLatest message entities: {tracker.latest_message.get(entities, [])})这些日志不会污染你的rasa shell终端输出因为它们属于actions进程但会清晰地打印在你启动rasa run actions的那个终端里。更重要的是当你在 VS Code 里 attach 调试器时这些logger.debug的输出会实时出现在 VS Code 的“调试控制台”Debug Console里和你的断点调试形成互补断点告诉你“此刻变量是什么”日志告诉你“之前发生了什么之后将要发生什么”。这是一种立体化的调试视角。4. 实操过程与核心环节实现4.1 第一步初始化项目与虚拟环境5分钟打开终端导航到你的工作目录执行以下命令# 1. 创建项目目录并进入 mkdir my_rasa_bot cd my_rasa_bot # 2. 初始化 Rasa 项目会生成 data/, domain.yml 等基础文件 rasa init # 3. 创建虚拟环境注意必须用 python3.8Rasa 3.x 不支持 3.11 python3.9 -m venv .venv # 4. 激活虚拟环境 source .venv/bin/activate # macOS/Linux # 或 .venv\Scripts\activate.bat # Windows # 5. 安装 Rasa 和 SDK指定稳定版本 pip install rasa3.5.10 rasa-sdk0.37.2 # 6. 创建 actions 目录结构 mkdir -p actions touch actions/__init__.py touch actions/actions.py # 7. 创建 VS Code 配置目录 mkdir .vscode提示rasa init会自动创建一个简单的domain.yml和stories.yml里面包含了greet和goodbye的示例。这很好我们不需要从零开始。关键是它帮你建立了 Rasa 项目的最小可行骨架避免了手动创建几十个空文件的繁琐。4.2 第二步配置 VS Code 的 Python 解释器与调试器3分钟打开 VS Code用File Open Folder...打开my_rasa_bot目录。此时VS Code 右下角会弹出一个提示“Select Python Interpreter”。点击它然后在弹出的列表中选择你刚刚创建的.venv/bin/pythonmacOS/Linux或.venv\Scripts\python.exeWindows。VS Code 会自动在.vscode/settings.json中写入python.defaultInterpreterPath。接下来创建.vscode/launch.json{ version: 0.2.0, configurations: [ { name: Python: Attach to Rasa Actions, type: python, request: attach, connect: { host: localhost, port: 5678 }, pathMappings: [ { localRoot: ${workspaceFolder}, remoteRoot: ${workspaceFolder} } ], justMyCode: true } ] }这里的关键是port: 5678。这是 VS Code 调试器默认的 attach 端口。但rasa run actions默认并不开启调试端口。所以我们需要一个“桥梁”——ptvsd或debugpy。由于debugpy是微软官方维护、VS Code 原生支持的调试器我们选用它# 在已激活的 .venv 环境中安装 debugpy pip install debugpy现在debugpy已经安装好了但它还不会自动启动。我们需要修改actions.py的入口点让它在启动时主动监听调试端口。4.3 第三步修改actions.py以支持调试器 attach2分钟在actions/actions.py的最顶部import语句之前添加以下代码import os import debugpy # 如果环境变量 DEBUG_MODE 被设置则启动 debugpy 服务器 if os.getenv(DEBUG_MODE): debugpy.listen((localhost, 5678)) print(⏳ debugpy is listening on localhost:5678. Attach your debugger now!) # 可选让程序在此处暂停等待调试器连接 # debugpy.wait_for_client() # print( Debugger attached!)这段代码的意思是只有当系统环境变量DEBUG_MODE被设置时debugpy才会启动一个监听5678端口的调试服务器。这给了你完全的控制权平时开发你不用管它需要调试时你只需在启动rasa run actions前设置这个环境变量即可。debugpy.wait_for_client()是一个可选的阻塞调用它会让rasa run actions进程暂停直到你点击 VS Code 的“运行并调试”按钮并成功连接。这对于确保调试器一定在代码执行前就位非常有用但会稍微拖慢启动速度。我通常在首次调试时启用它确认流程无误后再注释掉。4.4 第四步启动 Rasa Action Server 并 attach 调试器3分钟现在打开一个新的终端窗口不要关闭之前的确保它也处于my_rasa_bot目录下并且.venv已激活。执行# 设置 DEBUG_MODE 环境变量 export DEBUG_MODE1 # macOS/Linux # 或 set DEBUG_MODE1 # Windows (cmd) # 或 $env:DEBUG_MODE1 # Windows (PowerShell) # 启动 Rasa Action Server rasa run actions --enable-api --cors * --debug你会看到终端输出⏳ debugpy is listening on localhost:5678. Attach your debugger now! ... Action endpoint is up and running on http://localhost:5055此时rasa run actions进程已经启动并且debugpy正在监听5678端口。现在回到 VS Code按下CtrlShiftDWindows/Linux或CmdShiftDmacOS打开“运行和调试”视图。在左上角的下拉菜单中选择Python: Attach to Rasa Actions然后点击绿色的“运行”三角形按钮。几秒钟后VS Code 底部状态栏会显示Debugging并且右上角会出现一个红色的“停止”按钮。恭喜你已经成功 attach 到了 action server 进程4.5 第五步实战调试一个自定义 Action10分钟让我们创建一个真实的、会出错的 action 来练习。编辑actions/actions.py添加一个故意会出错的类class ActionDivideNumbers(Action): def name(self) - Text: return action_divide_numbers def run( self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict[Text, Any], ) - List[Dict[Text, Any]]: # 从槽位提取两个数字 num1 tracker.get_slot(number_a) num2 tracker.get_slot(number_b) logger.debug(fAttempting to divide {num1} by {num2}) # 故意不处理除零错误 result num1 / num2 dispatcher.utter_message(textf计算结果是{result}) return [SlotSet(calculation_result, result)]然后在domain.yml的actions列表里添加action_divide_numbers并在slots里添加number_a和number_b。现在打开第三个终端启动 Rasa Corerasa shell在rasa shell的交互界面里输入hi What is 10 divided by 0?Rasa Core 会尝试调用action_divide_numbers但num2是0Python 会抛出ZeroDivisionError。此时回到 VS Code你会看到调试器已经自动停在了result num1 / num2这一行左侧的“变量”面板Variables里num1的值是10.0num2的值是0.0一目了然。你甚至可以把鼠标悬停在/运算符上VS Code 会提示“Division by zero”。这就是调试的魔力错误不再是一个模糊的ActionExecutionRejection日志而是一个精确到字符的、可交互的现场。4.6 第六步利用 VS Code 的高级调试功能8分钟VS Code 的调试器远不止“设断点”这么简单。在刚才的ZeroDivisionError断点处试试这些操作Watch 窗口在“调试”侧边栏的WATCH区域点击号输入type(num1)你会看到它返回class float再输入tracker.latest_message.get(intent, {}).get(name)你会看到当前意图是calculate。这是动态检查 tracker 状态的最快方式。调试控制台Debug Console在“调试”侧边栏底部切换到DEBUG CONSOLE标签页。在这里你可以像在 Python REPL 里一样直接执行任意 Python 表达式。例如输入10 / 0.1它会立刻返回100.0输入dir(tracker)它会列出 tracker 对象的所有可用方法。这比反复修改代码、重启服务要高效得多。条件断点右键点击断点左侧的红点选择Edit Breakpoint然后输入num2 0。这样断点只会在num2确实为0时才触发避免了在正常情况下被频繁打断。断点禁用/启用在“断点”侧边栏BREAKPOINTS你可以勾选或取消勾选某个断点而无需删除它。这对于临时屏蔽某个调试点非常有用。Step Over / Step Into当你的run()方法里调用了另一个函数比如self._fetch_from_api()按F10Step Over会执行完这一行跳到下一行按F11Step Into则会进入那个函数的内部让你可以逐行跟踪它的执行。这是理解第三方库或复杂逻辑的利器。4.7 第七步自动化调试流程5分钟手动设置环境变量、启动两个终端、再 attach 调试器重复多了也会累。我用 VS Code 的任务Tasks功能把它自动化。在.vscode/tasks.json中添加{ version: 2.0.0, tasks: [ { label: Start Rasa Actions (Debug), type: shell, command: DEBUG_MODE1 rasa run actions --enable-api --cors \*\ --debug, isBackground: true, problemMatcher: [], group: build } ] }然后在 VS Code 里按CtrlShiftPWindows/Linux或CmdShiftPmacOS输入Tasks: Run Task选择Start Rasa Actions (Debug)。VS Code 会自动在一个集成终端里启动 action server。你只需要再点击一次“运行并调试”按钮整个流程就完成了。更进一步你可以创建一个launch.json的复合配置Compound Configuration让 VS Code 一键启动 Rasa Core、Rasa Actions并 attach 调试器但这需要更复杂的进程管理对于大多数场景上面的两步法已经足够高效。5. 常见问题与排查技巧实录5.1 问题速查表那些让你抓狂的“Connection refused”现象最可能原因排查步骤解决方案rasa shell报错Connection refusedrasa run actions进程未启动或端口不匹配1. 在终端执行lsof -i :5055macOS/Linux或netstat -ano | findstr :5055Windows2. 检查endpoints.yml中的url是否为http://localhost:5055/webhook确保rasa run actions已成功启动检查endpoints.yml确保url字段拼写正确且协议为http不是httpsVS Code 显示Could not connect to debug adapterdebugpy未监听或端口被占用1. 检查启动rasa run actions的终端是否有⏳ debugpy is listening...输出2. 执行lsof -i :5678查看端口占用情况确认DEBUG_MODE环境变量已设置如果端口被占修改launch.json中的port为5679并在actions.py中同步修改debugpy.listen((localhost, 5679))断点变成空心圆Unverified breakpointVS Code 无法将源代码映射到正在运行的进程1. 检查launch.json中的pathMappings2. 确认 VS Code 当前打开的是项目根目录而非actions/子目录确保localRoot和remoteRoot都指向${workspaceFolder}绝对不要用相对路径如./actionsrasa run actions启动后立即退出无任何错误日志actions.py语法错误或rasa-sdk版本不兼容1. 在终端直接执行python actions/actions.py2. 检查pip list | grep rasa-sdk修复actions.py中的语法错误降级rasa-sdk到0.37.2调试器能 attach但断点不生效justMyCode设置为false或断点设在了import语句上1. 在launch.json中将justMyCode设为true2. 确保断点设在run()方法内部而非类定义或import行justMyCode: true是必须的它告诉调试器只关注你自己的代码忽略rasa-sdk的内部逻辑5.2 我踩过的三个深坑与独家避坑技巧坑一rasa run actions的--enable-api参数必须加这个参数的作用是启用一个额外的 HTTP API允许你通过curl手动触发 action例如curl -X POST http://localhost:5055/webhook -H Content-Type: application/json -d {sender: test, message: hi}。但更重要的是它是debugpy能够稳定工作的前提。我曾经在没有加--enable-api的情况下rasa run actions启动后debugpy的监听会很快超时并自动关闭。加上这个参数后action server 进入一个长连接的 HTTP 服务模式debugpy的监听才能持久化。这是一个 Rasa SDK 的内部机制官方文档并未明说但实测下来这是保证调试器长期在线的铁律。坑二Windows 用户的路径分隔符陷阱在 Windows 上rasa run actions默认会尝试在C:\Users\YourName\actions目录下查找actions.py而不是你项目里的.\actions\actions.py。这是因为 Windows 的环境变量路径分隔符是;而 Unix 是:rasa-sdk的路径解析逻辑在跨平台时有细微差异。解决方案是在启动命令前显式设置PYTHONPATH# Windows cmd set PYTHONPATH%cd%\actions rasa run actions --enable-api --cors * --debug这样rasa-sdk就会优先在你指定的actions目录下查找模块而不是去系统路径里瞎找。坑三tracker对象的“惰性求值”特性tracker对象里的很多属性比如tracker.latest_message、tracker.slots并不是在对象创建时就全部加载进内存的而是采用“惰性求值”Lazy Evaluation策略只有当你第一次访问它时才会去解析和构建。这意味着如果你在断点处把鼠标悬停在tracker上VS Code 可能只显示一个空的{}让你误以为 tracker 是空的。真正的技巧是在“调试控制台”里手动输入tracker.latest_message然后按回车。这时tracker才会真正执行它的propertygetter 方法把完整的latest_message字典打印出来。同理tracker.slots、tracker.events都适用此法。这是理解 Rasa 内部数据结构的关键心法。5.3 性能优化让调试不拖慢你的