Agent 一接筛选结果页就开始改到隐藏项:从 Result Scope 到 Visible Set Proof 的工程实战
很多团队把 Agent 接进后台列表页后第一反应是让它“学会筛选、勾选、批量提交”。真正上线后最难接受的错误却不是点错按钮而是筛选条件已经变了Agent 还拿着上一次的结果集继续改。一次批量改标签、改状态、改负责人最后改到隐藏项、跨页旧项事故往往就从这里开始。⚠️这类问题高频出现在带虚拟滚动、异步刷新、权限切换和多标签协作的后台。页面上看见的是“当前筛选结果”Agent 记住的却常是几秒前扫到的一组 DOM 节点。只要列表重排、条件回填或后端结果刷新之前那批对象就不再等于“当前允许提交的对象”。图 1筛选页里的危险点不是按钮而是结果集边界会变化问题为什么总发生 很多自动化只验证“有没有勾选到目标行”却没验证“这些行是否仍属于当前筛选结果”。一旦搜索词、分页游标、组织范围或排序条件变化旧选择就可能漂到新的列表语义里。此时模型不是不会操作而是在用过期结果集执行正确动作。更麻烦的是现代后台普遍用了虚拟列表。屏幕里可见的 20 行只是整个结果集的一个窗口DOM 复用后同一个行容器几秒后可能已经代表另一条记录。若系统只按位置或旧文本继续操作误改几乎不可避免。图 2异步刷新、虚拟滚动与权限切换会让“已选中”失去原语义一套能落地的约束 ✅先给结果集做Result Scope把本次提交绑定到筛选参数、排序方式、页码或游标版本而不是只绑定“选中了哪些行”。接着为每个待提交对象生成Visible Set Proof至少记录稳定主键、显示名称摘要和当前可见批次指纹。提交前再回证一次这些对象是否仍在当前结果集、是否仍可见、是否仍满足筛选条件。下面这段实现的核心不是自动点击而是提交前二次验明对象身份与结果集边界fromdataclassesimportdataclassimporthashlibdataclassclassScope:query:strsort:strcursor:strresult_hash:strdeffingerprint(ids:list[str])-str:returnhashlib.sha1(|.join(ids).encode()).hexdigest()defcan_commit(scope:Scope,live_query:str,live_sort:str,live_cursor:str,live_ids:list[str],selected_ids:list[str])-bool:if(scope.query,scope.sort,scope.cursor)!(live_query,live_sort,live_cursor):returnFalseifscope.result_hash!fingerprint(live_ids):returnFalsereturnall(item_idinlive_idsforitem_idinselected_ids)实战里怎么验 方案校验点好处风险只记勾选状态checkbox 是否选中实现快列表刷新后最容易误改只记对象 ID主键是否存在能防部分 DOM 复用无法发现筛选条件已变Scope Proof 提交前回证条件、结果集、对象身份同时校验最稳适合批量操作要多一次读取与比对线上经验是批量操作宁可慢一步也不要省掉提交前回证。尤其在 React 表格、虚拟滚动和服务端筛选页里“可见”不是视觉状态而是一次可验证的提交前契约。图 3真正该提交的是“当前结果集里的这批对象”不是几秒前的扫描快照值不值得这样做 笔者认为这类约束看上去像给 Agent 加刹车本质上是在给批量操作补交易边界。很多团队一开始只盯点击成功率后面才发现真正昂贵的是误改后的追账、回滚和人工复盘。把 Result Scope 和 Visible Set Proof 接进链路后系统从“会不会点”升级成“能不能安全提交”。接下来 3 到 6 个月后台 Agent 会越来越多地进入高风险批量场景营销名单、权限对象、工单集合、实验目标组都会被自动化接管。谁先把“结果集边界校验”做成默认能力谁的 Agent 就更像生产系统而不是会操作页面的演示脚本。如果团队也在做筛选页自动化最该先补的不是更长的提示词而是提交前这一次结果集回证。你们现在的批量操作真的是对“当前可见对象”生效吗