본문으로 건너뛰기

ADR 0042 — anomaly 가설 자동화 파이프라인 (LLM context + 검증 loop + opsKnowledge RAG)

날짜: 2026-05-08 상태: Accepted Tier: A (Architecture · ops 코파일럿 코어) 적용 범위: apps/ops/app/(ops)/dashboard/ (anomaly slot) · packages/business-logic/src/ops-anomaly/ (신설) · apps/web/lib/ai/ (LLM 호출 — server action 경유) · Firestore opsKnowledge 컬렉션 (신설)

결정문

ADR 0040 의 anomaly slot 을 채우는 4-layer LLM 파이프라인을 신설한다. 시그널 수집 → 너홀로프로-aware context builder → LLM 가설 생성 → 검증 closed-loop. 사용자 워딩 "어떻게 개선해야지" 의 본질. 단순 임계값 alert 가 아니라 자기 코드·ADR·post-mortem·메모리를 self-RAG 한 운영 인사이트 가 본 ADR 의 차별점.

배경

ADR 0040 PR-1 의 dashboard v2 는 anomaly slot 을 placeholder 로 두었고, 정적 if-else 가설만 표시한다 (recall < 0.05 → "publicDocuments statute coverage 부족"). PR-5 에서 정정한 사례 (statute coverage 부족 ❌ → sourceType filtering 미적용 ✅) 가 보여주듯, 정적 룰 기반 가설은 실측과 어긋날 위험이 크다.

너홀로프로 만이 가능한 차별점:

  • ADR 33 개 + post-mortem · retrospective 다수 + 메모리 50+ entries — 자기 운영 prior 풍부
  • ADR 0021 Platform RAG · ADR 0032 RAG 평가 인프라 ship — self-RAG 부품 모두 준비
  • ragEvalCases 14 + ragMergeQuality 시계열 — anomaly 입력 시그널 풍부

다른 SaaS ops 콘솔이 못 하는 이유는 자기 prior 를 self-RAG 하지 않아서. 우리는 인프라 있다.

결정 — 4 Layer 설계

Layer 1 — 시그널 수집 (multi-source)

신뢰도 분해 4 axis 별 source + cross-축:

