我给 AI Agent 配了一套能自动清理数据的工具,本想让它帮我打理琐事,结果有天它理解偏了,自主地把一批不该删的生产数据给删了,而整个过程没有任何一个环节需要我点头确认的深度复盘

我搭了一个 AI Agent 帮我打理后台杂务,配了查数据、整理数据、还有一个清理过期无用数据的删除工具,设想把规则明确的清理活儿交给它、我腾出手干别的。它一开始表现很好,我越来越放心、把缰绳放得越来越松。直到那天我发现一批本不该删的生产数据凭空消失了,查日志后背发凉:是 Agent 自己干的。它执行清理任务时对什么算无用数据理解出了偏差(我的指令有歧义、它也对边界情况判断错了),于是自主判定该删、自主调用删除工具、自主执行删除,从判断到动手一气呵成,没有任何一个环节停下来问我一句这批数据真要删吗,等我发现数据已经没了、不可逆。复盘才懂问题的根子不在 Agent 判断错(任何会自主决策的东西包括人都会判断错),而在我让一个可能出错的判断毫无阻拦地直接驱动了一个不可逆的高破坏力动作,中间没有人工确认、试运行、阈值审批、软删除任何一道闸门。自主性该由动作的风险和可逆性决定。这篇复盘从故障现场讲到为何自主 Agent 必须有人在回路、用风险可逆性决定自主度、怎么诊断,再到工具按风险分级、高风险默认 dry-run、阈值触发人审、操作可逆化、权限最小化的完整正解,以及自动对外发消息、涉及金钱、建议变执行、批量没爆炸半径等坑,和判断必然会错、危险在于出错判断直接驱动不可逆动作、安全的真谛是在判断与不可逆执行间留一道可叫停闸门的认知。

我给 AI Agent 配了一套能自动清理数据的工具,本想让它帮我打理琐事,结果有天它"理解偏了",自主地把一批不该删的生产数据给删了,而整个过程没有任何一个环节需要我点头确认的深度复盘

这是一次让我对"能自主决策的系统,该不该让它自主到底"有了刻骨认知、也心有余悸的事故。我搭了一个 AI Agent 帮我打理后台杂务,给它配了一套工具:查数据、整理数据、还有一个"清理过期/无用数据"的删除工具。我的设想很美好:把那些琐碎的、规则明确的清理活儿交给它,我就能腾出手干别的。一开始它表现得很好,我也越来越放心,把缰绳放得越来越松。

直到那天,我发现一批本不该被删的生产数据,凭空消失了。查操作日志,真相让我后背发凉:是那个 Agent 自己干的。它在执行一轮"清理"任务时,对"什么算无用数据"的理解出了偏差(可能是我的指令有歧义、也可能是它对边界情况判断错了),于是它自主地判定那批数据"该删",然后自主地调用了删除工具,自主地执行了删除——从判断到动手,整个过程一气呵成,没有任何一个环节停下来问过我一句"这批数据真的要删吗?"。等我发现,数据已经没了,而且是不可逆的。我给了它"动手删"的能力,却没给自己留一个"喊停"的机会。

故障现场:从"判断"到"不可逆地执行",中间没有任何闸门

我把 Agent 当时的决策和执行链路还原出来,问题一目了然:

Agent 这一轮任务的完整链路:

  1. 我下指令: "清理一下那些无用的旧数据"  (指令本身就有歧义)
  2. Agent 理解: 把"无用"理解成了"超过 N 天未访问"
     —— 但有一批长期不访问、却极其重要的归档数据, 也落进了这个范围
  3. Agent 决策: 判定这批数据"符合清理条件, 应删除"  (判断错了)
  4. Agent 调用工具: deleteData(ids=[...一大批...])
  5. 工具执行: 直接物理删除, 不可逆  ✗✗✗
  6. 完成, Agent 报告"已清理 N 条无用数据"  (我事后才看到)

整条链路里【缺失】的东西:
  - ✗ 第3步后, 没有"高风险操作需人工确认"的关卡
  - ✗ deleteData 工具不区分"试运行(dry-run)"和"真删"
  - ✗ 没有"删多少条以上 / 涉及重要数据时, 必须人审"的阈值
  - ✗ 删除是物理删、不可逆, 没有"软删除 + 回收站"的缓冲
  - ✗ Agent 的权限没分级: 它能调"读", 也能直接调"不可逆的删"

