Files
hyungi_document_server/app/prompts/query_analyze.txt
Hyungi Ahn d28ef2fca0 feat(search): Phase 2.1 QueryAnalyzer + LRU cache + confidence 3-tier
QueryAnalyzer 스켈레톤 구현. 자연어 쿼리를 구조화된 분석 결과로 변환.
Phase 2.1은 debug 노출 + tier 판정까지만 — retrieval 경로는 변경 X (회귀 0 목표).
multilingual/filter 실제 분기는 2.2/2.3에서 이 분석 결과를 활용.

app/prompts/query_analyze.txt
 - gemma-4 JSON-only 응답 규약
 - intent/query_type/domain_hint/language_scope/normalized_queries/
   hard_filters/soft_filters/expanded_terms/analyzer_confidence
 - 4가지 예시 (자연어 법령, 정확 조항, 뉴스 다국어, 의미 불명)
 - classify.txt 구조 참고

app/services/search/query_analyzer.py
 - LLM_TIMEOUT_MS=800 (MLX 멈춤 시 검색 전체 멈춤 방지, 절대 늘리지 말 것)
 - MAX_NORMALIZED_QUERIES=3 (multilingual explosion 방지)
 - in-memory FIFO LRU (maxsize=1000, TTL=86400)
 - cache key = sha256(query + PROMPT_VERSION + primary.model)
   → 모델/프롬프트 변경 시 자동 invalidate
 - 저신뢰(<0.5) / 실패 결과 캐시 금지
 - weight 합=1.0 정규화 (fusion 왜곡 방지)
 - 실패 시 analyzer_confidence=float 0.0 (None 금지, TypeError 방지)

app/api/search.py
 - ?analyze=true|false 파라미터 (default False — 회귀 영향 0)
 - query_analyzer.analyze() 호출 + timing["analyze_ms"] 기록
 - _analyzer_tier(conf) → "ignore" | "original_fallback" | "merge" | "analyzed"
   (tier 게이트: 0.5 / 0.7 / 0.85)
 - debug.query_analysis 필드 채움 + notes에 tier/fallback_reason
 - logger 라인에 analyzer conf/tier 병기

app/services/search_telemetry.py
 - record_search_event(analyzer_confidence=None) 추가
 - base_ctx에 analyzer_confidence 기록 (다층 confidence 시드)
 - result confidence와 분리된 축 — Phase 2.2+에서 failure 분류에 활용

검증:
 - python3 -m py_compile 통과
 - 런타임 검증은 GPU 재배포 후 수행 (fixed 7 query + 평가셋)

참조: ~/.claude/plans/zesty-painting-kahan.md (Phase 2.1 섹션)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 14:21:37 +09:00

191 lines
7.3 KiB
Plaintext

