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 或全局 Python?
Rasa 对 Python 版本和依赖库版本极其敏感。我在一个项目中用 conda 创建了python=3.9环境,安装了rasa==3.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/activate(macOS/Linux)或.venv\Scripts\activate.bat(Windows),最后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模式而非launch?
VS 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-sdk==3.0.0后,发现自定义 action 类的name()方法签名发生了变化,从def name(self) -> Text:变成了def name(self) -> Text: ...,但 Rasa Core 的调用逻辑还没跟上,导致ActionNotFound错误。经过比对官方文档的变更日志,我发现0.37.x系列(如0.37.2)是最后一个与rasa==3.5.x系列完全匹配的 SDK 版本。它的 API 稳定,文档齐全,社区案例丰富。更重要的是,它的ActionRunner内部实现清晰,当你在 VS Code 里打断点时,能看到完整的调用栈:ActionRunner.run()→YourCustomAction.run()→your_database_query(),每一层都干净利落。而新版 SDK 引入了异步async def run(),虽然性能更好,但对初学者来说,调试器在await处的跳转逻辑会变得复杂,容易迷失在事件循环里。所以我的经验是:除非你明确需要新版的某项特性(比如对 FastAPI 的原生支持),否则 stick withrasa-sdk==0.37.2+rasa==3.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_id,VS 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(f"Fetching profile for user_id: {user_id}") # 2. 显式调用封装好的服务函数 try: profile_data = self._fetch_from_api(user_id) except Exception as e: logger.error(f"API 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(text=f"你好,{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(f"https://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( level=logging.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(f"Tracker state: {tracker.current_state()}")logger.debug(f"Slots before action: {tracker.slots}")logger.debug(f"Latest 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.8+,Rasa 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 rasa==3.5.10 rasa-sdk==0.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/python(macOS/Linux)或.venv\Scripts\python.exe(Windows)。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以支持调试器 attach(2分钟)
在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_MODE=1 # macOS/Linux # 或 set DEBUG_MODE=1 # Windows (cmd) # 或 $env:DEBUG_MODE="1" # 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,按下Ctrl+Shift+D(Windows/Linux)或Cmd+Shift+D(macOS)打开“运行和调试”视图。在左上角的下拉菜单中,选择Python: Attach to Rasa Actions,然后点击绿色的“运行”三角形按钮。几秒钟后,VS Code 底部状态栏会显示Debugging,并且右上角会出现一个红色的“停止”按钮。恭喜,你已经成功 attach 到了 action server 进程!
4.5 第五步:实战调试一个自定义 Action(10分钟)
让我们创建一个真实的、会出错的 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(f"Attempting to divide {num1} by {num2}") # 故意不处理除零错误 result = num1 / num2 dispatcher.utter_message(text=f"计算结果是:{result}") return [SlotSet("calculation_result", result)]然后,在domain.yml的actions列表里添加"action_divide_numbers",并在slots里添加number_a和number_b。现在,打开第三个终端,启动 Rasa Core:
rasa shell在rasa shell的交互界面里,输入:
hi What is 10 divided by 0?Rasa Core 会尝试调用action_divide_numbers,但num2是0,Python 会抛出ZeroDivisionError。此时,回到 VS Code,你会看到调试器已经自动停在了result = num1 / num2这一行!左侧的“变量”面板(Variables)里,num1的值是10.0,num2的值是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()),按F10(Step Over)会执行完这一行,跳到下一行;按F11(Step 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_MODE=1 rasa run actions --enable-api --cors \"*\" --debug", "isBackground": true, "problemMatcher": [], "group": "build" } ] }然后,在 VS Code 里按Ctrl+Shift+P(Windows/Linux)或Cmd+Shift+P(macOS),输入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 refused | rasa run actions进程未启动,或端口不匹配 | 1. 在终端执行lsof -i :5055(macOS/Linux)或netstat -ano | findstr :5055(Windows)2. 检查 endpoints.yml中的url是否为http://localhost:5055/webhook | 确保rasa run actions已成功启动;检查endpoints.yml,确保url字段拼写正确,且协议为http(不是https) |
VS Code 显示Could not connect to debug adapter | debugpy未监听,或端口被占用 | 1. 检查启动rasa run actions的终端,是否有⏳ debugpy is listening...输出2. 执行 lsof -i :5678查看端口占用情况 | 确认DEBUG_MODE环境变量已设置;如果端口被占,修改launch.json中的port为5679,并在actions.py中同步修改debugpy.listen(("localhost", 5679)) |
| 断点变成空心圆(Unverified breakpoint) | VS Code 无法将源代码映射到正在运行的进程 | 1. 检查launch.json中的pathMappings2. 确认 VS Code 当前打开的是项目根目录,而非 actions/子目录 | 确保localRoot和remoteRoot都指向${workspaceFolder};绝对不要用相对路径如./actions |
rasa 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 内部数据结构的关键心法。
