很多团队把 Agent 接进实时协作文档后最先暴露的问题不是不会写而是会把别人刚改完的内容整块盖掉。页面上明明能看到光标和提示自动化一落笔旧段落还是被整段回写。问题不在模型理解差而在执行链路把“拿到当前 DOM”当成“拿到编辑权”。⚠️协作文档的真实状态并不只在屏幕。一个段落可能已被同事改写但本地缓存还没刷新另一个 Agent 可能拿着旧 selection 继续提交。此时如果系统仍按整页 diff 或整段 replace 执行覆盖事故只会从人工误操作变成自动化误操作。问题不在改字而在谁有资格提交实时协作文档最容易踩的坑是把 presence 只当 UI 信息。光标在线不等于当前 Agent 对目标块拥有可提交窗口。没有 lease写入就可能基于旧快照。另一个误区是直接对整篇文档做 patch。这样实现快但一旦双方分别改了不同块系统仍会把无关区域一起带回造成连带伤害。更稳的做法是先给目标块发放短时 Presence Lease再把提交粒度收窄到 block。只有 lease 未过期、块版本未变化、selection 仍命中原锚点时写入才允许落地。图 1协作文档的真正风险来自旧快照和提交窗口漂移一套能落地的三段式防线第一层是Presence Lease。系统在 Agent 决定修改某个块时不是立刻提交而是先记录doc_id、block_id、holder、expires_at和base_version。这样每次提交都能回答写入是否仍基于同一份世界状态。✅第二层是Block-Level Commit。提交对象只允许是目标块及其最小上下文而不是整页 HTML。块级提交让冲突域明显缩小失败时也只撤销该块不影响旁边段落、图片和评论流。第三层是Commit 前回放校验。真正发送 patch 前系统再抓一次当前块文本与版本校验 anchor、hash 和编辑人 presence 是否一致。只要其中一项漂移就转成 rebase 或人工确认而不是硬写。️fromdataclassesimportdataclassfromtimeimporttimedataclassclassLease:block_id:strholder:strbase_version:intexpires_at:floatdefcan_commit(lease:Lease,live_version:int,live_anchor:str,expected_anchor:str)-bool:iftime()lease.expires_at:returnFalseiflive_version!lease.base_version:returnFalsereturnlive_anchorexpected_anchor图 2把提交粒度收窄到 block才能把冲突限制在最小范围方案提交粒度冲突影响面回滚成本适用场景整页替换document极大高单人离线整理段落替换section中中低频协作块级提交 leaseblock小低多人实时协作、Agent 自动写入实战验证冲突率下降不靠更强模型在一个会议纪要自动回填链路里团队把“整段 replace 固定重试”改成“lease block commit 回放校验”后最直接的变化不是生成质量提升而是覆盖事故从高频偶发变成可拦截异常。旧方案里只要人工在 3 秒内改过相邻段落Agent 就可能把旧内容写回新方案里这类请求会先被判为版本漂移转入重抓和重算。更关键的是失败路径终于可观测。系统能区分 lease 过期、锚点失效、块版本变化和权限抢占而不是统一报“保存失败”。很多事故不是模型胡写而是提交前世界已经变了。图 3回放校验把“写错内容”转成“发现漂移后拒绝提交”真正要治理的是协作语义笔者认为协作文档里的 Agent 不是普通输入法而更像一个带副作用的协作者。系统设计重点不该放在“它能写多快”而该放在“它何时有资格写、写哪一块、写前如何再确认一次”。如果这三件事没有工程护栏再强的模型也会在多人协作里放大事故。未来 3 到 6 个月这类系统会从“整页生成”走向“块级事务化编辑”。真正有价值的方向不是让 Agent 更频繁地下笔而是让它像数据库事务一样只在拿到正确上下文、租约和版本时提交。你在协作文档自动化里更难接受的是覆盖冲突还是人工确认过多欢迎交流。