Skip to content
LlamaIndex 文档版本 跟随上游 源快照 dcbad67b 更新于 2026-10-15

注意:instrumentation 模块(llama-index v0.10.20 及之后版本可用)是为了取代旧的 callbacks 模块。在弃用过渡期内,llama-index 库同时支持这两个模块来为你的 LLM 应用做 instrumentation。但等所有现有集成都迁到新的 instrumentation 模块之后,我们将不再支持 callbacks 模块。

新的 instrumentation 模块可对 llama-index 应用做 instrumentation。具体来说,你可以用自定义逻辑,也可以用该模块自带的机制来处理事件、跟踪 span。用户还能定义自己的事件,并指定在代码逻辑的哪个位置、何时发出它们。下面列出该 instrumentation 模块的核心类及其简要说明:

  • Event —— 表示应用代码执行过程中某个事件发生的时间点。
  • EventHandler —— 监听 Event 的发生,并在这些时刻执行代码逻辑。
  • Span —— 表示应用中某段代码的执行流程,因而包含若干 Event。
  • SpanHandler —— 负责 Span 的进入、退出与丢弃(例如因错误提前退出)。
  • Dispatcher —— 发出 Event,并向相应的处理器传递进入/退出/丢弃 Span 的信号。

用 instrumentation 模块做可观测性

instrumentation 的一个核心用途就是可观测性。我们与第三方合作方间的原生 instrumentation 集成,可以让你拿到覆盖整个调用栈的细粒度轨迹。

关于支持的合作方,更多细节请看我们的可观测性指南。

用量

使用新的 instrumentation 模块包含 3 个高层步骤。

  1. 定义一个 dispatcher
  2. (可选)定义你的 EventHandler 并挂到 dispatcher 上
  3. (可选)定义你的 SpanHandler 并挂到 dispatcher 上

这样一来,你就能处理事件、拿到 llama-index 库及其扩展包传递出的 span。

举个例子,如果我想跟踪库里每一次 LLM 调用:

from typing import Dict, List

from llama_index.core.instrumentation.events.llm import (
    LLMChatEndEvent,
    LLMChatStartEvent,
    LLMChatInProgressEvent,
)

class ExampleEventHandler(BaseEventHandler):
    events: List[BaseEvent] = []

    @classmethod
    def class_name(cls) -> str:
        """Class name."""
        return "ExampleEventHandler"

    def handle(self, event: BaseEvent) -> None:
        """Logic for handling event."""
        print("-----------------------")
        # all events have these attributes
        print(event.id_)
        print(event.timestamp)
        print(event.span_id)

        # event specific attributes
        if isinstance(event, LLMChatStartEvent):
            # initial
            print(event.messages)
            print(event.additional_kwargs)
            print(event.model_dict)
        elif isinstance(event, LLMChatInProgressEvent):
            # streaming
            print(event.response.delta)
        elif isinstance(event, LLMChatEndEvent):
            # final response
            print(event.response)

        self.events.append(event)
        print("-----------------------")

关于 LlamaIndex 中记录的全部事件,见完整指南;更多细节请访问 api 参考。

定义自定义 EventHandler

用户可以继承 BaseEventHandler 并在抽象方法 handle() 里给出逻辑,从而创建自己的处理器。

from llama_index.core.instrumentation.event_handlers.base import (
    BaseEventHandler,
)

class MyEventHandler(BaseEventHandler):
    """My custom EventHandler."""

    @classmethod
    def class_name(cls) -> str:
        """Class name."""
        return "MyEventHandler"

    def handle(self, event: BaseEvent, **kwargs) -> Any:
        """Logic for handling event."""
        print(event.class_name())

my_event_handler = MyEventHandler()

定义好处理器之后,把它挂到你想要的 dispatcher 上:

import llama_index.core.instrumentation as instrument

dispatcher = instrument.get_dispatcher(__name__)
dispatcher.add_event_handler(my_event_handler)

定义自定义 Event

用户可以继承 BaseEvent 来创建自定义事件。BaseEvent 自带 timestamp 与 id_ 两个字段。想给事件载荷再加内容,直接按新的 Fields 添加即可(它们是 pydantic.BaseModel 的子类)。

from llama_index.core.instrumentation.event.base import BaseEvent

class MyEvent(BaseEvent):
    """My custom Event."""

    new_field_1 = Field(...)
    new_field_2 = Field(...)

自定义事件定义好之后,就可以用 dispatcher 在应用代码中合适的时机把它触发出去。

import llama_index.core.instrumentation as instrument

