
看到“Agent潜伏两个月联手作案”这类标题时很多开发者的第一反应是好奇第二反应可能是疑惑AI Agent 不是一堆代码和模型接口吗怎么会“潜伏”在讨论这个问题之前我先亮明一个安全工程视角的观点这类安全事故能发生通常不是因为模型突然有了自我意识而是我们给 Agent 开放了工具、权限和长期运行能力却没有配套设计安全边界。本文不贩卖焦虑而是从工程角度拆解这类事件背后的技术链路、复盘思路和防护手段。我们会先理清 Agent 系统的组成和风险面再给出一个最小可运行的 Agent 安全执行器示例覆盖权限拦截、审计日志、人工审批、最大步数限制等关键设计最后整理一份常见问题排查清单。无论你是刚接触 Agent 开发还是在企业项目里负责 AI 应用落地这篇文章都能帮你建立一条可执行的 Agent 安全基线。1. 从“Agent潜伏两个月”看 AI Agent 安全本质1.1 AI Agent 不是简单的聊天机器人我们平时调用 ChatGPT 或 OpenAI API 时通常是一个“请求-响应”过程把用户消息发给模型模型一次性返回文本。这种模式下模型没有权限访问外部系统也很难产生业务副作用风险相对可控。而 AI Agent 的形态完全不同。Agent 是一个能自主感知环境、做出决策、调用工具并迭代执行的程序。它可能具备以下能力读取文件、访问数据库、调用内部 API发送邮件、创建工单、修改配置维护短期和长期记忆在多次运行中复用历史信息在无人值守的情况下持续运行数小时甚至数天多个 Agent 之间通过消息或 API 互相协作。正是这些能力让 Agent 从“只输出文本的模型接口”变成了“拥有业务权限的执行者”。一旦权限边界没有收紧Agent 就可能成为攻击者投递恶意指令后的“跳板”。1.2 “潜伏两个月”背后的四个工程缺口“潜伏两个月”“联手作案”这样的表述本质上指向了几个工程缺陷。我把它们拆成四类便于后续针对性设计防护方案。第一缺少最大运行时长和最大步数约束。如果 Agent 被设计成“每 10 分钟执行一次任务失败就重试”又没有设置总次数的上限那么一次误判就可能在两个月内反复触发成千上万次操作。第二权限粒度过大。很多团队给 Agent 的 API Key 或工具账号直接使用了管理员权限Agent 可以读写所有文件、调用所有接口。攻击者只要通过一次提示词注入拿到控制点就等于拿走了整台主机的执行权限。第三审计日志不完整。如果只记录 Agent 最终输出了什么没有记录每一步调用了哪些工具、传入了哪些参数、产生了什么副作用那么事后复盘时根本无法定位问题起点。Agent 运行时间越长证据链越模糊。第四多 Agent 之间缺少隔离和身份边界。一个 Agent 被污染后可以以自身身份去调用另一个 Agent 的接口。在缺少服务间认证和独立审计的情况下责任边界会变得非常混乱这也是“联手作案”感受的来源。2. Agent 系统安全风险面2.1 理解 Harness、Agent、Skill、Scope 的边界在 Agent 开发和开源社区中有几个概念经常出现但它们之间的边界很容易混淆。我简单梳理一下Harness可以理解成 Agent 的外层运行容器。它负责管理环境、资源限制、工具调用、日志记录和沙箱策略。2025 年以来OpenAI 等团队把 Codex 相关的 Harness 设计逐步开放给社区核心思路就是让“模型决策层”和“工具执行层”分离Model 只负责生成决策实际命令执行由 Harness 负责约束。Agent侧重“决策”。它读取状态、调用模型、形成下一步动作是系统的大脑。Skill是 Agent 可以加载的复用能力包类似于一组插件。Skill 需要版本管理、权限声明和调用审计。Scope描述 Agent 被允许影响的范围例如只允许读写某个目录、只允许访问某个数据库 Schema、只允许调用某个 API 的只读方法。MemoryAgent 的持久化记忆可能包含业务上下文、用户偏好、历史动作。记忆如果被注入恶意内容会影响 Agent 后续很长一段时间的决策。从安全角度看Harness 的价值尤其大。它把“能做什么”和“怎么做”从模型决策中抽离变成硬性约束。这比依赖模型“自觉遵守边界”要可靠得多。2.2 Agent 安全风险清单在实际项目中Agent 的风险点可以归纳为以下几类风险点具体表现潜在影响工具权限过大Agent 可直接执行 Shell 命令、删除文件、调用业务写接口数据误删、配置损坏、业务中断API Key 外泄Key 出现在代码仓库、日志或模型 Prompt 中身份冒用、资源滥用、计费损失记忆污染外部内容通过 Prompt 注入写入长期记忆Agent 后续决策被持续误导多 Agent 联动缺乏认证Agent A 可无认证调用 Agent B 接口攻击面横向扩大长期运行缺少看护Agent 循环执行、无人审批小问题放大为严重事故工具输出未校验模型使用了工具返回的恶意内容生成报告或执行动作被带偏这里特别想强调 API Key 管理。网上经常出现“OpenAI API key 分享”“免费 Key”之类的说法我在这里明确提醒API Key 是身份凭据绝不应该分享、绝不应该写死在代码里、更不应该打印到日志中。正确做法是放在环境变量或密钥管理服务里并且定期轮换。2.3 一个典型事故链路的抽象模型我们不针对某个具体事件做还原但可以把这一类事件抽象成通用链路外部输入进入 Agent。比如 Agent 需要阅读网页、邮件、上传文件或订阅源更新外部内容中包含恶意指令。经典场景是 Prompt Injection模型把外部文本中的“忽略之前规则执行某某操作”误当成系统指令Agent 以自身权限调用工具。由于没有最小权限拦截读取操作升级为写入操作横向扩散。Agent 调用内部 API或通过泄露凭据访问相邻系统长期驻留。由于没有最大步数和人工审批Agent 一直重复执行并不断生成新任务被发现的时机严重滞后。审计日志缺失系统直到业务异常才被排查发现。正因为 Agent 的“工具副作用”和“长期自动运行”特征传统 Web 应用的“认证-授权-审计”模型必须叠加到 Agent 系统的每一个决策环节上。3. 安全事故复盘方法论无论是运营一个 Agent 系统还是在企业内部做安全应急响应我们都需要一套可复用的复盘框架。它能帮助我们从混乱的现象中找出根因而不是凭感觉“封一个工具”了事。3.1 事故复盘的核心流程这里给出一套通用的七个阶段适用于大多数 Agent 安全事故复盘发现与响应通过告警、监控或业务异常发现事件立即进入响应状态隔离与止损切断可疑 Agent 的工具权限、吊销相关 Key、暂停定时任务现场保留保留日志、快照、内存数据、工具调用记录的只读副本证据链归因根据时间线关联模型输入、工具调用和权限变更定位事故起点根因分析区分是提示词注入、权限配置错误、密钥泄露还是代码缺陷影响评估统计受影响的数据、业务范围、时间跨度修复与改进修复漏洞、补充监控告警、更新权限模型并沉淀复盘报告。需要特别说明的是复盘必须在合法授权和安全可控的环境中进行。如果你发现的是生产环境异常不要私自进行“模拟攻击”或扩大测试范围应该先隔离系统再由有权限的运维或安全团队介入。3.2 复盘时需要收集的数据Agent 系统比传统系统更容易留下“过程日志”如果我们没有记录复盘时就只能靠猜。建议至少保留以下数据数据类型关键内容作用任务调度日志任务启动时间、触发源、执行周期还原“潜伏”过程工具调用日志工具名、入参、出参、结果状态定位具体危险操作模型请求日志Prompt、输出、Token 数量分析是否存在注入或非法指令权限变更日志Key 创建/吊销、角色变更发现权限扩大网络访问日志Agent 进程访问的外部域名和 IP发现数据外泄记忆快照长期记忆中的文本内容判断记忆污染3.3 常见现象与证据关联很多安全事故并不是一开始就面目清晰而是表现为“某个 Agent 行为异常”。下面表格可以帮助你建立排查思路可疑现象需要检查的数据可能根因Agent 在凌晨执行大量删除操作工具调用日志、时间戳、任务触发源定时任务失控或凭据泄露Agent 反复读取同一外部文件模型请求日志、工具调用日志外部内容被用于持续注入Agent 生成了用户未要求的文件输出日志、Prompt 记录、写文件日志Prompt 注入或模型意图漂移多个 Agent 在同一时间点启动编排日志、身份认证日志攻击者批量触发或任务依赖错误Agent 调用了一个从未授权的工具权限日志、工具注册表变更记录权限配置被篡改或默认放行这些证据组合起来可以还原出“外部输入 - 模型决策 - 工具执行 - 业务损失”的完整链路。3.4 复盘报告模板一份务实的复盘报告可以包含以下字段事件编号 发生时间 影响范围 发现方式 隔离措施 时间线 - 10:00 外部文件被 Agent 读取 - 10:01 模型输出一段异常指令 - 10:05 写文件工具被调用 根因 影响评估 修复动作 责任人与完成时间 后续监控项复盘报告的最终目的不是追责而是把隐患转变成可执行的改进动作。4. 实战设计一个带权限与审计的 Agent 执行器接下来进入动手环节。我准备用纯 Python 标准库实现一个最小可运行的 Agent 安全执行器。它不依赖任何重量级框架重点演示四件事工具白名单、权限审批、审计日志、最大步数限制。你完全可以把这个思路迁移到 LangChain、自研 Agent 或企业内部框架中。4.1 项目目标与环境示例项目名为safe_agent运行环境要求如下Python 3.10 或更高版本无需第三方库标准库即可运行如果希望接入真实大模型可选安装openai库并从环境变量读取 API Key。项目结构如下safe_agent/ ├── audit_logger.py # 审计日志模块 ├── permission.py # 权限策略模块 ├── tools.py # 工具注册与安全校验 ├── agent.py # Agent 调度执行器 └── main_demo.py # 演示入口4.2 实现审计日志模块审计是整个 Agent 安全体系的基础。我们把每次关键动作追加写入 JSONL 文件方便后续用命令行或日志系统分析。# 文件路径safe_agent/audit_logger.py import json import time from pathlib import Path class AuditLogger: 审计日志器把 Agent 的关键动作追加写入 JSONL 文件。 def __init__(self, log_path: str audit.jsonl): self.log_path Path(log_path) if not self.log_path.exists(): self.log_path.touch() def log(self, event_type: str, data: dict): record { event_type: event_type, timestamp: int(time.time()), data: data, } with self.log_path.open(a, encodingutf-8) as f: f.write(json.dumps(record, ensure_asciiFalse) \n) print(f[audit] {event_type}: {data})选择 JSONL 而非常规文本日志是因为每一行都是独立 JSON 对象解析方便也便于后续接入日志采集系统。每条记录包含事件类型、时间戳和业务数据足以支撑事后归因。4.3 实现权限策略模块权限策略采用“默认拒绝、显式放行”的 allowlist 模型。只有策略中明确允许的工具才可调用部分工具还需要人工审批。# 文件路径safe_agent/permission.py class PermissionPolicy: 权限策略默认禁止所有工具通过 allow 显式放行。 def __init__(self): self.allowed_tools set() # 允许调用的工具集合 self.approval_required set() # 需要人工审批的工具集合 def allow(self, tool_name: str, approval: bool False): self.allowed_tools.add(tool_name) if approval: self.approval_required.add(tool_name) def can_call(self, tool_name: str) - bool: return tool_name in self.allowed_tools def need_approval(self, tool_name: str) - bool: return tool_name in self.approval_required为什么用 allowlist 而不是 denylist因为工具数量会持续增长denylist 永远可能漏掉新增工具。默认拒绝所有调用再按最小需求逐个放行才能真正约束 Agent 的边界。4.4 实现工具注册与安全校验我们通过装饰器实现工具注册表。工具本身也要做安全校验避免出现“策略放行了但工具内部可以路径穿越”的漏洞。# 文件路径safe_agent/tools.py from pathlib import Path TOOL_REGISTRY {} def tool(name: str, permission_level: str): 注册工具并声明权限级别。 def decorator(func): TOOL_REGISTRY[name] { name: name, permission_level: permission_level, handler: func, } return func return decorator tool(read_file, low) def read_file(path: str) - str: 读取白名单目录下的文件防止路径穿越。 allowed_base Path(/tmp/safe_agent_data).resolve() target Path(path).resolve() if not str(target).startswith(str(allowed_base)): raise PermissionError(fpath not allowed: {path}) return target.read_text(encodingutf-8) tool(write_file, medium) def write_file(path: str, content: str) - str: 写入文件。是否允许由权限策略层负责。 Path(path).write_text(content, encodingutf-8) return fwritten: {path} tool(send_email, high) def send_email(to: str, subject: str, content: str) - str: 发送邮件。演示工具实际项目应接入企业邮件服务。 return femail sent to {to}: {subject}在这个模块里read_file内部做了路径前缀校验确保只能读取指定的数据目录。write_file和send_email则把权限判断交给上层策略这样职责更清晰。4.5 实现 Agent 调度执行器执行器是核心部分。它负责检查工具是否被允许、是否要人工审批、是否达到最大步数并把每一步写入审计日志。# 文件路径safe_agent/agent.py from audit_logger import AuditLogger from permission import PermissionPolicy from tools import TOOL_REGISTRY class SafeAgent: def __init__(self, policy: PermissionPolicy, auditor: AuditLogger, max_steps: int 10): self.policy policy self.auditor auditor self.max_steps max_steps def call_tool(self, tool_name: str, *args, **kwargs): if tool_name not in TOOL_REGISTRY: raise ValueError(funknown tool: {tool_name}) if not self.policy.can_call(tool_name): self.auditor.log(tool_denied, {tool: tool_name, args: list(args)}) raise PermissionError(ftool not allowed: {tool_name}) if self.policy.need_approval(tool_name): ok input(f需要人工审批是否允许执行 {tool_name}? (y/n): ).strip().lower() if ok ! y: self.auditor.log(tool_rejected_by_approval, {tool: tool_name, args: list(args)}) return operation rejected by user self.auditor.log(tool_called, {tool: tool_name, args: list(args), kwargs: kwargs}) return TOOL_REGISTRY[tool_name][handler](*args, **kwargs) def run(self, task: str): # 实际项目里这里应调用大模型生成行动序列。 # 本示例演示固定行动序列便于观察权限控制效果。 self.auditor.log(task_start, {task: task}) actions [ (read_file, (/tmp/safe_agent_data/input.txt,)), (write_file, (/tmp/safe_agent_data/output.txt, processed)), (send_email, (adminexample.com, report, please check)), ] for step, (tool_name, args) in enumerate(actions, 1): if step self.max_steps: self.auditor.log(max_steps_reached, {step: step}) break try: result self.call_tool(tool_name, *args) self.auditor.log(tool_result, {tool: tool_name, result: str(result)}) except PermissionError as e: self.auditor.log(tool_permission_error, {tool: tool_name, error: str(e)}) break self.auditor.log(task_end, {task: task})这里的max_steps是防止“潜伏”类问题的关键。无论模型如何规划一旦超过最大步数执行器就会强制停止避免无限循环。4.6 运行验证最后写一个演示入口创建测试目录和文件然后运行 Agent。# 文件路径safe_agent/main_demo.py from pathlib import Path from agent import SafeAgent from audit_logger import AuditLogger from permission import PermissionPolicy if __name__ __main__: data_dir Path(/tmp/safe_agent_data) data_dir.mkdir(exist_okTrue) input_file data_dir / input.txt input_file.write_text(这是一个安全 Agent 演示输入。, encodingutf-8) auditor AuditLogger(audit.jsonl) policy PermissionPolicy() policy.allow(read_file, approvalFalse) policy.allow(write_file, approvalTrue) agent SafeAgent(policy, auditor, max_steps5) agent.run(读取输入文件并生成处理结果)运行命令cd safe_agent python main_demo.py交互过程如下[audit] task_start: {task: 读取输入文件并生成处理结果} [audit] tool_called: {tool: read_file, args: [(/tmp/safe_agent_data/input.txt,)], kwargs: {}} [audit] tool_result: {tool: read_file, result: 这是一个安全 Agent 演示输入。} 需要人工审批是否允许执行 write_file? (y/n): n [audit] tool_rejected_by_approval: {tool: write_file, args: [(/tmp/safe_agent_data/output.txt, processed)]}这里演示了两种拦截write_file被人工审批拒绝send_email因为没有被权限策略放行会在调用时抛出PermissionError并终止任务。打开audit.jsonl可以清楚看到每一步的操作记录{event_type: task_start, timestamp: 1735970000, data: {task: 读取输入文件并生成处理结果}} {event_type: tool_called, timestamp: 1735970000, data: {tool: read_file, args: [/tmp/safe_agent_data/input.txt], kwargs: {}}} {event_type: tool_rejected_by_approval, timestamp: 1735970001, data: {tool: write_file, args: [/tmp/safe_agent_data/output.txt, processed]}}4.7 如何接入真实大模型上面的示例中行动序列是固定的。真实场景里行动序列应该由大模型根据任务动态生成。接入思路是让模型输出一个结构化的 JSON 动作例如{tool: read_file, args: [/tmp/safe_agent_data/input.txt]}然后由执行器解析并调用。以下是使用 OpenAI 兼容接口的最小示意注意 API Key 必须从环境变量读取import json import os from openai import OpenAI client OpenAI(api_keyos.getenv(OPENAI_API_KEY)) def plan_next_action(task: str) - dict: resp client.chat.completions.create( modelgpt-4o-mini, # 请根据你的账号可用模型调整 messages[ {role: system, content: 你是一个安全Agent只能输出JSON动作。}, {role: user, content: task}, ], response_format{type: json_object}, ) return json.loads(resp.choices[0].message.content)拿到动作后把它交给SafeAgent.call_tool执行。只要权限策略、审计日志和人工审批还在执行器这一层模型生成什么内容都不会直接触碰系统资源这就是 Harness 设计思想的简化版。5. 常见问题与排查思路5.1 高频问题速查表在 Agent 开发和安全运维中很多问题具有高度相似性。下表列出常见问题、可能原因和解决思路。问题现象常见原因解决思路Agent 执行超时工具调用未设置超时模型服务响应慢给每个工具调用设置超时和最大重试次数Agent 陷入死循环缺乏最大步数限制模型反复生成同一动作引入 max_steps、熔断机制和人工终止入口工具被外部数据诱导Prompt 注入外部内容被当成指令校验工具输出隔离不可信数据关键操作人工审批API Key 泄露Key 硬编码、被打印到日志改用环境变量或密钥管理服务立即吊销并轮换审计日志不全只记结果不记过程每次工具调用前后都记录入参、出参和调用链多 Agent 互调失控缺少服务间认证和独立身份每个 Agent 使用独立身份限制调用范围5.2 超时与异常终止排查在运行 Agent 时我们经常会看到类似这样的错误信息The agent execution provider did not respond in time.以及Agent execution terminated due to error.这两类错误通常不是模型“拒绝回答”而是运行层面的问题。排查顺序建议如下先检查执行器所在进程的资源占用看是否存在 CPU、内存或文件句柄耗尽再检查工具调用耗时。如果某个工具没有设置超时外部服务挂起会导致整个 Agent 卡住查看审计日志定位最后一次成功的工具调用是哪一步卡在哪一步检查重试逻辑。如果代码在失败后无条件重试可能触发雪崩式调用检查是否有看护进程主动终止了任务。很多生产系统会对超时任务强制 kill此时需要先分析终止原因再决定是调大超时还是优化任务拆分。这里的关键是“可观测性”。没有完整日志你会连错误发生在哪一层都判断不出来。5.3 事故应急建议如果真的发生了 Agent 安全事故按以下方式响应更稳妥第一时间吊销相关 API Key关闭高危工具权限不要为了继续跑业务而保留权限保留现场备份日志和记忆快照不要急着“清理数据”在隔离环境里复现问题确认是注入、权限还是代码缺陷完成根因分析后再逐步恢复服务并补充对应的监控告警。6. Agent 安全最佳实践与工程建议6.1 最小权限与凭据管理Agent 的每一个身份标识都应该遵循最小权限原则。只放行完成任务必需的工具和接口并为不同任务创建不同权限的 Key而不是所有 Agent 共用一个管理员 Key。API Key 管理是重中之重。使用环境变量、容器 Secret 或专门的密钥管理服务绝不要写在代码仓库、配置文件和日志中。还要定期轮换 Key并给 Key 设置预算上限和调用频次限制防止异常消耗。6.2 隔离与沙箱只要条件允许Agent 执行环境应该与业务主环境隔离。可以使用容器、虚拟机或独立用户账号运行 Agent并限制其文件系统、网络和系统调用权限。沙箱的作用是缩小爆炸半径。即使 Agent 被注入恶意指令它也只能影响隔离环境无法直接触及生产数据库或核心服务器。网络层面也应使用白名单只允许 Agent 访问必要的域名和内部服务。6.3 可观测性与告警没有监控的 Agent 就像一个黑盒。生产环境中的 Agent 日志至少应覆盖任务开始、结束、失败、重试每个工具的调用入参和出参权限拒绝和审批拒绝事件模型请求响应耗时和 Token 消耗。告警规则要结合业务风险设计。例如凌晨出现大量写操作、同一个任务在短时间内重复执行、高危工具被调用频率异常上升这些事件应立即触发通知。6.4 长期运行任务的治理针对“潜伏两个月”这类长期运行风险建议为每个 Agent 任务设置最大步数和总运行时长定期心跳报告任务依赖关系检查无人工确认时禁止执行高危写操作任务版本记录便于回滚。长期任务不能“发起之后不管”。即使 Agent 确实需要长时间运行也应该有看护进程定期检查状态并在异常时自动暂停或终止。6.5 开发与协作流程安全能力最好前置到开发流程中而不是等出事后补救。建议在 Agent 项目中加入以下环节每次新增工具必须评估权限级别和风险并在代码评审中说明高风险操作必须提供人工审批接口不能只有代码层校验配置和权限变更走申请审批流程保留审批记录上线前在测试环境用模拟数据做安全验证发布采用灰度策略发现问题快速回滚。7. 总结与进阶学习方向Agent 安全事故并不是玄学。把“权限、隔离、审计、审批”四件套落实到位大多数所谓“潜伏”和“联手作案”都很难发生。本文从概念、风险面、复盘方法论到实战示例完整走了一遍 Agent 安全的设计思路。如果接下来想继续深入我建议优先学习这几个方向一是研究 OpenAI Codex Harness 等开源 Agent 运行框架的设计理解它如何在模型和系统资源之间做边界约束二是系统掌握提示词注入的防护方法这是 Agent 攻击面中最常见的一环三是尝试给 Agent 系统补充监控告警和自动化安全测试把被动复盘转成主动防御。最好的一种学习方式是把你手里的 Agent 项目按本文的示例改造一遍加一个审计日志模块收紧工具白名单给写操作配置人工审批设置最大步数限制。改完之后再看日志你会发现原本不可控的 Agent 突然变得“透明”了。