Skip to content

场景与事件

TFRobot 的遥测系统需要回答两个基本问题:事件从哪里来(Scene)和发生了什么(Event)。这两个维度的组合构成了整个可观测性体系的基础语义。

设计背景

一个典型的 TFRobot 调用链路是 Robot → Brain → Chain → LLM/Tool/Memory。链路中每个组件都可能产生值得关注的运行时信息——LLM 返回了什么、Tool 执行是否成功、Memory 检索命中了多少结果。

如果只是把这些信息"发出去",接收方(Hook)会面临一个困境:它拿到一个事件,却不知道该如何处理。比如一个只关心 LLM Token 用量的 Hook,不应该被 Tool 的事件打扰;一个只在操作结束后才需要介入的 Hook,不应该在操作开始时就被触发。

Scene 和 Event 的设计正是为了解决这个问题:

  • Scene(场景)提供空间维度的过滤——事件来自哪个组件
  • Event(事件)提供时间维度的过滤——操作处于哪个阶段

两者结合,让 Hook 能够精确地订阅"在 LLM 生成完成后"或"在 Tool 执行出错时"这样的具体时刻,而不是淹没在全量事件流中。

场景 Scene

Scene 标识事件的来源组件。它与 TFRobot 的仿生架构一一对应:

Scene 对应组件 职责
Robot 顶层机器人 接收用户输入,协调整个执行流程
Brain 大脑 管理推理过程,调度 Chain 执行
Chain 思维链 状态机驱动的推理单元
LLM 大语言模型 调用模型进行文本生成
Tool 工具 执行外部操作(搜索、计算等)
Memory 记忆 记忆的高层操作(检索、刷新、增强)
MemoryStore 记忆存储 记忆的底层存储操作(增删查序列化)
Drive 驱动 预留场景,尚未启用

Scene 在代码中定义为 Literal 类型,用于 EventContext 的 scene 字段:

EventScene: TypeAlias = Literal["Robot", "Brain", "Drive", "Memory", "MemoryStore", "Chain", "Tool", "LLM"]

Scene 的核心作用是路由:HookManager 根据 scene 字段决定将事件分发给哪些 Hook。当你通过 register_hook(hook, scene="LLM") 注册 Hook 时,只有 scene="LLM" 的事件才会触发该 Hook。

事件 Event

事件的生命周期模型

每个被监控的操作都遵循统一的四阶段生命周期:

BEFORE ──→ [操作执行] ──→ AFTER     (正常完成)
                      ├─→ RAISE     (操作抛出异常)
                      └─→ ABORT     (用户主动中止)

这个设计源自一个实际需求:仅靠"开始"和"结束"两个事件不够。在生产环境中,你需要区分三种截然不同的结束方式:

  • AFTER:操作正常完成,EventContext 中包含返回值(如 LLM 的生成结果、Tool 的执行返回)
  • RAISE:操作过程中抛出了异常,EventContext 中包含错误信息——你可能需要告警或记录
  • ABORT:用户主动中止了操作(TFUserInterruptError),这不是错误,而是正常的控制流——你可能只需要记录而不需要告警

如果把异常和中止混为一谈,监控系统就无法区分"LLM 接口挂了"和"用户按了取消",导致误报。

完整事件列表

Robot 事件

事件 触发时机
BEFORE_ROBOT_RUN before_robot_run Robot.run / async_run 执行前
AFTER_ROBOT_RUN after_robot_run Robot.run / async_run 正常完成
ROBOT_RUN_RAISE robot_run_raise 运行期间抛出异常
ROBOT_RUN_ABORT robot_run_abort 用户主动中止

Brain 事件

事件 触发时机
BEFORE_BRAIN_RUN before_brain_run Brain.run / async_run 执行前
AFTER_BRAIN_RUN after_brain_run Brain.run / async_run 正常完成
BRAIN_RUN_RAISE brain_run_raise 运行期间抛出异常
BRAIN_RUN_ABORT brain_run_abort 用户主动中止

Chain 事件

Chain 是事件最丰富的场景,因为思维链作为状态机驱动的推理核心,其运行状态最为复杂。