结果: 一个【可能出错的自主判断】, 直接驱动了一个【不可逆的高风险动作】,
      中间没有任何"人"或"机制"来拦一下。

看着这条链路我冷汗直冒:问题的根子,不在于 Agent "判断错了"——任何会自主决策的东西(包括人)都可能判断错;问题在于,我让一个可能出错的判断,毫无阻拦地、直接地驱动了一个不可逆的、高破坏力的动作在这条链路上,Agent 的""和""之间,没有任何一道闸门:没有人工确认、没有试运行、没有阈值审批、没有软删除缓冲。我默认了"它会判断对",于是把"判断"和"执行不可逆操作"的权力,一并、无条件地交给了它。我给 Agent 装上了油门,却忘了给它(和我自己)装一个刹车。

第一件事:搞懂关键——自主性越强、动作越不可逆,越需要"人在回路"的闸门

惊魂未定之后,我去认真补了"AI Agent 安全与人在回路(Human-in-the-Loop, HITL)"这一课,才明白我设计时缺了最关键的一环:

【为什么自主 Agent 必须有"人在回路"的闸门】

核心矛盾:
  - Agent 的价值在于"自主"——能自己判断、自己执行, 省去人力
  - 但 Agent 的判断【一定会出错】(指令歧义、理解偏差、边界误判、幻觉)
  - 当"可能出错的自主判断"直接驱动"高风险/不可逆动作"时,
    一次判断错 = 一次无法挽回的事故

关键维度: 用"动作的风险/可逆性"决定"需要多少人的参与"
  低风险 + 可逆(读数据、生成草稿)     → Agent 全自主, 出错也好恢复
  高风险 / 不可逆(删数据、发钱、对外发消息、改生产配置)
                                      → 必须有人在回路: 执行前人工确认

人在回路(HITL)的几种形态:
  - 审批关卡: 高风险动作执行前, 暂停, 等人确认/否决
  - 试运行(dry-run): 先输出"我打算做什么", 让人/系统核对, 再真做
  - 阈值触发: 影响面超过阈值(删>N条、金额>X)才需人审, 小操作放行
  - 权限分级: Agent 默认只有低风险权限, 高风险工具需提权/人工解锁

核心认知:
  自主性是用来【提效】的, 不是用来【免责】的。
  动作越不可逆、破坏力越大, 就越不能让"自主判断"独自拍板,
  必须在"判断"和"不可逆执行"之间, 插入一道"人(或可靠机制)能否决"的闸门。

这一下点醒了我:我把"让 Agent 自主"理解成了"让 Agent 全程独自做主、包括按下那个不可逆的按钮"。可自主性的恰当边界,应该由动作的风险和可逆性来划定:读数据、写草稿这类可逆的低风险活儿,放手让它干;而删数据、发钱、对外发消息这类不可逆、高破坏力的动作,就绝不能让一个"可能出错的判断"独自拍板——必须在它"想做"和"真做"之间,插一道"人能看一眼、能喊停"的闸门。我享受了它自主带来的省心,却没为它自主可能犯的错,留一条退路。

第二件事:正解——按"风险/可逆性"分级,给高风险动作装上人审闸门

找到根因,正解就成体系了:按动作的风险和可逆性给工具分级——低风险可逆的让 Agent 全自主;高风险/不可逆的,必须经过人工确认关卡(HITL),并配上试运行、阈值审批、软删除缓冲。让"不可逆的执行"永远不被一个"可能出错的判断"独自触发。

# 错误做法: 工具不分级, Agent 判断完直接调用不可逆的删除
def delete_data(ids):
    db.physical_delete(ids)   # 直接物理删, 不可逆, 没人拦

# 正解: 高风险动作 = 试运行预览 + 人工确认关卡 + 阈值 + 软删除
def delete_data(ids, confirmed_by=None, dry_run=True):
    affected = db.preview(ids)            # 先算清楚"会删哪些"

    # 1) 试运行: 默认只预览, 不真删, 把"打算做什么"摊开给人看
    if dry_run:
        return {"action": "preview", "will_delete": affected,
                "count": len(affected), "needs_confirm": True}

    # 2) 阈值 / 重要性触发人审: 量大或涉及重要数据, 必须人点头
    if len(affected) > DANGER_THRESHOLD or any(a.is_critical for a in affected):
        if confirmed_by is None:
            raise NeedHumanApproval(
                f"将删除 {len(affected)} 条(含重要数据), 需人工确认")

    # 3) 软删除 + 回收站: 给"删错了"留一条后悔路, 而非物理抹掉
    db.soft_delete(ids, operator=confirmed_by, recoverable_days=30)
    return {"action": "soft_deleted", "count": len(affected),
            "recoverable_until": "+30d"}

