mcpField
A
ag2-observers-and-alerts

ag2-observers-and-alerts

github.com/ag2aiAgent 工作流1265 次安装13 周前更新
代码仓库

关于 ag2-observers-and-alerts

为 AG2 Agent 提供事件观察与监控能力,适合需要记录响应、跟踪指标或实施运行时安全规则的开发者。支持无状态回调和有状态观察器,并可通过策略将致命告警转为中止。

主要功能

  1. 01按事件或字段筛选回调
  2. 02监控累计 Token 和重复调用
  3. 03按次数、时间或顺序触发观察
  4. 04通过告警策略中止 Agent

适用场景

  1. 01记录模型响应和工具调用
  2. 02在 Token 用量达到阈值时告警
  3. 03检测重复工具调用循环
  4. 04阻止危险工具调用后中止执行

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/ag2ai/ag2-skills --skill ag2-observers-and-alerts

安装后在 Claude Code 中输入/ag2-observers-and-alerts确认已加载。

附带文件assets/safety_guard.pyassets/token_watchdog.py

详细介绍

观察器、监视与告警

适用场景

  • 可观测性 — 记录模型响应、工具调用和 token 用量。
  • 运行时安全 — 阻止危险的工具参数,停止 Agent。
  • 响应式指标 — 每 N 次响应或每 M 秒触发一次。
  • 循环/重复检测 — 捕获无限工具调用循环。
  • 有状态监控 — 需要记住先前事件,以决定下一步操作的任何场景。

两种观察器形式

形式适用场景使用方式
无状态函数一次性事件钩子(日志记录、指标)@observer(EventType)
有状态类计数器/窗口/阈值/组合触发器子类化 BaseObserver

两者在底层都是流订阅者 — 注册在 Agent 上,而非直接注册到流上。

60 秒快速上手 — @observer

from ag2 import Agent, observer
from ag2.config import OpenAIConfig
from ag2.events import ModelResponse

@observer(ModelResponse)
async def log_response(event: ModelResponse) -> None:
    print(f"Model said: {event.content}")

agent = Agent(
    "assistant",
    config=OpenAIConfig(model="gpt-4o-mini"),
    observers=[log_response],
)

也可以在构造完成后使用 @agent.observer(...) 附加观察器。也支持针对单次调用的观察器(agent.ask("...", observers=[...]))。

观察器回调支持完整的依赖注入(Context、Inject、Variable、Depends)。可按事件类型、多个类型(ModelRequest | ModelResponse)或字段值(ToolCallEvent.name == "search")筛选。使用 interrupt=True 可在常规订阅者看到事件之前修改或抑制事件。

内置有状态观察器

from ag2 import Agent
from ag2.observers import LoopDetector, TokenMonitor

agent = Agent(
    "assistant",
    config=config,
    observers=[
        TokenMonitor(warn_threshold=50_000, alert_threshold=100_000),
        LoopDetector(window_size=10, repeat_threshold=3),
    ],
)
  • TokenMonitor — 跟踪 ModelResponse 和 TaskCompleted 中的累计 token 数。达到阈值时发出 WARNING/CRITICAL 级别的 ObserverAlert。通过 monitor.total_tokens 读取状态。
  • LoopDetector — 对近期工具调用使用滑动窗口。检测到连续 repeat_threshold 次相同调用时发出 WARNING 告警。

自定义 BaseObserver

BaseObserver 将一个 Watch(何时触发)与一个 process() 方法(要执行什么操作)组合在一起:

from ag2 import Context
from ag2.observers import BaseObserver
from ag2.watch import CadenceWatch
from ag2.events import BaseEvent, ModelResponse
from ag2.events.alert import ObserverAlert, Severity

class AvgCompletionObserver(BaseObserver):
    """Every N responses, emit an INFO alert with avg completion-token count."""

    def __init__(self, window: int = 5) -> None:
        super().__init__("avg-completion", watch=CadenceWatch(n=window, condition=ModelResponse))
        self._window = window

    async def process(self, events: list[BaseEvent], ctx: Context) -> ObserverAlert | None:
        tokens = [e.usage.completion_tokens for e in events if isinstance(e, ModelResponse) and e.usage]
        if not tokens:
            return None
        return ObserverAlert(
            source=self.name,
            severity=Severity.INFO,
            message=f"Avg completion tokens over last {self._window}: {sum(tokens) / len(tokens):.0f}",
        )

如果 process() 返回 ObserverAlert,基类会将其发送到流上。你也可以通过 await ctx.send(...) 手动发送事件。

Watch 基元 — 选择何时触发

需求使用方式
每次匹配的事件EventWatch(EventType) 或直接使用 stream.subscribe(fn, condition=...)
每 N 次匹配事件CadenceWatch(n=N, condition=EventType)
每 T 秒(缓冲事件)CadenceWatch(max_wait=T, condition=EventType)
满足任一阈值CadenceWatch(n=N, max_wait=T, condition=EventType)
延迟后触发一次DelayWatch(seconds)
定时周期触发IntervalWatch(seconds)
Cron 计划CronWatch("0 9 * * MON")
所有子监视都触发AllOf(w1, w2)
任一子监视触发AnyOf(w1, w2)
按顺序触发Sequence(w1, w2)

