← 数据分析 Agent

多步分析任务耗时很长,接口怎么设计?步骤成功怎么判定?

高频 中等 分析计划与多步执行 · 第 2 / 2 问 更新于 2026/09/29
Agent异步任务轮询状态机数据分析Agent
本题落地项目AI Agent数据分析平台

简化版

接口不要同步等所有步骤跑完。一个多步分析任务要调很多次模型,动辄一两分钟,同步请求会触发前端或网关超时。正确做法是:接口只负责创建任务和步骤记录、启动后台执行,然后立即返回;后台按顺序逐步执行,每一步把状态(待执行 → 执行中 → 已完成 / 执行失败)写库;前端轮询或通过 SSE 订阅状态,直到所有步骤结束。步骤成功与否不能以「没抛异常」为准,要看业务状态:SQL 校验失败、执行失败这些情况往往不抛异常,而是以状态值返回,必须显式判断。

详细版

前端点「执行计划」
  → 接口:清理上一轮 → 为每步插入「待执行」记录 → 启动后台任务 → 立即返回步骤列表
  → 前端每 2 秒查询一次步骤状态
后台逐步执行:
  步骤 i:状态改为「执行中」
        → 生成 SQL → 校验执行(失败则纠错一次再执行)
        → 业务状态为「已执行」? 否 → 「执行失败」,记录原因
                                  是 → 图表、解读 → 「已完成」,写输出摘要
        → 记录耗时、开始和结束时间
前端:所有步骤都是「已完成」或「执行失败」→ 停止轮询;有失败的步骤就提示警告

几个设计要点:

要点做法
快速返回接口只做创建和启动,不等执行结果
状态落库每步状态、耗时、错误写数据库,页面随时可查
成败判定按业务状态字段判断,不以是否抛异常判断
结束条件所有步骤进入终态才停止轮询
重复执行重跑前清理上一轮的步骤和中间产物
中断恢复服务重启后把停在「执行中」的任务标为失败,允许重新发起

完整版教学

一、为什么同步接口扛不住

按一次模型调用几秒的量级粗略估算一个多步分析任务的耗时(具体数字随模型和数据量变化):

每一步:生成 SQL(约 3 秒)+ 执行(约 0.5 秒)+ 纠错(偶尔,约 3 秒)
       + 图表推荐(约 2 秒)+ 数据解读(约 5 秒)≈ 10.5 ~ 13.5 秒
5 个步骤 ≈ 53 ~ 68 秒

前端调用接口时通常会给请求设一个超时(很多项目设在 30 秒左右),网关、负载均衡、反向代理也各有自己的超时(比如 Nginx 的 proxy_read_timeout 默认 60 秒)。同步等待的结果是:后台还在跑,前端已经报「请求超时」,用户以为失败了又点一次,同一个计划被执行了两遍。所以只要任务耗时可能超过几秒,就应该改成异步。知识库批量向量化是同一类问题,做法见「知识库几百上千个片段要批量向量化,任务怎么设计?失败了怎么处理?」。

二、异步的基本形态:创建、执行、查询分离

POST /executePlan/{planId}      创建步骤记录,启动后台任务,立即返回
GET  /steps?planId=...          查询当前各步骤的状态
(后台线程)                     逐步执行,更新状态

接口返回时,步骤记录已经存在(状态为「待执行」),页面能立刻展示完整的步骤列表,用户看到的是「任务已经开始,正在一步步推进」,而不是一个转圈的按钮。后台执行可以用线程池、异步任务框架或消息队列;学习和中小规模场景下,线程池足够,但要注意线程池大小和异常处理。

三、前端怎么拿到进度:轮询还是推送

方式做法适合
轮询每隔 2 秒请求一次状态接口实现简单,步骤级的粗粒度进度
SSE服务端按事件推送状态变化需要实时展示每个节点的细粒度进度
WebSocket双向通信需要中途干预(暂停、取消)的场景

轮询要注意两点:一是结束条件,所有步骤都进入终态(已完成或执行失败)才停止;二是离开页面要清理定时器,否则用户切到别的页面还在不停请求。

四、步骤成功不能以「没抛异常」判断

这是最容易写错的地方。假设执行一步的代码是这样的:

try {
    analysisService.generateSql(request);
    analysisService.validateAndExecute(queryId);   // 校验失败、执行失败都不抛异常
    analysisService.recommendChart(queryId);
    step.setStatus("已完成");                        // 错误:只要没进 catch 就算成功
} catch (Exception e) {
    step.setStatus("执行失败");
}

问题在于:SQL 安全校验不通过、执行报错时,业务方法通常把失败原因写进记录并正常返回,而不是抛异常——因为对单次分析页面来说,这是一个需要展示给用户的正常结果。于是这一步明明失败了,却被标成「已完成」。正确的写法是执行完关键环节后,显式检查业务状态:

// 校验或执行失败时业务状态不是「已执行」,必须显式判断
if (!"已执行".equals(query.getStatus())) {
    step.setStatus("执行失败");
    step.setErrorMessage(query.getErrorMessage());
    return;
}

易错点:业务失败 ≠ 异常。凡是以状态返回的失败,都要在编排层显式判断,否则失败会被当成成功继续往下走。

五、步骤状态机

每个步骤的状态只能按固定路径流转:

待执行 ──► 执行中 ──┬──► 已完成
                   └──► 执行失败