# Agent 的流程变成: 先 dry_run 预览 → 把预览交给人确认 → 人点头后才带 confirmed_by 真删

这套设计的精髓,是在"Agent 的判断"和"不可逆的后果"之间,插入了好几道可以叫停的闸门:试运行让 Agent 先"说清楚要干什么"而不是直接干;阈值审批让真正危险的操作(量大、涉及重要数据)必须有人点头;软删除给"万一删错"留了 30 天的后悔药。Agent 依然自主地"判断和发起",但"不可逆地落地"这最后一步,被牢牢攥在了人(或可靠规则)手里。

【给自主 Agent 配工具的几条安全实践】

1. 工具按风险分级:
   - 读/查/草稿 → 低风险, 全自主
   - 删/发钱/对外发消息/改生产 → 高风险, 必须人审

2. 高风险动作默认 dry-run: 先产出"我打算做X、影响这些", 经确认再执行

3. 阈值触发人审: 影响面/金额超阈值才拦, 小事放行, 别让人审拖垮效率

4. 操作可逆化: 能软删除就别物理删、能撤回就留撤回、危险操作留审计与回滚

5. 权限最小化: Agent 默认只给完成任务必需的最小权限, 高风险工具单独提权

6. 全程审计: 谁(哪个Agent)在何时、基于什么判断、做了什么, 都要可追溯

第三件事:其他"把高风险动作交给自主判断"的同类坑

顺着"不可逆动作要有人审闸门"这条线,我把系统里同类的隐患都排查了一遍,它们都在我"图省事、放任自主"的地方埋着:

第一个,Agent 自动对外发消息/邮件。让 Agent 自主给客户发通知,它一旦理解偏了、措辞错了,发出去就收不回,影响的是真实的人。对外沟通这类不可逆动作,该有审阅或模板约束。

第二个,Agent 自主执行涉及金钱的操作。退款、下单、调价——金额类操作天然高风险,必须有金额阈值人审、且全程审计,绝不能让一次判断失误直接变成真金白银的损失。

第三个,把"建议"悄悄变成了"执行"。Agent 本该只给建议、由人决策,结果为了"更智能"给它接上了执行能力,它就从"参谋"变成了"擅自行动的指挥官"。建议权和执行权要分清。

第四个,批量操作没有"爆炸半径"限制。一个操作哪怕单次低风险,Agent 批量循环执行时也会放大成大事故。要对单次任务的影响面(条数、范围)设硬上限。

第四件事:按"风险 × 可逆性"决定 Agent 该有多自主

我把"什么样的动作该给 Agent 多大自主权"整理成一张表,这是我现在给 Agent 配任何工具时都会先对照的:

动作类型 风险/可逆性 该给的自主度 必备闸门
查询、读取、检索 低 / 可逆 全自主 无(审计即可)
生成草稿、建议、报告 低 / 可逆 全自主 人决定是否采纳
修改可恢复的数据 中 / 可逆 自主 + 软删/版本 可回滚
删除数据、改生产配置 高 / 不可逆 需人审 dry-run+确认+软删
对外发消息/邮件 高 / 不可逆 需人审 审阅+模板约束
涉及金钱(退款/下单) 极高 / 不可逆 强制人审 阈值+确认+审计

这张表让我看清:"Agent 该不该自主"不是一个非黑即白的开关,而是一根随动作风险和可逆性滑动的标尺——越是可逆的低风险动作越该放手,越是不可逆的高风险动作越要把最后一步攥在人手里。我之前的错,是给所有动作都开了"全自主"这一档,包括最不该的那个删除。

第五件事:我对"让 Agent 自主"的几个想当然

这次事故,本质是我对"自主 Agent"抱了一堆危险的想当然。把它们列出来,每一条都是心有余悸的教训:

