Skip to content

监控模块#

注意instrumentation模块(在llama-index v0.10.20及更高版本中可用)旨在取代传统的callbacks模块。在弃用过渡期间,llama-index库同时支持这两个模块来实现LLM应用的监控功能。不过,在所有现有集成都迁移到新的instrumentation模块后,我们将不再支持callbacks模块。

新的instrumentation模块允许对llama-index应用进行监控。具体而言,用户既可以使用模块提供的功能,也可以通过自定义逻辑来处理事件和跟踪跨度。用户还可以定义自己的事件,并指定在代码逻辑的哪些位置触发这些事件。以下是instrumentation模块的核心类及其简要说明:

  • Event — 表示应用程序代码执行过程中某个特定时刻发生的事件
  • EventHandler — 监听Event的发生,并在这些时刻执行代码逻辑
  • Span — 表示应用程序代码中特定部分的执行流程,因此包含多个Event
  • SpanHandler — 负责处理Span的进入、退出和丢弃(即由于错误导致的提前退出)
  • Dispatcher — 将Event以及进入/退出/丢弃Span的信号发送给相应的处理器

使用监控模块实现可观测性#

监控的核心应用场景之一是实现可观测性。我们与第三方合作伙伴的原生监控集成,可以获取整个调用栈的详细追踪信息。

查看我们的可观测性指南了解支持的合作伙伴详情。

使用方法#

使用新的instrumentation模块包含3个高级步骤:

  1. 定义dispatcher
  2. (可选)定义并将EventHandler附加到dispatcher
  3. (可选)定义并将SpanHandler附加到dispatcher

完成这些步骤后,就能处理事件并获取在llama-index库及其扩展包中传输的跨度数据。

例如,如果要跟踪库中所有的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类自带timestampid_字段。要添加更多字段到事件负载中,只需将它们添加为新的Field(因为它们继承自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#

SpanEvent类似,都是结构化数据类。但不同于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的层级结构与标准Pythonlogging库及其Logger类类似。具体来说,除根dispatcher外,所有dispatcher都有一个父级,并且在处理事件或跨度时可以将它们传播到其父级(这是默认行为)。这种处理事件和跨度的层级方法允许定义"全局"事件处理器和"局部"处理器。

考虑以下项目结构。有3个dispatcher:一个在project顶层,另外两个在单独的llama1llama2子模块中。通过这种设置,附加到项目根dispatcher的任何EventHandler都将订阅llama1llama2代码执行中发生的所有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

笔记本指南:#

API参考#