异步任务、长任务、流式执行

异步任务、长任务、流式执行

这篇主要考察什么

大模型应用的一个现实问题是:很多任务根本不适合用用户发请求,服务同步返回结果这一种模式来做。

例如:

  • 深度调研任务可能要跑几分钟;
  • 多网页抓取和整理可能涉及几十次工具调用;
  • 代码 Agent 可能要不断 build、test、patch;
  • 多 Agent 协作可能存在多个远端等待点;
  • 文件解析、批处理、报表生成、批量操作更不可能在一个同步请求里优雅完成。

所以,当面试官问长任务怎么做、异步怎么设计、流式输出怎么配合后台执行,本质上是在看你是否理解:Agent 不只是一个 HTTP handler,而是一个可能跨时间运行的交互系统。

可以在面试里这么表述:

:::color1

同步适合短、确定、结果马上可用的任务;异步适合长时间、可能中断、需要后台继续的任务;流式执行适合把中间进展持续反馈给用户。三者不是互斥关系:一个任务可以既是异步后台执行的,又对前端提供流式进度,还支持取消和恢复。

:::

为什么 Agent 特别需要异步与流式

1. 模型和工具链本身耗时不可忽略

即使单次模型推理不算太慢,一旦叠加:

  • 检索;
  • 网页抓取;
  • 文件读取;
  • 多次 tool loop;
  • rerank;
  • 多 Agent 协作;
  • 审批等待;

整体时长很容易超出用户可接受的同步等待时间。

2. 用户对正在做什么很敏感

如果前端只是转圈,用户会非常焦虑:

  • 它是不是卡死了?
  • 到底在查什么?
  • 有没有拿到部分结果?
  • 需要我补充信息吗?

流式状态和阶段性反馈可以显著改善体验。

3. 长任务会遇到连接不可靠

网页关闭、网络抖动、移动端切后台都很常见。

如果任务只能绑在一次前端连接上,可靠性会很差。

所以必须把任务执行与前端连接分离开。

一图看懂三种执行形态

复制代码
flowchart LR
    A[用户请求] --> B{执行模式}
    B --> C[同步请求-响应]
    B --> D[异步后台任务]
    B --> E[流式反馈]

    D --> F[状态存储 / 任务表]
    E --> G[前端阶段更新 / SSE / WebSocket / 增量消息]
    D --> G

一句话解释:

同步是结果交付方式,异步是执行方式,流式是反馈方式。

三者可以组合,而不是只能三选一。

同步、异步、流式分别适合什么场景

1. 同步

适合:

  • 简单问答;
  • 少量工具查询;
  • 结果可在几秒内稳定返回;
  • 不涉及审批和长等待;
  • 用户必须立即得到最终答案。

优点:

  • 实现简单;
  • 产品心智直观;
  • 状态管理较轻。

缺点:

  • 一旦任务变长,体验和可靠性都迅速变差;
  • 不适合断点恢复;
  • 难插入复杂等待。

2. 异步后台

适合:

  • 长时间任务;
  • 需要在用户离开页面后继续;
  • 需要和队列、调度器、工作流结合;
  • 可能依赖外部慢系统;
  • 需要取消、恢复、回调通知。

优点:

  • 执行与前端连接解耦;
  • 更适合资源调度、超时、重试和恢复;
  • 能承载真正的后台 Agent。

缺点:

  • 需要任务状态存储;
  • 用户如何得知进展要额外设计;
  • 一致性和取消语义更复杂。

3. 流式反馈

适合:

  • 任务过程本身对用户有价值;
  • 需要逐步展示 reasoning phase、阶段进展或部分结果;
  • 希望提升体感速度;
  • 需要在任务很长时持续建立用户信任。

优点:

  • 反馈快;
  • 适合展示中间结果;
  • 便于提示用户何时需要补充输入。

缺点:

  • 前端和协议更复杂;
  • 需要定义进度事件与断流恢复;
  • 不是所有内容都适合实时暴露。

OpenAI 背景任务(Background Mode)可以怎么讲

这是当前很新的高频点。

你不需要背全部 API 细节,但建议知道以下几个要点。

1. Background Mode 解决什么问题

它把需要较长时间完成的模型任务从同步请求中拆出来,让任务在后台继续运行。

这非常适合:

  • 深度调研;
  • 长 reasoning;
  • 多步工具调用;
  • 需要几分钟的复杂任务。

2. 基本交互模型

典型模式是:

  1. 发起一个后台响应任务;
  2. 立即拿到任务标识或响应对象;
  3. 后续通过轮询状态、流式恢复或 webhook 获取完成结果;
  4. 必要时发起取消。

这个设计的关键价值是:

任务继续跑,但前端连接不需要一直悬挂。

3. Background 和 Streaming 可以组合

一个很值得你在面试里主动提的点是:后台执行不代表完全没有实时反馈。

