요약
검색 신호 확장(P48)의 0단계를 끝내고 1단계 코드를 오프라인 검증까지 마쳤습니다.
남은 것은 실데이터 실측 하나이며, 측정 DB 접근이 없어 여기서 멈췄습니다.
artifact 값을 추정하거나 생성하지 않았습니다.
- 브랜치:
leo (base dev)
- 커밋:
34c4595 · 252c187 · fcb397c
app/ 0줄 · 응답 계약 불변 · core.* 미접근
PR 은 아직 열지 않았습니다 — CI 의 Validate PR title 이 S15P11A705-N 을 강제하는데
티켓이 미발급이라, 키가 나온 뒤 열어야 처음부터 CI 가 초록입니다. 코드는 브랜치로 바로
받을 수 있습니다.
git fetch origin leo && git switch leo
왜 했나
현행 검색은 판단 근거가 질의–Context 코사인 하나입니다. 순위를 만드는 것도 그것이고
그 순위에서 무엇을 남길지 정하는 것도 그것이라, 원인이 여러 개인데 조절 지점이 컷밖에
없습니다. S15P11A705-266 이 τ_abs 를 질의 길이로 가른 것이 그 한계의 표현입니다.
S15P11A705-255 가 가른 한 줄이 근거입니다.
본문에 그대로 있는 짧은 질의가, 본문에 없는 같은 길이의 질의보다 낮은 유사도를 받는다.
「본문에 있음」이라는 정보가 현행 신호에 반영되지 않습니다. 컷의 문제도 임계값의
문제도 아니고, 그 정보를 담은 신호가 없다는 문제입니다.
무엇이 들어 있나
| 파일 |
역할 |
호출 |
tools/search_cut/rank_score.py |
순위 지표 baseline (Hit·Recall·MRR·nDCG) |
DB 0 · GMS 0 |
tools/search_cut/fusion.py |
keyword 신호 순수 로직 (앱·파일 비의존) |
없음 |
tools/search_cut/keyword_matrix.py |
artifact 생성 |
GMS 배치 1 + DB 읽기 |
tools/search_cut/fusion_sweep.py |
오프라인 비교 |
DB 0 · GMS 0 |
word_matrix.py · recall_probe.py |
results 행에 context_id 추가 |
— |
tests/test_search_fusion.py |
픽스처 24종 |
없음 |
docs/proposals/P48-…md |
제안 (2차 개정) |
— |
docs/implements/2026-08-05-…md |
0단계 리포트 (I51) |
— |
0단계 실측 결과
Record 42건 · 소유자 3명 · tau_abs=0.30 · tau_word=0.24 · r=0.60 · limit=20
| 세그먼트 |
n |
hit@1 |
hit@3 |
mrr |
recall@3 |
ndcg@3 |
zero |
반환 |
| 문장형(정답) 컷후 |
12 |
0.8333 |
1.0000 |
0.9028 |
1.0000 |
0.9276 |
0.0000 |
4.08 |
| 단어형(정답) 컷후 |
66 |
0.8636 |
0.9394 |
0.9015 |
0.9205 |
0.9007 |
0.0152 |
2.77 |
| 무관-문장형 |
15 |
— |
— |
— |
— |
— |
0.7333 |
0.33 |
| 무관-단어형 |
45 |
— |
— |
— |
— |
— |
0.4222 |
1.89 |
| 타인소유-단어형 |
96 |
— |
— |
— |
— |
— |
0.3646 |
1.74 |
재구성이 맞다는 독립 증거 2건
무관-문장형 침묵률 0.7333 = 11/15 → -213 이 기록한 값과 일치
스팟 정답 유사도 0.2438 → config.py 주석의 마진 0.0038 과 일치
사례 4건 — 판정 하나가 바뀝니다
| 질의 |
정답 |
top1 |
tau_word |
r·top1 |
컷 후 |
판정 |
| 그네 |
0.2671 (3위) |
0.2871 |
0.24 |
0.1723 |
3위 생존 |
① 해소됨 |
| 스팟 |
0.2438 (1위) |
0.2438 |
0.24 |
0.1463 |
1위 생존 |
마진 0.0038 |
| 신한 |
0.2301 (6위) |
0.3889 |
0.24 |
0.2333 |
잘림 |
③ 유지 |
| 부캠 |
0.2105 (8위) |
0.4102 |
0.24 |
0.2461 |
잘림 |
③ 유지 |
-255 의 ① 판정은 -266 의 단어형 분기로 이미 닫혀 있었습니다.
신한·부캠 은 두 컷 모두 아래이고, r 은 top1 이 높을수록 세게 자르므로
무관한 1위가 강할수록 정답이 확실히 잘립니다. 컷으로는 닿지 않고 순위가 올라와야
합니다 — 1단계의 표적입니다.
담당자 인계 — 실측 재개 절차
필요한 환경
|
비고 |
| Docker 데몬 |
|
측정 DB :15432 |
가드가 이 포트를 요구합니다(T33). 실사용자 기록 포함 42건 |
GMS_API_KEY |
.env (이 저장소에 커밋되지 않음) |
venv |
pytest 실행용 |
실행
# ① 행렬 재생성 — context_id 가 추가됐으므로 기존 3종 중 둘을 다시 뜹니다
DATABASE_URL=<:15432> python tools/search_cut/word_matrix.py # GMS 배치 1회
DATABASE_URL=<:15432> python tools/search_cut/recall_probe.py # GMS 배치 1회
# ② keyword 신호 artifact
DATABASE_URL=<:15432> python tools/search_cut/keyword_matrix.py # GMS 배치 1회 + DB
# ③ 비교 — 여기서부터는 GMS·DB 를 부르지 않습니다
python tools/search_cut/fusion_sweep.py --rrf-cutoff-grid "0,0.008,0.02" \
--json .search/fusion_result.json
fusion_sweep 의 조절 축: --top-k · --floor · --null-policy · --null-fill ·
--tau · --tau-word · --ratio · --rrf-cutoff · --rrf-cutoff-grid · --rrf-k
가드 — 어긋나면 수치를 내지 않고 멈춥니다
DATABASE_URL 이 :15432 가 아님 데이터 없는 DB 를 재면 「컷이 아무것도 자르지 않는다」가 결론이 됩니다
행렬 profile 불일치
행렬에 context_id 없음 ① 을 안 돌린 경우
낡은 artifact (record_count 변동)
질의에 user_id 없음
Preset version 이 섞임
채택 조건
단어형과 문장형을 합산한 평균만으로 채택하지 않는다
기존 정답의 Hit@3 · Recall@3 퇴행이 없어야 한다
최소 한 세그먼트에서 MRR 또는 nDCG 가 개선되어야 한다
무관 질의 통과율이 유의미하게 나빠지면 기각한다
신한 · 부캠 · 그네 · 스팟 을 개별 사례로 결과표에 남긴다
Preset 으로 표현할 수 없는 질의는 별도 세그먼트로 보고한다 (sweep 이 자동 집계)
RRF 는 cutoff=0 결과만으로 채택하지 않습니다 — 표에 ⚠ 잠정 으로 표시되고
JSON 에 adoptable_alone: false 로 기록됩니다. 양수 후보를 실측해 비교하고, 채택 시
측정된 양수 값 또는 0 유지 중 하나를 근거와 함께 명시해야 합니다. rrf_k 를
바꾸면 점수 스케일이 바뀌므로 cutoff 를 다시 측정합니다.
Record 42건 규모이므로 결과를 일반화하지 않고 퇴행 탐지 근거로만 사용합니다.
idf 는 df 가 불안정해 참고용이며 주 채택 근거로 쓰지 않습니다.
검증 현황
픽스처 24/24 통과 python tests/test_search_fusion.py
0단계 baseline 결정성 두 번 실행 JSON 바이트 일치
가드 6종 전부 exit 1 확인
ruff check . All checks passed!
python -m compileall app tools 통과
문서 색인 정합 통과
합성 픽스처 end-to-end sweep 4세그먼트 × 6방식 정상
② 확장 artifact = 원본 DB/GMS 계산 일치 미실행 (환경 필요)
③ sweep 반복 실행 결과 일치 미실행 (확장 artifact 필요)
pytest 는 이 머신에 venv 가 없어 미실행이며, 단독 실행으로 24/24 확인했습니다.
tests/test_search_fusion.py 는 pytest 로도 수집됩니다.
픽스처가 실데이터 전에 잡은 결함 3건
실측을 미루고 순수 로직을 먼저 검증한 것이 유효했습니다.
user_id 조인이 조용히 비었습니다. recall_probe.json 은 user_id 를 최상위에만
두는데 질의 행에서 읽고 있었습니다. 사례 4건이 전 방식에서 순위 불변이었고, 실데이터로
먼저 돌렸다면 「keyword 신호는 효과 없다」로 방식 자체를 기각했을 오판입니다.
- 모집단에
embedding_status='COMPLETED' 가 빠져 검색 후보가 아닌 Context 가 유령
후보로 올라왔고, 그것이 코사인 없는 행(sim=None)으로 나타났습니다. similarity 의
cosine float 계약이 깨지는 경로였습니다.
rrf_k 가 하드코딩돼 기록되지 않았습니다. 값이 바뀌어도 흔적이 남지 않아 낡은
cutoff 를 그대로 쓰게 됩니다.
셋 다 픽스처로 회귀를 고정했습니다.
미완
- fusion 실측 — 이 이슈의 목적
tools/search_cut/README.md 갱신 — 실측으로 artifact 스키마가 확정된 뒤 함께 쓰는 편이
낫다고 판단해 미뤘습니다
- PR — Jira 키 발급 후. 브랜치명도
{type}/{jira-key}-{summary} 로 변경 필요
docs/WORKLOG.md 한 줄 — 병합 시점에 추가
참고
- 제안:
docs/proposals/P48-search-signal-expansion.md
- 0단계 리포트:
docs/implements/2026-08-05-search-rank-baseline.md (I51)
- 선행:
S15P11A705-213 · S15P11A705-255 · S15P11A705-266
🤖 Generated with Claude Code
요약
검색 신호 확장(P48)의 0단계를 끝내고 1단계 코드를 오프라인 검증까지 마쳤습니다.
남은 것은 실데이터 실측 하나이며, 측정 DB 접근이 없어 여기서 멈췄습니다.
artifact 값을 추정하거나 생성하지 않았습니다.
leo(basedev)34c4595·252c187·fcb397capp/0줄 · 응답 계약 불변 ·core.*미접근PR 은 아직 열지 않았습니다 — CI 의
Validate PR title이S15P11A705-N을 강제하는데티켓이 미발급이라, 키가 나온 뒤 열어야 처음부터 CI 가 초록입니다. 코드는 브랜치로 바로
받을 수 있습니다.
git fetch origin leo && git switch leo왜 했나
현행 검색은 판단 근거가 질의–Context 코사인 하나입니다. 순위를 만드는 것도 그것이고
그 순위에서 무엇을 남길지 정하는 것도 그것이라, 원인이 여러 개인데 조절 지점이 컷밖에
없습니다.
S15P11A705-266이τ_abs를 질의 길이로 가른 것이 그 한계의 표현입니다.S15P11A705-255가 가른 한 줄이 근거입니다.「본문에 있음」이라는 정보가 현행 신호에 반영되지 않습니다. 컷의 문제도 임계값의
문제도 아니고, 그 정보를 담은 신호가 없다는 문제입니다.
무엇이 들어 있나
tools/search_cut/rank_score.pytools/search_cut/fusion.pytools/search_cut/keyword_matrix.pytools/search_cut/fusion_sweep.pyword_matrix.py·recall_probe.pyresults행에context_id추가tests/test_search_fusion.pydocs/proposals/P48-…mddocs/implements/2026-08-05-…md0단계 실측 결과
Record 42건 · 소유자 3명 ·
tau_abs=0.30·tau_word=0.24·r=0.60·limit=20재구성이 맞다는 독립 증거 2건
사례 4건 — 판정 하나가 바뀝니다
tau_wordr·top1-255의 ① 판정은-266의 단어형 분기로 이미 닫혀 있었습니다.신한·부캠은 두 컷 모두 아래이고,r은 top1 이 높을수록 세게 자르므로무관한 1위가 강할수록 정답이 확실히 잘립니다. 컷으로는 닿지 않고 순위가 올라와야
합니다 — 1단계의 표적입니다.
담당자 인계 — 실측 재개 절차
필요한 환경
:15432GMS_API_KEY.env(이 저장소에 커밋되지 않음)venvpytest실행용실행
fusion_sweep의 조절 축:--top-k·--floor·--null-policy·--null-fill·--tau·--tau-word·--ratio·--rrf-cutoff·--rrf-cutoff-grid·--rrf-k가드 — 어긋나면 수치를 내지 않고 멈춥니다
채택 조건
RRF 는
cutoff=0결과만으로 채택하지 않습니다 — 표에⚠ 잠정으로 표시되고JSON 에
adoptable_alone: false로 기록됩니다. 양수 후보를 실측해 비교하고, 채택 시측정된 양수 값 또는 0 유지 중 하나를 근거와 함께 명시해야 합니다.
rrf_k를바꾸면 점수 스케일이 바뀌므로 cutoff 를 다시 측정합니다.
Record 42건 규모이므로 결과를 일반화하지 않고 퇴행 탐지 근거로만 사용합니다.
idf는 df 가 불안정해 참고용이며 주 채택 근거로 쓰지 않습니다.검증 현황
pytest는 이 머신에 venv 가 없어 미실행이며, 단독 실행으로 24/24 확인했습니다.tests/test_search_fusion.py는 pytest 로도 수집됩니다.픽스처가 실데이터 전에 잡은 결함 3건
실측을 미루고 순수 로직을 먼저 검증한 것이 유효했습니다.
user_id조인이 조용히 비었습니다.recall_probe.json은user_id를 최상위에만두는데 질의 행에서 읽고 있었습니다. 사례 4건이 전 방식에서 순위 불변이었고, 실데이터로
먼저 돌렸다면 「keyword 신호는 효과 없다」로 방식 자체를 기각했을 오판입니다.
embedding_status='COMPLETED'가 빠져 검색 후보가 아닌 Context 가 유령후보로 올라왔고, 그것이 코사인 없는 행(
sim=None)으로 나타났습니다.similarity의cosine float 계약이 깨지는 경로였습니다.
rrf_k가 하드코딩돼 기록되지 않았습니다. 값이 바뀌어도 흔적이 남지 않아 낡은cutoff 를 그대로 쓰게 됩니다.
셋 다 픽스처로 회귀를 고정했습니다.
미완
tools/search_cut/README.md갱신 — 실측으로 artifact 스키마가 확정된 뒤 함께 쓰는 편이낫다고 판단해 미뤘습니다
{type}/{jira-key}-{summary}로 변경 필요docs/WORKLOG.md한 줄 — 병합 시점에 추가참고
docs/proposals/P48-search-signal-expansion.mddocs/implements/2026-08-05-search-rank-baseline.md(I51)S15P11A705-213·S15P11A705-255·S15P11A705-266🤖 Generated with Claude Code