均可从 ag2.watch 导入。回调签名统一为:async def cb(events: list[BaseEvent], ctx: Context) -> None。由时间驱动的监视会传入 events=[]。

ObserverAlert — 告警类型

from ag2.events.alert import ObserverAlert, Severity

ObserverAlert(
    source="my-observer",
    severity=Severity.WARNING,    # INFO, WARNING, CRITICAL, FATAL
    message="What happened",
)

重要提示: ObserverAlert 会出现在流上并保存在历史记录中,但默认的提供方映射器不会将其呈现给大模型。要让 Agent 看到告警,请将 AlertPolicy() 添加到 assembly=[...] 中:

from ag2.policies import AlertPolicy
agent = Agent("assistant", config=config, assembly=[AlertPolicy()])

FATAL 告警 → HaltEvent → 短路

AlertPolicy 在 Severity.FATAL 时会执行两项操作:

  1. 在流上发出 HaltEvent。
  2. 将停止通知附加到系统提示词中。

当 assembly=[...] 非空时,运行框架会自动接入 _HaltCheckMiddleware,它会检测 HaltEvent,并通过合成的 HALTED: ... 响应短路下一次 LLM 调用。

from ag2 import Context
from ag2.observers import BaseObserver
from ag2.events import BaseEvent, ToolCallEvent
from ag2.events.alert import HaltEvent, ObserverAlert, Severity
from ag2.policies import AlertPolicy
from ag2.watch import EventWatch

class PathGuardian(BaseObserver):
    def __init__(self) -> None:
        super().__init__("path-guardian", watch=EventWatch(ToolCallEvent))

    async def process(self, events: list[BaseEvent], ctx: Context) -> ObserverAlert | None:
        for event in events:
            if not isinstance(event, ToolCallEvent) or event.name != "write_file":
                continue
            if "/etc/" in event.arguments or "/usr/" in event.arguments:
                return ObserverAlert(
                    source=self.name,
                    severity=Severity.FATAL,
                    message=f"blocked dangerous write: {event.arguments}",
                )
        return None

agent = Agent(
    "safe-shell",
    prompt="...",
    config=config,
    tools=[write_file],
    observers=[PathGuardian()],
    assembly=[AlertPolicy()],   # routes FATAL → HaltEvent
)

首次危险的工具调用会触发 FATAL → 停止;Agent 的下一次 ask 会被短路。完整的可运行演示:assets/safety_guard.py。

从外部订阅告警和停止事件

from ag2 import MemoryStream
from ag2.events.alert import HaltEvent, ObserverAlert

stream = MemoryStream()
stream.where(ObserverAlert).subscribe(lambda e: print(f"[{e.severity}] {e.source}: {e.message}"))
stream.where(HaltEvent).subscribe(lambda e: print(f"HALT: {e.reason}"))
await agent.ask("...", stream=stream)

观察器 vs 中间件 vs Stream 订阅者

特性观察器中间件Stream 订阅者
注册对象AgentAgentStream
生命周期限定于执行期间限定于执行期间手动
样板代码函数(或 BaseObserver)BaseMiddleware 类函数
能否修改事件interrupt=True可以(包装执行过程)interrupt=True
支持 DI是是是
用例监控、指标、告警横切关注点(重试、身份验证、速率限制)底层事件接线

深入了解

  • assets/token_watchdog.py — 一个 Agent 上的三个观察器(TokenMonitor、LoopDetector、自定义 AlertConsole)。与 code_examples/04 对应。
  • assets/safety_guard.py — PathGuardian → FATAL → AlertPolicy → HaltEvent → 短路。与 code_examples/08 对应。
  • 源文档:
    • website/docs/user-guide/advanced/observers.mdx — @observer、BaseObserver、注册、内置观察器、ObserverAlert。
    • website/docs/user-guide/advanced/watches.mdx — 所有 Watch 原语及组合规则。
    • website/docs/user-guide/advanced/stream.mdx — Stream API、where、subscribe、中断器、RedisStream。
    • website/docs/user-guide/advanced/assembly.mdx — AlertPolicy 的排序和去重。

常见问题

  • 告警没有传达到模型 — ObserverAlert 事件会出现在 Stream 上,但默认对 LLM 不可见。将 AlertPolicy() 添加到 assembly=[...]。
  • FATAL 未能停止执行 — AlertPolicy 负责创建 HaltEvent。如果没有 assembly=[..., AlertPolicy(), ...](或任何能启用 _HaltCheckMiddleware 的非空装配链),执行就不会停止。
  • 在多个 Agent 间共享同一个 AlertPolicy() — 去重状态保存在实例上。每个 Agent 都应使用自己的实例。
  • Watch 回调假定 events 非空 — 对于基于时间驱动的 Watch(DelayWatch、IntervalWatch、CronWatch),events 始终为 []。
  • 忘记 process() 是异步的 — BaseObserver.process 必须是 async def。
  • 想用 subscribe() 装饰器,却使用了 subscribe(fn) — 两种方式都可用;直接调用形式是 stream.subscribe(fn),装饰器形式是 @stream.subscribe()(带括号)。
  • 使用 CadenceWatch 时既未设置 n,也未设置 max_wait — 会引发 ValueError;至少需要设置其中一个。

评论

支持 Markdown