AIGC标识 全栈视角下的 Agent 应用:任务状态、接口契约与工具权限

做 Agent 应用时,聊天界面通常很快就能搭起来。真正需要全栈协同处理的,是界面之外的几个问题:工具到底执行到哪一步了?页面刷新后如何恢复?用户点了取消,服务端是否真的停止?请求超时后,再点一次会不会产生重复操作?

这些问题横跨前端、后端、任务执行器和外部系统。本文结合我在个人网站中记录的工程思考,从全栈视角梳理一套设计方法。下面的状态与接口示例用于解释方案,不代表某个已经上线的 Agent 平台,也不包含性能实测结论。

一、先定义任务状态,再实现聊天界面

流式输出解决的是“文字怎样逐步显示”,任务协议解决的则是“业务实际发生了什么”。

一次模型输出结束,不一定意味着资料已经导出、文件已经生成或外部操作已经完成。因此,建议把聊天消息与业务任务分开建模:

  • 消息负责呈现用户输入、阶段说明和最终答复。
  • 任务记录执行状态、当前步骤、结果与恢复信息。
  • 工具调用记录一次具体操作的输入、授权和执行结果。

可以先设计这些状态:

  • queued:任务已接收,等待执行。
  • running:正在执行。
  • waiting_input:等待补充信息或确认。
  • cancelling:已请求取消,仍在确认停止结果。
  • verifying:执行结果暂不确定,正在核对外部状态。
  • succeeded、failed、cancelled:已确认的终态。

这只是一个示例,实际项目可以合并或扩展。关键是把状态含义写清楚,尤其不能把“网络断开”“模型输出结束”和“业务完成”当成同一件事。

例如,用户点击取消后,前端可以立即显示“正在取消”,但不能直接展示“已经停止”。只有执行器确认停止,或服务端核对到明确结果后,才能进入对应终态。对于已经完成的步骤,还需要展示保留下来的结果。

二、让服务端状态成为恢复依据

页面刷新、网络重连和多端访问都可能让前端丢失上下文。只依靠浏览器内存里的消息数组,很难恢复一次执行中的任务。

一个可行的设计是:服务端保存任务快照,并为同一任务的已持久化事件分配递增序号。前端先读取快照,再按事件推进界面。

下面是一条示意事件:

{
  "taskId": "task-example",
  "seq": 12,
  "status": "running",
  "step": "retrieve_documents",
  "occurredAt": "2026-11-10T12:00:00Z"
}

这里的时间和标识都是示例值。字段承担的职责应当明确:

  • taskId:定位同一项工作。
  • seq:判断同一任务内的事件顺序,识别重复事件。
  • status:描述服务端确认的任务状态。
  • step:给用户显示可理解的当前阶段。
  • occurredAt:记录发生时间,辅助排查问题。

事件序号应由服务端统一分配,不能让多个执行器各自递增,再假定它们天然有序。前端遇到旧事件可以忽略,发现序号缺口则应补拉事件或重新获取快照。终态以后出现的迟到结果,也需要由任务协议规定如何处理。

这种设计把“刷新后如何恢复”变成了接口能力,而不是一个页面层面的补丁。

三、接口契约不仅要有参数,还要描述副作用

普通业务接口通常会定义请求参数、返回值和错误码。Agent 的工具接口还需要说明:

  1. 操作的目标资源是什么。
  2. 是只读查询,还是会产生写入或外部影响。
  3. 需要什么权限和确认。
  4. 怎样判断成功、失败或结果未知。
  5. 是否支持幂等,以及如何查询执行结果。

以“导出业务报表”为例,前端可以展示时间范围、资源范围和导出目的地;后端必须再次校验登录身份、数据范围和当前权限。工具不能因为自己的描述写着“允许执行”,就获得额外权限。

MCP 可以统一工具发现和调用方式,但不能代替业务系统的授权逻辑。权限判断仍要落在宿主与服务端;页面上禁用一个按钮,也不能作为安全边界。

授权还应与具体动作和资源绑定。如果原本确认的是 A 项目、某个日期范围,执行参数后来变成 B 项目或更大的范围,就应重新判断已有授权是否适用。

四、重试之前,先区分“失败”和“结果未知”

全栈链路中,一个容易被低估的问题是:客户端没有收到成功响应,不等于服务端没有执行。

例如,导出任务已经创建,但响应在返回途中断开。此时如果重新发送创建请求,可能得到两个导出任务。更危险的场景是工具已经对外写入,却在返回结果前超时。

建议至少区分三类情况:

  • 确认未执行:可以根据错误原因决定是否重试。
  • 确认已执行:返回已有结果,避免重复操作。
  • 无法确认是否执行:先查询外部状态或进入核对流程,不能直接当作失败重跑。

对于有副作用的操作,可以设计幂等键,但幂等能力不是在请求中加一个字符串就完成了。服务端需要明确键的作用范围、保存时长、并发处理方式,以及同一个键对应不同参数时如何拒绝或处理。

如果外部系统不支持幂等,也不能声称本系统已经保证“恰好执行一次”。需要结合外部查询、业务唯一约束或人工核对来控制重复执行风险。

五、可观察性要贯穿链路,但不要把日志当作状态存储

用户需要知道任务在检索、执行还是等待确认;开发者则需要定位哪一步慢了、失败了,以及相关请求发生在哪里。

可以用任务标识关联模型请求、接口调用和工具调用,再使用 trace 与 span 描述各段执行过程。OpenTelemetry 提供了这些可观察性概念。

这里有两个边界:

第一,界面展示可验证的阶段和结果,不需要展示模型内部推理。 “正在检索资料”“等待确认导出范围”通常比一长串内部过程更有用。

第二,链路日志不能代替持久化的任务状态和必要审计记录。 追踪数据可能被采样、过期或暂时不可用,业务恢复不能只依赖它。任务快照、执行结果和关键操作记录应当有自己的存储与保留规则。

记录工具参数时,也应采用字段白名单和脱敏,避免为了排障把令牌、完整业务数据或私人内容复制到日志里。

六、用故障场景验收,而不只看正常流程

正常流程能跑通,只能证明最顺利的一条路径可用。联调时,可以把下面几项作为验收清单:

  • 断网重连:页面能否恢复到服务端最新状态,已有结果是否保留。
  • 重复或乱序事件:界面是否重复追加内容,是否被旧状态覆盖。
  • 取消与完成同时发生:最终状态是否反映实际结果,而不是只反映按钮点击。
  • 工具超时但外部已执行:系统能否先核对结果,避免重复写入。
  • 多步骤任务部分成功:能否清楚展示已完成和未完成的部分。
  • 权限变化或过期:执行前是否重新校验,而不是沿用页面打开时的判断。

对于补偿操作,也要明确哪些可以自动执行,哪些需要重新确认。删除已生成的文件、撤销外部变更,并不总是一个无风险的“回滚”。

小结

从全栈角度看,Agent 应用需要一套清晰的业务执行协议:

  • 前端准确呈现任务阶段,处理重连与迟到事件。
  • 后端保存可信状态,校验权限并协调执行。
  • 工具层说明副作用、幂等与结果查询方式。
  • 可观察性与审计帮助定位问题,但不替代业务状态。

最终要让用户能回答三个问题:已经发生了什么,现在还在做什么,下一步可以做什么。 这比单纯让聊天界面更像“智能助手”更值得优先做好。


本文由邢淼整理,基于本人个人网站的工程笔记扩展为全栈视角。

本文为个人工程设计建议,内容不代表任职单位的官方立场。

posted @ 2026-11-11 01:00  邢淼  阅读(0)  评论(0)    收藏  举报