mcpField
G
gtm-workflow

gtm-workflow

github.com/eliasstravik营销与增长22335 次安装2 周前更新
代码仓库

关于 gtm-workflow

gtm-workflow 是管理 GTM Workflow 生命周期的 Agent 技能,面向创建、修改、运行和部署工作流的用户。它涵盖本地与已部署工作区中的工作流、运行记录和界面操作。

主要功能

  1. 01创建、修改和删除工作流
  2. 02运行、测试、安排和部署
  3. 03管理分组、运行记录与图表
  4. 04处理工作区和数据库迁移

适用场景

  1. 01从模板创建新的 GTM Workflow
  2. 02修改现有工作流并保留数据
  3. 03运行工作流或部署到托管副本
  4. 04检查运行记录并打开工作流界面

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/eliasstravik/gtm-skills --skill gtm-workflow

安装后在 Claude Code 中输入/gtm-workflow确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

recipes/
README.md
recipes/enrich-network/
acceptance.md
interactions.md
README.md
recipe.md
references/
connections.md
deploy.md
entry-links.md
imports.md
interactions.md
linked-data.md
local-and-production.md
local.md
patterns.md
profile-field-inventory.json
profile-fields.md
reliability.md
shared-profiles.md
viewer.md
scripts/
doctor.mjs
hosted.mjs
root-files.mjs
scaffold-manifest.mjs
setup.mjs
share-firewall.mjs
template.mjs
upgrade-package.mjs

详细介绍

GTM 工作流

触发条件

当请求涉及已保存的工作流时,应用此技能:创建、更改、运行、安排、部署、升级、检查、打开其 UI、分组或删除工作流,或涉及其表格、运行记录、图表或分组。

创建或更改工作流时,检查 recipes(例如网络信息补全),找到与请求匹配的方案并遵循;此技能仍负责整个生命周期。运行未更改的工作流时,无需使用方案。

仅当用户的整个请求是 /gtm-workflow 或 /gtm-workflows,且不包含操作或其他文字时,才显示意图菜单。询问 选择一项工作流操作。,并提供三个选项:打开 GTM Workflows、创建工作流、管理工作流,然后等待。管理操作会询问要管理哪个工作流以及要执行什么操作:更改、运行/测试、安排/部署、分组、升级/修复或删除。仅输入命令不会创建、执行或部署任何内容。

打开 GTM Workflows 已是明确的打开请求。使用 入口链接解析已验证的查看器 URL,并立即返回;不要显示意图菜单,也不要再次询问用户是否要打开。加载此技能不会将明确请求转变为仅输入命令。其他明确操作也应直接进入相应流程。如果打开时未找到工作区,请说明工作区设置方法;如果工作区中没有工作流,请显示空列表。

范围

此技能负责 <workspace>/workflows/:这是一个基于 Nitro 的 Vercel Workflow 运行时,首次使用时会从模板的 templates/ 复制而来;其中包含工作流文件、db/tables/、迁移文件、vercel.json、.env、本地数据库、运行记录、图表页面和已部署副本。默认情况下,运行会在本地针对此工作区自己的 data/pg 中的 Postgres 执行;用户提出要求或主机声明没有本地运行记录时,则会针对已部署副本执行;托管结果通过已部署副本的查询路由读取。本地密钥存储在 workflows/.env.local 中,部署后则存储在项目的 Production 变量中;两者都通过查看器的 Keys 页面(Connections 标签页)编辑。有关设置、密钥发现和密钥更改,请阅读 Keys。workflows/ 之外的其他工作区文件由其他 gtm 技能负责。

此技能中不附带工作流模板。从此技能文件夹运行 node scripts/template.mjs,会从 gtm-skills 仓库中获取此技能版本对应的模板一次,标签为 v<metadata.version>(绝不使用分支;优先使用 git,因此 exe.dev VM 上会应用 github.localhost 代理,然后才使用发行版 tarball),将其缓存到 ~/.cache/gtm-skills/ 下,并输出 JSON,其中 templates 是要复制到 workflows/ 的运行时文件夹,root 则包含工作区根目录文件。Setup、Doctor 和 upgrade-package.mjs 会自行调用此脚本;此技能或其参考文档中提到的 templates/ 或 root/ 均指这两个文件夹。如果获取失败(退出码为 1,template_fetch_failed),请报告原因;工作区中的其他内容不会发生变化。

输入

用户请求;根据约定找到的工作区;工作流按 slug 读取的 ICP 和 persona;关于命令、密钥和已验证事实的 references/local.md;关于托管的 references/deploy.md。

角色

