news 2026/9/24 16:32:50

Phoenix 可观测性实战:用 TypeScript 构建并追踪一个支持 Agent(LLM 调用、工具执行、RAG 与 Sessions 全流程指南)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Phoenix 可观测性实战:用 TypeScript 构建并追踪一个支持 Agent(LLM 调用、工具执行、RAG 与 Sessions 全流程指南)
  • 可观测性
  • AI 评测
  • LLMOps
  • AI 应用
  • 人工智能

【免费下载链接】phoenix

AI Observability & Evaluation

项目地址:https://gitcode.com/gh_mirrors/phoenix13/phoenix
点击查看免费下载

本篇技术指南围绕当前仓库中的可运行示例项目 js/examples/apps/tracing-tutorial 展开,完整演示如何用 TypeScript(AI SDK + Phoenix SDK)构建一个客服支持 Agent(SupportBot),并借助 Phoenix 对每一次 LLM 调用、工具执行、RAG 检索进行追踪,通过 Annotation 与 LLM-as-Judge 评估回答质量,最终用 Sessions 把多轮对话串成完整会话。读完本文,你将掌握 Phoenix 可观测性体系的四个核心能力:接入 Tracing、阅读 Trace 执行树、运行自动化评估、跟踪并评估多轮会话。

教程概览与前置条件

该示例是一个随官方文档发布的配套工程,对应文档中的三个章节:

  • 第 1 章:你的第一批 Trace:构建支持 Agent 并追踪所有内部操作
  • 第 2 章:Annotation 与评估:用人工反馈与 LLM-as-Judge 评估回答质量
  • 第 3 章:Sessions:将多轮对话作为会话进行跟踪与评估

