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应用,这样事件一产生就能被及时处理。
框架介绍
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标记)。
接入步骤
接入探针
- 安装探针。
下载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编译。
- 配置环境变量。
您需要手动为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 获取环境变量信息
- 启动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事件。如果此时还没有事件文件,采集器会等待。
- 方式一:CLI 命令行启动(推荐)
- 启动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类附加事件记录能力。
工作流程如下:
- 找到您的应用所使用的Pi框架包(默认是@earendil-works/pi-agent-core)。
- 给这个包中的Agent类增加事件记录能力。这里说的“增加事件记录能力”,是指在原有代码基础上附加记录功能,而不是修改原有代码本身。当Agent的prompt和continue方法被调用时,探针会自动捕获执行过程。
- 将捕获到的事件写入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等事件类型。当这些事件发生时,探针会自动记录。
- 方式一:接入 pi-agent-core(preload 自动接入,推荐)
- 验证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 的事件
若文件未生成,请检查:
- 确认PI_INSTRUMENTATION_MODE不为off,检查命令如下:
echo $PI_INSTRUMENTATION_MODE
可选值为auto、core、coding、off。如果输出为空或为off,说明探针不会工作。需要重新设置(auto或core或coding),并确保这个环境变量在启动Pi应用的那个终端里生效。
- 确认--import路径是否正确,preload.js是否已加载。可设置PI_DEBUG=true查看探针自身日志。
先执行以下命令获取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 参数是否写在正确的位置。
- 确认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探针已经正常工作,事件文件正在生成,采集器会读取并上报。
- 确认PI_INSTRUMENTATION_MODE不为off,检查命令如下:
环境变量说明
| 环境变量 | 说明 | 适用进程 | 是否必填 | 默认值 / 示例 |
|---|---|---|---|---|
| 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。
整体流程
- 创建项目目录和代码文件。
- 安装Node.js依赖。
- 修改代码中的配置项(API Key、探针路径等)。
- 在终端中配置环境变量并启动Python采集器。
- 在另一个终端中启动Pi Agent。
- 验证事件文件是否生成。
- 在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
修改代码配置
- 修改bootstrap.mjs中的patchEntry路径。
代码文件中的<$OTEL_PY_HOME>需要替换为下面命令的实际输出路径:
python -c "import opentelemetry; print(list(opentelemetry.__path__)[0])"
- 修改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编码。
- 给run.sh添加可执行权限(对于Linux / macOS系统)。
chmod +x run.sh
启动Python采集器
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智能体平台的观测功能中查看监控数据。
- 登录AgentArts 智能体平台。
- 在左侧导航栏选择“观测与优化 > 观测”。
- 在“智能体列表”页面,找到已接入的智能体,单击智能体名称进入详情页面。
查看业务指标
在“指标分析”页签中,可以查看关键业务指标:
- 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。
常见问题
无数据上报
- 检查 PI_JSONL_DIR 指向的目录下是否存在 *.jsonl 事件文件。执行 ls -la $PI_JSONL_DIR/。
- 确认TS探针已加载。设置 PI_DEBUG=true 后重新启动Pi应用,观察Node侧日志。同时确认 PI_INSTRUMENTATION_MODE 不为 off。
- 确认Python采集器已启动。执行 ps -ef | grep pi_collector 查看 pi_collector 进程是否存在。
- 检查 PI_EXPORTER 是否设置为 otlp,以及APM/AOM的endpoint是否正确。
- 检查APM/AOM凭证(Token/Access Code)是否有效,可从AgentArts平台重新获取。
- 查看采集器日志,寻找OTLP上报错误或事件解析错误。
JSONL事件文件未生成
- 确认Pi框架应用已正常运行,并触发了Agent/LLM/Tool调用。
- 确认TS探针已正确加载。检查preload路径是否正确、目标框架包 @earendil-works/pi-agent-core 是否可以正常解析(可执行 ls -la 检查文件是否存在,或设 PI_DEBUG=true 看日志)。
- 确认 PI_JSONL_DIR 配置正确,且Pi应用进程对目录有写权限(可执行 touch $PI_JSONL_DIR/test_write 验证)。
事件丢失或重复上报
- 采集器通过checkpoint文件记录进度,确认PI_CHECKPOINT路径可写且未被意外删除。
- 事件去重依赖event_id,确认Pi框架生成的event_id全局唯一。
- 若事件文件被截断或inode变化,采集器会自动重置offset重新读取。
部分Span缺失或不完整
- Span重建依赖operation_id与parent_operation_id的父子关系,确认Pi事件字段完整。
- 若父操作事件缺失,采集器会丢弃孤儿子操作的事件并在日志中告警。
- 若采集器被强制终止,未关闭的Span会标记pi.incomplete=true后强制结束。
指标上报失败(invalid temporality and type combination)
- AOM后端仅接受Gauge类型,采集器已统一使用Gauge,确认使用最新版本探针。
- 检查OTEL_EXPORTER_OTLP_METRICS_ENDPOINT是否指向AOM的OTLP地址。