关于 carto-create-workflow
用于编写和管理可重复运行的定时 CARTO Workflows,涵盖 DAG 设计以及通过命令行或 MCP 操作。还介绍如何用命令行在不同配置间推广工作流。
主要功能
- 01规划并构建工作流 DAG
- 02检查实时模式和组件
- 03验证、运行和安排工作流
- 04跨配置复制工作流
- 05将工作流发布为 MCP 工具或 API
适用场景
- 01创建多步骤数仓转换
- 02安排定期工作流运行
- 03将工作流从 dev 推广到 prod
MCP 相关
在通过 OAuth 认证的会话中,可使用 MCP 工具创建、验证、运行、安排和发布工作流。令牌认证会话仅开放运行和验证操作;跨配置复制等任务仅支持命令行。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ cartodb/ agent-skills --skill carto-create-workflow安装后在 Claude Code 中输入/carto-create-workflow确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/canvas-notes.mdcross-profile-copy.mdmcp-and-api-publish.mdschedule-readd.mdscheduling.mdreferences/providers/bigquery.mddatabricks.mdsnowflake.md详细介绍
carto-create-workflow
CARTO Workflows 是一款可视化 DAG 编写应用,可将 DAG 编译为数仓 SQL。每个工作流都在已连接的数仓内部运行——执行时不会涉及 CARTO 计算资源。本技能涵盖完整生命周期:构建 DAG(本文的主要内容)、通过 CLI 操作(CRUD、计划),以及跨配置文件复制(dev → prod 晋级、通过 carto workflows copy 实现客户隔离的工作区)——请参阅下方的参考资料。
Workflows 与 Builder 是不同的应用。 本技能针对 Workflows(CARTO 的 DAG/编排应用,URL 中为
/workflows/<id>)。Builder 是单独的地图编写应用(/builder/<id>)。本技能中提到“Workflows 画布”“在 Workflows 中”或“DAG 视图”时,绝不是指 Builder——它们是不同的产品,只共享组织和连接,除此之外毫无关联。工作流输出(包含几何信息的结果表)通常会随后在 Builder 中可视化;这也是这两个应用唯一的关联之处。
对于一次性的临时 SQL,请使用 carto-query-datawarehouse——工作流适用于可重复、按计划运行的多步骤 DAG。
访问路径选择。 交互式生命周期对应经过 OAuth 身份验证的 MCP 会话中的 MCP 工具:
create_workflow、update_workflow(method=update|share|unshare|publish|unpublish)、validate_workflow(method=validate|verify|to_sql)、read_workflows(method=list|get|list_mcp_tools|get_mcp_tool)、run_workflow(method=run|status|results)、schedule_workflow(method=add|update|remove),以及read_workflow_components(method=list|get——组件目录)。以下各阶段适用于两种路径——如果某个步骤指定了carto workflows命令,请改用对应的 MCP 工具(例如,将verify-remote替换为validate_workflow method=verify)。脚本编写/CI、Bundle 架构自省(carto workflows schema——MCP 中无对应项)、跨配置文件复制(carto workflows copy——仅 CLI 支持)以及使用日志审计(carto activity query——仅 CLI 支持),请使用 CLI。当服务未连接,或 MCP 会话使用 token 身份验证时,请退回使用 CLI——在这种情况下,编写/管理工具会被隐藏;仅保留run_workflow和validate_workflow。检测信号:carto-basics/references/access-paths.md。
Bundle 结构、组件架构、输入格式和注意事项均可实时获取——通过 CLI(carto workflows schema / components),或在 MCP 路径中通过 read_workflow_components(method=list|get)获取。切勿将其硬编码或想当然。 实时自省才是事实来源。
实时自省(使用任何参考文件之前)。在 MCP 中,read_workflow_components(method=list|get)涵盖组件条目;Bundle/DAG 的 schema 部分仅 CLI 支持。
| 命令 | 提供的内容 |
|---|---|
carto workflows schema | 所有 bundle/DAG 架构部分的索引 |
carto workflows schema bundle | 顶层 bundle 结构(id、title、connectionId、config、privacy、tags)。privacy 是一个 $ref——使用 carto workflows schema privacy 获取其结构。最简有效形式:"privacy": { "privacy": "private" }(内部字符串不是单独的 "private"——enums 列出了该内部字段允许的值)。 |
carto workflows schema config | 完整的 DAG 配置(schemaVersion、connectionProvider 枚举、nodes、edges、variables、viewport、useCache、executionSettings、schedule) |
carto workflows schema node[.source|.customsql] | 通用节点结构(data.version、data.title 与 data.label);.source 添加 data.id == data.inputs[0].value 不变量;.customsql 提供完整的 customsql 规范 |
carto workflows schema customsql | 可复制粘贴的 customsql 节点模板(含 version: "2.0.0") |
carto workflows schema edge / schema handles | Edge 结构;以及edge 句柄命名参考(按节点类型、运算符、组件区分的 sourceHandle/targetHandle——对创建有效 edge 至关重要) |
carto workflows schema variable | 变量(参数)结构——{ order, name, type, value, public } |
carto workflows schema schedule / schema enums | 声明式 schedule 元数据;以及所有有效枚举值(节点类型、提供程序、隐私级别、schedule 频率) |
carto workflows components list | get <names> --connection <conn> --json | 组件目录;各组件的 inputs、outputs、notes。在 get 中添加 --input-formats,可获取输入类型的 format、examples、pitfalls。MCP:read_workflow_components。 |
carto workflows --help | 完整命令参考,包括各引擎的 schedule 表达式方言 |
参考资料(仅涵盖实时自省未提供的内容):
references/canvas-notes.md— 使用type: "note"节点记录画布,包括包围所描述节点的分区容器。实时自省不会提供 note 的架构;可见卡片使用data.width/data.height(节点级的宽/高为data + 160/+ 128)——若将其反向处理,每张卡片都会变得过小。references/providers/— 各数据仓库的详细信息(BigQuery、Snowflake、Databricks):标识符引用、列名大小写、AT 路径。references/scheduling.md—add与update的语义、bundle 级 schedule 警告、活动日志验证。references/mcp-and-api-publish.md— 将工作流发布为 MCP 工具或可调用的 API 端点(MCP:update_workflow method=publish):bundle 要求(native.mcptooloutput+ 作用域变量 + 草稿描述)、{{@var}}与@var替换语法、Number → FLOAT64的LIMIT注意事项、发布后验证。references/cross-profile-copy.md—workflows copy的机制(仅 CLI)、连接映射、--skip-source-validation,以及副本为何总是新工作流。references/schedule-readd.md— schedule 不会随workflows copy一起迁移;如何重新添加,包括源引擎与目标引擎不同时的方言转换。
connectionProvider必须与连接匹配。config.connectionProvider(schema enums中的枚举值)必须与连接的实际提供程序匹配——不匹配会生成错误的 SQL 方言,并在运行时出错。可使用carto connections list --search <name> --json(CLI)或 MCP 路径上的explore_data(method=list_connections|get_connection)查询。
开发流程
**每个工作流请求都必须按顺序遵循以下 6 个阶段。**不得跳过或调整顺序。
第 1 阶段 — 收集信息
-
确定数据源。 如果用户指定了表,请记下。否则,查找可用的数据源 — CLI
carto connections list/carto connections describe <connection> "<fqn>",或 MCPexplore_data(method=list_connections|list_resources|describe)。 -
明确目标。 要进行什么转换?输出是什么?有哪些筛选条件?
-
确定连接并记下其
provider(bigquery/snowflake/databricks)— 下一步需要用到。CLIcarto connections list或 MCPexplore_data method=list_connections。 -
阅读提供商参考文档。
编写任何节点之前,你 MUST 打开 `references/providers/.md`(例如 `references/providers/bigquery.md`)并从头到尾阅读。这是不可妥协的要求。其中包含标识符引用规则、列名大小写行为、Analytics Toolbox 路径、调度表达式方言,以及 customsql
$a/$b占位符要求;这些问题无法通过validate检查。它们只有在后续的verify-remote失败或运行时 SQL 出错时才会显现,也是后期返工最常见的原因。即使接下来的阶段看起来很具体,也不要跳过这一步。不要依赖之前运行时的记忆 — 提供商文件可能会变化。
-
获取组件目录 —
carto workflows components list --connection <connection> --json(CLI),或read_workflow_components method=list(MCP)。这是组件名称的唯一真实来源。
第 2 阶段 — 设计方案
- 从获取的目录中选择组件。
- 获取计划使用的每个组件的架构。 CLI
carto workflows components get <name1>,<name2> --connection <connection> --json(或 MCPread_workflow_components method=get)会返回inputs、outputs和notes。仔细阅读notes数组 — 其中包含容易忽略的问题。 - 获取输入类型格式。 CLI
carto workflows components get <component1>,<component2> --connection <connection> --input-formats --json会返回每种输入/输出类型的format、examples和pitfalls。传入组件名称(例如native.buffer),而不是输入类型名称。 - 设计原则:
- 全程保留标识符列和空间列。
- 优先使用原生组件,而不是
native.customsql。这不是可有可无的偏好。 参见原生优先规则。 - H3/Quadbin 列无需提取几何数据即可用于可视化。
- 可视化使用标准名称:
geom、h3、quadbin。
第 3 阶段 — 提交计划、指出缺项并确认
提交工作流计划(组件、数据流、决策),然后在构建前明确列出所有缺项:
- 尚未确定的参数 — 阈值、半径、筛选值、时间范围、k-NN 中的 k、聚合列、输出表名称等。
- 留待用户决定的分析事项 — 显著性水平、距离度量、连接类型、空值处理、去重键、CRS、H3/quadbin 分辨率。
- 请求中的歧义 — 任何需要你猜测意图的内容。
对于每项缺项,提出合理的默认值并说明理由(例如:“p 值阈值:建议使用 0.05 — 常见的显著性水平”;“缓冲距离:建议使用 1000m — 与输入数据的街区尺度相符”),并询问用户确认或修改。绝不要擅自为面向用户的分析参数选定数值。构建前等待确认。
第 4 阶段 — 构建工作流
-
创建工作流文件。通过
carto workflows schema [section]获取 bundle/node/edge/variable 的结构(从bundle开始,然后是node、node.source、node.customsql、edge、handles)。对于 customsql 节点,请复制carto workflows schema customsql中的模板。 如果设置可选的顶层privacy,它必须是一个对象,而不是字符串:"privacy": { "privacy": "private" }(字段名需要嵌套)。如果不需要,完全省略该字段——"privacy": "private"会导致validate失败。源节点(
type: "source")——像对待其他组件一样处理ReadTable:运行carto workflows components get ReadTable --connection <conn> --json获取规范的inputs[*].title和inputs[*].description。(ReadTable因归类为__internal而隐藏在components list中,但get可以正常返回它。)get无法告诉你的两个仅适用于源节点的规则,都来自schema node.source:- 画布显示名称位于
data.label,而非data.title。通用节点使用title;源节点使用label。 data.id和data.inputs[0].value必须是相同的 FQN。
画布布局与命名——每个节点、每个工作流都要遵循。 这些都不会影响执行,但用户会在 Workflows 中打开 DAG,杂乱的画布会显得质量低下。这些数值简单且固定;直接照做即可。
- 网格间距为 16 px。 每个
x和y都必须满足% 16 == 0;偏离网格的值与用户移动过的元素相邻时,会显得略微错位。 - 按节点类型设置卡片宽度: 源节点为 192 px(12 个单元格),通用组件为 64 px(4 个单元格)。高度固定: 每张卡片高 80 px(5 个单元格),下方有 16 px 的标签(标签位于与下一张卡片之间的间隙中,而非卡片内部)。
- 卡片间规范间距(右边缘 → 下一张卡片左边缘): 紧凑/线性排列时为 80 px(5 个单元格);扇入时为 128 px(8 个单元格),即连接节点的左侧输入处,来自另一行的边需要留出空间。间距是固定值;左边缘的 Δx 会因卡片宽度而异——通用组件→通用组件线性排列 = Δx 144(9 个单元格),源节点→通用组件 = Δx 272(17 个单元格),通用组件→通用组件扇入排列 = Δx 192(12 个单元格)。
- 规范垂直间距(卡片主体底部 → 下一张卡片主体顶部): 80 px(5 个单元格)= 16 px 标签 + 64 px 空白。因此,垂直堆叠步骤的顶部到顶部 Δy = 160 px(10 个单元格)。
- 布局。 源节点堆叠在最左侧列,
x相同,Δy = 144 px(9 个单元格)。主流程位于源节点各行的 y 中线处(源节点位于 y=80 和 y=224 → 流程位于 y=160),这样连接节点接收两路输入时是对称的。 data.title和data.label是不同字段——绝不要重复填写。title= 简短且针对实例的动作描述(≤ 15 个字符),说明此节点的用途("Rank"、"Join to score"、"To H3")。label= 新添加节点时该组件的规范类型名称("Join"、"Create Column")——从components get <name> --json→components[0].title读取。源节点只会渲染data.label(表的简短别名:"Candidates"、"Score grid C")。
- 画布显示名称位于
-
每次写入文件后都运行
validate。 它无需联网、速度快,能立即发现结构错误——使用 CLIcarto workflows validate workflow.json --json,或 MCPvalidate_workflow method=validate:carto workflows validate workflow.json --json任何未经
validate通过检查的保存都应视为有问题——修复后再继续处理下一个节点/边。validate具有权威性。 如果components get返回的组件架构与validate接受的内容不一致,以validate为准,并调整 bundle 以满足它。如果 bundle 已通过验证,不要为了匹配架构而“修复”它。 -
在分支边界运行
verify,而不是每次保存后都运行。它会访问数仓(速度较慢,需要身份验证),因此只在整个子 DAG 的结构通过验证且无误后运行一次,并在最后呈现前再运行一次——使用 CLIverify-remote,或 MCPvalidate_workflow method=verify:carto workflows verify-remote workflow.json --connection <connection-name> --jsonverify可以捕获列类型不匹配、缺少表和 AT 解析等问题——这些是validate无法发现的。 读取响应。 首先查看deep.errors——其中的任何内容都会阻止运行时执行。deep.warnings则更加细分:COMPONENT_INVALID项描述运行时问题(引擎无法识别的选项、可能导致失败的列引用);SCHEMA_TRACE_SKIPPED和VERIFY_SKIPPED属于诊断信息(静态检查器跳过了此节点,并不表示它有问题)。顶层的valid汇总了错误和警告,因此即使工作流仍能成功上传(deep.valid: true且deep.errors为空),它也可能显示为false。将deep.errors为空视为上传的门槛;将顶层valid: false视为“需要仔细检查,但通常不会阻止上传”。 -
静默修复错误——不要向用户透露实现细节。
-
反复迭代,直到完成,且
validate和最终的verify均无问题。
阶段 5 — 呈现结果
总结构建的内容。确认验证成功。等待用户确认。
阶段 6 — 上传到 CARTO
- 询问用户是否要上传。
- 上传并提供 URL——使用 CLI
carto workflows create,或 MCPcreate_workflow:
连接信息来自 bundle 中的carto workflows create --file workflow.json --verifyconnectionId——此处不要使用--connection标志。 - 确认上传时输入没有被静默丢弃。 创建后立即读取已保存的工作流(
carto workflows get <id> --json或read_workflows method=get),并将config.nodes[*].data.inputs与上传的 bundle 进行比较。当输入验证失败时,引擎会在保存时静默拒绝相应的值(最常见的情况是 Selection 输入接收了显示标签,而不是 wire 值——参见获取组件和输入信息中的“显示标签与 wire 值”)。发生这种情况时,validate、verify(当deep.valid: true且只有deep.warnings时)和 create 都会报告成功;首次打开时,Workflows 会在该节点上显示红色错误指示标记。任何在本地存在但服务器上缺失的value都已被静默拒绝——修复源 bundle 并重新上传(不要尝试在服务器上编辑)。 - 除非用户明确要求,否则不要自动执行。
原生优先规则
native.customsql 是最后才考虑的工具,而非首选。在编写 customsql 节点之前,先尝试使用原生节点链。仅当满足以下至少一项时,才回退到 customsql:
- 要表达相同逻辑,原生节点链需要超过约 4-5 个节点。
- 某项特定操作完全没有对应的原生节点(通过
carto workflows components list验证)。 - 表达式确实需要原始数据仓库 SQL(例如
ST_UNION_AGG、LOGICAL_OR、ML.PREDICT、最近 N 项窗口计算)。
常见操作及其对应的原生节点——请先尝试使用这些节点:
| 如果你会编写类似这样的 SQL…… | 使用原生节点 |
|---|---|
WHERE x = … / 多条件筛选 | native.where(谓词)、native.wheresimplified(基于表单的筛选 UI)、native.spatialfilter(按几何匹配/不匹配拆分)、native.select(列投影) |
SELECT a, b, c FROM t(多列投影 / 重命名 / 多表达式) | native.select(一个节点,自由格式 SELECT 主体) |
SELECT ..., expr AS c FROM t(添加一个计算列) | native.selectexpression(每个节点添加一列和一个表达式) |
GROUP BY k, SUM(x), AVG(y), COUNT(*)(单键) | native.groupby——groupby 输入是单个 Column,而非多列。多键分组请使用 native.customsql。 |
JOIN ... ON a.k = b.k(任意联接类型) | native.joinv2 |
JOIN ... ON ST_INTERSECTS / ST_CONTAINS / ST_WITHIN | native.spatialjoin |
对两个表执行 MIN(ST_DISTANCE(a.geom, b.geom)) | native.distance(就地扩充主表,添加 nearest_id + nearest_distance——如果要对两个参考表串联两个 native.distance 节点,请按数据源分别重命名这些列) |
ST_BUFFER(geom, d) | native.buffer |
| H3 分箱 / 边界 / 中心 / 多边形填充 | native.h3frompoint、native.h3boundary(输出几何列名为 <h3col>_geo,例如 index_geo——不是 geom)、native.h3center、native.h3polyfill |
ORDER BY ... LIMIT n | native.orderby + native.limit |
| z 分数 / 标准化 | native.normalize |
| 加权综合评分 | native.spatialcompositeunsupervised(加权/PCA)、native.spatialcompositesupervised(目标驱动) |
| Getis-Ord Gi*、GWR、等值线 | native.getisord、native.gwr、native.isolines |
| 将最终节点保存到表 | native.saveastable |
以下迹象表明你过早地使用了 customsql——请停下来改为寻找原生节点链:
- customsql 只包含一个
WHERE子句、一个JOIN、包含一两个聚合函数的GROUP BY,或列投影。 - 它只封装了一个数据仓库函数(
ST_BUFFER、H3_FROMGEOGPOINT等),而该函数有专用的原生节点。 - 它唯一的用途是投影/重命名/重新转换列类型——多列请使用
native.select(自由格式 SELECT 主体,一个节点);native.selectexpression用于添加单个计算列。 - 你在多个 customsql 节点之间串联 customsql 输出——请改为串联原生节点。
确实应该使用 customsql 时,各数据仓库 SQL 方言容易踩坑之处请参见对应的 references/providers/*.md(BigQuery 反引号、Snowflake 大小写规则、Databricks 标识符)。
Customsql 架构追踪级联——误导性的“使用了表 X,但未提供”错误。 如果 verify-remote 针对 customsql 节点报告 Table "X" used in statement but not provided,而其 sourcea / sourceb 明确已连接,那么根本原因几乎总是上游节点的架构追踪失败——即使上游节点只有警告级别的 COMPONENT_INVALID,也足以中断级联,并在下游 customsql 节点上引发错误。请先修复上游节点;下游错误会随之消失。在上游问题解决之前,不要花时间重写 SQL、更换反引号或尝试别名。
获取组件和输入信息
不要依赖记忆中的组件架构或输入格式。 始终实时获取信息 — carto workflows components list | get <names> [--input-formats] --connection <conn> --json(CLI),或在 MCP 路径上调用 read_workflow_components(method=list|get)(参见上方的内省表)。
请留意响应中的以下内容:
- 组件
notes— 注意事项:不明显的行为、已弃用状态、输出列命名。 - 输入
format— 描述预期值形态的文字。 - 输入
examples— 展示正确用法的具体 JSON 代码片段。 - 输入
pitfalls— 常见错误、求值顺序、格式特殊情况。 - 组件
version— 原样复制到所编写节点的data.version(字符串)中。缺少此字段的通用节点会在 Workflows 中被标记为 OUTDATED。 - 输入
options(Selection / Enum) — 引擎会精确匹配值。逐字复制每个选项字符串 — 保留大小写,绝不改写措辞或改为标题式大小写(例如,spatialjoin 的jointype接受"inner",不接受"Inner")。 - 显示标签与线缆值。 一些组件(例如
native.isolines.mode)带有单独的optionsText字段,用于提供便于人类阅读的标签,而components get --json可能会在options键下显示这些标签。如果verify-remote拒绝你填写的“原样”值,并通过Valid options: <list>列出大小写相反的选项,则 CLI 提供的是显示标签 — 请通过carto workflows get <id> --json与已知可用的工作流交叉核对,找到真实的线缆值。小写或蛇形命名形式(walk、public_transport)在 v2 组件中很常见。 verify-remote可能会同时运行多个组件版本。 如果错误和警告针对同一输入列出互相矛盾的“有效选项”(例如,一个接受"Walk",另一个接受"walk"),引擎可能同时对你的节点运行了 v1 和 v2 校验器。请以从components get复制的版本为准,并通过上传仅含单个节点的测试工作流进行确认。
对于可能随时间变化的值(组件版本、捆绑包/配置默认值、枚举选项列表),请将 CLI 的 components get / schema 输出视为唯一事实来源 — 切勿在自己的模板中硬编码值。具体来说:
config.schemaVersion— 通过carto workflows schema config --json→properties.schemaVersion.default读取当前默认值。目前为"1.0.0"(字符串),但应在编写时解析,以便未来升级时无需更新技能。
提供商专属说明
不同数据仓库使用不同的 SQL 方言、表命名惯例和列大小写规则 — 请始终查看对应的提供商指南(bigquery、snowflake、databricks)。输入类型的格式(Table、Column、ColumnsForJoin、SelectColumnAggregation 等)以及各组件的注意事项(包括“AT 组件需要 verify,而非 validate”这一规则)均来自实时内省 — 请参阅获取组件和输入信息。
运行工作流(构建完成后)
在 CARTO 中创建工作流后,两种方式都支持 CRUD、运行和日程管理。MCP:read_workflows(list|get)、update_workflow(update|share|unshare|publish|unpublish)、run_workflow(run|status|results)、schedule_workflow(add|update|remove)、delete kind=workflow。CLI 快速参考:
carto workflows list --json # list / inspect
carto workflows get <id>
carto workflows update <id> --file workflow.json # update (replaces whole DAG)
carto workflows schedule add <id> --expression "every day 08:00"
carto workflows schedule remove <id>
始终适用的指南:
- 工作流在连接所使用的数据仓库上运行。 使用 BigQuery 连接的工作流无法使用 Snowflake 专属 SQL。
- 日程表达式语法取决于引擎——BQ/CARTO DW 使用自然语言(
"every day 08:00"),Snowflake/Postgres 使用 cron("0 8 * * *"),Databricks 使用 Quartz cron("0 0 8 * * ?")。请参阅references/scheduling.md。选错语法类型会导致添加日程时失败。 - 跨配置文件复制工作流(dev → prod、按客户隔离的工作区)仅支持 CLI——请参阅
references/cross-profile-copy.md。日程不会随之迁移——请参阅references/schedule-readd.md。 - 删除工作流不会删除其输出。 工作流在数据仓库中创建的表/视图会保留;如有需要,可使用
carto sql job(或 MCPexecute_query)清理。 update会替换整个 DAG。 不支持按节点单独修补。务必先get,再编辑,最后执行update。- 工作流执行状态记录在活动日志中(事件类型为
WorkflowRun、WorkflowExecutionComplete)。如需监控已安排工作流的运行状况,请通过carto-query-datawarehouse查询该日志——参阅该技能中的references/activity-queries.md。