用户的请求授权在配置的行数和费用上限内执行常规代码更改和真实运行。说明预估值并继续;仅在共享交互规则规定的破坏性操作中才需要确认。工作流项目通过共享设置 --deploy(deploy.md)上线;可直接运行,也可作为可选 gtm-agent 设置的最后一步运行。用户在每个工作区的 .env 中选择一次 AI 后端;Agent 负责编写、运行并报告。Deploy 即推送。

流程

持久化已丰富的个人或公司信息时,请遵循共享档案中的存储、身份、时效性、付费尝试和范围化 Data 视图规范。对于多阶段信息丰富任务,其双阶段工作器和通用预算取代下文的通用自我扇出模式。档案表是架构 gtm 中的运行时表;scripts/migrate.mjs 会像迁移其他内容一样迁移这些表,升级时会保留它们。

对话中首次使用的 gtm 技能会按更新说明所述,仅检查一次是否有更新版本。交流时遵循交互规范中的六条规则;复现这些对话。工作流 slug 遵循契约中的 slug 规则;显示名称取自文档注释的第一行。

JobDo
创建在个人电脑上首次使用:运行 Keys 中的共享本地设置,它会创建一个空注册表并准备本地数据库,但不会执行任何内容。托管式创作使用已配置的运行时。启动 npm run dev(唯一的本地命令:查看器、Keys 页面和运行;打开它不会运行任何内容)。询问行的数据来源。当工作流涉及人员时(某个阶段需要审批,或调用 notify),只问一个问题:它应该发到哪个 Slack 频道?请提供频道 ID(C0…,显示在频道 About 标签页底部),或回答“这里”;“这里”表示当 Agent 知道当前对话所在频道的 ID 时,使用该频道。答案会成为工作流文件顶部的 NOTIFY 常量,并作为 notify 传给每个阶段和每次 notify 调用,绝不会是项目变量。无需逐个工作流询问后端:Agent 阶段在运行时从 .env 读取 GTM_AGENT_BACKEND(个人电脑上使用作者订阅时为 claude 或 codex,使用 AI Gateway 时不设置),因此同一个文件在本地使用订阅运行,在托管环境中使用 Gateway 运行。在个人电脑上生成脚手架时,如果 PATH 中有 claude 或 codex,且没有 Gateway 密钥,就设置一次并说明;否则保持未设置。按照 viewer 编写字面量 viewer.businessGraph 和 viewer.description(约 280 个字符,最多 500 个字符),并根据新代码核对标签和分支。根据 example-scores.ts,或在包含 Agent 阶段时根据 example-research.ts,编写 workflows/<slug>.ts;在 db/tables/<name>.ts 中编写其表,在 workflows/index.ts 和 db/tables/index.ts 中注册两者,并在 workflows/groups.ts 的某个分组中写入其 slug(参见 Group;计划中要说明分组名称和原因,并与计划其他部分一同获批)(保留示例工作流及其表;在同一次 generate 中删除一张表并添加一张表,会使生成器提出无法由任何主机回答的重命名问题),运行 npm run db:generate -- --name <name>,运行一次 npm run viewer:register 以分配新的工作流身份,重新构建,打开其 Diagram 页面,提交更改,并以已创建内容和 Open GTM Workflows 入口作为结尾。在无法本地运行的主机上:绝不提供 localhost 链接;推送后,每 10 秒读取已部署副本中的 GET /api/link/<slug>,最多等待 3 分钟,直到返回的 commit 满足 git merge-base --is-ancestor HEAD <commit>,然后才发布经验证的 viewerUrl;在满足条件前写出的回复不得包含链接,并须说明部署正在进行。用户要求运行时,检查密钥和上限后执行所请求的范围,无需再次确认,并说明估算费用;在无法本地运行的主机上若没有已连接的项目,则以说明 deploy.md 中的连接步骤作为结尾。
更新同时修改工作流、其 viewer.businessGraph 和 viewer.description,包括新增的业务决策分支和负责人详情;保留 viewer.id,用 node scripts/build-viewer.mjs 验证元数据,将 export const criteria 副本移到 readIcp/readPersona(参见下文 Criteria),添加列(然后运行 db:generate;运行中的 npm run dev 会应用变更);保留现有数据;以变更内容和 Open GTM Workflows 入口作为结尾。
运行目标:个人电脑上默认在本地运行;用户要求或主机说明无法本地运行时,则在已部署副本上运行,但需先连接项目并通过 Deploy 的就绪检查。运行前检查:行数、行数 × 估算值是否超出任一上限、工作流使用的每个连接是否存在于 Connections 的目标环境清单中,以及工作流自上次运行以来是否为新增或有变更。用户要求运行时,无论是一行还是多行,都无需询问许可或是否先测试:说明行数和预估费用,然后运行所请求的范围。缺少密钥或超出上限时,停止执行并指出阻碍因素。通过路由启动,通过读取路由轮询,最后用通俗语言说明结果:完成、失败、跳过、费用;不提供链接,除非链接自本线程上次显示后有变更。读取路由列出待处理审批时,停止轮询,并在一条消息中向用户询问(说明工具、用通俗语言描述其输入,以及已知时的价格);然后根据用户的答复和理由,通过审批路由作出决定,并恢复轮询;若运行时启用了 stream,可在运行期间通过 stream 路由播报进度。托管运行后接着进行本地运行时,说明本地运行可能会对托管副本已处理的行再次花费费用。
打开 UI对于“打开 GTM Workflows UI”或明确要求提供链接的请求,请遵循 entry links。通过稳定身份解析讨论中的工作流;没有稳定身份时,打开列表。检查就绪状态。在本地需要时启动 npm run dev;其 Connections 标签页(<viewer origin>/connections,提供密钥时使用的地址)是同一服务器上的 Keys 页面。打开 UI 绝不会执行工作流、运行迁移或部署缺失的主机。无法使用时,说明具体的设置阻碍因素。明确要求时,始终返回入口链接,即使之前已经提供过。
分享仅限托管环境:使用 /api/viewer/service?v=3&workflow=<id>&op=grants 读取 grants。在明确保存前,显示所选标签页和当前获准数据的摘要。POST saveLink,传入 {views, policy, save}。policy 是显示的哈希值;save: true 表示明确批准范围或策略变更。未作更改的副本会复用该链接。默认显示 Diagram,且永不过期。使用有效 ID POST revokeGrant 以停用链接。重新启用会创建新的密钥。使用返回的原样 URL。参见 viewer。
部署部署即推送;按照 deploy.md 完成一次性连接和就绪检查。推送不会影响已经在运行的任务:任务会使用启动时的代码完成。托管副本无论本地 .env 中写了什么,都会通过 Gateway 运行每个 Agent 阶段。
升级首先在此技能的文件夹中运行 node scripts/upgrade-package.mjs <workflows>(预览,不会写入任何内容):若此技能的模板比工作区旧,它会拒绝升级,并在此处停止,提示“技能版本落后于此工作区;请先更新技能”,绝不降级。将 workflows/ 与模板的 templates/ 比较(node scripts/template.mjs 会打印其文件夹路径);说明差异;Postgres 运行时之前创建的工作区(含有 db/tables/cache.ts)不由此任务升级:须在用户同意后,按照 updates 说明迁移;更新模板所有的 lib/、server/、viewer/、connections-ui/、share-server/(删除残留的 viewer-server/)、scripts/、drizzle-runtime/、nitro.config.ts、drizzle.config.ts、drizzle-runtime.config.ts、tsconfig.json、share-firewall.json,并仅删除某个版本已废弃的模板文件(0.2.7 废弃了文件夹运行时:lib/workflow-folders.ts、lib/shared-folders.ts、lib/shared-folder-transport.ts、lib/shared-folder-service.ts、server/api/workflow-folders.ts;工作区自己在 lib/ 中的文件保留);工作区没有 skills/ 时,从模板复制;从 root/ 更新工作区根目录的 .gitignore 和 .github/workflows/check.yml,保留工作区新增的 CI 步骤;按照 runtime reliability 将依赖和启动脚本合并到 package.json,保留用户添加的依赖和自定义命令,然后将模板的 package-lock.json 复制到工作区,删除 node_modules,运行 npm install 并通过 npm ci --dry-run 确认(在旧锁文件上安装会删除其他平台的包,导致托管构建失败;从零生成的锁文件会被 npm ci 拒绝);重新生成 workflows/index.ts、db/tables/index.ts 和 skills/index.ts,保留每个工作流条目中由作者编写的 viewer、data、intake 及其他属性,包括导入的元数据文件;按工作区原样保留 workflows/groups.ts(共享设置会在文件缺失时添加空文件),但从 0.2.7 升级时例外:从列表中移除列出的阶段,因为阶段现在会显示在其所属流水线下(构建会逐一说明:“ is a stage of …”),并说明哪些阶段移动了;绝不触碰 workflows/、db/tables/、skills/、drizzle/、.env、data/ 中的其他文件;vercel.json 由模板管理,但其 crons 除外,共享设置会合并 crons;合并生成资源和 .env.* 的忽略条目(包括 lib/criteria.generated.ts);按照 Keys 的说明运行共享设置 --local;仅对缺少身份的条目运行 npm run viewer:register;从 0.2.6 或更早版本升级时,按照 local and production 的说明将工作流文件夹转换为分组(运行 node scripts/folders-to-groups.mjs;若没有文件夹包含工作流,再运行 --suggest;显示建议的分组,保存,然后删除 data/workflow-folder-mode.json);构建,启动一次 npm run dev 以迁移本地数据库,然后验证托管的 private/share 构建。读取每个现有工作流,并在构建前添加或更正其 businessGraph 和 description;如果工作流包含 export const criteria 或写在其代码中的 ICP 或 persona 规则,则按照 Criteria 的说明将其移到 readIcp/readPersona,并说明哪些工作流有变更。保留所有工作流 ID。通过 npm run dev 重启本地服务器,并验证其启动设置和图表页面;托管升级须在就绪检查后验证已部署副本,并重新运行托管设置,以重新应用 share rate limits。
分组分组位于 workflows/groups.ts(viewer):仅有一层;一个工作流最多属于一个分组;文件中的顺序即页面显示顺序。只列出独立工作流:一个工作流若仅由另一个工作流启动,则它是阶段,绝不单独列出,而是嵌套显示在启动它的工作流下方,并归属于该工作流所在的分组。创建时,根据新工作流共享的内容在计划中提出分组:选择会读写相同表的工作流所在分组;否则新建一个以任务命名的分组;若工作流独立运行,则不设分组。新建阶段无需分组,说明它会显示在所属流水线下。“将 X 移入 Y”、“拆分 Y”、“重命名 Y”或“将这些内容放入新分组”都是编辑该文件的请求:用一句话说明变更,编辑文件,构建(node scripts/build-viewer.mjs 会拒绝未知 slug、已列出的阶段、属于两个分组的工作流或空分组),提交,并以该分组的链接作为结尾。对于“ 的进展如何”,读取 GET /api/link/group/<id> 获取其 runsUrl,读取工作区 Runs(托管环境使用 /api/viewer/service?v=3&op=workspaceRuns&group=<id>),并为每个工作流读取 op=summary 获取最近一次运行情况;根据这些运行情况回答,并附上该分组的链接。绝不添加分组存储、Move 按钮、文件夹或标签。
删除删除工作流文件和对应的注册表条目、workflows/groups.ts 中的 slug(如果该分组因此变为空,也删除该分组),以及 vercel.json 中对应的 cron;如果该工作流会启动阶段,这些阶段现在可独立运行:在原分组中列出它们,或说明它们会移至 Ungrouped;保留表及其数据,并说明这一点;已连接项目时,说明保存后部署会从托管副本中移除该工作流及其计划。
对于 Create、Update 和 Upgrade,请阅读 viewer。保持业务图有效,并确保每个 Data 标签页都有有效的 viewer.sharePolicy。保存前先构建;缺少共享元数据或元数据不匹配属于编写错误。准备好策略后,Data 才可选择;更改公共链接的访问权限仍需明确的共享请求。

