最近我带的Agent应用一上生产,最先顶不住的已经不是模型回答得对不对,而是它在后台到底干了什么。一套AI Agent如果没有把Harness Engineering那层监控告警做好,异常行为实时检测基本就是靠人肉盯日志,等发现问题时,钱已经烧掉一大截,或者某个越权动作已经跑完了。这里的Harness Engineering,我更愿意把它理解为“承载Agent推理循环和工具调用的工程骨架”——模型能力、工具能力、记忆能力、权限能力全被它穿在一起。监控告警要做的就是在这层骨架上装一套仪表盘,对异常行为做实时检测:循环打转、工具风暴、目标漂移、参数越界、内容注入、成本失控,必须在线发现、在线响应。这篇文章我会把整套方案的思路、架构、实现细节和我踩过的坑从头到尾说清楚,适合正在做Agent平台、机器人流程自动化、智能客服或自动化编排的工程师参考。1. Agent Harness 到底是什么:为什么监控要单独做1.1 先搞清楚一件事:Harness 管的是哪一段很多人做Agent应用,习惯性把精力全放在模型提示词和工具定义上,觉得只要模型够聪明、工具够多,系统就自然稳定。但实际跑起来会发现,真正的风险根本不在单次模型输出的质量,而在整条“感知—决策—行动”链路的编排过程。Agent Harness 管的核心就是这一段:模型怎么被调用、工具怎么被选择、参数怎么被校验、记忆怎么被读写、上下文怎么被组装、多轮循环怎么被终止。你可以把它想象成给Agent穿上的安全带和脚手架,模型是引擎,Harness是车身和电路系统,车身不牢,引擎再强也容易出事。监控告警如果只盯着模型服务的QPS、错误率,看不到车身里的电路异常,那就等于仪表盘上全是假数据。我见过很多团队第一版Agent平台,工具调用日志散落在各个服务里,连一个统一的traceId都没有。出了问题只能逐台机器翻日志,靠人工把时间线拼起来。这种状态根本谈不上监控告警,更谈不上实时检测。所以这篇内容的出发点只有一个:把Harness这层当成一等公民来治理,所有Agent行为都要有结构化的观测数据。1.2 通用监控为什么不能直接套用常规监控体系擅长的是资源维度和服务维度:CPU、内存、QPS、延迟、错误码。这些指标对Agent应用当然重要,但它们描述的是“系统健不健康”,不是“Agent在干什么”。举个例子,一个Agent可能所有后端指标都正常,容器CPU平稳、接口延迟很低、没有报错,但它在循环里反复调用同一个搜索工具,每一次都在问相似的问题,跑的搜索次数已经超过正常任务几百倍。传统监控根本不会觉得有异常,因为每一次工具调用本身都是成功的HTTP请求。只有从“Agent行为轨迹”的视角去看,才能发现这是典型的循环失控。再举个例子,Agent输出看起来完全正常,但它在这一轮偷偷调用了超出权限范围的删除接口。如果只监控服务端错误率,删除接口返回200,一切正常。但Harness层的权限检查记录会告诉你,这次调用越权了。这就是为什么通用监控不能直接套用:它监控的是请求-响应,而Agent需要的监控是“行为-轨迹-边界”。1.3 谁最需要这套检测能力如果你只是做个Demo演示Agent能力,那确实不需要这套体系。但如果你是下面这几种情况,我建议你把异常行为实时检测当成优先级最高的事情来投入。一类是做Agent平台的,平台上跑着大量不同用途的Agent,任何一个失控都可能影响到其他租户,出一次安全事件就可能导致整个平台没人敢用。还有一类是把Agent接入核心业务系统的,比如客服、运营、自动执行任务,Agent动作直接操作真实业务数据,越权、错删、误发邮件这类风险是企业完全不能接受的。另外,凡是对成本敏感、Agent调用量大的团队也需要这套检测,因为很多异常行为的第一表现就是Token消耗和API成本飙升,没有监控,月底账单能让人失眠。2. 异常行为实时检测到底要抓什么:四类核心目标2.1 轨迹级异常:循环、风暴与目标漂移轨迹级异常关注的是Agent“一连串动作”的整体形态,单看任何一个动作都正常,连起来看就明显不对劲。循环打转是最常见的一种。Agent对同一个问题反复尝试不同的表达方式,工具调用一轮接一轮,每次都拿到相似但无用的结果,但Harness层没有步数上限,也没有循环识别,于是它就一直转下去。早期我见过一个搜索型Agent在单次任务里循环调用搜索工具五十多次,用户早就离开页面了,它还在后台继续跑。工具调用风暴是另一种形态,往往发生在Agent试图用暴力枚举去解决问题的时候。比如某个Agent为了找一个订单号,短时间内在订单列表接口上翻了几百页。这类行为即使参数合法、权限通过,也是对下游系统的真实冲击。目标漂移相对隐蔽。用户最初的需求是查询天气,但Agent在多轮对话中逐渐偏离,开始调用和用户目标完全无关的接口。这种漂移可能是模型的理解问题,也可能是被中间输入故意带偏。检测思路一般是对动作序列做语义分析,看调用的工具集是否仍与初始目标相关,而不是只看最后一个动作的对错。2.2 工具调用级异常:参数越界、高频失败与权限逃逸工具调用是Agent改变世界的方式,所以工具调用级异常直接关系到业务安全和系统稳定。参数越界是最常见的一类。Agent本应查询库存,却把删除操作带进了参数;或者用户只提供了城市名,Agent却自动补上了管理员的身份字段。这类问题的本质是Harness在工具调用时缺少严格的参数schema校验,让模型自认为合理的输入直接穿透到了业务接口。高频失败也需要重点盯。Agent反复调用一个工具,每次都被业务侧返回错误,可能因为参数格式不对、权限不够,或者下游接口临时故障。单看一次失败没什么,但如果两分钟内同一个Agent对同一个工具连续失败几十次,说明Agent已经陷入了“错误重试—继续失败—继续重试”的补偿循环,对下游的冲击可能比成功调用还大。权限逃逸是底线问题。Harness在配置工具时声明了Agent可调用的范围,但实际执行时模型可能会绕过预期路径,尝试调用未授权的工具或携带特殊参数。这类行为必须实时告警,并且默认触发阻断,不能只记录不处理。2.3 输出与内容级异常:幻觉、注入与敏感信息外泄内容级异常检测关注的是模型输出的文本本身。幻觉在这里不只是“事实错误”,还包括输出结构的崩坏。比如约定Agent只能输出JSON格式,但某轮输出突然变成大段Markdown,调用方根本解析不了。或者Agent在回答中重复输出同一段话,语义卡死在一个局部。注入检测也很关键。外界输入可能在用户消息、文档内容甚至工具返回结果中夹带“忽略之前指令”“现在你是另一个角色”这类对抗文本,诱导Agent执行非预期行为。实时检测这类内容需要在输入和工具返回两处同时设防。敏感信息外泄是更严重的红线。客服Agent在处理用户问题时,可能在输出中带出业务库里的手机号、身份证号、订单明细,哪怕模型没有恶意,这种外泄也会直接变成合规事故。检测方案通常是在Harness层的输出出口做一遍规则加模型的组合过滤。2.4 成本与资源级异常:Token消耗与并发激增成本异常往往最后才被业务感知,因为账单是滞后的。但如果你等账单到了再反应,损失已经发生了。最典型的场景是某个Agent因为循环失控,单次任务消耗了正常情况下几百倍的Token。尤其当模型输出一个很长的列表,Agent又反复把它作为下一轮输入带回模型时,输入Token会指数级膨胀。另一个场景是并发激增,某个入口被外部用户大量触发,或者Agent自身进入了并发队列的雪崩式扩张。这类检测需要把Token消耗、API调用次数、单任务时长、并发Agent数量打成指标,做动态基线和实时阈值。成本异常不一定代表Agent行为有问题,但它一定是行为异常的先行指标,抓不到这一层,很多问题会在小规模时期被忽略。3. 实时检测管线的技术选型与架构设计3.1 埋点先行:没有全量轨迹就没有实时检测任何检测方案的前提都是数据。不要先选告警系统,不要先写规则引擎,先把Harness层的埋点做全做对。在Harness层,至少要给四类事件打点:一是模型请求事件,包括输入提示词、模型输出、Token数、响应时间;二是工具调用事件,包括工具名、参数、返回值摘要、权限检查结果;三是记忆读写事件,包括读写动作、记忆片段摘要、命中情况;四是Agent生命周期事件,包括任务开始、任务结束、步数、单步耗时。每一类事件都必须携带同一个traceId,用来把单个任务内的所有动作串成一条轨迹。事件的字段要提前设计好,宁可多记不要漏记,因为事后补埋点的成本远比一开始设计高得多。我在实际项目里最救命的经验是:把权限检查结果也放进工具调用事件里,这样后续做告警时可以直接引用,不用再翻权限系统的日志。3.2 三档检测机制:硬规则、统计基线、语义模型检测机制建议分成三档,不是所有异常都要上模型。第一档是硬规则,适合确定性强、不能妥协的底线场景。比如最大步数限制、禁止调用名单、参数白名单、私密字段正则。硬规则的优势是延迟低、解释性强,命中就是命中,没有概率问题。缺点是覆盖不全,只能抓已知异常。第二档是统计基线,适合刹车型的异常,比如“这个Agent平时代价均值是200 Token,现在5分钟内翻了10倍”。你不用理解Agent在干什么,数据变化本身就是信号。常用的算法有滑动窗口均值、指数加权移动平均、四分位距等,计算量小,适合在线实时跑。第三档是语义模型,适合规则覆盖不了的软性问题,比如目标漂移、注入话术、输出语义异常。这类检测需要把文本变成向量,再做分类或相似度判别。语义模型成本高、延迟高,适合放在高优先级线路的旁路或异步复核链路里。三档的衔接逻辑是从便宜到贵,先用硬规则兜底,再用统计基线抓漂移,语义模型做兜底检查和深度排查。不要一上来就在主链路上塞一个大模型推理,线上根本扛不住。3.3 事件流链路与选型参考实时检测的整体链路可以概括为:埋点 → 事件消息组件 → 流式处理 → 规则引擎 → 告警中心 → 通知/阻断。对于中小规模团队,建议先用开源的消息队列组件承载事件流,加上一套流式处理程序做窗口聚合和特征计算,再配一个轻量规则引擎。这套组合足够覆盖每分钟几万级Agent动作的吞吐。如果数据量更大、需要对状态做更复杂的流式计算,可以引入专门的流式计算框架或云厂商的托管流服务。这里有个容易踩的坑:很多人想把检测功能直接塞进现有的指标监控系统里,虽然也支持自定义指标和告警,但Agent的轨迹异常往往需要窗口聚合和跨事件关联,硬塞进去会导致规则表达受限或者查询慢。我建议把“事件型告警”和“指标型告警”分开处理:指标型告警仍然用现成监控系统,事件型告警走独立链路。4. 从零搭建:一条告警是怎么从动作事件变成通知的4.1 在Harness层构造结构化动作事件先看一段我在Harness调用工具时构造事件对象的示例。这里的思路是所有工具调用统一通过一个出口执行,在执行前后收集上下文,形成标准事件。# 示例:Harness 工具调用出口的事件构造 from dataclasses import dataclass, field from typing import Any, Optional @dataclass class AgentActionEvent: trace_id: str agent_id: str action_type: str # tool_call / model_call / memory_op action_name: str params: dict param_fingerprint: str # 规范化后用于循环检测 latency_ms: float token_cost: int permission_result: str # allow / deny / review status: str # success / failed created_at: float def execute_tool_with_event(ctx, tool_name, input_params): permission_result = ctx.check_permission(tool_name, input_params) start = time.time() status, raw_result = ctx.dispatch_tool(tool_name, input_params) latency = (time.time() - start) * 1000 event = AgentActionEvent( trace_id=ctx.trace_id, agent_id=ctx.agent_id, action_type="tool_call", action_name=tool_name, params=input_params, param_fingerprint=normalize_params(input_params), latency_ms=latency, token_cost=ctx.count_token(input_params, raw_result), permission_result=permission_result, status="success" if status else "failed", created_at=time.time() ) ctx.emit_event(event) return raw_result这段代码看起来简单,但它确立了几个关键原则:工具调用必须有统一出口,权限结果必须进入事件,每次调用必须生成可用于检测的指纹。指纹的作用在循环检测时会体现出来,我现在先埋个伏笔。4.2 硬规则检测:循环识别与调用风暴有了结构化事件,硬规则可以直接在消费端实现。这里以循环识别为例,核心是“规范化指纹 + 滑动窗口计数”。# 示例:基础版循环检测 from collections import deque LOOP_WINDOW_SIZE = 20 LOOP_THRESHOLD = 5 loop_deque = {} def check_loop(agent_id, action_name, param_fingerprint): key = f"{action_name}|{param_fingerprint}" if agent_id not in loop_deque: loop_deque[agent_id] = deque(maxlen=LOOP_WINDOW_SIZE) loop_deque[agent_id].append(key) # 统计当前窗口内相同动作指纹出现次数 count = sum(1 for k in loop_deque[agent_id] if k == key) if count >= LOOP_THRESHOLD: return True return False这里最容易忽略的就是 param_fingerprint 的规范化方式。我第一次上线时就吃了亏:Agent 连续调用同一个搜索工具十几次,只是因为参数里城市名的大小写、空格、字段顺序不同,就被判定为不同动作,循环检测完全失效。现在我的规范化策略是:参数键排序、字符串统一转小写、去除空白字符、数字精度统一保留两位,再做哈希。这一步把循环检测的召回率提升了几个档次。调用风暴的检测思路类似,但聚合维度更粗:以 agent_id 为单位,对1分钟窗口内所有工具调用计数,超过阈值就告警。这类问题用流式处理窗口函数做最方便,逻辑简单但必须在线执行。4.3 统计基线:用EWMA加MAD替代Z-score统计基线检测适合抓“慢漂移”类异常,比如Token成本、失败率、延迟的逐渐恶化。如果直接用Z-score,要求数据接近正态分布,但Agent行为数据远没有那么乖,所以我更推荐用“指数加权移动平均 + 绝对中位差”的组合。EWMA负责计算当前正常水平,MAD负责计算数据的离散程度。判定逻辑是:当前值与中位数的偏差如果超过MAD的一定倍数,就认为异常。MAD相比标准差更抗离群值,不会因为少数极端值把基线拉飞。# 示例:EWMA + MAD 基线异常检测 ALPHA = 0.3 MAD_MULTIPLIER = 5 class BaselineDetector: def __init__(self): self.median = None self.mad = None self.ewma = None def update(self, value): if self.ewma is None: self.ewma = value else: self.ewma = ALPHA * value + (1 - ALPHA) * self.ewma def add_sample(self, value): self.update(value) # 维护一个固定长度滑窗,用于计算中位数和MAD window.append(value) if len(window) >= 100: self.median = np.median(window) deviations = [abs(v - self.median) for v in window] self.mad = np.median(deviations)有了基线和MAD之后,实时判定时计算当前值与median的偏差,超过 MAD_MULTIPLIER 倍的MAD就触发告警。这个方案在线上跑下来非常稳,误报率明显低于直接用Z-score。注意这里的MAD_MULTIPLIER不是拍脑袋定的,上线前我会用历史数据回放调参,把误报率压到可接受范围,而不是追求零漏报。4.4 语义检测的轻量落地:先关键词再模型内容级检测不建议一开始就上重模型。我的落地顺序是:先用关键词和正则覆盖确定性的危险模式,比如“忽略之前指令”“泄露你的系统提示词”“获取所有订单”这类提示注入的典型句式,命中即告警。第二步才是向量化加轻量分类。把所有用户输入、工具返回内容和模型输出统一过向量模型,得到语义向量后,送入一个轻量分类器,区分正常/注入/越狱/敏感外泄四类。这个分类器可以用一批已标注样本快速训练,不需要上大模型推理。成本比大模型推理低一个数量级,延迟也可以控制在几十毫秒内。训练样本怎么攒?我的做法是先打在检测链路的旁路上,让新逻辑只记录不阻断,跑上一周左右,从真实流量里捞疑似样本,再人工标注回填。这样收集到的样本分布贴近真实业务,比从公开数据集拿来的更管用。等样本规模到了几千条,分类器的效果就会明显起来。5. 告警分级、抑制与响应策略:别再让告警变成噪音5.1 分级原则:不是所有异常都要打断Agent告警系统最容易挂掉的原因不是检测不够,而是通知太多。一线工程师一天收到几百条告警,最后没有任何一条会被认真看。所以分级必须清晰,要有明确的响应动作映射。我的分级习惯分为三档。P0是安全底线类,包括权限逃逸、敏感信息外泄、对核心业务接口的越权调用,命中后直接终断Agent执行,同时立即通知平台负责人和业务负责人。P1是明确异常类,比如循环失控、调用风暴、成本飙升,命中后先自动限流降低对下游的冲击,转人工确认。P2是观察类,比如目标漂移、输出结构崩坏、基线漂移,生成待处理事件,由业务在告警中心确认是否介入。一张简化的映射表大概长这样:级别典型场景实时响应动作通知方式P0权限逃逸、数据外泄立即终止任务、冻结Agent电话/短信+即时消息P1循环失控、调用风暴、成本激增限流降级、转人工确认即时消息+工单P2目标漂移、输出异常、基线偏离生成事件、等待确认告警中心列表分级的核心目的不是给告警贴标签,而是让每个级别都有明确的动作,避免“收到告警不知道怎么办”的尴尬。5.2 告警聚合与抑制:用指纹把相同问题合并上线初期最容易翻车的就是告警风暴。一个Agent开始循环,每分钟可能会触发几十条内容相同的告警,直接把IM群刷爆。抑制和聚合必须从一开始就设计进去。我常用的做法是给告警事件算一个“告警指纹”,指纹可以是“detector_id + agent_id + action_name + 异常特征摘要”。同一指纹在5分钟窗口内只发送一条通知,后续命中只做计数和状态更新。对多个Agent同时出现相同模式的情况,还可以做跨Agent聚合,比如只发一条总告警加上受影响Agent列表。除了频控,还要设计自动恢复机制。当一条P1级告警触发了限流,Agent后续恢复正常后,系统应自动把告警状态更新为已恢复,而不是让工程师手动关闭。没有恢复机制,群里会堆满“僵尸告警”,没过多久大家就对告警失去敏感度了。5.3 联动动作:自动降级、熔断与人工回放告警发出后,检测链路的价值需要靠响应动作来兑现。我最推荐的是“自动降级”和“半阻断”策略:检测到异常后,先把Agent切到需要人工确认的模式,所有高风险工具调用暂停,由工程师在页面上确认后放行或终止。这样既能拦住风险,又不至于出现误杀正常流程的尴尬。另一个重要能力是人工回放。告警中心里至少要能一键看到这个Agent的完整轨迹时间线,按traceId拉出所有事件,按时间排序渲染成可视化时间线。工程师收到告警后,不是去看一堆JSON日志,而是像看视频一样从头到尾过一遍Agent的每一步动作。这个能力看着不起眼,实际排查问题时效率能差出好几倍。自动终断要谨慎开放。初期我只对P0场景做自动终断,其他异常一律走限流和人工确认,经过一段时间观察、把误报率压到足够低之后,才逐步打开终断权限。6. 实操踩坑与避坑清单6.1 常见问题速查表现象可能原因解决办法告警刷屏缺少指纹聚合与通知限频引入告警指纹、5分钟窗口内合并同类告警检测延迟高语义模型直接串入在线链路分层处理,硬规则在线、语义模型旁路异步循环检测漏报参数未规范化,大小写/顺序差异绕过指纹参数排序、转小写、去空白后再生成指纹误报太多阈值设定不合理,基线被极端值拉偏用MAD替代标准差,结合历史数据回放调参权限异常找不到记录权限检查结果没有写入工具调用事件在Harness统一出口把permission_result写入事件成本异常发现太晚Token事件没有实时进入检测链路在模型调用出口同步构造model_call事件并发送目标漂移误判只比较“初始目标”与“当前输入”的向量相似度改为检测“动作使用的工具集合是否偏离目标相关工具集”6.2 我踩过的坑:三条真实教训第一个坑是循环检测第一次上线时,我以为把工具名和原始参数放进指纹就万事大吉了。结果Agent连续十几次调用同一个工具,只是参数里的大小写和空格不同,全部绕过检测。当时的修复方案就是前面提到的规范化流程:键排序、转小写、去空白、数字精度归一。这个改造上线后,循环检测的召回率立刻上了一个台阶。第二个坑和延迟有关。当时为了把检测做得“更智能”,直接把语义模型放到了实时链路上,结果单次工具调用整体延迟从120毫秒飙到900多毫秒,线上体验瞬间崩溃。后来我把检测拆成两档:硬规则和统计基线在线实时计算,语义模型放到异步旁路做深度复核。这样既有实时防护,又不牺牲性能。第三个坑是目标漂移的误报。一开始我对“初始目标”和“当前输入”做向量相似度,想判断Agent有没有跑偏,结果用户只是正常补充需求,相似度就骤降,误报不断。后来调整了思路,不再盯“当前输入像不像初始目标”,而是盯“当前动作涉及的工具集合是否属于初始目标关联工具集”。改动后误报大幅下降,检测逻辑也更符合实际业务含义。6.3 上线建议与迭代节奏如果你是从零开始建这套监控告警,我的建议是“先旁路,再半阻断,最后终断”。第一周把所有检测器全部旁路运行,只记录、只告警、不阻断,同时通过回放排查误报。第二周打开P1级的限流和人工确认,至少让高风险场景能够被人工介入。等误报率降到团队可以接受的范围,再逐步打开终断能力。阈值的设定不要纯靠拍脑袋。拿一周的真实历史数据回放,把不同阈值下的误报率和漏报率算出来,画一条曲线,找到平衡点。这个过程虽然麻烦,但一次做好,后面节省的时间远超投入。迭代节奏上,我建议先做两三条“保险线”级别的硬规则,比如最大步数、禁止调用名单、权限逃逸检测,先把底线守住。数据闭环跑通之后,再逐步增加统计基线检测和语义模型。不要想着一次就把所有异常都覆盖到,检测体系是滚雪球式增长的,每加一条规则都需要观察误报、调试阈值、标注样本,形成自己的数据飞轮之后才会越来越顺。根据我个人排障这么多轮的经验,这套系统最关键的其实不是算法多复杂,而是整个数据链路通不通。只要每个Agent动作都有标准事件、每个事件都有traceId、每条告警都能回放现场,后面的所有优化都会变得非常顺畅。哪怕一开始只有两条硬规则,这个闭环跑起来之后,你加的每一条规则都是在给整套体系增加长期价值。
最近我带的Agent应用一上生产,最先顶不住的已经不是模型回答得对不对,而是它在后台到底干了什么。一套AI Agent如果没有把Harness Engineering那层监控告警做好,异常行为实时检测基本就是靠人肉盯日志,等发现问题时,钱已经烧掉一大截,或者某个越权动作已经跑完了。这里的Harness Engineering,我更愿意把它理解为“承载Agent推理循环和工具调用的工程骨架”——模型能力、工具能力、记忆能力、权限能力全被它穿在一起。监控告警要做的就是在这层骨架上装一套仪表盘,对异常行为做实时检测:循环打转、工具风暴、目标漂移、参数越界、内容注入、成本失控,必须在线发现、在线响应。这篇文章我会把整套方案的思路、架构、实现细节和我踩过的坑从头到尾说清楚,适合正在做Agent平台、机器人流程自动化、智能客服或自动化编排的工程师参考。1. Agent Harness 到底是什么:为什么监控要单独做1.1 先搞清楚一件事:Harness 管的是哪一段很多人做Agent应用,习惯性把精力全放在模型提示词和工具定义上,觉得只要模型够聪明、工具够多,系统就自然稳定。但实际跑起来会发现,真正的风险根本不在单次模型输出的质量,而在整条“感知—决策—行动”链路的编排过程。Agent Harness 管的核心就是这一段:模型怎么被调用、工具怎么被选择、参数怎么被校验、记忆怎么被读写、上下文怎么被组装、多轮循环怎么被终止。你可以把它想象成给Agent穿上的安全带和脚手架,模型是引擎,Harness是车身和电路系统,车身不牢,引擎再强也容易出事。监控告警如果只盯着模型服务的QPS、错误率,看不到车身里的电路异常,那就等于仪表盘上全是假数据。我见过很多团队第一版Agent平台,工具调用日志散落在各个服务里,连一个统一的traceId都没有。出了问题只能逐台机器翻日志,靠人工把时间线拼起来。这种状态根本谈不上监控告警,更谈不上实时检测。所以这篇内容的出发点只有一个:把Harness这层当成一等公民来治理,所有Agent行为都要有结构化的观测数据。1.2 通用监控为什么不能直接套用常规监控体系擅长的是资源维度和服务维度:CPU、内存、QPS、延迟、错误码。这些指标对Agent应用当然重要,但它们描述的是“系统健不健康”,不是“Agent在干什么”。举个例子,一个Agent可能所有后端指标都正常,容器CPU平稳、接口延迟很低、没有报错,但它在循环里反复调用同一个搜索工具,每一次都在问相似的问题,跑的搜索次数已经超过正常任务几百倍。传统监控根本不会觉得有异常,因为每一次工具调用本身都是成功的HTTP请求。只有从“Agent行为轨迹”的视角去看,才能发现这是典型的循环失控。再举个例子,Agent输出看起来完全正常,但它在这一轮偷偷调用了超出权限范围的删除接口。如果只监控服务端错误率,删除接口返回200,一切正常。但Harness层的权限检查记录会告诉你,这次调用越权了。这就是为什么通用监控不能直接套用:它监控的是请求-响应,而Agent需要的监控是“行为-轨迹-边界”。1.3 谁最需要这套检测能力如果你只是做个Demo演示Agent能力,那确实不需要这套体系。但如果你是下面这几种情况,我建议你把异常行为实时检测当成优先级最高的事情来投入。一类是做Agent平台的,平台上跑着大量不同用途的Agent,任何一个失控都可能影响到其他租户,出一次安全事件就可能导致整个平台没人敢用。还有一类是把Agent接入核心业务系统的,比如客服、运营、自动执行任务,Agent动作直接操作真实业务数据,越权、错删、误发邮件这类风险是企业完全不能接受的。另外,凡是对成本敏感、Agent调用量大的团队也需要这套检测,因为很多异常行为的第一表现就是Token消耗和API成本飙升,没有监控,月底账单能让人失眠。2. 异常行为实时检测到底要抓什么:四类核心目标2.1 轨迹级异常:循环、风暴与目标漂移轨迹级异常关注的是Agent“一连串动作”的整体形态,单看任何一个动作都正常,连起来看就明显不对劲。循环打转是最常见的一种。Agent对同一个问题反复尝试不同的表达方式,工具调用一轮接一轮,每次都拿到相似但无用的结果,但Harness层没有步数上限,也没有循环识别,于是它就一直转下去。早期我见过一个搜索型Agent在单次任务里循环调用搜索工具五十多次,用户早就离开页面了,它还在后台继续跑。工具调用风暴是另一种形态,往往发生在Agent试图用暴力枚举去解决问题的时候。比如某个Agent为了找一个订单号,短时间内在订单列表接口上翻了几百页。这类行为即使参数合法、权限通过,也是对下游系统的真实冲击。目标漂移相对隐蔽。用户最初的需求是查询天气,但Agent在多轮对话中逐渐偏离,开始调用和用户目标完全无关的接口。这种漂移可能是模型的理解问题,也可能是被中间输入故意带偏。检测思路一般是对动作序列做语义分析,看调用的工具集是否仍与初始目标相关,而不是只看最后一个动作的对错。2.2 工具调用级异常:参数越界、高频失败与权限逃逸工具调用是Agent改变世界的方式,所以工具调用级异常直接关系到业务安全和系统稳定。参数越界是最常见的一类。Agent本应查询库存,却把删除操作带进了参数;或者用户只提供了城市名,Agent却自动补上了管理员的身份字段。这类问题的本质是Harness在工具调用时缺少严格的参数schema校验,让模型自认为合理的输入直接穿透到了业务接口。高频失败也需要重点盯。Agent反复调用一个工具,每次都被业务侧返回错误,可能因为参数格式不对、权限不够,或者下游接口临时故障。单看一次失败没什么,但如果两分钟内同一个Agent对同一个工具连续失败几十次,说明Agent已经陷入了“错误重试—继续失败—继续重试”的补偿循环,对下游的冲击可能比成功调用还大。权限逃逸是底线问题。Harness在配置工具时声明了Agent可调用的范围,但实际执行时模型可能会绕过预期路径,尝试调用未授权的工具或携带特殊参数。这类行为必须实时告警,并且默认触发阻断,不能只记录不处理。2.3 输出与内容级异常:幻觉、注入与敏感信息外泄内容级异常检测关注的是模型输出的文本本身。幻觉在这里不只是“事实错误”,还包括输出结构的崩坏。比如约定Agent只能输出JSON格式,但某轮输出突然变成大段Markdown,调用方根本解析不了。或者Agent在回答中重复输出同一段话,语义卡死在一个局部。注入检测也很关键。外界输入可能在用户消息、文档内容甚至工具返回结果中夹带“忽略之前指令”“现在你是另一个角色”这类对抗文本,诱导Agent执行非预期行为。实时检测这类内容需要在输入和工具返回两处同时设防。敏感信息外泄是更严重的红线。客服Agent在处理用户问题时,可能在输出中带出业务库里的手机号、身份证号、订单明细,哪怕模型没有恶意,这种外泄也会直接变成合规事故。检测方案通常是在Harness层的输出出口做一遍规则加模型的组合过滤。2.4 成本与资源级异常:Token消耗与并发激增成本异常往往最后才被业务感知,因为账单是滞后的。但如果你等账单到了再反应,损失已经发生了。最典型的场景是某个Agent因为循环失控,单次任务消耗了正常情况下几百倍的Token。尤其当模型输出一个很长的列表,Agent又反复把它作为下一轮输入带回模型时,输入Token会指数级膨胀。另一个场景是并发激增,某个入口被外部用户大量触发,或者Agent自身进入了并发队列的雪崩式扩张。这类检测需要把Token消耗、API调用次数、单任务时长、并发Agent数量打成指标,做动态基线和实时阈值。成本异常不一定代表Agent行为有问题,但它一定是行为异常的先行指标,抓不到这一层,很多问题会在小规模时期被忽略。3. 实时检测管线的技术选型与架构设计3.1 埋点先行:没有全量轨迹就没有实时检测任何检测方案的前提都是数据。不要先选告警系统,不要先写规则引擎,先把Harness层的埋点做全做对。在Harness层,至少要给四类事件打点:一是模型请求事件,包括输入提示词、模型输出、Token数、响应时间;二是工具调用事件,包括工具名、参数、返回值摘要、权限检查结果;三是记忆读写事件,包括读写动作、记忆片段摘要、命中情况;四是Agent生命周期事件,包括任务开始、任务结束、步数、单步耗时。每一类事件都必须携带同一个traceId,用来把单个任务内的所有动作串成一条轨迹。事件的字段要提前设计好,宁可多记不要漏记,因为事后补埋点的成本远比一开始设计高得多。我在实际项目里最救命的经验是:把权限检查结果也放进工具调用事件里,这样后续做告警时可以直接引用,不用再翻权限系统的日志。3.2 三档检测机制:硬规则、统计基线、语义模型检测机制建议分成三档,不是所有异常都要上模型。第一档是硬规则,适合确定性强、不能妥协的底线场景。比如最大步数限制、禁止调用名单、参数白名单、私密字段正则。硬规则的优势是延迟低、解释性强,命中就是命中,没有概率问题。缺点是覆盖不全,只能抓已知异常。第二档是统计基线,适合刹车型的异常,比如“这个Agent平时代价均值是200 Token,现在5分钟内翻了10倍”。你不用理解Agent在干什么,数据变化本身就是信号。常用的算法有滑动窗口均值、指数加权移动平均、四分位距等,计算量小,适合在线实时跑。第三档是语义模型,适合规则覆盖不了的软性问题,比如目标漂移、注入话术、输出语义异常。这类检测需要把文本变成向量,再做分类或相似度判别。语义模型成本高、延迟高,适合放在高优先级线路的旁路或异步复核链路里。三档的衔接逻辑是从便宜到贵,先用硬规则兜底,再用统计基线抓漂移,语义模型做兜底检查和深度排查。不要一上来就在主链路上塞一个大模型推理,线上根本扛不住。3.3 事件流链路与选型参考实时检测的整体链路可以概括为:埋点 → 事件消息组件 → 流式处理 → 规则引擎 → 告警中心 → 通知/阻断。对于中小规模团队,建议先用开源的消息队列组件承载事件流,加上一套流式处理程序做窗口聚合和特征计算,再配一个轻量规则引擎。这套组合足够覆盖每分钟几万级Agent动作的吞吐。如果数据量更大、需要对状态做更复杂的流式计算,可以引入专门的流式计算框架或云厂商的托管流服务。这里有个容易踩的坑:很多人想把检测功能直接塞进现有的指标监控系统里,虽然也支持自定义指标和告警,但Agent的轨迹异常往往需要窗口聚合和跨事件关联,硬塞进去会导致规则表达受限或者查询慢。我建议把“事件型告警”和“指标型告警”分开处理:指标型告警仍然用现成监控系统,事件型告警走独立链路。4. 从零搭建:一条告警是怎么从动作事件变成通知的4.1 在Harness层构造结构化动作事件先看一段我在Harness调用工具时构造事件对象的示例。这里的思路是所有工具调用统一通过一个出口执行,在执行前后收集上下文,形成标准事件。# 示例:Harness 工具调用出口的事件构造 from dataclasses import dataclass, field from typing import Any, Optional @dataclass class AgentActionEvent: trace_id: str agent_id: str action_type: str # tool_call / model_call / memory_op action_name: str params: dict param_fingerprint: str # 规范化后用于循环检测 latency_ms: float token_cost: int permission_result: str # allow / deny / review status: str # success / failed created_at: float def execute_tool_with_event(ctx, tool_name, input_params): permission_result = ctx.check_permission(tool_name, input_params) start = time.time() status, raw_result = ctx.dispatch_tool(tool_name, input_params) latency = (time.time() - start) * 1000 event = AgentActionEvent( trace_id=ctx.trace_id, agent_id=ctx.agent_id, action_type="tool_call", action_name=tool_name, params=input_params, param_fingerprint=normalize_params(input_params), latency_ms=latency, token_cost=ctx.count_token(input_params, raw_result), permission_result=permission_result, status="success" if status else "failed", created_at=time.time() ) ctx.emit_event(event) return raw_result这段代码看起来简单,但它确立了几个关键原则:工具调用必须有统一出口,权限结果必须进入事件,每次调用必须生成可用于检测的指纹。指纹的作用在循环检测时会体现出来,我现在先埋个伏笔。4.2 硬规则检测:循环识别与调用风暴有了结构化事件,硬规则可以直接在消费端实现。这里以循环识别为例,核心是“规范化指纹 + 滑动窗口计数”。# 示例:基础版循环检测 from collections import deque LOOP_WINDOW_SIZE = 20 LOOP_THRESHOLD = 5 loop_deque = {} def check_loop(agent_id, action_name, param_fingerprint): key = f"{action_name}|{param_fingerprint}" if agent_id not in loop_deque: loop_deque[agent_id] = deque(maxlen=LOOP_WINDOW_SIZE) loop_deque[agent_id].append(key) # 统计当前窗口内相同动作指纹出现次数 count = sum(1 for k in loop_deque[agent_id] if k == key) if count >= LOOP_THRESHOLD: return True return False这里最容易忽略的就是 param_fingerprint 的规范化方式。我第一次上线时就吃了亏:Agent 连续调用同一个搜索工具十几次,只是因为参数里城市名的大小写、空格、字段顺序不同,就被判定为不同动作,循环检测完全失效。现在我的规范化策略是:参数键排序、字符串统一转小写、去除空白字符、数字精度统一保留两位,再做哈希。这一步把循环检测的召回率提升了几个档次。调用风暴的检测思路类似,但聚合维度更粗:以 agent_id 为单位,对1分钟窗口内所有工具调用计数,超过阈值就告警。这类问题用流式处理窗口函数做最方便,逻辑简单但必须在线执行。4.3 统计基线:用EWMA加MAD替代Z-score统计基线检测适合抓“慢漂移”类异常,比如Token成本、失败率、延迟的逐渐恶化。如果直接用Z-score,要求数据接近正态分布,但Agent行为数据远没有那么乖,所以我更推荐用“指数加权移动平均 + 绝对中位差”的组合。EWMA负责计算当前正常水平,MAD负责计算数据的离散程度。判定逻辑是:当前值与中位数的偏差如果超过MAD的一定倍数,就认为异常。MAD相比标准差更抗离群值,不会因为少数极端值把基线拉飞。# 示例:EWMA + MAD 基线异常检测 ALPHA = 0.3 MAD_MULTIPLIER = 5 class BaselineDetector: def __init__(self): self.median = None self.mad = None self.ewma = None def update(self, value): if self.ewma is None: self.ewma = value else: self.ewma = ALPHA * value + (1 - ALPHA) * self.ewma def add_sample(self, value): self.update(value) # 维护一个固定长度滑窗,用于计算中位数和MAD window.append(value) if len(window) >= 100: self.median = np.median(window) deviations = [abs(v - self.median) for v in window] self.mad = np.median(deviations)有了基线和MAD之后,实时判定时计算当前值与median的偏差,超过 MAD_MULTIPLIER 倍的MAD就触发告警。这个方案在线上跑下来非常稳,误报率明显低于直接用Z-score。注意这里的MAD_MULTIPLIER不是拍脑袋定的,上线前我会用历史数据回放调参,把误报率压到可接受范围,而不是追求零漏报。4.4 语义检测的轻量落地:先关键词再模型内容级检测不建议一开始就上重模型。我的落地顺序是:先用关键词和正则覆盖确定性的危险模式,比如“忽略之前指令”“泄露你的系统提示词”“获取所有订单”这类提示注入的典型句式,命中即告警。第二步才是向量化加轻量分类。把所有用户输入、工具返回内容和模型输出统一过向量模型,得到语义向量后,送入一个轻量分类器,区分正常/注入/越狱/敏感外泄四类。这个分类器可以用一批已标注样本快速训练,不需要上大模型推理。成本比大模型推理低一个数量级,延迟也可以控制在几十毫秒内。训练样本怎么攒?我的做法是先打在检测链路的旁路上,让新逻辑只记录不阻断,跑上一周左右,从真实流量里捞疑似样本,再人工标注回填。这样收集到的样本分布贴近真实业务,比从公开数据集拿来的更管用。等样本规模到了几千条,分类器的效果就会明显起来。5. 告警分级、抑制与响应策略:别再让告警变成噪音5.1 分级原则:不是所有异常都要打断Agent告警系统最容易挂掉的原因不是检测不够,而是通知太多。一线工程师一天收到几百条告警,最后没有任何一条会被认真看。所以分级必须清晰,要有明确的响应动作映射。我的分级习惯分为三档。P0是安全底线类,包括权限逃逸、敏感信息外泄、对核心业务接口的越权调用,命中后直接终断Agent执行,同时立即通知平台负责人和业务负责人。P1是明确异常类,比如循环失控、调用风暴、成本飙升,命中后先自动限流降低对下游的冲击,转人工确认。P2是观察类,比如目标漂移、输出结构崩坏、基线漂移,生成待处理事件,由业务在告警中心确认是否介入。一张简化的映射表大概长这样:级别典型场景实时响应动作通知方式P0权限逃逸、数据外泄立即终止任务、冻结Agent电话/短信+即时消息P1循环失控、调用风暴、成本激增限流降级、转人工确认即时消息+工单P2目标漂移、输出异常、基线偏离生成事件、等待确认告警中心列表分级的核心目的不是给告警贴标签,而是让每个级别都有明确的动作,避免“收到告警不知道怎么办”的尴尬。5.2 告警聚合与抑制:用指纹把相同问题合并上线初期最容易翻车的就是告警风暴。一个Agent开始循环,每分钟可能会触发几十条内容相同的告警,直接把IM群刷爆。抑制和聚合必须从一开始就设计进去。我常用的做法是给告警事件算一个“告警指纹”,指纹可以是“detector_id + agent_id + action_name + 异常特征摘要”。同一指纹在5分钟窗口内只发送一条通知,后续命中只做计数和状态更新。对多个Agent同时出现相同模式的情况,还可以做跨Agent聚合,比如只发一条总告警加上受影响Agent列表。除了频控,还要设计自动恢复机制。当一条P1级告警触发了限流,Agent后续恢复正常后,系统应自动把告警状态更新为已恢复,而不是让工程师手动关闭。没有恢复机制,群里会堆满“僵尸告警”,没过多久大家就对告警失去敏感度了。5.3 联动动作:自动降级、熔断与人工回放告警发出后,检测链路的价值需要靠响应动作来兑现。我最推荐的是“自动降级”和“半阻断”策略:检测到异常后,先把Agent切到需要人工确认的模式,所有高风险工具调用暂停,由工程师在页面上确认后放行或终止。这样既能拦住风险,又不至于出现误杀正常流程的尴尬。另一个重要能力是人工回放。告警中心里至少要能一键看到这个Agent的完整轨迹时间线,按traceId拉出所有事件,按时间排序渲染成可视化时间线。工程师收到告警后,不是去看一堆JSON日志,而是像看视频一样从头到尾过一遍Agent的每一步动作。这个能力看着不起眼,实际排查问题时效率能差出好几倍。自动终断要谨慎开放。初期我只对P0场景做自动终断,其他异常一律走限流和人工确认,经过一段时间观察、把误报率压到足够低之后,才逐步打开终断权限。6. 实操踩坑与避坑清单6.1 常见问题速查表现象可能原因解决办法告警刷屏缺少指纹聚合与通知限频引入告警指纹、5分钟窗口内合并同类告警检测延迟高语义模型直接串入在线链路分层处理,硬规则在线、语义模型旁路异步循环检测漏报参数未规范化,大小写/顺序差异绕过指纹参数排序、转小写、去空白后再生成指纹误报太多阈值设定不合理,基线被极端值拉偏用MAD替代标准差,结合历史数据回放调参权限异常找不到记录权限检查结果没有写入工具调用事件在Harness统一出口把permission_result写入事件成本异常发现太晚Token事件没有实时进入检测链路在模型调用出口同步构造model_call事件并发送目标漂移误判只比较“初始目标”与“当前输入”的向量相似度改为检测“动作使用的工具集合是否偏离目标相关工具集”6.2 我踩过的坑:三条真实教训第一个坑是循环检测第一次上线时,我以为把工具名和原始参数放进指纹就万事大吉了。结果Agent连续十几次调用同一个工具,只是参数里的大小写和空格不同,全部绕过检测。当时的修复方案就是前面提到的规范化流程:键排序、转小写、去空白、数字精度归一。这个改造上线后,循环检测的召回率立刻上了一个台阶。第二个坑和延迟有关。当时为了把检测做得“更智能”,直接把语义模型放到了实时链路上,结果单次工具调用整体延迟从120毫秒飙到900多毫秒,线上体验瞬间崩溃。后来我把检测拆成两档:硬规则和统计基线在线实时计算,语义模型放到异步旁路做深度复核。这样既有实时防护,又不牺牲性能。第三个坑是目标漂移的误报。一开始我对“初始目标”和“当前输入”做向量相似度,想判断Agent有没有跑偏,结果用户只是正常补充需求,相似度就骤降,误报不断。后来调整了思路,不再盯“当前输入像不像初始目标”,而是盯“当前动作涉及的工具集合是否属于初始目标关联工具集”。改动后误报大幅下降,检测逻辑也更符合实际业务含义。6.3 上线建议与迭代节奏如果你是从零开始建这套监控告警,我的建议是“先旁路,再半阻断,最后终断”。第一周把所有检测器全部旁路运行,只记录、只告警、不阻断,同时通过回放排查误报。第二周打开P1级的限流和人工确认,至少让高风险场景能够被人工介入。等误报率降到团队可以接受的范围,再逐步打开终断能力。阈值的设定不要纯靠拍脑袋。拿一周的真实历史数据回放,把不同阈值下的误报率和漏报率算出来,画一条曲线,找到平衡点。这个过程虽然麻烦,但一次做好,后面节省的时间远超投入。迭代节奏上,我建议先做两三条“保险线”级别的硬规则,比如最大步数、禁止调用名单、权限逃逸检测,先把底线守住。数据闭环跑通之后,再逐步增加统计基线检测和语义模型。不要想着一次就把所有异常都覆盖到,检测体系是滚雪球式增长的,每加一条规则都需要观察误报、调试阈值、标注样本,形成自己的数据飞轮之后才会越来越顺。根据我个人排障这么多轮的经验,这套系统最关键的其实不是算法多复杂,而是整个数据链路通不通。只要每个Agent动作都有标准事件、每个事件都有traceId、每条告警都能回放现场,后面的所有优化都会变得非常顺畅。哪怕一开始只有两条硬规则,这个闭环跑起来之后,你加的每一条规则都是在给整套体系增加长期价值。