Hyungi Ahn
25ef3996ec
feat(chunk): Phase 1.2-G embedding 입력 강화 (title + section + text)
...
Phase 1.2-G hybrid retrieval 측정 결과 Recall 0.66 정체 + 진단:
직접 nl 쿼리 시도 결과 일부 정답 doc(3854, 3981, 3982, 3920, 3921)이
top-100에도 못 들어옴. doc은 corpus + chunks + embedding 모두 정상.
진짜 원인: 자연어 query ↔ 법령 조항 의미 거리 + 짧은 본문 embedding signal 약함.
- query: '유해화학물질을 다루는 회사가 지켜야 할 안전 의무'
- 본문: '화학물질관리법 제4장 유해화학물질 영업자'
- bge-m3 입장: chunk text만으로는 같은 의미인지 못 알아봄
해결: chunks embedding 입력에 doc.title + section_title 포함.
- before: embed(c['text'])
- after: embed('[제목] {title}\n[섹션] {section}\n[본문] {text}')
기대 효과:
- 짧은 조항 문서 매칭 회복 (3920/3921 등 300자대)
- 자연어 query → 법령 조항 의미 매칭 개선
- Recall 0.66 → 0.72~0.78
영향: chunks embedding 차원/구조 변경 X — 입력 텍스트 prefix만 다름.
재인덱싱 1회로 모든 chunks 재생성 필요.
2026-04-08 13:08:23 +09:00
Hyungi Ahn
2ca67dacea
feat(search): Phase 1.2-G hybrid retrieval (doc + chunks)
...
Phase 1.2-C 평가셋: chunks-only Recall 0.788 → 0.660 catastrophic.
ivfflat probes 1 → 10 → 20 진단 결과 잔여 차이는 chunks vs docs embedding의
본질적 차이 (segment 매칭 vs 전체 본문 평균).
해결: doc + chunks hybrid retrieval (정석).
신규 구조:
- search_vector(): 두 SQL을 asyncio.gather로 병렬 호출
- _search_vector_docs(): documents.embedding cosine top N (recall robust)
- _search_vector_chunks(): document_chunks.embedding window partition
(doc당 top 2 chunks, ivfflat top inner_k 후 ROW_NUMBER PARTITION)
- _merge_doc_and_chunk_vectors(): 가중치 + dedup
- chunk score * 1.2 (segment 매칭 더 정확)
- doc score * 1.0 (recall 보완)
- doc_id 기준 dedup, chunks 우선
데이터 흐름:
1. query embedding 1번 (bge-m3)
2. asyncio.gather([_docs_call(), _chunks_call()])
3. _merge_doc_and_chunk_vectors → list[SearchResult]
4. compress_chunks_to_docs (그대로 사용)
5. fusion (그대로)
6. (Phase 1.3) chunks_by_doc 회수 → reranker
검증 게이트 (회복 목표):
- Recall@10 ≥ 0.75 (baseline 0.788 - 0.04 이내)
- unique_docs per query ≥ 8
- natural_language_ko Recall ≥ 0.65
- latency p95 < 250ms
2026-04-08 13:02:23 +09:00
Hyungi Ahn
f4f9de4402
fix(search): Phase 1.2-C doc-level aggregation으로 다양성 회복
...
Phase 1.2-C 평가셋: Recall 0.788 → 0.531, natural_language 0.73 → 0.07.
진단:
단순 chunk top-N(limit*5=25)으로 raw chunks 가져왔는데 같은 doc의
여러 chunks가 상위에 몰림 → unique doc 다양성 붕괴.
warm test debug: 'chunks raw=16 compressed=5 unique_docs=10'
해결 (사용자 추천 C):
Window function ROW_NUMBER() PARTITION BY doc_id로 doc당 top 2 chunks만 반환.
SQL 흐름:
1. inner CTE topk: ivfflat 인덱스로 top inner_k chunks 빠르게
(inner_k = max(limit*10, 200))
2. ranked CTE: PARTITION BY doc_id ORDER BY dist ROW_NUMBER
3. outer: rn <= 2 (doc당 max 2 chunks) + JOIN documents
4. limit = limit * 4 (chunks 단위, ~limit*2 unique docs)
reranker 호환:
doc당 max 2 chunks 그대로 반환 → chunks_by_doc 보존
compress_chunks_to_docs는 그대로 동작 (best chunk per doc)
Phase 1.3 reranker가 chunks_by_doc에서 raw chunks 회수 가능
핵심 원칙: vector retrieval은 chunk로 찾고 doc으로 선택해야 한다.
2026-04-08 12:47:22 +09:00
Hyungi Ahn
76e723cdb1
feat(search): Phase 1.3 TEI reranker 통합 (코드 골격)
...
데이터 흐름 원칙: fusion=doc 기준 / reranker=chunk 기준 — 절대 섞지 말 것.
신규/수정:
- ai/client.py: rerank() 메서드 추가 (TEI POST /rerank API)
- services/search/rerank_service.py:
- rerank_chunks() — asyncio.Semaphore(2) + 5s soft timeout + RRF fallback
- _make_snippet/_extract_window — title + query 중심 200~400 토큰
(keyword 매치 없으면 첫 800자 fallback)
- apply_diversity() — max_per_doc=2, top score>=0.90 unlimited
- warmup_reranker() — 10회 retry + 3초 간격 (TEI 모델 로딩 대기)
- MAX_RERANK_INPUT=200, MAX_CHUNKS_PER_DOC=2 hard cap
- services/search_telemetry.py: compute_confidence_reranked() — sigmoid score 임계값
- api/search.py:
- ?rerank=true|false 파라미터 (기본 true, hybrid 모드만)
- 흐름: fused_docs(limit*5) → chunks_by_doc 회수 → rerank_chunks → apply_diversity
- text-only 매치 doc은 doc 자체를 chunk처럼 wrap (fallback)
- rerank 활성 시 confidence는 reranker score 기반
- tests/search_eval/run_eval.py: --rerank true|false 플래그
GPU 적용 보류:
- TEI 컨테이너 추가 (docker-compose.yml) — 별도 작업
- config.yaml rerank.endpoint 갱신 — GPU 직접 (commit 없음)
- 재인덱싱 완료 후 build + warmup + 평가셋 측정
2026-04-08 12:41:47 +09:00
Hyungi Ahn
b80116243f
feat(search): Phase 1.2-C chunks 기반 vector retrieval + raw chunks 보존
...
retrieval_service.search_vector를 documents.embedding → document_chunks.embedding로 전환.
fetch_limit = limit*5로 raw chunks를 넓게 가져온 후 doc 기준 압축.
신규: compress_chunks_to_docs(chunks, limit) → (doc_results, chunks_by_doc)
- doc_id 별 best score chunk만 doc_results (fusion 입력)
- 모든 raw chunks는 chunks_by_doc dict에 보존 (Phase 1.3 reranker용)
- '같은 doc 중복으로 RRF가 false boost' 방지
SearchResult: chunk_id / chunk_index / section_title optional 필드 추가.
- text 검색 결과는 None (doc-level)
- vector 검색 결과는 채워짐 (chunk-level)
search.py 흐름:
1. raw_chunks = await search_vector(...)
2. vector_results, chunks_by_doc = compress_chunks_to_docs(raw_chunks, limit)
3. fusion(text_results, vector_results) — doc 기준
4. (Phase 1.3) chunks_by_doc → reranker — chunk 기준
debug notes: raw=N compressed=M unique_docs=K로 흐름 검증.
데이터 의존: 재인덱싱(reindex_all_chunks.py 진행 중) 완료 후 평가셋으로 검증.
2026-04-08 12:36:47 +09:00
Hyungi Ahn
731d1396e8
fix(chunk): _chunk_legal 영어 법령 sliding window fallback
...
영어/외국 법령(ai_domain Foreign_Law 등)은 '제N조' 패턴이 없어 split 결과가
1개 element만 나옴 → 서문 chunk(첫 1500자)만 생성되고 본문 대부분 손실.
발견: doc 3759 (Industrial Safety, 93KB 영어) → 1개 chunk만 생성.
수정: parts split 결과가 1개 이하면 _chunk_sliding fallback 호출.
한국어 법령(제N조 패턴 있음)은 기존 분할 로직 그대로 작동.
Phase 1.2-D smoke test에서 발견. 재인덱싱 전 fix 필수.
2026-04-08 12:26:38 +09:00
Hyungi Ahn
f9af8dd355
fix(search): trigram threshold 0.3 → 0.15 (set_limit)
...
Phase 1.2-B 평가셋 결과 recall 0.788 → 0.750 회귀.
원인: trigram default threshold 0.3이 multi-token 쿼리에서 너무 엄격.
예: '이란 미국 전쟁 글로벌 반응' 같은 5단어 한국어 뉴스 쿼리는
title/ai_summary trigram 매칭이 거의 안 됨.
해결: search_text 시작 시 set_limit(0.15) 호출.
- trigram 매칭 더 관대 (recall ↑)
- precision은 ORDER BY similarity 가중 합산이 보정
- p95 latency 169ms 여유 충분 (목표 500ms)
2026-04-08 11:58:41 +09:00
Hyungi Ahn
ca3e1952d2
fix(search): trigram % operator escape 수정 (%% → %)
...
SQLAlchemy text() + asyncpg dialect에서 trigram operator 위치의 %%는
unescape 안 되어 'text %% unknown' 에러 발생. 단일 %로 변경.
ILIKE의 string literal 안의 %%는 PostgreSQL에서 두 wildcard로 동작했으나,
operator 위치는 escape 처리 경로가 다름.
2026-04-08 11:53:24 +09:00
Hyungi Ahn
fab3c81a0f
fix(search): Phase 1.2-B UNION 분해로 trigram/FTS 인덱스 강제 활용
...
EXPLAIN 진단: OR 통합 WHERE는 PostgreSQL planner가 인덱스 결합 못 함
(small table 765 docs라 Seq Scan 선택). Filter 524ms.
해결: WHERE OR을 CTE candidates UNION으로 분해.
- title trigram → idx_documents_title_trgm (0.5ms)
- ai_summary trigram → idx_documents_ai_summary_trgm (length>0 매치 추가)
- FTS @@ → idx_documents_fts_full (0.05ms)
EXPLAIN 측정: 525ms → 26ms (95% 감소).
본 SELECT(similarity 가중 합산 + ORDER BY) 추가하면 100~150ms 예상.
2026-04-08 11:51:06 +09:00
Hyungi Ahn
22117a2a6d
feat(search): Phase 1.2-AB — migration 016 + trigram retrieval
...
migration 016: documents FTS 확장 + trigram 인덱스 (1.5초 빌드)
- idx_documents_fts_full — title+ai_tags+ai_summary+user_note+extracted_text 통합 FTS
- idx_documents_title_trgm — title 단독 trigram
- idx_documents_extracted_text_trgm — 본문 trigram (NULL 제외)
- idx_documents_ai_summary_trgm — AI 요약 trigram
- CONCURRENTLY 불필요 (765 docs / 6.5MB)
retrieval_service.search_text: ILIKE 완전 제거 → trigram % + similarity()
- WHERE: title %, ai_summary %, FTS @@ (모두 인덱스 활용)
- ORDER BY: 5컬럼 similarity 가중 합산 + ts_rank * 2.0
- 가중치 그대로 (title 3.0 / tags 2.5 / note 2.0 / summary 1.5 / extracted 1.0)
- threshold default 0.3 (필요 시 set_limit으로 조정)
목표: text_ms 470ms → 100~200ms (ILIKE 풀스캔 제거 효과)
2026-04-07 14:36:22 +09:00
Hyungi Ahn
a4eb71d368
feat(search): Phase 1.1a 모듈 분리 — services/search/ 디렉토리
...
검색 로직을 services/search/* 모듈로 분리. trigram 도입은 Phase 1.2 인덱스와 함께.
신규:
- services/search/{__init__,retrieval_service,rerank_service,query_analyzer,evidence_service,synthesis_service}.py
- retrieval_service는 search_text/search_vector 이전 (ILIKE 동작 그대로)
- 나머지는 Phase 1.3/2/3 placeholder
이동:
- services/search_fusion.py → services/search/fusion_service.py (R100)
수정:
- api/search.py — thin orchestrator로 축소 (251줄 → 178줄)
동작 변경 없음 — 구조만 분리. 회귀 검증 후 Phase 1.2 진입.
2026-04-07 13:46:04 +09:00
Hyungi Ahn
378fbc7845
feat(chunk): Phase 0.1 chunk 인덱싱 — ORM/worker/migration 정리
...
GPU 서버에 untracked로만 존재하던 Phase 0.1 코드를 정식 commit:
- app/models/chunk.py — DocumentChunk ORM (country/source/domain 메타 포함)
- app/workers/chunk_worker.py — 6가지 chunking 전략 (legal/news/markdown/email/long_pdf/default)
- migrations/014_document_chunks.sql — pgvector + FTS + trigram 인덱스
- app/models/queue.py — ProcessingQueue enum에 'chunk' stage 추가
- app/workers/queue_consumer.py — chunk stage 등록, classify→[embed,chunk] 자동 연결
Phase 1 reranker 통합 작업의 전제 조건. document_chunks 테이블 기반 retrieval에 사용.
2026-04-07 13:26:37 +09:00
Hyungi Ahn
161ff18a31
feat(search): Phase 0.5 RRF fusion + 강한 신호 boost
...
기존 weighted-sum merge를 Reciprocal Rank Fusion으로 교체.
정확 키워드 매치에서 RRF가 평탄화되는 문제는 boost로 보완.
신규 모듈 app/services/search_fusion.py:
- FusionStrategy ABC
- LegacyWeightedSum : 기존 _merge_results 동작 (A/B 비교용)
- RRFOnly : 순수 RRF, k=60
- RRFWithBoost : RRF + title/tags/법령조문/high-text-score boost (default)
- normalize_display_scores: SearchResult.score를 [0..1] 랭크 기반 정규화
(프론트엔드가 score*100을 % 표시하므로 RRF 원본 점수 노출 시 표시 깨짐)
search.py:
- ?fusion=legacy|rrf|rrf_boost 파라미터 (default rrf_boost)
- _merge_results 제거 (LegacyWeightedSum에 흡수)
- pre-fusion confidence: hybrid는 raw text/vector 신호로 계산
(fused score는 fusion 전략마다 스케일이 달라 일관 비교 불가)
- timing에 fusion_ms 추가
- debug notes에 fusion 전략 표시
telemetry:
- compute_confidence_hybrid(text_results, vector_results) 헬퍼
- record_search_event에 confidence override 파라미터
run_eval.py:
- --fusion CLI 옵션, call_search 쿼리 파라미터에 전달
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-07 08:58:33 +09:00
Hyungi Ahn
1af94d1004
fix(search): timing 로그를 setup_logger로 출력
...
logging.getLogger("search")만 사용하면 uvicorn 기본 설정에서 INFO가
stdout에 안 나옴. 기존 core.utils.setup_logger 패턴 사용:
- logs/search.log 파일 핸들러
- stdout 콘솔 핸들러
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-07 08:43:26 +09:00
Hyungi Ahn
473e7e2e6d
feat(search): Phase 0.4 debug 응답 옵션 + timing 로그
...
?debug=true로 호출 시 단계별 candidates + timing을 응답에 포함.
디버그 옵션과 별개로 모든 검색에 timing 라인을 구조화 로그로 출력
(사용자 feedback: 운영 관찰엔 debug 응답만으론 부족).
신규 응답 필드 (debug=true 시):
- timing_ms: text_ms / vector_ms / merge_ms / total_ms
- text_candidates / vector_candidates / fused_candidates (top 20)
- confidence (telemetry와 동일 휴리스틱)
- notes (예: vector 검색 실패 시 fallback 표시)
- query_analysis / reranker_scores: Phase 1/2용 placeholder
기본 응답(debug=false)은 변화 없음 (results, total, query, mode).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-07 08:41:33 +09:00
Hyungi Ahn
70b27d4a51
fix(search): confidence 임계값 완화 + hybrid +vector boost 가산
...
baseline 평가셋 실행 시 'summary+vector' top_score 2.39가 임계값 2.5에
미달해 정답 쿼리(산업안전보건법 제6장)가 low_confidence로 잘못 잡힘.
- 텍스트 매치 임계값 0.5씩 완화 (실측 분포 반영)
- '+vector' 접미사가 있으면 hybrid 합성 매치이므로 confidence +0.10 가산
- 정답률 5/5 → 4/5 false-positive 1건 제거 기대
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-07 08:37:13 +09:00
Hyungi Ahn
50e6b5ad90
fix(search): confidence 휴리스틱 vector-only amplify 버그 수정
...
vector-only 매치(match_reason == 'vector')에서 raw 코사인 0.43이
0.6으로 잘못 amplify되어 low_confidence threshold(0.5)를 못 넘기던 문제.
- vector-only 분기: amplify 제거, _cosine_to_confidence로 일관 환산
- _cosine_to_confidence: bge-m3 코사인 분포 (무관 텍스트 ~0.4) 반영
- 코사인 0.55 = threshold 경계(0.50), 0.45 미만은 명확히 low
smoke test 결과 zzzqxywvkpqxnj1234 같은 무의미 쿼리(top cosine 0.43)가
low_confidence로 잡히지 않던 문제 해결.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-07 08:33:25 +09:00
Hyungi Ahn
f005922483
feat(search): Phase 0.3 검색 실패 자동 로깅
...
검색 실패 케이스를 자동 수집해 gold dataset 시드로 활용.
wiggly-weaving-puppy 플랜 Phase 0.3 산출물.
자동 수집 트리거 (3가지):
- result_count == 0 → no_result
- confidence < 0.5 → low_confidence
- 60초 내 동일 사용자 재쿼리 → user_reformulated (이전 쿼리 기록)
confidence는 Phase 0.3 휴리스틱 (top score + match_reason).
Phase 2 QueryAnalyzer 도입 후 LLM 기반으로 교체 예정.
구현:
- migrations/015_search_failure_logs.sql: 테이블 + 3개 인덱스
- app/models/search_failure.py: ORM
- app/services/search_telemetry.py: confidence 계산 + recent 트래커 + INSERT
- app/api/search.py: BackgroundTasks로 dispatch (응답 latency 영향 X)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-07 08:29:12 +09:00
Hyungi Ahn
e10b0f2883
fix: 뉴스 분야 필터 — file_path 폴더명 기반 매칭
...
경향신문/문화 → file_path LIKE 'news/경향신문 문화/%'
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-06 15:27:01 +09:00
Hyungi Ahn
8bb2ea4f29
fix: 뉴스 필터 트리 — 신문사명 정확 추출 + API datetime 수정
...
- PAPER_NAMES 매핑으로 'Le Monde', 'Der Spiegel' 등 정확 분리
- NewsSourceResponse datetime 타입 수정
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-06 15:18:50 +09:00
Hyungi Ahn
3cd65e4c26
fix: 사이드바 트리에서 News 제외 + 뉴스 페이지 ☰ 숨김
...
- tree API: ai_domain != 'News' 필터
- +layout: /news 경로에서 사이드바 토글 버튼 숨김
- DB: 뉴스 ai_sub_group을 신문사명으로 재설정
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-06 15:15:15 +09:00
Hyungi Ahn
557165db11
feat: 뉴스 필터 트리 (신문사 → 분야) + ai_summary 우선 표시
...
- 좌측 필터: 신문사 펼침 → 분야별 필터 (News/경향신문/문화)
- API: source 파라미터 '신문사' 또는 '신문사/분야' 지원
- 리스트: ai_summary 있으면 우선, 없으면 extracted_text fallback
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-06 15:08:50 +09:00
Hyungi Ahn
49cc86db80
feat: summarize 전용 stage — 뉴스 AI 요약 (classify 없이)
...
- summarize_worker: 요약만 생성 (분류 안 함)
- queue_consumer: summarize stage 추가 (batch 3)
- news_collector: summarize + embed 큐 등록
- process_stage enum에 'summarize' 추가
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-06 15:00:14 +09:00
Hyungi Ahn
ef6f857a6d
fix: 뉴스 페이지 — 닫기 버튼 + 페이지네이션 + 상세 링크 + 본문 입력
...
- 미리보기 닫기 버튼 추가
- 페이지네이션 (30건 단위)
- "상세" 링크 → /documents/{id}
- "본문/메모 입력" → user_note 저장
- DocumentUpdate에 is_read 필드 추가
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-06 14:38:42 +09:00
Hyungi Ahn
7ca3abf17c
feat: 뉴스 전용 페이지 + 분류 격리 + 읽음 상태
...
- /news 전용 페이지: 신문사 필터, 읽지않음 필터, 시간순 리스트, 미리보기
- 뉴스 분류 격리: ai_domain='News', classify 제거, embed만 등록
- is_read: 클릭 시 자동 읽음, 전체 읽음 API
- documents 목록에서 뉴스 제외 (source_channel != 'news')
- nav에 뉴스 링크 추가
- GET /api/news/articles, POST /api/news/mark-all-read
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-06 14:16:00 +09:00
Hyungi Ahn
d03fa0df37
fix: source_channel enum에 'news' 추가 (ORM 누락)
...
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-06 13:41:20 +09:00
Hyungi Ahn
a6c19ef76c
feat: 뉴스 자동 수집 시스템 — 6개국 신문 RSS/API
...
- news_sources 테이블 (소스 관리, UI 동적 제어)
- news_collector 워커: RSS(feedparser) + NYT API
- 중복 체크: hash(title+date+source) + URL normalize
- category 표준화, summary HTML 정제, timezone UTC
- 30일 이내만 embed, source별 try/catch
- News API: 소스 CRUD + 수동 수집 트리거
- APScheduler: 6시간 간격 자동 수집
- 대상: 경향/아사히/NYT/르몽드/신화/슈피겔
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-06 13:38:07 +09:00
Hyungi Ahn
bf8efd1cd3
feat: 임베딩 모델 변경 — nomic-embed-text → bge-m3 (1024차원, 다국어)
...
- config.yaml: embedding model → bge-m3
- document.py: Vector(768) → Vector(1024)
- embed_worker.py: 모델 버전 업데이트
- migration 011: 벡터 컬럼 재생성 (기존 임베딩 초기화)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-06 12:49:45 +09:00
Hyungi Ahn
24142ea605
fix: Codex 리뷰 5건 수정 (critical 1 + high 4)
...
1. [critical] config.yaml → settings 객체에서 taxonomy 로드 (import crash 방지)
2. [high] ODF 변환: file_path 유지, derived_path 별도 필드 (무한 중복 방지)
3. [high] 법령 분할: 첫 장 이전 조문을 "서문"으로 보존
4. [high] Inbox: review_status 필드 분리 (pending/approved/rejected)
5. [high] 삭제: soft-delete (deleted_at) + worker 방어 + active_documents 뷰
- 모든 조회에 deleted_at IS NULL 일관 적용
- queue_consumer: row 없으면 gracefully skip
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-06 07:15:13 +09:00
Hyungi Ahn
06da098eab
fix: 법령 분할 — 조문키 000 기반 장(章) 단위 분할로 변경
...
국가법령 XML은 <편>/<장> 태그가 아닌 <조문단위 조문키="xxxx000">에
"제X장 ..." 형태로 장 구분자가 포함됨. 이를 파싱하여 분할.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-03 15:05:48 +09:00
Hyungi Ahn
1668be0a75
fix: 법령 저장 후 즉시 commit — 알림 실패가 DB 롤백하지 않도록
...
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-03 14:50:39 +09:00
Hyungi Ahn
93c5805060
feat: 법령 API 전면 개편 — 26개 법령, 분할 저장, 변경 이력 추적
...
- 모니터링 법령 12개 → 26개 (산업안전/건설/위험물/소방/전기/가스/근로/환경)
- lawSearch.do로 검색, lawService.do로 본문 조회
- 대형 법령 편/장 단위 분할 저장 (fallback: 편→장→전체)
- 저장 경로: PKM/Inbox/ (AI 자동 분류 연계)
- 변경 감지 시 user_note에 이력 자동 기록
- CalDAV + SMTP 알림
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-03 14:47:08 +09:00
Hyungi Ahn
b4ca918125
fix: 벡터 검색 asyncpg 캐스트 — ::vector → cast(:embedding AS vector)
...
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-03 14:35:14 +09:00
Hyungi Ahn
e23c4feaa0
feat: 검색 전면 개편 — 필드별 가중치 + 벡터 합산 + match reason
...
검색 대상: title > ai_tags > user_note > ai_summary > extracted_text
- 필드별 가중치: title(3.0), tags(2.5), note(2.0), summary(1.5), text(1.0)
- 벡터 검색: 별도 쿼리로 분리, 결과 합산 (asyncpg 충돌 방지)
- match_reason: 어떤 필드에서 매칭됐는지 반환
- 중복 제거 + 점수 합산
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-03 14:33:34 +09:00
Hyungi Ahn
e7cd710e69
fix: hybrid 검색 단순화 — FTS + ILIKE (vector/trgm 복잡 쿼리 제거)
...
asyncpg 파라미터 바인딩 충돌 문제 근본 해결.
한국어 검색: ILIKE fallback으로 안정 동작.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-03 14:16:36 +09:00
Hyungi Ahn
3236b8d812
fix: 검색 500 에러 (ILIKE % 이스케이프) + 한글 조합 중 Enter 방지
...
- ILIKE '%' → '%%' (SQLAlchemy text() 파라미터 충돌 해결)
- e.isComposing 체크로 한글 조합 완료 전 Enter 무시
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-03 14:14:07 +09:00
Hyungi Ahn
4d205b67c2
fix: 검색 UX 개선 — Enter 키 기반 + 한국어 검색 ILIKE fallback
...
- 프론트: debounce 자동검색 제거 → Enter 키로만 검색 (한글 조합 문제 해결)
- 백엔드: trgm threshold 0.1로 낮춤 + ILIKE '%검색어%' fallback 추가
- hybrid 검색 score threshold 0.01 → 0.001로 낮춤
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-03 14:10:47 +09:00
Hyungi Ahn
b54cc25650
fix: 미분류 판단 기준 변경 — file_path 기반 → ai_domain 없음 기준
...
파일을 물리적으로 이동하지 않으므로 file_path로 미분류 판단 불가.
ai_domain이 NULL 또는 빈 문자열인 문서를 미분류로 취급.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-03 14:05:41 +09:00
Hyungi Ahn
d63a6b85e1
feat: 사이드바 3단계 재귀 트리 + 너비 확장 (320px)
...
- tree API: domain 경로를 파싱하여 계층 구조로 반환
(Industrial_Safety → Practice → Patrol_Inspection)
- Sidebar: 재귀 snippet으로 N단계 트리 렌더링
- domain 필터: prefix 매칭 (상위 클릭 시 하위 전부 포함)
- 사이드바 너비: 260px → 320px
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-03 14:03:36 +09:00
Hyungi Ahn
7f5e09096a
fix: 문서 삭제 시 processing_queue FK 제약 해결 + 변환본/preview 함께 삭제
...
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-03 13:51:35 +09:00
Hyungi Ahn
63f75de89d
fix: Qwen3.5 thinking 모드 비활성화 (enable_thinking: false)
...
JSON 응답에 Thinking Process 텍스트가 섞이는 문제 해결.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-03 13:38:10 +09:00
Hyungi Ahn
6d73e7ee12
feat: 분류 체계 전면 개편 — taxonomy + document_type + confidence
...
- config.yaml: 6개 domain × 3단계 taxonomy + 13개 document_types 정의
- classify.txt: 영문 프롬프트, taxonomy 경로 기반 분류 + 분류 규칙 주입
- classify_worker: taxonomy 검증, confidence 기반 분류, document_type 저장
- migration 008: document_type, importance, ai_confidence 컬럼
- API: DocumentResponse에 document_type, importance, ai_confidence 추가
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-03 13:32:20 +09:00
Hyungi Ahn
1b5fa95a9f
feat: 오피스 → ODF 변환 + 원본/편집본 분리 아키텍처
...
- original_path/format/hash + conversion_status 필드 추가 (migration 007)
- extract_worker: 텍스트 추출 후 xlsx→ods, docx→odt 등 ODF 변환
- 변환본은 .derived/{doc_id}.ods 에 저장
- 원본 메타 보존 (original_path/format/hash)
- file_watcher: .derived/ .preview/ 디렉토리 제외
- DocumentViewer: ODF 포맷이면 편집 버튼 자동 표시
- edit_url 있으면 "편집", 없으면 "Synology Drive에서 열기"
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-03 13:11:43 +09:00
Hyungi Ahn
b937eb948b
feat: Noto 다국어 폰트 추가 (fonts-noto-core/extra — 라틴/아랍/태국 등)
...
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-03 12:45:02 +09:00
Hyungi Ahn
1030bffc82
fix: LibreOffice 한글/CJK 폰트 추가 (fonts-noto-cjk, fonts-nanum)
...
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-03 12:43:13 +09:00
Hyungi Ahn
733f730e16
fix: preview enum 누락 + AI summary thinking 제거 + CLAUDE.md 전면 갱신
...
- queue.py: process_stage enum에 'preview' 추가
- classify_worker: ai_summary에 strip_thinking() 적용
- CLAUDE.md: 현재 아키텍처 전면 반영 (파이프라인, UI, 인프라, 코딩규칙)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-03 12:38:59 +09:00
Hyungi Ahn
6893ea132d
refactor: preview 병렬 트리거 + 파일 이동 제거 + domain 색상 바
...
- queue_consumer: extract 완료 시 classify + preview 동시 등록
- classify_worker: _move_to_knowledge() 제거, 파일 원본 위치 유지
- DocumentCard: 좌측 domain별 색상 바 (4px) 추가
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-03 12:31:57 +09:00
Hyungi Ahn
47e9981660
fix: Qwen3.5 Thinking Process 텍스트 제거 — JSON 파싱 개선
...
첫 번째 { 이전의 모든 비-JSON 텍스트를 제거하여
thinking/reasoning preamble이 있어도 JSON 추출 가능.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-03 11:44:21 +09:00
Hyungi Ahn
03b0612aa2
fix: extract_worker OFFICE_FORMATS 블록에 return 누락 수정
...
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-03 11:28:09 +09:00
Hyungi Ahn
a5186bf4aa
fix: 스프레드시트 텍스트 추출 — csv 필터 사용 (txt:Text는 Calc 미지원)
...
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-03 11:21:29 +09:00