Panda:覆盖整个数据栈的一站式工具

EthPandaOps 发布于 2026-06-03 阅读 190

ethPandaOps团队推出了Panda工具,用于简化以太坊观测数据的查询。

引言

过去十年,工程师与他们所构建平台的可观测性数据进行交互的方式逐渐趋同:将少数几个数据源接入一个集中可视化工具(通常是 Grafana)。当需要寻找答案时,有时已有现成的仪表盘能解决你的确切问题。而其他时候,你得编写一次性查询并祈祷自己没有遗漏某些东西。

自 2022 年我们团队成立以来,查询 ethPandaOps 技术栈的方式一直非常相似。Prometheus 指标存储在 VictoriaMetrics 中,日志在 Loki 中,Xatu 数据存放在 ClickHouse 中,全部可通过 Grafana 访问。每种数据源都有不同的查询语言,跨数据源关联数据既痛苦又繁琐。这个过程还需要人工参与,需要借助经过身份验证的 Web 浏览器点击 Grafana UI,给代理造成了不便。

我们构建了 Panda,以明确且流畅的方式解决这些问题,目标是解锁新的工作方式。Panda 将所有内容整合到一个统一接口中。它提供了 CLI、MCP 服务器以及沙盒化的 Python 运行时,已经知道如何访问我们运行的所有数据源,并且通过我们托管的凭据代理,对任何以太坊核心开发者都是开放的。本文将介绍它如何工作,以及它如何成为我们日常工作流程的一部分。

哪些地方不奏效

将数据栈连接到 LLM 的显而易见的方法是每个数据源对应一个 MCP 工具。一年前我们正是这样做的:一个 ethpandaops-data MCP 服务器,包含 loki_toolprometheus_toolclickhouse_tool,每个都指向我们的 Grafana 数据源。

它勉强能用,但有一个巨大问题。每个工具都将原始结果直接返回给模型,序列化为格式化后的 JSON。如果你用 clickhouse_tool 查询过去一天每个区块的 blob 数量,大约 7000 行 JSON 会直接塞进上下文窗口。这不仅极其昂贵,还限制了代理的复杂性和可靠性,因为上下文腐烂会破坏原始问题,导致代理混淆(有时在发出一次查询后就完全忘记了初始任务!)

这种做法在实用性上也有限。一个上下文包含约 7000 行的代理很难轻松计算所有行的聚合值。我们尝试过通过 jq 管道传输等想法,但发现它对所有用例来说都过于僵化。

Panda:赋能代理

Panda 为模型提供了一个运行时环境,而不是一堆工具。MCP 接口只包含三个工具,每个工具都对应一个 CLI 命令:

  • execute_python (panda execute) 在沙盒中运行代码
  • manage_session (panda session) 在调用之间保持沙盒热状态
  • search (panda search) 对查询示例、操作手册、EIP 和共识规范进行语义搜索

除此之外的一切都是沙盒内的 Python 库,涵盖了整个技术栈:ClickHouse、Prometheus、Loki、Dora 以及信标节点和执行节点。之前会淹没窗口的 blob 查询现在以摘要的形式返回:

panda execute --code '
from ethpandaops import clickhouse

df = clickhouse.query("xatu-cbt", """
    SELECT blob_count
    FROM mainnet.fct_block_blob_count_head FINAL
    WHERE slot_start_date_time >= now() - INTERVAL 1 DAY
""")
print(df["blob_count"].describe())
'

那约 7000 行数据在沙盒内处理并消失,只有 describe() 打印的八行摘要返回给模型。当模型需要循环或连接时,这些操作也在沙盒内运行,而不会变成数百次工具调用,每次中间行都经过窗口传回。

Anthropic 在 《Code execution with MCP》 中很好地描述了这种模式,我们强烈推荐它。 当前的模型非常擅长编写代码,所以这让我们一夜之间生产力倍增。

架构

