ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

技术思考问题16:AI智能体模型Meta-Muse-Wonder越聪明,刹车应该谁来踩?

技术思考问题16:AI智能体模型Meta-Muse-Wonder越聪明,刹车应该谁来踩?

能力圈与反演:智能体权限不能交给模型自己判断

记忆给的是意图证据,不是授权证据,一字之差就是一次越权。

[读者] 正在做企业级 Agent 平台的后端与架构同学 [痛点] 记忆、主动执行都做出来了,权限却还靠提示词约束 [现在读] 越权动作已经能一路走到退款与退票 [读完] 能自己设计一套工具授权分级 + 服务端兜底的最小防线
[旧方案] 一个大工具接口 + 提示词里写一句"高风险操作请确认" | v [新需求] Agent 具备跨会话记忆与遇阻重试,会自己连续调用工具 | v [冲突] 记忆只证明"用户想要的",不证明"用户此刻授权了" | v [后果] 一次重试就是一次重复扣款,一次跨租户记忆命中就是一次数据外泄

我是老李,在一家做出行与企业服务的公司带基础架构组。去年我们在内部做了一套企业级智能体平台,给客服和运营用:能查订单、能改签、能退票、能发起退款。

平台的 V1 其实很好用。模型接上跨会话记忆之后,记得住每个客户的常旅客号、常用舱位、默认支付方式;接上遇阻重试之后,工具调用超时能自己重试,参数报错能自己换一种写法再试一次。运营同学的反馈是:终于不用一步一步点按钮了。

变化出在今年三月。我们把它接进了真正的交易链路,不再只是查询和建议,而是可以提交退票与退款。任务是明确的:让 Agent 在用户授权的前提下,完成改签、退票、退款这三类写操作。

但冲突也很明确。模型的记忆和专注,让它越来越像一个"知道该怎么做"的执行者;而交易链路需要的是一个"知道你有没有资格这么做"的守门人。在 V1 里,这两件事就是同一个东西。

你有没有想过:一个记得住你所有偏好的助手,和一个有权动你账户的助手,到底是不是同一个角色?

如果系统只在提示词里写一句"高风险操作请确认",那谁来确认、确认什么、确认之后留下什么证据?

要是这一步选错了,代价不是一次回答错误,而是一笔已经落地的钱,和一条事后说不清的审计记录。

01、故事

这个平台最初的工具层只有一个统一入口,看起来干净得让人放心:

defcall_tool(name:str,args:dict,user_ctx:dict):returnTOOLS[name](**args)

它把"读订单"和"退订单"放在同一个优先级上。模型只要在对话里表达出"用户要退票",就会直接调用。

第一次出事是在灰度第二天。一位运营同学在测试环境用客服账号,让 Agent 帮客户"看看明天那张去上海的票能不能退"。Agent 读了订单,跨会话记忆记下了这位客户的常用线路,遇阻重试模块发现退票接口返回 400(其实是测试环境参数问题),于是它换了三种参数组合,重试了五次。

第五次,接口通了。票退了。

那位客户后来在工单里问:我没说过要退票,为什么给我退了?

这类问题的技术本质是:模型把"记得"当成了"被授权"。

芒格的能力圈告诉我们:只在明确可判断、可承担后果的范围内行动。这里的"可判断",在系统里不是模型觉得可判断,而是系统能验证它可判断。反演思维则更进一步:不要问"Agent 会不会误操作",而是先假定误操作已经发生、钱已经出去了,然后再倒推,在这条路径上我们本可以设置哪几道闸门。

记忆证其意
授权另开证
一处不设闸
全链皆可越

02、问题

先看旧方案为什么顶不住。

V1 的风控其实是三个东西拼起来的:提示词里的一句"高风险操作请确认"、工具函数里各自写的 if 判断、以及事后的人工抽查。这三样在只读场景下够用,一旦出现写操作就同时失效。

失效点有三个。

第一,提示词是模型的输入,不是系统的约束。长上下文里的一句"忽略上面的限制",或者一次检索里带进来的注入文本,都能改写它。约束写在可以被输入改写的地方,等于没有约束。

