generated from kgod/ai-review-template
自内部仓库剥离深度优化与 RAG 知识库后的交付版本: - Builder 对话式简历生成(FSM + 意图路由 LLM 兜底增强) - 条目级轻度优化:事实覆盖门禁 + STAR/bullet 修复链,功能/简介/成果与技术栈同级保护 - 简历导入:DOCX/PDF 解析、结构归一、手机号脱敏 - PostgreSQL 运行时 + Alembic 迁移链 Co-Authored-By: Claude <noreply@anthropic.com>
61 lines
2.3 KiB
Python
61 lines
2.3 KiB
Python
"""P0 shadow rollout: run the LLM intent classifier beside rule routing, log only.
|
|
|
|
Observation-only — the LLM result never influences routing. Disagreements are
|
|
logged as `chat_intent_shadow` events to build the P1 golden dataset.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import dataclasses
|
|
import logging
|
|
from typing import Any
|
|
|
|
from .chat_intent_classifier import (
|
|
ChatIntentClassifier,
|
|
LLMChatIntentClassifier,
|
|
RuleBasedChatIntentClassifier,
|
|
)
|
|
from .chat_intents import CHAT_INTENT_REGISTRY_VERSION
|
|
from .llm_services import OpenAICompatibleStructuredClient, log_ai_event
|
|
from .settings import Settings
|
|
|
|
|
|
class ChatIntentShadowLogger:
|
|
"""Compares LLM vs rule classification per message. Never used for routing."""
|
|
|
|
def __init__(self, primary: ChatIntentClassifier, rules: ChatIntentClassifier) -> None:
|
|
self.primary = primary
|
|
self.rules = rules
|
|
|
|
def observe(self, message: str, *, state_summary: dict[str, Any]) -> None:
|
|
rule_result = self.rules.classify(message, state_summary=state_summary)
|
|
try:
|
|
llm_result = self.primary.classify(message, state_summary=state_summary)
|
|
except Exception as exc:
|
|
log_ai_event(
|
|
"chat_intent_shadow_error",
|
|
level=logging.WARNING,
|
|
exception=type(exc).__name__,
|
|
trace_id=getattr(exc, "trace_id", None),
|
|
)
|
|
return
|
|
log_ai_event(
|
|
"chat_intent_shadow",
|
|
registry_version=CHAT_INTENT_REGISTRY_VERSION,
|
|
rule_intent=rule_result.intent.value,
|
|
llm_intent=llm_result.intent.value,
|
|
llm_confidence=llm_result.confidence,
|
|
disagreement=llm_result.intent != rule_result.intent,
|
|
)
|
|
|
|
|
|
def build_chat_intent_shadow(settings: Settings, client: Any | None = None) -> ChatIntentShadowLogger | None:
|
|
"""Shadow only when explicitly enabled *and* an LLM provider is configured."""
|
|
if settings.intent_router_mode != "shadow" or not settings.use_openai:
|
|
return None
|
|
llm_settings = settings
|
|
if settings.intent_model:
|
|
llm_settings = dataclasses.replace(settings, openai_model=settings.intent_model)
|
|
completion = OpenAICompatibleStructuredClient(llm_settings, client)
|
|
return ChatIntentShadowLogger(LLMChatIntentClassifier(completion), RuleBasedChatIntentClassifier())
|