运行示例需要满足以下前提:

  • Node.js 18+(本仓库当前依赖的 AI SDK 版本较新,若使用最新 AI SDK 可能需要 Node.js 22+)
  • 本地运行中的 Phoenix 服务uvx arize-phoenix serve,或pip install arize-phoenix && phoenix serve
  • OpenAI API Key(示例使用gpt-4o-minitext-embedding-ada-002

从 package.json 可以看到该工程的依赖组成,它们正好对应追踪链路的每一环:

依赖包在教程中的作用
@arizeai/phoenix-otel注册 OpenTelemetry,把 Trace 发往 Phoenix
@arizeai/phoenix-client通过 Phoenix Client 获取 Span、回写 Annotation
@arizeai/phoenix-evals提供createClassificationEvaluator等 LLM-as-Judge 评估器
@arizeai/openinference-core提供setSession等会话上下文传播能力
@arizeai/openinference-semantic-conventions提供 OpenInference 语义约定常量(如SESSION_ID
ai+@ai-sdk/openaiAI SDK 的generateTextembedtoolAPI 与 OpenAI 模型接入
zod工具输入参数的运行时校验 Schema

工程还依赖tsx(TypeScript 直接运行器)与typescript,四个 npm scripts 对应本教程的三个章节(见 package.json):

"scripts": { "evaluate": "npx tsx evaluate-traces.ts", "evaluate:sessions": "npx tsx evaluate-traces.ts --sessions", "sessions": "npx tsx support-agent.ts --sessions", "start": "npx tsx support-agent.ts" }

环境搭建:安装依赖、配置环境变量、启动 Phoenix

1. 安装依赖

在 js/examples/apps/tracing-tutorial 目录下执行:

pnpm install

2. 设置环境变量

# OpenAI API key(必填) export OPENAI_API_KEY=your-openai-api-key # 可选:自定义 Phoenix 端点(默认为 http://localhost:6006) export PHOENIX_COLLECTOR_ENDPOINT=http://localhost:6006

关于PHOENIX_COLLECTOR_ENDPOINT的解析逻辑,可以从 js/packages/phoenix-otel/src/register.ts 的源码注释得到印证:register()url参数若未提供,会优先读取PHOENIX_COLLECTOR_ENDPOINT环境变量;URL 若未带/v1/traces路径会被自动规范化补全。此外,工程还支持PHOENIX_PROJECT(项目名)、PHOENIX_API_KEY(认证,自动以 Bearer Token 形式加入 Authorization 头)等环境变量。

3. 启动 Phoenix

若使用本地方式运行 Phoenix 服务:

pip install arize-phoenix phoenix serve

服务启动后默认监听http://localhost:6006,这也是 Trace 收集端点的默认地址。

追踪接入:理解 instrumentation.ts 做了什么

追踪的开关在 instrumentation.ts 中,它必须在每个脚本的最顶部被导入:

import { register } from "@arizeai/phoenix-otel"; // Register with Phoenix - this handles all the OpenTelemetry boilerplate export const provider = register({ projectName: "support-bot", // Optional: set batch to false for immediate span delivery during development batch: false, });

这里的register()一次性完成了 OpenTelemetry 的全部样板工作。从 register.ts 的类型定义可以了解到它支持的核心配置项:

配置项默认值说明
projectName"default"Span 在 Phoenix 中归属的项目名,用于 UI 分组与过滤;未传时读PHOENIX_PROJECT环境变量
urlPHOENIX_COLLECTOR_ENDPOINTPhoenix 服务地址,自动补全/v1/traces路径
apiKeyPHOENIX_API_KEY云端/认证场景下以 Bearer Token 认证
batchtruetrue用批量 Span 处理器(生产推荐,减少网络开销);false用简单处理器即时导出(调试方便)
globaltrue是否把 TracerProvider 注册为全局 Provider
instrumentations需要自动注册的 OpenTelemetry 插桩列表(注意 ESM 项目可能需手动注册)
spanProcessors自定义 Span 处理器,提供后会覆盖由url/apiKey/batch生成的默认处理器

教程中把batch显式设为false,是为了让开发调试阶段的 Span 立即送达 Phoenix,不必等待批处理窗口。对应的实现位于 lazyOpenInferenceSpanProcessor.ts,它根据batch标志在批量处理器与简单处理器之间切换。

另外注意:示例代码在每次 Agent 执行完毕后会调用provider.forceFlush()(见 support-agent.ts),确保脚本退出前所有 Span 已落盘,这一点在手动跑脚本时很关键。

第 1 章:你的第一批 Trace

运行单轮演示

pnpm start

这条命令运行 support-agent.ts 的默认路径,用 7 条精心设计的测试查询去"压测"Agent:

const queries = [ "What's the status of order ORD-12345?", // → 订单状态 → 工具调用(订单存在) "How can I get a refund?", // → FAQ → RAG(知识库中已有) "Where is my order ORD-67890?", // → 订单状态 → 工具调用(订单存在) "I forgot my password", // → FAQ → RAG(知识库中已有) "What's the status of order ORD-99999?", // → 订单不存在,触发失败路径 "How do I upgrade to a premium plan?", // → 知识库中没有,Agent 无法回答 "Can you help me with something random?",// → 模糊请求 ];

其中前 4 条是"好查询",后 3 条刻意设计为"坏查询",用来制造真实的失败场景,供后续章节的评估环节分析。

Agent 内部结构与 Trace 形态

handleSupportQuery是整个 Agent 的核心(见 support-agent.ts),它用tracer.startActiveSpan("support-agent", ...)开启一个openinference.span.kind: AGENT的父 Span,把一次请求内的所有操作都嵌套进去。流程分为两步:

Step 1 - 查询分类:用generateTextgpt-4o-mini输出 JSON(category/confidence/reasoning),把查询路由到order_statusfaq。分类结果会被写回父 Span 的属性(classification.categoryclassification.confidence),供 Phoenix UI 直接查看。

Step 2 - 按分类路由

  • 订单状态路径:用 AI SDK 的tool()定义lookupOrderStatus工具(输入 Schema 由 zod 校验),maxSteps: 2允许"决定调用工具 → 拿到结果"两步;工具内部先模拟 300ms 延迟,再从内存中的orderDatabase查订单,查不到时返回{ error: "Order ... not found in our system" }。拿到结果后再用一次generateText汇总成面向客户的友好回复。
  • FAQ 路径(RAG):先用text-embedding-ada-002对查询做embed,再与 FAQ 库中预先嵌入的向量做余弦相似度排序,取 Top-2 作为上下文,最后让gpt-4o-mini"只使用给定上下文"生成答案。

由于 AI SDK 的generateTextembedtool调用都带experimental_telemetry: { isEnabled: true },它们会被自动打点为子 Span。因此,在 Phoenix 中每条support-agentTrace 的执行树如下:

订单状态查询:

support-agent (AGENT) ├── ai.generateText (classification → "order_status") ├── ai.generateText (with tool call) │ └── tool: lookupOrderStatus └── ai.generateText (summarizes tool result)

FAQ 查询:

support-agent (AGENT) ├── ai.generateText (classification → "faq") ├── ai.embed (query embedding) └── ai.generateText (RAG generation)

每个 LLM Span 会记录输入消息(system/user 提示词)、输出、模型名与提供商、调用参数、Token 用量与延迟;RAG 的生成 Span 系统提示词里直接包含检索到的上下文,方便你一眼看出"检索是否找对了文档"。

交互式反馈采集

Agent 跑完所有查询后会进入交互式反馈环节(collectUserFeedback,见 support-agent.ts):对每条响应输入y(👍 有用)、n(👎 无用)或s(跳过)。关键点在于,Agent 在父 Span 创建时通过agentSpan.spanContext().spanId捕获了 Span ID(见第 L219 行),反馈随之通过logSpanAnnotationsuser_feedback的名义、annotatorKind: "HUMAN"写回 Phoenix:

await logSpanAnnotations({ spanAnnotations: annotations, sync: false, // async mode - Phoenix processes in background });

annotatorKind字段区分标注来源(HUMAN人工 /LLM模型评估),metadata 里记录了分类类别与来源(interactive_tutorial),方便后续按维度聚合。

第 2 章:Annotation 与 LLM-as-Judge 评估

跑完 Agent、收集完人工反馈后,运行评估脚本:

pnpm evaluate

该命令对应 evaluate-traces.ts 的默认路径,核心流程是"取 Span → 分类评估 → 回写 Annotation → 输出汇总"。

流程一:从 Phoenix 拉取 Span

通过@arizeai/phoenix-client/spansgetSpans按项目名support-bot拉取最近 100 条 Span(见 evaluate-traces.ts)。如果 Phoenix 未启动或尚未生成 Trace,脚本会给出明确的排查提示。

流程二:工具结果检查(tool_result)

从 Span 列表里过滤出name === "ai.toolCall"的工具 Span,对output.value做纯代码级检查——输出中若包含errornot found则判为error,否则为success(见 evaluate-traces.ts):

const hasError = output.toLowerCase().includes("error") || output.toLowerCase().includes("not found"); const status = hasError ? "❌ ERROR" : "✅ SUCCESS"; annotations.push({ spanId, name: "tool_result", label: hasError ? "error" : "success", score: hasError ? 0 : 1, explanation: hasError ? "Tool returned an error or 'not found' response" : "Tool executed successfully", annotatorKind: "LLM" as const, // 代码级检查,仅为了与评估流程统一 metadata: { evaluator: "tool_result", type: "code" }, });

这正是排查"回答为什么没用"的第一层证据:只要在 Phoenix 中看到tool_result = error的 Annotation,就知道是订单在数据库里不存在(例如 ORD-99999)。

流程三:检索相关性评估(retrieval_relevance,LLM-as-Judge)

对 RAG 生成阶段的 LLM Span(按系统提示词特征过滤,见 evaluate-traces.ts),使用@arizeai/phoenix-evalscreateClassificationEvaluator创建评估器:

const retrievalRelevanceEvaluator = createClassificationEvaluator({ name: "retrieval_relevance", model: openai("gpt-4o-mini"), choices: { relevant: 1, irrelevant: 0, }, promptTemplate: `You are evaluating whether the retrieved context is relevant to answering the user's prompt. Classify the retrieval as: - RELEVANT: The context contains information that directly helps answer the question - IRRELEVANT: The context does NOT contain useful information for the question You are comparing the "Context" object and the "prompt" object. [Context and Prompt]: {{input}} `, });

createClassificationEvaluator的本质是返回一个ClassificationEvaluator实例(见 js/packages/phoenix-evals/src/llm/createClassificationEvaluator.ts),它把自定义promptTemplatechoices(标签到分数的映射)绑定到指定模型上,调用evaluate({ input })即返回{ label, score, explanation }。评估器还会自动生成 explanation,解释"为什么相关/不相关",便于快速定位原因。

评估输入取自 RAG 生成 Span 的input.value(其中包含检索上下文),每评估完一条 Span 会setTimeout 500ms做限流(见第 L301 行)。

流程四:回写 Annotation 与结果汇总

两类评估结果统一通过logSpanAnnotations以异步模式(sync: false)写回 Phoenix,落在子 Span上——这正是"点开一条不理想的 Trace,立刻知道哪一步出了问题"的关键设计。脚本最后会打印两类汇总:

🔧 Tool Calls (lookupOrderStatus): Success: N | Errors: M 📚 FAQ Retrieval: Relevant: N | Irrelevant: M

可复用的排障工作流

至此你拥有了一个完整的闭环(与文档 annotations-and-evaluations.mdx 的 "The Debugging Workflow" 小节一致):

  1. 运行 Agent(pnpm start),对响应给出 👍/👎;
  2. 运行评估(pnpm evaluate),给子 Span 打 Annotation;
  3. 在 Phoenix 中点开被标记为不理想的 Trace;
  4. 查看子 Span 的 Annotation 定位根因:
    • tool_result = error→ 订单不存在;
    • retrieval_relevance = irrelevant→ FAQ 不在知识库中。

这正是"可扩展的调试方式":用人工反馈定位失败,用自动化评估诊断原因,用 Trace 细节理解根因,而不是逐条人工翻看。

第 3 章:Sessions 多轮会话跟踪

运行多轮会话演示

pnpm sessions

pnpm sessions等价于npx tsx support-agent.ts --sessions,运行三组预置的会话场景(见 support-agent.ts):

  • 订单咨询(Order Inquiry):客户问订单状态,再追问到货时间、追踪号;
  • FAQ 会话(FAQ Conversation):同一会话里连续问密码重置、退款政策;
  • 混合会话(Mixed Conversation):在订单与 FAQ 主题间来回切换,测试 Agent 的上下文保持能力。

每个会话通过crypto.randomUUID()生成唯一 Session ID,所有轮次共享同一个 ID。演示还会维护conversationHistory(消息历史)与sessionContext(记住客户提到过的订单号),并在每轮之后用正则/ORD-\d+/i从消息中提取订单号存入上下文(见 support-agent.ts),这样客户在后续轮次说"my order"时 Agent 能接得上。

会话如何被追踪

Sessions 的核心机制在handleSupportQuery中(见 support-agent.ts):

// If we have a session ID, propagate it to all child spans if (sessionId) { return context.with(setSession(context.active(), { sessionId }), runAgent); }

关键点有三:

  1. 父 Span 上写入标准属性session.id(源码中用SemanticConventions.SESSION_ID常量,见第 L213 行),同时记录conversation.turn轮次号;
  2. setSession()来自@arizeai/openinference-core,把会话 ID 注入到 OpenTelemetry 的 Context 中;
  3. context.with()确保整个 Agent 执行期间该上下文处于激活状态,从而让所有子 Span 自动继承会话 ID。

文档 sessions.mdx 中把这一机制总结得很精辟:Session ID 本质上只是 Span 属性——在父 Span 上设置它,Phoenix 就会自动把所有相关 Trace 按会话分组。

在 Phoenix 中查看会话

打开 Phoenix 的Sessions标签页,可以看到:

  • 对话线程(Conversation threads):所有轮次按 Session ID 分组;
  • 聊天视图(Chat view):点进某个会话看到完整的你来我往;
  • 会话级 Annotation:落在最后一轮上的连贯性与解决状态评估。

你可以按conversation_coherenceresolution_status过滤会话,快速挑出有问题的对话。

会话级评估

pnpm evaluate:sessions

等价于npx tsx evaluate-traces.ts --sessions,执行 evaluate-traces.ts 中的evaluateSessions()。流程如下:

1. 按会话分组:拉取最多 200 条 Span,过滤出name === "support-agent"的 Span,读取session.id属性分组(见groupSpansBySession,第 L184-L195 行)。

2. 构建会话转录(transcript):组内 Span 按conversation.turn排序,把每轮的input.value/output.value拼成Turn N:\nUser: ...\nAgent: ...格式的完整对话文本。

3. 运行两个会话级评估器,均使用gpt-5模型:

  • conversation_coherence(对话连贯性):判断 Agent 是否记住了前文信息、有没有重复询问、回复是否承接上文。coherent: 1 / incoherent: 0
  • resolution_status(问题解决状态):判断对话结束时客户的诉求是否得到满足。resolved: 1 / unresolved: 0

两个评估器的判定标准都写在promptTemplate里,并且自动生成 explanation——被标记为 incoherent 或 unresolved 时,点进去即可看到具体原因。

4. 写回会话级 Annotation:与 Span 级不同,这里使用@arizeai/phoenix-client/sessionslogSessionAnnotations,Annotation 落在会话而非单个 Span 上(见第 L504-L516 行),metadata 中记录model: "gpt-5"turnCount

5. 汇总输出

🧠 Conversation Coherence: Coherent: N/M ✅ Issue Resolution: Resolved: N/M

会话评估的价值:一个真实的上下文保持案例

文档 sessions.mdx 给出了一个很有说服力的分析案例。在"混合会话"场景中:

  • 第 1 轮:用户问 ORD-67890 的状态,Agent 正确查单并回复"处理中,预计 12 月 15 日送达";
  • 第 2 轮:用户完全切换话题——"怎么取消订阅?",Agent 走 RAG 给出正确指引;
  • 第 3 轮(真正的考验):用户只说"回到我的订单——承运商是谁?",没有重复订单号。Agent 正确回忆起 ORD-67890 并回答(承运商 pending),全程没让用户重复。

会话级 Annotation 印证了这一点:conversation_coherence: coherent (1.0)resolution_status: resolved (1.0),explanation 明确指出"Agent 跨轮正确引用了订单号与一致细节,同时没有丢失对订阅问题的处理"。

这就是会话级评估的意义:不用逐轮人工检查,通过连贯性与解决率即可扫描全部会话,发现异常后点进去看 explanation 就能定位问题。

在 Phoenix 中应该看什么

打开http://localhost:6006,按章节对照查看:

Traces(第 1 章)

每条support-agentTrace 展示完整请求流。订单路径能看到"分类 → 工具决策 → 工具执行 → 结果汇总"四段;FAQ 路径能看到"分类 → 嵌入 → RAG 生成"。注意分类置信度属性:如果某条查询的confidence: low,通常意味着查询超出了 Agent 的能力范围(例如"Can you help me with something random?")。

Annotations(第 2 章)

每个 Trace 的Annotations标签页会显示三类标注:

  • user_feedback:用户在终端交互输入的 👍/👎(HUMAN);
  • tool_result:代码级 success/error 检查;
  • retrieval_relevance:LLM 评估的 relevant/irrelevant。

按 Annotation 值过滤 Trace,可以快速发现失败模式(例如所有tool_result = error的 Trace 都对应不存在的订单号)。

Sessions(第 3 章)

Sessions标签页展示完整对话线程与聊天视图,会话级 Annotation 落在最后一轮。按conversation_coherence/resolution_status过滤,即可找到"忘记上下文"或"问题未解决"的会话。

项目结构速览

js/examples/apps/tracing-tutorial/ ├── package.json # 依赖与脚本(start / sessions / evaluate / evaluate:sessions) ├── tsconfig.json # TypeScript 配置(ES2022 / strict / bundler 解析) ├── instrumentation.ts # Phoenix/OpenTelemetry 接入(register + batch:false) ├── support-agent.ts # 第 1 & 3 章:支持 Agent(含会话支持与交互反馈) ├── evaluate-traces.ts # 第 2 & 3 章:LLM-as-Judge 评估(Span 级 + 会话级) └── README.md # 本教程说明

各模块的底层实现可以在对应包中找到:追踪注册逻辑见 js/packages/phoenix-otel/src/register.ts,Span Annotation 写入见 js/packages/phoenix-client/src/spans/logSpanAnnotations.ts,会话 Annotation 写入见 js/packages/phoenix-client/src/sessions/logSessionAnnotations.ts,分类评估器工厂见 js/packages/phoenix-evals/src/llm/createClassificationEvaluator.ts。

总结

通过这个 TypeScript 教程工程,你掌握了一套完整的 LLM 应用可观测性方法论:

  1. 追踪:接入@arizeai/phoenix-otel后,AI SDK 的每次generateTextembedtool调用都会自动成为 Trace 中的节点;再用父 Span 把一次请求的全部操作聚合成一棵执行树;
  2. 评估:用人工反馈(👍/👎)标注好坏,用代码级检查与 LLM-as-Judge 自动化诊断失败原因,Annotation 直接落在对应的 Span 上;
  3. 会话:通过setSession+context.with让会话 ID 沿上下文传播,把孤立的单次查询串成对话线程,并用会话级评估器回答"上下文是否保持""问题是否解决"。

这套"观察一切 → 度量重点 → 用数据改进"的模式适用于任何 LLM 应用:评估器和指标会随业务变化,但追踪、标注、评估、会话的闭环方法是通用的。你也可以参照仓库中其余示例(如 js/examples/apps 目录下的其他应用)将同样的模式迁移到 LangGraph、OpenAI Agents 等不同框架之上。

  • 可观测性
  • AI 评测
  • LLMOps
  • AI 应用
  • 人工智能

【免费下载链接】phoenix

AI Observability & Evaluation

项目地址:https://gitcode.com/gh_mirrors/phoenix13/phoenix
点击查看免费下载

相关推荐

上一篇:如何轻松提升暗黑破坏神3游戏效率:智能按键工具的终极指南
下一篇:网盘直链下载助手:如何轻松获取8大网盘真实下载链接?

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/24 16:32:43

EmDash Seed 文件完全指南:从 Schema 定义到数据导出的实战手册

EmDash Seed 文件完全指南:从 Schema 定义到数据导出的实战手册 【免费下载链接】emdash EmDash is a full-stack TypeScript CMS based on Astro; the spiritual successor to WordPress 项目地址: https://gitcode.com/gh_mirrors/emdas/emdash seed 文件&…

作者头像 李华
网站建设 2026/9/24 16:32:15

热门题目分类+清单

题单来源:https://leetcode.cn/studyplan/top-100-liked/ 类别题目解题思路哈希表1.两数之和(简单)49.字母异位词分组(中等)128.最长连续序列(中等)①以每个item为开端且item-1不在set里面&…

作者头像 李华
网站建设 2026/9/24 16:23:42

Wand-Enhancer 上手记:一次本地补丁,免费解锁 WeMod 专业版

Wand-Enhancer 上手记:一次本地补丁,免费解锁 WeMod 专业版 【免费下载链接】Wand-Enhancer Advanced UX and interoperability extension for Wand (WeMod) app 项目地址: https://gitcode.com/GitHub_Trending/we/Wand-Enhancer Wand-Enhancer …

作者头像 李华