更新时间:2026-11-04 GMT+08:00
分享

Pi框架

观测功能支持对Pi框架(pi-agent-core / pi-coding-agent)开发的智能体进行可观测。Python探针是华为云AgentArts产品自研的Python语言的可观测采集探针,其基于 OpenTelemetry 标准实现了数据采集与上报能力。

本文介绍如何将Pi框架应用接入AgentArts的观测功能,以帮助用户实时了解AI应用运行状态。

相关概念

  • Node进程:Pi框架应用本身是一个Node.js应用,运行在Node进程中。所谓“Node进程”,就是指用node或 npx tsx 启动的那个应用进程。
  • Python进程:一个独立的Python程序,负责读取Node进程产生的事件文件,并把数据上报到华为云。它与Node进程互不干扰。
  • TS探针:一段TypeScript代码,随Node进程一起启动,负责在Pi应用运行时把Agent、LLM、工具调用等事件写成JSONL文件。探针是指它像一根探针插到应用里,观察应用的运行状态,但不改变应用的行为。
  • Python采集器(pi_collector):一个Python程序,负责读取TS探针写出的JSONL文件,把零散的事件整理成完整的调用链(Trace)和指标(Metrics),再上传到华为云中。

Pi框架的可观测采用“Node 采集 + Python 上报”的跨进程架构:

  • Node侧(TS探针):运行在Pi应用进程内,负责采集Agent、LLM、Tool等执行事件,并把事件写成 JSONL 文件。
  • Python侧(pi_collector):作为独立进程运行,旁路读取JSONL事件文件,重建OpenTelemetry Span调用链,并通过OTLP上报到华为云AgentArts。

您可以理解为Pi应用负责“产生事件”,TS探针负责“记录事件到文件”,Python采集器负责“读文件、整理成调用链、上传到AgentArts。”

把TS探针挂载到Pi应用上有三种方式,后续步骤中会分别介绍:

  • preload自动接入:在启动Pi应用时,通过Node的启动参数提前加载探针。应用代码完全不用改。
  • 显式插桩:在应用代码里手动调用一次探针提供的函数,把Agent实例交给探针。需要改少量代码。
  • Extension Hook:把探针作为Pi Coding Agent的一个扩展插件注册进去,由Pi框架自己调用。需要改配置文件。

