توطين الأخطاء لكل خطوة: استخدام تقييم المسار لمعرفة أين يفشل الوكلاء
استخدم مخطط trajectory_eval في Potato لتوطين الأخطاء لكل خطوة، مع تصنيفات أخطاء هرمية وتسجيل للخطورة وتتبّع درجة جارية عبر تتبعات الوكلاء.
المشكلة: معرفة أن الوكيل فشل لا تكفي
A trajectory error taxonomy
تشغّل وكيلك على معيار قياس فيحصل على 63% في إتمام المهام. ثم ماذا؟
رقم نجاح/فشل يخبرك أن الوكيل فشل في 37% من المهام ولا شيء غير ذلك. لا يخبرك أين في التتبع ساءت الأمور، ولا أي نوع من الأخطاء ارتكب الوكيل، ولا كم كان الخطأ سيئاً. أكان خطأً واحداً كارثياً في الخطوة 2، أم خمس عشرة خطوة من أخطاء تفكير صغيرة تراكمت؟ أأساء الوكيل استخدام أداة، أم انطلق في تفكيره من مقدمة خاطئة؟
من دون توطين الأخطاء لكل خطوة، لا يمكنك تشخيص أنماط الفشل، ولا تحديد ما تصلحه أولاً، ولا بناء بيانات تدريب لنماذج مكافأة العملية. أنت تضبط المعاملات الفائقة في العتمة.
مخطط التعليق التوضيحي trajectory_eval في Potato يعالج هذا. يمر المعلّقون على كل خطوة من التتبع ويسجلون:
- الصحة: هل هذه الخطوة صحيحة أم خاطئة؟
- نوع الخطأ: يُختار من تصنيف هرمي تعرّفه أنت
- مستوى الخطورة: طفيف أو كبير أو حرج، بأوزان درجات قابلة للتهيئة
- التبرير: شرح نصي حر للخطأ (اختياري)
- الدرجة الجارية: درجة تراكمية تتناقص بحسب الخطورة، فتعطيك منحنى جودة لكل تتبع
يغطي هذا الدليل الإعداد كاملاً: تعريف تصنيف الأخطاء، وتشغيل التعليق التوضيحي، وتحليل البيانات المجموعة. ولمرجع تهيئة المخطط، انظر الوثائق المصدرية.
نظرة عامة على مخطط تقييم المسار
بُني مخطط trajectory_eval لتقييم تتبعات الوكلاء متعددة الخطوات بالتسلسل. فبدل تقييم جودة كلي واحد، ينتج تعليقاً منظماً عن الأخطاء لكل خطوة، فتخرج بخريطة تفصيلية لموضع فشل الوكيل وسببه.
إليك ما تفعله واجهة التعليق التوضيحي عند كل خطوة:
- يرى المعلّق محتوى الخطوة الحالية (تفكير، إجراء، ملاحظة، كود، إلخ)
- يضع الخطوة صحيحة أو خاطئة
- إن كانت خاطئة، يختار نوع الخطأ من التصنيف الهرمي
- يسند مستوى خطورة (طفيف أو كبير أو حرج)
- اختيارياً، يكتب تبريراً يشرح الخطأ
- تتحدّث الدرجة الجارية أعلى الواجهة تلقائياً
يتنقل المعلّق عبر التتبع خطوة خطوة، فيبني ملفاً كاملاً للأخطاء.
تعرض واجهة تقييم المسار كل خطوة مع درجتها:
Each step gets a correctness rating, error type, and severity level with a running score that decrements based on severity
تصميم تصنيف هرمي للأخطاء
التصنيف هو ما يجعل تقييم المسار مجدياً. فإن أحكمته أمكنك تجميع الأخطاء عبر التتبعات ورصد أنماط الفشل المنهجية، وإن أخطأت فيه لن تُفضي تسمياتك إلى شيء. إليك تصنيفاً أبدأ منه، بأربع فئات عليا.
أخطاء التفكير
تقع حين يكون تفكير الوكيل معيباً، حتى لو كان ما يراه وما يفعله سليماً فيما عدا ذلك.
| نوع الخطأ | الوصف | مثال |
|---|---|---|
logical_error | استنتاج منطقي غير صحيح | «بما أن A تستلزم B، وB صحيحة، فلا بد أن A صحيحة» (إثبات التالي) |
incorrect_assumption | يفترض ما لا تدعمه الأدلة | يفترض وجود ملف من دون التحقق |
over_generalization | يستخلص نتيجة أوسع مما تسمح به الأدلة المحدودة | «فشلت هذه الدالة مرة، إذن واجهة API كلها معطّلة» |
circular_reasoning | تُستخدم النتيجة مقدمةً | «الجواب هو X لأن X صحيح» |
incorrect_calculation | خطأ في حساب رياضي أو منطقي | خطأ بمقدار واحد في التفكير بحدود الحلقة |
أخطاء الإدراك
تقع حين يسيء الوكيل قراءة المعلومات في ملاحظاته أو تفسيرها أو يغفل عنها.
| نوع الخطأ | الوصف | مثال |
|---|---|---|
missed_element | يخفق في ملاحظة معلومة ذات صلة | يغفل رسالة خطأ في مخرجات الطرفية |
misidentified_element | يسيء تفسير ما يراه | يقرأ خطأ 404 على أنه استجابة ناجحة |
hallucinated_element | يشير إلى شيء غير موجود | يحيل إلى معامل دالة لا وجود له |
outdated_reference | يستخدم معلومة قديمة من خطوة سابقة | يستخدم قيمة متغير جرت الكتابة فوقها |
أخطاء الإجراء
تقع حين يتخذ الوكيل الإجراء الخاطئ، أو الإجراء الصحيح بطريقة خاطئة.
| نوع الخطأ | الوصف | مثال |
|---|---|---|
wrong_tool | يختار أداة غير مناسبة للمهمة | يستخدم grep حيث تلزم find |
wrong_arguments | الأداة صحيحة والمعاملات خاطئة | يمرر مسار ملف خاطئاً إلى أمر تحرير |
premature_termination | يتوقف قبل اكتمال المهمة | يعيد جواباً بعد أن وجد معلومة جزئية |
unnecessary_action | يتخذ إجراءً بلا فائدة | يعيد قراءة ملف قرأه للتو |
destructive_action | يتخذ إجراءً يسبب ضرراً | يحذف ملفاً من دون نسخة احتياطية |
أخطاء التواصل
تظهر في ردود الوكيل على المستخدمين أو في طريقة سرده لعمله.
| نوع الخطأ | الوصف | مثال |
|---|---|---|
unclear_explanation | الشرح مُربك أو ملتبس | يصف إصلاحاً من دون أن يقول ما الذي كان معطّلاً |
missing_context | يحذف سياقاً جوهرياً من الرد | يبلّغ بالنجاح من دون ذكر التحفظات |
incorrect_summary | الملخص لا يطابق الإجراءات الفعلية | يزعم أنه حرّر 3 ملفات بينما تغيّر ملفان فقط |
overconfident_claim | يعرض الاحتمال على أنه يقين | «هذا سيصلح المشكلة قطعاً» عن تغيير لم يُختبر |
مستويات الخطورة وأوزان الدرجات
يحصل كل خطأ على مستوى خطورة. والأوزان الافتراضية هي:
| الخطورة | الوزن | الوصف |
|---|---|---|
minor | -1 | مشكلات صغيرة لا تُخرج التتبع عن مساره (مثل إجراء غير ضروري، شرح غير واضح) |
major | -5 | أخطاء مؤثرة تهدر الجهد أو تنتج نتائج خاطئة جزئياً (مثل أداة خاطئة، افتراض غير صحيح) |
critical | -10 | أخطاء تكسر التتبع من أساسه (مثل إجراء تدميري، إنهاء مبكر بجواب خاطئ) |
تبدأ الدرجة الجارية من 100 وتنخفض بوزن الخطورة عند كل خطأ. فالتتبع الذي ينتهي عند 85 وقع فيه بضع مشكلات طفيفة؛ والذي ينتهي عند 40 وقعت فيه إخفاقات كبيرة عدة.
يمكنك تغيير هذه الأوزان في التهيئة:
severity_levels:
- name: minor
weight: -1
description: "Small issue, does not derail the overall trace"
- name: major
weight: -5
description: "Significant error that wastes effort or produces wrong intermediate results"
- name: critical
weight: -10
description: "Fundamental failure that breaks the trace or causes harm"تهيئة YAML الكاملة
إليك ملف config.yaml كاملاً لتقييم المسار بالتصنيف الكامل:
annotation_task_name: "Agent Trajectory Error Localization"
data_files:
- "data/traces.jsonl"
item_properties:
id_key: "trace_id"
text_key: "task"
# Display agent traces with step-by-step rendering
display:
type: "agent_trace"
trace_key: "trace"
step_display:
thought: { label: "Thought", color: "#E8F0FE" }
action: { label: "Action", color: "#FFF3E0" }
observation: { label: "Observation", color: "#F1F8E9" }
code: { label: "Code", color: "#F3E5F5" }
annotation_schemes:
- annotation_type: "trajectory_eval"
# Per-step correctness check
# Hierarchical error taxonomy (shown when step is marked incorrect)
- category: "perception"
label: "Perception Error"
types:
- name: "missed_element"
label: "Missed Element"
description: "Fails to notice relevant information in observations"
- name: "misidentified_element"
label: "Misidentified Element"
description: "Misinterprets what it observes"
- name: "hallucinated_element"
label: "Hallucinated Element"
description: "Refers to something not present in the context"
- name: "outdated_reference"
label: "Outdated Reference"
description: "Uses stale information from a previous step"
- category: "action"
label: "Action Error"
types:
- name: "wrong_tool"
label: "Wrong Tool"
description: "Selects an inappropriate tool for the task"
- name: "wrong_arguments"
label: "Wrong Arguments"
description: "Correct tool but incorrect parameters"
- name: "premature_termination"
label: "Premature Termination"
description: "Stops before the task is complete"
- name: "unnecessary_action"
label: "Unnecessary Action"
description: "Takes an action that adds no value"
- name: "destructive_action"
label: "Destructive Action"
description: "Takes an action that causes harm or data loss"
- category: "communication"
label: "Communication Error"
types:
- name: "unclear_explanation"
label: "Unclear Explanation"
description: "Explanation is confusing or ambiguous"
- name: "missing_context"
label: "Missing Context"
description: "Omits critical context from the response"
- name: "incorrect_summary"
label: "Incorrect Summary"
description: "Summary does not match the actual actions taken"
- name: "overconfident_claim"
label: "Overconfident Claim"
description: "States uncertain outcomes as certainties"
# Severity levels with score weights
# Running score configuration
# Optional rationale field
# Overall trace quality (optional, shown at the end)
- annotation_type: "radio"
# Annotator settings
annotator_config:
allow_back_navigation: true
require_all_steps: true
# Output settings
output:
path: "output/"
format: "jsonl"الإعداد خطوة بخطوة
1. جهّز تتبعات الوكيل
تأتي بيانات التتبع بصيغة JSONL، تتبع واحد لكل سطر. ويحتاج كل تتبع إلى معرّف ووصف مهمة وقائمة خطوات:
{
"trace_id": "trace_042",
"task": "Find the bug in the calculate_discount function and fix it",
"trace": [
{
"type": "thought",
"content": "I need to look at the calculate_discount function to find the bug. Let me search for it."
},
{
"type": "action",
"content": "search",
"action_input": "def calculate_discount"
},
{
"type": "observation",
"content": "Found in pricing.py line 45:\ndef calculate_discount(price, discount_pct):\n return price * discount_pct / 100"
},
{
"type": "thought",
"content": "The function calculates the discount amount but doesn't subtract it from the price. It should return price - (price * discount_pct / 100)."
},
{
"type": "action",
"content": "edit_file",
"action_input": "pricing.py:45: return price - (price * discount_pct / 100)"
},
{
"type": "observation",
"content": "File edited successfully."
},
{
"type": "action",
"content": "finish",
"action_input": "Fixed the calculate_discount function. It was returning the discount amount instead of the discounted price."
}
]
}إذا كانت تتبعاتك بصيغة أخرى (رسائل OpenAI، أو تشغيلات LangChain، أو سجلات محادثات Claude)، استخدم محوّل التتبعات في Potato:
python -m potato.trace_converter \
--input raw_traces/ \
--output data/traces.jsonl \
--input-format react2. هيّئ تصنيفك
ابدأ من التصنيف الكامل أعلاه، ثم اقتطع منه أو وسّعه بما يناسب وكيلك. فلوكيل برمجة، مثلاً، قد تضيف فئة code_quality:
- category: "code_quality"
label: "Code Quality Error"
types:
- name: "syntax_error"
label: "Syntax Error"
description: "Generated code has syntax errors"
- name: "runtime_error"
label: "Runtime Error"
description: "Code runs but produces an error"
- name: "logic_bug"
label: "Logic Bug"
description: "Code runs without errors but produces wrong output"
- name: "style_violation"
label: "Style Violation"
description: "Code works but violates project conventions"ولتتبعات وكلاء البرمجة، يعرض التقييم الـ diffs ومخرجات الطرفية إلى جانب التسجيل:
CodingTraceDisplay renders diffs, terminal blocks, and file reads alongside trajectory evaluation controls
3. شغّل خادم التعليق التوضيحي
potato start config.yaml -p 8000افتح http://localhost:8000 في متصفحك. سترى أول تتبع بعرض خطوة بخطوة.
4. اكتب إرشادات التعليق التوضيحي
أعطِ المعلّقين تعليمات واضحة. وثّق كحد أدنى:
- متى تُعلَّم الخطوة خاطئة مقابل صحيحة لكن دون المثلى
- كيف يُختار بين فئات الأخطاء حين ينطبق أكثر من فئة (استخدم الأكثر تحديداً)
- متى يُسند كل مستوى خطورة، بأمثلة ملموسة
- هل تُقيَّم الخطوات بناءً على المعلومات المتاحة عندها أم بأثر رجعي
سير عمل التعليق التوضيحي
حين يفتح المعلّق تتبعاً، يقع وصف المهمة في الأعلى والخطوة الأولى تحته. وتقرأ الدرجة الجارية 100 في الزاوية العلوية اليمنى.
عند كل خطوة، يقوم المعلّق بما يلي:
- يقرأ محتوى الخطوة في سياق الخطوات السابقة
- يعلّم الصحة بالنقر على «Correct» أو «Incorrect»
- إن كانت خاطئة، يختار فئة الخطأ (مثل «Reasoning Error») ثم النوع المحدد (مثل «Incorrect Assumption»)
- يسند الخطورة: طفيف أو كبير أو حرج
- يكتب تبريراً (إن كان مفعّلاً): «يفترض الوكيل أن الملف في المجلد الحالي من دون تحقق، لكن نتائج البحث أظهرت أنه في src/utils/»
- ينتقل إلى الخطوة التالية بالنقر على «Next Step» أو بضغط سهم اليمين
تتحدّث الدرجة الجارية بعد كل خطأ. علّم الخطوة 3 خطأً كبيراً (-5) فتهبط الدرجة من 100 إلى 95. وعلّم الخطوة 7 حرجاً (-10) فتنزل إلى 85.
وفي نهاية التتبع، يعطي المعلّق تقييم النجاح/الجزئي/الفشل الكلي ويرسل.
تحليل النتائج
تحميل بيانات التعليق التوضيحي
import json
import pandas as pd
from collections import Counter
from pathlib import Path
# Load all annotation files
annotations = []
output_dir = Path("output/")
for f in output_dir.glob("*.jsonl"):
with open(f) as fh:
for line in fh:
annotations.append(json.loads(line))
print(f"Loaded {len(annotations)} annotated traces")تحليل توزيع الأخطاء
# Extract all errors across all traces
errors = []
for ann in annotations:
for step_ann in ann.get("error_localization", []):
if step_ann["correctness"] == "incorrect":
errors.append({
"trace_id": ann["trace_id"],
"step_index": step_ann["step_index"],
"category": step_ann["error_category"],
"error_type": step_ann["error_type"],
"severity": step_ann["severity"],
"rationale": step_ann.get("rationale", ""),
})
error_df = pd.DataFrame(errors)
print(f"Total errors found: {len(error_df)}")
print()
# Error distribution by category
print("Errors by category:")
print(error_df["category"].value_counts())
print()
# Most common specific error types
print("Top 10 error types:")
print(error_df["error_type"].value_counts().head(10))
print()
# Severity distribution
print("Severity distribution:")
print(error_df["severity"].value_counts())تحليل موضع الخطأ
النظر في أين تميل الأخطاء إلى الوقوع داخل التتبع يكشف غالباً أنماطاً منهجية:
import matplotlib.pyplot as plt
import numpy as np
# Normalize step positions to [0, 1] range
for ann in annotations:
trace_length = len(ann.get("error_localization", []))
for step_ann in ann["error_localization"]:
if step_ann["correctness"] == "incorrect":
step_ann["normalized_position"] = step_ann["step_index"] / max(trace_length - 1, 1)
# Collect normalized positions
positions = [
step_ann["normalized_position"]
for ann in annotations
for step_ann in ann.get("error_localization", [])
if step_ann["correctness"] == "incorrect"
and "normalized_position" in step_ann
]
plt.figure(figsize=(10, 4))
plt.hist(positions, bins=20, edgecolor="black", alpha=0.7)
plt.xlabel("Normalized Position in Trace (0 = start, 1 = end)")
plt.ylabel("Error Count")
plt.title("Where Do Agent Errors Occur?")
plt.tight_layout()
plt.savefig("error_position_distribution.png", dpi=150)
print("Saved error_position_distribution.png")توزيعات الدرجة الجارية
# Extract final running scores
final_scores = []
for ann in annotations:
score = 100
severity_weights = {"minor": -1, "major": -5, "critical": -10}
for step_ann in ann.get("error_localization", []):
if step_ann["correctness"] == "incorrect":
score += severity_weights.get(step_ann["severity"], 0)
score = max(score, 0)
final_scores.append({
"trace_id": ann["trace_id"],
"final_score": score,
"overall_success": ann.get("overall_success", "unknown"),
})
score_df = pd.DataFrame(final_scores)
print("Score statistics:")
print(score_df["final_score"].describe())
print()
# Score distribution by overall success
for label in ["success", "partial", "failure"]:
subset = score_df[score_df["overall_success"] == label]
if len(subset) > 0:
print(f"{label}: mean={subset['final_score'].mean():.1f}, "
f"median={subset['final_score'].median():.1f}, "
f"n={len(subset)}")أكثر أنماط الفشل شيوعاً
# Group errors by category + type for a failure mode analysis
failure_modes = (
error_df.groupby(["category", "error_type"])
.agg(
count=("severity", "size"),
avg_severity_weight=("severity", lambda x: x.map(
{"minor": 1, "major": 5, "critical": 10}
).mean()),
)
.sort_values("count", ascending=False)
)
print("Top failure modes (by frequency):")
print(failure_modes.head(15).to_string())
print()
# Impact-weighted failure modes (frequency x average severity)
failure_modes["impact"] = failure_modes["count"] * failure_modes["avg_severity_weight"]
print("Top failure modes (by impact):")
print(failure_modes.sort_values("impact", ascending=False).head(10).to_string())السياق البحثي
يتماشى توطين الأخطاء لكل خطوة مع عدة خيوط حديثة في تقييم الوكلاء:
وسم TRAIL (شركة Patronus AI، 2025) 148 مساراً لوكلاء مأخوذة من GAIA وSWE-bench Lite وفق تصنيف يضم أكثر من 20 نوع خطأ، بمجموع 841 خطأ. والنتيجة الجديرة بالانتباه هي صعوبة تحديد موضع الخطأ: أفضل نموذج استدلال طويل السياق جرّبوه بلغ 11% دقة مشتركة على فئة الخطأ وموضعه معاً. هذه هي المهمة التي يسندها trajectory_eval إلى المعلِّقين البشر، ولهذا تستحق تسمياتهم ما يُدفع فيها.
أما AgentRewardBench (مختبر McGill NLP، 2025) فذهب إلى الحكّام أنفسهم. يجمع 1302 مسار وكيل ويب عبر خمسة معايير قياس، ويطلب من خبير مراجعة كل مسار من حيث النجاح والآثار الجانبية والتكرار، ثم يقيّم اثني عشر حكماً من نماذج اللغة مقابل تلك المراجعات. لم يتصدّر أي حكم في كل المعايير، كما أن التقييمات القائمة على القواعد التي تأتي مع تلك المعايير قلّلت من عدد المرات التي نجح فيها الوكلاء فعلاً. إن كنت تنوي أتمتة جزء من هذا التصنيف بنموذج، فهذا هو شكل الفحص الذي تحتاجه.
كما أن تسميات صحة الخطوة ودرجة خطورتها الآتية من trajectory_eval تغذّي تدريب نماذج مكافأة العملية مباشرة: كل خطوة موسومة هي مثال تدريبي مع إشارة جودة مرجعية.
ومقالة Anthropic Demystifying evals for AI agents تقدّم الصيغة العملية للحجة نفسها: قيّم النص الكامل للجلسة لا النتيجة وحدها، واستخدم مقيّمين قائمين على النماذج مع معايير صريحة لكيفية استدعاء الوكيل للأدوات وحديثه مع المستخدم. وتحذّر أيضاً من التقييم مقابل تسلسل خطوات محدّد سلفاً، لأن الوكلاء يجدون باستمرار مسارات صحيحة لم يتوقّعها مصمّم التقييم. احتفظ بذلك وأنت تطبّق هذا التصنيف: الخطوة خطأ لأنها كانت خاطئة، لا لأنها كانت غير متوقَّعة.
كما تنطبق الدرجة الجارية المرجّحة بالخطورة على إشارات المكافأة المستخدمة في RLHF. فمنحنى درجات يهبط هبوطاً حاداً عند الخطوة 5 من تتبع من 20 خطوة يخبرك بالضبط أين يحتاج الوكيل إلى عمل، وهذا أقبل للتنفيذ بكثير من مكافأة واحدة في نهاية التتبع.
الخلاصة
يحوّل مخطط trajectory_eval تقييم الوكلاء من فحص نجاح/فشل إلى أداة تشخيص. فبتصنيف هرمي وتسجيل للخطورة ودرجة جارية، ترى أي خطوة أخطأت، وأي نوع من الخطأ كان، وكم كان سيئاً، وأين تميل الأخطاء إلى التجمّع عبر التتبعات. كما أن التسميات على مستوى الخطوة جاهزة للاستخدام بيانات تدريب لنماذج مكافأة العملية.
ابدأ من التصنيف الكامل في هذا الدليل، ثم صقله بما يناسب وكيلك وأنماط الأخطاء التي تراها فعلاً. وأفضل تصنيف هو الذي يشير إلى إصلاحات تستطيع إجراءها.