Files
hyungi_document_server/app/models/ask_event.py
T
Hyungi Ahn 59e38d80b0 feat(api): Phase E.1 — ask_events 측정 필드 확장 (answer_length/prompt_version)
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>
2026-04-16 13:52:14 +09:00

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
)