본문으로 건너뛰기

ADR-013: LLM 세그먼트 요약 + 벡터 RAG

항목
상태Accepted
날짜2026-03-18
범위LlmSegmentSummarizer, EmbeddingProvider, VectorStore, EmbeddingPipeline, VectorRetriever, SemanticSearch, WeeklyDigest

배경

적응형 계층 메모리(ADR-012)는 데스크톱 활동을 세그먼트로 나누고 규칙 기반 통계를 만듭니다. 인텔리전스 사이클을 완성하려면 세그먼트가 LLM 으로 생성된 자연어 요약과 시맨틱 검색용 벡터 임베딩이 필요합니다. 이를 통해 LLM 분석 파이프라인이 제안 생성 시 관련 과거 컨텍스트를 참조할 수 있고, 사용자가 의미로 자신의 활동 이력을 검색할 수 있습니다.

결정

§1 2-Phase 세그먼트 처리

세그먼트 종료 시:

  • Phase 1 (즉시): 규칙 기반 통계 저장, ContentActivity 라벨이 임베드되어 벡터로 저장. 모니터 루프 차단 안 함.
  • Phase 2 (비동기): AnalysisProvider::summarize_text() 로 LLM 요약 생성, activity_segments 에 저장 후 추가 벡터로 임베드.

Graceful degradation: LLM 또는 임베딩 실패가 세그먼트 저장을 막지 않음. 세그먼트는 항상 최소한 규칙 기반 통계와 함께 영속됩니다.

§2 AnalysisProvider 확장

기존 AnalysisProvider port trait 에 summarize_text() 기본 메서드 추가. Vec<Suggestion> 대신 plain String 반환. 기본 구현은 analyze() 를 호출해 첫 결과의 content 를 추출. 어댑터는 더 효율적인 single-completion 호출로 override 가능.

§3 신규 maekon-embedding Crate

fastembed-rs (ONNX Runtime wrapper)는 무거운 의존성(~30 MB dylib). maekon-network 에서 격리하기 위해:

  • 신규 crate maekon-embeddingmaekon-core 에만 의존
  • fastembed-rs 를 사용하는 LocalEmbeddingProvider 포함
  • src-tauri 에서 feature gating: embedding = ["dep:maekon-embedding"]
  • fastembed::TextEmbedding::embed() 는 동기 — tokio::task::spawn_blocking 으로 wrap

워크스페이스가 11 → 12 crate 로 증가.

§4 EmbeddingProvider Port (async)

#[async_trait]
pub trait EmbeddingProvider: Send + Sync {
async fn embed(&self, text: &str) -> Result<Vec<f32>, CoreError>;
async fn embed_batch(&self, texts: &[String]) -> Result<Vec<Vec<f32>>, CoreError>;
fn dimensions(&self) -> usize;
fn model_id(&self) -> &str;
}

두 어댑터: LocalEmbeddingProvider (fastembed-rs, maekon-embedding 내) + RemoteEmbeddingProvider (OpenAI API, maekon-network 내).

§5 sqlite-vec fallback 이 있는 VectorStore Port

#[async_trait]
pub trait VectorStore: Send + Sync {
async fn store(&self, vector: Vec<f32>, metadata: EmbeddingMetadata) -> Result<(), CoreError>;
async fn search(&self, query: &[f32], limit: usize, time_decay_hours: f32) -> Result<Vec<SearchResult>, CoreError>;
async fn enforce_retention(&self, max_days: u32) -> Result<u64, CoreError>;
async fn mark_stale(&self, old_model_id: &str) -> Result<u64, CoreError>;
}

SQLite 구현:

  • 1차: vec0 virtual table 을 통한 KNN 검색용 sqlite-vec 익스텐션
  • Fallback: BLOB 벡터에 대한 Rust brute-force 코사인 유사도 (sqlite-vec 부재 시)
  • SqliteVectorStore.use_vec_extension: bool 가 init 시 감지

Rowid 동기화: embedding_vectors 메타데이터 테이블과 embedding_index virtual table 이 동일 transaction 내에서 rowid 로 연결.

§6 시간 감쇠 검색

결합 score: similarity × exp(-age_hours / decay_hours). 기본 감쇠: 168 시간 (1주 half-life). KNN 에서 후보 3 배수 over-fetch, 시간 감쇠로 re-rank 후 top-k 반환.

§7 임베딩 버저닝

각 벡터 row 가 model_id 저장. 모델 변경 시: stale 표시 → original_text 에서 백그라운드 재임베드 (사이클당 100 벡터, ~500ms). Stale 벡터는 재임베드될 때까지 검색 가능 상태 유지.

§8 Weekly Digest

세그먼트 데이터의 주간 롤업: regime/카테고리 분해, top 콘텐츠, deep work 시간, 컨텍스트 전환, 직전 주와 비교. summarize_text() 로 선택적 LLM 내러티브. 일요일 자정 또는 on-demand 생성.

§9 PII 필터링

모든 텍스트가 임베딩 이전에 PII 필터링 됩니다. maekon-vision 의 동일한 주입된 PiiFilter 클로저 사용. 임베딩은 필터링된 텍스트의 의미만 인코딩.

§10 개인정보: 행동 데이터로서의 임베딩 벡터

임베딩 벡터는 사용자 활동의 의미적 패턴을 인코딩합니다. PII 필터링 후에도 벡터는 다음을 드러낼 수 있습니다:

  • 사용자가 작업하는 프로젝트/파일
  • 작업 타이밍 패턴 (deep work 세션이 언제 발생하는지)
  • 컨텍스트 전환 행동

완화 조치:

  • 모든 텍스트가 임베딩 전에 PII 필터링됨 (§9). content_label 메타데이터 포함.
  • 벡터는 로컬에만 저장 (서버 동기화가 활성화되지 않은 한 전송되지 않음)
  • activity_pattern_learning 권한을 통한 consent gating (GDPR Tier 4)
  • 보존 정책 (기본 90일) 이 과거 노출을 제한
  • Activity segment 와 weekly digest 도 보존 강제 (90일 / 52주)
  • 사용자가 config 리셋 또는 consent 회수로 모든 벡터 삭제 가능

결과

  • 워크스페이스가 11 → 12 crate 로 증가 (maekon-embedding)
  • fastembed + ONNX Runtime 이 ~30 MB 외부 의존성 추가 (다운로드, 번들 안 함)
  • sqlite-vec 익스텐션이 ~1 MB 추가 (선택. brute-force fallback 있음)
  • AnalysisProvider trait 가 summarize_text() 메서드 획득 (backward-compat 기본 구현)
  • V10 마이그레이션이 embedding_vectors, embedding_index, weekly_digests 테이블 추가
  • ContextAssembler 가 RAG 로 enrich 된 LLM 컨텍스트를 위한 relevant_history 파라미터 획득
  • 두 신규 API 엔드포인트: 시맨틱 검색 + weekly digest

참고

  • ADR-011: 독립 분석 파이프라인
  • ADR-012: 적응형 계층 메모리
  • 설계 spec: 내부 LLM 요약/벡터 RAG 설계 노트
  • 연구: fastembed-rs, sqlite-vec, EWMA 시간 감쇠