安装Python探针wheel后,TypeScript探针的编译产物dist/src/*.js已随包发布,无需单独执行npm安装或tsc编译。

前提条件

  • Python 环境版本 >= 3.9
  • Node.js 版本 >= 20.6(TS探针在启动时使用了一个较新的顶层await语法,该语法要求Node.js 20.6或更高版本。)
  • Pi 框架(pi-agent-core或pi-coding-agent)已配置开启事件输出,事件schema版本为1.0。
  • TS探针运行在Pi应用进程内但不修改业务代码。Python采集器是一个独立进程,需要和Pi应用共享同一个文件系统,并且能读写事件输出目录。默认目录是.pi/events。
  • 采集器用一个checkpoint文件记录读取进度,支持断点续传,程重启后不会重复上报已经读过的内容。AOM后端只接受Gauge类型指标,Gauge表示某个时间点的瞬时值,采集器已经统一按这个类型上报。
  • 不要在同一个Pi应用进程里同时启用pi_agent_core和pi_coding_agent两套TS探针,否则会产生重复事件。auto模式下,Coding Extension会自动让Core探针停止写入。

建议先启动Python采集器,再启动Pi应用,这样事件一产生就能被及时处理。

  • 在AgentArts观测界面查看数据不收费。
  • 但数据上报会产生费用:
    • 调用链上报到APM(应用性能管理服务),费用明细请参考APM计费说明。
    • 指标上报到AOM(应用运维管理服务),费用明细请参考AOM计费说明。
  • AOM和APM都是按需计费,并有一定量的免费额度,超出后按实际使用量收费。

框架介绍

Pi框架是智能体开发框架,包含pi-agent-core与pi-coding-agent两种实现,提供了Agent编排、多轮对话(turn)、执行步骤(step)、大模型调用(LLM)、工具调用(Tool)等能力,并支持会话(session)级别的状态管理。Pi框架在运行时会将其内部执行过程以结构化事件的形式写入JSONL文件,供下游采集器消费。

pi_collector是Python探针提供的旁路采集组件。所谓“旁路”,是指它不参与Pi应用的业务逻辑,只是在一旁读取事件文件。它通过tail方式持续读取Pi框架输出的事件文件,依据事件的operation_id与parent_operation_id重建出完整的OpenTelemetrySpan调用链,并生成GenAI标准指标上报。

接入后,以下能力将被自动监控:

  • Agent执行链路(agent/turn/step的层级关系及各节点耗时)。
  • LLM调用(模型名称、Token用量、输入/输出内容、首Token耗时TTFT)。
  • 工具调用(Tool name、参数、返回结果、调用耗时)。
  • 会话生命周期(session.start/session.end)。
  • 错误事件(error事件及is_error标记)。

接入步骤

接入探针

  1. 安装探针。

    下载Pi探针,探针下载到本地项目后执行安装。

    pip install huaweicloud_opentelemetry_instrumentation_pi-1.0.0-py3-none-any.whl --force-reinstall

    安装 OpenTelemetry 依赖包(版本要求与探针 pyproject.toml 声明一致)。

    pip install "opentelemetry-api>=1.39.1"
    pip install "opentelemetry-sdk>=1.39.1"
    pip install "opentelemetry-exporter-otlp>=1.39.1"
    pip install "opentelemetry-exporter-otlp-proto-grpc>=1.39.1"
    pip install "opentelemetry-semantic-conventions==0.60b1"

    wheel包内已附带编译好的TypeScript探针产物,位于opentelemetry/instrumentation/pi_node/目录下,无需额外执行npm install安装或tsc编译。

  2. 配置环境变量。

    您需要手动为Python应用添加以下环境变量,环境变量的具体值可以在平台中获取:

    环境变量同时作用于TS探针(Node进程)与Python采集器(Python进程)。建议在启动两个进程之前,先在同一个终端中统一设置。

    # === 采集模式与共享目录(TS 探针 + 采集器共用)===
    # TS 探针模式:auto / core / coding / off
    #   方式一(preload 接入 pi-agent-core):core
    #   方式二(显式插桩 pi-agent-core):core
    #   方式三(Extension 接入 pi-coding-agent):coding
    #   不确定时可用 auto,探针会自动判断
    export PI_INSTRUMENTATION_MODE=auto                  # TS 探针模式:auto / core / coding / off
    export PI_JSONL_DIR=/var/lib/pi/events               # JSONL 事件输出目录(TS 探针写入,Python 采集器读取)
    export PI_CHECKPOINT=/var/lib/pi/collector-checkpoint.json  # 采集器断点续传文件路径,防止重复上报
    export PI_EXPORTER=otlp                              # 采集器上报后端:console(仅打印到屏幕,调试用)/ otlp(上报到云上)
    export PI_DEBUG=false                                # 是否输出探针内部错误日志
    
    # === 鉴权与上报地址(采集器上报使用)===
    export agentarts_agentops_otel_agent_apm_enable=true  # 启用 APM Trace,默认 false
    export agentarts_agentops_otel_agent_aom_enable=true  # 启用 AOM Metrics,默认 false
    
    # 智能体信息配置
    export agent_domain_id=<your-domain-id>          # 账号 domain ID
    export agentarts_project_id=<your-project-id>    # 账号 Project ID
    export agent_resource_id=<your-resource-id>      # 智能体 ID
    export resource_type=agent                       # 智能体类型:agent / multiagents / workflow
    
    # APM 配置(探针自动转换为标准 OTLP headers/endpoint,无需手动设置 OTEL_EXPORTER_OTLP_*)
    export agentarts_agentops_apm_token=<your-apm-token>             # APM 接入凭证 Token
    export agentarts_agentops_apm_exporter_endpoint=<apm-endpoint>   # APM 数据上报地址
    
    # AOM 配置(同上,探针自动转换)
    export agentarts_agentops_aom_access_code=<your-aom-access-code>  # AOM 接入凭证
    export agentarts_agentops_aom_exporter_endpoint=<aom-endpoint>    # AOM 数据上报地址
    export agentarts_agentops_aom_prometheus_id=<your-prometheus-id>  # Prometheus 实例 ID
    
    export OTEL_SERVICE_NAME=pi-collector

    上面这些agentarts_agentops_*变量,探针会在启动时自动转换成 OpenTelemetry 标准的环境变量,比如:

    • OTEL_EXPORTER_OTLP_TRACES_HEADERS
    • OTEL_EXPORTER_OTLP_TRACES_ENDPOINT
    • OTEL_EXPORTER_OTLP_METRICS_HEADERS
    • OTEL_EXPORTER_OTLP_METRICS_ENDPOINT

    pi_collector内部会读取这些标准变量来完成上报。因此您不需要手动设置OTEL_EXPORTER_OTLP_*变量。

    环境变量的具体值可以在AgentArts平台中找到。

    图1 获取环境变量信息

  3. 启动Python采集器。

    TS探针写入JSONL后,需要运行pi_collector读取事件并上报。建议先启动采集器,再启动Pi应用。

    • 方式一:CLI 命令行启动(推荐)
      使用 huaweicloud-instrument 命令启动。该命令是探针安装后提供的,作用是:先把 agentarts_agentops_* 鉴权变量转换成标准OTLP配置,然后执行后面的采集器程序。命令如下:
      huaweicloud-instrument python -m opentelemetry.instrumentation.pi_collector --exporter otlp

      若已通过包安装注册了顶层sitecustomize.py,也可直接使用python -m opentelemetry.instrumentation.pi_collector --exporter otlp命令启动;命令结尾如果改为--exporter console,则表示调试模式无需鉴权,可直接用python -m启动。

      采集器支持的常用启动参数:

      表1 启动参数说明

      参数

      说明

      默认值

      --input-dir

      Pi事件输出目录,采集器从这里读取JSONL文件

      PI_JSONL_DIR或.pi/events

      --checkpoint

      断点续传文件路径,记录上次读取位置

      PI_CHECKPOINT或.pi/collector-checkpoint.json

      --poll-interval

      轮询事件文件的间隔(秒)

      0.25

      --exporter

      上报后端:console(只打印,不上传)/ otlp(上传到云上)

      PI_EXPORTER或console

      --once

      只扫描一次事件目录,处理完已有事件后退出。用于调试,不常驻运行

      否

      --log-level

      日志级别

      PI_LOG_LEVEL或INFO

      关于--once参数:默认情况下,采集器启动后会一直运行,每隔--poll-interval秒扫描一次事件目录,持续处理新产生的事件。加上--once后,采集器只扫描一次,处理完当前已有的所有事件后就退出,不会驻留。它主要用于调试,比如想快速确认“当前事件文件能不能被正确解析”时使用,不适合长期运行。

      单次扫描调试示例,该命令只扫描一次事件目录,把解析结果打印到屏幕(console模式),不向云上上报,不需要鉴权。

      python -m opentelemetry.instrumentation.pi_collector --once --exporter console
    • 方式二:Python 代码内嵌启动

      方式一是把采集器作为独立进程运行。采集器是一个Python进程,Pi应用是一个Node进程,两个进程各跑各的。方式二则是把采集器嵌入到您自己的Python程序里运行,与Python程序共用一个Python进程。

      这种方式适合您已经在用Python写一些周边逻辑(比如批量处理、自动化脚本),不想再额外管理一个采集器进程的场景。注意:Pi应用本身仍然是Node进程,TS探针也仍在Node进程内,方式二只是把“读取JSONL并上报”这一步放到了您自己的Python进程里。

      from pathlib import Path
      from opentelemetry.instrumentation.pi_collector import PiCollector
      from opentelemetry.instrumentation.pi_collector.runtime import create_otel_runtime
      
      # 创建 OTel Runtime,使用 OTLP 上报(读取 OTEL_EXPORTER_OTLP_* 环境变量)
      runtime = create_otel_runtime("otlp")
      
      # 构建 PiCollector,读取 Pi 框架输出的事件文件
      collector = PiCollector(
          input_dir=Path("/var/lib/pi/events"),
          checkpoint_path=Path("/var/lib/pi/collector-checkpoint.json"),
          tracer=runtime.tracer,
          meter=runtime.meter,
          poll_interval=0.25,
      )
      
      try:
          # 持续轮询事件文件,转换为 OTel Span 与 Metrics 上报
          collector.run_forever()
      except KeyboardInterrupt:
          pass
      finally:
          collector.shutdown()
          runtime.shutdown()

      启动采集器进程:

      # OTLP 模式需通过 huaweicloud-instrument 启动,用以加载 sitecustomize 完成鉴权转换
      huaweicloud-instrument python collector_app.py

      如果仅用--exporter console调试,可以直接使用:

      python collector_app.py

      采集器启动后,会持续读取PI_JSONL_DIR目录下的JSONL事件。如果此时还没有事件文件,采集器会等待。

  4. 启动Pi应用并加载TS探针。

    Pi应用是Node.js应用,TS探针运行在Pi应用进程内。根据您使用的Pi框架,从下面三种方式中选一种。不要同时启用多种。

    先找到TS探针文件所在路径:

    # opentelemetry 是命名空间包,__file__ 为 None,需用 __path__ 取实际路径
    python -c "import opentelemetry; print(list(opentelemetry.__path__)[0])"

    输出示例:

    /opt/conda/lib/python3.11/site-packages/opentelemetry

    记住这个路径,下面用$OTEL_PY_HOME表示。TS探针的关键入口文件为:

    表2 TS探针入口文件

    探针

    入口文件(相对 $OTEL_PY_HOME)

    用途

    pi_agent_core

    instrumentation/pi_node/pi_agent_core/dist/src/preload.js

    用于preload方式:通过Node启动参数提前加载,自动为Agent类附加事件记录能力。

    pi_agent_core

    instrumentation/pi_node/pi_agent_core/dist/src/index.js

    用于显式插桩方式:在代码中手动调用 instrumentAgent(agent)。

    pi_coding_agent

    instrumentation/pi_node/pi_coding_agent/dist/src/index.js

    用于Extension方式:作为Pi Coding Agent的扩展插件注册。

    • 方式一:接入 pi-agent-core(preload 自动接入,推荐)

      什么是preload:preload是指在应用正式代码执行之前,先加载一段指定代码。Node.js 20.6+提供了--import参数,可以在启动时指定一个预加载脚本。通过把TS探针的preload.js作为预加载脚本传入,Node就会在运行你的应用之前先执行它。这样,探针就有机会在应用代码运行之前,为Pi框架的Agent类附加事件记录能力。

      工作流程如下:

      1. 找到您的应用所使用的Pi框架包(默认是@earendil-works/pi-agent-core)。
      2. 给这个包中的Agent类增加事件记录能力。这里说的“增加事件记录能力”,是指在原有代码基础上附加记录功能,而不是修改原有代码本身。当Agent的prompt和continue方法被调用时,探针会自动捕获执行过程。
      3. 将捕获到的事件写入JSONL文件。

      整个过程对业务代码没有侵入,您不需要在应用代码中import任何探针模块。

      启动命令:

      # 设置采集模式与事件输出目录
      export PI_INSTRUMENTATION_MODE=core
      export PI_JSONL_DIR=/var/lib/pi/events
      
      # 启动 Pi 应用
      # 注意:file:// 后是 wheel 内 preload.js 的绝对路径
      # <app.mjs> 替换为用户实际的 Pi 框架应用入口文件
      node --import "file:///opt/conda/lib/python3.11/site-packages/opentelemetry/instrumentation/pi_node/pi_agent_core/dist/src/preload.js" <app.mjs>

      命令说明:

      • node:Node.js运行时。
      • --import "file://...":Node.js的预加载参数。它表示“在运行应用代码之前,先执行这个参数指定的脚本”。它会先加载探针的preload.js,由探针完成对Agent类的处理。
      • <app.mjs>:用户自己开发的Pi框架应用入口文件(不是探针提供的文件),文件名与扩展名不限(.mjs / .js 均可),通常是用户创建Agent实例并调用prompt/continue的主文件。

      如果您的应用使用的包名不是默认的@earendil-works/pi-agent-core,需要提前设置:

      export PI_AGENT_CORE_PACKAGE=@your-org/pi-agent-core

      preload脚本不会阻止应用启动。即使它加载失败(例如找不到Pi框架包),应用仍然可以正常运行。但此时不会产生观测数据。如果设置了PI_DEBUG=true,可以在日志中看到失败原因。建议在接入阶段开启PI_DEBUG=true,确认preload正常工作后再关闭。

      如果您使用tsx运行TypeScript应用,可以通过NODE_OPTIONS传递--import:

      export NODE_OPTIONS="--import file:///opt/conda/lib/python3.11/site-packages/opentelemetry/instrumentation/pi_node/pi_agent_core/dist/src/preload.js"
      npx tsx src/app.ts

      这种方式与直接使用node --import等效。

    • 方式二:接入 pi-agent-core(显式插桩)

      什么是显式插桩:在应用入口代码中手动调用探针提供的instrumentAgent函数,把Agent实例交给探针处理。相比preload,这种方式需要改一点代码,但好处是控制更明确、不依赖启动参数。

      如果不方便修改启动命令,也可以在应用入口代码中显式调用:

      import { Agent } from "@earendil-works/pi-agent-core";
      import { instrumentAgent } from "/opt/conda/lib/python3.11/site-packages/opentelemetry/instrumentation/pi_node/pi_agent_core/dist/src/index.js";
      
      const agent = new Agent({ /* Pi 配置 */ });
      instrumentAgent(agent);
      await agent.prompt("hello");

      显式插桩与preload二选一,避免重复处理。

    • 方式三:接入 pi-coding-agent(Extension Hook)

      什么是Extension Hook:Pi Coding Agent支持通过配置文件加载扩展(Extension)。扩展可以在Pi框架运行过程中注册事件监听器,当指定事件发生时被调用。TS探针提供了一个Extension入口,把它注册到Pi Coding Agent的配置中,Pi框架就会在运行时自动调用它,达到记录事件的目的。

      将TS探针入口文件以绝对路径加入Pi Coding Agent配置目录下的settings.json。

      Pi Coding Agent的配置目录由Pi框架自身决定,常见为~/.pi/ 或 ~/.config/pi/,具体路径以Pi框架官方文档为准。如果不确定,可以执行pi --help查看,或检查~/.pi/下是否已有settings.json。

      示例:

      # 假设 Pi 配置目录为 ~/.pi/,将探针入口写入 settings.json(路径必须为绝对路径)
      cat > ~/.pi/settings.json <<'EOF'
      {
        "extensions": [
          "/opt/conda/lib/python3.11/site-packages/opentelemetry/instrumentation/pi_node/pi_coding_agent/dist/src/index.js"
        ]
      }
      EOF
      
      export PI_INSTRUMENTATION_MODE=coding
      pi

      这个Extension会在Pi Coding Agent中注册17种事件监听器,覆盖session、agent、turn、provider、message、tool等事件类型。当这些事件发生时,探针会自动记录。

  5. 验证TS探针是否生效。

    启动Pi应用并触发一次Agent/LLM/Tool调用后,检查PI_JSONL_DIR指向的目录下是否生成*.jsonl事件文件:

    ls -la $PI_JSONL_DIR/
    # 应看到类似 pi-agent-core-<timestamp>.jsonl 的事件文件
    # 取其中一个文件查看首行
    head -1 $PI_JSONL_DIR/*.jsonl | python -m json.tool
    # 应输出含 schema_version=1.0、source=pi-agent-core 或 pi-coding-agent 的事件

    若文件未生成,请检查:

    1. 确认PI_INSTRUMENTATION_MODE不为off,检查命令如下:
      echo $PI_INSTRUMENTATION_MODE

      可选值为auto、core、coding、off。如果输出为空或为off,说明探针不会工作。需要重新设置(auto或core或coding),并确保这个环境变量在启动Pi应用的那个终端里生效。

    2. 确认--import路径是否正确,preload.js是否已加载。可设置PI_DEBUG=true查看探针自身日志。

      第一步:确认 preload.js 文件是否存在。

      先执行以下命令获取Python包安装目录:

      python -c "import opentelemetry; print(list(opentelemetry.__path__)[0])"

      将输出与固定后缀拼接,得到实际路径:

      <Python包安装目录>/instrumentation/pi_node/pi_agent_core/dist/src/preload.js

      然后确认这个文件存在:

      ls -la <上一步拼接得到的实际路径>

      如果文件存在,命令会输出文件信息;如果输出类似No such file or directory信息,说明路径不对,或者wheel包未正确安装,需要重新检查安装步骤。

      第二步:确认preload是否被加载。

      设置环境变量并重新启动Pi应用:

      export PI_DEBUG=true

      然后启动Pi应用,观察启动日志:如果看到 [huawei-pi] bootstrap: Agent patched successfully,说明preload已经正常加载,并且成功为Agent类附加了事件记录能力;如果看到 failed to patch Agent,说明preload执行了,但处理Agent类时出错。根据错误信息进一步排查(比如目标框架包未安装、包名不匹配等);如果完全没有看到任何日志,说明preload根本没有被加载。回到第一步,确认路径是否正确、--import 参数是否写在正确的位置。

    3. 确认Pi应用进程对PI_JSONL_DIR有写权限。
      touch $PI_JSONL_DIR/test_write && echo "write ok" && rm $PI_JSONL_DIR/test_write

      如果提示Permission denied,说明当前用户没有写权限。可以用ls -ld $PI_JSONL_DIR查看目录属主和权限,必要时用chmod或chown调整,或换一个当前用户可写的目录作为PI_JSONL_DIR。

    如果这一步验证通过,说明TS探针已经正常工作,事件文件正在生成,采集器会读取并上报。

环境变量说明

表3 环境变量说明

环境变量

说明

适用进程

是否必填

默认值 / 示例

PI_INSTRUMENTATION_MODE

TS探针工作模式:auto(自动判断)/ core(用于pi-agent-core)/ coding(用于pi-coding-agent)/ off(关闭)

Node

是

auto

PI_JSONL_DIR

JSONL事件输出目录。TS探针写入,Python采集器读取

两者

是

.pi/events

PI_AGENT_CORE_PACKAGE

pi-agent-core的目标包名,仅在preload方式下使用。用于告诉探针去哪个包找Agent类

Node

否

@earendil-works/pi-agent-core

PI_CHECKPOINT

采集器断点续传文件路径。记录上次读取到哪个位置,重启后从该位置继续

Python

是

.pi/collector-checkpoint.json

PI_EXPORTER

采集器上报后端:console(仅打印到屏幕,调试用)/ otlp(上报到云上)

Python

是

console

PI_DEBUG

是否输出探针内部错误日志。设为true时,探针在启动和处理过程中会打印详细日志,便于排查问题

两者

否

false

OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT

是否采集LLM的输入/输出内容。设为false时,上报的数据中不包含对话内容,只保留结构和统计信息,适用于对数据敏感的场景

Node

否

false

PI_REDACT_SECRETS

是否对常见的敏感信息(如API Key、Token)做脱敏处理后再写入事件文件。设为true时,探针会将这些字段替换成占位符

Node

否

true

PI_MAX_STRING_BYTES

事件中单个字符串的最大字节数。超过部分会被截断,防止单个字段过大

Node

否

16384

PI_MAX_FILE_BYTES

单个JSONL文件的分段大小。文件达到此大小后会滚动生成新文件

Node

否

67108864

agent_domain_id

账号 domain ID

Python

是

your-domain-id

agentarts_project_id

华为云项目 ID

Python

是

your-project-id

agent_resource_id

智能体 ID

Python

是

your-agent-id

resource_type

智能体类型:agent / multiagents / workflow

Python

是

agent

agentarts_agentops_otel_agent_apm_enable

是否启用 APM Trace

Python

是

默认 false

agentarts_agentops_otel_agent_aom_enable

是否启用 AOM Metrics

Python

是

默认 false

agentarts_agentops_apm_token

APM 接入凭证(自动转换为 OTEL_EXPORTER_OTLP_TRACES_HEADERS)

Python

APM 启用时必填

your-apm-token

agentarts_agentops_apm_exporter_endpoint

APM OTLP gRPC 上报地址(自动转换为 OTEL_EXPORTER_OTLP_TRACES_ENDPOINT)

Python

APM 启用时必填

apm-endpoint

agentarts_agentops_aom_access_code

AOM 接入凭证(自动转换为 Metrics headers 的 Bearer Token)

Python

AOM 启用时必填

your-aom-access-code

agentarts_agentops_aom_exporter_endpoint

AOM OTLP gRPC 上报地址(自动转换为 OTEL_EXPORTER_OTLP_METRICS_ENDPOINT)

Python

AOM 启用时必填

aom-endpoint

agentarts_agentops_aom_prometheus_id

Prometheus 实例 ID

Python

AOM 启用时必填

your-prometheus-id

OTEL_SERVICE_NAME

采集器服务名称

Python

否

pi-collector

OTEL_SERVICE_VERSION

采集器服务版本

Python

否

0.1.0

OTEL_DEPLOYMENT_ENVIRONMENT

部署环境名称

Python

否

development

OTEL_METRIC_EXPORT_INTERVAL

指标上报周期(毫秒)

Python

否

60000

PI_LOG_LEVEL

采集器日志级别

Python

否

INFO

示例代码

以下示例展示一个基于Pi Agent Core框架创建的Agent,并通过TS探针接入可观测。示例中配置了read_file和list_files两个工具。

示例中使用华为云MaaS服务提供的大模型接口,可登录华为云MaaS服务控制台,创建API Key。

图2 创建API Key

整体流程

  1. 创建项目目录和代码文件。
  2. 安装Node.js依赖。
  3. 修改代码中的配置项(API Key、探针路径等)。
  4. 在终端中配置环境变量并启动Python采集器。
  5. 在另一个终端中启动Pi Agent。
  6. 验证事件文件是否生成。
  7. 在AgentArts平台查看调用链和指标。

代码目录结构

按照以下目录要求创建文件夹及代码文件。

core_agent_demo/
├── src/
│   ├── app.ts          # Agent 主程序,放业务逻辑,不包含探针代码
│   └── tools.ts        # 工具定义(read_file, list_files)
├── bootstrap.mjs        # 探针预加载入口由NODE_OPTIONS=--import加载,在应用启动前为Agent类附加事件记录能力
├── package.json         # Node.js项目依赖配置文件。声明项目名称、依赖包和启动脚本
├── tsconfig.json        # TypeScript 编译配置
├── README.md            # 测试用文件(Agent 会读取)
└── run.bat              # 启动脚本(含 preload + 环境变量)
  • package.json:项目依赖文件

    这个文件告诉Node.js:项目叫什么、依赖哪些第三方包、用哪个命令启动。执行 npm install 时,npm会读取这个文件,自动下载其中声明的依赖包。

    {
      "name": "pi-core-agent-demo",
      "version": "1.0.0",
      "private": true,
      "type": "module",
      "scripts": {
        "start": "tsx src/app.ts"
      },
      "dependencies": {
        "@earendil-works/pi-agent-core": "^0.85.1",
        "@earendil-works/pi-ai": "^0.85.1"
      },
      "devDependencies": {
        "tsx": "^4.0.0",
        "typescript": "^5.9.0",
        "@types/node": "^22.0.0"
      }
    }
  • src/tools.ts:工具定义文件

    Pi Agent Core的工具是一个包含name、description、parameters(JSON Schema)、execute方法的对象。大模型会根据description和parameters自动决定什么时候调用工具、传什么参数。

    import * as fs from "node:fs";
    
    // ── read_file 工具 ──
    export const read_file = {
      name: "read_file",
      description: "读取指定路径的文件内容",
      parameters: {
        type: "object" as const,
        properties: {
          path: {
            type: "string",
            description: "要读取的文件路径",
          },
        },
        required: ["path"],
      },
    
      async execute(toolCallId: string, params: { path: string }) {
        try {
          const content = fs.readFileSync(params.path, "utf-8");
          const truncated = content.length > 4000
            ? content.substring(0, 4000) + "\n...[truncated]"
            : content;
          return {
            content: [{ type: "text" as const, text: truncated }],
          };
        } catch (err: any) {
          return {
            content: [{ type: "text" as const, text: `读取文件失败: ${err.message}` }],
            isError: true,
          };
        }
      },
    };
    
    // ── list_files 工具 ──
    export const list_files = {
      name: "list_files",
      description: "列出指定目录下的文件和子目录",
      parameters: {
        type: "object" as const,
        properties: {
          path: {
            type: "string",
            description: "要列出的目录路径,默认为当前目录",
          },
        },
        required: [],
      },
    
      async execute(toolCallId: string, params: { path?: string }) {
        try {
          const dir = params.path || ".";
          const entries = fs.readdirSync(dir, { withFileTypes: true });
          const items = entries.map((entry) => {
            const type = entry.isDirectory() ? "DIR " : "FILE";
            return `${type}  ${entry.name}`;
          });
          const result = items.length > 0 ? items.join("\n") : "(空目录)";
          return {
            content: [{ type: "text" as const, text: `${dir}:\n${result}` }],
          };
        } catch (err: any) {
          return {
            content: [{ type: "text" as const, text: `列目录失败: ${err.message}` }],
            isError: true,
          };
        }
      },
    };
  • tsconfig.json:TypeScript编译配置文件

    这个文件告诉TypeScript编译器:按什么规则编译 .ts 文件。这里的 module 和 moduleResolution 配置是为了让代码能与Pi框架和tsx运行时正确配合:

    • module: "ESNext":使用最新的ES模块语法(import/export)。
    • moduleResolution: "bundler":使用打包工具风格的模块解析方式,能正确处理Pi框架的subpath exports(即允许import ... from "@earendil-works/pi-ai/compat"这种带子路径的导入)。
    {
      "compilerOptions": {
        "target": "ES2022",
        "module": "ESNext",
        "moduleResolution": "bundler",
        "esModuleInterop": true,
        "strict": true,
        "skipLibCheck": true,
        "outDir": "dist"
      },
      "include": ["src/**/*.ts"]
    }
  • src/app.ts:Agent智能体的主程序
    这是Agent的业务代码文件。它不包含任何探针相关代码,探针会在应用启动时通过bootstrap.mjs自动加载。
    import { Agent } from "@earendil-works/pi-agent-core";
    import { createProvider, envApiKeyAuth, openAICompletionsApi } from "@earendil-works/pi-ai/compat";
    import { read_file, list_files } from "./tools.js";
    
    // ── LLM 配置(华为云 MaaS 服务,OpenAI 兼容接口)──
    const LLM_BASE_URL = "https://api.modelarts-maas.com/openai/v1";
    const LLM_API_KEY = process.env.LLM_API_KEY || "";
    const LLM_MODEL_ID = "deepseek-v4.1-flash";
    
    // ── 1. 创建 Pi 标准 Provider ──
    const provider = createProvider({
      id: "maas",
      name: "Huawei Cloud MaaS",
      baseUrl: LLM_BASE_URL,
      auth: envApiKeyAuth("Huawei Cloud MaaS API key", ["LLM_API_KEY"]),
      models: [{
        id: LLM_MODEL_ID,
        name: "deepseek-v4.1-flash",
        api: "openai-completions" as const,
        provider: "maas",
        baseUrl: LLM_BASE_URL,
        reasoning: false,
        input: ["text"],
        cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
        contextWindow: 128000,
        maxTokens: 4096,
      }],
      api: openAICompletionsApi(),
    });
    
    const model = provider.getModels()[0];
    
    async function main() {
      // ── 校验 API Key ──
      if (!LLM_API_KEY) {
        console.error("[demo] 请设置环境变量 LLM_API_KEY(在华为云 MaaS 控制台创建 API Key)");
        process.exit(1);
      }
    
      // ── 2. 创建 Agent ──
      // 注意:streamFn 使用 .bind(provider) 确保 provider 上下文不丢失
      const agent = new Agent({
        streamFn: provider.streamSimple.bind(provider),
        initialState: {
          model: model,
          systemPrompt: "你是一个代码分析助手。你可以使用工具读取文件和列出目录内容。请用中文回答。",
          tools: [read_file, list_files],
        },
        sessionId: `core-demo-${Date.now()}`,
        getApiKey: async (providerName: string) => {
          if (providerName === "maas") return LLM_API_KEY;
          return undefined;
        },
      });
    
      // ── 3. 运行 Agent ──
      const task = process.argv[2] || "列出当前目录的文件,读取 README.md 并总结内容";
      console.log(`[demo] 用户: ${task}`);
    
      try {
        await agent.prompt(task);
        await agent.waitForIdle();
        console.log("[demo] Agent 运行完成");
      } catch (err: any) {
        console.log("[demo] Agent 运行出错:", err.message);
      }
    }
    
    main().catch((err) => {
      console.error("[demo] 运行失败:", err);
      process.exit(1);
    });
    • app.ts不会import任何探针代码。
    • streamFn: provider.streamSimple.bind(provider) 中,provider.streamSimple是provider对象上的方法。如果直接赋值给streamFn,会丢失this上下文,导致运行时内部访问this.baseUrl / this.models 等属性失败。使用.bind(provider)绑定上下文,确保方法内部this指向正确的provider实例。
  • bootstrap.mjs:preload入口

    这是探针的预加载入口,由NODE_OPTIONS=--import加载。它会在业务代码运行之前,找到Pi框架的Agent类,并给它附加事件记录能力。

    // bootstrap.mjs - 在用户代码 import 之前,先加载并 patch pi-agent-core
    
    // patchEntry 指向 wheel 内编译好的 patch.js
    // 通过 python -c "import opentelemetry; print(list(opentelemetry.__path__)[0])" 获取路径
    // Linux 示例: /opt/conda/lib/python3.11/site-packages/opentelemetry/instrumentation/pi_node/pi_agent_core/dist/src/patch.js
    // Windows 示例: C:/Users/xxx/site-packages/opentelemetry/instrumentation/pi_node/pi_agent_core/dist/src/patch.js
    // 路径中的空格用 %20 编码(如 Program%20Files)
    const patchEntry = "file:///<$OTEL_PY_HOME>/instrumentation/pi_node/pi_agent_core/dist/src/patch.js";
    
    try {
      // 使用动态 import 让 Node 模块解析自动处理路径,无需手动拼接 node_modules 路径
      const piModule = await import("@earendil-works/pi-agent-core");
      if (typeof piModule.Agent === "function") {
        const patchModule = await import(patchEntry);
        if (typeof patchModule.patchAgentClass === "function") {
          patchModule.patchAgentClass(piModule.Agent);
          if (process.env.PI_DEBUG === "true") {
            console.warn("[huawei-pi] bootstrap: Agent patched successfully");
          }
        } else {
          if (process.env.PI_DEBUG === "true") {
            console.warn("[huawei-pi] bootstrap: patchAgentClass not found in patch module");
          }
        }
      }
    } catch (error) {
      if (process.env.PI_DEBUG === "true") {
        console.warn("[huawei-pi] bootstrap: failed to patch Agent", error);
      }
    }

    <$OTEL_PY_HOME>需要替换为下面命令的实际输出路径:

    python -c "import opentelemetry; print(list(opentelemetry.__path__)[0])"
  • README.md:创建一个简单的测试文件,内容随意,例如:
    # Pi Agent Core Demo
    
    这是一个用于测试 Pi 框架接入 AgentArts 观测的示例项目。
  • run.sh / run.bat:启动脚本

    脚本中各参数取值请按照实际填写。

    Linux/macOS系统

    #!/bin/bash
    # run.sh (Linux/macOS)
    cd "$(dirname "$0")"
    
    # JSONL 事件输出目录(与 Collector 共用)
    export PI_JSONL_DIR=/var/lib/pi/events
    
    # TS 探针调试日志
    export PI_DEBUG=true
    
    # 采集 LLM 输入/输出内容
    export OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true
    
    # LLM API Key(在华为云 MaaS 控制台创建)
    export LLM_API_KEY=<your-api-key>
    
    # Pi 业务属性(写入 JSONL resource 字段)
    export PI_DOMAIN_ID=<your-domain-id>
    export PI_PROJECT_ID=<your-project-id>
    export PI_GEN_AI_RESOURCE_ID=<your-agent-id>
    export PI_GEN_AI_RESOURCE_TYPE=agent
    export PI_USER_ID=pi-demo-user
    export PI_AGENT_NAME=pi-demo-agent
    export PI_RESOURCE_VERSION=default
    
    # 通过 bootstrap.mjs 预加载探针
    export NODE_OPTIONS="--import file:///path/to/core_agent_demo/bootstrap.mjs"
    
    npx tsx src/app.ts "$@"

    Windows系统

    @echo off
    REM run.bat (Windows)
    cd /d "%~dp0"
    
    set "PI_JSONL_DIR=F:\pi-demo\events"
    set "PI_DEBUG=true"
    set "OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true"
    set "LLM_API_KEY=<your-api-key>"
    set "PI_DOMAIN_ID=<your-domain-id>"
    set "PI_PROJECT_ID=<your-project-id>"
    set "PI_GEN_AI_RESOURCE_ID=<your-agent-id>"
    set "PI_GEN_AI_RESOURCE_TYPE=agent"
    set "PI_USER_ID=pi-demo-user"
    set "PI_AGENT_NAME=pi-demo-agent"
    set "PI_RESOURCE_VERSION=default"
    
    REM Windows 路径含空格需用 %%20 编码
    set "NODE_OPTIONS=--import file:///F:/path/to/core_agent_demo/bootstrap.mjs"
    
    npx tsx src\app.ts %*

    Windows系统中.bat文件注意事项:.bat文件必须使用CRLF行尾(LF行尾会导致set命令解析失败);路径中的空格用%20编码(%在.bat中需转义为%%)。