第二,遇阻重试的重试语义和写操作的幂等语义绑在一起。模型每次重试都会重新生成一次调用参数,如果服务端只看参数不看身份,重试次数就直接等于副作用次数。

第三,跨会话记忆的记忆键空间没有租户维度。记忆是按 user_id 存的,而 user_id 在不同租户下并不唯一。

业务影响很直接:误退票、误退款属于资金与合规风险;跨租户记忆命中属于数据泄露;更麻烦的是事后追问谁授权了这笔操作时,审计日志里答不上来。

技术表现上,审计日志只有一条tool_call,缺少三个关键字段:actor(谁发起的)、grant(凭什么允许)、consent(用户在哪一轮同意的)。

所以完成标准必须可验证,而且不能从模型输出里数:

  • 未授权变更次数等于 0,计数口径是审计轨迹里的拒绝记录,不是模型的自我报告。
  • 撤销生效时间:撤销一条记忆或一份授权之后,下一次同类调用必须被拒绝,且链路内没有旧值缓存。
  • 任务成功率:在满足前两条的前提下不能显著下降,否则说明拦得太死。
  • 人工接管率:高风险动作进入人工确认的比例,可按工具等级调节。

旧策寄一文
新需落千金
先立可验标
再论巧与速

03、原理

本篇只讲两个模型,加两条反直觉判断。

能力圈(Circle of Competence)。能力圈不是"模型能做什么",而是"系统能判断、也能承担后果的那部分"。一个动作要进入能力圈,需要同时满足三条:系统能独立判断这个动作该不该发生,且这个判断不依赖模型的自我声明;系统能承担错误后果,也就是有确认、有上限、有补偿;系统能留下可追溯的证据。三条缺一条,这个动作就在圈外。

反演思维(Inversion)。不要问"怎么让 Agent 更聪明",而是先假定事故已经发生,再倒推防线。把"一笔未授权的退票成功提交"当作起点,倒推出来的东西很具体:退票工具必须有独立的授权凭证,于是有了服务端二次确认;重试不能重复提交,于是有了幂等键;记忆不能跨租户,于是有了租户前缀;出错必须能回滚,于是有了补偿流程。

反直觉判断:记忆越准确、专注越持久,越权风险越高,而不是越低。

原因是记忆和专注解决的是"怎么把一件事做成",它们提升的是执行力和持续性;而权限解决的是"该不该做这件事",它需要的是外部证据。一个记不住上下文的 Agent 大概率做不成越权的事,因为它连订单号都拼不齐;而一个记得住、还不达目的不罢休的 Agent,恰恰是最有能力把越权一路做到底的那个。

反直觉判断:基准分数和权限安全不在同一个坐标系里。

引用材料中提到,Meta 的 Muse Wonder 系列在 Intelligence Index v4.3 上有明显分数提升,并声称在智能体、记忆与编码基准上达到 SOTA(涉及具体分数、排名与能力表述,需按当前官方文档核验)。但这里要说清楚的是:这些指标衡量的是模型在给定任务上"能不能做对",权限安全衡量的是系统在模型做错、或者被诱导做错时"能不能拦住"。前者是能力,后者是约束;能力提升不能代替约束,两者只能分别建设。排行榜上涨一名,不会让任何一条未授权变更自动消失。

圈内方可行
圈外先设闸
能强非权限
反向推防线

04、架构

[输入] 用户自然语言 + 会话记忆 + 租户上下文 | v [模块] 意图层(模型):只产出 Action Proposal,不直接执行 | v [数据/状态] 授权层:工具授权等级 + 租户策略 + 授权凭证 + 幂等键 | v [处理] 执行层:服务端校验 -> 二次确认 -> 幂等执行 -> 写审计 | v [输出] 操作结果 + 审计轨迹 + 可补偿状态

四层里,意图层是唯一允许"聪明"的地方,它只负责把自然语言翻译成一个结构化的 Action Proposal。授权层不接受模型对自身权限的任何声明,只认两样东西:工具在注册表里的等级,和调用方带来的授权凭证。

