fix: normalize non-scalar prediction; harden predictions insert
This commit is contained in:
@@ -14,6 +14,7 @@ from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import sqlite3
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING, Any
|
||||
@@ -162,6 +163,24 @@ def _to_text_field(value: Any) -> str:
|
||||
return json.dumps(value, ensure_ascii=False)
|
||||
|
||||
|
||||
def _normalize_prediction(answer: object) -> str | None:
|
||||
"""归一化 prediction 落库值。
|
||||
|
||||
LLM 提交的 answer 有时是 list/dict(如 {'answer': ['B']}),sqlite 无法绑定
|
||||
非标量类型直接入库会抛 ProgrammingError 击穿整轮 gather。None 保留(INFRA 空
|
||||
预测语义,供正确率判定天然计错);str 原样;其余 JSON 序列化为文本。
|
||||
|
||||
参数:
|
||||
answer: LoopResult.result 中的 answer 原始值(可能是 None/str/list/dict)。
|
||||
|
||||
返回:
|
||||
None(保留空预测语义)或可直接入库的字符串。
|
||||
"""
|
||||
if answer is None or isinstance(answer, str):
|
||||
return answer
|
||||
return _to_text_field(answer)
|
||||
|
||||
|
||||
def _zero_result(run_id: str) -> InferenceResult:
|
||||
"""空记录时的零值 InferenceResult。
|
||||
|
||||
@@ -419,7 +438,7 @@ async def _run_single_question(
|
||||
reasoning = _to_text_field(result_dict.get("reasoning", ""))
|
||||
record.update(
|
||||
{
|
||||
"prediction": result_dict.get("answer"),
|
||||
"prediction": _normalize_prediction(result_dict.get("answer")),
|
||||
"evidence": evidence,
|
||||
"reasoning": reasoning,
|
||||
"steps_used": loop_result.steps_used,
|
||||
@@ -442,8 +461,18 @@ async def _run_single_question(
|
||||
except Exception:
|
||||
logger.exception("[{}] QA {} 执行异常", qa.video_id, qa.question_id)
|
||||
|
||||
# prediction 必落库(try 外,无论成败)
|
||||
await asyncio.to_thread(log.insert, "predictions", record)
|
||||
# prediction 必落库(try 外,无论成败);绑定异常降级为最小 error 行,不击穿 gather
|
||||
try:
|
||||
await asyncio.to_thread(log.insert, "predictions", record)
|
||||
except (sqlite3.InterfaceError, sqlite3.ProgrammingError):
|
||||
logger.exception("[{}] QA {} 落库绑定异常,降级为 error 行", qa.video_id, qa.question_id)
|
||||
record["prediction"] = None
|
||||
record["stop_reason"] = "error"
|
||||
await asyncio.to_thread(
|
||||
log.insert,
|
||||
"predictions",
|
||||
{k: v for k, v in record.items() if isinstance(v, (str, int, float, type(None)))},
|
||||
)
|
||||
return record
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user