finalize medical consultation agent backend
This commit is contained in:
@@ -35,7 +35,8 @@ class PdfExportService:
|
||||
timestamp = datetime.utcnow().strftime("%Y%m%d%H%M%S")
|
||||
file_name = f"training_record_{record.id}_{record.score_type}_{timestamp}_{uuid.uuid4().hex[:6]}.pdf"
|
||||
file_path = output_dir / file_name
|
||||
self._write_pdf(file_path, record, session)
|
||||
score_details = self.repo.list_score_details(record.id)
|
||||
self._write_pdf(file_path, record, session, score_details)
|
||||
|
||||
record.pdf_file_path = str(file_path)
|
||||
recommendation = dict(record.recommendation_result or {})
|
||||
@@ -57,7 +58,7 @@ class PdfExportService:
|
||||
)
|
||||
return self.db.scalar(stmt)
|
||||
|
||||
def _write_pdf(self, file_path: Path, record: TrainingRecord, session: TrainingSession | None) -> None:
|
||||
def _write_pdf(self, file_path: Path, record: TrainingRecord, session: TrainingSession | None, score_details: list | None = None) -> None:
|
||||
"""PDF 写入:使用 reportlab 生成 Acrobat 可正常打开的标准 PDF。"""
|
||||
try:
|
||||
from reportlab.lib import colors
|
||||
@@ -133,7 +134,7 @@ class PdfExportService:
|
||||
bottomMargin=14 * mm,
|
||||
title="医疗问诊 Agent 训练评价报告",
|
||||
)
|
||||
doc.build(self._build_story(record, session, context))
|
||||
doc.build(self._build_story(record, session, context, score_details or []))
|
||||
except ModuleNotFoundError:
|
||||
self._write_minimal_pdf(file_path, record)
|
||||
except Exception as exc:
|
||||
@@ -184,7 +185,7 @@ class PdfExportService:
|
||||
)
|
||||
file_path.write_bytes(bytes(content))
|
||||
|
||||
def _build_story(self, record: TrainingRecord, session: TrainingSession | None, context: dict[str, Any]) -> list:
|
||||
def _build_story(self, record: TrainingRecord, session: TrainingSession | None, context: dict[str, Any], score_details: list | None = None) -> list:
|
||||
"""报告模板:按基本信息、病例、提交、检查、评分细则和改进计划组织内容。"""
|
||||
Paragraph = context["Paragraph"]
|
||||
Spacer = context["Spacer"]
|
||||
@@ -220,7 +221,7 @@ class PdfExportService:
|
||||
self._append_case_section(story, case, context)
|
||||
self._append_submission_section(story, submission, context)
|
||||
self._append_order_section(story, orders, context)
|
||||
self._append_dimension_section(story, structured.get("dimension_scores") or [], context)
|
||||
self._append_dimension_section(story, score_details or structured.get("dimension_scores") or [], context)
|
||||
self._append_error_section(story, structured.get("errors") or record.wrong_points or [], context)
|
||||
self._append_list_section(story, "七、改进计划", structured.get("improvement_plan") or (record.recommendation_result or {}).get("improvement_plan") or [], context)
|
||||
self._append_list_section(story, "八、证据摘要", structured.get("evidence_summary") or [], context)
|
||||
@@ -284,17 +285,49 @@ class PdfExportService:
|
||||
if not dimensions:
|
||||
story.append(context["Paragraph"]("暂无维度评分。", context["styles"]["body"]))
|
||||
return
|
||||
rows = [["维度", "得分", "评价"]]
|
||||
rows = [["维度", "得分", "扣分原因", "置信度", "评语"]]
|
||||
for item in dimensions:
|
||||
rows.append([item.get("dimension", "未命名维度"), f"{item.get('score', 0)} / {item.get('max_score', '-')}", item.get("comment", "")])
|
||||
self._append_table(story, rows, [88, 58, 339], context)
|
||||
payload = self._score_detail_payload(item)
|
||||
rows.append([
|
||||
payload["dimension"],
|
||||
payload["score_text"],
|
||||
payload["deducted_reason"],
|
||||
payload["ai_confidence"],
|
||||
payload["comment"],
|
||||
])
|
||||
self._append_table(story, rows, [70, 45, 160, 45, 165], context)
|
||||
for index, item in enumerate(dimensions, start=1):
|
||||
title = f"{index}. {item.get('dimension', '未命名维度')}:{item.get('score', 0)} / {item.get('max_score', '-')}"
|
||||
payload = self._score_detail_payload(item)
|
||||
title = f"{index}. {payload['dimension']}:{payload['score_text']}"
|
||||
story.append(context["Paragraph"](self._safe_text(title), context["styles"]["body"]))
|
||||
self._append_list_section(story, "证据", item.get("evidence") or [], context, compact=True)
|
||||
self._append_list_section(story, "扣分原因", item.get("deductions") or [], context, compact=True)
|
||||
if item.get("improvement"):
|
||||
story.append(context["Paragraph"](f"改进动作:{self._safe_text(item.get('improvement'))}", context["styles"]["small"]))
|
||||
self._append_list_section(story, "证据", payload["evidence"], context, compact=True)
|
||||
self._append_list_section(story, "扣分原因", [payload["deducted_reason"]] if payload["deducted_reason"] else [], context, compact=True)
|
||||
if payload["comment"]:
|
||||
story.append(context["Paragraph"](f"评语:{self._safe_text(payload['comment'])}", context["styles"]["small"]))
|
||||
|
||||
def _score_detail_payload(self, item: Any) -> dict[str, Any]:
|
||||
"""评分明细展示:兼容 training_score_detail ORM 和旧 dimension_scores 字典。"""
|
||||
if isinstance(item, dict):
|
||||
score = item.get("score", 0)
|
||||
max_score = item.get("max_score")
|
||||
return {
|
||||
"dimension": item.get("dimension", "未命名维度"),
|
||||
"score_text": f"{score} / {max_score}" if max_score is not None else str(score),
|
||||
"deducted_reason": item.get("deducted_reason") or ";".join(str(value) for value in item.get("deductions", []) if value),
|
||||
"ai_confidence": item.get("ai_confidence") or "未记录",
|
||||
"comment": item.get("comment") or item.get("improvement") or "",
|
||||
"evidence": item.get("evidence_message_ids") or item.get("evidence") or [],
|
||||
}
|
||||
score = getattr(item, "score", None)
|
||||
confidence = getattr(item, "ai_confidence", None)
|
||||
return {
|
||||
"dimension": getattr(item, "dimension", "未命名维度"),
|
||||
"score_text": f"{float(score):g}" if score is not None else "未记录",
|
||||
"deducted_reason": getattr(item, "deducted_reason", "") or "",
|
||||
"ai_confidence": f"{float(confidence):g}" if confidence is not None else "未记录",
|
||||
"comment": getattr(item, "comment", "") or "",
|
||||
"evidence": getattr(item, "evidence_message_ids", None) or [],
|
||||
}
|
||||
|
||||
def _append_error_section(self, story: list, errors: list, context: dict[str, Any]) -> None:
|
||||
"""扣分问题分节:展示模型识别出的关键问题和严重程度。"""
|
||||
|
||||
Reference in New Issue
Block a user