工具授权等级按副作用定义,而不是按名字定义:

  • L0 读取:查询订单、查余额、查政策。可以直接放行,但必须带租户过滤。
  • L1 建议:生成退款方案、改签建议。只产出文本,不落库、不调外部写接口。
  • L2 变更:改签、修改收货地址、变更乘机人。需要用户在本轮明确确认,并由服务端校验业务状态机是否允许。
  • L3 资金与销毁:退票、退款、扣款、删除。服务端强校验 + 二次确认 + 幂等键 + 审计轨迹 + 可补偿流程。

边界很清楚:意图层永远不能越过授权层。哪怕模型置信度极高、哪怕记忆里写着"这位客户从来只坐经济舱",没有凭证就走不动。收益是误操作从"可以在模型层被诱发"变成"必须在服务端通过校验";代价是链路变长、需要维护状态机与幂等表、用户体验上多一次确认。适用条件也很清楚:只要模型能发起写操作,就必须启用;如果整条链路确实只有只读查询,那么 L0 与 L1 就够了,不必引入 L2、L3 的复杂度。

分级定风险
分层为兜底
意图不过界
凭证方通行

05、实战一次

先做一个最小但完整的 V1:一个只做一件事的授权网关——没有服务端签发的授权凭证,资金级动作一律走不动。环境用 Python 3.11 与 FastAPI(具体版本以当前官方文档为准),依赖fastapi、uvicorn、pydantic。

核心实现:

# gateway.pyfromenumimportIntEnumfromtypingimportAny,CallableimporthashlibimportjsonimporttimefromfastapiimportFastAPI,HTTPExceptionfrompydanticimportBaseModelclassLevel(IntEnum):READ=0SUGGEST=1MUTATE=2PAYMENT=3REGISTRY:dict[str,dict]={}deftool(name:str,level:Level):defdeco(fn:Callable[...,Any]):REGISTRY[name]={"level":level,"handler":fn}returnfnreturndeco# 风险等级由服务端在注册时固定,模型无权修改@tool("query_order",Level.READ)defquery_order(tenant_id:str,order_id:str,**kw):return{"order_id":order_id,"status":"PAID","amount":1280}@tool("refund_order",Level.PAYMENT)defrefund_order(tenant_id:str,order_id:str,idem_key:str,**kw):return{"order_id":order_id,"refunded":True,"idem_key":idem_key}classProposal(BaseModel):tenant_id:struser_id:strtool:strargs:dictconsent_token:str|None=Noneidem_key:str|None=NoneIDEM_STORE:dict[str,dict]={}CONSENT_STORE:dict[str,float]={}# token -> expire_atdefmake_idem_key(tenant_id:str,tool_name:str,args:dict)->str:raw=json.dumps({"t":tenant_id,"n":tool_name,"a":args},sort_keys=True,ensure_ascii=False,)returnhashlib.sha256(raw.encode("utf-8")).hexdigest()[:32]app=FastAPI()@app.post("/execute")defexecute(p:Proposal):spec=REGISTRY.get(p.tool)ifspecisNone:raiseHTTPException(status_code=404,detail="unknown tool")# L2/L3:必须有本轮授权凭证,且未过期ifspec["level"]>=Level.MUTATE:expire_at=CONSENT_STORE.get(p.consent_tokenor"",0)ifexpire_at<time.time():raiseHTTPException(status_code=403,detail="missing or expired consent")# L3:必须带服务端可复算的幂等键ifspec["level"]==Level.PAYMENT:expected=make_idem_key(p.tenant_id,p.tool,p.args)ifp.idem_key!=expected:raiseHTTPException(status_code=400,detail="idem_key mismatch")ifp.idem_keyinIDEM_STORE:return{"replayed":True,**IDEM_STORE[p.idem_key]}result=spec["handler"](tenant_id=p.tenant_id,**p.args)ifspec["level"]==Level.PAYMENT:IDEM_STORE[p.idem_key]=resultreturn{"replayed":False,**result}

启动:

uvicorn gateway:app--host0.0.0.0--port8000

先验一条只读调用,确认基础链路是通的:

curl-s-XPOST localhost:8000/execute\-H'content-type: application/json'\-d'{"tenant_id":"t-001","user_id":"u-9","tool":"query_order","args":{"order_id":"O-20261"}}'