我们的可观测性平台有一个独特的访问模式:使用它的不只是我们!许多以太坊核心开发者每天都会使用我们的数据来检查他们的 devnet 第 854 次迭代运行情况,或者检查主网是否仍在运行。向所有这些人分发个人凭据不可维护,而且我们使用的工具并非都支持细粒度权限。

此外,我们现在还允许代理直接访问我们的后端基础设施,如 VictoriaMetrics、Clickhouse 和 Loki。这意味着代理可以轻易地从其运行时环境中提取这些凭据,从而将其泄露给推理提供商。

为了解决这些问题,我们将 Panda 拆分为多个组件:

请求流程:你机器上的沙盒和服务器向持有凭据的代理发送请求,代理查询 ClickHouse、Prometheus、Loki 和一个以太坊节点,返回行数据。沙盒不持有任何凭据。

服务器

服务器是人和代理的主要交互点。它暴露一个 MCP 和 CLI 接口,提供对其他组件的访问。它负责与凭据代理(proxy)协商身份验证。它在你的机器上运行,是将所有其他组件粘合在一起的胶水。

凭据代理

凭据代理是唯一持有我们数据源密码的组件。我们的团队托管了一个 proxy 实例,它实际与 Clickhouse、Victoriametrics 和 Loki 等后端服务通信。它验证身份验证 token,并且只向用户展示用户实际有权访问的数据源。它还为我们提供了所有这些数据源一致的可观测性表面,以便我们了解实际使用情况。

对于 Panda 用户,入门流程大致如下:你通过 GitHub 登录托管代理,查询在你自己机器本地运行,无需担心任何凭据。新数据源会在我们的团队添加后自动出现在 Panda 中。

沙盒

沙盒负责繁重的工作。它是一个在你的机器上运行的 Python 容器,因为我们发现计算最好靠近提问者。它使用与 Panda 一起提供的自定义 Docker 镜像,其中包含用于与我们的数据源交互的 Python 包装器。

在沙盒中运行的代码很可能由 LLM 编写,并且即将执行,因此沙盒将其视为不安全的。每个容器运行:

  • nobody 身份运行,根文件系统只读
  • 放弃所有 Linux 能力,设置 no-new-privileges
  • 在 pid、内存和 CPU 限制下
  • 在隔离的 Docker 网络上,而不是主机网络

沙盒一开始就不持有任何凭据。它只获得一个 API URL 和一个作用域限于本地服务器的 token,仅此而已。因此,即使生成的代码试图寻找要泄露的秘密,容器中也没有任何秘密可拿。可写空间很小:/tmp/workspace(在同一个会话的调用之间保留),以及一个用于取出文件的 /output 挂载点。

会话

沙盒不会在单次调用后被丢弃。当将上一次响应中的 session_id 传递给下一次调用时,你会得到同一个热容器,其中导入的模块仍然加载,你写入 /workspace 的任何内容仍在磁盘上。一系列 execute_python 调用就变成了一次可以自我构建的分析。

当某一步骤代价高昂,而后续步骤想要重用结果时,这种方法非常有效。第一次调用将 30 天的区块提议者缓存到一个 parquet 文件中:

panda execute --code '
from ethpandaops import clickhouse

proposers = clickhouse.query("xatu-cbt", """
    SELECT slot, proposer_validator_index AS validator
    FROM mainnet.fct_block_proposer FINAL
    WHERE slot_start_date_time >= now() - INTERVAL 30 DAY
""")
proposers.to_parquet("/workspace/proposers.parquet")
print(len(proposers), "proposals cached")
'

同一会话中的后续调用直接读取该文件,并与第二次查询连接,而无需再次为第一次查询付费:

panda execute --session 9bdd56e2 --code '
import pandas as pd
from ethpandaops import clickhouse

