@cargo-ai/workflow-sdk (中文(简体) )
在 TypeScript 中定义货物工作流程 .
import { z } from "zod";
import { defineWorkflow, connectorRef } from "@cargo-ai/workflow-sdk";
export default defineWorkflow(
"name-and-domain-to-email",
{
input: z.object({
first_name: z.string(),
last_name: z.string(),
domain: z.string(),
}),
output: z.object({ email: z.string().nullable() }),
uses: {
hunter: connectorRef("7eff455t-…", "hunter"),
leadmagic: connectorRef("9c26d1f2-…", "leadmagic"),
},
},
({ input, uses }) => {
const hunter = uses.hunter.findEmail({
first_name: input.first_name,
last_name: input.last_name,
domain: input.domain,
});
const leadmagic = uses.leadmagic.findEmail({
firstName: input.first_name,
lastName: input.last_name,
domain: input.domain,
});
return { email: hunter.email ?? leadmagic.email ?? null };
},
);
编译的产出是 Node[] 现有货运发动机通过同一方式接受的JSON文物 cargo-ai orchestration release deploy-draft CLI画布用户今天运行. 以 TypeScript 编写的工作流程和在画布上同样运行的工作流程 .
抬头: 身体回调是 解析后未执行. SDK读取函数的源,将其重写入引擎的有线格式,并逐字逐字地运出——所以你体内的任何写作都必须是支持的JS表达(参见: 得到支持的联署材料 页:1 关闭变量、同步代码、抛出和
console.log电话在这里不适用.
部署
使用 CLI 直接从源头部署工作流程模块 —— 它将 TypeScript 编译, 并将生成的节点作为新发布 :
# Preview compiled nodes + form fields (no API calls)
cargo-ai orchestration release deploy-draft --file ./my-workflow.ts --dry-run
# Deploy to an existing workflow (minor version auto-bumped)
cargo-ai orchestration release deploy-draft --file ./my-workflow.ts \
--slug name-and-domain-to-email
使用CLI 软件 --slug (或 --workflow-uuid)针对正确的工作流程. 然后运行模块通过 defineWorkflow时,发出 Node[] 图,POST将其输入画布使用的同一放出端点.
教学
1. 界定投入和产出
defineWorkflow(slug, options, build) 这是唯一的切入点. options.input 是 Zod 对象计划,成为工作流程的形式字段(每个顶级键一个). options.output 是描述工作流程回放形状的 Zod schema. 这个 build callback 得到一个范围控件并返回工作流程的结果.
defineWorkflow(
"score-lead",
{
input: z.object({
first_name: z.string(),
score: z.number(),
}),
output: z.string().nullable(),
},
({ input }) => input.first_name,
);
2. 呼叫工具/代理/连接器/本地助手
瞄准镜手柄暴露了 model.* 存储命名空间和独立的本地助手( )allocate, (中文(简体) ), classify, (中文(简体) ), delay, (中文(简体) ), python, (中文(简体) ), scoring, (中文(简体) ), fileSearch, (中文(简体) ), writeFile, (中文(简体) ), readFile, (中文(简体) ), getFileUrl, (中文(简体) ), sendEmail, (中文(简体) ), waitEmailEvent, (中文(简体) ), listEmailEvents+ 输入手柄(input页:1 uses 映射(您在页眉中声明的工具/代理/连接符-见下文),内线 ai() 帮助者 js() 和流动的辅助者balance, (中文(简体) ), split, (中文(简体) ), humanReview, (中文(简体) ), memory 看到了吗 第7节) (中文(简体) ). 没有 native 弹丸登记和无 integrations 登记范围。 每个调用返回一个可以点-访问的打出手柄——但调用本身在解析时间被捕获并作为工作流程节点被放出:
defineWorkflow(
"find-and-verify",
{
input: z.object({ first_name: z.string(), domain: z.string() }),
output: z.string().nullable(),
uses: {
hunter: connectorRef("9c26d1f2-…", "hunter"), // a connector, by handle
validate: toolRef("3e0c5fde-…"), // a tool, by handle (see §2)
},
},
({ input, uses }) => {
const found = uses.hunter.findEmail({
first_name: input.first_name,
domain: input.domain,
});
const verified = uses.validate({ email: found.email });
return verified.is_safe ? found.email : null;
},
);
上面的例子发出两个节点(a) hunter.findEmail 连接器和 a validate 连接到工作流程的链条 end 节点。 工具、代理和连接器没有弹壳登记册——通过手柄参考 uses (下册). 每个 uses.<key> 调用输入到引用资源输入和输出中 uses.validate({ email }) 需求 email 返回 a Ref 您可以访问点( t)verified.is_safe) (中文(简体) ).
土著的平台行动通过手写表面被称为同样的方式—— model.* 存储命名空间和独立的助手 :
({ input, model, allocate }) => {
const accounts = model.search({ modelUuid: input.modelUuid, limit: 50 });
const result = allocate({
recordId: input.recordId,
type: "territory",
territoryUuid: input.territoryUuid,
});
return result.member;
};
说明: 没有运行时间
integrations要填充的机体注册 - 连接器通过手柄调用uses,而不是通过捆绑的注册。 运行cargo-ai project types生成Integrations类型 页:1 工作空间,所以在uses自动完成它的真实动作(参见 登记册的输入) (中文(简体) ). 平台的原生动作是手写并有强烈型号的平面与SDK相通相通相通相通相通相通相通model.*——不需要同步. 工具、代理和连接器都通过手柄通过uses。 。 。 .
这个 tools 编号 : agents 注册密钥是 模板弹片 —— 工作空间工具和代理没有自己的弹子,只有他们创建的模板. 参见a 具体 工具/代理工具(没有可用模板 sattle 的工作空间实例,或 @cargo-ai/cdk defineTool 编号 : defineAgent 在工作流程中声明 uses 并按键称呼它——所以uuid留在头部,而不是分散在身体上:
import { toolRef, agentRef } from "@cargo-ai/workflow-sdk";
defineWorkflow(
"qualify",
{
input: z.object({ email: z.string() }),
output: z.object({ verified: z.unknown(), answer: z.unknown() }),
uses: {
validate: toolRef("3e0c5fde-9f0e-4f60-b4cb-8a4e29d8a001"),
qualify: agentRef("7b7a2f64-2f5c-49c0-9c19-d6a01a2b4c11"),
},
},
({ input, uses }) => {
const verified = uses.validate({ email: input.email });
const answer = uses.qualify({ prompt: `qualify ${input.email}` });
return { verified, answer };
},
);
uses 接受任意 { uuid, resource } 手柄. toolRef(uuid) 编号 : agentRef(uuid) 是原始uuid的逃生舱; a @cargo-ai/cdk 手柄已经是这样的参考,所以直接通过(uses: { enrich: enrichTool })——部署后命令依赖,并注入真正的uuid.
imports —— 参考构建时控件名称和常数
uses 覆盖您的资源 调用 (工具,代理,连接器). 不过 model.* 编号 : allocate 带一个 乌伊德,不是一个可调用的——所以一个模型,领土,或能力把手不能骑入 uses。并且因为身体是从源头解析的(Function.prototype.toString()),赤裸裸的关闭参考像 accounts.uuid 无法解析: 解析器只看到名称 accounts,而JavaScript 无法读取字符串函数的关闭. 参考它抛出 identifier 'accounts' is not in scope。 。 。 .
imports 缩小了差距。 列出由标识符键入的外部值:
import { defineModel, defineTerritory } from "@cargo-ai/cdk";
const accounts = defineModel("accounts", {
/* … */
});
const inbound = defineTerritory("inbound", {
/* … */
});
defineWorkflow(
"route-inbound",
{
input: z.object({ recordId: z.string(), domain: z.string() }),
output: z.unknown(),
imports: { accounts, inbound },
},
({ input, model, allocate }) => {
model.upsert({
modelUuid: accounts.uuid, // deferred token — deploy-ordered & resolved
matchingColumnSlug: "domain",
matchingValue: input.domain,
});
return allocate({
recordId: input.recordId,
type: "territory",
territoryUuid: inbound.uuid,
});
},
);
A 类 @cargo-ai/cdk 手柄 .uuid 保留a 推迟符号: 它将逐字记录成节点配置, 所以主机命令依赖性(accounts 首先部署),然后在实际部署的uid中进行互换——就像 uses。普通常数(imports: { limit: 50 }在配置位置和计算表达式中,以文字表示.
因为身体仍然引用真实的外接, TypeScript 类型检查 accounts.uuid 与实际手柄相对 imports 是一个纯粹的运行时间 通过。 两个限制: 只能使用被推迟的符号 直接作为配置值 (不在内) {{ }} 表达式 / 模板文字,其中主机的部署- 时间分辨率从未到达—— 解析器拒绝),以及 imports 被 SDK 解析器所遵守, 因此一个赤裸的标识符只有在其中列出值时才有效 .
连接器在 uses ——唤作接线人的方法
连接器总是在 uses 被召为 uses.<key>.<action>(input) 没有 integrations 机构登记。 以一个 @cargo-ai/cdk defineConnector 操作或使用 connectorRef(uuid, integrationSlug),然后按关键来称呼其行动。 这把针 具体 连接器实例(它的uuid骑行在所发射的节点上而不是运行时从弹出中解开)——这正是当有两个HubSpot连接器(prod + sandbox),当需要再生接针,或者当您正在给CDK手柄接线时你想要的:
import { connectorRef } from "@cargo-ai/workflow-sdk";
defineWorkflow(
"sync-contact",
{
input: z.object({ email: z.string() }),
output: z.object({ ok: z.unknown() }),
uses: {
// a specific connector — or pass a `defineConnector` handle directly
hs: connectorRef("9c26d1f2-…", "hubspot"),
},
},
({ input, uses }) => {
// a connector exposes its actions: `uses.<key>.<actionSlug>(input)`
const ok = uses.hs.upsertContact({ email: input.email });
return { ok };
},
);
与工具/代理不同(单机) uses.<key>(input)),连接器是 动作映射 —— 说 uses.<key>.<actionSlug>(input) ——按集成同步动作计划打出(运行) cargo-ai project types当弹壳被知道, 或者一个松散的地图。 A 类 @cargo-ai/cdk defineConnector 控件已经是一个连接器,所以 uses: { hs: hubspot } 工作与部署在此工作流程前命令连接器并注入其真实的uuid.
按呼选项——重试+倒计时. 任何注册呼叫都需要一个可选的第二个参数来设置节点 重试 政策和/或让失败 继续 而不是停止运行 :
// uses: { hunter: connectorRef("9c26d1f2-…", "hunter") }
({ input, uses }) => {
// retry up to 3 times; on final failure, continue to the next node
const email = uses.hunter.findEmail(
{ domain: input.domain },
{ retry: 3, continueOnFailure: true },
);
return email;
};
retry 要么是正整数(简指 maximumAttempts)或(或) { maximumAttempts?, initialInterval?, backoffCoefficient? } (间隔数以秒计). continueOnFailure: true 向节点的正常后继者路径失败( 最后一个节点掉入工作流程中) end)而不是停止跑步. 这两个值必须是静态的。 一样 (input, options) 形状在 uses.*, (中文(简体) ), model.*当地援助者 memory.*。 。 。 .
3. 使用非典型的JS控制流量
解析器将熟悉的JS控制流构建成相应的本土节点:
if (a) { … } else { … }→branch节点if (a) … else if (b) … else …→switch节点,每个路径一个else iffor (const item of array) { … }→group节点延后arrayreturn expr在块内以expr
defineWorkflow(
"qualify",
{
input: z.object({ score: z.number() }),
output: z.string().nullable(),
},
({ input, ai }) => {
if (input.score > 80) {
return ai("write a VIP greeting");
} else if (input.score > 50) {
return ai("write a standard greeting");
} else {
return null;
}
},
);
4. 普通的联署材料表达方式
任何地方你都会写一个值——一个对集成呼叫的论证,一个的右侧 const页:1 return 表达式, a if 条件——您可以使用从下面列出的被支持子集所构建的普通JS表达式. SDK 重写标识符到匹配 nodes.<slug> 路径,将打印源字符串传送到引擎 {{ <expr> }}。 。 。 .
defineWorkflow(
"compose",
{
input: z.object({ first: z.string(), last: z.string() }),
output: z.string(),
},
({ input }) => `${input.first.toLowerCase()} ${input.last.toLowerCase()}`,
);
上面的身体编译为单一 end.variables[0] 其价值为 {{ \${ nodes. start. first. to LowerCase ()}${ nodes. start. last. to LowerCase ()}\\\\\\\\\\ n.
几个全球可直接使用: Date, (中文(简体) ), Math, (中文(简体) ), JSON, (中文(简体) ), Number, (中文(简体) ), String, (中文(简体) ), Boolean, (中文(简体) ), Array, (中文(简体) ), Object, (中文(简体) ), RegExp (例如:. new Date(), (中文(简体) ), Math.random()) (中文(简体) ). 他们被评估了 运行时的每个记录这样 new Date() 在绘图中,每个记录的写出时间。 其他全球项目crypto, (中文(简体) ), process,.)不可用,在编译时被拒绝.
5. 联合来文通过 js(({ nodes }) => …)
对于不符合所支持的表达式子集(自定义数组操纵,多声明变换等)的逻辑,发出 a native.script 节点为 js()数字 :
defineWorkflow(
"shape",
{
input: z.object({}),
output: z.array(z.object({ x: z.number() })),
uses: { hunter: connectorRef("9c26d1f2-…", "hunter") },
},
({ uses, js }) => {
const search = uses.hunter.findEmail({});
return js(({ nodes }) => {
return nodes.search.results.map((r: { y: number }) => ({ x: r.y }));
});
},
);
js() 将函数的体形逐字逐一地载走。 该功能可能 没有 为: async,而运行时间只显示 { nodes, parentNodes } 参数——关闭变量不在范围.
6. 内部AI完成 ai("…")
ai() 是一个内置的 AI 完成表达式的缩写。 它接受单一模板字符串或字符串连接表达式,并发出一个 templateExpression 与 instructTo: "ai"引擎运行它通过 相同的AI管道 帆布做.
defineWorkflow(
"describe",
{
input: z.object({ company: z.string() }),
output: z.string().nullable(),
},
({ input, ai }) => ai(`write a one-sentence description of ${input.company}`),
);
7. 流动助手 - balance, (中文(简体) ), split, (中文(简体) ), humanReview, (中文(简体) ), memory
一些引擎节点没有JS-语法等同,因此范围将它们暴露为由解析器识别出的名称的辅助函数. 和身体里的其他一切一样,电话是 解析后未执行 ——路由正体必须是内置的零参数箭头/函数正文.
balance([...]) ——圆罗宾. 每个记录都运行一个准确的路由;返回值结合了每个路由的输出:
// uses: { hunter: connectorRef("9c26d1f2-…", "hunter"), leadmagic: connectorRef("9c26d1f2-…", "leadmagic") }
({ input, uses, balance }) => {
const found = balance([
() => uses.hunter.findEmail({ domain: input.domain }),
() =>
uses.leadmagic.findEmail({
firstName: input.first,
lastName: input.last,
}),
]);
return found;
};
split(percentage, { left, right }) ——随机A/B拆分. 记录卷 [0];下卷 percentage 运行 left,其余运行 right数字 :
// uses: { hunter: connectorRef("9c26d1f2-…", "hunter"), leadmagic: connectorRef("9c26d1f2-…", "leadmagic") }
({ uses, split }) =>
split(30, {
left: () => uses.hunter.findEmail({}),
right: () => uses.leadmagic.findEmail({ firstName: "a", lastName: "b" }),
});
humanReview(config, bodies?) – 贴出一条Slack消息并屏蔽,直到审查者点击批准或下降(或超时火灾). 可选 approved 编号 : declined 机构运行在匹配结果上 :
// uses: { hunter: connectorRef("9c26d1f2-…", "hunter") }
({ input, uses, humanReview }) =>
humanReview(
{
connectorUuid: "<slack-connector-uuid>",
channelId: "C0123456789",
title: "Approve outreach?",
content: input.email,
},
{
approved: () => uses.hunter.verifyEmail({ email: input.email }),
},
);
memory.<action>(opts) ——工作流程/工作空间关键值存储. 行动: get, (中文(简体) ), set, (中文(简体) ), remove, (中文(简体) ), increment, (中文(简体) ), decrement, (中文(简体) ), getOrSet每个调用都发出一个节点并返回 a Ref<{ result: T }>数字 :
({ memory }) => {
const count = memory.increment({
key: "emails_sent",
expiresIn: { value: 1, unit: "day" },
});
return count.result;
};
expiresIn 用于 set, (中文(简体) ), getOrSet, (中文(简体) ), increment,以及 decrement· ; scope 默认为 "workflow" (通过) "workspace" 工作流程共享).
得到支持的联署材料
身体回调被解析. 每个表达位置必须是:
| 表单 | 示例 |
|---|---|
| 字义 | 42, (中文(简体) ), "alice", (中文(简体) ), true, (中文(简体) ), null, (中文(简体) ), /regex/i |
| 范围标识符 | input,当地 const 编号 : let页:1 for…of 循环变量 |
| 成员访问 | input.score, (中文(简体) ), enriched.email, (中文(简体) ), rows[0] |
| 可选链条 | enriched?.email |
| 二进制运算符 | a + b, (中文(简体) ), a > b, (中文(简体) ), a === b, (中文(简体) ), a % b |
| 逻辑操作符 | a && b, (中文(简体) ), a || b, (中文(简体) ), a ?? b |
| 一元运算符 | !a, (中文(简体) ), -x, (中文(简体) ), typeof a |
| 初专 | a > 0 ? "yes" : "no" |
| 模板文字 | `hi ${name}` |
| 对象/ 阵列字节 | { a: 1, b: input.x }, (中文(简体) ), [1, 2, ...rest] |
| 方法调用一个值 | email.includes("@"), (中文(简体) ), /re/.test(s) |
| 书记官处电话 | uses.<key>.<action>({...}), (中文(简体) ), uses.validate(…), (中文(简体) ), model.search(…), (中文(简体) ), delay(…) |
ai("…") 编号 : js((rt) => …) | 内置 AI 表达式 / 内置脚本节点 |
| 流程助手呼叫 | balance([…]), (中文(简体) ), split(…), (中文(简体) ), humanReview(…), (中文(简体) ), memory.get(…) |
new / 成员呼叫全球引擎 | new Date(s), (中文(简体) ), Math.random(), (中文(简体) ), JSON.stringify(x) |
机构内部支持的语句级别表格:
const编号 :let约束单一标识符的声明(无分解)if编号 :else if编号 :elsefor (const x of array) { … }return <expr>编号 :return;
其他一切(关闭, try/catch, (中文(简体) ), await, (中文(简体) ), throw, (中文(简体) ), function 声明,分解结构)在编译时被拒绝,并有明显的错误.
工具链要求
SDK读取您的工作流程正文 Function.prototype.toString() 并解析来源。 因此JS引擎的功能必须是你写的代码:
- 用现代目标编译工作流程文件 (ES2022或后期). 下层重写
for…of,模板文字, 和可选的链条 成形 解析器拒绝—— 更糟糕的是,你没有写代码. - 不要乐器工作流程文件. 代码覆盖工具(istanbul/nyc,vitest的默认V8覆盖度为细)将计数器注入函数源并分解解. 将工作流程定义文件排除在仪器之外.
- 标准类型Script类型语法很好——
tsc并且不接触运行时代码就建立条纹类型.
如果尸体无法解剖, defineWorkflow 丢出一个 could not parse the workflow body 解释此制约时出错 .
登记册的输入
您的工作空间的连接器类型来自 cargo-ai project types并写道:
.cargo-ai/cargo-types.d.ts—— 说declare module扩能. 它增加了你的工作空间的集成弹Integrations接口(由每个动作的 schema 生成输入类型) – 现在用于键入uses.<key>.<action>({ … })因此,一个连接器宣布在uses对照真实形状进行类型检查——以及类型@cargo-ai/cdk是因为defineConnector编号 :defineModel针对连接器/提取器的策略配置
添加 .cargo-ai 给您的 tsconfig.json include 带有明显的光泽(".cargo-ai/**/*.d.ts" ——一个赤裸的点-dir路径被TypeScript所忽略. 重新运行 cargo-ai project types 当工作空间的连接器改变.
工具和代理人 没有 同步——通过手柄查询 defineWorkflow是因为 uses 联合国 @cargo-ai/cdk defineTool编号 :defineAgent 手柄,或 toolRef编号 :agentRef("<uuid>")) (中文(简体) ). 平台的本土动作也没有同步:每个工作空间都相同,因此它们与SDK一起以手写为主,有强烈型的表面相接相接相接相接相接model.* 以及独立援助者.
状态
早入道场. 船舶 defineWorkflow 加上手写原生地表(model.* 和以手柄为基础的辅助者 uses 上述表面(工具、物剂、连接器)。 Canvas-side渲染由SDK授权的工作流程是有效的,因为线条格式相同.
SDK今天可以表达的节点类型 :
| 引擎节点 | 你怎么写 |
|---|---|
connector | uses.<key>.<actionSlug>({ … }) (通过 connectorRef / 项目资源控件) |
tool | uses.<key>({ … }) (通过 toolRef / 项目资源控件) |
agent | uses.<key>({ … }) (通过 agentRef / 项目资源控件) |
native (储存) | model.search / upsert / update / insert / remove / record / ask / customColumn |
native (数据助手) | allocate / classify / delay / python / scoring / fileSearch / writeFile / readFile / getFileUrl / sendEmail / waitEmailEvent / listEmailEvents |
native.branch 编号 : switch | if 编号 : else if 编号 : else |
native.group | for (const x of items) { … } |
native.script | js((runtime) => …) |
native.balance | balance([() => …, () => …]) |
native.split | split(percentage, { left, right }) |
native.humanReview | humanReview(config, { approved?, declined? }) |
native.memory | memory.get / set / remove / increment / decrement / getOrSet |
start 编号 : end | 隐性( a)defineWorkflow 框架每个工作流程) |
文档
{\fn黑体\fs22\bord1\shad0\3ahbe\4ah00\fscx67\fscy66\2chffffff\3ch808080}请检查date=中的日期值 (帮助)