Langfuse WorkshopClickHouse Workshops

02 Pelacakan

Ini adalah batu loncatan kosong untuk langkah pelacakan — kode yang sama seperti checkpoint/01-base-app, tanpa kabel Langfuse. Paket Langfuse sudah ada di package.json — jalankan npm inst...

Materi workshop dikelola di repositori publik langfuse/langfuse-workshop. Gunakan repositori untuk aplikasi yang dapat dijalankan, cabang checkpoint, dan setup lokal.

Lihat file Markdown ini

Titik awal

git checkout checkpoint/02-tracing

Ini adalah batu loncatan kosong untuk langkah pelacakan — kode yang sama seperti checkpoint/01-base-app, tanpa kabel Langfuse. Paket Langfuse sudah ada di package.json — jalankan npm install jika Anda belum. Pastikan .env memiliki OPENAI_API_KEY dan kunci Langfuse Anda.

Mengapa kami melacak

Pelacakan mencatat setiap langkah yang diambil agen Anda — setiap panggilan model, setiap pemanggilan alat, masukan yang masuk dan keluaran yang kembali — dalam urutan yang terjadi. Ini mengubah agen dari kotak hitam menjadi sesuatu yang dapat Anda buka dan periksa setelah fakta, jadi ketika jawaban salah Anda dapat menunjuk ke langkah pasti di mana hal itu salah alih-alih menebak.

Jika Anda menginginkan motivasi gambaran yang lebih besar, lihat pelajaran Akademi Langfuse tentang pelacakan. Jika Anda menginginkan detail teknis (opsi SDK, internal OpenTelemetry, atribut span), dokumen pelacakan mencakup itu.

Tujuan

Ketika Dad bertanya "Bagaimana cara menyalakan Bluetooth?", agen tidak hanya mengenai OpenAI sekali. Di balik layar, ia bertanya kepada OpenAI apa yang harus dilakukan, memanggil get_support_context untuk mengambil setup iPhone Dad, bertanya kepada OpenAI lagi, memanggil search_help_library untuk langkah Bluetooth, kemudian bertanya kepada OpenAI sekali lagi untuk menghasilkan jawaban bernomor. Semua itu tidak terlihat hari ini.

Tujuan bab ini adalah membuat setiap satu langkah itu terlihat di Langfuse — satu giliran chat menjadi satu jejak bersarang dengan jalannya agen, generasi OpenAI, dan dua panggilan alat semua dicatat secara berurutan.

Proses langkah demi langkah Spec

Kami akan membangun jejak dalam tiga langkah yang mencerminkan struktur agen:

  1. Jejak pertama — catat generasi OpenAI sendiri.
  2. Jejak bersarang — kelompokkan generasi di bawah satu jalannya agen per giliran.
  3. Pemanggilan alat perekaman — buat setiap pemanggilan alat menjadi pengamatannya sendiri.

Langkah 1 — Jejak pertama

Kami menginginkan observabilitas pada panggilan OpenAI sendiri untuk melihat apa masukan dan keluarannya, dan berapa banyak biaya, token, dan waktu yang dihabiskan. Dua perubahan sudah cukup.

src/server/index.ts

Mulai pemroses span Langfuse di dekat bagian atas file:

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

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

Pemroses membaca LANGFUSE_PUBLIC_KEY, LANGFUSE_SECRET_KEY, dan LANGFUSE_BASE_URL dari lingkungan proses Node. Dalam workshop ini, server memuat repositori .env sebelum SDK Langfuse dimulai, jadi edit .env sebagai gantinya dari mengandalkan nilai yang diekspor shell.

Catatan samping: jika jejak terakhir kadang-kadang muncul terlambat ketika Anda menghentikan atau memulai ulang npm run dev, kembali ke index.ts dan ubah baris satu-liner di atas menjadi langfuseSpanProcessor dan sdk yang dinamai sehingga shutdown() dapat menggosoknya:

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

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

Anda tidak perlu ini untuk memahami model pelacakan, tetapi ini menghindari momen "di mana pergi jejak terakhir saya?" yang membingungkan dalam dev lokal.

src/server/support-agent.ts

Tambahkan impor:

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

Kemudian bungkus klien OpenAI tempat Anda membuatnya. Temukan baris ini di runSupportConversation:

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

dan ubah menjadi:

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

Itu seluruh diff. Tidak ada fungsi pabrik, tidak ada klien mentah terpisah — observeOpenAI membungkus klien OpenAI secara inline di situs panggilan, dan openai.chat.completions.create(...) di bawahnya sekarang memancarkan jejak untuk setiap panggilan.

Verifikasi: npm run dev, tanyakan satu pertanyaan, segarkan Langfuse — Anda harus melihat satu generasi per panggilan OpenAI dengan prompt, respons, token, dan latensi. Setiap generasi masih merupakan jejak tingkat atas miliknya sendiri; kami memperbaiki itu berikutnya.

Tampilan Jejak Langfuse setelah Langkah 1 — setiap giliran chat muncul sebagai generasi openai-chat-completion mandiri.