示例输出:

{"replayed":false,"order_id":"O-20261","status":"PAID","amount":1280}

再验关键行为:不带授权凭证的资金级调用必须被拒绝。

curl-s-XPOST localhost:8000/execute\-H'content-type: application/json'\-d'{"tenant_id":"t-001","user_id":"u-9","tool":"refund_order","args":{"order_id":"O-20261"},"consent_token":null,"idem_key":null}'

示例输出:

{"detail":"missing or expired consent"}

未在当前环境实测,以上为预期结果。但 V1 已经留下了三个必须在下一步回答的问题:授权凭证由谁签发、租户 ID 从哪里来(不能是模型给的)、以及模型重试时怎么保证幂等键稳定不变。

先跑最简形
再谈完备性
一证不过手
千金不出门

06、排查

诊断一:同一笔订单出现两条退款记录。

现象:同一订单两条退款记录,间隔 3 秒,金额一致。

怀疑:模型重试导致重复提交。

检查审计日志,发现两次tool_call间隔 3 秒,第一次返回 5xx,第二次成功。但两次调用里的idem_key都是空,说明重试发生在模型侧,服务端完全没有参与。

证据(示例日志):

2026-03-18T09:14:02.113Z tool=refund_order order=O-7721 status=500 idem_key=- 2026-03-18T09:14:05.408Z tool=refund_order order=O-7721 status=200 idem_key=-

错误尝试:第一反应是把重试次数从五次改成一次,以为不重试就不会重复。结果重复是消失了,任务成功率也跟着掉——真正的瞬时故障失去了重试机会。这个方向错在把"重试"和"重复执行"混为一谈:重试是必要的,重复执行才是要消除的,正确做法是让重试带上同一个幂等键,而不是取消重试。

根因:模型侧的重试语义没有和写操作的幂等语义对齐,幂等键根本不存在。

修复:幂等键改由服务端从 (tenant_id, tool, args) 派生,模型重试时必须复用同一个键;首次结果写入幂等表,后续命中直接回放,不再触发副作用。

诊断二:租户 A 的运营看到了陌生客户的常旅客号。

现象:租户 A 的运营在测试时,Agent 推荐了一个不属于该租户的常旅客号。

怀疑:模型幻觉。

检查记忆存储,发现记忆表的主键是 user_id,没有 tenant_id 前缀;两个租户下恰好存在相同的 user_id 取值。

证据(示例存储):

{"key":"u-9","field":"frequent_flyer_no","value":"CZ-8812"}

错误尝试:第二反应是在提示词里加一句"注意租户隔离"。这没有用,因为租户维度是在写入记忆的那一刻丢掉的,读取阶段再怎么提醒也补不回来。约束发生在错误的位置,就等于不存在。

根因:记忆的键空间缺少租户维度,并且没有做字段级最小化,敏感字段被整体写入。

修复:记忆主键改为 (tenant_id, user_id, field);写入前按敏感字段清单裁剪;每条记忆带 TTL 与撤销标记,撤销后立即不可检索。

现象先落笔
怀疑后落锤
错路要留痕
根因方落地

07、优化

严格按 06 的两条根因形成 V2。

根因一:重试语义与幂等语义未对齐。

修改:幂等键由服务端从 (tenant_id, tool, args) 派生,模型重试必须复用同一键。

原因:把"执行一次"的标识从调用方移到服务端之后,调用方无法通过换一组参数绕过。

新行为:同一个逻辑操作无论被调用多少次,只产生一次副作用。

验证:连续提交同一 proposal 三次,第一次返回replayed=false,后两次返回replayed=true且结果体一致。

根因二:记忆键空间缺租户维度。

修改:主键改为 (tenant_id, user_id, field),写入前按敏感字段清单裁剪,每条记忆带 TTL 与撤销标记。

原因:租户隔离必须发生在存储层,而不是检索层。

新行为:撤销之后,下一次检索必然命中不到该条记忆。

验证:写入一条记忆 → 撤销 → 立即检索,返回为空;同时确认链路内没有本地或分布式缓存持有旧值。