它可以和 streaming 结合:

  • 一边后台跑;
  • 一边输出阶段性事件;
  • 连接断了后还能从 cursor 继续取增量。

这体现了执行方式和反馈方式是两件事。

4. Cancel 要做成幂等操作

长任务一旦支持取消,就要考虑:

  • 用户可能点两次取消;
  • 前端和后端可能重复发取消;
  • 任务已经完成后又收到取消。

因此取消动作本身也要设计成幂等且可安全落空。

5. Webhook 可以让任务完成通知更自然

对真正的后台任务来说,轮询虽然简单,但并不总是最优。

如果平台支持 webhook,任务完成、失败或需要人工介入时,可以主动通知应用侧。

这对于集成工作流和企业通知系统很有价值。

流式执行到底流什么

很多人一提 streaming,只想到模型 token 一点点吐字。

但在 Agent 系统里,更有价值的流式内容往往包括:

1. 文本增量

最直观的一种。

适合:

  • 逐字输出答案;
  • 展示草稿形成过程;
  • 给用户体感上的即时反馈。

2. 阶段事件

例如:

  • 已完成需求理解;
  • 正在搜索网页;
  • 已找到 12 个候选结果;
  • 正在调用财务 Agent;
  • 等待用户确认;
  • 已进入最终生成阶段。

这种事件比纯文本 token 更有产品价值,因为它让用户知道系统当前在哪个阶段。

3. 工具事件

例如:

  • 某个 tool call 发起;
  • tool result 返回;
  • 某个并行子任务完成;
  • 某个远端 Agent 返回 artifact。

这类事件对调试和高级用户很有用,也适合作为 trace 的用户可见子集。

4. 部分结果 / Artifact 增量

例如:

  • 已生成报告大纲;
  • 已完成代码补丁初稿;
  • 已汇总前 20 条调研结果;
  • 已生成表格第一页。

这在长任务中很重要,因为用户经常不需要全做完才开始利用结果。

流式输出不等于暴露全部 Chain-of-Thought

这是一个需要小心说的点。

工程上,流式阶段更新和进度可见性非常重要,但不代表你要把模型的全部内部思维原样暴露给用户。

更合理的做法是输出:

  • 阶段描述;
  • 已执行动作;
  • 可理解的中间状态;
  • 用户需要知道的下一步。

也就是说,流式是产品反馈机制,不等于把内部推理全部公开。

长任务的核心设计:执行与呈现分离

这是非常重要的系统设计原则。

执行层负责

  • 真正跑模型与工具;
  • 维护任务状态;
  • 写 checkpoint / trace;
  • 处理重试、取消、超时和恢复;
  • 记录 artifact。

呈现层负责

  • 把当前状态映射成用户可见进度;
  • 展示阶段消息、部分结果、最终结果;
  • 处理断线重连;
  • 订阅 webhook / polling / stream;
  • 给用户提供取消、继续、补充输入入口。

如果你把这两层混在一起,例如执行线程直接维护前端长连接并把所有状态都绑死在连接上,系统会很脆弱。

Polling、Streaming、Webhook 怎么选

1. Polling

适合:

  • 实现简单优先;
  • 前端或调用方暂时不方便接 webhook;
  • 任务频率不高;
  • 能接受秒级状态刷新。

优点:

  • 简单稳定;
  • 容易调试;
  • 很适合作为基础兜底机制。

缺点:

  • 有额外无效请求;
  • 状态更新不够实时;
  • 轮询间隔要权衡时延与成本。

2. Streaming

适合:

  • 用户在线等待时;
  • 中间进度很重要;
  • 希望体感更丝滑;
  • 前端能稳定处理增量事件。

优点:

  • 延迟低;
  • 用户体验好;
  • 适合多阶段 Agent 可视化。

缺点:

  • 连接管理复杂;
  • 需要处理断流和恢复;
  • 前后端事件协议要设计好。

3. Webhook / Push Notification

适合:

  • 任务可能很久;
  • 应用侧是服务对服务集成;
  • 需要被动接收完成通知;
  • 不希望靠频繁轮询。

优点:

  • 更节省查询流量;
  • 适合后台集成;
  • 与工作流和事件总线结合自然。

缺点:

  • 需要安全验证;
  • 回调失败要重试;
  • 复杂度高于简单 polling。

成熟系统通常是:

  • 在线场景用 streaming;
  • 离线场景用 webhook;
  • 始终保留 polling 作为保底查询。

取消(Cancel)为什么比想象中更复杂

很多人以为 cancel 就是停掉任务。

真实系统里,取消至少要回答:

  1. 当前任务是还没开始,还是正在执行,还是已经完成?
  2. 如果正在执行一个长外部调用,能否真正中断?
  3. 如果某一步已经产生副作用,取消后是否需要补偿?
  4. 如果任务是多子任务并行,取消是全取消还是局部取消?
  5. 取消请求重复到达时怎么处理?

所以一个成熟回答会说:

取消本质上是状态转移,不一定保证物理中止所有动作,但必须保证系统进入一致的 canceled / canceling 语义。

恢复(Resume)和断点续跑

异步长任务一旦存在,就必须考虑恢复。

恢复通常分三种:

1. 前端恢复观察

用户刷新页面后,继续查看任务当前状态和历史进度。

2. 执行恢复

Worker 崩溃或连接断开后,系统从 checkpoint 或持久状态继续执行任务。

3. 人工继续

任务进入 input-required,用户补充资料后,同一 task 再次继续。

你在面试里可以直接说:

查看状态和继续执行是两种不同恢复。前者恢复观察通道,后者恢复执行语义。

长任务里最容易被忽略的五个状态字段

这部分很适合拿来做工程化加分:

  1. phase:当前阶段,如 planning / searching / waiting_input / finalizing;
  2. progress:阶段进度,可选百分比或可读进度;
  3. last_event_id / cursor:用于流式恢复;
  4. cancel_requested:取消意图标记,避免硬杀一切;
  5. resume_token / task_id:恢复执行所需标识。

这些字段让长任务从只有一个 pending / done变成真正可管理的任务实体。

A2A、MCP 与异步流式的关系

A2A

A2A 天然适合异步和流式,因为远端 Agent 任务本来就可能持续很久,并且有 task state、artifact、push notification、resubscribe 等语义。

所以如果面试问多 Agent 长任务怎么做,A2A 是很好的切入点。

MCP

MCP 更常和能力接入相关,但在远程 HTTP 与事件流场景下,也会涉及长连接、流式事件、工具返回 UI 或任务状态等问题。

你不必把它讲得和 A2A 一样复杂,但要知道 MCP 也不是只能短平快同步调用。

常见误区

误区 1:异步就是把请求丢进线程池

太浅。

真正的异步任务需要状态存储、取消、恢复、通知和失败处理,而不是只把请求搬到后台线程。

误区 2:流式只是逐字输出

不完整。

在 Agent 里,流式更有价值的是阶段事件、工具事件和部分结果。

误区 3:长任务可以一直靠前端连接等着

不现实。

前端连接会断,用户会离开页面,所以必须把执行和连接解耦。

误区 4:支持取消就等于能立即停机

不一定。

很多外部调用无法瞬时中断,取消更常见的是让系统进入一致的取消状态,并停止后续步骤。

误区 5:有 streaming 就不需要 webhook / polling

不对。

流式适合在线观察,webhook 适合后台通知,polling 适合保底查询。三者往往是互补的。

高频面试题与回答模板

1. 为什么 Agent 特别适合异步执行?

标准回答:因为 Agent 任务经常是多步、多工具、可能很长、可能等待外部系统或人工输入。同步请求-响应模式不适合承载这类长生命周期任务。

2. 流式执行和异步执行是什么关系?

标准回答:异步是执行方式,流式是反馈方式。一个任务可以在后台异步跑,同时把阶段性进度流式推给前端。

3. 为什么长任务不能只靠轮询?

标准回答:可以用轮询兜底,但仅靠轮询会带来时延和无效请求。在线场景通常配合 streaming,后台集成通常配合 webhook。

4. Cancel 最重要的设计点是什么?

标准回答:把取消看成状态转移而不是简单杀进程,并处理重复取消、部分已执行副作用和后续补偿逻辑。

5. Resume 需要保存哪些信息?

标准回答:至少要有 task_id / run_id、当前阶段、最近事件 cursor、已完成步骤、已生成 artifact、等待输入状态和取消标记。

6. Background Mode 的价值是什么?

标准回答:它把长时间模型任务从同步请求中解耦出来,让任务能后台继续执行,并通过 polling、streaming、webhook 等方式获取进度和结果,更适合长 reasoning 和复杂工具链任务。

项目表达模板

我们的 Agent 有一些任务会跑很久,比如深度调研和批量自动化处理,所以我们没有把所有执行都绑在一次前端请求里。系统会把长任务提交到后台运行时,生成 task 状态并持续写入 phase、progress、artifact 和 trace。用户在线时,我们通过流式事件返回阶段进展和部分结果;用户离线时,可以靠 webhook 通知或后续轮询恢复查看。取消操作只修改任务状态并停止后续步骤,已执行副作用则通过补偿逻辑处理。这样执行层和呈现层被彻底分开,系统既能长时间运行,也能保持良好交互体验。

小结

把这篇压缩成四句话:

  1. 同步适合短任务,异步适合长任务,流式适合持续反馈;
  2. Agent 的长任务本质上要求执行与连接分离,不能绑在一个前端请求上;
  3. Polling、Streaming、Webhook 不是替代关系,而是不同场景下的组合;
  4. 取消、恢复、进度、部分结果和任务状态,是长任务设计的核心。

如果你能围绕这四句话讲清后台执行、流式事件、取消恢复和任务状态字段,这一题就已经非常像真正做过系统的人在回答。