普通聊天接口通常是一问一答,客户端发出请求,服务生成内容,再把响应返回。
Deep Research 不一样。
一次任务可能包含多次模型调用、搜索、网页访问、计算和报告更新。任何一步都可能排队、超时、限流或等待外部 IO。
如果仍用一个同步 HTTP 请求从头等到尾,连接中断就很难判断任务是否继续,网关超时可能误杀正在运行的任务,客户端重试还可能重复创建两份研究。
相关工程教程给出的方向是异步任务加进度查询,并要求重试、降级、断点和状态隔离。
所以面试官真正想问的是:
长时 Agent 如何拆成任务控制面和执行面,任务状态机怎样设计,怎样处理幂等、租约、断点、取消、重试和背压,GPU 推理与外部工具并发如何协调,又如何定义可验证的服务目标而不虚构现有 QPS 或 SLA?
同步请求为什么容易出问题?
第一,生命周期不匹配。
网关、负载均衡和客户端都有超时,研究任务却可能更久。连接结束不代表任务应该被杀掉。
第二,失败边界模糊。
客户端没收到响应,无法知道服务还在跑、已经完成,还是失败。再次提交可能重复消耗资源。
第三,无法稳定恢复。
所有状态只在进程内存里,Worker 重启后,已查证据、剩余问题和工具调用都丢失。
第四,进度不可见。
用户只看到一直加载,不知道是在排队、搜索、访问页面、综合答案还是已经卡死。
第五,资源难调度。
长任务占住连接和 Worker,外部 IO 等待期间也可能阻塞其他请求。
异步任务把任务生命周期、客户端连接和资源调度解耦;接口形式看起来是否高级,与这个工程价值无关。

客户端连接可以结束,任务仍由可持久化状态机管理。
一次异步任务的完整链路
提交层接收问题、用户约束、预算和幂等键,完成鉴权、配额与输入校验。
任务仓库创建记录,分配 task ID,初始状态为 queued。
调度器根据租户配额、优先级、模型资源和工具限流选择可运行任务。
Worker 获取 lease 后开始执行。每完成一个可恢复步骤,就写入 action、observation、证据、状态摘要与剩余预算。
等待 Search、Visit 等外部 IO 时,任务状态可以标为 waiting_tool,不把整个线程或 GPU 槽位长期占住。
模型调用通过推理服务执行,工具调用通过统一网关执行。两类资源分别限流和监控。
完成后写入结果与证据索引,状态变为 succeeded 或 partial。无法恢复的错误进入 failed,用户取消进入 cancelled。
客户端使用 task ID 查询状态,或者订阅事件。任务结果读取与运行本身解耦。
状态机为什么不能只有“成功”和“失败”?
queued 表示等待调度,尚未消耗执行资源。
running 表示 Worker 正在执行模型或控制逻辑。
waiting_tool 表示等待外部工具,适合单独观察 IO 瓶颈。
retry_wait 表示遇到可重试错误,等待退避窗口。
paused 表示系统或用户显式暂停,并有完整 checkpoint。
partial 表示只完成了明确范围,结果可交付但不是完整成功。
succeeded、failed 和 cancelled 是终态,但 failed 还应保存错误分类。
状态转换必须由事件驱动并校验版本,避免两个 Worker 同时把同一任务从 running 写成不同结果。
这些状态为重试、监控、用户体验和成本归因提供明确语义,增加枚举只是实现上的结果。
幂等怎样防止重复创建研究任务?
客户端提交时带幂等键,服务把它与用户、任务类型和有效请求摘要绑定。
相同键的重试若请求一致,返回原 task ID;若内容不同,则拒绝复用,避免误把两次任务合并。
幂等记录要有合理生命周期。永久保存所有键成本高,过早过期又可能重复执行。
工具调用也要考虑幂等。
搜索和网页访问通常是只读操作,但写文件、发消息或创建外部资源可能有副作用。Worker 重启后不能盲目重放。
每个步骤保存 operation ID 与执行状态。恢复时先查询上次调用是否完成,再决定重试。
任务幂等解决重复提交,步骤幂等解决重复副作用,两层缺一不可。
Lease 和 Heartbeat 在解决什么?
队列把任务交给 Worker 后,需要知道 Worker 是否仍然拥有执行权。
Lease 是有限时间的租约。Worker 周期性 heartbeat 续约,表示自己仍在运行。
若 Worker 崩溃或网络隔离,租约到期,调度器可以把任务交给其他 Worker 从 checkpoint 恢复。
但新 Worker 接管前要使用 fencing token 或版本号,阻止旧 Worker 恢复网络后继续写入。
Heartbeat 只能证明进程活着,不能证明任务有进展。还要记录 last_progress_at、当前步骤和等待原因。
如果 Worker 一直续约却重复相同步骤,系统应触发无进展告警,而不是让任务永久运行。

