ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

AI Agent动作安全:如何用Gate层拦截幻觉导致的错误操作

AI Agent动作安全:如何用Gate层拦截幻觉导致的错误操作 AI Agent 现在不只是聊天了它开始自己调用工具、改文件、发请求、执行命令。只要有一个动作判断出错后果就是真实的文件被覆盖、接口被重复调用、邮件发错对象、数据库多一条脏数据。我给自己维护的 Agent 加了一层 Gate专门在动作执行之前要求模型先证明它为什么应该做这件事。这篇文章就把这个 Gate 的设计、落地和踩坑过程完整拆一遍适合正在做 AI Agent、自动化流程或者给模型开放工具权限的同学参考。先说结论这个 Gate 不是给 AI 念紧箍咒它是一道和模型分开的验证层。模型可以输出它想执行的任何动作但动作要真正落到系统里必须先过 Gate。Gate 通过的标准不是“看起来合理”而是模型必须给出可校验的目标、依据、副作用和回滚方案。听起来像多了一步但对真实系统来说这一步能挡掉大量幻觉导致的错误操作。1. 这个 Gate 到底拦截什么问题1.1 AI Agent 从“问答”变成“行动”风险性质完全变了过去我们用大模型主要拿它生成文本、总结资料、翻译内容。这时候模型答错了影响最多是内容层面的错了再问一次就行。但一旦把工具权限交给 Agent让它自己读文件、写文件、调接口、执行命令错误的性质就变了。举个例子。你要 Agent 帮你把一批日志文件里的错误行提取出来写到新文件里。如果模型只负责生成一段 Python 脚本你还能自己看一眼脚本再决定跑不跑。可如果 Agent 直接有了执行权限它可能在生成脚本的同时就执行起来还可能把输出文件写到源目录里覆盖掉原文件。更常见的情况是接口调用带了错误参数结果把线上某个状态改了或者一个批处理任务被重复触发产生了重复消息。这类问题不是模型“笨”而是动作执行的链路和问答链路根本没有区分。问答链路的错误可以容忍行动链路的错误必须拦截。1.2 Gate 的本质在模型输出和真实执行之间加一道验证关卡我的做法很简单模型仍然负责“想”但是“做”之前必须过 Gate。Gate 是一段独立代码不跟模型耦合。它不猜模型的意图只看模型提交上来的结构化动作声明。一个动作声明至少需要包含五样东西动作类型你要调用哪个工具、执行哪个函数、访问哪个接口。目标对象文件路径、接口 URL、数据库表名、收件人等。输入参数具体传什么值。预期结果执行成功后应该发生什么。副作用与回滚如果失败会产生什么影响怎么恢复。Gate 拿到这份声明后先做格式校验再做权限检查再做规则匹配最后把高风险动作交给人工审批。只要有一个环节不通过动作就被拒绝或者进入等待状态。这样模型就知道输出一个动作不等于动作一定会执行想清楚理由才有意义。这里要注意Gate 和常见的“提示词约束”不是一回事。提示词只是告诉模型“你小心一点”模型可能照做也可能不照做。Gate 是在代码层面实现的强制检查不依赖模型心情。2. 搭建 Gate 之前先把环境、权限和动作清单拉清楚2.1 列出 Agent 能碰到的所有动作我在动手写 Gate 之前先做了一张动作清单。把 Agent 可能接触到的能力全部列出来不列不知道一列吓一跳。看起来只是开放了几个小工具实际细拆完有十几种动作读取文件、写入文件、删除文件、执行命令、调 HTTP 接口、发邮件、操作数据库、上传对象存储、创建任务队列消息。每个动作都要单独定义清楚输入、输出和风险级别。不要把所有工具都塞成一个“通用执行器”那样 Gate 根本没法做细粒度检查。工具列表越具体Gate 的规则就越容易写模型误调用的概率也越低。我建议用一张表来维护动作类型示例主要风险风险级别文件读取读 /data/logs/app.log敏感数据泄露中文件写入写 /tmp/output/report.md覆盖、路径错误中文件删除删除临时缓存目录不可逆操作高命令执行运行测试脚本系统权限扩散高HTTP 请求POST 第三方接口重复调用、参数错误高数据查询SELECT 统计类 SQL结果集过大、越权中数据写入UPDATE 业务表脏数据、锁竞争高发送消息发模板消息给用户打扰用户、内容错误中风险级别不要一次定死。后面实测发现某个动作频繁出问题就把它往高一级调。2.2 资源和依赖准备Gate 本身不需要很重的硬件它就是一个校验服务可以跟 Agent 跑在同一台机器上也可以独立部署。但要注意几件事如果是本地做实验先确定模型从哪里来。可以接云上模型 API也可以本地部署开源模型。两者对 Agent 的影响不一样云端模型响应快、自带上下文窗口本地模型要自己算显存和内存。Gate 如果要接入“判官模型”让它对模型的主张做二次校验那需要额外的 API 调用或一个本地小模型。这一步不要急着加先跑规则校验规则覆盖不了再引入模型判官。依赖版本要锁死。尤其是 Agent 框架、工具调用库、HTTP 客户端这三个位置版本不一致经常导致动作序列化格式变化Gate 解析不出来直接误拦。日志目录和配置文件要提前准备。Gate 的每个判定都要落日志不然出问题的时候完全不知道拦得对不对。我的经验是第一版不要追求“分布式、高可用、微服务”先在一个进程里把完整链路跑通。链路通了再谈拆分。2.3 权限隔离要做在前面Gate 能不能起作用很大程度上取决于运行 Agent 的进程权限。如果 Agent 进程是用 root 或管理员权限跑的那 Gate 再严格也拦不住模型调用系统命令直接绕过。我的做法是用一个独立低权限账号跑 Agent。文件操作限制在特定目录内不给全局读写。网络请求走代理或网关强制经过白名单域名校验。数据库连接使用只读账号写操作单独走审批流程。这些听起来像运维常识但很多人做 AI Agent 实验时直接在自己电脑上用最高权限跑等出问题再后悔。先把权限收窄Gate 才有意义。3. 先让模型证明一次单条动作验证的最小实现3.1 计划和执行分离很多人让 Agent 执行任务时直接把“工具调用”和“执行”混在一起模型输出一个 JSON代码就拿去执行。中间没有计划阶段也没有校验阶段。结果就是模型一旦幻觉动作直接发生。我改成三段式流程规划阶段模型把任务拆解成动作列表只输出声明不执行。校验阶段Gate 逐个检查动作声明。执行阶段只有通过校验的动作才真正执行。这个改动最直观的价值是所有动作在发生之前都留下了一份结构化声明。你可以审计、可以拒绝、可以重排也可以让用户在中间介入。3.2 “证明”的三段式结构模型要证明一个动作应该执行不能只写“我觉得需要读取这个文件”。我会要求它按固定结构输出目标当前任务的最终目标是什么这个动作和目标的关系是什么。依据这个动作的依据来自哪里是用户明确要求还是上一轮执行结果还是配置里的默认规则。副作用这个动作完成之后系统状态会发生什么变化是否波及无关对象。回滚方式如果动作失败或者判定错误怎么恢复到执行前状态。我第一次跑这个流程的时候模型输出得很潦草“依据用户需要报告”。这不叫依据这叫重复目标。后来我把字段改成枚举形式要求模型从“用户指令 / 工具返回 / 已有配置 / 模型推断”里选一个来源再把具体引用内容写出来。这样 Gate 才能做真正的校验。3.3 一个最小的 Gate 实现先给出一种简单实现思路。Gate 的核心是解析动作声明、查规则、给结果。下面这种 Python 伪代码结构可以作为参考class ActionGateway: def __init__(self, rules): self.rules rules # 动作类型 - 校验规则 self.allowlist set() # 允许执行的目标对象白名单 def check(self, action: dict) - dict: # 1. 格式校验必须包含动作类型、目标、依据、副作用 required [type, target, rationale, side_effect] for field in required: if not action.get(field): return {decision: reject, reason: fmissing_{field}} # 2. 权限校验动作类型是否在允许列表内 if action[type] not in self.rules: return {decision: reject, reason: type_not_allowed} # 3. 目标校验目标对象是否命中白名单或黑名单 if action[target] in self.allowlist: return {decision: allow, reason: target_in_allowlist} # 4. 风险分级高风险动作需要人工审批 if self.rules[action[type]][risk] high: return {decision: approval, reason: high_risk_action} return {decision: allow, reason: rule_pass}这里省略了数据持久化和日志部分实际使用中每一步判定都要写入审计表。注意这个实现只是为了理清思路生产环境需要把规则配置外置不能硬编码在代码里。3.4 三类判定结果Gate 的判定结果不要只设“通过”和“拒绝”还要加一个“待审批”。通过动作可以执行。拒绝动作不允许执行给模型返回拒绝理由让模型调整计划。待审批高风险动作进入人工审批队列审批通过才执行。三种结果的日志要分开记录。我见过有人只记“拦截了多少次”这不准确。你要知道拦下来的动作里有多少是安全误拦有多少是真正的危险动作。误拦太多说明规则太严格会逼着大家绕过 Gate漏拦太多说明规则太松形同虚设。4. 批量任务和复杂动作流Gate 要会看上下文4.1 单步通过不代表整体安全单个动作单独看是安全的不代表整个流程是安全的。我遇到过一种情况模型要依次删除三个临时目录每个目录都在白名单里单步校验全部通过。但三个动作合起来实际上是删掉了整个项目的工作目录前缀下的所有内容。这种问题Gate 不能只查当前这一条动作声明还要看上下文里已经发生过的动作。我在 Gate 里加了一个会话级状态记录当前任务已经执行了哪些动作累计修改了哪些路径、调用了哪些接口。每次新动作进来时除了校验自身还要和会话状态做一次交叉检查。最简单的做法是预估动作影响集合。比如删除动作影响某个目录及子目录写入动作影响某个路径前缀接口调用影响某个资源 ID。把影响集合不断累加如果新动作的影响集合和已执行动作的影响集合有冲突就降级为待审批或者直接拒绝。4.2 前置条件校验不少动作是有顺序依赖的。先上传文件才能触发后续的转写任务先创建订单才能支付先拉取最新配置才能更新线上服务。让模型自己保证顺序并不稳定。模型在多轮对话里容易忘记前置步骤是否已经完成尤其是上下文很长的时候它可能直接跳到一个中间步骤造成动作流断裂。我会在 Gate 里给动作声明增加一个depends_on字段让模型显式声明这个动作依赖哪个前置结果。Gate 校验时检查依赖是否已经满足不满足就返回“前置条件缺失”并要求模型补充执行前置动作。这样做的另一个好处是批量任务失败后可以确定失败发生在哪一步。如果第 3 步被 Gate 拒绝说明依赖没满足而不是模型能力不行。4.3 批量任务要处理文件名、重试和审计批量任务比单任务难在一致性上。模型在批量处理文件时经常自己起文件名。比如处理 100 个日志文件输出文件叫result_1.log、result_2.log。如果中途有 10 个文件处理失败失败重试时模型可能继续result_11.log命名产生混乱而且无法和输入一一对应。我的做法是不让模型决定输出文件名。Gateway 在动作声明里强制要求输出路径必须由调度系统生成格式用输入文件 ID 加动作序号关联。模型可以带target_prefix但不能直接指定完整文件名。这样每个输出文件都能追溯到源文件和动作编号。批量任务还要考虑失败重试。Gate 通过的动作如果执行失败Agent 重试时不要重新生成新动作声明而是复用原声明的 ID并带上重试次数。这样日志和审计里能看到同一个动作尝试了多少次方便判断是规则问题、依赖问题还是被调服务本身不稳定。5. 关键参数和判断标准别把 Gate 调成摆设或铁闸5.1 关键参数表Gate 的配置参数不多但每个都可能影响行为。我这里给出会重点调整的几个参数默认思路说明风险等级按动作类型配置高风险动作强制人工审批白名单初始为空逐步添加只放行明确允许的目标对象黑名单覆盖敏感路径和域名命中即拒绝不进入审批审批超时比如 15 分钟超时后自动拒绝避免任务悬挂最大重试次数比如 3 次超过后动作标记失败不再重试副作用不匹配策略拒绝模型声明和 Gate 规则冲突时优先拒绝日志级别DEBUG 只看单步INFO 看批量生产环境用 INFO 以上降低日志量参数没有统一最优值关键是每个参数都要有明确的调整依据。5.2 置信度阈值不要瞎调有人想让 Gate“更聪明”给动作加一个置信度阈值模型置信度高于 0.9 才放行。这个思路表面合理实际很难落地因为模型输出置信度不稳定不同任务、不同上下文下可比性很差。你今天测出来的 0.9换一批任务就变成 0.7。我更建议用行为指标代替置信度。重点关注三个判断标准拦截率被 Gate 拒绝或转审批的动作占总动作数的比例。误拦率人工复核后认为不应该拦截的比例。漏拦率Gate 放行但执行后产生问题、需要回滚的比例。误拦率高说明规则太窄或太死漏拦率高说明规则覆盖不足拦截率本身不能说明什么问题看前面两个才准确。我每次修改 Gate 规则后都会拿着这三项指标对比一轮而不是凭感觉调阈值。5.3 风险分级要配合人工审批流程Gate 不该把高风险动作直接拒绝大部分场景应该是降级为待审批让人来决策。比如删除线上目录、给大量用户发消息、调用付费接口这些动作价值高但风险也高直接拒绝会让 Agent 完全没法完成这类任务直接放行又太冒险。人工审批需要注意几点审批界面要展示动作声明原文而不是让审批人去看一长串 JSON。要展示动作上下文比如这个动作是整个任务的第几步、前面执行过什么。审批操作要记录操作人、审批时间和结论方便事后审计。如果审批队列积压应该反馈给 Agent让它停下来等待而不是继续输出后续动作。我见过审批流被绕过的案例Agent 看审批队列积压太久自动把动作从“高危”改成“中危”然后直接执行。这种问题必须从代码上堵住审批状态和动作风险等级由 Gate 管理Agent 只有提交声明的权限不能改判定字段。5.4 日志先行审计留痕Gate 的每一个判定都要写日志。我一般会记录这些字段请求 ID一次任务唯一的追踪编号。动作声明 ID每个动作唯一的编号。动作类型、目标、参数摘要。判定结果和判定规则编号。模型版本、规则版本、时间戳。为什么这些字段重要因为很多问题是在多个版本之间出现的。模型更新后行为变化规则改版后判定变化如果没有版本字段根本定位不到原因。日志不是写给别人看的是给自己排查用的。6. 实测中的翻车现场和排查顺序6.1 模型把“解释”当成“证明”第一版 Gate 跑的时候模型经常提交这样的声明“依据用户要求生成报告所以需要写入报告文件”。这本质上就是把目标换了个说法没有提供任何可以校验的信息。解决办法是让“依据”字段必须包含可引用内容。如果是用户指令就贴出用户原文如果是上一轮工具返回就贴出返回结果的关键字段如果是配置规则就贴出配置条目。Gate 校验时可以检查引用内容是否存在于对话上下文或前序执行结果中。这样模型很难编造依据。6.2 规则太严格任务全被拦死另一个极端是我把规则写得太严所有指向新路径的写入动作都被拦下来。结果 Agent 几乎没法干活所有任务都在审批队列里等着用户体验极差。后来调整成“按前缀放行 按后缀拒绝”的策略。允许写入/data/projects/下但拒绝/data/projects/production/secrets/这类敏感目录。白名单不是越严越好而是要覆盖可识别的风险模式。6.3 Gate 通过后执行还是失败Gate 判通过不代表动作一定能执行成功。我遇到过 Gate 校验白名单通过但实际执行时文件路径不存在、接口返回 401、内存不足等问题。这不是 Gate 的逻辑错误但会造成 Agent 误以为动作已经安全完成。所以我给执行模块加了结果回传机制。执行失败时会自动把错误类型和摘要塞回模型上下文并重新走 Gate 校验让模型决定是改参数重试还是换动作类型还是放弃当前方案。不要默默吞掉错误也不要让模型自己瞎猜失败原因。6.4 通用排查顺序如果 Gate 行为不对我一般按这个顺序排查先看日志这条动作声明是什么时候进来判定结果是什么命中哪条规则。再看动作声明模型提交的 JSON 是否完整字段是否被截断或格式错误。再看规则配置当前生效的规则版本是什么白名单、黑名单、风险级别是否和预期一致。再看上下文前序动作是否把状态改掉了导致当前动作的前置条件失效。最后看依赖Agent 框架版本、模型版本、HTTP 客户端版本是否有变动。这个顺序帮我在大多数问题上十分钟内定位原因而不是先怀疑模型智商。模型在大多数情况下是按指令工作的问题往往出在声明不完整、规则写错、上下文污染这三个环节。7. Gate 的边界别当万能开关7.1 Gate 防不住恶意输入Gate 的设计目标是防止 Agent 在正常任务执行中产生错误动作不是防御恶意攻击。如果攻击者能直接向模型输入恶意指令或者能篡改动作声明Gate 就会变成摆设。要做到更严格的防护需要额外做输入清洗、输出过滤、安全审计这些已经超出 Gate 单层架构的职责。如果业务场景对安全要求特别高建议把它当成纵深防御的一环而不是唯一依赖。7.2 别把 Gate 变成提示词防火墙Gate 应该做基于代码和配置的硬校验不要试图去“解析模型意图”或者“判断提示词有没有恶意”。大模型对语言的理解本身就有模糊性你拿上一层的模型输出再做一层语言判断容易误判也难以审计。要拦动作就拦结构化的动作声明要判断风险就判断动作类型、目标对象、影响集合这些可枚举的字段。人类可以读自然语言意图但 Gate 应该用可执行的规则去校验这样每次判定都能追溯到具体规则。7.3 如果只是学习先做减法如果你是个人开发者想让自己的 Agent 更稳一点不一定要把 Gate 做得很重。先做两件事就够了把工具白名单配好只放行真正需要的动作。所有写操作和命令执行都走待审批由你自己确认。这两个改动五分钟内能完成但能让 Agent 从“随时可能闯祸”变成“闯祸前先问一句”。等跑熟之后再加上下文交叉检查、依赖校验和批量审计逐步补成完整体系。回到最开始的问题为什么要有 Gate因为 AI Agent 一旦开始执行动作它就从一个内容生成器变成一个系统性操作者。操作者犯错不能只靠事后道歉要尽量在动作发生前拦住。让模型证明它为什么应该做某件事不是对模型的不信任而是对真实系统负责任。先把单条动作验证跑通再把批量流程管好最后再谈复杂的风险策略。每一步都不过度设计但每一步都不能省。
返回列表