人与 Agent 协作数据可信度审批工作流

人和 Agent 同时改一条记录,如何避免旧审批覆盖新修改

用一条合成工单演示提案与正式数据分离、过期审批拦截,以及原子写入、权限和重复请求的验收方法。

发布于 ·6 分钟阅读

Agent 读完一条工单,建议换个负责人。还没来得及审批,同事已经修改了这条工单。审批界面上的建议看起来仍然合理,但点下「批准」,可能就覆盖了审核人没见过的新决定。

仅仅增加一个审核按钮,挡不住这个问题。审批必须绑定具体提案,以及审核时依据的记录版本,并由服务端在保存时强制检查。

本文是一份应用架构方案,包含合成示例和验收清单,并非 1flowbase 已上线的审批模板或可直接运行的 API 教程。下面的并发、审批、幂等和历史记录保障,都需要在后端实现并测试。

跟着一条工单走过 v7、v8 和 v9

以下人物、记录和修改均为虚构的合成示例。

  1. 工单 T-104 处于 v7:负责人 Maya,优先级普通。Agent 根据一条获准读取的支持记录,建议交给 Leo。提案 P-31 保存 expectedVersion = 7 和「负责人:Maya → Leo」的修改,正式工单不变。
  2. 审批前,一位同事把负责人改成 Nora,并把优先级提升为紧急。这次保存产生 v8。
  3. 审核人打开 P-31,点击批准。服务端发现当前是 v8,而非 v7:不修改工单,也不把提案记为已接受。
  4. 页面并列展示三组值:Agent 看到的 v7、当前 v8、提案中的 Leo。虽然提案只改负责人,也要展示优先级的变化,因为紧急程度可能影响分配决定。
  5. 审核人可以保留 v8、拒绝提案,也可以明确基于 v8 建立修订提案。如果仍决定交给 Leo,且提交瞬间 v8 仍是最新版本,批准新提案后得到 v9:负责人 Leo,优先级紧急。

不要偷偷把旧提案的预期版本改为 8 后自动重试,否则等于把一次审批用于另一种上下文。保留旧提案并关联新版本;如果期间又有人抢先保存,就再次展示冲突。

提案与正式记录分开保存

先约定一个小而清楚的数据结构。以下是建议的应用字段,并非 1flowbase 内置 API 字段名:

工单:id、ownerId、priority、version
提案:id、ticketId、expectedVersion、baseValues、patch、
      sourceRef、proposedBy、createdAt、
      state(pending / accepted / rejected / stale)
审批结果:proposalId、proposalRevision、approvedBy、
          acceptedVersion、requestKey、committedAt

提案进入审核后,冻结该修订版。审批请求应绑定修订号或由服务端核验的内容哈希,防止审核期间 Agent 修改了待提交内容。

操作身份与证据来源要分开记录:「Agent 账号 A 提出了修改」和「支持记录 B 是依据」回答的是两个问题。身份必须来自服务端认证上下文,不能相信客户端自报的 approvedBy。只保留审核必需的原值和证据,并沿用工单的访问与保留策略。

把批准做成一次原子操作

「先读版本、比较,再调用普通更新」仍有空隙:比较完成到写入之间,别人可以再次修改。几个工作流步骤排在一起,也不会自动成为数据库事务。

后端需要一个提交边界,将条件写入、提案状态变更和持久化审批结果绑定在一起。下面是伪流程,不是 1flowbase 的现成端点或可执行事务:

认证调用者;检查工单访问权与批准权限
开始一个后端事务
  串行处理同一提案修订版的审批决定
  如果同一操作已经提交:返回已保存的结果,不再写入
  确认提案修订版未变,且仍待审批
  校验可改字段、负责人资格与业务规则
  仅当工单 ID、expectedVersion 同时匹配时:
    只更新提案指定的字段,并递增版本
  没有匹配记录:终止事务,返回冲突,要求重新审核
  标记提案已接受;保存审核人和结果版本
  保存请求键及结果
