Files
fastapi/app/agents/report_agent.py
T
2026-06-04 10:55:23 +08:00

74 lines
3.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
class ReportAgent:
"""报告 Agent:整理评分结果为接口和 PDF 可复用的报告结构。"""
def build_report(self, scoring_result: dict) -> dict:
"""报告整理:校验评分结果字段并补齐展示默认值,不重新评分。"""
dimension_scores = self._normalize_dimension_scores(scoring_result.get("dimension_scores", []))
total_score = self._safe_float(scoring_result.get("total_score"), 0)
return {
"score_type": scoring_result.get("score_type", "percentage"),
"total_score": total_score,
"dimension_scores": dimension_scores,
"score_details": self._normalize_score_details(scoring_result.get("score_details", []), dimension_scores),
"errors": self._ensure_list(scoring_result.get("errors")),
"improvement_plan": self._ensure_list(scoring_result.get("improvement_plan")),
"evidence_summary": self._ensure_list(scoring_result.get("evidence_summary")),
"guideline_refs": self._ensure_list(scoring_result.get("guideline_refs")),
"overall_comment": scoring_result.get("overall_comment", ""),
"_llm_model": scoring_result.get("_llm_model"),
"_latency_metrics": scoring_result.get("_latency_metrics", {}),
}
def _normalize_dimension_scores(self, raw_scores: object) -> list[dict]:
"""维度校验:把模型输出归一为前端和数据库可保存的评分列表。"""
if not isinstance(raw_scores, list):
return []
normalized: list[dict] = []
for item in raw_scores:
if not isinstance(item, dict):
continue
normalized.append(
{
"dimension": str(item.get("dimension", "未命名维度")),
"score": self._safe_float(item.get("score"), 0),
"max_score": self._safe_float(item.get("max_score"), 0),
"comment": str(item.get("comment", "")),
"evidence": self._ensure_list(item.get("evidence")),
"deductions": self._ensure_list(item.get("deductions")),
"improvement": str(item.get("improvement", "")),
}
)
return normalized
def _normalize_score_details(self, raw_details: object, dimension_scores: list[dict]) -> list[dict]:
"""评分明细校验:保留可写入 training_score_detail 的细粒度字段。"""
source = raw_details if isinstance(raw_details, list) and raw_details else dimension_scores
normalized: list[dict] = []
for item in source:
if not isinstance(item, dict):
continue
deductions = self._ensure_list(item.get("deductions"))
normalized.append(
{
"rule_id": item.get("rule_id"),
"dimension": str(item.get("dimension", "综合表现")),
"score": self._safe_float(item.get("score"), 0),
"deducted_reason": str(item.get("deducted_reason") or "".join(str(value) for value in deductions)),
"evidence_message_ids": self._ensure_list(item.get("evidence_message_ids") or item.get("evidence")),
"ai_confidence": self._safe_float(item.get("ai_confidence"), 0.85),
"comment": str(item.get("comment") or item.get("improvement") or ""),
}
)
return normalized
def _ensure_list(self, value: object) -> list:
"""列表校验:保证报告中的数组字段稳定返回 list。"""
return value if isinstance(value, list) else []
def _safe_float(self, value: object, default: float) -> float:
"""数值校验:把模型输出中的分数安全转换为 float。"""
try:
return float(value)
except (TypeError, ValueError):
return default