事件 触发时机
BEFORE_CHAIN_RUN before_chain_run 单链执行前
AFTER_CHAIN_RUN after_chain_run 单链正常完成
CHAIN_RUN_RAISE chain_run_raise 单链执行期间抛出异常
CHAIN_RUN_ABORT chain_run_abort 单链被用户中止
BEFORE_CHAINS_RUN before_chains_run 多链(串行/并行/树/图)执行前
AFTER_CHAINS_RUN after_chains_run 多链正常完成
CHAINS_RUN_RAISE chains_run_raise 多链执行期间抛出异常
CHAINS_RUN_ABORT chains_run_abort 多链被用户中止
CHAIN_TOKEN_LIMIT chain_token_limit 上下文 Token 接近限制
CHAIN_COMPACT chain_compact 触发上下文压缩
CHAIN_NON_BLOCKING_EXCEPTION chain_non_blocking_exception 非阻塞异常(链可继续运行)
CHAIN_BLOCKING_EXCEPTION chain_blocking_exception 阻塞异常(链被迫终止)

单链 vs 多链

CHAIN_RUN 系列对应单个 Chain 的执行;CHAINS_RUN 系列对应 SeqChains、ParallelChains 等多链编排的整体执行。两者的 EventContext 结构相同,但粒度不同。

LLM 事件

LLM 区分普通生成流式输出两种模式,因为它们的监控需求不同:普通生成只需要关注"开始"和"结束";流式输出还需要监听中间的每一个 chunk(LLM_STREAMING 事件)。

事件 触发时机
BEFORE_LLM_GENERATE before_llm_generate 普通生成前
AFTER_LLM_GENERATE after_llm_generate 普通生成完成
LLM_GENERATE_RAISE llm_generate_raise 普通生成期间抛出异常
LLM_GENERATE_ABORT llm_generate_abort 普通生成被用户中止
BEFORE_LLM_STREAMING before_llm_streaming 流式输出开始前
LLM_STREAMING llm_streaming 流式输出的每个 chunk
AFTER_LLM_STREAMING after_llm_streaming 流式输出完成
LLM_STREAMING_RAISE llm_streaming_raise 流式输出期间抛出异常
LLM_STREAMING_ABORT llm_streaming_abort 流式输出被用户中止

Tool 事件

事件 触发时机
BEFORE_TOOL_RUN before_tool_run 工具执行前
AFTER_TOOL_RUN after_tool_run 工具执行完成
TOOL_RUN_RAISE tool_run_raise 工具执行期间抛出异常
TOOL_RUN_ABORT tool_run_abort 工具执行被用户中止

Memory 事件

Memory 场景覆盖三种高层操作:检索(recall)、刷新(refresh)、增强(enrich)。

事件 触发时机
BEFORE_MEMORY_RECALL before_memory_recall 记忆检索前
AFTER_MEMORY_RECALL after_memory_recall 记忆检索完成
MEMORY_RECALL_RAISE memory_recall_raise 检索期间抛出异常
MEMORY_RECALL_ABORT memory_recall_abort 检索被用户中止
BEFORE_MEMORY_REFRESH before_memory_refresh 记忆刷新前
AFTER_MEMORY_REFRESH after_memory_refresh 记忆刷新完成
MEMORY_REFRESH_RAISE memory_refresh_raise 刷新期间抛出异常
MEMORY_REFRESH_ABORT memory_refresh_abort 刷新被用户中止
BEFORE_MEMORY_ENRICH before_memory_enrich 记忆增强前
AFTER_MEMORY_ENRICH after_memory_enrich 记忆增强完成
MEMORY_ENRICH_RAISE memory_enrich_raise 增强期间抛出异常
MEMORY_ENRICH_ABORT memory_enrich_abort 增强被用户中止

MemoryStore 事件

MemoryStore 覆盖底层存储操作:增(add)、删(delete)、查(get)、清(clear)、序列化(serialization)、学习(learn)。

事件 触发时机
BEFORE_MEMORY_STORE_GET before_memory_store_get 存储查询前
AFTER_MEMORY_STORE_GET after_memory_store_get 存储查询完成
MEMORY_STORE_GET_RAISE memory_store_get_raise 查询期间抛出异常
MEMORY_STORE_GET_ABORT memory_store_get_abort 查询被用户中止
BEFORE_MEMORY_STORE_ADD before_memory_store_add 存储添加前
AFTER_MEMORY_STORE_ADD after_memory_store_add 存储添加完成
MEMORY_STORE_ADD_RAISE memory_store_add_raise 添加期间抛出异常
MEMORY_STORE_ADD_ABORT memory_store_add_abort 添加被用户中止
BEFORE_MEMORY_STORE_DELETE before_memory_store_delete 存储删除前
AFTER_MEMORY_STORE_DELETE after_memory_store_delete 存储删除完成
MEMORY_STORE_DELETE_RAISE memory_store_delete_raise 删除期间抛出异常
MEMORY_STORE_DELETE_ABORT memory_store_delete_abort 删除被用户中止
BEFORE_MEMORY_STORE_CLEAR before_memory_store_clear 存储清空前
AFTER_MEMORY_STORE_CLEAR after_memory_store_clear 存储清空完成
MEMORY_STORE_CLEAR_RAISE memory_store_clear_raise 清空期间抛出异常
MEMORY_STORE_CLEAR_ABORT memory_store_clear_abort 清空被用户中止
BEFORE_MEMORY_STORE_SERIALIZATION before_memory_store_serialization 存储序列化前
AFTER_MEMORY_STORE_SERIALIZATION after_memory_store_serialization 存储序列化完成
MEMORY_STORE_SERIALIZATION_RAISE memory_store_serialization_raise 序列化期间抛出异常
MEMORY_STORE_SERIALIZATION_ABORT memory_store_serialization_abort 序列化被用户中止
BEFORE_MEMORY_LEARN before_memory_learn 记忆学习前
AFTER_MEMORY_LEARN after_memory_learn 记忆学习完成
MEMORY_LEARN_RAISE memory_learn_raise 学习期间抛出异常
MEMORY_LEARN_PROGRESS memory_learn_progress 学习中间进度