状态机的好处是可检查:前端据此判断是否继续轮询;报告生成时只汇总已完成的步骤;排查问题时,停在「执行中」很久的步骤说明后台卡住了。每次状态变化同时记录时间,结束时计算耗时,这些数据会直接展示在执行追踪页面上。

六、重复执行和中断恢复

两个在异步场景下必须考虑的问题:

重复执行。 用户对同一个计划点两次执行,如果不处理,会有两轮步骤同时在跑、互相覆盖。做法是执行前先清理上一轮的步骤、中间分析记录和基于旧步骤的报告;更严格的场景下还要加锁,拒绝对正在执行的计划重复发起。

中断恢复。 后台任务执行到一半,服务重启了,数据库里的步骤会永远停在「执行中」。启动时应该扫描这类记录,把它们标为失败并写明「服务重启,执行中断」,让用户可以重新发起,而不是面对一个永远不会结束的进度条。

七、失败的步骤如何影响整体

一个计划里某一步失败,后面的步骤要不要继续?数据分析场景下通常继续执行:每一步分析的是相对独立的维度,第 3 步没取到数,不影响第 4 步看另一个维度。失败步骤单独记录原因,报告生成时基于已完成的步骤汇总,并说明哪些维度没有取到数据。只有当步骤之间有强依赖(后一步要用前一步的结果)时,才需要在前置步骤失败时终止后续步骤。

八、常见误区与追问

  • 误区:把前端超时时间调长就能解决长任务问题。 网关、负载均衡也有超时,用户刷新页面还会重复提交,长任务应改为异步加进度查询。
  • 误区:代码没抛异常,步骤就算成功。 校验失败、执行失败常以状态值返回而不抛异常,必须显式检查业务状态。
  • 误区:轮询到有结果就可以停。 要等所有步骤进入终态才停止,并在离开页面时清理定时器。
  • 误区:重新执行计划时直接追加新步骤。 新旧两轮的步骤会混在一起,应该先清理上一轮的步骤和中间产物。
  • 误区:服务重启后任务会自然恢复。 内存里的执行状态会丢失,要在启动时把中断的任务标为失败,允许用户重新发起。
  • 追问:轮询和 SSE 怎么选? 步骤级的粗粒度进度用轮询足够;需要实时展示节点级进度或流式输出时用 SSE。
  • 追问:某一步失败后,后续步骤还执行吗? 步骤相互独立时继续执行并单独标记失败;有强依赖时,前置失败就终止后续步骤。

九、加强记忆

多步分析动辄一两分钟,同步接口会超时,还会让用户重复提交。设计成创建、执行、查询分离:接口插入「待执行」步骤、启动后台任务后立即返回,后台逐步执行并把状态、耗时、错误写库,前端轮询或用 SSE 取进度,所有步骤进入终态才停,离开页面清定时器。步骤状态按「待执行 → 执行中 → 已完成 / 执行失败」流转;成败判定看业务状态,不看有没有抛异常,因为校验失败、执行失败常以状态返回。重复执行先清理上一轮,服务重启后把停在执行中的任务标为失败;步骤独立时单步失败不影响其他步骤。

项目实战落地

项目里怎么做的

《AI Agent数据分析平台》的「Agent 执行追踪」页面是这样设计的:

  • 接口快速返回:点击「执行计划」后,后端先清理这个计划上一轮留下的步骤、Agent 中间分析记录和旧报告,再为每一步插入一条「待执行」的 agent_execution_step 记录,用 CompletableFuture.runAsync 启动后台执行,立即把步骤列表返回给前端;
  • 前端轮询:前端每 2 秒查询一次步骤列表,只要还有「待执行」或「执行中」的步骤就继续;全部进入「已完成」或「执行失败」后停止,存在失败步骤时显示警告而不是「执行完成」;离开页面时清理定时器;
  • 成败按业务状态判断:每一步复用生成 SQL、校验执行的能力后,检查分析记录的状态是不是「已执行」,第一次不是就自动纠错一次再执行,仍然不是就把这一步标为「执行失败」并写入错误信息;只有 SQL、图表和解读都成功才标为「已完成」,并把数据解读写入 output_summary;
  • 过程留痕:每一步记录序号、名称、分析问题、SQL 目的、建议图表、状态、耗时、开始和结束时间、错误信息,以及关联的 analysis_query_id,可以追到具体的 SQL 和结果。

为什么这样取舍

  • 异步加轮询:计划通常包含多次模型调用,如果一直使用同步请求,很容易超过前端 30 秒的请求超时。
  • 自动纠错只重试一次:后台执行没有人盯着,重试必须有上限,避免错误 SQL 造成无限循环。

面试官还会追问

  • 同一个计划重新执行时,为什么要先删除上一轮步骤关联的分析记录,再删除步骤本身?
  • 执行追踪页的计划下拉框为什么只列出状态为「已生成」的计划?

学完《AI Agent数据分析平台》,上面这些追问你都会迎刃而解。

本题落地项目登峰造极AI Agent数据分析平台基于Agent、Text-to-SQL和安全查询执行,实现数据集管理、指标口径维护、分析意图识别、SQL纠错、图表推荐、数据解读和报告生成,适合BI分析落地、过程追踪和多轮会话。SpringbootSpringAIAgentText-to-SQLLLM源码+SQL喂饭学习教程配套面试文档环境安装文档项目运行文档 学习这个项目