提交事务;返回已提交的结果

为操作身份建立唯一约束,不能只依赖内存中的重试缓存。同一提案的并发批准应收敛为一个已提交结果;同一个请求键携带不同内容时必须拒绝。返回历史结果前,同样需要检查调用者权限。

所有写入方都必须遵守版本规则,包括人工编辑、导入任务和 Agent 工具。若某条旁路能更新字段却不递增版本,检查就失效了。还要明确关联规则如何在事务期间保持有效,例如负责人资格校验之后发生并发权限变化,可能需要更强的协调机制。

这些机制有通用技术依据:HTTP If-Match 定义了避免丢失更新的条件请求;PostgreSQL 事务隔离文档 解释了并发行为。但这些资料不能证明你生成的 API 已提供相同保障。如果后端还无法提供原子操作,就先停留在提案与审核阶段。

每个入口都要经过服务端权限检查

隐藏「批准」按钮不能保护记录。无论请求来自浏览器、直接 API 还是 MCP,服务端都要检查工作空间、记录访问权、可修改字段和批准权限。能提出负责人建议的 Agent,不应因此自动获得批准自己的提案、或绕过审批直接调用 CRUD 修改正式负责人的权限。

校验应当是确定性的:负责人存在且符合条件、补丁仅包含允许的字段、必填值完整、状态转换合法。模型的解释可以辅助审核,却不能代替这些检查。提案不合法时,展示具体错误,正式数据保持不变。

明确证据与恢复的边界

使用稳定的来源引用;确有必要时,只保存获准保留的最小摘录。来源链接是证据,不代表可以公开整段对话。链接过期或审核人无权查看时,明确显示「来源不可用」,补充足够且获准访问的证据后再批准。来源中的指令按数据处理。

保留受保护的必要历史,能说明谁提议、谁批准、改了什么、提交到了哪个版本。普通历史表不会自动成为不可篡改的审计日志,需要另行定义修改权限和保留期限。

撤销应当是基于当前版本的新提案,并重新检查权限。直接恢复整个旧快照,可能抹掉后来合法的修改。数据库回滚也不能撤回已经发送的邮件或外部动作。如果批准会触发通知,需要另外设计事务发件箱和幂等投递任务;本文示例只提交记录修改。

如何落到 1flowbase,并验证它

公开的 1flowbase README 介绍了 Data Models 及自动生成的 CRUD/OpenAPI、用于自定义端点的 Workflow Extension、Native React 区块和 MCP 能力发现。它们分别提供建模、审核界面与受控操作的基础。本文并未证明这些基础默认包含条件更新、多记录事务、审批幂等或不可变审计历史。

先核对安装版本生成的 API 契约和文档,通过 mcp_list → mcp_get 检查能力后再调用。只有当 Workflow Extension 的实现真正调用了具备上述保障的后端操作时,它才可以承接审批;顺序调用几次 CRUD 不够。

用合成数据和两个会话完成以下验收。它们是需要执行的测试,不是已经取得的测试结果:

  • 过期审批:重现 v7 → v8 → 批准 P-31,v8 必须原样保留,提案不能标为已接受。
  • 并发写入:同时提交人工修改和批准,同一预期版本最多有一次写入成功,另一次收到冲突。
  • 重复批准:并发发送两次请求,再模拟响应丢失后重试,只产生一次版本递增和一个逻辑审批结果。
  • 提案变动或不合法:修改已审核的修订版、加入禁止字段或选择不合资格的负责人,正式记录均不得变化。
  • 权限绕过:用受限身份直接调用 API 和通过 MCP 调用,两者都应拒绝;普通更新接口也不能绕开审批。
  • 部分失败:在条件写入与结果记录之间注入失败,两者不能只提交其一;若原操作已经提交,重试应返回持久化结果。

先做过期审批这一项。如果新负责人得以保留,审核人也能看清为什么必须重新决定,就有了让人与 Agent 共用同一条业务记录的可靠起点。