59e38d80b0
E.3 400→600자 튜닝 전후 비교 + 단계 5 failure mode 분석의 기준 필드 추가. - migrations/135: answer_length/covered_aspects/missing_aspects/model_name/prompt_version 컬럼 + prompt_version 인덱스 - ORM: ask_event.py에 동일 5개 필드 매핑 - prompt_versions.py: ASK_PROMPT_VERSION="search_synthesis.v1-400char" 상수 + resolve_primary_model() helper - search_telemetry.record_ask_event: 시그니처에 keyword-only 필드 5개 추가 (하위 호환) - search.py: refused + success 두 호출사이트에서 새 필드 전달. answer_length는 len(sr.answer or ""), model_name/prompt_version은 상수 모듈 기반 기존 호출 구조(이미 search_telemetry+background_tasks로 DB insert 중)는 유지. 순수 확장 커밋. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
45 lines
2.1 KiB
Python
45 lines
2.1 KiB
Python
"""ask_events 테이블 ORM — /ask 호출 관측 (Phase 3.5a migration 102, Phase 3.5b 배선)
|
|
|
|
threshold calibration + verifier FP 분석 + defense layer 디버깅 데이터.
|
|
"""
|
|
|
|
from datetime import datetime
|
|
from typing import Any
|
|
|
|
from sqlalchemy import BigInteger, Boolean, DateTime, Float, ForeignKey, Integer, String, Text
|
|
from sqlalchemy.dialects.postgresql import JSONB
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
|
|
from core.database import Base
|
|
|
|
|
|
class AskEvent(Base):
|
|
__tablename__ = "ask_events"
|
|
|
|
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
|
|
query: Mapped[str] = mapped_column(Text, nullable=False)
|
|
user_id: Mapped[int | None] = mapped_column(
|
|
BigInteger, ForeignKey("users.id", ondelete="SET NULL")
|
|
)
|
|
completeness: Mapped[str | None] = mapped_column(Text) # full / partial / insufficient
|
|
synthesis_status: Mapped[str | None] = mapped_column(Text)
|
|
confidence: Mapped[str | None] = mapped_column(Text) # high / medium / low
|
|
refused: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
|
classifier_verdict: Mapped[str | None] = mapped_column(Text) # sufficient / insufficient
|
|
max_rerank_score: Mapped[float | None] = mapped_column(Float)
|
|
aggregate_score: Mapped[float | None] = mapped_column(Float)
|
|
hallucination_flags: Mapped[list[Any] | None] = mapped_column(JSONB, default=list)
|
|
evidence_count: Mapped[int | None] = mapped_column(Integer)
|
|
citation_count: Mapped[int | None] = mapped_column(Integer)
|
|
defense_layers: Mapped[dict[str, Any] | None] = mapped_column(JSONB)
|
|
total_ms: Mapped[int | None] = mapped_column(Integer)
|
|
# Phase E.1: 측정 필드 확장 (answer_length가 E.3 400→600자 비교 핵심)
|
|
answer_length: Mapped[int | None] = mapped_column(Integer)
|
|
covered_aspects: Mapped[list[Any] | None] = mapped_column(JSONB)
|
|
missing_aspects: Mapped[list[Any] | None] = mapped_column(JSONB)
|
|
model_name: Mapped[str | None] = mapped_column(Text)
|
|
prompt_version: Mapped[str | None] = mapped_column(Text)
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), default=datetime.now, nullable=False
|
|
)
|