安装依赖

在项目根目录 core_agent_demo 下执行:

npm init -y
npm install @earendil-works/pi-agent-core@^0.85.1 @earendil-works/pi-ai@^0.85.1
npm install -D tsx@^4.0.0 typescript@^5.9.0 @types/node@^22.0.0

修改代码配置

  1. 修改bootstrap.mjs中的patchEntry路径。

    代码文件中的<$OTEL_PY_HOME>需要替换为下面命令的实际输出路径:

    python -c "import opentelemetry; print(list(opentelemetry.__path__)[0])"

  2. 修改run.sh或run.bat中的环境变量。

    • LLM_API_KEY:华为云MaaS模型的API Key。
    • PI_DOMAIN_ID、PI_PROJECT_ID、PI_GEN_AI_RESOURCE_ID:AgentArts平台创建的Pi框架智能体身份信息。
    • PI_JSONL_DIR:这个变量指定JSONL事件文件的输出目录。它的值由用户自己选择。代码示例中的值 /var/lib/pi/events(Linux/macOS)或 F:\pi-demo\events(Windows)只是一个示例,可以改成任意合适的目录。建议使用一个专门的空目录,避免与已有文件混在一起。
    • NODE_OPTIONS:这个路径必须替换为你项目中 bootstrap.mjs 的绝对路径,不能直接使用示例中的 /path/to/core_agent_demo/bootstrap.mjs。

      获取方法:

      • Linux/macOS:在项目根目录执行 realpath bootstrap.mjs,输出的就是绝对路径。
      • Windows:右键单击bootstrap.mjs文件,选择“属性”,查看“位置”字段,与文件名拼接得到完整路径。注意路径中的反斜杠\在NODE_OPTIONS中需要改为正斜杠/,且路径中的空格要用%20编码。

  3. 给run.sh添加可执行权限(对于Linux / macOS系统)。

    chmod +x run.sh