proposers = pd.read_parquet("/workspace/proposers.parquet")   # 仍在这里,来自上次调用
correctness = clickhouse.query("xatu-cbt", """
    SELECT attesting_validator_index AS validator,
           avg(inclusion_distance)   AS avg_inclusion_distance
    FROM mainnet.fct_attestation_correctness_by_validator_canonical FINAL
    WHERE slot_start_date_time >= now() - INTERVAL 30 DAY
    GROUP BY validator
""")
joined = proposers.merge(correctness, on="validator", how="left")
print(joined["avg_inclusion_distance"].describe())
'

发现

不幸的是,我们仍然面临一个发现方面的问题。代理(以及人类!)是非确定性的。我们需要一种方法来引导 Panda 用户找到正确的工作流程,而无需预先加载他们的上下文。

首先,我们创建了 panda getting-started,作为用户了解如何获取更多信息的第一跳。所有数据源都能够公开它们所提供数据的文档。例如,Clickhouse 数据源通过 panda schema $table 暴露其连接的表的结构。

这涵盖了存在哪些东西,但确切的工作流程更复杂。我们最终实现了一个包含 4 种不同类型文档的嵌入索引,使我们能够对它们进行语义搜索。

  • 查询示例(单个 SQL、PromQL 和 LogQL 片段)
  • 调查操作手册(多步骤流程)
  • EIP
  • 共识规范(协议常量与规范文本)

这样,代理就能可靠地解决像“从 aggregate_and_proof 中获取每个 slot 的唯一验证者”这样模糊的请求,而无需预先知道确切的表名。这也为我们的团队提供了一个地方来捕捉我们多年来积累的模式和操作手册,供我们任何人重用。

panda search runbooks "网络未最终确定"
panda search eips "blob 基础费用"
panda search consensus-specs "MAX_EFFECTIVE_BALANCE"
panda search queries "区块到达"

共识规范更进一步,在沙盒内可以直接调用,因此查询可以内联解析常量,而无需硬编码:

panda execute --code '
from ethpandaops import specs
print(specs.get_constant("MAX_EFFECTIVE_BALANCE"))
print(specs.get_spec("deneb", "beacon-chain")["title"])
'

CLI 还是 MCP?

两者我们都不在意。

同一个服务器同时服务于 MCP 工具和 Panda CLI,因此它们共享库、凭据代理和凭据边界。代理可以直接调用 MCP 工具,也可以像人类一样通过 shell 调用 CLI。

CLI 提供了两种访问数据的方式:panda clickhouse query 将单个原始查询直接代理到数据源,而 panda execute 先在沙盒中运行你的代码。前沿模型也能够自行选择。

panda clickhouse query xatu-cbt "SELECT count() FROM mainnet.fct_block FINAL"
panda execute --code 'from ethpandaops import clickhouse; print(clickhouse.query("xatu-cbt", "SELECT count() FROM mainnet.fct_block FINAL"))'

我们的使用方式

实践中,Panda 是我们每天处理以太坊协议和内部平台时都会用到的工具。通常这些都是一次性问题,之前调查起来太繁琐:“如果聚合者位于澳大利亚 vs 欧洲,有多少证明会在聚合截止时间后到达?”我们将一些较长的调查记录在 investigations.ethpandaops.io

近期一个关于我们内部平台的查询是“为什么 kafka 延迟在增加?”代理使用 Panda 检查 VictoriaMetrics,通过 Prometheus 指标找出主题为何延迟,然后转向 Loki 查询消费实例的错误日志,最后通过 Clickhouse 的指标得出结论:ClickHouse 的写入吞吐量已饱和。

我们越来越频繁地使用代理来增强我们对 Panda 的使用。 通常让 Claude 与 Panda 交互比我们自己用 bash 运行命令要容易得多。

快来试试

对于那些好奇 Panda 内部工作原理的人:完整源代码、可自托管的凭据代理以及架构文档都位于 github.com/ethpandaops/panda。如果你是以太坊核心开发者,你可以通过 panda init 使用 ethPandaOps 代理。


爱你们的,

ethPandaOps 团队

  • 原文链接: ethpandaops.io/posts/pan...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~

相关文章

0 条评论