perf: skip fact extraction for fully graded projects on batch retries

This commit is contained in:
2026-10-17 20:44:24 +08:00
parent 050aa4e02a
commit 14cef5c929
2 changed files with 49 additions and 0 deletions
+37
View File
@@ -402,6 +402,43 @@ def test_failed_model_call_cannot_overwrite_a_concurrent_teacher_review(sqlite_d
assert db.get(ScoringBatch, batch.id).progress_done == 1
@pytest.mark.parametrize("complete", [True, False])
def test_retry_skips_facts_only_for_fully_graded_projects(sqlite_db, monkeypatch, complete):
from wisegrade import scoring
db = sqlite_db
item = project(db)
created = services.create_batch(SimpleNamespace(course_version_id=1, rubric_version_id=1), db)
batch = db.get(ScoringBatch, created["id"])
criteria = batch.rubric_snapshot["criteria"][:2]
batch.rubric_snapshot = {**batch.rubric_snapshot, "criteria": criteria}
batch.status, batch.progress_total, batch.progress_done = "running", 2, 0
item.facts = {"conclusions": ["已抽取并确认的事实"]}
db.add_all([
CriterionScore(batch_id=batch.id, project_id=item.id, criterion_id=criteria[0]["criterion_id"], status="scored", model_score=0),
CriterionScore(batch_id=batch.id, project_id=item.id, criterion_id=criteria[1]["criterion_id"],
status="reviewed" if complete else "needs_review", teacher_score=0 if complete else None),
])
db.commit()
calls = []
def client_factory(*args):
calls.append("client")
def facts(*args, **kwargs):
calls.append("facts")
return {}
def scored(session, batch, item, criterion, *args, **kwargs):
return session.scalar(select(CriterionScore).where(CriterionScore.batch_id == batch.id,
CriterionScore.project_id == item.id, CriterionScore.criterion_id == criterion["criterion_id"]))
monkeypatch.setattr(scoring, "_project_facts", facts)
monkeypatch.setattr(scoring, "_score_criterion", scored)
factory = sessionmaker(bind=db.get_bind(), expire_on_commit=False)
failures, reviews = scoring._run_project(batch.id, item.id, factory, client_factory, FLAT, lambda *a, **k: [])
assert calls == ([] if complete else ["client", "facts"])
assert failures == 0 and reviews == (0 if complete else 1)
db.expire_all()
assert db.get(ScoringBatch, batch.id).progress_done == 2
assert db.get(Project, item.id).facts == {"conclusions": ["已抽取并确认的事实"]}
def test_unrebuilt_course_document_does_not_complete_task(sqlite_db):
db = sqlite_db
doc = KnowledgeDocument(course_version_id=1, title="无页面资料", document_type="course",
+12
View File
@@ -358,6 +358,18 @@ def _run_project(batch_id, project_id, session_factory, client_factory, embedder
with session_factory() as db:
batch = db.get(ScoringBatch, batch_id)
project = db.get(Project, project_id)
existing = db.scalars(select(CriterionScore).where(CriterionScore.batch_id == batch_id,
CriterionScore.project_id == project_id)).all()
graded = {score.criterion_id for score in existing
if score.status in (STATUS_SCORED, STATUS_REVIEWED)}
criteria = batch.rubric_snapshot["criteria"]
if all(criterion["criterion_id"] in graded for criterion in criteria):
# A retry in a multi-student batch must not spend two more model calls
# extracting facts for a student whose scores will all be skipped.
db.execute(update(ScoringBatch).where(ScoringBatch.id == batch_id).values(
progress_done=ScoringBatch.progress_done + len(criteria)))
db.commit()
return 0, 0
try:
client = client_factory(db, batch)
except Exception: