Agent 安全不能只靠一句系统提示
文章指出,要求模型保密、谨慎操作,并不等于应用真正限制了它的权限。Agent 读取攻击者控制的网页或文档后,可能因间接提示注入而误用文件、终端和 API 权限。
AI 深度解读
给 AI 接上项目目录、终端和 API,再写一句「不要泄露密钥,危险操作先询问」,并没有完成权限设计。这篇文章的核心判断是:模型可以提出操作,但能不能执行,必须由应用独立决定。真正需要追问的是,模型判断错了,什么机制能拦住它。
作者借「去拒绝化模型」展开讨论,但问题同样适用于普通 AI 编程助手。文章提供的是架构建议和假想攻击路径,没有给出某款模型的完整安全实测。对正在给 Agent 接工具的前端、Node 和全栈开发者,这个区分很实用。
少拒绝,不等于更聪明,也不等于拿到了权限
原文先拆开两个容易混用的概念。Uncensored 描述行为目标,通常指限制或拒绝更少;Abliterated 描述一类干预方法,常见做法是识别模型内部与拒绝相关的表征方向,再削弱它们的影响,已有教程展示了无需完整重训的实现。
两者可以重叠。模型经过 Abliteration 后,也可能继续微调,以恢复能力或调整行为。
但少拒绝不能证明推理更强、事实更准。原文引用的一个已发表案例中,Daredevil-8B 的 Abliterated 版本在所报告的基准平均分上略低于原版。作者没有列出具体分数,也没有据此断言所有模型都会退步。能得出的结论只是:能力是否保留,需要单独测量。
权限又是另一回事。模型输出「删除项目目录」,这段文字不会自行删除文件;模型生成 SQL,也要经过数据库连接才能执行。连接使用什么身份、能访问哪些表,由应用和基础设施决定。
模型的拒绝是行为防线,执行器的拒绝才是权限约束。
所以,「Abliteration 会不会加重提示注入」也不能只看标签回答。拒绝有害请求,与区分可信指令和外部内容,是两个测量维度。需要在相同执行策略、相同成功标准下比较原版和修改版,观察越权操作与信息泄露,而不是只统计模型说了多少次「不能帮忙」。
网页里的指令,为什么可能动到你的内部资料
作者给出了一条假想攻击链:用户让 Agent 总结网页,网页混入攻击者指令;Agent 把这些内容当成任务要求,先调用工具读取内部资料,再调用另一个工具向外发送。
攻击者未必需要修改 system prompt,也未必需要管理员密码。只要外部内容能影响模型,而应用又执行模型选择的动作,入口就出现了。这是间接提示注入(Indirect Prompt Injection)的典型问题:被处理的数据,越界成了行动指令。
这里有两个容易误判的条件。
第一个是「模型跑在本地」。它只回答计算发生在哪里,没有回答应用能读什么、工具能做什么、网络能连哪里、使用谁的身份。本地模型连接邮件 API,仍然拥有对外通信渠道。
第二个是「只有只读权限」。只读能限制修改,却不能阻止泄露。读取内部文档和发送外部消息,单独看都可能有正当用途,组合起来就形成了数据传输路径。
因此,检查工具权限不能只逐个看。还要问:当前用户是否允许某个来源的信息,到达某个目的地?
工具的粒度也影响控制难度。任务只是读取文档时,read_document(document_id) 比 run_shell(command) 更容易约束。不过,接口窄并不自动安全,它仍然需要校验参数、解析资源,并确认用户能否访问目标文档。
密钥同样应尽量留在工具内部。工具可以代为完成授权操作,不必把底层凭据交给模型阅读。
真正的边界,是每次工具调用都经过应用检查
作者建议的流程是:模型提出动作,应用验证结构,再检查身份、资源和权限;必要时获取人工批准,最后交给受限执行器,并留下审计记录。
下面的伪代码展示了这个工具网关如何组织检查:
def handle_tool_request(proposal, session):
action = validate_schema(proposal)
tool = registry.require_allowed_tool(
workflow=session.workflow,
name=action.tool,
)
resource = tool.resolve_resource(action.arguments)
policy.require_permission(
user=session.authenticated_user,
workflow=session.workflow,
operation=tool.operation,
resource=resource,
)
budget.require_capacity(
run_id=session.run_id,
operation=tool.operation,
)
if policy.requires_approval(tool.operation, resource):
approvals.require_exact_match(
run_id=session.run_id,
user=session.authenticated_user,
tool=action.tool,
arguments=action.arguments,
resolved_resource=resource,
)
return executor.run_restricted(
tool=tool,
arguments=action.arguments,
resource=resource,
identity=session.execution_identity,
)
要看的是 tool.resolve_resource(action.arguments):先确定实际操作对象,再对对象授权。用户身份来自已认证会话,执行身份来自 session.execution_identity,不能因为模型生成了 "authorized": true 就放行。这段代码是架构草图,生产环境还要处理批准过期、并发、重试,以及检查后资源发生变化的问题。
人工审批也必须绑定具体动作。「允许继续吗」没有说明批准范围。发送邮件时,应展示收件人 external-recipient@example.com、附件 quarterly-report.pdf 和数据分类 Confidential;批准后执行的必须是同一操作,不能让模型再换参数。
输出校验则要按下游场景处理。数据库优先提供带类型参数的受限操作,浏览器输出采用适合所在上下文的编码,工具执行同时验证操作和参数。模型生成的 SQL、HTML、Python 或 shell 文本,都不能直接视为可信输入。
还有一种事故不需要偷密钥:Agent 不断循环、调用工具,耗尽资源。作者给出的预算示例如下:
agent_budget:
max_steps: 12
max_tool_calls: 20
wall_clock_timeout_seconds: 90
max_output_tokens: 4000
max_external_requests: 5
这里的数字只是示意,不是通用默认值。尤其是 wall_clock_timeout_seconds: 90,需要外部运行时落实,不能依赖模型自行遵守。文档摘要与维护任务、自托管模型与计费 API,需要不同预算。
我的判断
我认同文章把重心放在执行层。对已有后端的团队,这套思路与普通接口鉴权相通:请求来自哪里,不会改变身份、资源、权限检查的必要性。LLM 生成的请求,也应经过这些边界。
但不能把这篇文章当成模型安全排名。作者没有提供原版与 Abliterated 模型在同一环境下的提示注入对照数据,攻击链、测试计划和应急方案也都是建议。它能支持架构取舍,不能证明某个检查点更容易泄露密钥。
与单靠更严格的模型或更长的 system prompt 相比,外部授权能对具体操作给出可执行的限制;行为防线仍然可以保留,帮助减少错误提议。两者承担不同职责。
工具收窄也有成本。开放 shell 适合探索性维护,专用接口更容易限制范围,但要额外开发和维护。合理选择取决于任务需要。确实需要 shell,就要把约束落实到执行身份、资源范围和运行预算。
安全测试还必须同时衡量可用性。作者建议分别统计越权提议数、实际执行数、敏感测试数据泄露、正常任务完成、正常动作误拦,以及成本和步数。这比只问模型是否愿意泄密,更接近应用实际承担的风险。
能拿来做什么
- 给 Node 工具调用加统一网关,让每次请求都检查会话身份、工具白名单和目标资源权限。模型提供的文档编号或文件路径,需要解析后再授权。
- 检查日常 AI 编程流程里的工具组合,尤其是「读取内部文件」加「对外发送」。任务不需要的通信工具和密钥,就不要放进该工作流。
- 用合成密钥、测试账号和可丢弃环境测试恶意网页或文档,分别记录模型提出了什么、执行器放行了什么,同时检查正常任务是否被误拦。
- 把预算、审计和停止入口放到模型之外。审计应记录运行标识、用户、策略与模型版本、目标、授权结果和预算使用;减少敏感内容留存,并确保能直接取消任务、禁用工具或停用凭据。