34f79f84f2
Plan 본래 의도: 근거 선별은 4B, 합성은 26B. - evidence_service: LLM 호출을 primary(26B MLX) → triage(4B Ollama) 로 전환. Ollama concurrent 가능하므로 get_mlx_gate() 제거. synthesis 는 여전히 llm_gate Semaphore(1) 경유로 MLX 보호. - prompt_version v3-evidence-triage bump (synthesis 프롬프트 자체는 v2-600char 그대로, evidence LLM 경로 변경을 분리 추적). - migrations 161/162: analyze_events 에 answerability / partial_basis / suggested_query_count 컬럼 + partial index. /ask 는 이미 ask_events 에 completeness (full/partial/insufficient) 기록 운영 중이므로, analyze_events 쪽은 향후 문서 분석에서 answerability 개념 도입 시 활용 예비. - telemetry record_analyze_event 에 answerability / partial_basis / suggested_query_count 파라미터 확장. 기존 /ask 3-state completeness 로직 (classifier_service + 7-tier gate) 은 그대로 유지 — 이미 Phase 3.5a 에서 완성된 상태. B-2 는 LLM 부하 재분배와 관측성 확장에 집중. MLX 부하 감소 효과: 이전엔 쿼리 1건당 evidence(26B) + synthesis(26B) 2번 MLX 호출. 이제는 evidence(4B Ollama) + synthesis(26B MLX) 로 MLX 호출 절반. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
64 lines
3.3 KiB
Python
64 lines
3.3 KiB
Python
"""analyze_events 테이블 ORM — POST /documents/{id}/analyze 호출 관측 (Phase E.2)
|
|
|
|
목적: 분석 failure mode 분류 (timeout / parse / llm / missing_summary) +
|
|
source 별 사용 패턴 (document_server / synology_chat / ui_search / ui_detail / eval).
|
|
단계 3 snapshot DB 설계 입력이 됨.
|
|
"""
|
|
|
|
from datetime import datetime
|
|
from typing import Any
|
|
|
|
from sqlalchemy import ARRAY, BigInteger, Boolean, DateTime, Float, ForeignKey, Integer, Text
|
|
from sqlalchemy.dialects.postgresql import JSONB
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
|
|
from core.database import Base
|
|
|
|
|
|
class AnalyzeEvent(Base):
|
|
__tablename__ = "analyze_events"
|
|
|
|
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
|
|
doc_id: Mapped[int] = mapped_column(
|
|
BigInteger, ForeignKey("documents.id", ondelete="CASCADE"), nullable=False
|
|
)
|
|
user_id: Mapped[int | None] = mapped_column(
|
|
BigInteger, ForeignKey("users.id", ondelete="SET NULL")
|
|
)
|
|
mode: Mapped[str] = mapped_column(Text, default="quick", nullable=False) # quick / full / summary_triage / summary_deep / retrieval_select / synthesis
|
|
text_limit: Mapped[int | None] = mapped_column(Integer)
|
|
truncated: Mapped[bool] = mapped_column(Boolean, default=False)
|
|
layers_returned: Mapped[list[Any] | None] = mapped_column(JSONB, default=list)
|
|
cached: Mapped[bool] = mapped_column(Boolean, default=False)
|
|
latency_ms: Mapped[int | None] = mapped_column(Integer)
|
|
model_name: Mapped[str | None] = mapped_column(Text)
|
|
prompt_version: Mapped[str | None] = mapped_column(Text)
|
|
# None (success) | "timeout" | "llm" | "parse" | "missing_summary" | "no_text"
|
|
error_code: Mapped[str | None] = mapped_column(Text)
|
|
# document_server / synology_chat / ui_search / ui_detail / eval / unknown
|
|
source: Mapped[str] = mapped_column(Text, default="document_server", nullable=False)
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), default=datetime.now, nullable=False
|
|
)
|
|
|
|
# PR-A (migration 153) — routing shadow observability
|
|
subject_domain: Mapped[str | None] = mapped_column(Text)
|
|
risk_flags: Mapped[list[str] | None] = mapped_column(ARRAY(Text))
|
|
high_impact_task: Mapped[bool | None] = mapped_column(Boolean)
|
|
escalated_to_26b: Mapped[bool | None] = mapped_column(Boolean)
|
|
escalation_reasons: Mapped[list[str] | None] = mapped_column(ARRAY(Text))
|
|
confidence: Mapped[float | None] = mapped_column(Float)
|
|
policy_violation: Mapped[bool | None] = mapped_column(Boolean)
|
|
policy_violation_ids: Mapped[list[str] | None] = mapped_column(ARRAY(Text))
|
|
shadow_would_route_to: Mapped[str | None] = mapped_column(Text)
|
|
policy_version: Mapped[str | None] = mapped_column(Text)
|
|
|
|
# PR-B (migration 159) — 실제 호출 tier 와 R2 backlog guard 이벤트
|
|
tier: Mapped[str | None] = mapped_column(Text) # 'triage' | 'primary' | 'fallback'
|
|
suppressed_reason: Mapped[str | None] = mapped_column(Text) # 'backlog_guard(ratio=0.42,pending=7)'
|
|
|
|
# PR-B B-2 (migration 161) — /ask 3-state answerability 독립 컬럼
|
|
answerability: Mapped[str | None] = mapped_column(Text) # 'direct' | 'partial' | 'insufficient'
|
|
partial_basis: Mapped[bool | None] = mapped_column(Boolean) # partial 답변이 실제 생성됐는지
|
|
suggested_query_count: Mapped[int | None] = mapped_column(Integer)
|