From b318c9fd4116ff64caeb8bebb5614a4040a0a0f8 Mon Sep 17 00:00:00 2001 From: lhl Date: Thu, 13 Aug 2026 11:05:01 +0800 Subject: [PATCH] feat(qa): add QAReport + QAValidator.validate_doc --- src/genesis/qa/report.py | 15 +++++++++++++++ src/genesis/qa/validator.py | 16 ++++++++++++++++ tests/test_phase5_report.py | 23 +++++++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 src/genesis/qa/report.py create mode 100644 tests/test_phase5_report.py diff --git a/src/genesis/qa/report.py b/src/genesis/qa/report.py new file mode 100644 index 0000000..650d91d --- /dev/null +++ b/src/genesis/qa/report.py @@ -0,0 +1,15 @@ +"""QA 报告(Phase 5)。""" +from __future__ import annotations + +from dataclasses import dataclass + +from genesis.eval.scorer import EvalReport + + +@dataclass +class QAReport: + passed: bool + overall_score: float + per_chapter: list[EvalReport] + failed_chapters: list[str] + summary: str diff --git a/src/genesis/qa/validator.py b/src/genesis/qa/validator.py index c4fc545..0482f4a 100644 --- a/src/genesis/qa/validator.py +++ b/src/genesis/qa/validator.py @@ -2,6 +2,7 @@ from __future__ import annotations from genesis.eval.scorer import ChapterScorer, EvalReport, ChapterArtifact +from genesis.qa.report import QAReport from genesis.writer.models import ChapterContent @@ -29,3 +30,18 @@ class QAValidator: def validate_document(self, contents: list[ChapterContent], structured_source) -> list[tuple[bool, EvalReport]]: return [self.validate_chapter(c, structured_source) for c in contents] + + def validate_doc(self, contents: list[ChapterContent], structured_source) -> QAReport: + results = self.validate_document(contents, structured_source) + passed_flags = [ok for ok, _ in results] + reports = [rep for _, rep in results] + failed = [c.chapter_id for c, ok in zip(contents, passed_flags) if not ok] + overall = sum(r.total_score for r in reports) / len(reports) if reports else 0.0 + summary = "全部章节通过" if all(passed_flags) else f"{len(failed)} 章未通过: {failed}" + return QAReport( + passed=all(passed_flags), + overall_score=round(overall, 4), + per_chapter=reports, + failed_chapters=failed, + summary=summary, + ) diff --git a/tests/test_phase5_report.py b/tests/test_phase5_report.py new file mode 100644 index 0000000..b766779 --- /dev/null +++ b/tests/test_phase5_report.py @@ -0,0 +1,23 @@ +from genesis.qa.report import QAReport +from genesis.qa.validator import QAValidator +from genesis.writer.models import ChapterContent, ContentBlock + + +def _chapter(cid, text): + return ChapterContent( + chapter_id=cid, version=1, title=cid, + blocks=[ContentBlock(block_id="1", type="paragraph", text=text)], + ) + + +def test_validate_doc_aggregates_qa_report(): + v = QAValidator() + report = v.validate_doc( + [_chapter("a", "充分且规范的说明内容,满足写入规则要求。"), _chapter("b", "x")], None + ) + assert isinstance(report, QAReport) + assert report.passed is False + assert "b" in report.failed_chapters + assert isinstance(report.overall_score, float) + assert len(report.per_chapter) == 2 + assert "未通过" in report.summary