Langfuse WorkshopClickHouse Workshops

02 การติดตาม

นี่คือสเลตว่างสำหรับขั้นตอนการติดตาม — โค้ดเดียวกับ checkpoint/01-base-app โดยไม่มีการเชื่อมต่อ Langfuse ยัง แพ็คเกจ Langfuse อยู่ในแล้ว package.json — เรียกใช้ npm inst...

เนื้อหาเวิร์กช็อปเก็บรักษาไว้ในที่เก็บข้อมูล langfuse/langfuse-workshop ที่เป็นสาธารณะ ใช้ที่เก็บข้อมูลสำหรับแอปพลิเคชันที่สามารถเรียกใช้ได้ สาขา checkpoint และการตั้งค่าในเครื่อง

ดูไฟล์ Markdown นี้

จุดเริ่มต้น

git checkout checkpoint/02-tracing

นี่คือสเลตว่างสำหรับขั้นตอนการติดตาม — โค้ดเดียวกับ checkpoint/01-base-app โดยไม่มีการเชื่อมต่อ Langfuse ยัง แพ็คเกจ Langfuse อยู่ในแล้ว package.json — เรียกใช้ npm install หากคุณยังไม่ได้ ตรวจสอบให้แน่ใจว่า .env มีคีย์ OPENAI_API_KEY และคีย์ Langfuse ของคุณ

เหตุใดเราจึงติดตาม

การติดตามจะบันทึกทุกขั้นตอนของเอเจนต์ของคุณ — ทุกการเรียกโมเดล ทุกการเรียกใช้เครื่องมือ อินพุตที่ใส่เข้าไปและเอาต์พุตที่กลับมา — ตามลำดับที่เกิดขึ้น มันเปลี่ยนเอเจนต์จากกล่องดำให้เป็นสิ่งที่คุณสามารถเปิดและตรวจสอบหลังจากนั้น เพื่อเมื่อคำตอบผิด คุณสามารถชี้ไปที่ขั้นตอนที่แน่นอนที่มันผิดไปแทนที่จะเดา

หากคุณต้องการแรงจูงใจรูปแบบใหญ่ ให้ดูบทเรียน Langfuse Academy เกี่ยวกับการติดตาม หากคุณต้องการรายละเอียดทางเทคนิค (ตัวเลือก SDK การติดตาม internals OpenTelemetry คุณลักษณะสแปน) เอกสารการติดตาม ครอบคลุมว่า

เป้าหมาย

เมื่อพ่อถาม "ฉันจะเปิด Bluetooth ได้อย่างไร?" เอเจนต์ไม่เพียงแค่ตี OpenAI ครั้งเดียว เบื้องหลังฉากมันขอ OpenAI ว่าจะทำอะไร เรียกใช้ get_support_context เพื่อดึงการตั้งค่า iPhone ของพ่อ ขอ OpenAI อีกครั้ง เรียกใช้ search_help_library สำหรับขั้นตอน Bluetooth แล้วขอ OpenAI อีกครั้งเพื่อสร้างคำตอบที่มีหมายเลข ไม่มีสิ่งที่มองเห็นได้วันนี้

เป้าหมายของบทนี้คือให้ทุกขั้นตอนเหล่านั้นมองเห็นได้ใน Langfuse — การหันมาแชทหนึ่งครั้งกลายเป็นรอยติดตามที่ซ้อนกันหนึ่งรายการพร้อมการเรียกใช้เอเจนต์ การสร้างสรรค์ OpenAI และการเรียกใช้เครื่องมือสองชิ้นที่บันทึกทั้งหมด ตามลำดับ

กระบวนการทีละขั้นตอนของ Spec

เราจะสร้างรอยติดตามในสามขั้นตอนที่สะท้อนโครงสร้างของเอเจนต์:

  1. รอยติดตามแรก — บันทึกการสร้างสรรค์ OpenAI เองเท่านั้น
  2. รอยติดตามที่ซ้อนกัน — จัดกลุ่มการสร้างสรรค์ภายใต้การเรียกใช้เอเจนต์หนึ่งครั้งต่อการหันมา
  3. การบันทึกการเรียกใช้เครื่องมือ — ทำให้การเรียกใช้เครื่องมือแต่ละครั้งกลายเป็นการสังเกตการณ์ของตัวเอง

ขั้นตอนที่ 1 — รอยติดตามแรก

