在昨天的 [Day 19] 中,我們透過 Gemini 1.5 原生 JSON Schema 與 Prompt 護欄工程,解決了結構化輸出解析與基礎內容越界問題。
但在生產環境中,光有「語法護欄」還不夠。當我們優化 Prompt、更換 Gemini 模型版本(如從 gemini-1.5-flash 切換至 gemini-1.5-pro)、或調整系統參數時,我們該如何用數據客觀評估 AI 的提煉品質是變好還是變壞? 當使用者回報「生成內容不準確」時,我們又該如何快速定位是 Prompt 瑕疵、脈絡遺失,還是模型本身的思考偏差?
今天,我們將為 OmniVibe AI 打造現代化全棧 AI 可觀測性(AI Observability)架構,導入 LLM-as-a-Judge 自動化評測管線 與 OpenTelemetry 追蹤(Tracing)機制!
一個健全的生產級 AI SaaS 系統需要兩種層面的監控機制:
graph TD
User[使用者請求] --> API[Next.js API Route]
API --> OTel[OpenTelemetry Tracer]
subgraph Tracing Pipeline
OTel --> Span1[Gemini API Call Span]
OTel --> Span2[Token & Latency Metrics]
Span1 --> Phoenix[Arize Phoenix / OpenTelemetry Collector]
end
subgraph LLM-as-a-Judge Eval Pipeline
Span1 --> AsyncEval[非同步評測工作]
AsyncEval --> Judge[Gemini 1.5 Pro 裁判模型]
Judge --> Score[產出 Faithfulness & Relevance 分數]
Score --> DB[(品質數據庫 / Dashboard)]
end
我們採用 Ragas 評測框架核心思想,定義兩個最重要的品質指標:
我們利用 Gemini 1.5 Pro 作為高階評測裁判,配合 Structured Outputs 直接產出 JSON 評測報告:
// src/lib/evals/llm-judge.ts
import { GoogleGenerativeAI, SchemaType } from '@google/generative-ai';
import { z } from 'zod';
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || '');
// 評測報告 JSON Schema
const evalSchema = {
type: SchemaType.OBJECT,
properties: {
faithfulnessScore: {
type: SchemaType.NUMBER,
description: '忠實度評分 (0.0 至 1.0),檢查生成內容是否有根據原始脈絡。',
},
relevanceScore: {
type: SchemaType.NUMBER,
description: '相關性評分 (0.0 至 1.0),檢查內容是否精準切中主題。',
},
reasoning: {
type: SchemaType.STRING,
description: '裁判給出的詳細扣分或加分原因與改進建議',
},
hallucinationDetected: {
type: SchemaType.BOOLEAN,
description: '是否檢測到明確的 AI 幻覺',
},
},
required: ['faithfulnessScore', 'relevanceScore', 'reasoning', 'hallucinationDetected'],
};
export const EvalResultSchema = z.object({
faithfulnessScore: z.number(),
relevanceScore: z.number(),
reasoning: z.string(),
hallucinationDetected: z.boolean(),
});
export type EvalResult = z.infer<typeof EvalResultSchema>;
export async function evaluateDistillationQuality(
sourceContext: string,
generatedOutput: string
): Promise<EvalResult> {
// 使用高推論能力的 Gemini 1.5 Pro 作為裁判員
const judgeModel = genAI.getGenerativeModel({
model: 'gemini-1.5-pro',
systemInstruction: `
你是一位極其嚴苛的 AI 內容評測裁判 (LLM Judge)。
你的任務是比對【原始素材內容 (Context)】與【AI 生成的提煉結果 (Output)】,並客觀打分。
評分標準:
1. Faithfulness (0.0 - 1.0): 檢查 Output 中的每一句陳述是否都能在 Context 中找到證據。若有編造數據或無中生有,大幅扣分並將 hallucinationDetected 設為 true。
2. Relevance (0.0 - 1.0): 檢查 Output 是否精準提煉重點,有無離題。
`,
generationConfig: {
responseMimeType: 'application/json',
responseSchema: evalSchema,
temperature: 0.0, // 裁判模型使用 0 溫度確保評分客觀一致
},
});
const prompt = `
---【原始素材內容 (Context)】---
${sourceContext}
---【AI 生成的提煉結果 (Output)】---
${generatedOutput}
請開始進行嚴格比對並輸出 JSON 評測結果:
`;
const response = await judgeModel.generateContent(prompt);
const resultText = response.response.text();
return EvalResultSchema.parse(JSON.parse(resultText));
}
src/lib/telemetry/tracer.ts)為了掌握 API 請求的延遲、Token 使用量與內部執行步驟,我們利用 OpenTelemetry SDK 建立標準 Span 追蹤包裝器:
// src/lib/telemetry/tracer.ts
import { trace, SpanStatusCode, Span } from '@opentelemetry/api';
const tracer = trace.getTracer('omnivibe-gemini-service', '1.0.0');
export async function traceGeminiCall<T>(
spanName: string,
fn: (span: Span) => Promise<T>,
metadata?: Record<string, any>
): Promise<T> {
return tracer.startActiveSpan(spanName, async (span) => {
if (metadata) {
Object.entries(metadata).forEach(([key, value]) => {
span.setAttribute(`ai.${key}`, typeof value === 'object' ? JSON.stringify(value) : value);
});
}
const startTime = Date.now();
try {
const result = await fn(span);
span.setStatus({ code: SpanStatusCode.OK });
return result;
} catch (error: any) {
span.setStatus({
code: SpanStatusCode.ERROR,
message: error?.message || 'Gemini API Execution Failed',
});
span.recordException(error);
throw error;
} finally {
span.setAttribute('ai.latency_ms', Date.now() - startTime);
span.end();
}
});
}
我們將追蹤管線與評測機制無縫整合至 OmniVibe AI 的主 API 端點中:
// src/app/api/distill/route.ts
import { NextResponse } from 'next/server';
import { generateGuardedDistillation } from '@/lib/gemini/guarded-distill';
import { evaluateDistillationQuality } from '@/lib/evals/llm-judge';
import { traceGeminiCall } from '@/lib/telemetry/tracer';
export async function POST(req: Request) {
const { fileUri, mimeType, rawTranscript } = await req.json();
try {
// 1. 帶有 OpenTelemetry Tracing 的 AI 生成 Call
const distilledResult = await traceGeminiCall(
'Gemini_Distill_Execution',
async (span) => {
const res = await generateGuardedDistillation(fileUri, mimeType);
// 記錄 Token 與重要屬性
span.setAttribute('ai.model', 'gemini-1.5-flash');
span.setAttribute('ai.output_confidence', res.meta.confidenceScore);
return res;
},
{ fileUri, mimeType }
);
// 2. 非同步/背景執行 LLM-as-a-Judge 品質打分 (不阻塞主邏輯回應)
if (rawTranscript) {
traceGeminiCall('LLM_Judge_Evaluation', async (span) => {
const evalReport = await evaluateDistillationQuality(
rawTranscript,
JSON.stringify(distilledResult)
);
span.setAttribute('ai.eval.faithfulness', evalReport.faithfulnessScore);
span.setAttribute('ai.eval.relevance', evalReport.relevanceScore);
span.setAttribute('ai.eval.hallucination', evalReport.hallucinationDetected);
console.log('[AI Eval Report]', evalReport);
// TODO: 將評測結果非同步寫入數據庫,提供給 Grafana / Phoenix Dashboard 監控
}).catch((err) => console.error('[Eval Error]', err));
}
return NextResponse.json({ success: true, data: distilledResult });
} catch (err: any) {
return NextResponse.json({ success: false, error: err.message }, { status: 500 });
}
}
當 API 完成提煉並觸發 Gemini 1.5 Pro 評測時,系統會自動產出如下的數據化品質報告:
{
"faithfulnessScore": 0.98,
"relevanceScore": 0.95,
"hallucinationDetected": false,
"reasoning": "生成內容精準提煉了原始逐字稿中關於 HBM4 記憶體架構與晶片互連的重點,所有數據(如 35% 效能提升)均可在 Context 中取得佐證,無任何憑空編造事實。"
}
若某次 Prompt 調整導致模型產生了幻覺,評測器將立即捕捉:
{
"faithfulnessScore": 0.45,
"relevanceScore": 0.80,
"hallucinationDetected": true,
"reasoning": "【幻覺警示】生成內容中的時間軸標記 (18:30) 超越了原始影片長度 (15:00),且提及的『NVLink 5.0』並未在原始逐字稿中被論及。"
}
今天我們成功為 OmniVibe AI 構建了自動化 AI 品質評測與可觀測性架構:
當用戶量爆發、API 呼叫量暴增時,頻繁呼叫大模型不僅成本高昂,重複內容的處理更會帶來不必要的 Latency。
👉 明天(Day 21),我們將進入【效能與成本最佳化篇】:實戰 Gemini Context Caching 快取機制與 Semantic Cache(語意快取)架構!看我們如何大幅降低 API 成本高達 75% 並實現毫秒級回應!
我們明天見!🔥