关于 catalyst-workflow-scaffold
catalyst-workflow-scaffold 是一种在 Catalyst 上搭建 Dapr Workflow 的技能,涵盖识别仓库语言、启动运行实例并读取状态。它强调可安全重放的工作流设计,并要求验证实际运行,而非止步于代码编译。
主要功能
- 01识别语言并选择对应的 Dapr SDK
- 02指导使用 Catalyst 的 default 项目
- 03采用可安全重放的工作流设计
- 04启动并检查工作流运行实例
- 05提供控制台链接或运行标识
适用场景
- 01在现有仓库中搭建工作流
- 02避免与重放有关的工作流缺陷
- 03验证工作流已在 Catalyst 启动
MCP 相关
该技能通过 Catalyst MCP 工具执行项目和应用操作、启动工作流实例并读取状态。Catalyst 操作仅使用可用的 MCP 工具。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ diagridio/ catalyst-ai --skill catalyst-workflow-scaffold安装后在 Claude Code 中输入/catalyst-workflow-scaffold确认已加载。
详细介绍
在 Catalyst 上搭建 Dapr 工作流
目标:完成此技能时,工作流已完成注册、已在 Catalyst 上启动一次,并已读取其状态。能编译但从未启动的代码不算完成搭建——这里大多数问题都会在首次实际运行时出现。
1. 检测语言,不要询问
语言是此技能的一个参数,而不是它的变体。请先检查仓库。
| 顶层找到的文件 | 语言 | 工作流 SDK |
|---|---|---|
pyproject.toml、requirements.txt、*.py | Python | dapr + dapr-ext-workflow |
*.csproj、*.sln、global.json | .NET | Dapr.Workflow |
pom.xml、build.gradle | Java | io.dapr:dapr-sdk-workflows |
go.mod | Go | github.com/dapr/durabletask-go |
package.json、tsconfig.json | TypeScript / JavaScript | @dapr/dapr |
只有在目录为空,或者其中有两个这类项目文件并列、工作流可能合理地属于任意一个时,才询问。猜错会导致项目无法构建。
2. 添加 Dapr SDK,而不是 Diagrid 适配器
Dapr 工作流需要使用与检测到的语言对应的 Dapr SDK。这五种语言的 SDK 均已发布。Diagrid 的 *-ai 包是 Agent 框架桥接包,此处不需要——它们适用于搭建 Agent 的流程。
| 语言 | 坐标 | 已验证的最新稳定版本 |
|---|---|---|
| Python | dapr、dapr-ext-workflow | 1.18.3 |
| .NET | Dapr.Workflow | 1.18.5 |
| Java | io.dapr:dapr-sdk-workflows | 1.18.1 |
| Go | github.com/dapr/durabletask-go | v0.14.0 |
| TypeScript | @dapr/dapr | 3.18.0 |
以下三个坐标陷阱都曾让人踩坑:
- Go 工作流编写不属于
github.com/dapr/go-sdk/workflow。 该包存在于 go-sdk v1.10.0 至 v1.13.0,并在 v1.14.0 中被移除,这就是过时教程仍让你导入它、而如今对它执行go get会失败的原因。编写工作流时,导入github.com/dapr/durabletask-go/workflow;使用 Dapr API 时,导入github.com/dapr/go-sdk/client。 Dapr.Workflow1.19.0 仅供预览。 请锁定最新稳定版本,而不是最新版本。- 不要为工作流编造 Diagrid 包。 不存在
Diagrid.Agents.Workflow,任何注册表中也没有 Diagrid 工作流包。
绝不要使用未经你亲眼确认能解析的坐标。无效的包 ID 会让用户去排查一个根本不存在的 Catalyst 问题,这比没有技能更糟。
3. 使用名为 default 的项目
不要创建项目。 每个组织都有一个名为 default 的项目,其中已附带托管 pub/sub、托管 KV 存储、工作流存储和 Agent 基础设施。工作流历史记录存储在该托管存储中,因此手动创建项目是新搭建的工作流能够启动却始终不显示的最常见原因。
组织首次完成协调时会引导创建 default,如果有人删除了它,它不会重新创建。请使用 catalyst_list_projects 确认它存在,不要想当然;如果它已不存在,请询问应改用哪个项目,而不是创建一个。当前没有默认项目,因此每次调用都要传入项目。
托管组件的名称是固定的——代理为 pubsub,KV 存储为 kvstore。托管工作流存储会自动接入,不具备组件名称。Agent 基础设施随托管 KV 存储一同提供。
无论何种套餐,一个项目都只拥有一个托管 pub/sub 和一个托管 KV 存储,升级套餐也不会增加数量,因此不要将升级作为解决方案。请将工作分散到同一个代理上的多个主题中。这些是按组织叠加的套餐额度,而不是代码中的常量,因此请使用 catalyst_get_usage 读取实时配额,不要断言数量为 1;也不要承诺可以为用户提高额度,因为这是商业问题,不是你能回答的问题。
4. 编写工作流,确保重放时结果不变
每次重放都会从头重新执行工作流正文。任何第二次得出不同结果的操作都会破坏运行。让正文成为历史记录的纯函数,并将所有副作用放入活动中。
| 工作流正文中的风险 | 重放时的影响 | 应改为这样编写 |
|---|---|---|
读取墙上时钟(now、UtcNow、time.Now) | 每次重放都会得到新值 | 使用上下文的当前时间和持久化计时器 API |
random、新 UUID | 与记录的历史记录不一致 | 作为输入传入,或在活动中生成 |
| HTTP、数据库和 Dapr 调用 | 每次重放都会重新触发 | 移入活动 |
| 遍历映射或字典 | 重放时顺序不稳定 | 先对键排序,再遍历 |
| 环境变量、文件、全局变量、静态变量 | 主机状态会在重放之间变化 | 作为输入传入 |
sleep、线程、goroutine、Task.Delay | 不会记录到历史记录中 | 使用上下文的持久化计时器 |
活动采用至少一次执行:一个活动可能会针对同一个逻辑步骤运行两次。请确保每个活动都具备幂等性,以实例 ID 加稳定的步骤名称作为键,并且绝不要让同一工作流中的两个活动同时负责同一项写入。
当有实例仍在运行时,重命名、重新排序或移除工作流中的活动,会导致旧历史记录被新代码重放,从而使运行失败。请改用新的工作流名称,并让旧实例自然完成。工作流版本控制不会自动为你解决这个问题。
5. 对照 Catalyst 运行
- 确保应用已存在。 对项目和应用名称调用
catalyst_get_app;如果应用不存在,则通过catalyst_apply创建一个App(先阅读catalyst_get_resource_schema,使用dry_run运行并展示给用户,然后再应用)。需要写入角色:如果工具列表中没有catalyst_apply,请说明并停止。 - 运行工作进程。
catalyst-develop的第 3 节是唯一说明如何将catalyst_get_connection返回的连接值设为环境变量并启动进程的位置。请按该说明操作,不要自行发挥。
纯工作流工作进程不需要应用端口。 它会主动连接 Catalyst 并轮询工作项,因此无需暴露入站端点,也不应有任何服务在监听。没有已注册端点的应用才是工作进程的正确配置。若仍为其配置端点(启用健康检查的应用端点,或隧道上的 --app-port),Catalyst 会在该端点对应用进行健康检查,却发现没有任何响应,因此不会启动工作流:每次启动都会失败,并提示 failed to create workflow instance: context canceled。
不要为了满足一个并不需要的端口而虚构 HTTP 服务器。这是首次运行时常见的失败情况,而且报错令人困惑——工作进程成功注册工作流和所有活动后,却因端口已被其他程序占用而在绑定时崩溃,因此日志会显示启动正常,随后又出现一个与工作流毫无关系的错误。只有在 Catalyst 会主动调用应用时,才为应用配置端点:服务调用、发布/订阅投递或 Agent 端点。
应用、Agent 和 MCP 服务各自都由一个身份标识(某些 API 中称为“App ID”)支持。工具请求或返回 appId 时,指的就是该身份的名称。请从资源的 status.appIds 中读取(get 工具会包含该字段)。如果 Agent 是通过你自己的代码注册的,则该名称是其注册记录中的 appId。
请合理规划身份配额。每个应用、Agent 和 MCP 服务都会占用一个身份,计入每个区域的配额;可通过 catalyst_get_usage 查看。为工作流搭建一个应用,只有在用户提出要求时才添加更多。工作流本身不计入配额——实例免费,应用不免费。
6. 声称成功之前,先回读结果
启动一个实例并检查:
- 使用
catalyst_start_workflow,指定项目、应用标识和工作流名称;如果工作流需要输入,也要提供输入。该工具会分配并返回运行 ID;请妥善保存。 - 如果丢失了运行 ID,使用
catalyst_list_workflow_runs查询项目中的工作流运行。 - 使用
catalyst_get_workflow_run查询该运行:查看执行图,以及当前所在步骤或失败的步骤。
catalyst_start_workflow 是写入工具,对于无写入权限的角色不可用。
工作流探索仅适用于托管工作流存储;这是继续使用 default 的另一个理由。
字段缺失不等于字段为空。 隐去载荷时,会删除对应键,而不是返回空值。在默认的 metadata 数据共享级别下,运行中的 input、output 和 customStatus 都会被移除。只有组织管理员可以将级别提升到 full,你无法在此处更改。绝不要伪造数据共享标头,也不要为了完成回答而要求管理员提升级别。
如果载荷确实缺失,请说明这一点并指出级别:“在此组织的数据共享级别(metadata)下,未共享 output”。绝不要说“工作流没有产生输出”——这会让用户去调试本来能正常运行的代码。请提供第 7 节中的控制台链接,让用户自行查看。
工具拒绝执行,同样不代表工作流失败。请报告该拒绝。
7. 提供链接,而非空口断言
应用创建完成且运行已启动后,请向用户提供控制台链接,让他们自行查看。生产服务的控制台地址为 https://catalyst.diagrid.io。仅存在以下路由:
| 显示内容 | 路由 |
|---|---|
| 应用 | /apps/details/<appId> |
| 工作流运行 | /workflows/<appId>/<runId>,可选添加 /<tab> |
| 应用指标 | /metrics/appids/<appId> |
没有项目详情页,也没有配额页面。不要链接到这两者;项目路由只有管理路由。
项目通过查询参数传递,两种写法不可互换:
- 类似名称的 ID(如
default)使用?project=<name> - 数字 UID 可带或不带
prj-前缀,使用?projectId=<uid> - 如果两者都出现,以
projectId为准
如果将数字 UID 放入 ?project=,控制台会搜索名称恰好为该数字的项目,找不到后便会静默回退到用户的默认项目——不会报错,但显示的数据是错的。名称放入 project,数字放入 projectId;如果无法判断当前 ID 属于哪种,省略该参数。
如果无法构建可信的链接,请以纯文本输出应用 ID 和运行 ID——会返回 404 或跳转到错误项目的链接,还不如没有链接。
规则
- 所有 Catalyst 操作都使用 Catalyst MCP 工具。
- 不要创建项目。 使用
default。如果该项目已不存在,请询问应使用哪个现有项目;如果有人没有项目,请让其前往控制台创建。 - 不要指定尚未确认的包版本。 请查询注册表,或只写包名,不写版本。
- 不要为此技能搭建按语言区分的多个变体。 一个技能负责检测语言;五个几乎相同的技能会争夺相同请求,最终都无法胜出。
- 不要在工作流主体中执行副作用。 应使用活动来执行。
- 只使用
catalyst_*工具。 如果某项能力没有对应工具,请说明它目前无法通过 MCP 使用,然后停止。 - 绝不要将被隐去的字段报告为空结果。
- 绝不要猜测控制台 URL。 使用上述路由,将项目放入正确的参数;无法构建可信链接时,输出纯文本标识符。