MEMORY_LEARN_PROGRESS

学习操作是唯一使用 PROGRESS 事件而非 ABORT 事件的场景。这是因为学习是一个批量长时操作,需要报告中间进度(已处理数量、成功/失败计数等),而不仅仅是开始和结束。

EventContext:事件携带的上下文

每个事件都携带一个 EventContext,包含该事件的结构化信息。所有场景共享 BaseEventContext 的基础字段,各场景在此基础上扩展专属字段。

关于 EventContext 各场景字段的完整参考,参见 Event Hook — EventContext 参考

这里重点说明两个容易混淆的字段:

字段 面向对象 用途
desc 开发者 描述事件的性质(如"Before run"、"After generate"),与 scene + action 相关
info 用户 事件的具体内容(如用户输入的文本、LLM 返回的结果),适合直接打印到日志

desc 帮助开发者在调试时快速定位事件的来源和阶段;info 帮助用户了解实际发生了什么。两者分离是因为开发者需要的信息粒度和用户需要的可读性是不同的。

事件如何产生:span_decorator

事件不需要业务代码手动触发。TFRobot 的各基类(Robot、Brain、Chain、LLM、Tool、Memory)在初始化时通过 decorate_span 方法,用 span_decorator 装饰器自动包装关键方法。

以 Robot 为例,decorate_span 的核心逻辑是:

# tfrobot/base.py
@classmethod
def decorate_span(cls) -> None:
    decorated_run = span_decorator(
        {"scene": "Robot", "desc": "Robot Run method"},
        SpanEvent.BEFORE_ROBOT_RUN,    # 方法执行前触发
        SpanEvent.AFTER_ROBOT_RUN,     # 方法正常完成触发
        SpanEvent.ROBOT_RUN_RAISE,     # 异常触发
        SpanEvent.ROBOT_RUN_ABORT,     # 用户中止触发
        partial(cls._generate_context, desc="Before run"),
        partial(cls._generate_context, desc="After run"),
    )(cls.run)
    setattr(cls, "run", decorated_run)

span_decorator 在被包装方法的执行前后自动构造 EventContext 并通过 TFSpan.add_event 发出事件。对于异常和用户中止,装饰器会捕获对应的异常类型并触发相应事件。

防重复包装

span_decorator 通过 __is_tf_span_wrapped__ 标记防止同一方法在继承链中被多次包装。子类如果没有覆写父类方法,不会重复添加装饰器。

与其他模块的协作

Scene + Event ──→ EventContext ──→ TFSpan.add_event ──→ HookManager ──→ Hook
   (本文档)         (本文档)        (Trace 扩展)        (Event Hook)    (用户代码)
  • Trace 扩展:TFSpan 的 add_event 是事件进入 OpenTelemetry 和 HookManager 的入口
  • Event Hook:Hook 通过 Scene 过滤事件,通过 Event 名称判断执行时机
  • Meter 指标采集:指标采集本身也通过 Hook 机制实现,监听 LLM 事件采集 Token 用量
  • 透传原始上下文:EventContext 的 baggage 字段来自 OpenTelemetry Baggage,由调用方注入

扩展指南

如果内置事件无法满足需求,你可以在自己的组件中添加自定义事件。

简单方式:直接在 Span 上添加事件字符串,不需要修改 SpanEvent 枚举。你可以在任何拥有 Span 的地方调用 span.add_event("my_custom_event", attributes)

完整方式:如果需要严格的类型安全和框架级集成,参见 扩展事件