启动Python采集器

打开一个终端(假设为终端A),进入项目目录,执行以下命令。
huaweicloud-instrument python -m opentelemetry.instrumentation.pi_collector --exporter otlp

采集器会持续运行,读取PI_JSONL_DIR目录下的JSONL事件并上报。请保持此终端窗口运行,不要关闭。

启动Pi Agent

打开另一个终端(假设为终端B),进入项目目录core_agent_demo,执行:

# Linux / macOS用户执行:
./run.sh
# Windows 用户执行:
run.bat

预期输出:

[huawei-pi] bootstrap: Agent patched successfully
==================================================
Core Agent Demo (Pi Agent Core + DeepSeek-V4.1-Flash)
模型: DeepSeek-V4.1-Flash (华为云 MaaS)
工具: read_file, list_files
==================================================
[demo] 用户: 列出当前目录的文件,读取 README.md 并总结内容

(Agent 调用 list_files 列目录)
(Agent 调用 read_file 读取 README.md)
(Agent 综合结果输出总结)

[demo] Agent 运行完成

验证事件文件

在终端B中执行:

ls -la $PI_JSONL_DIR/

应看到类似pi-agent-core-<pid>-<uuid>-00000.jsonl的文件。

查看其中一个文件的首行:

head -1 $(ls $PI_JSONL_DIR/*.jsonl | head -1) | python -m json.tool

应输出包含schema_version=1.0、source=pi-agent-core的事件。

在AgentArts查看调用链

Agent运行后,Python采集器会自动读取JSONL事件并上报。在AgentArts平台的“观测 > 调用链分析”中,可看到如下类似的调用链结构:

pi agent (AGENT)
└─ pi turn (WORKFLOW)
    ├─ chat deepseek-v4.1-flash (LLM) → execute_tool list_files (PLUGIN)
    └─ chat deepseek-v4.1-flash (LLM) → execute_tool read_file (PLUGIN)
        └─ chat deepseek-v4.1-flash (LLM) → 最终总结

查看监控数据

数据上报成功后,可在AgentArts智能体平台的观测功能中查看监控数据。

  1. 登录AgentArts 智能体平台。
  2. 在左侧导航栏选择“观测与优化 > 观测”。
  3. 在“智能体列表”页面,找到已接入的智能体,单击智能体名称进入详情页面。

查看业务指标

在“指标分析”页签中,可以查看关键业务指标:

  • Tokens消耗:Input Tokens和Output Tokens的消耗总量
  • 模型调用次数:应用调用大模型的累计次数
  • 模型调用平均耗时:模型调用的平均响应时间
  • 模型调用成功率:大模型调用成功的比例
  • 会话数:应用产生的会话总数
  • 响应成功率:服务响应的成功率
  • 工具调用次数:Agent 调用工具的累计次数及平均耗时
  • 事件采集延迟:采集器处理事件相对事件产生时间的延迟

查看调用链信息

在“调用链分析”页签中,可以查看完整的Trace调用链路:

  • 查看Agent、Workflow、LLM、Plugin等Span类型的层级关系及各节点耗时
    • Agent节点对应 agent.start / agent.end
    • Workflow节点对应 turn 与 step 执行
    • LLM节点对应 llm.start / llm.end
    • Plugin节点对应工具调用 tool.start / tool.end
  • 单击具体Span查看详细信息:
    • LLM节点:可查看模型名称、输入/输出消息(Input/Output Messages)、Token消耗(Total tokens)、首Token耗时(TTFT)
    • Tool节点:可查看工具名称、输入参数、返回结果、调用耗时
    • Agent节点:可查看会话级累计Token用量(含cache_read/cache_write/reasoning tokens)及总成本

查看会话分析

在“会话分析”页签中,可以查看智能体与用户对话的历史数据:

  • 追踪对话上下文
  • 分析模型响应耗时
  • 统计Token消耗
  • 快速定位对话异常问题

更多参考

  • 传递session_id与user_id

    Pi框架在事件中已携带 session_id(事件必填字段)与 user_id(事件resource字段)。开发者需在Pi框架应用侧通过框架API设置会话标识与用户标识,这些信息会随事件写入JSONL文件,采集器自动关联到Span和指标上:

    # Pi 框架应用侧(伪代码,具体 API 以 Pi 框架文档为准)
    agent.run(
        session_id="session-01",
        user_id="pi_user",
        messages=[{"role": "user", "content": "你好"}],
    )
    • session_id:会话标识,用于关联同一会话内的多轮调用,会写入Span的gen_ai.conversation.id属性。
    • user_id:用户标识,用于按用户维度统计和过滤,会写入Span的gen_ai.user.id属性。
  • 跨进程链路关联

    Pi事件支持upstream_traceparent字段,用于将Pi框架产生的调用链与上游服务(如API网关、Web框架)的Trace上下文关联。若上游服务已接入OpenTelemetry,将W3C Traceparent字符串传入Pi框架事件即可实现跨进程链路串联。

  • 控制内容采集

    默认情况下,采集器会采集Pi事件中messages与output_messages字段的LLM 输入/输出内容。如需关闭内容采集,可在Pi框架应用侧配置不输出messages字段,事件中缺失该字段时采集器自动跳过;或设置OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=false。

常见问题

无数据上报

  1. 检查 PI_JSONL_DIR 指向的目录下是否存在 *.jsonl 事件文件。执行 ls -la $PI_JSONL_DIR/。
  2. 确认TS探针已加载。设置 PI_DEBUG=true 后重新启动Pi应用,观察Node侧日志。同时确认 PI_INSTRUMENTATION_MODE 不为 off。
  3. 确认Python采集器已启动。执行 ps -ef | grep pi_collector 查看 pi_collector 进程是否存在。
  4. 检查 PI_EXPORTER 是否设置为 otlp,以及APM/AOM的endpoint是否正确。
  5. 检查APM/AOM凭证(Token/Access Code)是否有效,可从AgentArts平台重新获取。
  6. 查看采集器日志,寻找OTLP上报错误或事件解析错误。

JSONL事件文件未生成

  1. 确认Pi框架应用已正常运行,并触发了Agent/LLM/Tool调用。
  2. 确认TS探针已正确加载。检查preload路径是否正确、目标框架包 @earendil-works/pi-agent-core 是否可以正常解析(可执行 ls -la 检查文件是否存在,或设 PI_DEBUG=true 看日志)。
  3. 确认 PI_JSONL_DIR 配置正确,且Pi应用进程对目录有写权限(可执行 touch $PI_JSONL_DIR/test_write 验证)。

事件丢失或重复上报

  1. 采集器通过checkpoint文件记录进度,确认PI_CHECKPOINT路径可写且未被意外删除。
  2. 事件去重依赖event_id,确认Pi框架生成的event_id全局唯一。
  3. 若事件文件被截断或inode变化,采集器会自动重置offset重新读取。

部分Span缺失或不完整

  1. Span重建依赖operation_id与parent_operation_id的父子关系,确认Pi事件字段完整。
  2. 若父操作事件缺失,采集器会丢弃孤儿子操作的事件并在日志中告警。
  3. 若采集器被强制终止,未关闭的Span会标记pi.incomplete=true后强制结束。

指标上报失败(invalid temporality and type combination)

  1. AOM后端仅接受Gauge类型,采集器已统一使用Gauge,确认使用最新版本探针。
  2. 检查OTEL_EXPORTER_OTLP_METRICS_ENDPOINT是否指向AOM的OTLP地址。

相关文档