每次保存:用一句话说明将要更改的内容;如果存在 origin/main,先拉取;通过主机的写入路径进行编辑;在 main 上提交,并使用通俗易懂的提交说明;如果存在远程仓库,则推送;验证提交(如果存在远程仓库,还要验证提交已到达 origin/main);最后说明创建、更改或删除了什么。

对于明确说明没有本地运行的主机,相关值存储在主机的环境中(脚手架在任何主机上都不会提供 env.example);每次运行都以推送后且 Deploy 就绪检查通过后的已部署副本为目标;Upgrade 在该检查通过后,通过已部署的图表页面进行验证,而不是重启服务器。无论 Agent 还做什么,这类主机都必须遵守以下两条规则:

  • 链接:可以启动开发服务器或本地构建进行检查,但绝不发布其提供的任何内容。回复中只能包含就绪等待后由已部署副本的链接路由返回且已验证的 viewerUrl;回复中绝不能出现 localhost。
  • 密钥:通过受保护的工作流传输读取 GET /api/connections,检查密钥是否存在及其声明用途,包括未使用的服务。使用已保存的 Note 作为服务名称,并按照 Connections 命名规则为代码使用准确的变量名。缺少密钥时,通过私有链接路由返回的已验证 connectionsUrl 直接填写。绝不在聊天中收集密钥。保存的更改会在有意部署后生效;密钥存在并不能证明其有效。