Langkah 2 — Jejak bersarang

Untuk menempatkan generasi ke dalam konteks kami mengelompokkannya di bawah satu jalannya agen per giliran. Tiga edit di src/server/support-agent.ts — tidak ada perubahan badan fungsi.

1. Tambahkan impor:

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

2. Turunkan fungsi yang ada. Temukan:

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

Turunkan export dan ganti namanya:

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

Badan tetap persis seperti itu.

3. Tambahkan ekspor bungkus di bagian bawah file:

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

index.ts masih mengimpor runSupportConversation dengan cara yang sama. observe(...) secara otomatis menangkap argumen fungsi sebagai masukan jejak dan nilai kembali sebagai keluaran jejak.

Verifikasi: satu giliran chat sekarang harus muncul sebagai pengamatan dad-it-support-chat-turn tunggal dengan generasi OpenAI bersarang di bawahnya.

Pohon jejak setelah Langkah 2 — satu akar agen dad-it-support-chat-turn dengan generasi OpenAI sebagai anak.

Langkah 3 — Pemanggilan alat perekaman

Generasi OpenAI sudah menyebutkan panggilan alat dalam keluarannya tool_calls, tetapi kami tidak memiliki pengamatan untuk eksekusi alat aktual — tidak ada cara untuk melihat apa masukan yang masuk dan apa yang keluar. Pola yang sama observe(...), dapat diterapkan ke setiap alat.

src/server/tools.ts

Tambahkan impor dan dua pembantu yang diamati di atas executeTool, kemudian ganti executeTool yang ada dengan versi di bawah sehingga saklar memanggil pembantu yang dibungkus alih-alih melakukan pekerjaan secara inline. TOOL_DEFINITIONS tetap tidak tersentuh.

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}` };
  }
}

Jika npm run dev berhenti dengan Multiple exports with the same name "executeTool", executeTool asli masih lebih jauh ke bawah file. Hapusnya dan simpan hanya versi di atas.

Jejak lengkap setelah Langkah 3 — dad-it-support-chat-turn (agen) dengan generasi OpenAI dan pengamatan alat get_support_context + search_help_library sebagai saudara kandung di bawahnya.

Cara memverifikasi Anda selesai

  • Satu giliran pengguna membuat satu jejak di Langfuse.
  • Pengamatan akar: dad-it-support-chat-turn (tipe agent).
  • Generasi anak dari observeOpenAI(...) dengan prompt, respons, token, latensi.
  • Pengamatan alat anak: get_support_context, search_help_library.
  • Masukan akar adalah permintaan chat; keluaran akar adalah respons chat.

Kesimpulan

Pola yang sama, tipe pengamatan yang berbeda, konsep yang sama: observe(fn, { asType }) membungkus fungsi dan memancarkan span dengan nama dan tipe yang Anda berikan. observeOpenAI(client) adalah versi khusus dari itu bungkus untuk SDK OpenAI.

Cara yang lebih mudah untuk menambahkan pelacakan kaya sesuai dengan praktik terbaik Langfuse adalah keterampilan Langfuse (/langfuse). Ini menerapkan pola yang direkomendasikan ke basis kode Anda tanpa Anda menggulung setiap bungkus dengan tangan. Panduan ini ada sehingga Anda memahami apa yang dilakukan keterampilan di bawah tenda.

observeOpenAI sendiri membungkus SDK OpenAI resmi — di bawah tenda itu sama dengan auto-instrumentation Langfuse untuk OpenAI JS. Jika Anda menggunakan SDK yang berbeda (Anthropic, Vercel AI SDK, klien HTTP Anda sendiri), katalog integrasi Langfuse memiliki pembungkus setara atau panduan auto-instrumentation.

Bagian Appendix/Bonus — ID pengguna dan sesi

Panduan di atas membuat Anda melacak dengan bentuk induk yang bersih → generasi → alat. Hal berikutnya yang ingin dilakukan sebagian besar tim adalah memotong jejak berdasarkan pengguna dan berdasarkan sesi — sehingga Anda dapat menarik "setiap giliran pengguna ini dengan agen" atau "sesi multi-giliran lengkap dari kemarin pagi." Demi alasan kesederhanaan kami melewatkan langkah ini dalam panduan pelacakan langsung, tetapi checkpoint setelah pelacakan memasukkannya sehingga bab nanti dapat menggunakan tampilan sesi/pengguna tanpa langkah kode lain. Lihat informasi tentang Sesi dan Pengguna dalam dokumen Langfuse.

Singkatnya:

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...
  }
);

Apa pun di dalam blok propagateAttributes(...) — termasuk semua span anak yang dipancarkan oleh observeOpenAI — secara otomatis mendapat userId, sessionId, dan tag terlampir. Tampilan Pengguna Langfuse, tampilan Sesi, dan filter tag semuanya menyala segera setelah atribut ada.

Status akhir

Aplikasi terlacak selesai ini adalah titik awal untuk 03-prompt-management dan 04-monitoring.

Di halaman ini

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.

ID