另外补上 04 章埋下的那一条:授权凭证由服务端签发,短 TTL,绑定 (tenant_id, user_id, tool, args_hash),模型不能自己宣布"用户已经确认"。

四项验收指标的具体数值需要在真实流量下观测,本篇不给出任何未经测量的数字。建议的观测方式是:未授权变更次数从审计轨迹的拒绝记录里统计;撤销生效时间从撤销时间戳到首次被拒时间戳之间取差;任务成功率与人工接管率按工具等级分组。这些是方法,不是结果。

一因一改法
一改一验证
未见真实数
便不说已然

08、演进

[同一输入:用户说"我明天那张去上海的票不想去了"] | +--[V1] 直接调用 refund_order | 副作用立即发生,无凭证、无幂等、无租户隔离 | 代价:一条提示词即可诱发一次真实退款 | +--[V2] 生成 Action Proposal -> 授权层判定为 L3 -> 要求 consent_token 且未过期 -> 服务端校验业务状态机 -> 幂等执行并写审计 -> 失败可补偿 代价:多一次往返、需要状态机与幂等表、人工确认率上升 | [Trade-off] 得到:越权动作无法仅凭模型输出落地;副作用可去重、可追溯、可回滚。 失去:端到端延迟增加、实现复杂度上升、部分低风险场景显得多余啰嗦。 适用边界:模型能发起写操作就必须启用;纯只读场景可退回 L0/L1。

逐项比较。

正确性:V1 的正确性依赖提示词,属于概率性正确;V2 的正确性由服务端校验保证,属于确定性正确。

稳定性:V1 在模型重试、上下文注入、参数漂移下表现不稳定;V2 把这些变量挡在执行层之外。

复杂度:V2 多了授权层、状态机、幂等表和补偿流程,代码量与运维面都上去了。

成本:V2 的成本主要来自延迟与人力确认,不是机器成本。

适用范围:任何"模型可发起写操作"的场景;纯只读链路不必引入。

遗留问题:授权凭证的 TTL 取多长、状态机与业务系统的最终一致性、以及人工确认的疲劳问题——确认弹得太频繁,用户就会无脑点同意,那等于把二次确认变成了走过场;还有撤销在多级缓存下的传播延迟。

同入不同途
一取一舍间
得在拦截处
失在绕路时

09、洞见

9.1 记忆是意图证据,授权证据必须另立

模型记住"这位客户每次都坐经济舱,只提前一天订票",这是意图证据,它说明客户大概率会怎么选;但授权证据回答的是另一个问题:此刻,这个人有没有同意这笔操作发生。两者可以高度相关,但不能互相替代。系统的判断逻辑必须建立在授权证据上,意图证据只能用来改善体验,比如把默认选项排在前面。

9.2 反直觉判断:没有约束时,主动性越强风险越高

通常我们会觉得 Agent 越强越安全,因为它更"懂"。但在权限这件事上恰恰相反。记忆提升的是意图还原度,专注提升的是执行的持续性,两者都在增加"把一件事做到底"的概率——如果这件事本身不该做,那么更强的主动性只是让越权走得更远、更快、更难被中途打断。所以对高风险动作,主动性的默认值应该是收敛的,而不是放开的。

9.3 反直觉判断:基准分数和权限安全不可互推

Intelligence Index 之类的评测衡量的是任务表现,它回答"模型能不能做对";权限安全回答的是"系统能不能拦住做错的模型"。前者涨了不等于后者涨了,因为拦住一个错误动作依靠的是服务端校验、幂等键和审计轨迹,这些东西和模型分数没有关系。引用材料里提到的 Muse Wonder 系列在相关基准上的表现(需按当前官方文档核验),可以参考,但不能被翻译成"因此可以让它自己判断风险"。

9.4 授权等级必须由服务端定义,模型不能自我降级

等级写在注册表里,意味着模型无法把一次 L3 调用声明成 L0。这一条看起来平淡,但它是整个防线成立的前提:只要等级判定还允许模型参与,前面所有校验都可以被一次精心构造的输入绕开。

洞见非口号
条条有出处
前文无证据
此处不立言

10、系统落地

原来有什么:一套能查、能建议、能改签退票的平台,工具走一个统一入口,风控寄托在提示词里。