租约、心跳、进度与 checkpoint 一起防止僵尸和重复执行。
Checkpoint 应该保存到什么粒度?
太粗,恢复时要重做大量昂贵步骤;太细,每个 token 都落盘又会拖慢系统。
比较实用的粒度是完整 Agent 步骤或阶段边界。
保存原任务、当前计划、已确认主张、证据 ID、未决问题、工具调用与返回、演进报告、预算和版本。
不要只保存自然语言历史。恢复需要结构化知道哪些工具真实执行过,哪些结论已经由哪些来源支持。
Checkpoint 写入要原子化。状态显示步骤完成时,对应证据和预算变更也应已经提交。
动态来源需要保存抓取时间和必要快照。恢复运行时要判断旧 observation 是否还能继续使用。
敏感数据应按租户隔离并加密,checkpoint 不能成为跨用户泄漏入口。
重试应该在哪一层做?
网络抖动和服务端临时错误可以在工具适配层短重试。
查询过宽导致空结果,需要 Agent 修改参数,不应由底层无限重放同一个请求。
模型推理瞬时失败可以由推理客户端重试,但要确认请求幂等和采样语义。
Worker 崩溃属于任务级恢复,从 checkpoint 接管,而不是把全部研究从头再跑。
超过重试预算后进入降级或失败,错误类型写入状态。
各层都做无上限重试会形成乘法放大。工具重试多次,任务再重试多次,客户端又重复提交,最终成本和压力失控。
因此需要统一 retry budget,并让上层知道下层已经尝试了多少次。
取消任务为什么也需要协议?
用户点击取消后,服务先把 cancellation requested 持久化。
Worker 在安全点检查标记,停止发起新工具和模型调用,完成必要清理,再把任务转为 cancelled。
已经发出的外部请求可能无法瞬间撤销,需要记录最终状态,避免恢复时误判。
取消不能直接删除任务记录。状态、已发生成本、部分证据和审计日志仍需保留到合规期限。
如果允许交付部分结果,应明确它是用户取消前已核验范围,不是完整报告。
系统关闭或发布时也可以用同一排空协议:停止接收新任务,让运行中任务到安全点 checkpoint,再迁移或恢复。
进度应该告诉用户什么?
进度不能伪装成精确百分比。
研究路径会动态变化,查到冲突证据后可能增加步骤。把“已访问两个页面”映射成 80% 容易误导。
更适合展示阶段和可验证事件:正在排队、正在发现来源、正在核对原文、正在整理证据、正在生成报告。
可以同时显示已完成对象、已核验来源数、剩余任务合同和预算状态,但不要暴露模型私有推理文本。
事件要有单调序号,客户端断线重连后可以从上次位置继续消费。
SSE 适合服务器单向推送,WebSocket 适合双向交互,轮询最简单稳妥。选择取决于产品需求,不是技术越复杂越好。
背压和并发配额怎样设计?
系统同时受模型 GPU、搜索 API、网页访问、数据库和队列容量限制。
只按 HTTP 并发数限流不够。一个任务可能产生许多模型与工具调用。
可以设置租户级运行任务数、全局模型并发、工具并发、每任务最大并行分支和队列长度。
队列接近上限时,优先拒绝或延迟低优先级新任务,而不是让所有运行任务一起超时。
调度还要防止大任务长期占用资源。可按步骤让出执行权,或者使用公平队列与预算配额。
高优先级不能无限饿死普通任务,租户之间也不能共享状态和配额。
这些都是服务设计原则,本文不虚构已经达到的并发数、QPS 或排队时长。
Continuous Batching 能解决整个长任务调度吗?
不能。
Continuous batching 解决的是模型推理服务内部,不同请求解码长度不一致造成的槽位浪费。完成的请求退出后,新的请求可以进入批次。
Deep Research 的一个任务却在模型调用之间穿插外部 IO、控制逻辑和状态持久化。
推理服务看到的是许多独立 LLM 请求,Agent 调度器看到的是有依赖的长任务步骤。
两层需要协同:Agent 在等待工具时释放模型资源,模型服务对就绪调用做动态批处理。
即使模型吞吐提高,搜索 API 可能成为新瓶颈;即使工具很快,状态数据库也可能争用。
所以监控必须按整条任务链路拆解,不能把 GPU 利用率当作服务完成度。
超时应该分几层?
连接超时控制客户端等待,不应直接等于任务终止。
单次模型调用与工具调用有独立超时。
每个步骤有执行预算,整个任务还有总时间与成本预算。
排队等待也应有期限,避免任务永远留在 queued。
不同超时需要不同动作:单工具超时可换源,任务预算将尽可保存状态并部分交付,排队超时可提示用户稍后重试。
超时值要由真实负载和用户需求验证。本文不提供固定 SLA,也不声称已有服务达到了某个时长。
多租户状态怎样防串?
每个 task ID 必须绑定 tenant ID 与 owner ID,所有读取、事件订阅、checkpoint 和工具凭证都进行授权检查。
队列消息不能只带 task ID 后信任 Worker,要在服务端重新加载租户上下文。
缓存键包含租户和权限范围,不能让一个用户命中另一个用户的私有工具结果。
日志与 Trace 做脱敏,敏感 observation 不进入普通监控标签。
取消、重试和结果下载都要验证所有权,不能因为知道 task ID 就可操作。
多用户并发最危险的不只是性能下降,而是中间报告、记忆和证据串到其他任务。
优先级调度怎样避免大任务饿死小任务?
只用先进先出,前面一个超长任务可能让后续简单任务等待很久。
只用最高优先级,又可能让普通租户永久饥饿。
调度器可以把任务级优先级、租户配额、等待时间和步骤预算一起考虑。长任务每完成一个阶段后重新排队,让其他任务获得执行机会。
模型调用和工具调用还需要分别调度。一个任务等待网页访问时,不应继续占用模型并发名额。
对于有截止时间的任务,可以预估剩余工作,但估计应作为调度输入,不向用户承诺未经验证的完成时间。
公平性要有指标,例如各租户排队分布、被延迟原因和长期未调度任务。不能只看全局平均等待,少数大租户可能掩盖普通用户饥饿。
调度策略更新后要回放历史任务分布,检查是否把某类复杂任务系统性降级。
进度事件需要“恰好一次”吗?
分布式系统里,事件重复往往比绝不重复更容易实现。
可以为每个 task 维护单调 event sequence。消费者根据 task ID 与序号去重,断线后从 last seen sequence 继续读取。
若事件先写消息队列、状态后写数据库,任一环节失败都可能出现不一致。更稳妥的是让状态变更和待发布事件进入同一事务记录,再由发布器发送。
前端收到重复进度不应重复追加相同证据或错误地增加百分比。
最终结果也要按版本读取。任务从 partial 更新到 succeeded 时,客户端能知道结果版本发生变化。
所谓 exactly once 通常需要明确作用域。服务更常通过至少一次投递加幂等消费获得可观察的等价效果,而不是口头承诺绝对不重复。
发布新版本时,运行中任务怎么办?
任务启动时记录模型、Prompt、工具协议、状态 schema 和执行代码版本。
滚动发布后,新 Worker 接管旧 checkpoint 前,要确认能够读取旧 schema。无法兼容时继续由旧版本 Worker 排空,或者运行显式迁移。
不能让同一任务前半程使用旧工具协议,后半程无记录切到新协议。这样结果出现变化时无法归因。
对于安全修复,可以强制暂停旧任务,但要把终止原因和用户提示写清楚。
发布演练应包含运行中、等待工具、重试等待和部分完成等状态,而不只测试空队列部署。
回滚也一样。模型权重、Prompt、工具和状态处理代码是一个兼容组合,不能只回滚某个容器镜像。
日志和 Checkpoint 应该保存多久?
保存越久越方便排障,但也增加隐私、合规和存储风险。
可以把运行状态、结果、工具原文、脱敏 Trace 和聚合指标设置不同保留策略。
包含用户输入、私有网页或工具凭证的 observation 不应进入普通指标标签,也不能无限期保留。
删除任务时,还要处理 checkpoint、缓存、事件、搜索索引和备份中的关联数据。
审计日志可以只保留操作人、时间、资源 ID 与结果,不必复制全部敏感内容。
恢复窗口与保留期限要协调。若 checkpoint 已过期,服务不能仍告诉用户任务可恢复。
具体期限取决于业务与法规,文章不提供统一天数,也不声称现有服务已经完成某项合规认证。
上线前怎样验收长任务服务?
功能层覆盖提交、幂等查询、进度、取消、部分完成和结果读取。
故障层注入 Worker 崩溃、队列重复投递、工具超时、模型失败、数据库短暂不可用和网络分区。
恢复层检查 lease 接管、fencing、checkpoint、幂等步骤与事件续传。
压力层逐步增加任务与步骤,不直接声称某个目标 QPS。观察队列等待、在途任务、模型与工具饱和点。
安全层检查跨租户访问、缓存隔离、日志脱敏和取消权限。
质量层确认重试与降级没有制造重复证据、旧缓存或部分结果冒充完整答案。
每个测试都应输出可重放 Trace 和失败分类。没有证据的“压测通过”不能支持 SLA。

