Guide · 使用教程
Inngest AI Agent 教程:持久化步骤、人工审批与失败续跑
用 Inngest TypeScript v4 构建带人工审批的 Agent 工作流,演练步骤级恢复、外部写入幂等及 executions 成本。
本教程用 Inngest TypeScript v4 做一个最小的“生成建议—等待人工审批—完成任务”流程,并演练中途故障后从已完成步骤继续。它面向已经有 Node.js/Next.js 应用的开发者。示例中的建议内容是模拟数据,不调用真实模型;验证工作流正确后,再把你的模型 SDK 放进对应的 step.run()。这样可以先把审批与恢复机制跑通,避免把模型账单混进基础测试。
这篇教程会完成什么
一次业务请求发出 demo/agent.requested 事件,工作流保存草稿步骤的结果,等待具有相同 requestId 的审批事件;拒绝或超时即结束,同意后执行最终步骤。你会在本地 Dev Server 查看步骤轨迹,理解为什么一个步骤失败时不必重新执行之前成功的步骤,并为真实外部写入设计幂等键。
准备环境
使用已有 Next.js App Router 项目,安装 inngest:npm install inngest。按官方 TypeScript 快速开始创建 Inngest 客户端与 /api/inngest serve route,把下方函数加入 functions 数组。启动应用时使用 INNGEST_DEV=1 npm run dev,另开终端运行 npx --ignore-scripts=false inngest-cli@latest dev -u http://localhost:3000/api/inngest。若应用使用其他端口,应同步修改 URL。正式部署按官方环境变量与签名验证文档配置,不能把本地开发设置当生产配置。
先定义一个客户端,ID 用你自己的应用标识:
// src/inngest/client.ts
import { Inngest } from "inngest";
export const inngest = new Inngest({ id: "approval-demo" });
第一步:把任务拆成可恢复步骤
下面的函数只保存业务 ID、草稿和审批结果;真实客户资料不应直接放进事件载荷。部署前应给事件数据做 schema 校验,并按访问权限读取业务数据。
// src/inngest/functions.ts
import { inngest } from "./client";
export const approvalDemo = inngest.createFunction(
{ id: "approval-demo", triggers: { event: "demo/agent.requested" } },
async ({ event, step }) => {
const { requestId } = event.data;
const proposal = await step.run("prepare-proposal", async () => {
return { requestId, text: "建议先由人工检查,再执行操作。" };
});
const decision = await step.waitForEvent("wait-for-review", {
event: "demo/approval.received",
match: "data.requestId",
timeout: "24h",
});
if (!decision) return { requestId, status: "timed_out" };
if (decision.data.approved !== true) {
return { requestId, status: "rejected" };
}
const result = await step.run("record-approved-result", async () => {
return { requestId, approvedText: proposal.text };
});
return { requestId, status: "approved", result };
}
);
在 src/app/api/inngest/route.ts 中使用 serve({ client: inngest, functions: [approvalDemo] }) 导出 GET、POST、PUT;官方快速开始提供完整文件布局。这里每个步骤都要有稳定 ID,不要在已有运行尚未结束时随意改变步骤 ID,避免恢复轨迹与预期不一致。
第二步:发送请求与审批事件
在 Dev Server 打开 Functions,选择 approval-demo,用 {"data":{"requestId":"demo-001"}} 调用。运行应完成 prepare-proposal 并停在 wait-for-review。接着从你自己的服务端代码发送:
await inngest.send({
name: "demo/approval.received",
data: { requestId: "demo-001", approved: true },
});
审批事件必须使用相同的 requestId,否则不能匹配等待中的运行。再测试 approved: false 和超过 24 小时的超时分支。真实产品应由已登录、具备权限的审核人通过你的后端接口发送审批事件;不应让网页直接接受任意 requestId 的批准请求。对重复审批与过期审批,后端也要检查业务状态。
第三步:演练失败续跑
把模拟草稿改为真实检索或模型调用时,每次外部调用尽量放进独立 step.run();不要把十步串在一个大步骤里,否则某一步失败会重做整个步骤。让第二个步骤调用一个可控的测试接口,在第一次请求时返回暂时性错误,第二次成功。运行轨迹应显示失败步骤的重试次数,而 prepare-proposal 只保存一次结果。永久性输入错误应停止重试,暂时性的超时才进入有限重试;默认重试次数以当前官方文档为准。
关键边界:step.run() 结果保存之前,外部写入可能已成功但响应丢失。发邮件、扣款、创建工单时,务必使用 requestId + 操作类型 之类稳定的业务幂等键,或通过唯一约束和查询判重。Inngest 的完成步骤复用不能替代外部系统的幂等保障。
第四步:做成本、权限和数据验收
到价格页按当前计划核算:一次函数运行与每个 step.run() 都可能计入 executions;等待、并发步骤、事件量和观察数据也有各自的计划边界。不要把一次 Agent 请求当成一次 execution。模型 token、应用宿主和外部工具费用另外记录。用 100 条真实但脱敏的任务统计每次成功请求的步骤数、重试数、等待时长和总成本,再决定并发阈值。
对敏感资料,只在事件中放业务 ID,数据从受控存储读取。官方加密中间件默认保护步骤数据与函数输出,但事件中只有指定的 data.encrypted 字段默认受保护;不能误以为全部 event.data 自动加密。还应检查日志保留、密钥管理和审批记录的访问权限。
常见错误
- 函数没有出现在 Dev Server:检查 serve route 是否注册
approvalDemo,并核对实际端口和INNGEST_DEV。 - 审批后没有继续:检查事件名、
requestId匹配、审批发送时机与等待期限。 - 重试重复写入:为外部 API 增加稳定幂等键,并演练“成功但回执丢失”。
- 运行状态太大:步骤只返回后续所需的 ID 或短摘要,大文件放受控存储。
- 账单高于预估:统计函数运行与每个步骤,分开核对模型、宿主与平台费用。
资料:TypeScript 快速开始、人工审批、步骤与重试、幂等、限制。
常见问题
- 审批事件为什么没有唤醒工作流?
- 先核对事件名、requestId 匹配字段、审批发送时间和等待超时;再看 Dev Server 运行轨迹。
- 一个步骤失败会重做前面的模型调用吗?
- 已保存的早期步骤结果可复用,但失败步骤本身会重试;外部调用仍需考虑回执丢失。
- 有 step.run 就不用做幂等了吗?
- 仍需。外部副作用可能已经成功,而工作流尚未记录结果,重试可能再次调用。
- 审批可以由前端直接发送吗?
- 应通过受控后端接口校验审核人身份、权限和业务状态,再发送审批事件。
- Free 的 5 万 executions 是 5 万次 Agent 请求吗?
- 不是。函数运行与步骤分别计数,复杂 Agent 的执行数可能远多于请求数。
- 事件里能放客户全文和密钥吗?
- 不要放明文密钥;敏感数据优先只传业务 ID,并按官方加密中间件和访问策略保护。