feat: add persistent chat agent and indexed batch workflows
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
from contextlib import contextmanager
|
||||
import fcntl
|
||||
|
||||
from sqlalchemy import select, update
|
||||
|
||||
@@ -13,7 +15,25 @@ from .storage import secure_path, sha256
|
||||
from .settings_store import client_for_batch
|
||||
|
||||
|
||||
@contextmanager
|
||||
def file_index_lock(file_id: int):
|
||||
root = get_settings().processed_path
|
||||
root.mkdir(parents=True, exist_ok=True)
|
||||
with (root / f"file-{file_id}.lock").open("a+b") as handle:
|
||||
fcntl.flock(handle, fcntl.LOCK_EX)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
fcntl.flock(handle, fcntl.LOCK_UN)
|
||||
|
||||
|
||||
def parse_submission(db, file: SubmissionFile, *, embedder=None):
|
||||
with file_index_lock(file.id):
|
||||
db.refresh(file)
|
||||
return _parse_submission_locked(db, file, embedder=embedder)
|
||||
|
||||
|
||||
def _parse_submission_locked(db, file: SubmissionFile, *, embedder=None):
|
||||
path = secure_path(file.relative_path)
|
||||
if sha256(path) != file.sha256:
|
||||
file.parse_status, file.parse_error = "changed", "源文件已变化,请重新扫描并确认导入"
|
||||
@@ -59,6 +79,8 @@ def _project_facts(db, project, course_snapshot, client, *, retriever):
|
||||
category=category, query="项目名称 目标 指标 硬件 软件 实验 数据 成本 结论", limit=3, include_public=False))
|
||||
if not snippets:
|
||||
return {}
|
||||
if client is None:
|
||||
raise ModelError("未配置或无法连接评分模型")
|
||||
facts = client.json_call(prompts["facts"], {"evidence": snippets}, FactResult).model_dump()
|
||||
from .models import Student
|
||||
student = db.get(Student, project.student_id)
|
||||
@@ -102,6 +124,8 @@ def _score_criterion(db, batch, project, criterion, client, *, retriever):
|
||||
score.status, score.error = "unscorable", "无可检索的材料证据"
|
||||
db.commit()
|
||||
return score
|
||||
if client is None:
|
||||
raise ModelError("未配置或无法连接评分模型")
|
||||
page_paths = {page["page_number"]: page.get("rendered_image_path") for page in ready[0].parsed["pages"]}
|
||||
images = [page_paths[page] for page in dict.fromkeys(chunk["page"] for chunk in chunks[:2]) if page_paths.get(page)]
|
||||
public_context = retriever(db, course_version_id=batch.course_version_id, project_id=project.id,
|
||||
@@ -140,7 +164,10 @@ 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)
|
||||
client = client_factory(db, batch)
|
||||
try:
|
||||
client = client_factory(db, batch)
|
||||
except Exception:
|
||||
client = None
|
||||
files = db.scalars(select(SubmissionFile).where(SubmissionFile.project_id == project.id)).all()
|
||||
for file in files:
|
||||
if file.recognition_status == "recognized":
|
||||
@@ -178,16 +205,11 @@ def run_batch(batch_id: int, *, session_factory=SessionLocal, client_factory=cli
|
||||
batch = db.get(ScoringBatch, batch_id)
|
||||
if not batch:
|
||||
raise ValueError("评分批次不存在")
|
||||
project_ids = db.scalars(select(Project.id).where(Project.course_version_id == batch.course_version_id).order_by(Project.id)).all()
|
||||
project_ids = batch.project_ids if batch.project_ids is not None else db.scalars(
|
||||
select(Project.id).where(Project.course_version_id == batch.course_version_id).order_by(Project.id)).all()
|
||||
criteria = batch.rubric_snapshot["criteria"]
|
||||
batch.status, batch.progress_total, batch.progress_done, batch.finished_at = "running", len(project_ids) * len(criteria), 0, None
|
||||
db.commit()
|
||||
try:
|
||||
client_factory(db, batch)
|
||||
except Exception as exc:
|
||||
batch.status, batch.error, batch.finished_at = "failed", str(exc), now()
|
||||
db.commit()
|
||||
return
|
||||
failures = 0
|
||||
with ThreadPoolExecutor(max_workers=max(1, int(batch.model_config_snapshot.get("concurrency", 1)))) as pool:
|
||||
futures = [pool.submit(_run_project, batch_id, project_id, session_factory, client_factory, embedder, retriever) for project_id in project_ids]
|
||||
|
||||
Reference in New Issue
Block a user