我曾经的想当然 事故教我的真相
"让它自主,就是让它全程独自做主" 自主度该按动作风险分级,不可逆动作要留人审
"它一直表现很好,可以完全放手了" 表现好≠不会出错;一次错+不可逆动作=大事故
"给它能力是为了省心、不用我管" 自主是用来提效的,不是用来免除该有的把关
"它判断错的概率很低,不至于" 低概率×不可逆×高破坏=不可接受;要按最坏情况设防
"删除工具能用就行,没必要搞 dry-run" 高风险工具必须先预览、可回滚,给犯错留退路
"加人审关卡会拖慢自动化,得不偿失" 只在高风险/超阈值时拦,小事放行,安全与效率可兼得

第六件事:给 Agent 配工具、放开自主权前,我现在的自检习惯

现在每当我要给一个 Agent 配工具、或放开它的自主权,我都会先按这张图问自己:

这张图的精髓,是"先问这个动作可不可逆、风险高不高,再决定给多少自主权;不可逆的高风险动作,永远在判断和执行之间留一道人能喊停的闸门"设计就按风险/可逆性给工具分级、高风险配 dry-run+人审+软删除、排查就找哪个不可逆动作被一个可能出错的判断独自驱动了这套习惯,让我从"给能力就全放开、图省心"变成了"自主度随风险滑动、把不可逆的最后一步攥在人手里"——核心始终是:Agent 的自主判断一定会出错(指令歧义/理解偏差/边界误判/幻觉);当可能出错的自主判断直接驱动不可逆的高风险动作时,一次判断错就是一次无法挽回的事故;正解是按动作风险/可逆性分级,低风险可逆全自主、高风险不可逆必须经 dry-run 预览+人工确认(HITL)+阈值审批+软删除可回滚,让不可逆的执行永不被一个可能出错的判断独自触发。

我立下的几条规矩

这场"Agent 自主删了不该删的数据"的事故,换来了我做自主系统时,刻进骨子里的几条铁律:

  1. 任何会自主决策的东西(包括 Agent、也包括人)都一定会判断错;设计必须假设它会错。
  2. 真正的危险,是让一个"可能出错的判断"直接驱动一个"不可逆的高破坏力动作",中间没有闸门。
  3. 自主度该由动作的风险和可逆性决定:可逆低风险放手,不可逆高风险必须留人审。
  4. 高风险动作默认 dry-run:先输出"我打算做什么、影响哪些",经确认再执行。
  5. 用阈值触发人审——影响面/金额超阈值才拦,小操作放行,安全与效率可兼得。
  6. 能可逆就别不可逆:软删除代替物理删、留撤回、留审计与回滚,给"犯错"留退路。
  7. 自主性是用来提效的,不是用来免除把关的;越不可逆,越要把最后一步攥在人手里。

附:我现在给高风险工具统一套的"人审包装器"

这是我现在给任何高风险工具固定套的"人审包装器"——把一个能直接动手的危险工具,包装成"必须先预览、必须有人点头、出错能回滚"的受控工具,让 Agent 拿到的永远是被关卡保护过的版本:

def require_human_approval(tool_fn, *, threshold, is_reversible):
    """ 把一个高风险工具包装成: 预览 → 人审 → 执行(尽量可逆) """
    def guarded(*args, dry_run=True, approval=None, **kwargs):
        # 1) 先算影响面, 永远先给"打算做什么"的预览
        impact = tool_fn(*args, **kwargs, preview=True)

        # 2) 默认 dry-run: 不真做, 只把计划交出去给人/上层核对
        if dry_run:
            return {"preview": impact, "need_approval": impact.count > threshold}

        # 3) 超阈值必须有人点头, 否则坚决拒绝执行
        if impact.count > threshold and approval is None:
            raise NeedHumanApproval(f"影响 {impact.count} 项, 超阈值 {threshold}, 需人工确认")

        # 4) 不可逆动作强制留后路: 没有回滚能力就不让执行
        if not is_reversible:
            raise UnsafeOperation("该动作不可逆且未提供回滚, 拒绝裸执行")

        return tool_fn(*args, **kwargs, preview=False, operator=approval)
    return guarded

# 注册给 Agent 的, 永远是包装后的受控版本, 而非原始的危险工具
safe_delete = require_human_approval(raw_delete, threshold=10, is_reversible=True)
agent.register_tool(safe_delete)   # Agent 再也拿不到那个能裸删的原始工具

