        labels={r.get('id'):r.get('label') for r in output.get('labels',[])};expected={r['id']:('refund' if n<4 else 'delivery' if n<8 else 'invoice') for n,r in enumerate(original['tickets'])};classes=['refund','delivery','invoice'];check('12工单各分类一次且类别受控',set(labels)==set(expected) and len(output.get('labels',[]))==12 and all(v in classes for v in labels.values()));matrix=[[0]*3 for _ in classes];predictions=[]
        for key,gold in expected.items():
            guess=labels.get(key);predictions.append({'id':key,'gold':gold,'predicted':guess,'correct':gold==guess})
            if guess in classes:matrix[classes.index(gold)][classes.index(guess)]+=1
        metrics=[]
        for j,cls in enumerate(classes):
            tp=matrix[j][j];fp=sum(matrix[k][j] for k in range(3) if k!=j);fn=sum(matrix[j][k] for k in range(3) if k!=j);metrics.append({'class':cls,'tp':tp,'fp':fp,'fn':fn,'precision':tp/(tp+fp) if tp+fp else None,'recall':tp/(tp+fn) if tp+fn else None})
        check('混淆矩阵总数等于有效分类数',sum(map(sum,matrix))==sum(v in classes for v in labels.values()));check('宏平均与全局准确率分别计算',len(metrics)==3);write_csv(artifact('class-metrics.csv'),metrics);write_csv(artifact('ticket-predictions.csv'),predictions)
        import matplotlib;matplotlib.use('Agg');import matplotlib.pyplot as plt
        fig,ax=plt.subplots(figsize=(7,5));ax.imshow(matrix,cmap='Purples');ax.set_xticks(range(3),classes);ax.set_yticks(range(3),classes);ax.set_xlabel('Predicted');ax.set_ylabel('Human fixture label')
        for y in range(3):
            for x in range(3):ax.text(x,y,str(matrix[y][x]),ha='center',va='center',color='white' if matrix[y][x]>=3 else '#291343')
        fig.tight_layout();fig.savefig(artifact('confusion-matrix.png'),dpi=170);plt.close(fig);result={'matrix':matrix,'classes':classes,'metrics':metrics,'accuracy':sum(r['correct'] for r in predictions)/len(expected),'errors':[r for r in predictions if not r['correct']],'evaluationScope':'12 synthetic examples, not general benchmark'}