You are a search query analyzer. Analyze the query below and respond ONLY in JSON format. No other text, no markdown code blocks, no explanation.
## Response Format (return ONLY this JSON object)
{
"intent": "fact_lookup | semantic_search | filter_browse",
"query_type": "natural_language | keyword | phrase",
"domain_hint": "document | news | mixed",
"language_scope": "limited | global",
"keywords": ["..."],
"must_terms": ["..."],
"optional_terms": ["..."],
"hard_filters": {},
"soft_filters": {"domain": [], "document_type": []},
"normalized_queries": [
{"lang": "ko", "text": "...", "weight": 1.0},
{"lang": "en", "text": "...", "weight": 0.8}
],
"expanded_terms": ["..."],
"synonyms": {},
"analyzer_confidence": 0.92
}
## Field Rules
### intent (exactly one)
- `fact_lookup` — 특정 사실/조항/이름/숫자를 찾는 경우 (예: "산업안전보건법 제6장", "회사 설립일")
- `semantic_search` — 자연어 주제 검색, 개념적 매칭 (예: "기계 사고 관련 법령", "AI 안전성 동향")
- `filter_browse` — 필터/탐색 중심 (예: "2024년 PDF 문서", "Industrial_Safety domain 최근 보고서")
### query_type (exactly one)
- `natural_language` — 문장형, 조사/동사 포함 ("...에 대해 알고 싶다", "...가 뭐야")
- `keyword` — 단어 나열, 조사 없음 ("산업안전 법령 PDF")
- `phrase` — 큰따옴표로 감싸진 정확 문구 또는 고유명사/법 조항명
### domain_hint (exactly one)
- `document` — 사용자가 소유한 문서/법령/매뉴얼/보고서 영역
- `news` — 뉴스/시사/최근 동향 영역 (시간 민감, 다국어 소스)
- `mixed` — 둘 다 가능, 또는 판단 불명
### language_scope (exactly one)
- `limited` — 한국어 + 영어만 필요 (대부분 문서 검색)
- `global` — 다국어 필요 (일본어/중국어/유럽어 포함, 뉴스나 국제 주제)
### keywords / must_terms / optional_terms
- `keywords` — 쿼리의 핵심 명사/개념 (최대 8개)
- `must_terms` — 결과에 반드시 포함되어야 하는 단어 (고유명사, 법 조항 번호 등)
- `optional_terms` — 있으면 좋지만 없어도 무방
### hard_filters (빈 객체 `{}` 기본)
LLM은 쿼리에 명시적으로 나타난 경우에만 채운다. 쿼리에 명시 없으면 반드시 비운다.
가능한 키:
- `file_format`: ["pdf", "docx", "xlsx", "md", ...]
- `year`: 2024 같은 정수
- `country`: ["KR", "US", "JP", ...]
### soft_filters
추론 기반 필터. boost용. 확정 아님.
- `domain`: 아래 Domain Taxonomy에서 선택 (최대 3개)
- `document_type`: 아래 Document Types에서 선택 (최대 2개)
### normalized_queries (핵심)
같은 의미를 다른 언어/표현으로 재작성한 쿼리 목록.
- **원문 언어 항목은 반드시 포함** (weight=1.0)
- 영어 쿼리는 한국어 포함 권장 (weight=0.8)
- 한국어 쿼리는 영어 포함 권장 (weight=0.8)
- `domain_hint == "news"`일 때만 ja/zh/fr/de 추가 가능 (weight=0.5~0.6)
- **최대 3개까지만 생성** (성능 보호)
- 각 항목: `{"lang": "ko|en|ja|zh|fr|de", "text": "재작성", "weight": 0.0~1.0}`
### expanded_terms
쿼리의 동의어/관련어 확장. 검색 보조용. (최대 5개)
### synonyms
필요시 `{"원어": ["동의어1", "동의어2"]}`. 생략 가능.
### analyzer_confidence (CRITICAL)
쿼리 분석 자체의 신뢰도 (0.0 ~ 1.0). 아래 기준에 따라 엄격하게 채점:
- **0.9+** : 쿼리 의도가 명확, 도메인/언어 확실, 키워드 추출 분명
- **0.7~0.9** : 의도 대체로 명확, 일부 모호함
- **0.5~0.7** : 의도 모호, 다중 해석 가능
- **< 0.5** : 분석 불가 수준 (빈 쿼리, 의미 불명 기호열 등)
## Analysis Steps (내부 사고, 출력하지 말 것)
1. 쿼리 언어 감지
2. intent 분류 (사실 찾기 vs 주제 검색 vs 필터 탐색)
3. domain_hint 판단 (문서 vs 뉴스)
4. 핵심 키워드 추출
5. 다국어 재작성 (반드시 원문 언어 포함)
6. 필터 추론 (hard는 명시 사항만, soft는 추측 가능)
7. 자가 평가 → analyzer_confidence
## Domain Taxonomy (soft_filters.domain 후보)
Philosophy, Language, Engineering, Industrial_Safety, Programming, General
세부: Industrial_Safety/Legislation, Industrial_Safety/Practice, Programming/AI_ML, Engineering/Mechanical 등 2-level 경로 허용.
## Document Types (soft_filters.document_type 후보)
Reference, Standard, Manual, Drawing, Template, Note, Academic_Paper, Law_Document, Report, Memo, Checklist, Meeting_Minutes, Specification
## Examples
### Example 1: 자연어 한국어, 법령 검색
query: `기계 사고 관련 법령`
response:
{
"intent": "semantic_search",
"query_type": "natural_language",
"domain_hint": "document",
"language_scope": "limited",
"keywords": ["기계", "사고", "법령"],
"must_terms": [],
"optional_terms": ["안전", "규정"],
"hard_filters": {},
"soft_filters": {"domain": ["Industrial_Safety/Legislation"], "document_type": ["Law_Document"]},
"normalized_queries": [
{"lang": "ko", "text": "기계 사고 관련 법령", "weight": 1.0},
{"lang": "en", "text": "machinery accident related laws and regulations", "weight": 0.8}
],
"expanded_terms": ["산업안전", "기계안전", "사고예방"],
"synonyms": {"기계": ["설비", "machinery"]},
"analyzer_confidence": 0.88
}
### Example 2: 정확 법령명 조항
query: `산업안전보건법 제6장`
response:
{
"intent": "fact_lookup",
"query_type": "phrase",
"domain_hint": "document",
"language_scope": "limited",
"keywords": ["산업안전보건법", "제6장"],
"must_terms": ["산업안전보건법", "제6장"],
"optional_terms": [],
"hard_filters": {},
"soft_filters": {"domain": ["Industrial_Safety/Legislation"], "document_type": ["Law_Document"]},
"normalized_queries": [
{"lang": "ko", "text": "산업안전보건법 제6장", "weight": 1.0},
{"lang": "en", "text": "Occupational Safety and Health Act Chapter 6", "weight": 0.8}
],
"expanded_terms": ["산안법", "OSHA Korea"],
"synonyms": {},
"analyzer_confidence": 0.95
}
### Example 3: 뉴스 + 다국어
query: `recent AI safety news from Europe`
response:
{
"intent": "semantic_search",
"query_type": "natural_language",
"domain_hint": "news",
"language_scope": "global",
"keywords": ["AI safety", "Europe", "recent"],
"must_terms": [],
"optional_terms": ["regulation", "policy"],
"hard_filters": {},
"soft_filters": {"domain": ["Programming/AI_ML"], "document_type": []},
"normalized_queries": [
{"lang": "en", "text": "recent AI safety news from Europe", "weight": 1.0},
{"lang": "ko", "text": "유럽 AI 안전 최신 뉴스", "weight": 0.8},
{"lang": "fr", "text": "actualités récentes sur la sécurité de l'IA en Europe", "weight": 0.6}
],
"expanded_terms": ["AI regulation", "EU AI Act", "AI governance"],
"synonyms": {},
"analyzer_confidence": 0.90
}
### Example 4: 의미 불명
query: `???`
response:
{
"intent": "semantic_search",
"query_type": "keyword",
"domain_hint": "mixed",
"language_scope": "limited",
"keywords": [],
"must_terms": [],
"optional_terms": [],
"hard_filters": {},
"soft_filters": {"domain": [], "document_type": []},
"normalized_queries": [
{"lang": "ko", "text": "???", "weight": 1.0}
],
"expanded_terms": [],
"synonyms": {},
"analyzer_confidence": 0.1
}
## Query to Analyze
{query}