Agent 2.0 exdev 서버 배포 스택
- server-dev start/stop/deploy 및 Gitea push 자동 배포 - local-dev 로컬 개발 환경 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,193 @@
|
||||
"""
|
||||
제안 핸들러 모듈
|
||||
──────────────
|
||||
낮은 신뢰도 시 대안 질문 제안
|
||||
"""
|
||||
|
||||
from typing import List, Dict, Any, Optional
|
||||
|
||||
|
||||
SUGGESTION_BLOCK_MARKER = "💡 혹시 이런 것을 찾으셨나요?"
|
||||
SUGGESTION_BLOCK_MARKER_ALT = "혹시 이런 것을 찾으셨나요?"
|
||||
|
||||
|
||||
def strip_suggestion_block(text: Optional[str]) -> str:
|
||||
"""LLM 이력용: 추천 블록(및 LLM 모방 블록) 제거, FAQ 본문만 반환"""
|
||||
if not text:
|
||||
return text or ""
|
||||
|
||||
indices = []
|
||||
for marker in (SUGGESTION_BLOCK_MARKER, SUGGESTION_BLOCK_MARKER_ALT):
|
||||
idx = text.find(marker)
|
||||
if idx != -1:
|
||||
indices.append(idx)
|
||||
|
||||
if not indices:
|
||||
return text
|
||||
|
||||
return text[: min(indices)].rstrip()
|
||||
|
||||
|
||||
def has_suggestion_block(text: Optional[str]) -> bool:
|
||||
if not text:
|
||||
return False
|
||||
return any(marker in text for marker in (SUGGESTION_BLOCK_MARKER, SUGGESTION_BLOCK_MARKER_ALT))
|
||||
|
||||
|
||||
class SuggestionHandler:
|
||||
"""낮은 신뢰도 답변에 대한 대안 질문 제안"""
|
||||
|
||||
# 신뢰도 임계값 (기본값 — Config.from_env / LOW_CONFIDENCE_THRESHOLD 로 override)
|
||||
DEFAULT_LOW_CONFIDENCE_THRESHOLD = 0.65
|
||||
DEFAULT_HIGH_CONFIDENCE_THRESHOLD = 0.75
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
low_confidence_threshold: float = DEFAULT_LOW_CONFIDENCE_THRESHOLD,
|
||||
high_confidence_threshold: float = DEFAULT_HIGH_CONFIDENCE_THRESHOLD,
|
||||
):
|
||||
"""
|
||||
Args:
|
||||
low_confidence_threshold: 낮은 신뢰도 기준 (기본 0.65, env LOW_CONFIDENCE_THRESHOLD)
|
||||
high_confidence_threshold: 높은 신뢰도 기준 (기본 0.75, env HIGH_CONFIDENCE_THRESHOLD)
|
||||
"""
|
||||
self.low_confidence_threshold = low_confidence_threshold
|
||||
self.high_confidence_threshold = high_confidence_threshold
|
||||
|
||||
def is_low_confidence(self, top_score: Optional[float]) -> bool:
|
||||
"""신뢰도가 낮은지 판단
|
||||
|
||||
Args:
|
||||
top_score: 최상위 검색 결과의 점수
|
||||
|
||||
Returns:
|
||||
낮은 신뢰도 여부
|
||||
"""
|
||||
if top_score is None:
|
||||
return True
|
||||
return top_score < self.low_confidence_threshold
|
||||
|
||||
def generate_suggestions(
|
||||
self,
|
||||
top_results: List[Dict[str, Any]],
|
||||
top_scores: List[float],
|
||||
max_suggestions: int = 3
|
||||
) -> Optional[str]:
|
||||
"""대안 질문 제안 생성
|
||||
|
||||
Args:
|
||||
top_results: 검색 결과 리스트
|
||||
top_scores: 검색 점수 리스트
|
||||
max_suggestions: 최대 제안 개수 (기본 3개)
|
||||
|
||||
Returns:
|
||||
제안 문자열 또는 None
|
||||
"""
|
||||
if not top_results or not top_scores:
|
||||
return None
|
||||
|
||||
# 최상위 점수가 충분히 높으면 제안 불필요
|
||||
if not self.is_low_confidence(top_scores[0]):
|
||||
return None
|
||||
|
||||
# 1위 포함 상위 max_suggestions개 질문 제안 (번호 1부터)
|
||||
suggestion_count = min(max_suggestions, len(top_results))
|
||||
|
||||
if suggestion_count <= 0:
|
||||
return None
|
||||
|
||||
suggestions = []
|
||||
for i in range(suggestion_count):
|
||||
question = top_results[i]["q"]
|
||||
score = top_scores[i] if i < len(top_scores) else None
|
||||
suggestions.append({
|
||||
"index": i + 1,
|
||||
"question": question,
|
||||
"score": score,
|
||||
})
|
||||
|
||||
if not suggestions:
|
||||
return None
|
||||
|
||||
# 제안 텍스트 생성
|
||||
suggestion_text = f"\n\n{SUGGESTION_BLOCK_MARKER}\n"
|
||||
|
||||
for suggestion in suggestions:
|
||||
# 점수가 있으면 신뢰도 표시 (선택적)
|
||||
# suggestion_text += f"{suggestion['index']}. {suggestion['question']} (유사도: {suggestion['score']:.2f})\n"
|
||||
suggestion_text += f"{suggestion['index']}. {suggestion['question']}\n"
|
||||
|
||||
suggestion_text += "\n위 질문 중 하나를 선택하시면 정확한 답변을 드리겠습니다."
|
||||
|
||||
return suggestion_text
|
||||
|
||||
def enhance_answer_with_suggestions(
|
||||
self,
|
||||
answer: str,
|
||||
top_results: List[Dict[str, Any]],
|
||||
top_scores: List[float],
|
||||
max_suggestions: int = 3
|
||||
) -> str:
|
||||
"""답변에 제안 추가
|
||||
|
||||
Args:
|
||||
answer: 원본 답변
|
||||
top_results: 검색 결과
|
||||
top_scores: 검색 점수
|
||||
max_suggestions: 최대 제안 개수
|
||||
|
||||
Returns:
|
||||
제안이 추가된 답변 (또는 원본)
|
||||
"""
|
||||
if has_suggestion_block(answer):
|
||||
return answer
|
||||
|
||||
suggestions = self.generate_suggestions(top_results, top_scores, max_suggestions)
|
||||
|
||||
if suggestions:
|
||||
return answer + suggestions
|
||||
|
||||
return answer
|
||||
|
||||
def get_confidence_level(self, score: Optional[float]) -> str:
|
||||
"""점수를 신뢰도 레벨로 변환
|
||||
|
||||
Args:
|
||||
score: 검색 점수
|
||||
|
||||
Returns:
|
||||
"high", "medium", "low" 중 하나
|
||||
"""
|
||||
if score is None:
|
||||
return "low"
|
||||
|
||||
if score >= self.high_confidence_threshold:
|
||||
return "high"
|
||||
elif score >= self.low_confidence_threshold:
|
||||
return "medium"
|
||||
else:
|
||||
return "low"
|
||||
|
||||
def should_show_confidence_warning(self, top_score: Optional[float]) -> bool:
|
||||
"""신뢰도 경고를 표시해야 하는지
|
||||
|
||||
Args:
|
||||
top_score: 최상위 점수
|
||||
|
||||
Returns:
|
||||
경고 표시 여부
|
||||
"""
|
||||
if top_score is None:
|
||||
return True
|
||||
|
||||
# 매우 낮은 신뢰도 (0.50 미만)
|
||||
return top_score < 0.50
|
||||
|
||||
def generate_confidence_warning(self) -> str:
|
||||
"""신뢰도 경고 메시지 생성"""
|
||||
return (
|
||||
"\n\n⚠️ **정확도 안내**\n"
|
||||
"질문과 정확히 일치하는 정보를 찾기 어려웠습니다.\n"
|
||||
"더 구체적으로 질문하시거나, 아래 옵션을 참고해 주세요.\n"
|
||||
"정확한 답변이 필요하시면 한국도로공사 콜센터(1588-2504)로 문의해 주세요."
|
||||
)
|
||||
Reference in New Issue
Block a user