inngest-agents

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Inngest Agents

Inngest Agents

Use this skill when the user wants to build, migrate, or debug an AI agent, multi-step AI workflow, tool-calling loop, support agent, research agent, human-in-the-loop review flow, or realtime agent UI.
Inngest's AgentKit defines agents with
createAgent
; when an AgentKit run is owned by an Inngest function, model calls use Inngest
step.ai
so they retry and cache model results durably. Use the lower-level Inngest step primitives around the agent for database reads/writes, tool side effects, waits, approvals, realtime progress, and flow control.
Official references:
当用户想要构建、迁移或调试AI Agent、多步骤AI工作流、工具调用循环、支持Agent、研究Agent、人机协同审核流程或实时Agent UI时,可使用此技能。
Inngest的AgentKit通过
createAgent
定义Agent;当AgentKit运行由Inngest函数托管时,模型调用会使用Inngest
step.ai
,从而实现模型结果的持久化重试与缓存。可在Agent周边使用底层Inngest步骤原语来处理数据库读写、工具副作用、等待、审批、实时进度和流程控制。
官方参考文档:

Copyable Example

可复制示例

When starting a durable support or tool-calling agent from scratch, inspect the companion example at
../../examples/durable-agent
. It shows the expected agent-first shape: quick HTTP trigger, typed events, AgentKit inside an Inngest function, step-scoped context loading, human approval with
step.waitForEvent
, and durable side effects after approval.
从零开始构建持久化支持型或工具调用型Agent时,可查看配套示例
../../examples/durable-agent
。该示例展示了以Agent为核心的预期架构:快速HTTP触发器、类型化事件、Inngest函数内部的AgentKit、步骤作用域的上下文加载、基于
step.waitForEvent
的人工审批,以及审批后的持久化副作用处理。

When to Use Inngest for Agents

何时为Agent使用Inngest

Good fit:
  • Agent can take longer than one HTTP request.
  • Agent calls tools, APIs, databases, browsers, sandboxes, or MCP servers.
  • Agent needs to survive deploys, crashes, serverless timeouts, or model/API failures.
  • Agent may wait for human approval, external callbacks, scheduled follow-up, or user input.
  • Agent progress should stream to a UI from the durable workflow.
  • Model/provider calls need concurrency or throttle limits.
  • Duplicate sends, charges, writes, or model calls would be costly.
Not usually worth it:
  • One short, read-only model call with no side effects and no need for durable progress.
  • UI-only autocomplete where losing the request is acceptable.
适用场景:
  • Agent运行时间超过单次HTTP请求时长。
  • Agent需要调用工具、API、数据库、浏览器、沙箱或MCP服务器。
  • Agent需要在部署、崩溃、无服务器超时或模型/API故障后仍能正常运行。
  • Agent可能需要等待人工审批、外部回调、定时跟进或用户输入。
  • Agent进度需要从持久化工作流流式传输到UI。
  • 模型/服务商调用需要并发或限流控制。
  • 重复发送、计费、写入或模型调用会产生较高成本。
通常不适用的场景:
  • 单次简短的只读模型调用,无副作用且无需持久化进度。
  • 仅UI端的自动补全,丢失请求可接受。

Architecture

架构

Use this shape unless the repo already has a stronger established pattern:
  1. The HTTP/server action layer validates auth, stores the user's intent if needed, emits an event with a stable
    id
    , and returns quickly.
  2. An Inngest function owns the agent run.
  3. Load state and external context inside
    step.run
    .
  4. Create AgentKit agents inside the function or import agent/network factories.
  5. Run model inference through AgentKit /
    step.ai
    ; wrap non-model tool side effects in
    step.run
    .
  6. Use
    step.waitForEvent
    or
    step.waitForSignal
    for human approval and external callbacks.
  7. Publish durable progress with native realtime.
  8. Add sessions and scores when the agent outcome needs to be evaluated later.
  9. Apply flow control at the function level for provider and tenant limits.
