Skip to content

Why NASDK?

一个长时任务中,多个应用之间经常需要的不只是一次远程函数调用。

一个任务可能持续数分钟,执行期间不断产生结果;调用方可能需要观察进度、发送补充信息,或者暂停、恢复和终止任务;多个任务还可能竞争同一块 GPU、同一个模型或其他有限资源。

TIP

是的孩子们,我就是在讲AgentRuntime经常遇到的问题。

这些需求可以分别用 RPC、消息队列、事件总线和任务调度器解决,但应用必须自行维护它们之间的身份、生命周期和状态关系。

并且,如果应用关注这些限制过多,会导致应用职责变得模糊。例如,如果LLMProvider过度去关注GPU的占用,则Provider很有可能会进一步长出队列和调度器,这就是经典的职责越界

Not Only Req-Res

传统 RPC 擅长表达「调用一个函数,然后取得一个结果」。

当一次调用包含过程输出和运行时控制时,通常还需要额外建立流式接口、回调通道或控制接口。

NASDK 保留 Request-Response,同时增加三种与请求相关的消息:

  • notify 推送零到多个过程消息。
  • signal 向仍在运行的 Event 发送信息或控制指令。
  • ack 确认可靠消息已经到达。

这些消息由NASDK自行内部匹配,不需要业务方单独记录id。

可编排的任务与流水线

复杂任务通常无法由一个函数一次完成。

它可能需要根据上一步结果选择下一步操作,在本地计算与远程调用之间切换,等待异步结果,派生子任务,并在任意步骤失败时收束整个执行过程。

NASDK 将这类工作组织为三个层次:

  • Event 表示一次完整任务,承载从进入队列到成功或失败的生命周期。
  • Pipeline 描述任务如何逐步推进,并根据上一步结果决定下一步执行什么。
  • Task 是实际执行工作的最小单元,可以进行本地计算、调用模型或等待远程结果。

Pipeline 不需要把整个流程写成一条难以观察的异步调用链。每一步 Task 都有明确的输入、输出和状态,步骤之间可以保存上下文,也可以通过内建任务结束流水线或派生新的 Event。

这种编排方式适合多阶段 AI 推理、Agent 工作流、数据处理流水线和其他需要长时间运行、动态分支或子任务协作的工作。

完善的传输协议与队列消息

长时任务对网络波动更加敏感,并且传输的内容可能包含大型二进制。

一次几秒钟的调用可以在断线后直接重试,但一个已经运行数分钟、产生大量过程状态的任务,不应该因为连接短暂中断就从头开始。

NASDK 将应用消息与底层连接分开处理。NApp 面向目标 App ID 发送消息,NACT 负责通过 TCP、WebSocket 或 Unix Socket 承载数据,NACP 则负责请求、响应、订阅、通知、信号和确认之间的关系。业务逻辑不需要根据传输方式分别实现调用协议。

对于需要可靠送达的消息,NASDK 使用 ACK 确认消息已经到达。未收到 ACK 的消息会保留在待确认队列中;连接中断后,它们会进入待发送队列,在目标 NApp 于宽限期内重新连接时按原有顺序继续发送。

不同消息也不必承担相同成本:

  • Request、Response、Signal 和协议控制消息需要可靠送达。
  • Notify 用于过程状态和事件推送,不等待 ACK,避免高频过程消息阻塞关键控制消息。
  • 重复到达的可靠消息会再次获得 ACK,但不会被重复交给业务处理。
  • 队列具有数量和容量限制,避免失联节点无限消耗发送方内存。

这套机制让应用能够把注意力放在任务本身,而不是在每个调用点重复实现消息编号、确认等待、断线缓存、重发和去重。

此外,NASDK还具有能够直接传输大型二进制的功能,允许用户直接将Buffer存储在JSON内通过NASDK发送到另一端,一定程度上承担消息队列能力。

有限资源应该由运行时调度

在 AI Infra 中,GPU、模型上下文和部分外部服务都不是可以无限并发的资源。

如果每个业务模块都自行判断 GPU 是否空闲,那么锁、等待队列、超时和任务优先级会逐渐进入 LLMProvider。Provider 原本只需要负责执行模型调用,最后却同时承担资源管理和任务调度,这正是前文提到的职责越界。

NASDK 将资源竞争放在 Task 调度层处理。Task 可以声明执行时需要占用的资源;运行时根据当前占用情况决定何时放行。等待网络、数据库或远程响应的异步 Task 不占用这些资源,仍然可以并发推进。

这样一来:

  • Provider 只负责完成一次具体执行。
  • Pipeline 只负责决定下一步做什么。
  • Task 调度器负责决定这一步什么时候可以运行。
  • 业务代码不需要重复实现锁和等待队列。

资源约束从业务实现中被抽离出来,但仍然和任务生命周期保持关联。任务暂停、失败或结束后,运行时可以统一处理资源释放和后续任务放行。

长时任务必须可观测

对于一个只运行几十毫秒的函数,等待返回结果通常已经足够。对于持续数分钟的任务,只知道「还没有结束」没有太大意义。

NASDK 会暴露连接、消息和执行过程中的 Event。应用可以用它们记录日志、统计耗时、展示实时进度,或者追踪一个 Event 当前位于 Pipeline 的哪一步、正在运行哪个 Task。

过程结果也不必等到任务完成后一次性返回。Task 可以持续上报处理结果,调用方通过 Event 请求附带的过程流实时消费这些数据,最终 Response 只负责表达任务的终结结果。

观测代码不需要进入业务 Handler,也不需要改变任务原本的控制流。它可以作为独立消费者存在,从而避免日志、监控和业务执行彼此耦合。

需要时,也能够介入

观察只能回答任务正在做什么,有些场景还需要改变它接下来怎么运行。

调用方可以向仍在运行的 Event 发送 Signal。普通 Signal 可以携带补充信息,控制 Signal 可以请求暂停、恢复或终止任务。NACEB 还提供生命周期 Hook,让运行时内部能够在明确的状态转移点执行同步逻辑。

Hook 中的 Veto 可以阻止部分状态转移,使运行时在下一刻重新检查条件。它适合实现动态门禁、人工确认或运行前检查,而不是把这些判断散落到每个 Task 内部。

Event 用于观察,Hook 用于介入。两者被刻意区分:监听日志不应该意外阻塞任务,而需要改变状态的逻辑也不应该依赖异步事件恰好及时执行。

NASDK 适合什么

NASDK 适合任务本身已经超出简单远程函数调用的场景,例如:

  • 多个独立应用需要相互发起调用。
  • 一次任务会持续产生过程结果。
  • 工作需要拆成可组合、可追踪的多个步骤。
  • 任务需要暂停、恢复、终止或接收运行时信息。
  • 多个任务需要竞争 GPU、模型或其他有限资源。
  • 应用需要远程事件订阅和消息推送。
  • 长时任务需要承受短暂的连接中断。

如果应用只需要一次简单调用,可以只使用 NApp 和 Ability;当任务逐渐需要过程流、Pipeline 和资源调度时,再使用 Event 与 NACEB。两种执行方式共享同一套 NApp 通信入口,不需要重新设计应用之间的连接和消息协议。

Released under the MIT License.