- 人工智能
- AI Agent
- Agent 框架
- 后端
- 多智能体
- RAG
- 工具调用
- Agent 记忆
【免费下载链接】voltagent
AI Agent Engineering Platform built on an Open Source TypeScript AI Agent Framework
本指南以 VoltAgent 官方示例 examples/with-workflow 为核心,系统讲解 VoltAgent 工作流(Workflow)编排引擎:从
createWorkflow的基本用法、andThen/andAgent等步骤原语,到人工审批、动态 Schema、定时休眠、循环分支与护栏等高级能力。读完本文,你将能基于@voltagent/core的createWorkflowAPI,把 AI Agent 与确定性的业务步骤组合成可运行、可恢复、类型安全的多步骤流程,并在本地一键启动演示服务。
一、Workflow 在 VoltAgent 中的定位
VoltAgent 是一个开源的 TypeScript AI Agent 框架,提供模块化的组件来构建、定制和扩展 AI Agent——从 API 连接到记忆管理、从多模型支持到可观测性。Workflow 模块是其中的"确定性编排"能力:当我们需要把 AI Agent 的灵活性与业务逻辑的确定性结合起来时(例如"先校验订单 → AI 判断风险 → 计算折扣 → 得出最终结论"),就可以用工作流把这些步骤串起来。
在 with-workflow 示例 中,一个完整的工作流演示程序同时注册了9 个不同类型的 workflow,覆盖了 VoltAgent 工作流引擎几乎全部的核心原语:
| # | 工作流 | 演示的核心概念 |
|---|---|---|
| 1 | order-processing | andThen、andAgent、条件逻辑、setWorkflowState/getStepData |
| 2 | expense-approval | suspend/resume人工介入、resumeSchema |
| 3 | content-analysis | andTap、inputSchema数据裁剪、数据转换 |
| 4 | article-summarizer | 基于输入动态生成 Schema |
| 5 | timed-reminder | andSleep、andSleepUntil时间控制 |
| 6 | batch-transform | andForEach、andMap批量处理 |
| 7 | loop-and-branch | andDoWhile、andDoUntil、andBranch循环与分支 |
| 8 | guardrail-workflow | 工作流级与步骤级护栏(Guardrail) |
| 9 | wrapped-agent-call | 在andThen中包装调用 Agent |
这些构造器均由 packages/core/src/workflow/index.ts 统一导出,本文后续会逐个结合源码展开。
二、快速开始:运行 with-workflow 示例
2.1 通过脚手架一键创建
示例 README 提供了最快捷的体验方式——使用 VoltAgent 官方脚手架创建项目:
npm create voltagent-app@latest -- --example with-workflow2.2 本地运行与构建
创建后的项目(即本仓库的 examples/with-workflow)在 package.json 中预置了以下脚本:
"scripts": { "build": "tsc", "dev": "tsx watch --env-file=.env ./src", "start": "node dist/index.js", "volt": "volt" }npm run dev:使用tsx watch监听源码并热重载,入口为./src(即 src/index.ts),可通过.env文件注入模型 API Key;npm run build:执行tsc将 TypeScript 编译到dist/;npm start:运行编译产物node dist/index.js;npm run volt:调用 VoltAgent CLI(@voltagent/cli)进行管理操作。
项目核心依赖包括@voltagent/core(工作流引擎)、@voltagent/logger(日志)、@voltagent/server-hono(HTTP 服务)与zod(Schema 校验),tsconfig.json采用ES2022+ESNext模块与strict模式,保证类型安全。
2.3 启动后的形态
示例末尾将全部工作流注册进VoltAgent实例,并挂载一个 Hono 服务器:
new VoltAgent({ agents: { analysisAgent, contentAgent }, logger, server: honoServer({ port: 3141 }), workflows: { orderProcessingWorkflow, expenseApprovalWorkflow, contentAnalysisWorkflow, articleSummarizationWorkflow, timedReminderWorkflow, batchTransformWorkflow, loopAndBranchWorkflow, guardrailWorkflow, wrappedAgentWorkflow, }, });运行后,这 9 个工作流即可通过 VoltAgent 提供的接口被触发与观察。
三、工作流基础:createWorkflow与 Schema 配置
3.1 函数签名与元信息
所有工作流都通过createWorkflow创建(核心实现在 packages/core/src/workflow/core.ts),其第一个参数是工作流配置对象,第二个及以后的参数是步骤。以示例 1 为例:
const orderProcessingWorkflow = createWorkflow( { id: "order-processing", name: "Order Processing Workflow", purpose: "Process orders with fraud detection and special handling for VIP customers", input: z.object({ orderId: z.string(), customerId: z.string(), amount: z.number(), items: z.array(z.string()), }), result: z.object({ orderId: z.string(), status: z.enum(["approved", "rejected", "needs-review"]), totalWithDiscount: z.number(), }), }, // ... 步骤列表 );配置项说明:
id:工作流唯一标识,用于注册、日志与状态持久化;name/purpose:人类可读的名称与用途描述,便于观测与检索;input:zod Schema,定义工作流的入口数据结构,运行前即做类型校验;result:zod Schema,定义工作流最终输出结构,保证返回值符合契约。
3.2 步骤执行上下文
每个步骤的execute函数接收一个上下文对象,示例中高频使用了以下能力:
data:当前步骤的输入数据(由前一个步骤的输出合并而来);setWorkflowState(updater):写入跨步骤持久化的工作流状态;workflowState:读取前面步骤写入的全局状态;getStepData(stepId):按步骤 id 读取任意历史步骤的输出;suspend(reason, payload):挂起工作流等待外部恢复(详见第五节)。
例如示例 1 中,校验步骤通过setWorkflowState记录校验元数据:
andThen({ id: "validate-order", execute: async ({ data, setWorkflowState }) => { const isValid = data.amount > 0 && data.items.length > 0; setWorkflowState((prev) => ({ ...prev, validatedAt: new Date().toISOString(), validatedBy: "system", })); return { ...data, isValid, baseTotal: data.amount }; }, }),而在最后的决策步骤中,通过getStepData("validate-order")?.output与workflowState.validatedAt分别读取步骤输出与全局状态,完成approved/rejected/needs-review三态判定——这正是andThen串联步骤之间数据传递的标准模式。
四、核心步骤原语:andThen、andAgent、andTap
4.1andThen:确定性函数步骤
andThen是最基础的步骤构造器,源码见 packages/core/src/workflow/steps/and-then.ts,它把一个 async 函数包装为type: "func"的步骤。除id与execute外,它还接受四个 Schema 参数:
inputSchema:限定本步骤可见的输入字段(见 4.4);outputSchema:校验本步骤输出;suspendSchema/resumeSchema:定义挂起载荷与恢复载荷的结构(见第五节)。
从源码可以看到,andThen会捕获WORKFLOW_SUSPENDED/WORKFLOW_CANCELLED信号,将它们作为正常流程而非错误处理,这是挂起/取消机制能在步骤内安全工作的关键。
4.2andAgent:AI Agent 步骤
andAgent是工作流引擎与 LLM 的桥接点。示例 1 用它让analysisAgent对订单做欺诈风险评估:
andAgent( async ({ data }) => ` Analyze this order for fraud risk: Order ID: ${data.orderId} Customer ID: ${data.customerId} Amount: $${data.amount} Items: ${data.items.join(", ")} Provide risk level (low/medium/high) and reasoning. `, analysisAgent, { schema: z.object({ riskLevel: z.enum(["low", "medium", "high"]), reasoning: z.string(), }), }, ),三个参数分别为:prompt 生成函数(接收上下文、返回指令文本)、Agent 实例、以及结构化输出 Schema——Agent 的返回会被强制解析为该 Schema,从而让后续步骤拿到类型确定的riskLevel字段。示例 3 中,andAgent还被用来同时完成情感分析、关键词抽取与摘要生成三类任务。
Agent 本身通过new Agent(...)定义,核心字段是name、model(如openai/gpt-4o-mini)与instructions:
const analysisAgent = new Agent({ name: "AnalysisAgent", model: "openai/gpt-4o-mini", instructions: "You are a data analyst. Provide clear, structured analysis.", });4.3andTap:旁路日志步骤
andTap用于"只观察、不改数据"的旁路步骤(源码 and-tap.ts)。示例 3 用它记录分析开始与最终指标:
andTap({ id: "log-start", execute: async ({ data }) => { console.log(`Starting analysis of ${data.content.length} characters`); console.log(`Language: ${data.language}`); }, }),andTap的返回值不会覆盖当前数据流,适合埋点、日志与指标采集。
4.4inputSchema:控制步骤可见数据
示例 3 的transform-results步骤展示了inputSchema的威力——它让 AI 步骤的输出在进入后续步骤前被"裁剪":
andThen({ id: "transform-results", inputSchema: z.object({ sentiment: z.enum(["positive", "negative", "neutral"]), keywords: z.array(z.string()), summary: z.string(), }), execute: async ({ data, getStepData }) => { const analysisData = getStepData("text-analysis")?.output; return { sentiment: data.sentiment, keywords: data.keywords, summary: data.summary, wordCount: analysisData?.wordCount || 0, }; }, }),这里inputSchema保证data中只包含声明的三个字段,而getStepData仍可跨步骤取回完整的中间数据,实现"数据可见性控制"与"数据回溯"的分离。最后的andTap也通过inputSchema只接收指标字段用于日志打印。
五、人工介入:suspend / resume 模式
示例 2(expense-approval)演示了 VoltAgent 工作流最具特色的能力之一——挂起等待人工决策。步骤内通过suspend暂停整个工作流,外部系统随后携带决策数据恢复执行。
andThen({ id: "check-approval-needed", resumeSchema: z.object({ approved: z.boolean(), managerId: z.string(), comments: z.string().optional(), adjustedAmount: z.number().optional(), }), execute: async ({ data, suspend, resumeData }) => { if (resumeData) { console.log(`Manager ${resumeData.managerId} made decision`); return { ...data, approved: resumeData.approved, approvedBy: resumeData.managerId, finalAmount: resumeData.adjustedAmount || data.amount, managerComments: resumeData.comments, }; } if (data.amount > 500) { console.log(`Expense of $${data.amount} requires manager approval`); await suspend("Manager approval required", { employeeId: data.employeeId, requestedAmount: data.amount, category: data.category, }); } return { ...data, approved: true, approvedBy: "system", finalAmount: data.amount }; }, }),要点拆解:
suspend(reason, payload):挂起工作流,reason供人理解,payload是随挂起事件对外暴露的上下文(如申请人、金额、类别);resumeSchema:声明"恢复时外部传入的数据"结构——在本例中即经理的审批结论、备注与可选的调整金额,运行时会强制校验;resumeData:当工作流被恢复时,该字段携带外部决策数据;步骤据此走"已审批"分支;- 金额 ≤ $500 时自动通过,
approvedBy记为system,体现"规则优先、AI/人工兜底"的编排思想。
挂起/恢复机制在核心层由 core.ts 中的 suspend controller 与状态管理器支撑,并有专门的 suspend-resume.spec.ts 测试覆盖。
六、动态 Schema:让约束随输入变化
示例 4(article-summarizer)展示了一个进阶能力:andAgent的schema不仅可以是一个静态 zod Schema,还可以是接收data返回 Schema 的函数,从而把输入参数(如摘要长度范围)注入到输出约束中:
andAgent( async ({ data }) => `Summarize the following article in ${data.min} to ${data.max} characters: Article: ${data.article}`, contentAgent, { schema: ({ data }) => { console.log(`Generating schema with min: ${data.min}, max: ${data.max}`); return z.object({ summary: z .string() .min(data.min, `Summary must be at least ${data.min} characters`) .max(data.max, `Summary must be at most ${data.max} characters`), }); }, }, ),工作流输入min/max带默认值(50 / 150),既约束了 prompt 的措辞,又通过z.string().min().max()在输出侧强制执行长度校验,实现"prompt 约束 + Schema 强校验"的双保险。这种"数据驱动 Schema"模式特别适合参数化、可复用的 Agent 步骤。
七、时间控制:andSleep 与 andSleepUntil
示例 5(timed-reminder)演示两个时间原语(源码见 and-sleep.ts 与 and-sleep-until.ts):
andSleep({ id: "pause-briefly", duration: ({ data }) => Math.max(0, data.waitMs), }), andSleepUntil({ id: "align-to-next-second", date: () => new Date(Date.now() + 1000), }), andThen({ id: "complete-reminder", execute: async ({ data }) => ({ userId: data.userId, status: "sent", resumedAt: new Date().toISOString(), }), }),andSleep.duration:既可以是毫秒数字,也可以是接收上下文的函数,示例用Math.max(0, data.waitMs)动态计算休眠时长;andSleepUntil.date:接收上下文的函数,返回目标时刻,示例将其对齐到下一秒(Date.now() + 1000);- 从 and-sleep.ts 的实现可以看到,休眠通过
waitWithSignal(durationMs, state.signal)实现,支持被取消信号中断,不会在取消时白白等待。
八、批量处理与循环:andForEach、andMap、andDoWhile、andDoUntil
8.1andForEach+andMap:批量变换
示例 6(batch-transform)的工作流输入直接是一个数字数组(input: z.array(z.number())),先用andForEach对每个元素并发执行步骤,再用andMap汇总结果:
andForEach({ id: "double-each", step: andThen({ id: "double", execute: async ({ data }) => data * 2, }), concurrency: 2, }), andMap({ id: "summarize-results", map: { original: { source: "input" }, doubled: { source: "data" }, count: { source: "fn", fn: ({ data }) => (Array.isArray(data) ? data.length : 0), }, total: { source: "fn", fn: ({ data }) => Array.isArray(data) ? data.reduce((sum, value) => sum + value, 0) : 0, }, }, }),从 and-foreach.ts 源码可见:
concurrency默认值为 1,内部通过"worker 池"模式实现并发——concurrency会被规范化为至少 1 的整数(Math.max(1, Math.floor(concurrency)));- 支持可选的
items(选择数组)与map(逐项塑形)参数; - 空数组时直接返回
[],不会执行任何子步骤。
andMap提供三种取值来源:source: "input"(取工作流原始输入)、source: "data"(取上游步骤数据)、source: "fn"(由函数计算),非常适合"批量处理 + 结果聚合"的流水线。
8.2andDoWhile/andDoUntil:条件循环
示例 7(loop-and-branch)先用两个循环原语构造计数器状态:
andDoWhile({ id: "warmup-loop", step: andThen({ id: "increment-warmup", execute: async ({ data }) => ({ ...data, counter: data.counter + 1 }), }), condition: ({ data }) => data.counter < 1, }), andDoUntil({ id: "retry-loop", step: andThen({ id: "increment-retry", execute: async ({ data }) => ({ ...data, counter: data.counter + 1 }), }), condition: ({ data }) => data.counter >= 3, }),andDoWhile:先执行后判断,只要condition为真就继续循环(至少执行一次);andDoUntil:先执行后判断,直到condition为真才退出(适合"重试直到成功"类场景)。
8.3andBranch:并行条件分支
紧接着,andBranch根据当前counter值分发到不同分支:
andBranch({ id: "categorize-counter", branches: [ { condition: ({ data }) => data.counter >= 3, step: andThen({ id: "mark-ready", execute: async ({ data }) => ({ ...data, label: "ready" as const }), }), }, { condition: ({ data }) => data.counter < 3, step: andThen({ id: "mark-warmup", execute: async ({ data }) => ({ ...data, label: "warmup" as const }), }), }, ], }),需要特别注意的是andBranch的语义(见 and-branch.ts):所有条件会被并行评估(Promise.all),所有条件命中的分支都会执行,结果以数组形式返回。因此在本示例的最后一个andThen中,代码需要从结果数组里挑选实际命中的分支输出:
andThen({ id: "select-branch", execute: async ({ data }) => { const results = Array.isArray(data) ? data : []; const selected = results.find((entry) => entry !== undefined); if (!selected) { return { counter: 0, label: "warmup" }; } return { counter: selected.counter, label: selected.label }; }, }),如果只需要"二选一"的互斥分支,应保证各分支条件互斥(如>= 3与< 3),这正是示例的做法。
九、护栏(Guardrail):输入输出安全过滤
示例 8(guardrail-workflow)演示了 VoltAgent 的护栏机制,可在工作流级与步骤级对输入输出做校验/改写。首先定义两个护栏:
const trimInput = createInputGuardrail({ name: "trim-input", handler: async ({ input }) => ({ pass: true, action: "modify", modifiedInput: typeof input === "string" ? input.trim() : input, }), }); const redactNumbers = createOutputGuardrail<string>({ name: "redact-numbers", handler: async ({ output }) => ({ pass: true, action: "modify", modifiedOutput: output.replace(/[0-9]/g, "*"), }), });随后在createWorkflow配置中挂载工作流级护栏,并在步骤中追加步骤级护栏:
const guardrailWorkflow = createWorkflow( { id: "guardrail-workflow", name: "Guardrail Workflow", purpose: "Applies guardrails to sanitize inputs and outputs", input: z.string(), result: z.string(), inputGuardrails: [trimInput], outputGuardrails: [redactNumbers], }, andGuardrail({ id: "sanitize-step", outputGuardrails: [redactNumbers], }), andThen({ id: "finish", execute: async ({ data }) => data, }), );要点:
createInputGuardrail/createOutputGuardrail:分别创建输入、输出护栏;handler返回{ pass, action: "modify", modifiedInput/modifiedOutput },表示"校验通过但内容被改写"(此处把字符串首尾空格裁掉、把数字替换为*);- 工作流级
inputGuardrails/outputGuardrails对工作流入口、出口全局生效; andGuardrail可在步骤级别插入护栏,实现"局部敏感数据脱敏";- 核心运行逻辑见 packages/core/src/workflow/internal/guardrails.ts,并有 guardrails.spec.ts 测试覆盖。
十、包装 Agent 调用:在自定义步骤中直接使用 Agent
示例 9(wrapped-agent-call)演示了"不用andAgent、也能在自定义步骤中调用 Agent"的灵活写法——即在andThen内部直接调用agent.generateText:
andThen({ id: "maybe-call-agent", execute: async ({ data }) => { if (!data.useAgent) { return { summary: `Skipped agent for ${data.topic}.`, usedAgent: false, }; } const { text } = await contentAgent.generateText( `Write a single-sentence summary about: ${data.topic}`, ); return { summary: text.trim(), usedAgent: true, }; }, }),这体现了工作流的开放性:andAgent是"结构化输出 + Schema 强校验"的便捷封装,而直接调用agent.generateText则把 Agent 调用完全交给开发者控制(例如按条件跳过、组合多次调用)。从源码结构与示例注释看,这种调用方式还继承了父步骤的 OpenTelemetry span,便于链路追踪(对应wrappedAgentWorkflow注释中的 "parent span inheritance")。
十一、把工作流接入 VoltAgent 运行时
所有工作流最终统一注册进VoltAgent实例,并配合日志与服务器:
const logger = createPinoLogger({ name: "with-workflow", level: "debug", }); new VoltAgent({ agents: { analysisAgent, contentAgent }, logger, server: honoServer({ port: 3141 }), workflows: { orderProcessingWorkflow, expenseApprovalWorkflow, contentAnalysisWorkflow, articleSummarizationWorkflow, timedReminderWorkflow, batchTransformWorkflow, loopAndBranchWorkflow, guardrailWorkflow, wrappedAgentWorkflow, }, });agents:注册工作流中引用的 Agent(analysisAgent、contentAgent),供andAgent与generateText解析;logger:使用@voltagent/logger的createPinoLogger,此处设为debug级别以获得更细的步骤执行日志;server:@voltagent/server-hono的honoServer({ port: 3141 })提供 HTTP 服务,便于通过接口触发与查看工作流;workflows:按 id 注册全部 9 个工作流。核心层通过WorkflowRegistry(见 packages/core/src/workflow/registry.ts)统一管理已注册工作流。
十二、工作流全家桶:更多原语与 Chaining API
除本文详解的步骤外,packages/core/src/workflow/index.ts 还导出了其他编排原语,可供进阶探索:
andWhen:条件式步骤(示例 1 注释中提及的 "conditional logic (andWhen)");andAll:并行执行多个步骤;andRace:多个步骤竞争,先完成者胜出;andWorkflow:把一个工作流作为另一个工作流的子步骤,实现嵌套编排。
另外,示例 README 特别提示:如果你更喜欢**流式链式调用(method-chaining)**的写法,可以查看 with-workflow-chain 示例——它使用createWorkflowChain以流畅的链式 API 构建工作流,与本文介绍的createWorkflow函数式写法互为补充。
十三、源码与测试指引
如果想深入理解工作流引擎的实现,建议按以下路径阅读当前仓库:
- 工作流核心实现:packages/core/src/workflow/core.ts(
createWorkflow、挂起/恢复、状态管理与取消信号); - 步骤原语源码:packages/core/src/workflow/steps/ 目录,覆盖
and-then.ts、and-agent.ts、and-tap.ts、and-sleep.ts、and-foreach.ts、and-branch.ts、and-loop.ts、and-map.ts、and-guardrail.ts、and-race.ts、and-when.ts等; - 测试用例:如 core.spec.ts、suspend-resume.spec.ts、guardrails.spec.ts 以及 steps 目录下的各
*.spec.ts,可帮助你验证各原语的真实行为边界。
总结
VoltAgent 的工作流模块在"AI 的开放性"与"业务的确定性"之间提供了优雅的折中:andThen/andTap负责确定性的业务逻辑,andAgent负责接入 LLM 并借助 zod 保证结构化输出,suspend/resume引入人工决策,andSleep*处理时间维度,andForEach/andBranch/andDoWhile覆盖批量、分支与循环,护栏机制则为输入输出安全兜底。通过 with-workflow 示例 的 9 个完整工作流,你可以快速掌握这套编排能力,并将其迁移到自己的 Agent 业务中。
- 人工智能
- AI Agent
- Agent 框架
- 后端
- 多智能体
- RAG
- 工具调用
- Agent 记忆
【免费下载链接】voltagent
AI Agent Engineering Platform built on an Open Source TypeScript AI Agent Framework
相关推荐
Memviz终极指南:如何用可视化图表调试Go数据结构
Memviz终极指南:如何用可视化图表调试Go数据结构 Memviz是一款强大的Go数据结构可视化工具,能够将复杂的Go程序内存结构以直观的图表形式展示出来,帮
用 VoltAgent 的 createWorkflowChain 以链式 API 编排 AI Agent 工作流
用 VoltAgent 的 createWorkflowChain 以链式 API 编排 AI Agent 工作流 导读 VoltAgent 是一个开源的 Ty
人工智能AI AgentAgent 框架后端多智能体RAG工具调用Agent 记忆Agent 工作流AI 评测MCP 服务MCP Clients语音构建复杂的多步骤AI工作流:ell高级编程完全指南
构建复杂的多步骤AI工作流:ell高级编程完全指南 在当今AI应用开发中,构建能够处理复杂多步骤任务的智能系统已成为关键需求。ell作为专业的语言模型编程库,提
人工智能大模型提示工程LLMOps
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考