除非仓库已有更成熟的既定模式,否则建议采用以下架构:
  1. HTTP/服务器动作层验证权限,必要时存储用户意图,携带稳定
    id
    触发事件并快速返回。
  2. 由Inngest函数托管Agent运行。
  3. step.run
    内部加载状态和外部上下文。
  4. 在函数内部创建AgentKit Agent,或导入Agent/网络工厂。
  5. 通过AgentKit /
    step.ai
    运行模型推理;将非模型工具副作用封装在
    step.run
    中。
  6. 使用
    step.waitForEvent
    step.waitForSignal
    处理人工审批和外部回调。
  7. 通过原生实时功能发布持久化进度。
  8. 当需要后续评估Agent结果时,添加会话和评分机制。
  9. 在函数层面应用流程控制,以满足服务商和租户的限制要求。

Basic AgentKit Function

基础AgentKit函数

Prefer a small, typed function first; add networks and extra tools after the single-agent path is proven.
typescript
import { createAgent, openai } from "@inngest/agent-kit";
import { inngest } from "@/inngest/client";

export const summarizeTicket = inngest.createFunction(
  {
    id: "summarize-ticket",
    triggers: [{ event: "support/ticket.created" }],
    concurrency: [{ key: "event.data.accountId", limit: 2 }]
  },
  async ({ event, step }) => {
    const ticket = await step.run("load-ticket", () => {
      return getTicket(event.data.ticketId);
    });

    const writer = createAgent({
      name: "support-summary-writer",
      system: "Write a concise support-ticket summary with next actions.",
      model: openai({ model: "gpt-4o" })
    });

    const { output } = await writer.run(JSON.stringify(ticket));

    await step.run("save-summary", () => {
      return saveTicketSummary(event.data.ticketId, output);
    });

    return { ticketId: event.data.ticketId };
  }
);
建议先从小型的类型化函数开始;在单Agent路径验证可行后,再添加网络和额外工具。
typescript
import { createAgent, openai } from "@inngest/agent-kit";
import { inngest } from "@/inngest/client";

export const summarizeTicket = inngest.createFunction(
  {
    id: "summarize-ticket",
    triggers: [{ event: "support/ticket.created" }],
    concurrency: [{ key: "event.data.accountId", limit: 2 }]
  },
  async ({ event, step }) => {
    const ticket = await step.run("load-ticket", () => {
      return getTicket(event.data.ticketId);
    });

    const writer = createAgent({
      name: "support-summary-writer",
      system: "Write a concise support-ticket summary with next actions.",
      model: openai({ model: "gpt-4o" })
    });

    const { output } = await writer.run(JSON.stringify(ticket));

    await step.run("save-summary", () => {
      return saveTicketSummary(event.data.ticketId, output);
    });

    return { ticketId: event.data.ticketId };
  }
);

Tool Calls

工具调用

Tools can be defined with AgentKit, but agent-safe tools should still follow durability rules:
  • Read-only tool calls can run as part of the agent when replaying is harmless.
  • External side effects should be isolated with stable IDs and
    step.run
    boundaries, or implemented as tool handlers that use the provided
    step
    .
  • Tool outputs should be small enough for step state limits.
  • Validate tool parameters with schemas; never trust model-provided arguments.
  • Use tenant/user IDs from authenticated event data, not only from model text.
Tool side-effect checklist:
text
- What external state can this tool change?
- What idempotency key prevents duplicate writes?
- What should happen if the model calls the same tool twice?
- Is the output safe to store in function run state?
- Does the tool need provider-specific concurrency or throttle limits?
工具可通过AgentKit定义,但Agent安全工具仍需遵循持久化规则:
  • 只读工具调用可作为Agent的一部分运行,因为重放不会产生危害。
  • 外部副作用应使用稳定ID和
    step.run
    边界隔离,或实现为使用提供的
    step
    的工具处理器。
  • 工具输出应足够小,以符合步骤状态限制。
  • 使用模式验证工具参数;绝不要信任模型提供的参数。
  • 使用来自已验证事件数据的租户/用户ID,而不仅仅依赖模型文本。
