让一个自主 Agent 跑起来三天就能做到。但让一个 Agent 在团队里长跑——权限收口、接管机制、记忆清洗、成本核算——这些债你一定会还问题只是主动还还是被动还。这是我们第 7 天补的那批账单。我很少在技术文章里聊团队这个词。这次要聊。不是因为 Agent 技术变难了而是因为我们意识到Agent 跑久了制造的不是 bug是治理空白。你的代码没写错模型没出问题但团队里开始出现一些奇怪的对话——“这个任务谁批的”“Agent 把那个配置改了是有人让它改的吗”“它昨晚干了啥我能看到吗”这类问题的共同来源是当初接入 Agent 的时候我们把它当成了一个工具——像 CI/CD 一样跑就跑挂了重启。但它实际上更接近一个有权限的同事——它能写、能改、能发、能删而且从不请假。这篇是我们openclaw-lab运行 7 天之后在团队层面补上的 5 笔治理债。没有宏观方法论只有实际踩过的坑和补上的机制。运维债 1权限矩阵——Agent 该能做什么你可能没认真想过Agent 上线前大多数团队做的权限设计是这样的“它需要调 API那就给它 API Key。它要发帖那就让它有发帖权限。”这不是权限设计这是权限发放。两者的区别权限发放把 Agent 需要用到的权限全给了然后祈祷它不出事权限设计把 Agent 在正常路径上需要的权限给了并且明确了它不应该能做什么我们在第 6 天做了一张权限矩阵这件事花了我们一个小时但此前五天没人做因为每个人都以为别人在管这件事。## Agent 权限矩阵模板填 ✅/❌/ | 操作 | 是否允许 | 是否需要审批 | 每日上限 | 备注 | |---|---|---|---|---| | 读取数据 | ✅ | ❌ | 无限制 | 只读操作无风险 | | 生成草稿 | ✅ | ❌ | 20 篇/天 | 草稿不发布不对外可见 | | 发布内容 | ✅ | ❌ | 5 篇/天 | cooldown 30min | | 修改已发布内容 | | ✅ | 5 次/天 | 需要 Telegram 确认 | | 删除内容 | ❌ | — | 0 | 永不允许只能由人操作 | | 调用外部 Webhook | | ✅ | 10 次/天 | 需记录 payload | | 修改系统配置 | ❌ | — | 0 | 高风险禁止 | | 发送通知/邮件 | ✅ | ❌ | 50 条/天 | 仅内部通知渠道 |有几个填表时冒出来的发现让我们意识到之前有多粗心Agent 能修改系统配置。我们的 Agent 有调用内部 API 的权限其中一个接口可以修改推送策略。当时给权限是因为有时候需要动态调整——但我们从没想清楚谁决定什么时候动态调整。Agent 能调用外部 Webhook。理由是有时候需要触发下游任务。但 Webhook 是最不可逆的操作之一——发出去就发出去了没有撤回。填完矩阵之后“删除和修改配置两列全是 ❌。为什么因为当你真的坐下来思考如果 Agent 今晚出 bug 误触发了这个操作我能接受吗”答案是显然的。没有 Agent 需要以自动方式删除东西。如果它需要那是流程设计的问题不是权限的问题。这张矩阵本身不是代码是共识。它的价值是让整个团队对Agent 有什么权力有一个统一的认知而不是各自揣测。运维债 2失败恢复——重试 3 次不是失败处理是鸵鸟Agent 的失败分两类处理方式完全不同混在一起处理是最常见的错误。第一类瞬时失败网络超时、限流 429、临时服务不可用。这类失败是可重试的指数退避就够了。第二类结构性失败内容违规被平台拒绝重试 100 次结果一样操作已成功执行但状态未记录重试会产生副作用认证过期重试会连续产生 401Agent 的逻辑本身进入了死循环把这两类都丢给重试 3 次的结果我们第 4 天领教过一个内容审核失败的任务重试了 5 次每次都发出了相同的内容被平台判定为刷量。我们后来做了一个简单的失败分类器# failure_classifier.pyfromenumimportEnumclassFailureType(Enum):TRANSIENTtransient# 可重试STRUCTURALstructural# 不可重试需人工介入SIDE_EFFECTside_effect# 操作可能已成功幂等检查后再决定defclassify_failure(error_code:int,error_msg:str,attempts:int)-FailureType:# 认证/授权失败 → 结构性别重试iferror_codein(401,403):returnFailureType.STRUCTURAL# 内容审核/业务逻辑拒绝 → 结构性iferror_codein(422,451)orcontent_policyinerror_msg:returnFailureType.STRUCTURAL# 已执行但未确认 → 先查幂等再决定iferror_code0andtimeoutinerror_msg:returnFailureType.SIDE_EFFECT# 限流/服务不可用 → 可重试读 Retry-Afteriferror_codein(429,503):returnFailureType.TRANSIENT# 超过重试次数 → 升级为结构性ifattempts3:returnFailureType.STRUCTURALreturnFailureType.TRANSIENT# 在 task runner 里使用asyncdefhandle_failure(task,error_code,error_msg):failure_typeclassify_failure(error_code,error_msg,task.attempts)iffailure_typeFailureType.TRANSIENT:delay2**task.attempts*60# 2m, 4m, 8mawaitqueue.retry_after(task,delay_secondsdelay)eliffailure_typeFailureType.SIDE_EFFECT:# 先检查操作是否已在目标侧生效already_doneawaitcheck_idempotency(task)ifalready_done:awaitqueue.complete(task)# 标记为完成不重试else:awaitqueue.retry_after(task,delay_seconds60)eliffailure_typeFailureType.STRUCTURAL:awaitqueue.fail_permanently(task)awaitnotify_on_call(fTask{task.id}hit structural failure:{error_msg})这段代码不长但它显式地区分了应该重试和不应该重试——这个区分本来应该在 Agent 系统设计的第一天就做而不是出了事故才做。还有一个比较反直觉的发现操作已成功但状态未记录的失败比显式错误更危险。显式错误422 Content Rejected你能看到。但 Agent 发完请求、服务端已执行、但响应在网络中丢失了——这种情况 Agent 以为操作失败了会重试而目标侧已经执行了两次。解法是 Outbox Pattern先写我要做 X执行完再写X 完成了两步写到不同存储进程重启后先查 Outbox。这块在上篇Agent 跑 24 小时后我补上的 6 个运维护栏有完整实现这里不重复。运维债 3人工接管机制——“暂停不等于关掉”这笔债我们欠得最晚但付出的代价最直接。第 5 天凌晨Agent 在执行一个批量任务时行为开始异常——不是报错而是开始生成质量极差的内容上下文丢失导致后来确认是 context window 溢出。我们想暂停它结果没有暂停入口只能kill。kill掉进程有三个问题丢失 in-progress 任务的状态重启后可能重跑不知道它当前做到哪一步了重启后 Agent 从头开始之前的错误状态比如错误的 context还在我们后来实现了一个轻量的分级接管协议核心就是 4 个端点# Level 1 - 软暂停完成当前任务后不接新任务curl-XPOST http://localhost:8765/control/pause\-HAuthorization: Bearer$ADMIN_TOKEN\-d{reason: 人工检查, operator: ethan}# Level 2 - 检查点停止跑完当前步骤就停curl-XPOST http://localhost:8765/control/checkpoint-stop# Level 3 - 状态快照后立即停止curl-XPOST http://localhost:8765/control/freeze\-d{save_context: true}# 恢复Level 1/2/3 均通用curl-XPOST http://localhost:8765/control/resume\-d{operator: ethan}四个端点核心是这样一张状态机RUNNING ──pause──→ PAUSING ──task_done──→ PAUSED │ │ ├──freeze──→ FROZEN resume│ │ │ └──terminate──→ TERMINATED RUNNING ←─┘这套协议的关键不是技术细节而是让暂停成为 Agent 内置行为而不是外部强杀。Agent 代码里每完成一个步骤都会检查一次控制状态——如果是PAUSING下一个任务不取了如果是FROZEN立刻保存状态并退出。有了这个机制之后我们遭遇的 2 次异常都在 2 分钟内完成了接管没有额外的状态损坏。运维债 4记忆污染——长跑 Agent 的 context 会越跑越脏这是最难察觉的一笔债。当 Agent 第一次跑的时候它的上下文是干净的——只有任务指令和当次的工具返回结果。但随着任务轮次累积很多团队的 Agent 会把历史任务的摘要追加进 context为了让它记住之前做了什么。这件事在短期内看起来很有用——Agent 知道今天已经发了 3 篇文章不会重复发。但有一个问题几乎没有人在设计阶段想到追加进去的历史摘要本身可能是错的。如果某次任务失败了失败的部分结果也可能被摘要进了 context。下一次任务开始时Agent 的起点认知就带着上次的错误残留。错误会随着轮次传播直到某次表现异常才被人发现——而此时追溯原因已经很困难了因为 context 已经被多轮任务的摘要叠加了好几层。我们叫它记忆污染Memory Poisoning。解法不是不用 context 记忆而是分清两种信息# memory_strategy.py# 类型 A事实性状态不会过期、不会出错# 存数据库Agent 每次启动时查询不放进 contextFACTUAL_STATE{published_articles_today:SELECT COUNT(*) FROM audit WHERE actionpublish AND datetoday(),pending_tasks:SELECT COUNT(*) FROM task_queue WHERE statuspending,last_run_at:SELECT MAX(completed_at) FROM task_queue WHERE statuscompleted}# 类型 B上下文理解当前任务内有效跨任务无效# 只放进单次任务的 context任务完成后丢弃EPHEMERAL_CONTEXT[当前任务的用户意图,这次调用的中间结果,临时的推理过程]# 错误的做法把 B 类信息持久化到下次任务defwrong_approach():summaryagent.summarize_last_run()# 包含了错误的中间状态next_contextf上次运行摘要{summary}\n\n{new_task}# 错误传播了# 正确的做法用数据库存 A 类B 类每轮清空defright_approach(new_task):# 每次启动时从数据库查询干净的事实状态statedb.query_state(FACTUAL_STATE)fresh_contextf 当前事实状态来自数据库不是上轮摘要 - 今日已发布{state[published_articles_today]}篇 - 待处理任务{state[pending_tasks]}个 当前任务{new_task}returnfresh_context这个改动之后我们的 Agent 在连续运行 48 小时后性能没有出现之前观察到的越跑越奇怪现象。不是因为 Agent 变聪明了而是因为它每次启动时拿到的是干净的事实而不是上次的推理残留。运维债 5成本归因——不知道钱花在哪就不知道该砍哪里我们用 Agent 跑了 7 天之后算了一下 token 账单比预期高了 2.3 倍。高在哪里这才是问题所在——我们说不清楚。Agent 每天跑很多任务每个任务都消耗 token但我们没有按任务类型聚合的账单。结果是一笔大数字没法决策是某类任务本来就贵还是某个步骤在无效循环是 context 带得太多还是工具调用太频繁补上成本归因只需要一个中间件层# token_tracker.pyimporttimefromdataclassesimportdataclass,fieldfromtypingimportOptionalimportsqlite3dataclassclassTokenRecord:task_id:strtask_type:str# write_draft, publish_check, research, etc.model:strinput_tokens:intoutput_tokens:intcost_usd:floatstep:str# 任务内的步骤名timestamp:floatfield(default_factorytime.time)# 按任务类型的成本汇总一周数据COST_SUMMARY_QUERY SELECT task_type, COUNT(*) as task_count, SUM(input_tokens) as total_input, SUM(output_tokens) as total_output, ROUND(SUM(cost_usd), 4) as total_cost_usd, ROUND(AVG(cost_usd), 4) as avg_cost_per_task FROM token_records WHERE timestamp strftime(%s, now, -7 days) GROUP BY task_type ORDER BY total_cost_usd DESC; 我们跑了这个查询之后看到的结果任务类型任务次数7天总成本USD单次均价占比research_topic34$4.21$0.12438%write_draft19$3.87$0.20435%publish_check89$1.44$0.01613%format_review56$0.98$0.0189%context_summary203$0.56$0.0035%context_summary单次很便宜但运行了 203 次——比所有其他任务加起来都多。往下钻一层才发现有一段逻辑在每次工具调用前都会重新 summarize 一次全量 context触发了 203 次 summary 任务。这个无效调用不改代码根本发现不了因为从日志里看它正常运行。修掉这个之后下一周账单降了 31%。这就是成本归因的价值——不是让你削减 Agent 能力而是让你找到那些功能上多余、费用上昂贵的调用。5 笔债的优先级和实施顺序不是所有团队都要同时补这 5 项按影响和成本排一下债务不补的最坏后果实施成本建议优先级权限矩阵Agent 误触不可逆操作删除/外发低2小时填表P0失败分类重试产生副作用被平台标记异常中1天代码P0人工接管异常时只能强杀状态损坏中1天代码P1记忆清洗长跑后 Agent 行为越来越不可预测中半天重构P1成本归因账单说不清楚优化无法决策低几小时加日志P2权限矩阵是最快能做的——不需要写代码只需要团队坐下来填一张表达成共识。但恰恰是这种不需要代码的事在工程师团队里最容易被推迟。有个观察可能反直觉权限矩阵的主要价值不是防止 Agent 乱来而是防止团队里的人对 “Agent 能做什么” 各执一词。出了事之后你会发现5 个人里可能有 3 种不同的理解——有人以为 Agent 不能改配置有人以为只要有 API Key 就可以改。矩阵的价值是消除这种信息差。我们在第 7 天发现的真正问题7 天的运维经历之后我觉得把所有问题归结为技术债有点过度简化了。真正的问题是这样的团队接入 Agent 的速度比团队建立“对 Agent 的共同认知”的速度快了一个数量级。每个人都知道我们有个 Agent 在跑但没有人能清楚说出它今天做了什么→ 需要审计日志摘要它有什么权力→ 需要权限矩阵出问题了谁负责→ 需要明确值班角色长期跑下去成本怎么算→ 需要成本归因这些不是工程问题是团队信息对齐问题。工程机制代码、协议、日志是手段目标是让团队里每个人对 Agent 的边界有一致的认知。可复制的治理清单把上面 5 项整理成团队可以直接执行的检查列表## AI Agent 治理清单 v1openclaw-lab 验证 ### 上线前必须 - [ ] 权限矩阵填写完成所有不可逆操作标注 ❌ 或 需审批 - [ ] 失败分类逻辑实现区分 transient / structural / side_effect - [ ] 人工接管端点存在至少支持 pause 和 resume - [ ] 幂等 key 设计发布/外发类操作必须有去重机制 ### 上线后 7 天内建议 - [ ] context 记忆策略审查事实性状态从数据库读不从上轮摘要读 - [ ] 成本归因上线按任务类型拆分 token 消耗 - [ ] 审计日志接入记录所有产生外部副作用的操作 ### 持续运营每周 - [ ] 有人每天花 5 分钟看 Agent 审计摘要 - [ ] 每周一次成本查询识别异常涨幅 - [ ] 每两周回顾一次权限矩阵是否有权限需要收紧这张清单不完整也不需要完整——它的目标是让团队在 7 天内建立最基础的可见性而不是一步到位搭建完整的 Agent 治理平台。常见问题Q权限矩阵应该多细粒度操作级还是接口级A从操作语义级别开始不要从接口级开始。“发布文章比调用 /api/v1/posts POST” 更容易达成共识——前者人人能理解后者只有写代码的人看得懂。接口级权限是实现细节矩阵是团队共识文档两个层面都要有但不要混在一张表里。Q记忆污染多久之后会开始明显影响输出质量A根据我们的观察context 带有历史摘要的 Agent在第 5-7 个任务周期之后开始出现可察觉的偏差比如推断任务状态时更依赖上次说过的话而非工具实时返回的结果。这个拐点因 context 窗口大小、摘要质量和任务类型不同而不同。没有通用数字但三天不出问题不代表没问题——改变通常是渐进的要主动测不能等到明显异常才排查。Q成本归因是实时的还是离线的A先做离线的。用 SQLite 存每次 LLM 调用的 token 数和模型名每天跑一次汇总查询。不需要实时 dashboard——对大多数团队来说离线日报的信息密度已经够做决策了。等到你识别出需要实时告警的指标比如某类任务成本突然涨了 50%再上实时监控。从离线查询开始工程成本低验证了价值再投入。