الفجوة التي لا تسدّها الاختبارات التقليدية
اختبارات الوحدة تؤكد نتائج محددة مسبقاً مثل add(2, 3) === 5. هذا الحتمية ينهار فور استدعاء نموذج لغوي. نفس الأمر في أيام مختلفة، أو مع إصدارات مختلفة من النموذج، أو بعد تعديل بسيط في الصياغة قد ينتج مخرجات تحصل على درجات مختلفة تماماً في الجودة. مجموعات الاختبار التقليدية لا تستطيع رصد هذا الانجراف.
Braintrust منصة تقييم ذكاء اصطناعي مبنية تحديداً لهذه المشكلة. بدلاً من تأكيدات لمرة واحدة، تُشغّل تجارب — دالة الذكاء الاصطناعي لديك مقابل مجموعة بيانات من المدخلات، تُسجَّل ويُقيَّم آلياً كل نتيجة، وتُقارَن عبر الزمن. بنهاية هذا البرنامج التعليمي ستمتلك:
- مشغّل تجارب محلي يُقيّم مخرجات النماذج اللغوية مقابل الإجابات الصحيحة
- مقيّماً يستخدم نموذجاً لغوياً كحكم للجودة المفتوحة
- تسجيلاً كاملاً للتتبّع في الإنتاج دون حاجة لأدوات إضافية
- بوابة في خط CI/CD تمنع النشر عند انخفاض الجودة
المتطلبات المسبقة
- Node.js 20 أو أحدث
- TypeScript 5+
- مفتاح OpenAI API (أي مزوّد متوافق يعمل)
- حساب Braintrust — الخطة المجانية كافية لهذا البرنامج التعليمي
- معرفة أساسية بـ TypeScript غير المتزامن ومفاهيم النماذج اللغوية
الخطوة 1: إعداد المشروع
أنشئ مشروع TypeScript جديداً أو أضف Braintrust إلى مشروع قائم.
npm install braintrust autoevals openai zodأضف مفاتيحك إلى .env.local:
BRAINTRUST_API_KEY=your_braintrust_key
OPENAI_API_KEY=your_openai_key
BRAINTRUST_PROJECT_NAME=my-ai-appأنشئ tsconfig.json يستهدف ES2022 أو أحدث لدعم await على المستوى الأعلى في نصوص التقييم:
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"outDir": "dist"
}
}الخطوة 2: تجربتك الأولى
التجربة في Braintrust تتكون من ثلاثة أجزاء: مجموعة بيانات من المدخلات (ومخرجات متوقعة اختيارية)، دالة المهمة التي تستدعي نموذج الذكاء الاصطناعي، ومقيّم واحد أو أكثر يُقيّم كل مخرج.
أنشئ lib/eval/summarize.eval.ts:
import { Eval } from "braintrust";
import OpenAI from "openai";
const openai = new OpenAI();
// المهمة: دالة الذكاء الاصطناعي الخاضعة للتقييم
async function summarize(input: { text: string }): Promise<string> {
const response = await openai.chat.completions.create({
model: "gpt-4o-mini",
messages: [
{
role: "system",
content: "لخّص النص التالي في جملتين إلى ثلاث جمل موجزة.",
},
{ role: "user", content: input.text },
],
});
return response.choices[0].message.content ?? "";
}
// المقيّم: يُكافئ التلخيصات الموجزة (أقل من 60 كلمة)
function concisenessScorer(args: { output: string }) {
const wordCount = args.output.split(/\s+/).length;
const score = wordCount <= 40 ? 1 : wordCount <= 70 ? 0.7 : 0.3;
return { name: "conciseness", score };
}
// مجموعة البيانات: أمثلة حقيقية مع إجابات متوقعة
const dataset = [
{
input: {
text: "قدّم Next.js 16 تحسينات كبيرة على App Router، تشمل Server Actions أسرع، نظام تخزين مؤقت معاد التصميم مع تحكم صريح للمطور، تحسّن البث للبيانات الكبيرة، وضع Partial Prerendering الجديد الذي يمزج العرض الثابت والديناميكي في نفس الصفحة.",
},
expected:
"حسّن Next.js 16 App Router بـ Server Actions أسرع، وتخزين مؤقت معاد التصميم، وبث محسّن، ونمط Partial Prerendering جديد.",
},
];
Eval("text-summarization", {
data: dataset,
task: summarize,
scores: [concisenessScorer],
});شغّل التجربة:
npx braintrust eval lib/eval/summarize.eval.tsيطبع Braintrust رابطاً للوحة التحكم حيث يمكنك فحص كل مدخل، والمخرج الذي أنتجته دالتك، والقيمة المتوقعة، وكل درجة. التشغيل الأول يصبح خطّك الأساسي.
الخطوة 3: التقييم باستخدام نموذج لغوي كحكم
مطابقة النصوص هشّة للمخرجات المفتوحة. نهج أفضل هو استخدام استدعاء نموذج لغوي ثانٍ للحكم على الجودة. حزمة autoevals تأتي بمقيّمات جاهزة للمهام الشائعة.
import { Factuality, Similarity } from "autoevals";
Eval("text-summarization-v2", {
data: dataset,
task: summarize,
scores: [
// يفحص الدقة الواقعية مقابل المخرج المتوقع
Factuality,
// يقيس التشابه الدلالي على مقياس 0-1
Similarity,
// فحص الإيجاز المخصص الخاص بك
concisenessScorer,
],
});Factuality يستخدم حكماً من نموذج لغوي بالتفكير المتسلسل لتقييم ما إذا كان مخرجك يقدّم هلوسات مقارنة بالإجابة المتوقعة. يرصد تراجعات الجودة التي تفوت المقارنة البسيطة للنصوص تماماً.
المقيّمات المتاحة في autoevals:
Factuality— كشف الهلوساتSimilarity— القرب الدلاليAnswerCorrectness— لمهام الإجابة على الأسئلةAnswerRelevancy— يتحقق من معالجة الإجابة للسؤالContextRecall— لخطوط أنابيب RAGToxicity،Moderation— جودة المحتوى
الخطوة 4: قالب حكم لغوي مخصص
للتقييم الخاص بمجالك، عرّف قالب حكمك الخاص:
import { LLMClassifierFromTemplate } from "autoevals";
const faithfulnessJudge = LLMClassifierFromTemplate({
name: "faithfulness",
promptTemplate: `أنت تُقيّم ما إذا كان الملخص المولّد بالذكاء الاصطناعي أميناً للنص المصدر.
النص المصدر:
{{input.text}}
الملخص:
{{output}}
قيّم الأمانة:
- "A" — دقيق تماماً، لا هلوسات
- "B" — دقيق في معظمه، حذوفات طفيفة
- "C" — غير دقيق أو مضلل
أجب بحرف واحد فقط.`,
choiceScores: { A: 1, B: 0.6, C: 0 },
useCoT: false,
});العناصر النائبة {{input.text}} و{{output}} يملأها Braintrust في وقت التشغيل. useCoT: true يطلب من الحكم التفكير قبل الإجابة، مما يحسّن الدقة في المهام المعقدة على حساب رموز إضافية.
الخطوة 5: تغليف عميلك لتتبّع الإنتاج
في الإنتاج تريد تسجيل كل استدعاء نموذج لغوي تلقائياً — اسم النموذج، الأمر، الرد، الكمون، وتكلفة الرموز — دون حاجة لتوثيق كل استدعاء يدوياً.
import { wrapOpenAI, initLogger } from "braintrust";
import OpenAI from "openai";
// تهيئة مرة واحدة عند بدء التطبيق
initLogger({
projectName: process.env.BRAINTRUST_PROJECT_NAME!,
apiKey: process.env.BRAINTRUST_API_KEY,
asyncFlush: true, // غير محجوب لبيئات serverless
});
// العميل المغلّف بديل مباشر للعميل القياسي
export const openai = wrapOpenAI(new OpenAI());كل استدعاء تجريه عبر openai يظهر الآن في مشروع Braintrust كمقطع مسجّل — لا تغييرات أخرى مطلوبة. يمكنك التصفية حسب النموذج، النطاق الزمني، محتوى الأمر، النسبة المئوية للكمون، أو تكلفة الرموز في لوحة التحكم.
الخطوة 6: إضافة مقاطع يدوية للخطوط المعقدة
عندما يقوم خط أنابيب الذكاء الاصطناعي بأكثر من استدعاء نموذج واحد — استرجاع، إعادة ترتيب، تفكير متعدد الخطوات — استخدم traced لتجميعها:
import { traced } from "braintrust";
export async function answerWithContext(question: string, docs: string[]) {
return traced(
async (span) => {
span.log({ input: { question, docCount: docs.length } });
const context = docs.join("\n\n");
const answer = await openai.chat.completions.create({
model: "gpt-4o",
messages: [
{
role: "system",
content: `أجب على السؤال باستخدام السياق المقدّم فقط.\n\nالسياق:\n${context}`,
},
{ role: "user", content: question },
],
});
const output = answer.choices[0].message.content ?? "";
span.log({
output,
metadata: { tokensUsed: answer.usage?.total_tokens },
});
return output;
},
{ name: "rag-answer" }
);
}يُظهر التتبّع الناتج في Braintrust شجرة المقاطع الكاملة: توقيت الاسترجاع، تفاصيل استدعاء النموذج، ووقت الاستجابة الإجمالي في عرض واحد.
الخطوة 7: التكامل مع App Router في Next.js
أضف العميل المُتتبَّع إلى مسار API الخاص بك:
// app/api/chat/route.ts
import { NextResponse } from "next/server";
import { traced, initLogger } from "braintrust";
import { openai } from "@/lib/braintrust"; // عميلك المغلّف
initLogger({
projectName: process.env.BRAINTRUST_PROJECT_NAME!,
apiKey: process.env.BRAINTRUST_API_KEY,
asyncFlush: true,
});
export async function POST(req: Request) {
const { message, userId } = (await req.json()) as {
message: string;
userId: string;
};
const response = await traced(
async (span) => {
span.log({ input: { message }, metadata: { userId } });
const completion = await openai.chat.completions.create({
model: "gpt-4o",
messages: [{ role: "user", content: message }],
});
const output = completion.choices[0].message.content ?? "";
span.log({ output });
return output;
},
{ name: "chat" }
);
return NextResponse.json({ response });
}بفضل asyncFlush: true، يُرسَل التتبّع إلى Braintrust في الخلفية بعد إرجاع الاستجابة — لا يرى المستخدمون أي تأخير إضافي.
الخطوة 8: بوابة جودة في CI/CD
أتمت التقييمات في كل طلب دمج يمسّ كود الذكاء الاصطناعي. أنشئ .github/workflows/eval.yml:
name: بوابة تقييم الذكاء الاصطناعي
on:
pull_request:
paths:
- "lib/ai/**"
- "lib/eval/**"
- "prompts/**"
- "app/api/**"
jobs:
eval:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- run: npm ci
- name: تشغيل تقييمات Braintrust
env:
BRAINTRUST_API_KEY: ${{ secrets.BRAINTRUST_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: |
npx braintrust eval lib/eval/*.eval.ts --threshold 0.75العلامة --threshold 0.75 تُنهي الأمر بكود غير صفري إذا انخفض المتوسط العام للدرجات عن 75%، مما يمنع الدمج. الحدّ قابل للتعديل — ارفعه مع نضج تقييماتك.
لـ GitLab CI، المرحلة المعادلة في .gitlab-ci.yml:
eval:
stage: test
image: node:20
only:
changes:
- lib/ai/**
- lib/eval/**
script:
- npm ci
- npx braintrust eval lib/eval/*.eval.ts --threshold 0.75
variables:
BRAINTRUST_API_KEY: $BRAINTRUST_API_KEY
OPENAI_API_KEY: $OPENAI_API_KEYالخطوة 9: مقارنة التجارب (اختبار A/B للأوامر)
شغّل نفس مجموعة البيانات على متغيّرَي أمر وقارن النتائج جنباً إلى جنب:
import { Eval } from "braintrust";
import { Factuality } from "autoevals";
const DATASET = [/* حالات الاختبار الخاصة بك */];
async function summarizeV1(input: { text: string }) {
const res = await openai.chat.completions.create({
model: "gpt-4o-mini",
messages: [
{ role: "system", content: "لخّص باختصار." },
{ role: "user", content: input.text },
],
});
return res.choices[0].message.content ?? "";
}
async function summarizeV2(input: { text: string }) {
const res = await openai.chat.completions.create({
model: "gpt-4o-mini",
messages: [
{
role: "system",
content:
"أنت كاتب تقني. لخّص النص أدناه في جملتين مع الحفاظ على جميع الحقائق الأساسية.",
},
{ role: "user", content: input.text },
],
});
return res.choices[0].message.content ?? "";
}
Eval("summarization-v1", { data: DATASET, task: summarizeV1, scores: [Factuality] });
Eval("summarization-v2", { data: DATASET, task: summarizeV2, scores: [Factuality] });افتح عرض المقارنة في Braintrust لترى أي المتغيّرَين يفوز على كل مقيّم، لكل مثال على حدة. هذا يجعل هندسة الأوامر عملية قائمة على الأدلة بدلاً من التخمين.
الخطوة 10: تتبّع التراجعات عبر الزمن
يُقارن Braintrust تلقائياً كل تشغيل تجربة جديد بالتشغيل السابق. الدرجات التي تنخفض تظهر باللون الأحمر؛ التحسينات باللون الأخضر. لتمييز تجربة كخطّ أساسي رسمي:
npx braintrust eval lib/eval/summarize.eval.ts --set-baselineالتشغيلات المستقبلية تستعرض هذا الخطّ الأساسي في لوحة التحكم. إذا أدّى ترقية النموذج إلى تراجع جودة التلخيص، ترى ذلك فوراً — لا بعد ثلاثة سبرنتات عندما يُبلّغ عنه مستخدم.
استكشاف الأخطاء وإصلاحها
الدرجات لا تظهر في لوحة التحكم: تأكد أن دالة المقيّم تُرجع { name: string; score: number }. كلا الحقلَين مطلوب؛ إرجاع الرقم فقط يُسقط الدرجة بصمت.
التتبّعات مفقودة في الإنتاج: على Vercel أو Cloudflare Workers، قد ينتهي المعالج قبل اكتمال التدفق غير المتزامن. أضف await logger.flush() في نهاية المعالج لضمان التسليم.
استهلاك رموز كثيرة خلال التقييمات: استخدم gpt-4o-mini أو claude-haiku-4-5 كنموذج حكم لمهام التصنيف البسيطة. احتفظ بالنماذج الكبيرة للمهام التي تتطلب تفكيراً عميقاً فعلاً.
حدود المعدل خلال التشغيلات المتوازية: يُشغّل Braintrust حالات مجموعة البيانات بشكل متزامن افتراضياً. استخدم محدّد معدّل، أو اضبط علامة --concurrency:
npx braintrust eval lib/eval/*.eval.ts --concurrency 3الخطوات التالية
- استكشف خادم Braintrust MCP لاستخدام Claude أو GPT-4o للمساعدة في تصميم مجموعات بيانات التقييم من وصف نصي طبيعي
- ابنِ حلقة تقييم عبر الإنترنت تأخذ عيّنة من 1-5% من تتبّعات الإنتاج وتُعيد تقييمها ليلاً، مع تنبيه عند الانجراف
- استخدم Braintrust Datasets API لمراجعة أمثلة ذهبية من سجلات الإنتاج وتوسيع مجموعة الاختبار باستمرار
- اجمع مع Langfuse لمراقبة شاملة: Braintrust للتقييمات، Langfuse لتتبّع وقت التشغيل وإدارة إصدارات الأوامر
خاتمة
بنيت خطّ تقييم ذكاء اصطناعي كاملاً: تجارب تُقيّم مخرجات النماذج اللغوية آلياً، حكماً لغوياً للجودة المفتوحة، تتبّع إنتاج دون كود إضافي على عميل OpenAI، وبوابة CI/CD تمنع النشر عند تراجع الجودة. تغييرات الأوامر الآن تُعامَل بنفس صرامة تغييرات الكود — ومستخدموك يستفيدون من تجربة ذكاء اصطناعي قابلة للقياس ومحسّنة باستمرار.