工具副作用检查清单:
text
- 此工具可以更改哪些外部状态?
- 使用什么幂等键防止重复写入?
- 如果模型两次调用同一工具,应如何处理?
- 输出是否可以安全存储在函数运行状态中?
- 工具是否需要特定服务商的并发或限流限制?

Human in the Loop

人机协同

Use a durable wait instead of polling a database or keeping state in memory.
typescript
const approval = await step.waitForEvent("wait-for-approval", {
  event: "support/reply.approved",
  timeout: "3d",
  match: "data.ticketId"
});

if (!approval) {
  await step.run("mark-review-timeout", () => {
    return markTicketNeedsManualReview(event.data.ticketId);
  });
  return { status: "timed_out" };
}

await step.run("send-reply", () => {
  return sendSupportReply({
    ticketId: event.data.ticketId,
    approvalId: approval.data.approvalId
  });
});
使用持久化等待,而非轮询数据库或在内存中保存状态。
typescript
const approval = await step.waitForEvent("wait-for-approval", {
  event: "support/reply.approved",
  timeout: "3d",
  match: "data.ticketId"
});

if (!approval) {
  await step.run("mark-review-timeout", () => {
    return markTicketNeedsManualReview(event.data.ticketId);
  });
  return { status: "timed_out" };
}

await step.run("send-reply", () => {
  return sendSupportReply({
    ticketId: event.data.ticketId,
    approvalId: approval.data.approvalId
  });
});

Realtime Progress

实时进度

For v4 native realtime:
  • Use
    step.realtime.publish
    between steps.
  • Use
    inngest.realtime.publish
    inside an existing
    step.run
    .
  • Do not install the v3
    @inngest/realtime
    package for v4 projects.
  • Do not build a process-local WebSocket as the only source of progress for a durable function.
For AgentKit-specific UI hooks, check the installed
@inngest/agent-kit
version and current docs before wiring
useAgent
or
useChat
.
对于v4原生实时功能:
  • 在步骤之间使用
    step.realtime.publish
  • 在现有
    step.run
    内部使用
    inngest.realtime.publish
  • 不要为v4项目安装v3版本的
    @inngest/realtime
    包。
  • 不要将进程本地WebSocket作为持久化函数进度的唯一来源。
对于AgentKit特定的UI钩子,在连接
useAgent
useChat
之前,请检查已安装的
@inngest/agent-kit
版本和当前文档。

Agent Evals

Agent Evals

Use
inngest-agent-evals
when the user asks to score an agent, compare prompts or models, track user feedback, group runs by conversation/ticket, or debug agent quality over time. In durable agent workflows, add
meta.sessions
at the event that starts or connects the user flow, use direct scoring for signals known during the run, and use deferred scorers for product outcomes that arrive later.
当用户要求为Agent评分、比较提示词或模型、跟踪用户反馈、按对话/工单分组运行或随时间调试Agent质量时,使用
inngest-agent-evals
。在持久化Agent工作流中,在启动或连接用户流程的事件中添加
meta.sessions
,对运行期间已知的信号使用直接评分,对后续出现的产品结果使用延迟评分器。

Flow Control and Cost

流程控制与成本

Agent workloads often need provider and tenant limits:
  • Use account-scoped concurrency or throttle keys for model providers.
  • Key per tenant or account where fairness matters.
  • Use deterministic event IDs so duplicate user actions do not spawn duplicate expensive runs.
  • Keep successful model/tool results in steps so retrying a later failure does not re-charge earlier model calls.