功能、故障、恢复、压力、安全和答案质量必须共同通过。
面试官继续追问,怎么接?
追问一:前端轮询不就够了吗?
轮询只是进度传输方式。后端仍需要持久化状态机、幂等、调度、租约、checkpoint 和恢复。
追问二:任务超时后直接重跑有什么问题?
会重复昂贵调用和有副作用工具,也可能获得不同动态证据。应从完整 checkpoint 恢复并检查幂等。
追问三:为什么 heartbeat 不等于任务健康?
进程活着也可能无进展循环。还要监控 last progress、当前步骤和重复行为。
追问四:continuous batching 可以提升长任务并发吗?
它优化 LLM 调用层吞吐,不能替代跨模型、工具、状态的 Agent 任务调度。
追问五:你们当前能扛多少 QPS?
材料没有可核验生产压测数据。面试中应说明指标、压测方法和瓶颈定位,不能虚构数字。
怎样把长任务服务方案讲完整
Deep Research 是长时间、多步骤、带外部 IO 的任务,不适合用一个同步请求硬等。我会把它拆成异步 Job:提交接口负责鉴权、幂等和落库,返回 task ID;调度器按租户与资源预算派发;Worker 用 lease 和 heartbeat 执行,并在完整步骤持久化 checkpoint;客户端用轮询或事件订阅查看进度。
状态机区分排队、运行、等待工具、重试等待、部分完成、成功、失败和取消。工具、模型与任务各有受控重试,统一预算防止重试放大;背压按模型、工具、租户和队列分别限流。Continuous batching 只优化 LLM 推理层,Agent 层仍要处理依赖、外部 IO、状态和恢复。
验收要注入 Worker 崩溃、重复投递、工具超时与网络分区,检查 fencing、幂等、checkpoint、事件续传和多租户隔离。相关材料提供异步、断点、并发和监控方向,没有现有 QPS、SLA 或生产压测数据,因此只能给出设计与验证方法,不能编造服务数字。

上线前必须证明的六件事
沿着一次任务从提交到恢复完整走一遍,下面每项都应有状态记录、故障演练或明确的待验证边界。
- 提交是否幂等并立即返回可授权的 task ID?
- 状态机是否区分等待工具、重试、部分完成、失败和取消?
- Worker 接管是否使用 lease、heartbeat 与 fencing?
- Checkpoint 是否保存结构化状态、证据、预算和版本?
- 模型、工具、队列与租户是否分别限流和监控?
- 是否明确没有虚构现有 QPS、SLA、生产压测或可用性数字?