本篇新增了什么:一个意图层与授权层分离的结构;一份按副作用定义的工具授权等级;一套服务端签发、短 TTL、绑定参数哈希的授权凭证;一个由服务端派生的幂等键;一条带租户维度的记忆键空间,以及 TTL 与撤销能力。

现在能做什么:模型可以照样主动、照样记忆、照样重试,但它产出的只是 Action Proposal;真正落地的那一步,必须经过服务端校验、二次确认、幂等执行和审计落库。也就是说,越权动作不再可能仅凭模型输出就发生。

还缺什么:凭证的签发入口(谁在什么界面点的那一下确认)、状态机与业务系统的最终一致性方案、以及一条能回放整条链路的审计查询。

下一步如何演进:把这套授权层做成平台级的统一网关,让所有接入的智能体共用同一份等级定义;再往上,把撤销做成事件,让记忆、凭证、缓存一起失效。

有者继续用
无者今补齐
缺者明其名
进者定其序

11、小结

Q1 → 不是同一个角色:记忆是意图证据,授权证据必须由服务端独立产生 Q2 → 确认由服务端完成,凭证绑定租户、用户、工具与参数,模型无法自证"用户已同意" Q3 → 代价不是答错一次,而是一笔已落地的资金和一条补不回来的审计记录 状态 → 可运行的工具授权分级 + 服务端二次确认 + 幂等与审计的最小网关

一问一证法
一答一落地
可交付为准
非愿景为凭

12、作业

12.1 理解题:为什么"Active Memory 记住了用户的退票偏好"不能作为退票操作的授权依据?

参考答案:因为记忆提供的是意图证据,它说明用户过去怎么选、大概率会怎么选;授权证据回答的是"此刻这个人有没有同意这笔操作发生"。两者相关但不能互推。一旦把前者当作后者,一次重试、一段注入文本或者一次参数漂移,就能把"我知道你想要"变成"我替你做了"。

12.2 实战题:在第 05 章的网关上,为 L3 工具补一条服务端确认链路,使同一笔退款重试三次只产生一次副作用。

参考答案:把幂等键从"调用方传入"改为"服务端派生",派生输入固定为 (tenant_id, tool, args),对参数做规范化排序后哈希;首次执行结果写入幂等表,后续命中直接回放并标记 replayed。同时把授权凭证做成短 TTL 且绑定参数哈希的结构,参数一变凭证即失效,避免"用旧凭证执行新参数"。

12.3 排障题:审计日志里出现了同一订单的两条退款记录,两次调用的 idem_key 都为空,应该先改哪里?

参考答案:先改幂等键的来源,而不是先关重试。idem_key 为空说明幂等逻辑根本没有进入服务端链路,问题在于重试语义与幂等语义没有对齐。先把键的派生收敛到服务端,再验证连续三次提交只产生一次副作用。直接砍掉重试会同时砍掉瞬时故障的恢复能力,属于用降低可用性换正确性,方向不对。

12.4 架构判断题:有同学提议"把工具授权等级的判定交给模型,因为它更懂上下文",是否可行?

参考答案:不可行。等级判定是授权层的第一道闸门,一旦允许模型参与,模型就可以把一次 L3 调用表达成 L0,前面所有校验都会被绕开。模型可以参与的是意图理解与参数补全,也就是它擅长的那部分;等级必须写在服务端的工具注册表里,由服务端在注册时固定。

题目皆可答
答案可复现
会做不算会
能证才算会

13、思考

回到最开始那个冲突:一个记得住你所有偏好的助手,和一个有权动你账户的助手,是不是同一个角色。答案已经很清楚了——不是。记忆和专注让 Agent 更像一个称职的助理,但称职不等于被授权。能力圈划定的是系统能判断、也能承担后果的范围,反演思维划定的是这个范围之外必须有多少道闸门。

这两条合起来的工程判断只有一句:智能越主动,权限越要由系统而不是模型决定。模型的输出是提案,系统的校验才是决定。凡是把"我知道"读成"我可以"的地方,迟早会有一笔已经落地的钱来提醒你。

一念辨边界
一证定根因
一改知取舍
一役见真章

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进