시그널 source컬렉션무엇을 측정
학습 회귀 통과율tenants/{tid}/ragMergeQuality/{date}recall · precision baseline 추세
편집률tenants/{tid}/draftDiffs/*jaccardSimilarity · editIntensity 7d 분포
docgen 성공률tenants/{tid}/docgenEvents/*failureReason · elapsedMs · outcome
exemplar selectiontenants/{tid}/refinementFeedback/*up/down 비율 · half-life 90일
Functions healthgcloud Logging APIseverity≥ERROR · memoryMb · cold start
Firestore indexfirestore.indexes.json + 정적 grepcomposite index 누락 · range-after-orderby
cross-tenant 기준선aggregateDocgenStats · aggregateRelatedMemoriesStats다른 사무소 평균 (sliding window)

packages/business-logic/src/ops-anomaly/signals.ts 에 통합 collector — 각 source 는 pure async 함수, 실패 시 silent empty (다른 source 영향 없음).

Layer 2 — LLM Context Builder (너홀로프로-aware self-RAG)

opsKnowledge 컬렉션 신설 — Firestore root, 임베딩 source = ADR + post-mortem + 메모리 + past resolved hypotheses. 새 LLM 가설 생성 시 anomaly fingerprint 로 findNearest top 5 → context 에 prior 인용:

opsKnowledge/{docId} schema:
{
source: "adr" | "retrospective" | "memory" | "past-hypothesis",
title: string,
content: string, // ADR 결정문 또는 메모리 본문
embedding: { version, dims, vector },
embeddedAt: Timestamp,
metadata: {
adrNumber?: string,
fileName?: string,
pastHypothesisFingerprint?: string,
},
}

입력 context 구조 (LLM prompt):

사무소 {id} · 24h 시그널:
학습 회귀: recall 2.4% (정상 >5%, 임계 baseline)
편집률: 18% (목표 <15%, 7d ↑ 6%p)
docgen 실패: 28% (정상 5%, lawsuit-loan 4건/5건 TIMEOUT)

cross-tenant 비교:
같은 docKind 다른 사무소 평균 9% 실패율
abc 가입 30일 전, 사건 12건

자기-RAG (top-3 prior, opsKnowledge findNearest):
[1] ADR 0015 PR-7: 스캔 배치 ≤5건 메모리 권고
[2] 2026-05-03 sweep: memoryMb<512 P0
[3] past-hypothesis 2026-05-09 (정확 5/7): "이미지 OCR 메모리 부족"

→ JSON Zod schema 로 가장 가능성 높은 3 가설 + 추천 액션 ranking

LLM 호출 = server action 경유 (apps/web/lib/ai/generate-anomaly-hypothesis.ts 또는 packages/business-logic/src/ops-anomaly/generate.ts). 클라이언트 직접 호출 금지 (PII 무전파 + assist counter weight 1 차감).

Layer 3 — 가설 검증 closed-loop

운영자가 추천 액션 실행 → 1시간 후 시그널 재측정 → outcome 자동 기록:

tenants/{tid}/anomalyHypotheses/{id} schema:
{
fingerprint: string, // hash(사무소 + 시그널 source + 임계 위반 axis)
generatedAt: Timestamp,
signals: { sourceMetric, value, threshold, status },
hypothesis: { description, confidence, evidenceFields, ragSources },
proposedActions: [{ label, autoExecutable, scenarioCardId?, estimatedImpactMs? }],
executedActionLabel?: string,
outcome?: "pending" | "resolved" | "persisted" | "false_positive",
resolvedAt?: Timestamp,
resolutionLagMs?: number,
operatorFeedback?: { match: boolean, comment?: string },
}

자주 맞는 가설 (정확도 ≥ 70% 누적 5회 이상) → opsKnowledge 의 past-hypothesis 로 자동 승격, 다음 LLM context 의 few-shot 예시.

Layer 4 — 메타-신뢰도 + 페일 모드 가드

위험가드
LLM 환각 (없는 ADR 인용)RAG citation verify (PR-1 의 verifyCitationExistence 재사용) — opsKnowledge docId 반드시 매칭
free-form 명령 추천proposedActions = 등록된 scenario card · toggle 화이트리스트만 (packages/business-logic/src/ops-anomaly/action-registry.ts)
잦은 false positivefingerprint 별 정확도 누적, < 60% 면 자동 dismiss + Anomaly badge "신뢰도 낮음"
비용 폭증anomaly detect 시점만 LLM 호출 + 24h fingerprint 캐싱 (resolved/false_positive 전까지)
confidence 낮은 가설< 0.6 면 표시 안 함 — "수동 점검" 카피만
cross-tenant mutationproposedActions 의 autoExecutable=true 는 reversible (메모리 토글·flag 토글 등) 만. payment·data delete 는 autoExecutable=false 강제

opsKnowledge 시드 전략

Phase 1 (본 ADR 의 PR-1 starter):

  • 4 source 모두 임베딩: ADR 33 개 + retrospectives 5 개 + memory MEMORY.md 의 200 lines + 신규 ragEvalCases 컨텍스트
  • scripts/embed-ops-knowledge.ts (신설) — 기존 ADR 0021 의 embedding 패턴 재사용
  • 한 번 batch 시드 후 새 ADR 추가 시 incremental embedding (ADR file 의 frontmatter.sidebar_position 변경 detect)

Phase 2 (PR-2 후속):

  • past-hypothesis 자동 승격 (Layer 3 outcome 정확도 누적 후)
  • 메모리 변경 추적 (MEMORY.md commit hash trigger embedding refresh)

dashboard slot 연결

ADR 0040 의 AnomalySlot 컴포넌트가 다음으로 진화:

// 변경 전 (PR-1, 정적 if-else)
if (rag.avgRecall < 0.05) {
return <StaticHypothesisCard ... />;
}

// 변경 후 (ADR 0042)
const { hypothesis, loading, error } = useLatestAnomalyHypothesis({ tenantId, fingerprint });
if (loading) return <Skeleton />;
if (!hypothesis || hypothesis.confidence < 0.6) return <ManualCheckCard />;
return (
<HypothesisCard
hypothesis={hypothesis}
onActionExecute={handleActionExecute}
onFeedback={handleFeedback}
/>
);

useLatestAnomalyHypothesis hook = tenants/{tid}/anomalyHypotheses 의 fingerprint 별 최신 1건 onSnapshot. 가설 생성은 server action generateAnomalyHypothesisAction (Layer 2 + 3 통합).

합성 corpus 위 검증 (메모리 close_the_learning_loop 적용)

본 결정의 ship 판정:

  • demo-firm-memory 위에서 LLM 가설 5건 생성 (각기 다른 fingerprint)
  • 각 가설의 confidence ≥ 0.6
  • citation verify (opsKnowledge docId 매칭) 100%
  • proposedActions 의 scenarioCardId 모두 존재 (action-registry 화이트리스트)

통과 안 하면 ship 표시 안 함 (ADR 0030 정합).

위험·완화

위험완화
Gemini 비용 폭증anomaly detect 시점만 호출, 24h fingerprint 캐시. 1 사무소 1일 평균 < 5 호출 예상
opsKnowledge 임베딩 stale새 ADR/메모리 추가 시 embed-ops-knowledge.ts 재실행 또는 Cloud Scheduler 매주
잘못된 추천 액션 자동 실행reversible 만 autoExecutable=true. 모든 액션 audit log + 1-click rollback
past-hypothesis 학습 편향정확도 매트릭으로 dismiss. 운영자가 false positive 라벨 가능
ADR 0040 의 정적 슬롯 vs LLM 슬롯 동시Phase 1 에서는 LLM 가설 없을 때 정적 fallback 유지 (PR-5 의 카피). 점진 마이그레이션

Non-goal

  • 자율 mutation (사용자 승인 없이 cross-tenant write) — 무조건 confirmation
  • 실시간 streaming LLM (Gemini SSE) — 단발 호출 only
  • ADR 0021 Platform RAG 의 publicDocuments 와 opsKnowledge 통합 — 의도적 분리 (다른 audience · 다른 임베딩 corpus)
  • multi-language hypothesis — 한국어 only
  • ADR 0040 의 정적 fallback 폐기 — Phase 2 에서 검토

구현 순서 (PR 단위)

PR산출물예상 시간
1본 ADR (doc-only)0:30
2opsKnowledge 컬렉션 schema + scripts/embed-ops-knowledge.ts (시드 batch)1:00
3Layer 1 시그널 collector (packages/business-logic/src/ops-anomaly/signals.ts) + Zod schema1:00
4Layer 2 LLM context builder + generateAnomalyHypothesisAction server action1:30
5Layer 3 검증 loop (anomalyHypotheses 컬렉션 writer + outcome 측정)1:00
6dashboard AnomalySlot LLM 가설 표시 (정적 fallback 유지)0:30
7Layer 4 메타-신뢰도 + action-registry 화이트리스트1:00
8합성 corpus 검증 + 메모리 갱신0:30

총 약 7 시간. 보수 추정 6 PR · 도전 추정 8 PR.

측정 가능한 성공 지표

  • LLM 가설 생성 5건 모두 confidence ≥ 0.6 (Phase 1 검증)
  • citation verify 100% (opsKnowledge docId 매칭 누락 0)
  • 추천 액션 모두 scenarioCardId 또는 toggle 화이트리스트 (free-form 0)
  • past-hypothesis 자동 승격 5회 누적 후 LLM context 에 인용 (Phase 2)
  • 운영자 클릭 깊이 — anomaly drill-down 1 클릭 + 액션 실행 1 클릭 (총 2 클릭)

후속 ADR

  • ADR 0043 (가칭) — anomaly 가설 시계열 분석 (시간대별 패턴 · 사무소 cohort)
  • ADR 0044 (가칭) — cross-pollinate 인프라 ("qrs 모범 패턴 → 다른 사무소" — exemplar/prompt 공유)

변경 이력

버전날짜변경
1.02026-05-08초안 — 4 layer + opsKnowledge self-RAG + 검증 closed-loop + 메타-신뢰도