آزمون رگرسیون آفلاین برای گردشکارهای مبتنی بر شواهد
پیش از بررسی کارایی، نسخه پایه و تغییر پیشنهادی را با موارد یکسان پشتیبانی، بازخورد محصول و مطالعه مقایسه کنید. این راهنمای تکمیلی برای گردشکارهای مبتنی بر شواهد، اعتبارسنجی خودکار را از داوری ثبتشده انسان جدا میکند: ساختار درست JSON و نقلقول واقعی همچنان ممکن است در کنار خلاصهای گمراهکننده قرار بگیرند.
این مثال، نمایشی اجرایی و آفلاین از سازوکار ارزیابی است؛ پیشنویسها و داوریهای نمونه را نویسنده آماده کرده است. نتایج آن عملکرد هیچ مدل واقعی را نمیسنجند. برچسب candidate_claimed_cheaper_faster ساختگی است و شاهدی برای قیمت یا تأخیر ندارد.
۱. دو برنامه را کنار هم ذخیره کنید
از Python 3.10 یا جدیدتر استفاده کنید. برنامه کامل workflow.py را از مثال قبلی و برنامه زیر را با نام eval_workflow.py در یک پوشه خصوصی ذخیره کنید. فقط کتابخانه استاندارد Python لازم است. وارد کردن workflow.py اعتبارسنجهای آن را در دسترس قرار میدهد؛ بخش اجرای زنده آن که شرط ورود دارد، فراخوانی نمیشود. این ارزیاب درخواست API نمیفرستد، کلیدی نمیخواند و گزارشی را خودکار در فایل نمینویسد.
هر مورد، ارجاعهای مورد انتظار به شواهد و پرسش بازبینی مستقل دارد. این انتظارها را از پاسخ نسخه پیشنهادی نسازید. بازبین بررسی میکند که آیا خلاصه از منبع نتیجه میشود، ابهام و نظرهای اقلیت را حفظ میکند و از وعده یا نتیجه ساختگی پرهیز میکند. داوری ثبتشده یکی از مقادیر pass، fail یا needs_review است و دلیل و چکیدهای دارد که آن را به رکوردهای منبع و پیشنویس پیوند میدهد. چکیده تغییر را آشکار میکند؛ هویت بازبین یا درستی داوری را ثابت نمیکند.
۲. ارزیاب کامل را کپی کنید
import hashlib
import json
from copy import deepcopy
from workflow import DEMOS, read_completion, validate_result
CASES = {
"support": {
"expected": {
("T1", "I was charged twice for order A42."),
("T2", "CSV export fails when I select the last 30 days."),
},
"rubric": "Identify both issues without approving a refund or promising a fix.",
},
"feedback": {
"expected": {
("F1", "We need CSV export to prepare our weekly report."),
("F2", "CSV export would remove our manual reporting step."),
("F3", "Dark mode would be useful at night."),
},
"rubric": "Preserve the minority theme; do not infer willingness to pay.",
},
"study": {
"expected": {
("S1", "retrieving information from memory without looking at the notes"),
("S2", "Spaced practice distributes study sessions over time."),
},
"rubric": "Explain both concepts without inventing an optimal interval.",
},
}
def review_digest(case_id, draft):
material = {"records": DEMOS[case_id][0], "draft": draft}
encoded = json.dumps(material, sort_keys=True, ensure_ascii=False).encode("utf-8")
return hashlib.sha256(encoded).hexdigest()
def illustrative_runs():
baseline, candidate = [], []
reasons = {
"support": "Both issues are identified and the refund remains undecided.",
"feedback": "The CSV theme and minority dark-mode request are preserved.",
"study": "Both concepts are supported and the study interval remains unknown.",
}
for case_id in CASES:
draft = deepcopy(DEMOS[case_id][1])
response = {"choices": [{
"finish_reason": "stop",
"message": {"content": json.dumps(draft), "refusal": None},
}]}
baseline.append({
"case_id": case_id,
"response": response,
"human_review": {
"verdict": "pass", "reason": reasons[case_id],
"sha256": review_digest(case_id, draft),
},
})
changed = deepcopy(draft)
verdict, reason = "pass", reasons[case_id]
if case_id == "support":
changed["findings"][0]["summary"] = "A refund for order A42 is approved."
verdict = "fail"
reason = "The genuine quote reports a charge; it does not approve a refund."
elif case_id == "study":
verdict = "needs_review"
reason = "Illustrative reviewer has not completed the semantic check."
changed_response = deepcopy(response)
changed_response["choices"][0]["message"]["content"] = json.dumps(changed)
candidate.append({
"case_id": case_id,
"response": changed_response,
"human_review": {
"verdict": verdict, "reason": reason,
"sha256": review_digest(case_id, changed),
},
})
return baseline, candidate
def evaluate_run(rows, label):
identifiers = [row.get("case_id") if isinstance(row, dict) else None
for row in rows] if isinstance(rows, list) else []
coverage_ok = (len(identifiers) == len(CASES)
and all(isinstance(item, str) for item in identifiers)
and set(identifiers) == set(CASES))
results = []
for case_id, case in CASES.items():
matches = [row for row in rows if isinstance(row, dict)
and row.get("case_id") == case_id] if isinstance(rows, list) else []
mechanical, human = "fail", "needs_review"
mechanical_reason = "Case is missing or duplicated."
human_reason = "A current human verdict is required."
if len(matches) == 1:
row = matches[0]
try:
response = row.get("response")
if (not isinstance(response, dict) or not isinstance(response.get("choices"), list)
or not response["choices"] or not isinstance(response["choices"][0], dict)
or not isinstance(response["choices"][0].get("message"), dict)):
raise ValueError("Malformed completion envelope.")
draft = read_completion(response)
validate_result(draft, DEMOS[case_id][0])
observed = {(item["id"], item["quote"])
for finding in draft["findings"] for item in finding["evidence"]}
if not case["expected"].issubset(observed):
raise ValueError("Required evidence reference is missing.")
mechanical, mechanical_reason = "pass", "Schema, quotes and expected references match."
review = row.get("human_review")
if (isinstance(review, dict)
and review.get("verdict") in ("pass", "fail", "needs_review")
and isinstance(review.get("reason"), str) and review["reason"].strip()
and review.get("sha256") == review_digest(case_id, draft)):
human, human_reason = review["verdict"], review["reason"]
except ValueError as error:
mechanical_reason = str(error)
results.append({
"case_id": case_id, "mechanical": mechanical, "human": human,
"mechanical_reason": mechanical_reason, "human_reason": human_reason,
"rubric": case["rubric"],
})
return {"label": label, "coverage": "pass" if coverage_ok else "fail", "cases": results}
def compare_runs(baseline, candidate):
runs = [evaluate_run(baseline, "baseline_fixture"),
evaluate_run(candidate, "candidate_claimed_cheaper_faster")]
cases = [case for run in runs for case in run["cases"]]
failed = (any(run["coverage"] == "fail" for run in runs)
or any(case["mechanical"] == "fail" or case["human"] == "fail" for case in cases))
pending = any(case["human"] == "needs_review" for case in cases)
return {
"mode": "offline_fixture",
"status": "blocked" if failed else "needs_human_review",
"fixture_quality_gate": "fail" if failed else "pending" if pending else "pass",
"runs": runs,
}
def main():
baseline, candidate = illustrative_runs()
print(json.dumps(compare_runs(baseline, candidate), ensure_ascii=False, indent=2))
if __name__ == "__main__":
main()۳. مقایسه را اجرا و بررسی کنید
python3 -B eval_workflow.pyخروجی کامل JSON هر مورد و دلیل داوری آن را نشان میدهد. نتیجه مورد انتظار چنین است:
| اجرای ساختگی | مورد | اعتبارسنجی خودکار | داوری ثبتشده انسان |
|---|---|---|---|
baseline_fixture | support | pass | pass |
baseline_fixture | feedback | pass | pass |
baseline_fixture | study | pass | pass |
candidate_claimed_cheaper_faster | support | pass | fail |
candidate_claimed_cheaper_faster | feedback | pass | pass |
candidate_claimed_cheaper_faster | study | pass | needs_review |
مقایسه مقادیر "status": "blocked" و "fixture_quality_gate": "fail" را دارد. خلاصه پشتیبانی نسخه پیشنهادی، بازپرداختی را تأیید میکند که منبع هرگز تأیید نکرده است، با اینکه نقلقول آن دقیق است. بازبینی مورد مطالعه نیز هنوز پایان نیافته است. یک امتیاز کلی ممکن است این تفاوتها را میان موارد پنهان کند.
شناسه شواهد ناشناخته، نقلقول ساختگی، حذف ارجاع مورد انتظار، خودداری مدل از پاسخ، پاسخ ناقص و ساختار پاسخ نادرست، اعتبارسنجی خودکار را ناموفق میکنند. هر دو اجرا باید تمام موارد را دقیقاً یک بار پوشش دهند؛ شناسه مفقود، تکراری یا ناشناخته مقایسه را مسدود میکند. داوری انسانی مفقود، نامعتبر یا مربوط به نسخه قبلی در وضعیت needs_review میماند. حتی اگر همه دادههای نمونه پذیرفته شوند، وضعیت مقایسه needs_human_review است: هر تغییر پیشنهادی همچنان به بازبینی نیاز دارد. هیچیک از این وضعیتها مجوز استقرار، پرداخت، پاسخ به مشتری یا اقدام دیگری نیست.
خلاصه یکی از موارد در اجرای برگشتی را بدون تغییر رکورد داوری ویرایش کنید: چکیده دیگر تطبیق نمیکند و بررسی انسانی در انتظار میماند. شناسه یک شاهد را به T99 تغییر دهید: اعتبارسنجی خودکار ناموفق میشود. برای آزمودن قبولی دادههای نمونه، با توابع بالا compare_runs(baseline, deepcopy(baseline)) را فراخوانی کنید؛ بازبینی نسخه پیشنهادی همچنان لازم است. این تغییرها سازوکار ارزیابی را میآزمایند، نه توانایی مدل را.
۴. ارزیابی واقعی را جداگانه طراحی کنید
از موارد توسعه برای بهبود دستورها، اعتبارسنجها و راهنمای بازبین استفاده کنید. موارد کنارگذاشتهشده برای ارزیابی نهایی را از این تکرارها جدا نگه دارید؛ پس از تثبیت طراحی نسخه پیشنهادی، آن را با این موارد بسنجید. این سه مورد عمومی که همه جزئیاتشان آشکار است، دادههای نمونه توسعه هستند، نه مجموعه ارزیابی نهایی. موارد مجاز و نمایندهای برای هر دو زبان آماده کنید که ابهام، اطلاعات مفقود، بازخورد اقلیت، دستورهای ناامن و خطاها را پوشش دهند. بازبینان باید پیش از دیدن پاسخ نسخه پیشنهادی، ارجاعهای مورد انتظار و معیارهای معنایی را از رکوردهای منبع بنویسند.
در اعتبارسنجی زنده بعدی، شناسه موارد و رکوردهای منبع را میان نسخه پایه و پیشنهادی یکسان نگه دارید و شناسه دقیق مدل، دستور و نسخه آن، خروجی، خودداری یا خطا و داوری انسانی را ثبت کنید. هنگام تکرار موارد، حافظه بازپخش پاسخ در اجراکننده را خاموش کنید تا هر آزمون درخواست تازهای بفرستد. حافظه نهان دستور نزد ارائهدهنده سازوکار جداگانهای است؛ هنگام مقایسه تأخیر و مصرف، شرایط آن را ثبت کنید. یک پاسخ بازپخششده یا یک اجرای مطلوب، بهبود را ثابت نمیکند. پس از بررسی تنظیمات و هزینهها، از Promptfoo با AvalAI برای ابزار ارزیابی زنده جداگانه استفاده کنید.
ابتدا کیفیت را بسنجید. سپس توزیع تأخیر مشاهدهشده و مصرف واقعی ثبتشده در AvalAI را مقایسه کنید؛ تلاشهای ناموفق و تکرارهایی را که دستی تأیید شدهاند نیز در نظر بگیرید. اندازه نمونه، شرایط اندازهگیری و موارد با بازبینی ناتمام را ثبت کنید. قیمتهای دفترچههای مرجع، شمارش توکن بهتنهایی، برچسبهای ساختگی و زمان اجرای این برنامه، هزینه دلاری یا صرفهجویی در AvalAI را ثابت نمیکنند. پیش از برنامهریزی اجراهای زنده، قیمتهای جاری و محدودیت نرخ را بررسی کنید. گزارشها متن منبع دارند؛ آنها را خصوصی نگه دارید و داده مشتریان یا کلاسهای مختلف را پیش از ارزیابی جدا کنید.
منابع و مرز اعتبارسنجی
منابع در ۱۴۰۵-۰۷-۲۰ / (2026-10-12) بررسی شدهاند. این بازنویسی از مقایسه کیفیت پیش از کارایی و شواهد مشترک موارد و ردپای اجرا در OpenAI Cookbook رسمی و مخزن openai/openai-cookbook استفاده میکند:
الگوی قابل انتقال ارزیابی به ارائهدهنده خاصی وابسته نیست. سرویس میزبانیشده Agents، یکپارچهسازیهای Batch و MCP و ادعاهای کارایی منابع مرجع، قابلیت ضمنی AvalAI نیستند. کد منتشرشده با دادههای نمونه ثابت و موارد رد شدن، آفلاین بررسی میشود؛ هیچ کلید AvalAI، پاسخ مدل زنده یا اندازهگیری عملکرد، اعتبار این مثال را تأیید نمیکند.