代码遵循的约定
  • 关联结果表: 关联数据定义了可选的 data 注册表属性,以及只读的 People/Companies 风格查看器。当用户需要浏览相关记录时,请注册此属性。私有 Data 链接来自同一个链接路由。升级期间保留 viewer、data 和 intake。共享是明确的私有查看器操作;它需要 viewer 中描述的带版本号的表/列/关系/行策略。
  • 步骤选择:每一步都采用能完成任务的最简单形式。确定性工作(解析、算术、获取数据、使用已知输入调用提供商或网关 API、发布 Slack 消息)使用普通的 "use step" 函数,不调用模型。对工作流已持有的文本进行判断(评分、分类、提取、起草),使用单次 AI 步骤,即带有 schema 的 generateObject。只有用户指定了 Agent,或工作需要模型自行选择工具调用(查找合适的端点、跨页面研究),且无法预先写出一系列普通步骤和 AI 步骤时,才使用 Agent 阶段(runAgent);如果某个阶段总是按相同顺序调用相同工具,就应直接使用那些步骤。用户要求使用 Agent、但普通步骤就够用时,用一句话说明,并按用户要求构建。
  • 行来自 defaultInput(内联)、"use step" 函数中的提供商或 API 调用、由另一个工作流填充的表,或入站 webhook(在 lib/intake.ts 中使用 export const intake = defineIntake({ secretEnv, signature, eventId, toRow }),并在 workflows/index.ts 中注册为 intake;POST /api/intake/<slug> 会根据原始请求体验证发送方签名,按事件 ID 去重 30 天,并用映射后的行启动一次运行;发送方将事件发送到公开的 share 项目,由其在 Vercel Authentication 之外转发已签名事件;构建或编辑入站 webhook 后,务必告知用户如何设置发送方,参见 deploy.md);绝不能来自工作区文件或仅本地数据。输入采用 lib/rows.ts 中的 RowsInput:{ rows?, maxRows?, maxSpendUsd? };每一行都是一个对象,包含必需的字符串 key,即表的主键;工作流常量是默认值。启动路由会将 POST 请求体合并到 defaultInput 上,因此限制运行范围的请求为 POST { maxRows: 1 }。
  • lib/rows.ts 中的 runRows({ rows, table, step, maxRows, maxSpendUsd, estimateUsd, freshForMs, concurrency?, saveEvery?, fanOut? }) 是循环处理函数;step(row) 是工作流作用域中的普通异步函数,按顺序等待 "use step" 函数,并返回 { ...columns, costUsd }。表按名称传入,并通过 db/tables/index.ts 解析。每 10 行完成后,在一个步骤中保存一次(saveEvery),因为每个步骤都会消耗一次引擎跳转;保存前崩溃不会丢失任何数据,引擎会重放已完成的步骤。每个工作流都会传入 fanOut: { workflow: <its own function>, input, chunkSize }:超过该行数后,运行会将列表拆分成该工作流自身的子运行,每次启动 4 个(每一波在一个步骤中启动),每个子运行获得精确分配的额度上限,并汇总总量,因此任意大小的列表都作为一次运行并返回一个结果。纯步骤工作流的 chunkSize 为 100,含 Agent 阶段的工作流为 20:引擎将每次运行限制为 25,000 个事件,而每行 Agent 约消耗 100 个事件。取消父运行时,也会取消其子运行。
  • 每个结果表都有 key、updated_at、cost_usd、error。所有数据库 I/O、每次 cached() 和 generateObject 调用都必须发生在 "use step" 函数内,声明形式为 async function name() { "use step"; },绝不能使用箭头函数或方法形式;每个付费或 AI 步骤都设置 name.maxRetries = 0。
  • 读取应一次性从数据库取出所需的小量数据:Postgres 发送给应用的每个字节都会计入 Neon 数据传输,因此读取时要明确列出列名(db().select({ key: t.key, score: t.score }),宽表绝不能使用 select();读取个人资料时使用 getProfile(db, entity, key, { columns }));筛选、计数和聚合都在 SQL 中执行(count(*)::int、jsonb_array_elements、= ANY(...));每个步骤只读取一次所需数据并保留,不要针对每个项目重复读取同一行;列表要分页;任何步骤都不能把整张表读入 Node 后再循环处理。所有读取路径都必须遵守 lib/read-budgets.ts 中的预算;本技能中的 tests/read-budgets.test.ts 会强制执行这些预算,local 说明了如何在发布前测量工作流自身的读取量。
  • Agent 阶段:工作流中的 Agent 始终通过 lib/agent.ts 中的 runAgent() 运行,并从工作流作用域中的普通异步函数(阶段函数,即一个 agent 节点)调用;绝不能直接使用 WorkflowAgent,也绝不能在 "use step" 函数内调用。该辅助函数会将每次模型调用和工具调用都包装成步骤,使用 sleep() 与时间限制竞速,清理 schema,并返回实际成本。其配置涵盖全部编写选项:model 和 reasoning(省略 model 时会使用 lib/models.ts 中的 gtmModel(),见下一条规则;reasoning 默认使用 GTM_REASONING,否则为 high)、instructions、skills(名称取自 skills/index.ts,每个技能都是 skills/ 下的文本模块)、tools(按名称指定的 mcp 服务,含 url、keyEnv、allow、maxCalls;免费的 web.fetch 是一个步骤;web.search 由 AI Gateway 执行(true 或 "gateway",使用 Exa,支持任何模型,无需密钥)或由 OpenAI 执行("openai",仅限 openai/* 模型),在模型调用内部执行而非作为步骤,因此无法要求批准或限制调用次数;custom 工具的 execute 必须是 "use step" 函数)、approve(需要破坏性操作确认或用户明确要求审核的工具名称;默认绝不添加常规审批关卡;该行会等待 hook,而运行的读取路由会列出请求,直到 POST /api/runs/<id>/approve 作出决定;审批可能等待时,按天设置 timeout)、stream(通过 GET /api/runs/<id>/stream 获取每个模型和工具事件)、maxSteps、maxUsd(软限制:Agent 在超出额度的那次调用后停止使用工具,并概述已有结果)、timeout(持续时间字符串;该行会失败)、schema(纯文本时可省略)、prompt 或 messages,以及 WorkflowAgent 支持的其他选项:agent(构造函数选项:采样、提供商选项、prepareStep、prepareCall、回调、遥测、上下文)和 call(每次调用的选项:toolChoice、activeTools、转换)。Schema 使用可为 null 的字段,绝不能使用可选字段,也不要使用字符串格式(使用 z.string(),不要使用 .url() 或 .email());字段含义写在描述中。绝不向 SDK 传入 timeout: 或 AbortSignal.timeout();绝不手动构建 MCP 客户端或工具。example-research.ts 是参考示例。
  • 单次 AI 步骤是在一个步骤内调用 generateObject({ model: gtmModel(), ... });任何使用工具的操作都属于 runAgent 阶段。工作流代码绝不指定模型 ID,也不直接读取 GTM_MODEL:模型 ID 仅存在于 lib/models.ts 中。用户选择了模型时,gtmModel() 返回 GTM_MODEL;否则根据 GTM_AGENT_BACKEND 返回默认模型:claude 使用最新的 Claude Haiku,codex 或其他任何值使用 GPT-6 Luna,确保托管副本与作者本地使用的模型系列一致。setup --deploy 会将 GTM_AGENT_BACKEND 复制到 Production,此时它仅用于选择默认模型。用户为部署副本指定模型时,应在 Vercel 项目(Production)中设置 GTM_MODEL,不要写入代码;只有确实需要不同模型的阶段,才单独设置 model。阶段的后端根据冻结的环境变量按每次运行决定:GTM_AGENT_BACKEND(claude 或 codex:使用作者的订阅,作为一个步骤,仅在此计算机运行;MCP 服务和 Web 工具仍可使用;codex 需要为每个 MCP 服务设置 allow 列表),或在未设置时使用 Gateway。使用自定义工具、approve、stream 或 messages 的阶段,以及托管副本上的所有阶段,始终通过 Gateway 运行。阶段上的 backend 仅覆盖该阶段的变量值;绝不要仅为选择本地或托管环境而设置 backend,环境变量才负责选择。不存在其他 AI 路径。
  • 标准:工作流绝不将 ICP 或用户画像复制到代码中(不从文件抄录职位名称列表、规模限制或规则文本)。运行开始时按 slug 读取:在 runRows 之前、工作流作用域中调用一次 lib/criteria.ts 中的 const icp = await readIcp("<slug>") 或 readPersona("<slug>"),并将结果传递给行步骤。每个函数都返回 { name, text, fields, signals, disqualifiers }:text 是供 AI 步骤提示词使用的文件内容;fields 按标签存放每个已声明字段(值为 Unknown 的字段会被省略);items(fields["Job titles"]) 用于拆分列表;规则步骤从中获取职位名称、规模、地点和排除条件。规则步骤会检查其行数据能够回答的每个已声明字段,并在原因中列出其无法检查的每个已声明字段(例如行数据没有语言信息时,用户画像中的 Languages),绝不能默默跳过任何字段。本地运行每次都会读取文件;构建时会将文件嵌入 lib/criteria.generated.ts(已加入 gitignore),供部署副本使用,因此 ICP 或用户画像的修改会在推送触发部署后应用于下一次运行。仍处于 freshForMs 时限内的行会保留使用旧标准生成的评分。找不到文件时,运行会按文件名报错。
  • 文档注释:第一行是标题,第二段是用一句话写成的摘要;如果 viewer.description 缺失,流程图页面会回退到该摘要。
  • 工作流描述:创建、更新和升级时,填写 viewer.description,用通俗语言说明整个工作流的功能,约 280 个字符,且不得超过 500 个字符(超过 500 个字符会导致构建失败);修改导致描述不准确时,应重写描述。参见 viewer。
  • Viewer 元数据:将 viewer.businessGraph 作为字面量编写,包含稳定的节点 ID、易懂的标签、输入/操作/决策/输出类型、说明以及有意义的决策边。提供商和缓存详情应放在明确的所属字段中。创建、更新和升级时,都要对照实际工作流代码检查流程图;保留 viewer.id。Schema 和验证规则见 viewer。
  • 查阅位置:创建/更改、部署/升级、常规运行以及用户明确提出请求后,按照 entry links 操作。将规范的私有 viewerUrl 作为 打开 GTM 工作流 返回。这与发放外部分享权限是两回事。
  • 连接:编写付费或 AI 步骤前,以及每次运行前检查时,都要读取目标连接清单。对缺少的连接只说明一次,并打开可信的 Connections 表单;判断需要哪个密钥时,使用工作流声明的网关关系。将字面量 viewer.connections 与业务图一同编写,每个代码读取的密钥(准确变量名)对应一项,以便工作流的 Connections 选项卡显示其已设置或缺失。未知密钥、激活状态和安全输入凭据的流程见 Connections。
  • 网关:使用主机通过网关工具(数据市场、MCP 服务)提供的提供商的付费步骤,应在代码中使用网关自身的密钥调用网关,密钥为通过 Connections 管理的 <GATEWAY>_API_KEY,绝不能绕过网关直接调用其背后的提供商。如果不清楚网关的 HTTP API,应先阅读其文档再编写步骤;不要猜测。
  • 原生使用引擎功能(参见 local.md):hook 或 sleep 是一个 wait 节点,子工作流是一个 sub 节点,runAgent 阶段是一个 agent 节点。
  • 联系人员:lib/notify.ts 中的 notify({ kind, text, approval?, target }) 会向 GTM Agent 的通知路由发送内容,该路由使用 Agent 的机器人令牌直接将文本发到 Slack,不调用模型;tell 和 show 是普通消息,ask 和 handoff 会创建一条消息并由 Agent 监听其对话线程,因此人员在该线程中的回复会唤醒 Agent。如果工作流项目设置了 GTM_AGENT_URL 和 GTM_NOTIFY_SECRET,阶段中的审批会自动发送通知。发往何处由代码决定:在工作流顶部定义 const NOTIFY = { channelId: "C0…" },并将 notify: input.notify ?? NOTIFY 传给每个阶段,将 target 传给每次 notify 调用;消息会作为顶层消息发到该频道,绝不会发送到启动运行的对话中。每行新闻都通过 runRows 发送,绝不在步骤内调用 notify:使用 notify: { target: input.notify ?? NOTIFY, every: "chunk", line: (row, columns) => … };every 可设为 row(每完成一行发送一条消息)、chunk(每批行发送一条消息,因此扇出后每个子运行发送一条;建议默认使用此项)或 run(结束时发送一条包含总量的消息)。内容应是数量,而不是判断;line 返回字符串,或在该行适合发送丰富消息时返回 { text, blocks }。阶段设置 notify: false 时,其审批通知会被静默处理。仅当工作流未指定通知位置时,才会使用 Agent 项目的 GTM_NOTIFY_CHANNEL 作为后备值。工作流调用 Agent 只为联系人员,绝不用于思考。
  • 只有在证明直接调用 SDK 会失败或重复扣费后,才允许将辅助函数加入 lib/;其他情况都应直接调用 SDK,具体示例见 patterns。
  • 路由(托管环境:位于 Vercel Authentication 之后,可在笔记本电脑上通过 vercel curl 访问,或由托管 Agent 使用主机提供的绕过方式访问;本地环境无需密码,仅此计算机可访问,且不得从其他网站触发更改):POST /api/run/<slug>、GET /api/run/<slug>(仅限 Vercel Cron,使用 CRON_SECRET;本地环境会拒绝)、POST /api/intake/<slug>(无 bearer;使用发送方签名;发送方通过 share 项目的中继发送)、GET /api/runs/<id>(状态、输出、错误和 approvals,待处理项优先)、POST /api/runs/<id>/approve,请求体为 { token, approved, reason? }、GET /api/runs/<id>/stream(Agent 阶段启用流式输出时,每行一个 JSON 事件)、POST /api/runs/<id>/cancel、GET /api/link/<slug>、GET /api/link/group/<id>。基地址:本地环境为 http://localhost:3939,部署副本使用 GTM_WORKFLOW_URL。当 GTM_AGENT_HOSTED 为 1 时,主机会为 Agent 无法访问范围之外的每次调用添加 Vercel 的保护绕过信息;不要发送凭据。

输出

workflows/<slug>.ts、其数据表和迁移、注册表条目、workflows/groups.ts 中对应的分组、定时运行时的 vercel.json 定时任务、图表页面、结果表中的行,以及项目连接后每次推送都会刷新的部署副本。

对于队列、提供商、MCP 或 CLI 故障,在归因原因或建议重试之前,请先阅读运行时可靠性。

异常

需要同时安装 gtm-workspace 技能;如果缺少 ../gtm-workspace/SKILL.md,请说明:用安装此技能的相同方式安装,即运行 npx skills add eliasstravik/gtm-skills -s gtm-workspace -y(如果此技能位于全局技能目录中,则添加 -g),然后重试。缺少密钥会在运行开始前中止运行,并指出缺少的密钥。在 Vercel 上没有 DATABASE_URL 的生产构建会有意失败;请报告这一点,并说明数据库通过 Neon 集成添加,具体如 deploy.md 所述。要在部署副本上运行,需要一个正在运行的项目(电脑上已关联的 workflows/.vercel/project.json;托管 Agent 上的 GTM_WORKFLOW_URL);如果没有,请改为说明如何像 Deploy 一样将其上线(在已登录 CLI 的电脑上运行共享设置 --deploy;gtm-agent 可选)。包含 scripts/gtm.ts 的 workflows/ 属于旧版运行时,Upgrade 不会修改它:请提供 updates 中的迁移方案(将其放到一边,保留在工作区的历史记录中,其部署副本会持续运行,直到下一次推送,然后在当前运行时上重建)。

质量检查

  • 每个工作流都有有效的业务图,其标签、分支和结果与实际代码一致。业务行为发生变化时,元数据也必须在同一次更改中更新。元数据构建必须通过;编译器图不能替代缺失的业务元数据。
  • 每个步骤都应采用能完成任务的最简单类型:确定性工作不使用 AI 步骤;若按固定顺序使用普通步骤和 AI 步骤即可完成,则不设置 Agent 阶段,除非用户指定了 Agent。
  • 对于新建或已更改的工作流,在首次运行超过一行数据之前,已通过 question 工具提供单行测试;单行请求无需先询问即可运行;每次运行前都已说明费用。
  • 结果表有四个固定列;付费步骤和 AI 步骤的 maxRetries = 0;工作流作用域内没有 cached() 或 generateObject 调用。
  • 每个 Agent 阶段都是工作流作用域内的 runAgent() 调用,并设置了 maxSteps、maxUsd 和 timeout;没有 WorkflowAgent 导入、没有 timeout: 选项、没有 AbortSignal.timeout、架构中没有 .url() 或 .email()、输出架构中没有可选字段;新建或已更改的 Agent 阶段已在单行数据上测试,回复中列明了其工具调用、停止原因和实际费用。
  • 每个工作流都通过 fanOut,并将自身作为参数;包含 Agent 阶段时,chunkSize 为 20;工作流导入的模块不会在 "use step" 函数之外访问数据库;仅路由使用的代码放在 *-api.ts 文件中。
  • 普通 Run 回复以结果结尾,且不重复任何条目,除非其地址发生变化或用户明确要求;保存操作以创建、更改或删除的内容结尾。

参考资料

interactions 对话;local and production 用于了解可连接到生产环境的原生命令;imports 用于了解 CSV 导入,导入会使用文件中的行启动运行;local.md 用于了解启动、密钥、数据、计划任务、表格、查看器、Agent 阶段、扇出、接入、通知和发现记录;patterns 用于查看带有完整示例的原生 SDK 调用;deploy.md;模板中的 templates/workflows/example-scores.ts 是参考实现,templates/workflows/example-research.ts 是 Agent 阶段参考(运行 node scripts/template.mjs 会打印文件夹);来自 gtm-workspace 的:interaction、contract。

评论

支持 Markdown