全栈视角下的 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 的工具接口还需要说明:
- 操作的目标资源是什么。
- 是只读查询,还是会产生写入或外部影响。
- 需要什么权限和确认。
- 怎样判断成功、失败或结果未知。
- 是否支持幂等,以及如何查询执行结果。
以“导出业务报表”为例,前端可以展示时间范围、资源范围和导出目的地;后端必须再次校验登录身份、数据范围和当前权限。工具不能因为自己的描述写着“允许执行”,就获得额外权限。
MCP 可以统一工具发现和调用方式,但不能代替业务系统的授权逻辑。权限判断仍要落在宿主与服务端;页面上禁用一个按钮,也不能作为安全边界。
授权还应与具体动作和资源绑定。如果原本确认的是 A 项目、某个日期范围,执行参数后来变成 B 项目或更大的范围,就应重新判断已有授权是否适用。
四、重试之前,先区分“失败”和“结果未知”
全栈链路中,一个容易被低估的问题是:客户端没有收到成功响应,不等于服务端没有执行。
例如,导出任务已经创建,但响应在返回途中断开。此时如果重新发送创建请求,可能得到两个导出任务。更危险的场景是工具已经对外写入,却在返回结果前超时。
建议至少区分三类情况:
- 确认未执行:可以根据错误原因决定是否重试。
- 确认已执行:返回已有结果,避免重复操作。
- 无法确认是否执行:先查询外部状态或进入核对流程,不能直接当作失败重跑。
对于有副作用的操作,可以设计幂等键,但幂等能力不是在请求中加一个字符串就完成了。服务端需要明确键的作用范围、保存时长、并发处理方式,以及同一个键对应不同参数时如何拒绝或处理。
如果外部系统不支持幂等,也不能声称本系统已经保证“恰好执行一次”。需要结合外部查询、业务唯一约束或人工核对来控制重复执行风险。
五、可观察性要贯穿链路,但不要把日志当作状态存储
用户需要知道任务在检索、执行还是等待确认;开发者则需要定位哪一步慢了、失败了,以及相关请求发生在哪里。
可以用任务标识关联模型请求、接口调用和工具调用,再使用 trace 与 span 描述各段执行过程。OpenTelemetry 提供了这些可观察性概念。
这里有两个边界:
第一,界面展示可验证的阶段和结果,不需要展示模型内部推理。 “正在检索资料”“等待确认导出范围”通常比一长串内部过程更有用。
第二,链路日志不能代替持久化的任务状态和必要审计记录。 追踪数据可能被采样、过期或暂时不可用,业务恢复不能只依赖它。任务快照、执行结果和关键操作记录应当有自己的存储与保留规则。
记录工具参数时,也应采用字段白名单和脱敏,避免为了排障把令牌、完整业务数据或私人内容复制到日志里。
六、用故障场景验收,而不只看正常流程
正常流程能跑通,只能证明最顺利的一条路径可用。联调时,可以把下面几项作为验收清单:
- 断网重连:页面能否恢复到服务端最新状态,已有结果是否保留。
- 重复或乱序事件:界面是否重复追加内容,是否被旧状态覆盖。
- 取消与完成同时发生:最终状态是否反映实际结果,而不是只反映按钮点击。
- 工具超时但外部已执行:系统能否先核对结果,避免重复写入。
- 多步骤任务部分成功:能否清楚展示已完成和未完成的部分。
- 权限变化或过期:执行前是否重新校验,而不是沿用页面打开时的判断。
对于补偿操作,也要明确哪些可以自动执行,哪些需要重新确认。删除已生成的文件、撤销外部变更,并不总是一个无风险的“回滚”。
小结
从全栈角度看,Agent 应用需要一套清晰的业务执行协议:
- 前端准确呈现任务阶段,处理重连与迟到事件。
- 后端保存可信状态,校验权限并协调执行。
- 工具层说明副作用、幂等与结果查询方式。
- 可观察性与审计帮助定位问题,但不替代业务状态。
最终要让用户能回答三个问题:已经发生了什么,现在还在做什么,下一步可以做什么。 这比单纯让聊天界面更像“智能助手”更值得优先做好。
本文由邢淼整理,基于本人个人网站的工程笔记扩展为全栈视角。
- 个人网站:邢淼的技术实践与工程笔记
- 相关原文:从前端架构到 Agent 交互:状态、可观察性与工具权限
- 技术参考:OpenTelemetry 可观察性基础、MCP 工具规范
本文为个人工程设计建议,内容不代表任职单位的官方立场。

浙公网安备 33010602011771号