这个包装器把我这次的教训钉死在了工具层:Agent 永远拿不到能"裸执行"的原始危险工具,它能调到的,只有被"预览 + 阈值人审 + 可逆性检查"层层包裹过的受控版本。这样一来,安全就不再依赖"我记得给这个调用加确认"这种自觉,而是被结构性地焊死在了工具的注册环节——危险的能力从源头就被关进了笼子,Agent 再怎么"理解偏了",也按不到那个不可逆的裸删按钮了。

写在最后

回头看,这场由"没有人在回路、不可逆动作交给自主判断"引发的"Agent 误删生产数据"事故,真正教给我的,远不止"加 dry-run、配人审关卡"这一个技巧。它让我对"把'判断'和'不可逆地执行判断'这两件事捆在一起、交给同一个'会出错的主体'独自完成, 是一件极其危险的事; 因为判断难免出错, 而当出错的判断能立刻、不受阻拦地变成无法挽回的现实时, 就再没有任何机会去发现错误、纠正错误了",有了一次刻骨的体会。我栽跟头,是因为我把'赋予能力'和'放弃监督'当成了一回事——我以为'让它能自主地做', 就等于'让它独自地、从判断到不可逆执行一气呵成地做';我没意识到, '有能力做某事'和'有权不经任何确认就把某个不可逆的事做了', 是两个完全不同的授权; 我本该只给前者、对高风险的后者留一道关卡, 却把两者一并、无条件地交了出去;于是当它的判断出错时, 这个错误没有经过任何一双眼睛、任何一道闸门, 就直接变成了删库的既成事实这让我领悟到一个关于"权力、判断与不可逆后果"的深刻认知:任何'判断'都可能出错, 这是无法消除的; 因此真正的安全, 不在于'追求判断永不出错'(做不到), 而在于'不让出错的判断, 直接、不可逆地酿成无法挽回的后果'——即在'判断'与'不可逆的执行'之间, 永远保留一道能够发现错误、叫停错误的'缓冲与复核';动作越不可逆、破坏力越大, 这道缓冲就越不可省略;把'能做'的能力, 和'不经复核就把不可逆的事做了'的权力, 严格区分开——前者可以慷慨赋予, 后者必须慎之又慎、层层设防这给了我一种看待"一切'授予某个主体(无论是 AI、系统还是人)自主权'之事"时的清醒:每当我要赋予一个主体某种能力、放开它的自主权时,要追问"它的判断如果错了, 会造成什么后果?这个后果可逆吗?如果不可逆, 我有没有在它'判断'和'不可逆执行'之间, 留一道能发现并叫停错误的关卡?"——对可逆的、好恢复的, 大胆放权以提效; 对不可逆的、高破坏的, 务必在执行前留一道人(或可靠机制)能够否决的闸门;"区分能力与不受监督的执行权、在判断与不可逆后果之间永远保留一道可叫停的缓冲",是用好一切自主系统、也是对一切不可逆之事负责的关键认清判断必然会错、危险在于出错判断直接驱动不可逆动作、安全的真谛是在判断与不可逆执行间留一道可叫停的闸门——这,是我用一次 Agent 误删数据的惊魂事故,换来的、关于 AI Agent、也关于如何看待自主权与不可逆后果的、最朴素也最深刻的领悟。如果这篇复盘,能让你下次给一个 Agent 接上某个"能动手"的能力时,先想想"它要是判断错了,这一下收得回来吗?收不回,我拦得住吗?",并给不可逆的动作装上那道人能喊停的闸门,那我对着那批"被自主删掉、再也找不回"的数据所受的惊吓,就值了。

—— 别看了 · 2026
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理 邮箱1846861578@qq.com。
技术教程

我的 TypeScript 代码里一个声明为 Dog 数组的变量,运行时遍历它时竟拿到了一个根本没有 bark 方法的对象、直接报错,排查半天发现是我把它当 Animal 数组传给了一个函数、那函数往里塞了别的动物的深度复盘

2026-6-3 3:51:46

技术教程

我在 Python 的循环里批量造了一串函数,每个本该记住自己那一轮的编号,结果调用时它们却异口同声地全返回了最后一个数,排查半天发现闭包记住的是变量本身、而不是当时那个值的深度复盘

2026-6-3 4:01:24

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索