dispatcher = instrument.get_dispatcher(__name__)
dispatcher.event(MyEvent(new_field_1=..., new_field_2=...))

定义自定义 Span

Span 与 Event 类似,二者都是结构化的数据类。不同之处在于,Span 如其名,跨越程序执行流中的一段时长。你可以定义自定义 Span 来存放任意你想要的信息。

from typing import Any
from llama_index.core.bridge.pydantic import Field

class MyCustomSpan(BaseSpan):
    custom_field_1: Any = Field(...)
    custom_field_2: Any = Field(...)

要处理你的新 Span 类型,还需要继承 BaseSpanHandler 类来自定义 SpanHandler。子类化这个基类时必须实现三个抽象方法:new_span()、prepare_to_exit_span() 与 prepare_to_drop_span()。

import inspect
from typing import Any, Dict, Optional
from llama_index.core.instrumentation.span.base import BaseSpan
from llama_index.core.instrumentation.span_handlers import BaseSpanHandler

class MyCustomSpanHandler(BaseSpanHandler[MyCustomSpan]):
    @classmethod
    def class_name(cls) -> str:
        """Class name."""
        return "MyCustomSpanHandler"

    def new_span(
        self,
        id_: str,
        bound_args: inspect.BoundArguments,
        instance: Optional[Any] = None,
        parent_span_id: Optional[str] = None,
        tags: Optional[Dict[str, Any]] = None,
        **kwargs: Any,
    ) -> Optional[MyCustomSpan]:
        """Create a span."""
        # logic for creating a new MyCustomSpan
        pass

    def prepare_to_exit_span(
        self,
        id_: str,
        bound_args: inspect.BoundArguments,
        instance: Optional[Any] = None,
        result: Optional[Any] = None,
        **kwargs: Any,
    ) -> Any:
        """Logic for preparing to exit a span."""
        pass

    def prepare_to_drop_span(
        self,
        id_: str,
        bound_args: inspect.BoundArguments,
        instance: Optional[Any] = None,
        err: Optional[BaseException] = None,
        **kwargs: Any,
    ) -> Any:
        """Logic for preparing to drop a span."""
        pass

要用上新的 SpanHandler(以及与之关联的 Span 类型),只需把它加入你想要的 dispatcher。

import llama_index.core.instrumentation as instrument
from llama_index.core.instrumentation.span_handler import SimpleSpanHandler

dispatcher = (
    instrument.get_dispatcher()
)  # with no name argument, defaults to root

my_span_handler = MyCustomSpanHandler()
dispatcher.add_span_handler(my_span_handler)

进入/退出 Span

要向 SpanHandler 发出进入/退出 Span 的信号,我们分别使用 span_enter()、span_exit() 方法。还有一个 span_drop() 方法,用于处理因被覆盖代码出错而使 Span 提前结束的情形。

import llama_index.core.instrumentation as instrument

dispatcher = instrument.get_dispatcher(__name__)

def func():
    dispatcher.span_enter(...)
    try:
        val = ...
    except:
        ...
        dispatcher.span_drop(...)
    else:
        dispatcher.span_exit(...)
        return val

# or, syntactic sugar via decorators

@dispatcher.span
def func():
    ...

利用 dispatcher 的层级结构

dispatcher 也有与 Python 标准 logging 库及其 Logger 类类似的层级结构。具体说,除根 dispatcher 外,每个 dispatcher 都有一个父级;在处理事件或 span 时可以向父级传播(这是默认行为)。这种层级化的处理方式让你既能定义“全局”的事件处理器,也能定义“局部”的。

看下 project 结构里定义的 3 个 dispatcher:一个在 project 顶层,另两个分别在其子模块 llama1 与 llama2 中。在这样的布置下,挂在 project 根 dispatcher 上的任何 EventHandler 都会订阅到 llama1、llama2 代码执行中发生的全部 Event;而定义在各 llama<x> 子模块里的 EventHandler 只会订阅到其所在子模块执行过程中发生的 Event。

终端窗口

project
├── __init__.py  # has a dispatcher=instrument.get_dispatcher(__name__)
├── llama1
│   ├── __init__.py  # has a dispatcher=instrument.get_dispatcher(__name__)
│   └── app_query_engine.py
└── llama2
    ├── __init__.py  # has a dispatcher=instrument.get_dispatcher(__name__)
    └── app_query_engine.py

Notebook 指南:

API 参考

BytesRouter 是社区翻译站点,所有译文仅供参考,一切以官方英文原文为准。