mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 21:42:00 +00:00
271bc6e782
真实模型下问「上周的销售额是多少」,助手写 SUM(amount) AS 销售额, COUNT(*) AS 订单数, MIN / MAX(paid_date) AS 区间首日 /
末日,报告四列都引用了,口径「周销售」只取 SUM(amount)。按「全部引用列」投影后还是 4 列,对不上口径的 1 列,整条比、
四个数都标相关口径,说明整段兜底。
- ledger/match._cells / _cell(algo 4):整条(含按全部引用列投影)没证明、又引用了这条查询两列以上的,被引用的每一列单独
投影到那一列,和每份口径的每个指标读的那一列(card_reads;口径版本没有口径卡的按取数的每一列)逐个比,仍只走
match_template / explain_sql_diff,上次的投影规则照用:投影不了(分组列、DISTINCT……)、过了时限、不可比的格照用整条
查询的那一个候选。结果记在 queries[].cells {列名: {status, candidates}},另记 one_row(sql_semantics.one_row:不分组的
聚合,一定只出一行)。整条这一层的结论、候选一字不改;老工件没有 cells,照旧按整条读。
- ledger/labels:单元格按它那一格的结论标(_view);指标用了同一条查询的好几格时,只有那条查询一定只出一行才把逐格的
结论合起来用(多行的结果里每一列各自对得上,不等于每一行的几列一起对得上)。整题照拍板 L1 不变:每个数都证明来自
同一份口径才等价;一格证明了、别的格只是相关的整题不等价,原因写「有的数和相关口径不一致,不来自口径」(以前一律说
「这次的推理」)。出具入口照 ledger/issue 现有判法只在整题等价时给,没有放宽。
- 逐格证明了的口径各处都算上(labels.candidates_of):工件 meta 的 calibers(召回按旧版本反查)、ledger/uses 的使用记录、
召回时找原答案对应的那一格、E4;E1 另数各种结论各几格(cells),E2 的兜底比例按标签上给人看的那一份说明数。
- GET /runs/{id}/ledger 的 queries 带上逐格的 cells(遮罩同候选);证据片段的 ledger 键、轮次摘要的计数本来就按每个数的
标签,数据变了、形状不变。前端只补类型;check-ledger 加一段:同一条查询一格来自口径(正文挂小标记)、一格相关(差在哪
先说多查了几列),整题不等价不给出具。
- 评测逐题比 SQL(eval/score.sql_match)取和期望配上的那一格(score.matched_cells:评分时配上的格),没给时取逐格证明了
的那一格;结果带 column。runner、rescore 传它。
测试:tests/test_ledger_cells.py(同一条查询一列等价、一列不等价各自标对;两份口径只证明了一份标那一份;口径多个指标按列
配对;指标跨两格只在一行的查询上用逐格结论;分组列、DISTINCT 逐格也不投影;照真实例子造的虚构库真跑一遍:销售额标等价
已证明,订单数、首末日标相关,整题不等价、不给出具、不自动出具,证据片段的 ledger 键一致;评测取配上的那一格)。去掉逐格
再跑,10 条失败;去掉「一行才合起来用」、去掉评测取格,各 1 条失败。
78 lines
3.8 KiB
Python
78 lines
3.8 KiB
Python
"""按新规则重新评分(细案 01 做法 10):评分规则改了(score.SCORER_VERSION 加一),已经评完的评测不必重跑一遍。
|
|
|
|
**只读封存,不调模型、不查业务库。** 每一项:期望用存下的那一份(EvalResult.gold:作答前后两次和结论),答案从作答那次运行的
|
|
封存里重新读(score.read_answer),文字要点用当时裁判给的判断(EvalResult.judge,不再请裁判),逐题 SQL 差异按同一个比对函数
|
|
再比一次(纯计算)。作答前就算不出期望、没作答的,照旧按存下的期望结论。总表跟着重算(runner.refresh_summary)。
|
|
|
|
明细(作答经过、直接回答的原文)在明细工件里:清理掉了的评测不能重评(接口先拦)。
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from typing import Any
|
|
|
|
from sqlalchemy import select
|
|
|
|
from app.db.base import SessionLocal, utcnow
|
|
from app.db.models_eval import EvalCase, EvalResult, EvalRun
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
async def rescore(eval_run_id: str) -> int:
|
|
"""重评一次评测(只这一臂)。交回结论变了的项数。"""
|
|
from app.core import artifact_store
|
|
from app.eval import runner, score
|
|
from app.eval.cases import case_fields
|
|
|
|
async with SessionLocal() as session:
|
|
run = await session.get(EvalRun, eval_run_id)
|
|
if run is None:
|
|
return 0
|
|
rows = list((await session.execute(select(EvalResult).where(EvalResult.eval_run_id == eval_run_id)
|
|
.order_by(EvalResult.seq))).scalars())
|
|
cases = {c.id: case_fields(c) for c in (await session.execute(
|
|
select(EvalCase).where(EvalCase.id.in_({r.case_id for r in rows if r.case_id})))).scalars()}
|
|
changed = 0
|
|
updates: dict[str, dict[str, Any]] = {}
|
|
for row in rows:
|
|
case = cases.get(row.case_id or "")
|
|
if row.status != "ok" or case is None:
|
|
continue
|
|
details: dict[str, Any] = {}
|
|
if row.details_artifact:
|
|
try:
|
|
details = artifact_store.load(row.details_artifact) or {}
|
|
except ValueError:
|
|
details = {}
|
|
turn = details.get("turn") or {}
|
|
if row.path is None:
|
|
graded = score.score_case(case, row.gold, score.Answer(run_id=None), path=None)
|
|
match = row.sql_match
|
|
else:
|
|
answer = await score.read_answer(row.run_id if row.path == "ran" else None, reply=turn.get("reply"))
|
|
graded = score.score_case(case, row.gold, answer, path=row.path, path_reason=turn.get("reason"),
|
|
judgement=(row.judge or {}).get("text"))
|
|
match = await score.sql_match(case, row.gold, answer, cells=score.matched_cells(graded))
|
|
if graded.verdict != row.verdict:
|
|
changed += 1
|
|
updates[row.id] = {"verdict": graded.verdict, "scores": graded.scores, "sql_match": match,
|
|
"error": graded.reason if graded.verdict != "pass" else None}
|
|
async with SessionLocal() as session:
|
|
for result_id, values in updates.items():
|
|
row = await session.get(EvalResult, result_id)
|
|
if row is not None:
|
|
for key, value in values.items():
|
|
setattr(row, key, value)
|
|
await session.flush()
|
|
run = await session.get(EvalRun, eval_run_id)
|
|
# 指纹里的评分规则版本跟着换:和别的评测比时照实标出「评分规则」一项变没变
|
|
run.fingerprint = {**dict(run.fingerprint or {}), "scorer_version": score.SCORER_VERSION}
|
|
summary = await runner.refresh_summary(session, run)
|
|
run.summary = {**summary, "rescored_at": utcnow().isoformat()}
|
|
await session.commit()
|
|
return changed
|
|
|
|
|
|
__all__ = ["rescore"]
|