Example:
typescript
{
  id: "support-agent-run",
  triggers: [{ event: "support/agent.requested" }],
  throttle: {
    limit: 120,
    period: "1m",
    key: `"openai"`
  },
  concurrency: [
    { key: "event.data.accountId", limit: 3 }
  ]
}
Agent工作负载通常需要服务商和租户限制:
  • 为模型服务商使用账户作用域的并发或限流键。
  • 在需要公平性的场景下,按租户或账户设置键。
  • 使用确定性事件ID,避免重复的用户操作触发重复的高成本运行。
  • 将成功的模型/工具结果保存在步骤中,这样后续步骤失败重试时不会重复计费之前的模型调用。
示例:
typescript
{
  id: "support-agent-run",
  triggers: [{ event: "support/agent.requested" }],
  throttle: {
    limit: 120,
    period: "1m",
    key: `"openai"`
  },
  concurrency: [
    { key: "event.data.accountId", limit: 3 }
  ]
}

Brownfield Migration

遗留系统迁移

When migrating an existing agent:
  1. Search for model calls, tool loops, in-memory state, streaming handlers, approval polling, and external side effects.
  2. Keep prompt/tool behavior stable at first.
  3. Move the trigger into an event and an Inngest function.
  4. Move model calls to AgentKit /
    step.ai
    .
  5. Move side-effecting tools into
    step.run
    or durable tool handlers.
  6. Replace process-local waits with
    step.waitForEvent
    or
    step.waitForSignal
    .
  7. Add realtime after the durable run is working.
Use
inngest-brownfield-audit
first when the repo has multiple possible workflows and the user has not picked one.
迁移现有Agent时:
  1. 查找模型调用、工具循环、内存状态、流处理器、审批轮询和外部副作用。
  2. 首先保持提示词/工具行为稳定。
  3. 将触发器迁移到事件和Inngest函数中。
  4. 将模型调用迁移到AgentKit /
    step.ai
  5. 将有副作用的工具迁移到
    step.run
    或持久化工具处理器中。
  6. 使用
    step.waitForEvent
    step.waitForSignal
    替代进程本地等待。
  7. 在持久化运行正常工作后添加实时功能。
当仓库中有多个可能的工作流且用户尚未选定其中一个时,请先使用
inngest-brownfield-audit

Anti-Patterns

反模式

  • Agent loop state only in memory.
  • One giant
    try/catch
    around all model and tool calls.
  • Retrying the entire agent after one tool failure.
  • Charging repeatedly for successful model calls after a later step fails.
  • setTimeout
    , cron polling, or Redis TTL as the human-review mechanism.
  • Side-effecting tools with no idempotency key.
  • Streaming progress from a server process that can die while the durable work continues elsewhere.
  • Adding AgentKit without registering the surrounding Inngest function.
  • Agent循环状态仅存储在内存中。
  • 在所有模型和工具调用外层包裹一个庞大的
    try/catch
  • 一次工具失败后重试整个Agent。
  • 后续步骤失败后,重复为成功的模型调用计费。
  • 使用
    setTimeout
    、定时任务轮询或Redis TTL作为人工审核机制。
  • 无幂等键的有副作用工具。
  • 从可能在持久化工作流继续运行时崩溃的服务器进程流式传输进度。
  • 添加AgentKit但未注册周边的Inngest函数。

Verification

验证

  • Typecheck the agent, tool schemas, and event payloads.
  • Unit-test tool handlers separately from model behavior.
  • Test that the HTTP entrypoint emits one deterministic event and returns fast.
  • Test that duplicate event IDs do not duplicate final side effects.
  • If possible, run the Inngest dev server and inspect the agent steps/traces.
  • 对Agent、工具模式和事件负载进行类型检查。
  • 单独测试工具处理器,与模型行为分离。
  • 测试HTTP入口点是否触发一个确定性事件并快速返回。
  • 测试重复事件ID是否不会重复执行最终副作用。
  • 如果可能,运行Inngest开发服务器并检查Agent步骤/追踪信息。