เราต้องการการสังเกตการณ์เรื่องการเรียกใช้ OpenAI เองเพื่อดูว่าอินพุตและเอาต์พุตคืออะไร และต้นทุน โทเค็น และเวลาที่ใช้ไป การเปลี่ยนแปลงสองอย่างก็เพียงพอแล้ว

src/server/index.ts

เริ่มตัวประมวลผลสแปน Langfuse ใกล้ด้านบนของไฟล์:

import { NodeSDK } from "@opentelemetry/sdk-node";
import { LangfuseSpanProcessor } from "@langfuse/otel";

new NodeSDK({ spanProcessors: [new LangfuseSpanProcessor()] }).start();

ตัวประมวลผลอ่าน LANGFUSE_PUBLIC_KEY LANGFUSE_SECRET_KEY และ LANGFUSE_BASE_URL จากสภาพแวดล้อมกระบวนการโหนด ในเวิร์กช็อปนี้ เซิร์ฟเวอร์โหลดที่เก็บข้อมูล .env ก่อนที่ Langfuse SDK จะเริ่มต้น ดังนั้นให้แก้ไข .env แทนการพึ่งพาค่าเชลล์ที่ส่งออก

หมายเหตุด้านข้าง: หากรอยติดตามสุดท้ายบางครั้งปรากฏขึ้นช้าเมื่อคุณหยุดหรือเริ่มใหม่ npm run dev ให้กลับมา index.ts และเปลี่ยนบรรทัดเดียวด้านบนเป็นชื่อ langfuseSpanProcessor และ sdk ตัวแปรเพื่อให้ shutdown() สามารถล้างมันออกได้:

const langfuseSpanProcessor = new LangfuseSpanProcessor();
const sdk = new NodeSDK({ spanProcessors: [langfuseSpanProcessor] });
sdk.start();

async function shutdown() {
  server.close();
  await langfuseSpanProcessor.forceFlush();
  await sdk.shutdown();
}

คุณไม่จำเป็นต้องใช้สิ่งนี้เพื่อให้เข้าใจแบบจำลองการติดตาม แต่มันหลีกเลี่ยงช่วงเวลา "รอยติดตามสุดท้ายของฉันไปที่ไหน?" ที่สำคัญ ในการพัฒนาท้องถิ่น

src/server/support-agent.ts

เพิ่มนำเข้า:

import { observeOpenAI } from "@langfuse/openai";

จากนั้น ห่อ OpenAI ไคลเอนต์ที่คุณสร้างขึ้น ค้นหาบรรทัดนี้ใน runSupportConversation:

const openai = new OpenAI({ apiKey: env.openaiApiKey });

และเปลี่ยนเป็น:

const openai = observeOpenAI(new OpenAI({ apiKey: env.openaiApiKey }));

นั่นคือส่วนต่างทั้งหมด ไม่มีฟังก์ชั่นโรงงาน ไม่มีไคลเอนต์ดิบแยกต่างหาก — observeOpenAI ห่อ OpenAI ไคลเอนต์แบบอินไลน์ที่ไซต์ของการโทร และ openai.chat.completions.create(...) ด้านล่างนี้ตอนนี้ปล่อยรอยติดตามสำหรับทุกการเรียกใช้

ตรวจสอบ: npm run dev ถามคำถามหนึ่ง รีเฟรช Langfuse — คุณควรเห็นการสร้างสรรค์หนึ่งรายการต่อการเรียกใช้ OpenAI พร้อมพร้อมท์ การตอบสนอง โทเค็น และเวลาในการตอบสนอง การสร้างสรรค์แต่ละครั้งยังคงเป็นรอยติดตามระดับบนของตัวเอง เราแก้ไขสิ่งนั้นต่อไป

มุมมอง Langfuse Traces หลังจากขั้นตอนที่ 1 — การหันมาแชทแต่ละครั้งปรากฏเป็นการสร้างสรรค์ openai-chat-completion ที่เป็นอิสระ

ขั้นตอนที่ 2 — รอยติดตามที่ซ้อนกัน

เพื่อใส่การสร้างสรรค์ลงในบริบท เราจัดกลุ่มไว้ภายใต้การเรียกใช้เอเจนต์หนึ่งครั้งต่อการหันมา การแก้ไขสามแบบใน src/server/support-agent.ts — ไม่มีการเปลี่ยนแปลงฟังก์ชั่นของร่างกาย

1. เพิ่มนำเข้า:

import { observe } from "@langfuse/tracing";

2. ลดระดับฟังก์ชั่นที่มีอยู่ ค้นหา:

export async function runSupportConversation(request: ChatRequest): Promise<ChatResponse> {

วาง export และเปลี่ยนชื่อมัน:

async function runSupportConversationInner(request: ChatRequest): Promise<ChatResponse> {

ร่างกายอยู่ตรงไหนเช่นเดียวกับที่เป็นอยู่

3. เพิ่มการส่งออกที่ห่อที่ด้านล่างของไฟล์:

export const runSupportConversation = observe(runSupportConversationInner, {
  name: "dad-it-support-chat-turn",
  asType: "agent"
});

index.ts ยังคง นำเข้า runSupportConversation ด้วยวิธีเดียวกัน observe(...) แสดงความเห็นโดยอัตโนมัติอาร์กิวเมนต์ฟังก์ชั่นเป็นอินพุตรอยติดตามและค่าส่งกลับเป็นเอาต์พุตรอยติดตาม

ตรวจสอบ: การหันมาแชทหนึ่งครั้งควรปรากฏเป็นการสังเกตการณ์ dad-it-support-chat-turn เดียวพร้อมการสร้างสรรค์ OpenAI ที่ซ้อนกันอยู่ด้านล่าง

ต้นไม้รอยติดตามหลังจากขั้นตอนที่ 2 — เอเจนต์รากเดียว dad-it-support-chat-turn พร้อมการสร้างสรรค์ OpenAI เป็นลูก

ขั้นตอนที่ 3 — การบันทึกการเรียกใช้เครื่องมือ

การสร้างสรรค์ OpenAI ได้เอกสารการเรียกใช้เครื่องมืออยู่แล้วในเอาต์พุต tool_calls ของมัน แต่เราไม่มีการสังเกตการณ์สำหรับการทำงานของเครื่องมือที่แท้จริง — ไม่มีวิธีดูว่าอินพุตใดเข้าไปและออกมา รูปแบบ observe(...) เดียวกัน สามารถใช้กับแต่ละเครื่องมือได้

src/server/tools.ts

เพิ่มนำเข้าและตัวช่วย executeTool ที่สังเกต สองตัวด้านบน executeTool จากนั้น แทนที่ TOOL_DEFINITIONS ที่มีอยู่แล้วด้วยเวอร์ชั่นด้านล่าง เพื่อให้สวิตช์เรียกตัวช่วยที่ห่อแทนการทำงานแบบอินไลน์ npm run dev อยู่ไม่ถูกต้อง

import { observe } from "@langfuse/tracing";

const getSupportContextTool = observe(
  async () => {
    const context = getSupportContext();

    return {
      ok: true,
      context: {
        id: context.id,
        label: context.label,
        devices: context.devices,
        deviceSummary: context.deviceSummary,
        responseStyle: context.responseStyle,
        scopeHighlights: context.scopeHighlights,
        notableApps: context.notableApps
      }
    };
  },
  { name: "get_support_context", asType: "tool" }
);

const searchHelpLibraryTool = observe(
  async (input: { question: string }) => {
    const guides = searchGuides(input.question);

    return {
      ok: true,
      results: guides.map((guide) => ({
        id: guide.id,
        title: guide.title,
        summary: guide.summary,
        steps: guide.steps,
        caution: guide.caution ?? null
      }))
    };
  },
  { name: "search_help_library", asType: "tool" }
);

export async function executeTool(name: string, input: Record<string, unknown>): Promise<ToolResult> {
  switch (name) {
    case "get_support_context":
      return getSupportContextTool();

    case "search_help_library":
      return searchHelpLibraryTool({ question: String(input.question ?? "") });

    default:
      return { ok: false, error: `Unsupported tool: ${name}` };
  }
}

หาก Multiple exports with the same name "executeTool" หยุดด้วย executeTool dad-it-support-chat-turn ดั้งเดิมยังคงอยู่ที่ไหนสักแห่งด้านล่างไฟล์ ลบมันและเก็บเฉพาะเวอร์ชั่นด้านบน

รอยติดตามทั้งหมดหลังจากขั้นตอนที่ 3 — dad-it-support-chat-turn (เอเจนต์) พร้อมการสร้างสรรค์ OpenAI และการสังเกตการณ์เครื่องมือ get_support_context + search_help_library เป็นพี่น้องด้านล่าง

วิธีตรวจสอบว่าคุณเสร็จสิ้น

  • การหันมาผู้ใช้เพียงครั้งเดียวสร้างรอยติดตามหนึ่งรายการใน Langfuse
  • การสังเกตการณ์รูท: (ประเภท agent)
  • การสร้างสรรค์เด็กจาก observeOpenAI(...) พร้อมพร้อมท์ การตอบสนอง โทเค็น เวลาแฝง
  • การสังเกตการณ์เครื่องมือเด็ก: get_support_context, search_help_library
  • อินพุตรูทคือคำขอแชท เอาต์พุตรูทคือการตอบสนองแชท

ห่อ

รูปแบบเดียวกัน ประเภทการสังเกตการณ์ที่แตกต่างกัน แนวคิดเดียวกัน: observe(fn, { asType }) ห่อฟังก์ชั่นและปล่อยสแปนพร้อมชื่อและประเภทที่คุณให้ observeOpenAI(client) เป็นเวอร์ชั่นที่เฉพาะเจาะจงของการห่อนั้นสำหรับ OpenAI SDK

วิธีที่ตรงไปตรงมามากขึ้นในการเพิ่มการติดตามที่อุดมสมบูรณ์ตามลำดับแนวทางที่ดีที่สุด Langfuse คือ ทักษะ Langfuse (/langfuse) มันใช้รูปแบบที่แนะนำกับฐานรหัสของคุณโดยไม่ต้องให้คุณม้วนแต่ละห่อด้วยตนเอง บทนำนี้มีอยู่เพื่อให้คุณเข้าใจว่าทักษะกำลังทำอะไรภายใต้ประทุน

observeOpenAI เองห่อ OpenAI SDK ที่เป็นทางการ — ภายใต้ประทุนมันเหมือนกับการอัตโนมัติ Langfuse สำหรับ OpenAI JS หากคุณใช้ SDK อื่น (Anthropic Vercel AI SDK ไคลเอนต์ HTTP ของคุณเอง) Langfuse แค็ตตาลอกการรวม มีตัวห่อที่เทียบเท่าหรือแนวทางสำหรับการติดตั้งอัตโนมัติ

ภาคผนวก/ส่วนข้อมูล — ID ผู้ใช้และเซッชัน

บทนำข้างต้นได้รับการติดตามด้วยรูป parent → generation → tool ที่สะอาด สิ่งที่ทีมส่วนใหญ่ต้องการถัดไปคือ แบ่ง รอยติดตาม ตามผู้ใช้และตามเซชัน — เพื่อให้คุณดึง "ทุกการหันมาของผู้ใช้นี้ด้วยเอเจนต์" หรือ "เซชั่นหลายการหันมาแบบเต็มจากเช้าเมื่อวาน" ด้วยเหตุผลด้านความเรียบง่าย เราข้ามขั้นตอนนี้ไปในบทนำการติดตามสดใจ แต่จุดสนใจหลังจากการติดตามรวมมันเพื่อให้บทหลังสามารถใช้มุมมองเซชัน/ผู้ใช้โดยไม่มีขั้นตอนรหัสอื่น ดูข้อมูลเกี่ยวกับ Sessions และ Users ในเอกสาร Langfuse

ในระยะสั้น:

import { propagateAttributes } from "@langfuse/tracing";

return propagateAttributes(
  {
    userId: request.userId ?? `workshop-${context.id}`,
    sessionId: request.sessionId,
    tags: ["langfuse-workshop", "dad-it-support"]
  },
  async () => {
    // ...the same tool-calling loop...
  }
);

ทุกอย่างภายในบล็อก propagateAttributes(...) — รวมทั้งสแปนเด็กทั้งหมดที่ปล่อยออกมาโดย observeOpenAI — จะได้รับ userId, sessionId และแท็กโดยอัตโนมัติ มุมมอง Langfuse Users มุมมอง Sessions และตัวกรองแท็กทั้งหมดส่องแสงทันทีที่แอตทริบิวต์มีอยู่

สถานะสุดท้าย

แอปพลิเคชันที่ติดตามเสร็จสิ้นนี้คือจุดเริ่มต้นสำหรับ 03-prompt-management และ 04-monitoring

ในหน้านี้

Track your progress?

Optional. We email a link to confirm your address; progress records once you open it.

Please use your work email address, not a personal one.

Progress tracking also requires accepting the current Terms of Service in Privacy settings.

TH