尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

用 Phoenix 查询 AI 应用总运行成本:TRAIL 基准任务 total-cost 深度解析

用 Phoenix 查询 AI 应用总运行成本:TRAIL 基准任务 total-cost 深度解析 可观测性AI 评测LLMOpsAI 应用人工智能【免费下载链接】phoenixAI Observability Evaluation项目地址https://gitcode.com/gh_mirrors/phoenix13/phoenix点击查看免费下载导读本文以 Phoenix 开源仓库中trail-benchmark-dev基准测试套件的total-cost任务为切入点完整拆解一个真实可运行的AI 应用成本查询任务从任务提问research-assistant 项目总共花了多少钱出发逐层剖析任务配置文件、参考求解实现、Phoenix GraphQL 成本查询 API、结果验证机制与整套 Harbor 基准测试运行链路。读完本文你将掌握如何通过 Phoenix 的 SpancostSummary字段汇总项目级 LLM 调用总成本理解 Phoenix 成本数据在 Trace 中的组织方式并了解 Harbor 任务instruction / solution / verifier的标准化结构——这套结构可以直接复用到你自己的 AI 应用可观测性与成本审计场景中。一、任务背景TRAIL 基准测试与 research-assistant 项目在 Phoenix 仓库的evals/harbor/tasks/trail-benchmark-dev/目录下维护着一组用于评测 AI Agent 在真实可观测性数据上完成分析任务的基准测试benchmark。每个任务目录都遵循统一的 Harbor 任务结构instruction.md向 Agent 提出的问题、task.toml任务运行配置、solution/参考求解脚本、tests/期望答案与评分脚本。total-cost任务提出的问题非常简洁只有一句话What did the research-assistant run cost in total?即research-assistant 这次运行的总成本是多少它要求 Agent 读取 Phoenix 中名为research-assistant的项目Project数据汇总所有 Span 的成本并给出一个美元金额。任务定义文件见 evals/harbor/tasks/trail-benchmark-dev/total-cost/instruction.md。同目录下的其他任务如count-traces问 How many traces are in the research-assistant project?、max-llm-calls、most-failing-tool、spend-concentration、error-rate-by-length等共同构成一套覆盖trace 计数、成本汇总、错误分析、工具调用分析等多个维度的 Agent 评测集合而total-cost专门负责考察 Agent 对LLM 调用成本数据的检索与汇总能力。二、任务结构剖析一个标准化 Benchmark 任务的组成每个任务目录是一个自洽、可独立运行的最小单元。以total-cost为例其结构为evals/harbor/tasks/trail-benchmark-dev/total-cost/ ├── instruction.md # 向 Agent 提出的问题 ├── task.toml # 任务运行环境与验证配置 ├── solution/ │ ├── solve.py # 参考求解实现Python │ └── solve.sh # 参考求解入口脚本 └── tests/ ├── expected.json # 期望答案与评分说明 └── test.sh # 评分入口脚本这套结构在 evals/harbor/tasks/trail-benchmark-dev/ 下的 10 个任务中保持一致。它的设计意图是同一个 fixture种子数据配上同一套任务模板即可批量评测不同 Agent 实现——正如 evals/harbor/jobs/trail-benchmark-dev.yaml 中所注释的Run every benchmark condition on every TRAIL task.2.1 task.toml任务运行环境声明task.toml 是任务的身份证声明了任务在 Harbor 沙箱中如何被拉起schema_version 1.3 artifacts [/var/lib/phoenix-eval/server.log] [task] name arize/trail-benchmark-total-cost description Total cost of the seeded project [metadata] fixture trail [environment] os linux cpus 2 memory_mb 4096 network_mode allowlist build_timeout_sec 1200.0 [environment.env] ANTHROPIC_API_KEY ${ANTHROPIC_API_KEY:-} OPENAI_API_KEY ${OPENAI_API_KEY:-} [environment.healthcheck] command sh /opt/phoenix-eval/start_phoenix_server.sh timeout_sec 180.0 retries 2 [agent] user agent timeout_sec 600.0 [verifier] network_mode allowlist allowed_hosts [api.openai.com] timeout_sec 180.0关键配置项解读artifacts任务结束后需要收集回传的产物这里是 Phoenix 服务日志/var/lib/phoenix-eval/server.log用于事后排查 Agent 与 Phoenix 交互过程。[task].name / description任务的全局唯一标识与一句话描述对应测试套件名arize/trail-benchmark-total-cost。[environment]沙箱资源规格——Linux 操作系统、2 核 CPU、4096 MB 内存、allowlist网络模式仅允许白名单域名并给出 1200 秒的镜像构建超时。[environment.env]注入运行 Agent 所需的模型 API KeyAnthropic / OpenAI默认值为空字符串${VAR:-}语法由 Harbor 运行时的宿主环境决定是否实际注入。[environment.healthcheck]Phoenix 服务的健康检查命令start_phoenix_server.sh启动本地 Phoenix 服务180 秒超时、最多重试 2 次确保 Agent 访问前服务已就绪。[agent]Agent 以agent用户身份运行单次任务最长 600 秒。[verifier]验证器同样运行在allowlist网络中且白名单只放行api.openai.com——这正是为 LLM Judge 预留的访问通道详见第五节。2.2 instruction.md给 Agent 的问题任务的 instruction 只有一句话但它定义了一个明确的检索目标从 Phoenix 的research-assistant项目中查询运行总成本。要回答它Agent 需要找到 Phoenix 服务本地 6006 端口定位名为research-assistant的 Project读取每个 Span 的costSummary.total.cost字段并求和输出格式化后的美元金额。参考实现solution给出了精确的求解路径下面展开。三、参考求解通过 Phoenix GraphQL API 汇总 Span 成本3.1 求解入口与核心调用solution/solve.py 是官方参考实现完整代码如下#!/usr/bin/env python3 Total cost of the seeded project, as Phoenix computes it. import sys sys.path.insert(0, /opt/verifier) from evals.harbor.verifiers.phoenix_api import span_costs, write_answer write_answer(f${sum(span_costs(research-assistant).values()):.2f})它只有三步引入 Harbor 提供的 Phoenix 查询辅助函数span_costs、对返回的spanId - cost字典求和、以$xx.xx格式写入答案文件并打印到任务日志。入口脚本 solution/solve.sh 则只是简单的exec python3 /solution/solve.py。3.2 span_costs分页拉取并累加每个 Span 的成本span_costs定义在 evals/harbor/verifiers/phoenix_api.py 中其文档字符串明确说明Per-span cost requires GraphQL, so span_costs uses the GraphQL API.逐 Span 的成本需要 GraphQL 才能拿到因此该函数走 GraphQL 而非类型化客户端。PHOENIX_URL http://127.0.0.1:6006 ANSWER_PATH /app/answer.txt SPAN_LIMIT 1_000_000 # The client fetches 100 spans per page up to this limit. def span_costs(project: str) - dict[str, float]: Return total cost by span ID, omitting spans without cost data. edges graphql({ projects(first: 100) { edges { node { id name } } } })[projects][edges] project_id json.dumps(next(e[node][id] for e in edges if e[node][name] project)) costs: dict[str, float] {} cursor: str | None None while True: after , after: json.dumps(cursor) if cursor else page graphql( { node(id: project_id ) { ... on Project { spans(first: 500 after ) { edges { node { spanId costSummary { total { cost } } } } pageInfo { hasNextPage endCursor } } } } } )[node][spans] for edge in page[edges]: cost ((edge[node].get(costSummary) or {}).get(total) or {}).get(cost) if cost: costs[edge[node][spanId]] float(cost) if not page[pageInfo][hasNextPage]: return costs cursor page[pageInfo][endCursor]这段代码揭示了 Phoenix 成本查询的完整链路其要点如下先按名称解析 Project 的全局节点 ID通过projects(first: 100)列出项目匹配name research-assistant的项目取其node.id全局唯一 ID形如 base64 编码的Project:xxx供后续node(id: ...)查询使用。按 Span 分页查询成本对目标 Project 执行spans(first: 500)游标分页逐页取每个 Span 节点的spanId与costSummary { total { cost } }通过pageInfo.hasNextPage / endCursor循环直到拉完所有 Span。容忍缺失的成本数据((...).get(costSummary) or {}).get(total) or {}的链式兜底意味着没有成本数据的 Span 会被安全跳过不参与求和——这正是只统计有 cost 数据的 Span的语义。分页上限单页 500 个 Span配合SPAN_LIMIT 1_000_000类型化客户端路径的拉取上限足以覆盖基准 fixture 的数据规模。3.3 graphql 辅助本地 GraphQL 客户端span_costs依赖的graphql()辅助函数同样位于 evals/harbor/verifiers/phoenix_api.pydef graphql(query: str) - dict[str, Any]: Return the data object from a local Phoenix GraphQL query. request urllib.request.Request( f{PHOENIX_URL}/graphql, datajson.dumps({query: query}).encode(), headers{Content-Type: application/json}, ) with urllib.request.urlopen(request, timeout60) as response: payload json.load(response) if payload.get(errors): raise SystemExit(payload[errors]) data: dict[str, Any] payload[data] return data它使用 Python 标准库urllib向本地 Phoenix 的/graphql端点发起 POST 请求60 秒超时遇到 GraphQLerrors时直接终止进程并打印错误——保证参考求解失败时能被评分系统明确感知。3.4 输出格式与答案文件write_answer负责把答案落盘def write_answer(answer: str) - None: Write the answer for grading and print it to the task log. with open(ANSWER_PATH, w) as handle: handle.write(answer \n) print(answer)答案写入/app/answer.txtoracle 模式评分读取的位置见下节同时打印到标准输出供任务日志留档。四、期望答案与评分机制4.1 expected.json基准答案与容差说明total-cost/tests/expected.json 定义了基准答案{ reference: $16.00, notes: The exact value is $16.0022; accept any rounding of it., source: solution/solve.sh against the seeded fixture, cross-checked against the TRAIL rows; Phoenix project costSummary.total.cost }三个字段的含义reference判分时的基准答案$16.00notes给评分器的补充说明——精确值是$16.0022接受任何合理舍入因此$16.00、$16.0022等均视为正确source答案来源记录——参考求解脚本在种子 fixture 上运行得到并与 TRAIL 数据行交叉校验同时对应 Phoenix 项目级costSummary.total.cost。4.2 评分流程exact match 与 LLM Judge 双通道verify.py 是 Harbor 的通用评分器其check()函数根据expected.json的内容选择评分通道def check(reply: str, expected: dict[str, Any]) - tuple[float, str]: if exact in expected: scores exact_match.evaluate( {output: normalize(reply), expected: normalize(str(expected[exact]))} ) return float(scores[0].score or 0.0), fexact match against {expected[exact]!r} if reference in expected: from evals.harbor.verifiers import llm_judge verdict llm_judge.matches_reference( reply, str(expected[reference]), notesstr(expected.get(notes, )) ) return float(verdict.score or 0.0), str(verdict.explanation or verdict.label) raise ValueError(expected.json needs an exact or a reference key)total-cost任务走的是LLM Judge 通道expected.json含reference键。判分前normalize()会先清洗回复去除*、、_等标记字符、压缩空白、去除末尾句点并统一小写之后再交给评判器。normalize的实现见 verify.pydef normalize(text: str) - str: return .join(_MARKUP.sub(, text).split()).rstrip(.!).casefold()4.3 LLM Judge语义等价判定而非字符串比对由于 Agent 的回复可能是Total cost is $16.00、The run cost $16.0022 in total等多样表述total-cost采用 LLM 评判器判断是否承诺了与基准答案相同的最终数值。评判逻辑定义在 evals/harbor/verifiers/llm_judge.pyJUDGE_PROVIDER os.environ.get(PHOENIX_EVAL_JUDGE_PROVIDER, openai) JUDGE_MODEL os.environ.get(PHOENIX_EVAL_JUDGE_MODEL, gpt-5-nano)评判 Prompt 明确要求措辞、格式、单位、额外正确上下文以及舍入精度都不影响判定但如果 Agent 给出了不同的数值、在候选答案间摇摆、答非所问或没有给出答案则判为不匹配mismatch。notes字段The exact value is $16.0022; accept any rounding of it.会作为附加指导注入评判 Prompt——这正是expected.json中notes的实际用途。这也解释了为什么 task.toml 的[verifier]配置要将allowed_hosts白名单设为[api.openai.com]LLM Judge 默认使用 OpenAI 的gpt-5-nano模型验证阶段需要放行该域名。4.4 测试入口脚本tests/test.sh 是评分入口#!/bin/sh set -eu PYTHONPATH/opt/verifier exec python -m evals.harbor.verifiers.verify --expected /tests/expected.json它通过PYTHONPATH/opt/verifier让评分器能找到evals.harbor.verifiers包然后执行verify模块的main()将 reward含轨迹测量指标tool_call_count、agent_turn_count写入/logs/verifier/reward.json并打印 JSON 摘要。五、Phoenix 侧的成本数据基础5.1 成本数据从哪里来costSummary.total.cost是 Phoenix 对每个 Span 记录的 LLM 调用成本来源于 OpenInference 语义约定下的成本属性。当应用通过 Phoenix 的 Tracer如 src/phoenix/tracers.py采集 LLM 调用时Prompt/Completion 的 Token 用量与单价会被记录在 Span 上Phoenix 据此在 Span 上生成costSummary结构包含输入、输出与总成本。因此total-cost任务本质上考察的是Agent 能否正确理解并聚合 Phoenix 的 Span 级成本元数据。5.2 数据规模与容错设计从 evals/harbor/verifiers/phoenix_api.py 可以看到两个工程细节类型化客户端Client(base_urlhttp://127.0.0.1:6006)提供spans.get_spans(project_identifierproject, limitSPAN_LIMIT)与get_span_annotations等便捷查询适用于不需要逐 Span 成本的场景如count-traces任务统计 trace 数而成本查询必须走 GraphQL 分页单页 500因为成本字段不在类型化客户端的默认 Span 模型中——这是参考求解选择 GraphQL 的根本原因。5.3 同类任务的横向参照同一 fixture 下的其他任务展示了 Phoenix 数据的不同查询维度可与total-cost对照理解任务提问考察能力count-tracesHow many traces are in the research-assistant project?项目级 trace 计数total-costWhat did the research-assistant run cost in total?Span 成本汇总max-llm-calls查询 LLM 调用次数最多的内容Span 属性排序most-failing-tool查询失败最多的工具工具调用错误分析spend-concentration成本集中度分析成本分布统计它们都共享 evals/harbor/verifiers/phoenix_api.py 的查询辅助函数与 verify.py 的评分框架验证了这套任务模板的复用性。六、如何运行与复现该任务该任务通过 Harbor 基准测试框架运行作业定义见 evals/harbor/jobs/trail-benchmark-dev.yaml其头部注释给出了运行方式# make harbor-run HARBOR_JOBevals/harbor/jobs/trail-benchmark-dev.yaml # Use HARBOR_ARGS to pass options such as -e docker, -k 3, --job-name, or # -a oracle. To select conditions or tasks, use scripts/subset_job.py.要点全量运行make harbor-run HARBOR_JOBevals/harbor/jobs/trail-benchmark-dev.yaml会按作业文件中的条件claude-code-mcp、claude-code-cli、codex-mcp、codex-cli、phoenix-chat-agent等 Agent 组合见 trail-benchmark-dev.yaml 的agents段跑完所有任务。按需裁剪通过HARBOR_ARGS传入-k 3并发数、--job-name、-a oracleoracle 模式直接执行 solution 脚本而非 Agent等参数evals/harbor/scripts/subset_job.py可用于挑选特定任务或条件。oracle 模式与 Agent 模式的评分差异oracle 模式没有 Agent 轨迹trajectory评分器直接从/app/answer.txt读取参考求解的答案Agent 模式则从/logs/agent/trajectory.json中提取最后一条 Agent 消息作为回复见 verify.py 的read_reply逻辑并附带tool_call_count、agent_turn_count两个轨迹测量指标。成本要求任务运行依赖 Phoenix 服务健康检查start_phoenix_server.sh以及注入的ANTHROPIC_API_KEY/OPENAI_API_KEY环境变量LLM Judge 评分还需要允许访问api.openai.com。七、小结从 total-cost 任务提炼的可复用经验total-cost虽是一个基准测试任务但它浓缩了 Phoenix 成本可观测性的完整实践范式可直接迁移到生产环境成本数据模型Phoenix 将 LLM 调用成本组织在 Span 的costSummary.total.cost上项目总成本 该 Project 下所有含成本数据 Span 的成本之和查询路径选择项目级遍历用类型化客户端spans.get_spans逐 Span 成本汇总走 GraphQL 分页node(id:)spans(first: 500, after:)游标并做好缺失成本字段的容错判分工程数值类答案优先使用归一化 LLM 语义评判而非严格字符串匹配用notes声明容差如接受$16.0022的任意舍入任务可复现性通过task.toml固定资源规格、网络白名单、健康检查与超时让不同 Agent 在同一 fixture 上的表现可比。如果你需要在自己的 AI 应用中做成本审计可以直接复用 evals/harbor/verifiers/phoenix_api.py 中span_costs的分页聚合思路结合 Phoenix 的 OpenInference 追踪语义构建项目 / 会话 / 单次运行多粒度的成本看板。赞分享可观测性AI 评测LLMOpsAI 应用人工智能【免费下载链接】phoenixAI Observability Evaluation项目地址https://gitcode.com/gh_mirrors/phoenix13/phoenix点击查看免费下载相关推荐aws-cli 成本与用量查询实战深入解析 aws ce get-cost-and-usage 命令aws cli 成本与用量查询实战深入解析 aws ce get cost and usage 命令 aws ce get cost and usage 是开发工具云原生运维RIOT OS 核心 API 运行时长基准测试runtime_coreapis 应用深度解析RIOT OS 核心 API 运行时长基准测试runtime_coreapis 应用深度解析 导读 本文围绕 RIOT OS 测试套件中的 tests/ben物联网嵌入式操作系统实时系统Turborepo Run Summary 深度解析turborepo-run-summary 的任务执行追踪与运行汇总机制Turborepo Run Summary 深度解析turborepo run summary 的任务执行追踪与运行汇总机制 本文以 TurborepoRu构建工具开发工具CLI上一篇SchemaSpy 项目推荐下一篇TinyEditor 项目推荐创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表