Obsidian MCP + 하이브리드 검색: 2026년 레퍼런스
# MCP를 통해 Obsidian을 Claude와 다른 에이전트에 연결하세요. 서버 설정, 하이브리드 BM25 + 벡터 검색, 16,894개 파일 볼트 색인까지 작동하는 설정과 함께 다룹니다.
Obsidian은 단순한 노트 앱이 아닙니다. local-first 방식의 일반 텍스트, 그래프 구조 markdown corpus이며, 검색 인프라를 더하면 AI context 저장소가 됩니다. 파일 16,894개. chunk 49,746개. 23ms 쿼리. API 호출 0회. 83 MB SQLite 파일 1개. 이 가이드는 vault(Obsidian 보관소) 아키텍처부터 hybrid 검색, MCP 통합, 운영 워크플로까지 전체 시스템을 다룹니다.
핵심 요약
메모 작성이 아니라 컨텍스트 엔지니어링입니다. AI에서 Obsidian vault의 가치는 메모 자체가 아니라, 메모를 쿼리할 수 있게 만드는 검색 계층에 있습니다. 검색 기능이 없는 16,000개 파일의 vault는 쓰기 전용 데이터베이스일 뿐입니다. hybrid 검색과 MCP 통합이 있는 200개 파일의 vault는 AI 지식 베이스가 됩니다. 검색 인프라가 제품이고, 메모는 원재료입니다.
Hybrid retrieval은 순수 키워드 검색이나 순수 의미 검색보다 낫습니다. BM25는 정확한 식별자와 기능 이름을 잡아냅니다. 벡터 검색은 서로 다른 용어 사이의 동의어와 개념적 일치를 잡아냅니다. Reciprocal Rank Fusion (RRF)은 점수 보정 없이 둘을 병합합니다. 어느 한 방법만으로는 두 실패 모드를 모두 커버할 수 없습니다. MS MARCO passage ranking 연구도 같은 패턴을 확인합니다. hybrid retrieval은 단독 방법보다 일관되게 더 좋은 성능을 냅니다.3 hybrid retriever 심층 분석에서는 RRF 수학, 실제 숫자를 사용한 예시, 실패 모드 분석, 인터랙티브 fusion 계산기를 다룹니다.
MCP는 AI 도구가 vault에 직접 접근하게 해줍니다. Model Context Protocol (MCP) 서버는 retriever를 Claude Code, Codex CLI, Cursor, 기타 AI 도구가 직접 호출할 수 있는 도구로 노출합니다. 에이전트는 vault를 쿼리하고, 출처 표시가 포함된 순위 결과를 받은 뒤, 전체 파일을 로드하지 않고 컨텍스트를 사용합니다. MCP 서버는 검색 엔진을 감싸는 얇은 래퍼입니다.
Local-first는 API 비용이 0이고 개인정보 보호를 완전히 통제한다는 뜻입니다. 전체 스택은 단일 머신에서 실행됩니다. 저장소는 SQLite, embeddings는 Model2Vec, 키워드 검색은 FTS5, 벡터 KNN은 sqlite-vec를 사용합니다. 클라우드 서비스도, API 호출도, 네트워크 의존성도 없습니다. 개인 메모는 머신 밖으로 나가지 않습니다. 49,746개 chunk를 전체 re-embed하는 비용은 OpenAI API 가격 기준으로 약 $0.30이지만, 실제 비용은 지연 시간, 개인정보 노출, 오프라인에서도 작동해야 하는 시스템에 네트워크 의존성이 생긴다는 점입니다.4
증분 인덱싱은 시스템을 10초 이내로 최신 상태로 유지합니다. 파일 수정 시간 비교로 변경 사항을 감지합니다. 수정된 파일만 다시 chunking하고 re-embed합니다. Apple M-series 하드웨어에서 전체 reindex는 약 4분이 걸립니다. 일반적인 하루 편집량에 대한 증분 업데이트는 10초 이내에 실행됩니다. 시스템은 수동 개입 없이 최신 상태를 유지합니다.
이 아키텍처는 200개 메모부터 20,000개 이상의 메모까지 확장됩니다. 동일한 3계층 설계, 즉 intake, retrieval, integration은 어떤 vault 크기에서도 작동합니다. 작은 vault에서는 BM25 전용 검색으로 시작하세요. 키워드 충돌이 문제가 되면 벡터 검색을 추가하세요. 정확한 일치와 의미적 일치가 모두 필요해지면 RRF fusion을 추가하세요. 각 계층은 독립적으로 유용하며, 독립적으로 제거할 수도 있습니다.
이 가이드를 사용하는 방법
이 가이드는 전체 시스템을 다룹니다. 어디에서 시작할지는 현재 상황에 따라 다릅니다.
| 현재 상황 | 여기서 시작하세요 | 그런 다음 살펴보세요 |
|---|---|---|
| Obsidian + AI가 처음인 경우 | AI Infrastructure에 Obsidian을 사용하는 이유, Obsidian MCP 설정 | Vault Architecture, MCP Server Architecture |
| 기존 vault가 있고 AI 접근을 원할 경우 | MCP Server Architecture, Claude Code Integration | Embedding Models, Full-Text Search |
| 검색 시스템을 구축하는 경우 | The Complete Retrieval Pipeline, Reciprocal Rank Fusion | Performance Tuning, Troubleshooting |
| 팀 또는 엔터프라이즈 환경인 경우 | Decision Framework, Knowledge Graph Patterns | Developer Workflow Recipes, Migration Guide |
Contract로 표시된 섹션에는 구현 세부 사항, 설정 블록, 실패 모드가 포함되어 있습니다. Narrative로 표시된 섹션은 개념, 아키텍처 결정, 설계 선택의 이유에 초점을 둡니다. Recipe로 표시된 섹션은 단계별 워크플로를 제공합니다.
AI Infrastructure에 Obsidian을 사용하는 이유
이 가이드의 핵심 주장은 다음과 같습니다. Obsidian vault는 local-first, plaintext, 그래프 구조를 갖추고 있으며 사용자가 스택의 모든 계층을 제어할 수 있기 때문에 개인 AI 지식 베이스를 위한 최고의 기반입니다.
Obsidian이 대안과 달리 AI에 제공하는 것
Plaintext markdown 파일. 모든 메모는 파일 시스템의 .md 파일입니다. 독점 형식도, 데이터베이스 export도, 콘텐츠를 읽기 위한 API도 필요 없습니다. 파일을 읽을 수 있는 모든 도구가 vault를 읽을 수 있습니다. grep, ripgrep, Python의 pathlib, SQLite FTS5는 모두 원본 파일에서 직접 작동합니다. 검색 시스템을 만들 때는 API 응답이 아니라 파일을 인덱싱합니다. 원본이 파일 시스템이므로 인덱스는 항상 원본과 일치합니다.
Local-first architecture. vault는 사용자의 머신에 있습니다. 서버도, 클라우드 동기화 의존성도, API rate limit도, 자신의 콘텐츠 처리 방식을 제한하는 서비스 약관도 없습니다. 외부 서비스 없이 메모를 embed, index, chunk, search할 수 있습니다. AI infrastructure에서는 이 점이 중요합니다. 검색 파이프라인은 API endpoint 응답 속도가 아니라 디스크 속도만큼 빠르게 실행되기 때문입니다. 개인정보 보호 측면에서도 중요합니다. 자격 증명, 건강 데이터, 금융 정보, 개인적인 생각이 포함된 개인 메모가 머신 밖으로 나가지 않습니다.
wiki-link를 통한 그래프 구조. Obsidian의 [[wiki-link]] 문법은 메모 사이에 방향 그래프를 만듭니다. OAuth 구현에 관한 메모는 토큰 회전, 세션 관리, API 보안에 관한 메모로 연결됩니다. 그래프 구조는 개념 사이의 사람이 직접 정리한 관계를 인코딩합니다. 벡터 embeddings는 의미적 유사성을 포착하지만, wiki-link는 작성자가 주제를 생각하면서 만든 의도적인 연결을 포착합니다. 그래프는 embeddings가 복제할 수 없는 신호입니다.
Plugin ecosystem. Obsidian에는 2,500개 이상의 커뮤니티 플러그인이 있습니다. 2026년 3월 기준이며, 2025년 중반의 1,800개 이상에서 늘었습니다. Dataview는 vault를 데이터베이스처럼 쿼리합니다. Templater는 JavaScript 로직이 포함된 템플릿에서 메모를 생성합니다. Git 통합은 vault를 repository에 동기화합니다. Linter는 formatting 일관성을 강제합니다. Bases core plugin(v1.9.10에서 도입)은 frontmatter 속성을 필드로 사용해 vault 파일 위에 테이블, 갤러리, 캘린더, kanban board 같은 데이터베이스형 view를 추가하며, .base 파일로 저장됩니다.15 이러한 플러그인은 기본 plaintext 형식을 바꾸지 않으면서 vault에 구조를 더합니다. 검색 시스템은 플러그인 자체가 아니라 플러그인의 출력물을 인덱싱합니다.
500만 명 이상의 사용자. Obsidian에는 템플릿, 워크플로, 플러그인, 문서를 만드는 큰 활성 커뮤니티가 있습니다. vault 구성이나 플러그인 설정에서 문제가 생기면 누군가 이미 해결책을 문서화했을 가능성이 높습니다. 커뮤니티는 Obsidian 인접 도구도 만듭니다. MCP 서버, 인덱싱 스크립트, publishing pipeline, API wrapper가 여기에 포함됩니다.
파일 시스템만으로는 제공하지 못하는 것
markdown 파일 디렉터리는 plaintext라는 장점이 있지만, Obsidian이 더해주는 다음 3가지는 없습니다.
-
양방향 링크. Obsidian은 backlink를 자동으로 추적합니다. Note A에서 Note B로 링크하면, Note B는 Note A가 자신을 참조한다는 사실을 보여줍니다. 그래프 패널은 연결 클러스터를 시각화합니다. 이런 양방향 인식은 원시 파일 시스템이 제공하지 않는 메타데이터입니다.
-
플러그인 렌더링이 포함된 live preview. Dataview 쿼리, Mermaid 다이어그램, callout block이 실시간으로 렌더링됩니다. 저장 형식은 plaintext로 유지하면서도 작성 경험은 텍스트 편집기보다 풍부합니다. 사용자는 풍부한 환경에서 작성하고 정리하며, 검색 시스템은 원시 markdown을 인덱싱합니다.
-
커뮤니티 인프라. 플러그인 탐색, 테마 marketplace, sync service(선택 사항), publish service(선택 사항), 문서 생태계가 있습니다. 개별 기능은 독립 도구로도 재현할 수 있지만, Obsidian은 이를 일관된 워크플로로 묶어줍니다.
Obsidian이 하지 않는 것과 사용자가 구축하는 것
Obsidian에는 검색 인프라가 포함되어 있지 않습니다. 기본 검색(full-text, filename, tag)은 있지만 embedding pipeline, vector search, fusion ranking, MCP 서버, credential filtering, chunking 전략, 외부 AI 도구용 integration hook은 없습니다. 이 가이드는 Obsidian 위에 구축하는 인프라를 다룹니다. vault는 기반입니다. 검색 파이프라인, MCP 서버, integration hook이 인프라입니다.
여기서 설명하는 아키텍처는 Obsidian 전용이 아니라 markdown-first입니다. Logseq, Foam, Dendron 또는 일반 markdown 파일 디렉터리를 사용하더라도 검색 파이프라인은 동일하게 작동합니다. chunker는 .md 파일을 읽습니다. embedder는 텍스트 문자열을 처리합니다. indexer는 SQLite에 씁니다. 어떤 구성 요소도 Obsidian 전용 기능에 의존하지 않습니다. Obsidian의 기여는 retriever가 인덱싱할 markdown 파일을 만들어내는 작성 및 구성 환경입니다.
Obsidian MCP 설정
Model Context Protocol(MCP)은 Claude Code, Codex CLI, Cursor 및 다른 AI 도구가 Obsidian vault에 직접 접근할 수 있게 해주는 표준 인터페이스입니다. 이 섹션에서는 5분 안에 vault를 AI 도구에 연결합니다. Obsidian을 설치하고, vault를 만들고, MCP 서버를 설치한 뒤 첫 쿼리를 실행합니다. 빠른 시작에서는 즉시 결과를 확인할 수 있도록 커뮤니티 MCP 서버를 사용합니다. 이후 섹션에서는 프로덕션용 커스텀 검색 파이프라인을 구축하는 방법을 다룹니다.
사전 요구 사항
- macOS, Linux 또는 Windows
- Node.js 18 이상(MCP 서버용)
- Obsidian 1.12 이상(CLI 통합용. 1.13.1이 현재 공개 데스크톱 릴리스이며, MCP 전용 설정에서는 이전 버전도 작동합니다)
- Claude Code, Codex CLI 또는 Cursor 설치 완료
1단계: vault 만들기
obsidian.md에서 Obsidian을 다운로드하고 새 vault를 만드세요. 나중에 기억할 수 있는 위치를 선택하세요. MCP 서버에는 절대 경로가 필요합니다.
# Example vault location
~/Documents/knowledge-base/
검색기가 처리할 수 있도록 노트 몇 개를 추가하세요. 10-20개 정도의 노트만 있어도 결과를 확인하기에 충분합니다. 각 노트는 의미 있는 제목과 최소 1개 문단의 내용을 가진 .md 파일이어야 합니다.
2단계: MCP 서버 설치하기
여러 커뮤니티 MCP 서버가 즉시 vault 접근 기능을 제공합니다. 이 생태계는 2025-2026년에 크게 성장했습니다. 주목할 만한 예로 MCPVault(npm @bitbonsai/mcpvault, repo bitbonsai/mcpvault)가 있으며, 현재 v0.12.1입니다. 아래의 MarkusPfundstein/mcp-obsidian과는 별도 프로젝트이며, 이름이 바뀐 것이 아닙니다. v0.11.0(2026년 3월)에서는 frontmatter와 해시태그를 개수와 함께 스캔하는 list_all_tags, 개선된 점이 포함된 폴더 처리, .base/.canvas 지원이 추가되었습니다. 경로 필터의 제한 디렉터리 deny-list에 대해 중간 심각도 권고 2건(GHSA-9c83-rr99-vfwj 및 GHSA-j99q-93c9-h869)이 공개되었으므로 최신 릴리스를 실행하세요.13
2026년 4월 변화 — 선호되는 브리지로서의 Obsidian CLI: Obsidian 1.12.0은 1급 CLI를 도입했고, 공개 1.12.7 설치 프로그램(2026년 3월 23일)은 터미널 워크플로를 더 쉽게 설치하고 실행할 수 있게 만든 독립 실행 바이너리, TUI, socket-file 개선 사항을 포함했습니다.16 현재 공개 데스크톱 릴리스인 1.13.1(공개 채널, 2026년 6월 9일)은 1.13.0 대비 버전 최신화 성격의 업데이트입니다. 설정 UX 개선과 CodeMirror 업그레이드가 포함되었지만, 1.12.x CLI 표면을 넘어서는 새로운 AI/자동화 기능은 없습니다.2526 커뮤니티 도구는
mcp-obsidian을 구동하던 Local REST API 플러그인에서 CLI 기반 통합으로 활발히 이동하고 있습니다. 이 방식이 더 빠르고 안정적이기 때문입니다.MarkusPfundstein/mcp-obsidianrepo는 여전히 유지 관리되고 있으며, 2026년 5월까지의 커밋에서search_by_tag와get_frontmatter를 포함한 도구가 추가되었습니다. 다만 태그된 릴리스는 제공하지 않으므로 고정된 커밋에서 설치하세요. 이 repo는 여전히 Local-REST-API 기반입니다. 새 설정에서는 CLI 브리지가 일반적으로 더 빠르고 안정적이므로, 이 브리지나 아래에 나열된 더 새로운 커뮤니티 대안을 선호하세요.20 권장 설정은 이 가이드의 뒤쪽에 있는 “AI 워크플로용 Obsidian CLI” 섹션을 참고하세요.
| 서버 | 작성자 | Transport | 플러그인 필요 여부 | 핵심 기능 |
|---|---|---|---|---|
| obsidian-mcp-server | StevenStavrakis | STDIO | 아니요 | 가볍고 파일 기반 |
| mcp-obsidian | MarkusPfundstein | STDIO | Local REST API | REST를 통한 전체 vault CRUD와 search_by_tag/get_frontmatter — 활발히 유지 관리됨(2026년 5월까지 커밋 있음). 태그된 릴리스는 없으므로 커밋을 고정하세요20 |
| obsidian-mcp-tools | jacksteamdev | STDIO | 예(플러그인) | 시맨틱 검색 + Templater |
| obsidian-claude-code-mcp | iansinnott | WebSocket | 예(플러그인) | Claude Code용 자동 검색 |
| obsidian-mcp-server | cyanheads | STDIO | Local REST API | 태그, frontmatter 관리 |
| Hybrid Search MCP | community | STDIO | 아니요 | BM25 + 시맨틱 검색 MCP 서버 + CLI. 2026년 4월 기준 새롭고 활발히 유지 관리됨. |
빠른 시작에서는 .md 파일을 직접 읽는 파일 기반 서버가 가장 간단한 선택입니다.
npm install -g obsidian-mcp-server
3단계: AI 도구 설정하기
Claude Code — ~/.claude/settings.json에 추가하세요.
{
"mcpServers": {
"obsidian": {
"command": "obsidian-mcp-server",
"args": ["--vault", "/absolute/path/to/your/vault"]
}
}
}
Codex CLI — .codex/config.toml에 추가하세요.
[mcp_servers.obsidian]
command = "obsidian-mcp-server"
args = ["--vault", "/absolute/path/to/your/vault"]
Cursor — .cursor/mcp.json에 추가하세요.
{
"mcpServers": {
"obsidian": {
"command": "obsidian-mcp-server",
"args": ["--vault", "/absolute/path/to/your/vault"]
}
}
}
4단계: 첫 쿼리 실행하기
AI 도구를 열고 vault 노트로 답할 수 있는 질문을 하세요.
Search my Obsidian vault for notes about [topic you wrote about]
AI 도구는 MCP 서버를 호출하고, 서버는 vault를 검색한 뒤 일치하는 콘텐츠를 반환합니다. 파일 경로와 관련 발췌문이 포함된 결과가 표시되어야 합니다.
연결 후 Claude로 할 수 있는 일
정확한 도구 이름은 서버마다 다르지만, 핵심 기능 범위는 구현 전반에서 일관됩니다.
| 기능 | 일반적인 도구 | 에이전트가 수행하는 작업 |
|---|---|---|
| vault 검색 | obsidian_search / search |
쿼리와 일치하는 노트를 찾아 파일 경로와 출처 표기가 포함된 순위화된 발췌문을 반환합니다 |
| 전체 노트 읽기 | obsidian_read_note / read_note |
검색 발췌문만으로 충분하지 않을 때 전체 노트 내용을 가져옵니다 |
| 목록 확인 및 탐색 | obsidian_list_notes / list_notes |
구체적인 쿼리가 없을 때 폴더, 태그 또는 날짜 범위별로 노트를 탐색합니다 |
| 형식화된 컨텍스트 가져오기 | obsidian_get_context |
대화에 바로 주입할 수 있도록 토큰 예산에 맞춘 주제 중심 컨텍스트 블록을 반환합니다 |
실제로 Claude는 출처 표기와 함께 노트 기반 질문에 답하고, 이전 결정 사항과 참고 자료를 코딩 세션으로 가져오며, 전체 파일을 컨텍스트에 로드하지 않고도 vault 구조를 탐색합니다. 일부 커뮤니티 서버는 쓰기 작업(생성, 추가, 태그 및 frontmatter 관리)도 노출합니다. 이 가이드의 뒤쪽에서 구축하는 커스텀 서버는 의도적으로 읽기 전용이며, 노트 생성은 hooks가 처리합니다.
심층 내용: 도구와 권한 설계는 MCP 서버 아키텍처, hooks와 브리지 패턴은 Claude Code 통합, 다른 에이전트는 Codex CLI 통합 및 Cursor와 기타 도구를 참고하세요.
방금 만든 것
표준 프로토콜을 통해 로컬 지식 베이스를 AI 도구에 연결했습니다. MCP 서버는 vault 파일을 읽고, 기본 검색을 수행하고, 결과를 반환합니다. 이것이 최소 실행 가능한 버전입니다.
이 빠른 시작에서 제공하지 않는 것: - Hybrid 검색(BM25 + 벡터 검색 + RRF fusion) - Embedding 기반 시맨틱 검색 - Credential filtering - 증분 인덱싱 - Hook 기반 자동 컨텍스트 주입
이 가이드의 나머지 부분에서는 이러한 기능을 하나씩 구축하는 방법을 다룹니다. 빠른 시작은 개념을 검증합니다. 전체 파이프라인은 프로덕션 품질의 검색을 제공합니다.
AI Workflows를 위한 Obsidian CLI
Obsidian 1.12(2026년 2월)는 AI workflows를 위한 새로운 통합 표면을 여는 내장 command line interface를 도입했습니다. 이 기능은 1.13.1 공개 데스크톱 릴리스(공개 채널, 2026년 6월 9일)에서도 최신 상태로 유지되며, 해당 릴리스는 설정 UX와 CodeMirror 버전 업데이트만 포함하고 새로운 CLI 기능은 추가하지 않았습니다.162526 CLI는 Obsidian GUI의 원격 제어 장치처럼 작동합니다. Obsidian이 실행 중이어야 하며, 실행 중이 아니면 첫 명령에서 자동으로 시작됩니다. Settings > General > Command line interface에서 활성화하세요.
AI 인프라에서 CLI가 중요한 이유
CLI는 이전에는 GUI나 plugin APIs가 필요했던 Obsidian 네이티브 작업에 프로그래밍 방식으로 접근할 수 있게 해줍니다. AI workflows에서 핵심 기능은 다음과 같습니다.
- 스크립트와 hooks에서 검색.
obsidian search "query"와obsidian search:context "query"는 모든 shell script, hook, automation pipeline에서 vault 검색을 실행합니다.search:context변형은 일치하는 줄과 주변 context를 반환하므로, 결과를 AI prompts에 넣을 때 유용합니다. - Daily notes 자동화.
obsidian daily는 오늘의 daily note를 열거나 생성합니다. shell scripting과 결합하면 자동화된 daily briefing workflows를 만들 수 있습니다. 예를 들어 hook이 AI가 생성한 요약을 daily note에 추가할 수 있습니다. - Template 기반 note 생성.
obsidian template list와obsidian template create는 Templater 또는 core templates에서 notes를 생성하므로, AI agents가 markdown files를 직접 쓰지 않고도 구조화된 vault entries를 만들 수 있습니다. - Property 관리.
obsidian property set과obsidian property get은 frontmatter properties를 읽고 쓰므로, YAML을 파싱하지 않고도 scripts에서 metadata를 업데이트할 수 있습니다. - Plugin 제어.
obsidian plugin enable/disable/list는 plugins를 프로그래밍 방식으로 관리하므로, batch operations 중 indexing plugins를 켜고 끌 때 유용합니다. - Task 관리.
obsidian task list/add/complete는 구조화된 task 접근을 제공하므로, vault의 work items를 관리하는 AI agents에 유용합니다.
AI access를 위한 CLI vs MCP
CLI와 MCP servers는 서로 다른 역할을 하며, 경쟁 관계가 아니라 상호 보완적입니다.
| Aspect | Obsidian CLI | MCP Server |
|---|---|---|
| Caller | Shell scripts, hooks, cron jobs | AI agents(Claude Code, Codex, Cursor) |
| Protocol | POSIX process(stdin/stdout/stderr) | MCP(STDIO 또는 HTTP를 통한 JSON-RPC) |
| Strength | Obsidian 네이티브 작업(templates, plugins, properties) | Custom retrieval(embeddings, BM25, RRF fusion) |
| Limitation | vector search 없음, embedding pipeline 없음 | Obsidian 내부 작업에 접근할 수 없음 |
| Best for | Automation scripts, intake pipelines, hook actions | 세션 중 실시간 AI agent queries |
추천: intake automation(notes 생성, properties 관리, Obsidian 네이티브 search 실행)에는 CLI를 사용하고, retrieval(embeddings를 활용한 hybrid search)에는 MCP를 사용하세요. PreToolUse hook은 ranked results를 위해 전체 MCP retriever로 넘어가기 전에 빠른 사전 확인용으로 obsidian search:context를 호출할 수 있습니다.
예시: CLI 기반 intake hook
#!/bin/bash
# Hook: append today's signals to daily note via CLI
DATE=$(date +%Y-%m-%d)
SUMMARY="$1"
obsidian daily # ensure daily note exists
obsidian file append "Daily Notes/${DATE}.md" "## AI Summary\n${SUMMARY}"
Obsidian Agent Plugins
Obsidian plugins의 성장 중인 한 범주는 AI coding agents를 vault UI 안에 직접 내장하여, 외부 MCP server configuration의 대안을 제공합니다. 이러한 plugins는 외부 tool에서 연결하는 대신 Obsidian의 sidebar 안에서 AI agent를 실행합니다.
Claudian
Claudian은 Claude Code를 vault의 AI collaborator로 내장합니다. vault directory는 Claude의 working directory가 되어, file read/write, search, bash commands, multi-step workflows 같은 전체 agentic capabilities를 제공합니다.17
AI 인프라를 위한 주요 기능:
- Context-aware prompts. focused note를 자동으로 첨부하고, @notename file mentions, tag 기반 제외, editor selection을 context로 지원합니다.
- Vision support. drag-and-drop, paste, file path를 통해 images를 분석합니다. vault에 캡처한 screenshots와 diagrams를 처리할 때 유용합니다.
- Slash commands. /command로 실행되는 재사용 가능한 prompt templates를 만들어 표준화된 vault operations를 활성화합니다.
- Permission modes. YOLO(auto-approve), Safe(각 action 승인), Plan(plan-only) modes를 제공하며 safety blocklist와 vault confinement를 포함합니다.
Agent Client
Agent Client는 Agent Client Protocol(ACP)을 통해 Claude Code, Codex CLI, Gemini CLI를 통합 Obsidian sidebar로 가져옵니다.18
주요 기능:
- Multi-agent switching. 같은 panel에서 Claude Code, Codex, Gemini CLI와 chat하고, 필요에 따라 agents를 전환합니다.
- Note mentions. @notename을 사용해 note contents를 prompts에 포함합니다. Claudian과 비슷하지만 agent에 종속되지 않습니다.
- Shell execution. chat 안에서 terminal commands를 inline으로 실행합니다. build scripts, git commands 또는 어떤 terminal operation이든 대화를 벗어나지 않고 실행할 수 있습니다.
- Action approval. file reads, edits, command executions에 대해 세밀하게 제어합니다.
Agent plugins와 external MCP를 언제 사용할지
| Scenario | Agent plugin | External MCP |
|---|---|---|
| AI assistance로 vault notes 작성 및 편집 | 더 적합함 - agent가 editor context를 봅니다 | 작동하지만 editor awareness가 없습니다 |
| 여러 repos에 걸친 code development | 제한적 - vault 범위에 한정됨 | 더 적합함 - 전체 filesystem을 갖춘 project 범위 |
| 대규모 indexed corpus에서 retrieval | 기본 search만 가능 | 전체 hybrid retrieval pipeline |
| note-taking sessions 중 빠른 vault Q&A | 이상적 - context switching 없음 | terminal로 전환해야 함 |
추천: vault 중심 workflows(notes 작성, 정리, 요약)에는 agent plugins를 사용하세요. AI agent가 전체 retrieval pipeline과 vault 밖 codebases 접근이 필요한 development workflows에는 external MCP servers를 사용하세요. 두 접근 방식은 함께 사용할 수 있습니다. note work에는 Obsidian 안에서 Claudian을 실행하고, development에는 외부에서 MCP와 함께 Claude Code를 사용하세요.
의사결정 프레임워크: Obsidian vs 대안
모든 사용 사례에 Obsidian이 필요한 것은 아니에요. 이 섹션에서는 언제 Obsidian이 적합한 기반인지, 언제 과한 선택인지, 언제 다른 도구가 더 잘 맞는지 정리해요.
의사결정 트리
START: What is your primary content type?
│
├─ Structured data (tables, records, schemas)
│ → Use a database. SQLite, PostgreSQL, or a spreadsheet.
│ → Obsidian is for prose, not tabular data.
│
├─ Ephemeral context (current project, temporary notes)
│ → Use CLAUDE.md / AGENTS.md in the project repo.
│ → These travel with the code and reset per project.
│
├─ Team wiki (shared documentation, onboarding)
│ → Evaluate Notion, Confluence, or a shared git repo.
│ → Obsidian vaults are personal-first. Team sync is possible
│ but not native.
│
└─ Growing personal knowledge corpus
│
├─ < 50 notes
│ → A folder of markdown files + grep is sufficient.
│ → Obsidian adds value mainly through the link graph,
│ which needs density to be useful.
│
├─ 50 - 500 notes
│ → Obsidian adds value. Wiki-links create a navigable graph.
│ → BM25-only search (FTS5) is sufficient at this scale.
│ → Skip vector search and RRF until keyword collisions appear.
│
├─ 500 - 5,000 notes
│ → Full hybrid retrieval becomes valuable. Keyword collisions
│ increase. Semantic search catches queries that BM25 misses.
│ → Add vector search + RRF fusion at this scale.
│
└─ 5,000+ notes
→ Full pipeline is essential. BM25-only returns too much noise.
→ Credential filtering becomes critical (more notes = more
accidentally pasted secrets).
→ Incremental indexing matters (full reindex takes minutes).
→ MCP integration pays dividends on every AI interaction.
비교 매트릭스
| 기준 | Obsidian | Notion | Apple Notes | 일반 파일시스템 | CLAUDE.md |
|---|---|---|---|---|---|
| Local-first | 예 | 아니요(클라우드) | 부분적(iCloud) | 예 | 예 |
| Plaintext | 예(markdown) | 아니요(블록) | 아니요(독점 형식) | 예 | 예 |
| Graph 구조 | 예(wiki-link) | 부분적(멘션) | 아니요 | 아니요 | 아니요 |
| AI 인덱싱 가능 | 직접 파일 접근 | API 필요 | 내보내기 필요 | 직접 파일 접근 | 이미 컨텍스트에 있음 |
| Plugin 생태계 | 2,500개 이상 plugins | 통합 기능 | 없음 | N/A | N/A |
| 오프라인 사용 가능 | 전체 가능 | 캐시된 읽기 전용 | 부분적 | 전체 가능 | 전체 가능 |
| 10K+ 노트까지 확장 | 예 | 예(API 사용 시) | 성능 저하 | 예 | 아니요(단일 파일) |
| 비용 | 무료(핵심 기능) | 월 $10 이상 | 무료 | 무료 | 무료 |
Obsidian이 과한 경우
- 단일 프로젝트 컨텍스트. AI가 현재 코드베이스에 대한 컨텍스트만 필요하다면
CLAUDE.md,AGENTS.md, 또는 프로젝트 수준 문서에 넣으세요. 이 파일들은 repo와 함께 이동하며 자동으로 로드돼요. - 구조화된 데이터. 콘텐츠가 표, 레코드, 스키마라면 데이터베이스를 사용하세요. Obsidian 노트는 prose 중심이에요. Dataview로 frontmatter 필드를 쿼리할 수는 있지만, 구조화된 쿼리는 실제 데이터베이스가 더 잘 처리해요.
- 임시 리서치. 프로젝트가 끝난 뒤 노트를 버릴 예정이라면 markdown 파일을 둔 임시 디렉터리가 더 단순해요. 일시적인 콘텐츠를 위해 retrieval 인프라를 만들지 마세요.
Obsidian이 적합한 경우
- 몇 달 또는 몇 년에 걸쳐 쌓이는 지식. 코퍼스가 커질수록 가치가 복리처럼 늘어나요. 6개월 동안 매일 쿼리하는 200개 노트 vault가 한 번만 쿼리하는 5,000개 노트 vault보다 더 큰 가치를 제공해요.
- 하나의 코퍼스에 여러 도메인이 있는 경우. 프로그래밍, 아키텍처, 보안, 디자인, 개인 프로젝트에 대한 노트가 들어 있는 vault는 프로젝트별
CLAUDE.md로는 제공할 수 없는 도메인 간 retrieval의 이점을 얻어요. - 프라이버시에 민감한 콘텐츠. Local-first는 retrieval 파이프라인이 콘텐츠를 외부 서비스로 보내지 않는다는 뜻이에요. vault에는 클라우드 서비스에 업로드하고 싶지 않은 콘텐츠를 포함해, 사용자가 넣는 모든 내용이 들어가요.
Mental Model: 3개 계층
이 시스템에는 독립적으로 작동하지만 결합되면 효과가 커지는 3개의 계층이 있어요. 각 계층은 다루는 관심사와 실패 모드가 달라요.
┌─────────────────────────────────────────────────────┐
│ INTEGRATION LAYER │
│ MCP servers, hooks, skills, context injection │
│ Concern: delivering context to AI tools │
│ Failure: wrong context, too much context, stale │
└──────────────────────┬──────────────────────────────┘
│ query + ranked results
┌──────────────────────┴──────────────────────────────┐
│ RETRIEVAL LAYER │
│ BM25, vector KNN, RRF fusion, token budget │
│ Concern: finding the right content for any query │
│ Failure: wrong ranking, missed results, slow queries │
└──────────────────────┬──────────────────────────────┘
│ chunked, embedded, indexed
┌──────────────────────┴──────────────────────────────┐
│ INTAKE LAYER │
│ Note creation, signal triage, vault organization │
│ Concern: what enters the vault and how it's stored │
│ Failure: noise, duplicates, missing structure │
└─────────────────────────────────────────────────────┘
Intake는 무엇이 vault에 들어오는지 결정해요. 큐레이션이 없으면 vault에는 노이즈가 쌓여요. 예를 들어 트윗 스크린샷, 주석 없이 복사해 붙여 넣은 글, 맥락 없는 미완성 생각 같은 것들이에요. Intake 계층은 항목이 들어오는 시점의 품질 관리를 담당해요. 점수화 파이프라인, 태깅 규칙, 수동 리뷰 프로세스처럼 vault에 retrieval할 가치가 있는 콘텐츠가 들어가도록 보장하는 모든 메커니즘이 여기에 해당해요.
Retrieval은 vault를 쿼리 가능하게 만들어요. 이 계층은 엔진이에요. 노트를 검색 단위로 chunking하고, chunk를 벡터 공간에 embeddings로 만들고, 키워드 검색과 의미 검색을 위해 인덱싱하고, RRF로 결과를 결합해요. Retrieval 계층은 파일 디렉터리를 쿼리 가능한 지식 베이스로 바꿔요. 이 계층이 없으면 vault는 수동 탐색과 기본 검색으로는 둘러볼 수 있지만, AI 도구가 프로그래밍 방식으로 접근할 수는 없어요.
Integration은 retrieval 계층을 AI 도구에 연결해요. MCP 서버는 retrieval을 호출 가능한 도구로 노출해요. Hooks는 컨텍스트를 자동으로 주입해요. Skills는 새로운 지식을 다시 vault에 캡처해요. Integration 계층은 지식 베이스와 이를 소비하는 AI agents 사이의 인터페이스예요.
계층들은 의도적으로 분리되어 있어요. Intake 점수화 파이프라인은 embeddings에 대해 아무것도 몰라요. Retriever는 신호 라우팅 규칙에 대해 아무것도 몰라요. MCP 서버는 노트가 어떻게 생성되었는지 아무것도 몰라요. 이렇게 분리되어 있기 때문에 어떤 계층이든 독립적으로 개선할 수 있어요. Intake 파이프라인을 바꾸지 않고 embedding 모델을 교체할 수 있어요. Retriever를 수정하지 않고 새로운 MCP 기능을 추가할 수 있어요. 인덱스를 건드리지 않고 신호 점수화 휴리스틱을 바꿀 수 있어요.
AI 활용에 맞춘 볼트 아키텍처
AI 검색에 최적화된 볼트는 개인 브라우징에 최적화된 볼트와 다른 규칙을 따릅니다. 이 섹션에서는 폴더 구조, 노트 스키마, frontmatter 규칙, 검색 품질을 높이는 구체적인 패턴을 다룹니다.
폴더 구조
최상위 폴더에는 번호 접두사를 사용해 예측 가능한 구성 계층을 만드세요. 번호는 우선순위를 뜻하지 않습니다. 관련 영역을 묶고 구조를 훑어보기 쉽게 만드는 역할을 합니다.
vault/
├── 00-inbox/ # Unsorted captures, pending triage
├── 01-projects/ # Active project notes
├── 02-areas/ # Ongoing areas of responsibility
├── 03-resources/ # Reference material by topic
│ ├── programming/
│ ├── security/
│ ├── ai-engineering/
│ ├── design/
│ └── devops/
├── 04-archive/ # Completed projects, old references
├── 05-signals/ # Scored signal intake
│ ├── ai-tooling/
│ ├── security/
│ ├── systems/
│ └── ...12 domain folders
├── 06-daily/ # Daily notes (if used)
├── 07-templates/ # Note templates (excluded from index)
├── 08-attachments/ # Images, PDFs (excluded from index)
├── .obsidian/ # Obsidian config (excluded from index)
└── .indexignore # Paths to exclude from retrieval index
인덱싱해야 하는 폴더: 프로젝트, 영역, 리소스, 신호, 일일 노트처럼 markdown 산문이 들어 있는 모든 폴더입니다.
인덱싱에서 제외해야 하는 폴더: 템플릿(콘텐츠가 아니라 placeholder 변수가 들어 있음), 첨부 파일(binary 파일), Obsidian 설정, 그리고 검색 인덱스에 넣고 싶지 않은 민감한 콘텐츠가 들어 있는 모든 폴더입니다.
.indexignore 파일
볼트 루트에 .indexignore 파일을 만들어 검색 인덱스에서 제외할 경로를 명시하세요. 문법은 .gitignore와 같습니다.
# Obsidian internal
.obsidian/
# Templates contain placeholders, not content
07-templates/
# Binary attachments
08-attachments/
# Personal health/medical notes
02-areas/health/
# Financial records
02-areas/finance/personal/
# Career documents (resumes, salary data)
02-areas/career/private/
인덱서는 스캔하기 전에 이 파일을 읽고, 일치하는 경로는 완전히 건너뜁니다. 제외된 경로의 파일은 chunking되지 않고, embeddings도 생성되지 않으며, 검색 결과에도 나타나지 않습니다.
노트 스키마
모든 노트에는 YAML frontmatter가 있어야 합니다. retriever는 필터링과 컨텍스트 보강에 frontmatter 필드를 사용합니다.
---
title: "OAuth Token Rotation Patterns"
type: note # note | signal | project | moc | daily
domain: security # primary domain for routing
tags:
- authentication
- oauth
- token-management
created: 2026-01-15
updated: 2026-02-28
source: "" # URL if captured from external source
status: active # active | archived | draft
---
검색에 필요한 필드:
title— 검색 결과 표시와 BM25용 제목 컨텍스트에 사용됩니다type— 유형별 필터링 쿼리를 가능하게 합니다(“MOC만 보여줘” 또는 “signals만”)tags— FTS5 제목 컨텍스트에 0.3 가중치로 인덱싱되어, 본문에서 다른 용어를 쓰더라도 키워드 매칭을 제공합니다
선택 사항이지만 유용한 필드:
domain— 도메인 범위 쿼리를 가능하게 합니다(“security 노트만 검색”)source— 캡처한 콘텐츠의 출처입니다. retriever가 결과에 소스 URL을 포함할 수 있습니다status— 보관되었거나 초안 상태인 노트를 활성 검색에서 제외할 수 있습니다
Chunking 규칙
retriever는 H2(##) 제목 경계에서 chunking합니다. 즉, 노트 구조가 검색 단위의 세밀함에 직접 영향을 줍니다.
검색에 좋은 구조:
## Token Rotation Strategy
The rotation interval depends on the threat model...
## Implementation with refresh_token
The OAuth 2.0 refresh token flow requires...
## Error Handling: Expired Tokens
When a token expires mid-request...
3개의 H2 섹션은 독립적으로 검색 가능한 3개의 chunk를 만듭니다. 각 chunk는 embeddings가 의미를 포착하기에 충분한 컨텍스트를 갖습니다. “expired token handling”에 대한 쿼리는 특히 세 번째 chunk와 매칭됩니다.
검색에 좋지 않은 구조:
# OAuth Notes
Token rotation depends on threat model. The OAuth 2.0 refresh
token flow requires storing the refresh token securely. When a
token expires mid-request, the client should retry after refresh.
The rotation interval is typically 15-30 minutes for access tokens
and 7-30 days for refresh tokens...
H2 제목이 없는 긴 섹션 하나는 큰 chunk 하나를 만듭니다. embeddings는 섹션 안의 모든 주제를 평균화합니다. 어떤 하위 주제에 대한 쿼리든 전체 노트와 비슷하게 매칭됩니다.
경험칙: 한 섹션이 하나보다 많은 개념을 다룬다면 H2 하위 섹션으로 나누세요. 나머지는 chunker가 처리합니다.
노트에 넣지 말아야 할 것
검색 품질을 떨어뜨리는 콘텐츠는 다음과 같습니다.
- 주석 없이 전체 글을 그대로 복사해 붙여 넣은 원문. retriever는 원문 글의 키워드를 인덱싱하기 때문에, 직접 작성하지 않은 콘텐츠로 볼트가 희석됩니다. 대신 요약을 추가하거나, 핵심 포인트를 추출하거나, 소스 URL로 링크하세요.
- 텍스트 설명이 없는 스크린샷. retriever는 markdown 텍스트를 인덱싱합니다. alt text나 주변 설명이 없는 이미지는 BM25와 vector search 모두에서 보이지 않습니다.
- 자격 증명 문자열. API 키, 토큰, 비밀번호, 연결 문자열입니다. credential filtering을 사용하더라도 가장 안전한 방법은 노트에 secrets를 절대 붙여 넣지 않는 것입니다. 대신 이름으로 참조하세요(예: “
~/.env에 있는 Cloudflare API token”). - 큐레이션되지 않은 자동 생성 콘텐츠. 도구가 노트를 생성했다면(회의 transcript, Readwise highlights, RSS import), 영구 볼트에 들어가기 전에 검토하고 주석을 추가하세요. 큐레이션되지 않은 자동 import는 검색 가능한 가치를 더하지 않고 양만 늘립니다.
AI Workflow를 위한 Plugin Ecosystem
AI 검색 품질을 높이는 Obsidian plugins는 세 가지 범주로 나눌 수 있습니다. 구조화(일관성 강제), 쿼리(메타데이터 노출), 동기화(볼트를 최신 상태로 유지)입니다.
필수 Plugins
Dataview. frontmatter 필드를 사용해 볼트를 데이터베이스처럼 쿼리합니다. “최근 30일 동안 업데이트된 security 태그가 있는 모든 노트”나 “상태가 active인 모든 프로젝트 노트” 같은 동적 인덱스를 만들 수 있습니다. Dataview가 검색에 직접 도움이 되지는 않지만, 볼트의 커버리지 공백을 파악하고 업데이트가 필요한 노트를 찾는 데 도움이 됩니다.
TABLE type, domain, updated
FROM "03-resources"
WHERE status = "active"
SORT updated DESC
LIMIT 20
Templater. 동적 필드가 있는 템플릿으로 노트를 만듭니다. created, type, domain 필드를 미리 채우는 템플릿을 사용하면 모든 새 노트가 올바른 frontmatter로 시작하도록 할 수 있습니다. 일관된 frontmatter는 검색 필터링을 개선합니다.
<%* /* New Resource Note Template */ %>
---
title: "<% tp.file.cursor() %>"
type: note
domain: <% tp.system.suggester(["programming", "security", "ai-engineering", "design", "devops"], ["programming", "security", "ai-engineering", "design", "devops"]) %>
tags: []
created: <% tp.date.now("YYYY-MM-DD") %>
updated: <% tp.date.now("YYYY-MM-DD") %>
source: ""
status: active
---
## Key Points
## Details
## References
Linter. 볼트 전체에 포맷 규칙을 적용합니다. 일관된 제목 계층 구조(제목은 H1, 섹션은 H2, 하위 섹션은 H3)는 chunker가 예측 가능한 결과를 만들도록 합니다. 검색에 중요한 Linter 규칙은 다음과 같습니다.
- 제목 증가: 순차적인 제목 수준을 강제합니다(H1에서 H3로 건너뛰지 않음)
- YAML 제목: 파일 이름과 일치시킵니다
- 후행 공백: 제거합니다(FTS5 토큰화 아티팩트를 방지)
- 연속 빈 줄: 1개로 제한합니다(더 깔끔한 chunks)
Git integration. 볼트를 위한 버전 관리입니다. 시간에 따른 변경 사항을 추적하고, 여러 머신 간에 동기화하며, 실수로 삭제한 내용을 복구할 수 있습니다. Git은 indexer가 증분 변경 감지에 사용하는 mtime 데이터도 제공합니다.
Indexing에 도움이 되는 Plugins
Smart Connections. Obsidian 자체에서 AI 기반 의미 검색을 제공하는 Obsidian plugin입니다. Smart Connections v4는 기본적으로 로컬 embeddings를 생성합니다. 볼트가 인덱싱되고 나면 의미 연결과 조회가 API 호출 없이 완전히 오프라인으로 작동합니다.11 v4.5.0(2026년 5월 5일)에서는 footer connections가 Smart Connections Core의 일부가 되어, 모든 설치 환경에서 사이드 패널을 열지 않고도 footer에 관련 노트 연결을 표시할 수 있습니다. 최근 v4 릴리스에는 connection lists용 graph views, 설정 가능한 dock locations, 중단된 indexing 실행 이후의 개선된 block-embedding 복구, 그리고 Smart Connections, Smart Chat, Smart Composer가 상태를 공유할 수 있게 하는 cross-plugin 환경인 “Substrate”도 추가되었습니다.21 이 가이드의 검색 시스템은 Obsidian 외부에서 실행되지만(Python pipeline으로 실행), Smart Connections는 글을 쓰는 동안 의미 관계를 탐색하는 데 유용합니다. 두 시스템은 같은 콘텐츠를 인덱싱하지만 서로 다른 용도를 제공합니다. Smart Connections는 에디터 안에서 발견을 돕고, 외부 retriever는 MCP를 통한 AI tool 통합을 담당합니다.
2026년 4월에 출시된 AI-native plugins. 새로운 커뮤니티 plugins의 흐름은 Claude Code / Codex / Gemini-CLI workflow를 직접 겨냥합니다.
| Plugin | 출시 | 기능 |
|---|---|---|
| Cortex | 4월 4일 | Claude Code 기반 볼트 agent입니다. 볼트를 단순한 노트 저장소가 아니라 agent workspace로 다룹니다 |
| VaultSearch | 4월 7일 | Local-first hybrid search: BM25 + semantic + fuzzy(이 가이드의 검색 스택과 직접 겹칩니다) |
| LLM Wiki | 4월 9일 | 볼트를 비공개로 쿼리할 수 있는 지식 베이스로 바꿉니다 |
| Drift | 4월 11일 | AI 기반 Obsidian 편집을 위한 VS Code 스타일 diff viewer입니다. Claude Code workflows에 맞춰져 있습니다 |
| EngramQuest | 4월 11일 | 노트에서 기억력 챌린지를 생성합니다. Claude Code / Gemini CLI / Cursor용 “AI Skills”를 제공합니다 |
| Hybrid Search MCP | 3월(여전히 신규) | BM25 + semantic search를 갖춘 MCP server + CLI입니다. AI assistants를 위해 설계되었습니다 |
이를 새롭게 떠오르는 영역으로 보세요. 이 중 몇 가지는 앞으로 몇 분기 안에 Smart Connections나 Obsidian core로 통합되거나 흡수될 가능성이 큽니다. 지금 하나를 고른다면 VaultSearch와 Hybrid Search MCP가 이 가이드의 외부 retriever와 철학적으로 가장 가깝습니다.
Dataview 참고: 오래된 Obsidian query plugin인 Dataview는 2025년 4월에 0.5.70을 마지막으로 릴리스했으며, 이후 사실상 정체된 상태입니다. 새 작업에는 Obsidian의 내장 Bases 기능(1.9+)이 암묵적인 후속 기능이자 권장 경로입니다.
Metadata Menu. 필드 값 자동 완성을 포함한 구조화된 frontmatter 편집을 제공합니다. type, domain, tags 필드의 오타를 줄입니다. 일관된 메타데이터는 검색 필터링 정확도를 높입니다.
Indexing에 해로운 Plugins
Excalidraw. 그림을 markdown 파일에 포함된 JSON로 저장합니다. JSON는 문법적으로 유효한 markdown이지만, chunking 및 embedding 시 불필요한 결과를 만듭니다. .indexignore를 통해 Excalidraw 파일을 인덱스에서 제외하거나 파일 확장자로 필터링하세요.
Kanban. 보드 상태를 특수한 형식의 markdown으로 저장합니다. 이 형식은 Kanban 렌더링을 위해 설계된 것이지, prose 검색을 위한 것이 아닙니다. chunker는 카드 제목과 메타데이터 조각을 만들지만, 이는 embedding 품질이 좋지 않습니다. Kanban 보드는 인덱스에서 제외하세요.
Calendar. 최소한의 콘텐츠만 있는 일일 노트를 만듭니다(대개 날짜 제목만 있음). 비어 있거나 거의 비어 있는 노트는 품질이 낮은 chunks를 만듭니다. 일일 노트를 사용한다면 그 안에 실질적인 내용을 작성하거나, 일일 노트 폴더를 인덱스에서 제외하세요.
중요한 Plugin 설정
File recovery → Enabled. 실수로 노트를 삭제하는 상황을 방지합니다. 검색과 직접 관련되지는 않지만, 의존하는 지식 베이스에는 중요합니다.
Strict line breaks → Disabled. Markdown 표준 줄바꿈(문단 구분에는 이중 줄바꿈)이 Obsidian의 strict mode(<br>에 단일 줄바꿈 사용)보다 더 깔끔한 chunks를 만듭니다.
Default new file location → Designated folder. 새 파일을 00-inbox/로 보내면 분류되지 않은 노트가 domain folders를 오염시키지 않습니다. inbox는 임시 준비 영역이며, 파일은 triage 후 domain folders로 이동합니다.
Wiki-link format → Shortest path when possible. 더 짧은 link targets는 link structure를 인덱싱할 때 retriever가 더 쉽게 해석할 수 있습니다.
Embedding Models: 선택과 설정
Embedding model은 텍스트 chunks를 semantic search에 사용할 숫자 vectors로 변환합니다. 어떤 model을 선택하느냐에 따라 retrieval 품질, index 크기, embedding 속도, runtime dependencies가 달라집니다. 이 섹션에서는 Model2Vec의 potion-base-8M을 기본 선택으로 쓰는 이유와 alternatives를 선택해야 하는 경우를 설명합니다.
Model2Vec potion-base-8M을 선택하는 이유
Model: minishlab/potion-base-8M
Parameters: 7.6 million
Dimensions: 256
Size: ~30 MB
Dependencies: model2vec (numpy only, no PyTorch)
Inference: CPU-only, static word embeddings (no attention layers)
Model2Vec은 sentence transformer의 지식을 static token embeddings로 distill합니다. BERT, MiniLM, 기타 transformer models처럼 입력에 attention layers를 실행하는 대신, Model2Vec은 미리 계산된 token embeddings의 가중 평균으로 vectors를 생성합니다.5 실제 효과는 분명합니다. sequential computation이 없기 때문에 embedding 속도가 transformer 기반 models보다 50-500배 빠릅니다.
현재 Model2Vec results page에서 potion-base-8M은 all-MiniLM-L6-v2의 all-task score 대비 약 92%에 도달합니다(51.32 vs 55.80). 그러면서도 속도는 몇 자릿수 더 빠르게 유지합니다.6 남는 품질 격차는 속도와 단순성의 이점을 얻기 위한 trade-off입니다. 짧은 markdown chunks(일반적인 vault에서 평균 200-400단어)에서는 긴 문서보다 품질 차이가 덜 두드러집니다. 짧고 초점이 명확한 텍스트에서는 두 models 모두 비슷한 representations로 수렴하기 때문입니다.
설정
# embedder.py
DEFAULT_MODEL = "minishlab/potion-base-8M"
EMBEDDING_DIM = 256
class Model2VecEmbedder:
def __init__(self, model_name=DEFAULT_MODEL):
self._model_name = model_name
self._model = None
def _ensure_model(self):
if self._model is not None:
return
_activate_venv() # Add isolated venv to sys.path
from model2vec import StaticModel
self._model = StaticModel.from_pretrained(self._model_name)
def embed_batch(self, texts):
self._ensure_model()
vecs = self._model.encode(texts)
return [v.tolist() for v in vecs]
Lazy loading. model은 import 시점이 아니라 처음 사용할 때 로드됩니다. retriever가 BM25-only fallback mode로 동작할 때(예: embedding venv가 설치되지 않은 경우) embedder module을 import해도 비용이 들지 않습니다.
분리된 virtual environment. model은 전용 venv(예: ~/.claude/venvs/memory/)에서 실행되어 나머지 toolchain과의 dependency conflicts를 피합니다. _activate_venv() function은 runtime에 venv의 site-packages를 sys.path에 추가합니다.
# Create isolated venv
python3 -m venv ~/.claude/venvs/memory
~/.claude/venvs/memory/bin/pip install model2vec
Batch processing. embedder는 Model2Vec의 overhead를 amortize하기 위해 texts를 64개씩 batch로 처리합니다. indexer는 chunk를 하나씩 embedding하지 않고 embed_batch()에 chunks를 전달합니다.
Alternatives를 선택해야 하는 경우
| Model | Dim | Size | Speed | Quality (MTEB) | Best for |
|---|---|---|---|---|---|
| potion-base-8M | 256 | 30 MB | 500x | 51.32 | 기본값: local, fast, no GPU |
| potion-base-32M | 256 | 120 MB | 400x | 52.83 | 더 높은 품질, 여전히 static |
| potion-retrieval-32M | 256 | 120 MB | 400x | 35.06 (retrieval) | Retrieval에 최적화된 static |
| potion-multilingual-128M | 256 | ~500 MB | 300x | — | Multilingual vaults (101 languages) |
| all-MiniLM-L6-v2 | 384 | 80 MB | 1x | 55.80 | 더 높은 품질, 여전히 local |
| nomic-embed-text-v1.5 | 768 | 270 MB | 0.5x | 62.28 | 최고의 local 품질 |
| text-embedding-3-small | 1536 | API | N/A | 62.30 | API 기반, 최고 품질 |
potion-base-32M을 선택하세요 static embedding 계열을 벗어나지 않으면서 potion-base-8M보다 더 나은 품질을 원할 때 적합합니다. baai/bge-base-en-v1.5에서 distill한 더 큰 vocabulary를 사용해 52.83 all-task score를 달성합니다(potion-base-8M보다 약 3% 높음). 동시에 동일한 256-dimensional output과 numpy-only dependency를 유지합니다.8 model file이 4배 커져 memory usage는 늘어나지만, embedding 속도는 여전히 transformer models보다 몇 자릿수 빠릅니다.
potion-retrieval-32M을 선택하세요 주요 use case가 retrieval일 때 적합합니다(vault search가 여기에 해당합니다). 이 variant는 retrieval tasks를 위해 potion-base-32M에서 특별히 fine-tuned되었으며, Model2Vec의 retrieval benchmark table에서 35.06점을 기록합니다. potion-base-32M은 32.67점입니다.8 trade-off는 general-purpose embedding quality가 아니라 retrieval에 최적화되어 있다는 점입니다.
potion-multilingual-128M을 선택하세요 vault에 여러 언어의 notes가 포함되어 있을 때 적합합니다. 2025년 5월에 released된 이 101-language model은 multilingual tasks에서 가장 성능이 좋은 static embedding model이며, 다른 potion models와 동일한 numpy-only dependency를 유지하면서 모든 언어의 텍스트에 대해 embeddings를 생성합니다.12 더 큰 model file(~500 MB)은 cross-lingual capability를 얻기 위한 trade-off입니다. 영어 content와 함께 일본어, 중국어, 독일어 또는 기타 비영어 notes가 있을 때 사용하세요.
all-MiniLM-L6-v2를 선택하세요 속도보다 retrieval 품질이 더 중요하고 PyTorch가 설치되어 있을 때 적합합니다. 384-dimensional vectors는 256-dim vectors와 비교해 SQLite database size를 약 50% 늘립니다. M-series hardware에서 15,000 files 전체를 reindex할 때 embedding 속도는 1분 미만에서 약 10분으로 느려집니다.
nomic-embed-text-v1.5를 선택하세요 가능한 최고의 local retrieval 품질이 필요하고 느린 indexing을 감수할 수 있을 때 적합합니다. 768-dimensional vectors는 database size를 대략 3배로 늘립니다. PyTorch와 최신 CPU 또는 GPU가 필요합니다.
text-embedding-3-small을 선택하세요 network latency와 privacy를 trade-offs로 받아들일 수 있을 때 적합합니다. API는 가장 높은 품질의 embeddings를 생성하지만, cloud dependency와 per-token cost($0.02/million tokens)가 생기며 content가 OpenAI의 servers로 전송됩니다.
그 외의 모든 경우에는 potion-base-8M을 유지하세요. 속도 이점은 iterative indexing(개발 중 reindex)에 중요하고, numpy-only dependency는 PyTorch 설치 복잡성을 피하게 해주며, 256-dimensional vectors는 database를 compact하게 유지합니다.
Quantization 및 Dimensionality Reduction
Model2Vec v0.5.0+는 reduced precision과 dimensions로 models를 로드하는 기능을 지원합니다.8 이는 제한된 hardware에 deployment하거나 models를 바꾸지 않고 database size를 줄일 때 유용합니다:
from model2vec import StaticModel
# Load with int8 quantization (25% of original size)
model = StaticModel.from_pretrained("minishlab/potion-base-8M", quantize=True)
# Load with reduced dimensions (e.g., 128 instead of 256)
model = StaticModel.from_pretrained("minishlab/potion-base-8M", dimensionality=128)
Quantized models는 memory footprint를 크게 줄이면서도 거의 동일한 retrieval 품질을 유지합니다. Dimensionality reduction은 Matryoshka-style truncation을 따릅니다. 즉, 처음 N dimensions가 가장 많은 정보를 담습니다. 256에서 128 dimensions로 줄이면 short-text retrieval에서 품질 손실을 최소화하면서 vector storage를 절반으로 줄일 수 있습니다.
Model2Vec v0.8.x는 tokenizer/persistence internals를 업데이트하고, Python 3.9 support를 deprecate하며, published results를 더 최신 MTEB tables로 refresh합니다. production indexer를 upgrade하기 전에는 model2vec을 pin하거나 test하세요. embedding model name이 그대로여도 library upgrade로 model-loading paths가 바뀔 수 있기 때문입니다.10
Vault별 Embeddings를 위한 Fine-Tuning
Model2Vec v0.4.0+는 static embeddings 위에서 custom classification models를 training하는 기능을 지원하고, v0.7.0은 vocabulary quantization과 distillation을 위한 configurable pooling을 추가하며, v0.8.x는 tokenizer와 persistence behavior를 refactor합니다.10 이는 default potion models가 semantic nuances를 충분히 포착하지 못할 수 있는 specialized vocabulary(의료 notes, 법률 references, domain-specific jargon)가 있는 vaults에 관련됩니다:
from model2vec import StaticModel
from model2vec.train import train_model
# Fine-tune on vault-specific data
model = StaticModel.from_pretrained("minishlab/potion-base-8M")
trained_model = train_model(model, train_texts, train_labels)
trained_model.save_pretrained("./vault-embeddings")
대부분의 vault에서는 default potion-base-8M이 충분한 retrieval 품질을 제공합니다. Fine-tuning은 general-purpose model이 포착하지 못하는 domain-specific connections를 retrieval이 반복적으로 놓칠 때만 할 만합니다.
Model Hash Tracking
indexer는 model name과 vocabulary size에서 파생된 hash를 저장합니다. embedding model을 바꾸면 indexer가 다음 incremental run에서 mismatch를 감지하고 자동으로 full reindex를 trigger합니다.
def _compute_model_hash(self):
"""Hash model name + vocab size for compatibility tracking."""
key = f"{self._model_name}:{self._model.vocab_size}"
return hashlib.sha256(key.encode()).hexdigest()[:16]
이렇게 하면 서로 다른 models에서 나온 vectors가 같은 database에 섞이는 일을 막을 수 있습니다. 섞이면 cosine similarity scores가 의미 없는 값이 됩니다.
Failure Modes
Model download failure. 첫 실행에서는 Hugging Face에서 model을 다운로드합니다. download가 실패하면(network issue, corporate firewall) retriever는 BM25-only mode로 fallback합니다. 첫 download 이후 model은 local에 cached됩니다.
Dimension mismatch. database를 지우지 않고 models를 바꾸면 저장된 vectors의 dimension이 새 embeddings와 달라집니다. indexer는 model hash를 통해 이를 감지하고 full reindex를 trigger합니다. hash check가 실패하면(proper hash가 없는 custom model) sqlite-vec는 dimension이 맞지 않는 KNN queries에서 error를 냅니다.
대규모 vault의 memory pressure. 50,000개 이상의 chunks를 단일 batch로 embedding하면 상당한 memory를 사용할 수 있습니다. indexer는 peak memory usage를 제한하기 위해 64개씩 batch로 처리합니다. memory가 여전히 문제라면 batch size를 줄이세요.
FTS5를 사용한 Full-Text Search
SQLite의 FTS5 확장은 BM25 순위를 사용한 Full-Text Search를 제공합니다. FTS5는 hybrid 검색 파이프라인의 키워드 검색 구성 요소입니다. 이 섹션에서는 FTS5 설정, BM25가 뛰어난 경우, 그리고 구체적인 실패 모드를 다룹니다.
FTS5 Virtual Table
CREATE VIRTUAL TABLE chunks_fts USING fts5(
chunk_text,
section,
heading_context,
content=chunks,
content_rowid=id
);
Content-sync 모드. content=chunks 매개변수는 FTS5가 텍스트의 중복 복사본을 저장하는 대신 chunks 테이블을 직접 참조하도록 합니다. 이렇게 하면 저장 공간 요구량이 절반으로 줄지만, chunk가 삽입, 업데이트, 삭제될 때 FTS5를 수동으로 동기화해야 합니다.
컬럼. 3개의 컬럼이 인덱싱됩니다.
- chunk_text — 각 chunk의 기본 콘텐츠(BM25 가중치: 1.0)
- section — H2 heading 텍스트(BM25 가중치: 0.5)
- heading_context — 노트 제목, 태그, metadata(BM25 가중치: 0.3)
BM25 순위
BM25는 term frequency, inverse document frequency, document length normalization을 기준으로 문서 순위를 매깁니다. FTS5의 bm25() 보조 기능은 컬럼별 가중치를 받습니다.
SELECT
c.id, c.file_path, c.section, c.chunk_text,
bm25(chunks_fts, 1.0, 0.5, 0.3) AS score
FROM chunks_fts
JOIN chunks c ON chunks_fts.rowid = c.id
WHERE chunks_fts MATCH ?
ORDER BY score
LIMIT 30;
컬럼 가중치(1.0, 0.5, 0.3)의 의미는 다음과 같습니다.
- chunk_text의 키워드 일치가 점수에 가장 크게 기여합니다
- section(heading)의 일치는 그 절반만큼 기여합니다
- heading_context(제목, 태그)의 일치는 30%만큼 기여합니다
이 가중치는 조정할 수 있습니다. vault에 콘텐츠 품질을 강하게 예측하는 설명적인 heading이 있다면 section 가중치를 높이세요. 태그가 포괄적이고 정확하다면 heading_context 가중치를 높이세요.
BM25가 이기는 경우
BM25는 정확한 식별자가 포함된 쿼리에서 뛰어납니다.
- 기능 이름:
_rrf_fuse,embed_batch,get_stale_files - CLI 플래그:
--incremental,--vault,--model - 설정 키:
bm25_weight,max_tokens,batch_size - 오류 메시지:
SQLITE_LOCKED,ConnectionRefusedError - 특정 전문 용어:
PostToolUse,PreToolUse,AGENTS.md
이런 쿼리에서는 BM25가 정확한 일치를 즉시 찾습니다. 벡터 검색은 의미적으로 관련된 콘텐츠를 반환하겠지만, 정확한 일치를 개념적 논의보다 낮게 순위 매길 수도 있습니다.
BM25가 실패하는 경우
BM25는 저장된 콘텐츠와 다른 용어를 사용하는 쿼리에서 실패합니다.
- 쿼리: “how to handle authentication failures” → Vault에는 “login error recovery”와 “session expiration handling”에 관한 노트가 있습니다. 키워드가 다르기 때문에 BM25는 일치시키지 못합니다.
- 쿼리: “what is the best way to manage state” → Vault에는 “Redux store patterns”와 “context providers”에 관한 노트가 있습니다. “state management”가 특정 기술 이름으로 표현되어 있기 때문에 BM25는 놓칩니다.
BM25는 규모가 커질 때 키워드 충돌에서도 실패합니다. 15,000개 파일이 있는 vault에서 “configuration”을 검색하면 거의 모든 프로젝트 노트가 configuration을 언급하기 때문에 수백 개의 노트가 일치합니다. 결과는 기술적으로 맞지만 실제로는 쓸모가 없습니다. 순위 시스템이 현재 쿼리와 관련 있는 “configuration” 노트가 무엇인지 판단할 수 없기 때문입니다.
FTS5 Tokenizer
FTS5는 기본적으로 ASCII와 Unicode 텍스트를 처리하는 unicode61 tokenizer를 사용합니다. CJK(중국어, 일본어, 한국어) 콘텐츠가 많은 vault라면 trigram tokenizer를 고려하세요.
-- For CJK-heavy vaults
CREATE VIRTUAL TABLE chunks_fts USING fts5(
chunk_text, section, heading_context,
content=chunks, content_rowid=id,
tokenize='trigram'
);
기본 unicode61 tokenizer는 단어 경계를 기준으로 분리하므로, 단어 사이에 공백이 없는 언어에서는 잘 작동하지 않습니다. trigram tokenizer는 3글자마다 분리해 substring matching을 가능하게 하지만, index 크기가 커지는 대가가 있습니다(대략 3배 더 큽니다).
유지 관리
FTS5는 기반 chunks 테이블이 변경될 때 명시적인 동기화가 필요합니다.
# After inserting chunks
cursor.execute("""
INSERT INTO chunks_fts(chunks_fts)
VALUES('rebuild')
""")
rebuild 명령은 콘텐츠 테이블에서 FTS5 index를 다시 구성합니다. 대량 삽입(full reindex) 후에는 실행하되, 개별 incremental 업데이트 후에는 실행하지 마세요. 그런 경우에는 INSERT INTO chunks_fts(rowid, chunk_text, section, heading_context)를 사용해 개별 row를 동기화하세요.
sqlite-vec를 사용한 Vector Search
sqlite-vec 확장은 SQLite에 vector KNN (K-Nearest Neighbors) 검색을 제공합니다. 이 섹션에서는 sqlite-vec 설정, 노트에서 검색 가능한 vector로 이어지는 embedding 파이프라인, 그리고 구체적인 쿼리 패턴을 다룹니다.
sqlite-vec Virtual Table
CREATE VIRTUAL TABLE chunk_vecs USING vec0(
id INTEGER PRIMARY KEY,
embedding float[256]
);
vec0 모듈은 256차원 float vector를 packed binary data로 저장합니다. id 열은 chunks 테이블과 1:1로 매핑되어 vector 결과와 chunk metadata를 조인할 수 있게 합니다.
Embedding Pipeline
파이프라인은 노트에서 검색 가능한 vector까지 다음 흐름으로 이어집니다.
Note (.md file)
→ Chunker: split at H2 boundaries
→ Chunks (30-2000 chars each)
→ Credential filter: scrub secrets
→ Embedder: Model2Vec encode
→ Vectors (256-dim float arrays)
→ sqlite-vec: store as packed binary
→ Ready for KNN queries
Vector Serialization
Python의 struct 모듈은 float vector를 sqlite-vec 저장용으로 직렬화합니다.
import struct
def _serialize_vector(vec):
"""Pack float list into binary for sqlite-vec."""
return struct.pack(f"{len(vec)}f", *vec)
def _deserialize_vector(blob, dim=256):
"""Unpack binary blob to float list."""
return list(struct.unpack(f"{dim}f", blob))
KNN Query
vector search 쿼리는 입력 쿼리를 embedding한 다음, cosine distance 기준으로 가장 가까운 K개의 chunk를 찾습니다.
def _vector_search(self, query_text, limit=30):
query_vec = self.embedder.embed_batch([query_text])[0]
packed = _serialize_vector(query_vec)
results = self.db.execute("""
SELECT
cv.id,
cv.distance,
c.file_path,
c.section,
c.chunk_text
FROM chunk_vecs cv
JOIN chunks c ON cv.id = c.id
WHERE embedding MATCH ?
AND k = ?
ORDER BY distance
""", [packed, limit]).fetchall()
return results
sqlite-vec의 MATCH 연산자는 approximate nearest neighbor search를 수행합니다. k 매개변수는 반환할 결과 수를 제어합니다. distance 열에는 cosine distance가 들어갑니다(0 = 동일, 2 = 반대).
Distance Constraints를 사용한 KNN Pagination
sqlite-vec v0.1.7부터 KNN 쿼리는 WHERE distance < ? 제약을 지원하므로, 이전 페이지를 다시 스캔하지 않고도 큰 결과 집합을 cursor 기반으로 pagination할 수 있습니다.14 이후 v0.1.8 및 v0.1.9 stable 릴리스는 새로운 query model 릴리스라기보다 packaging 및 DELETE 버그 수정 릴리스이므로, 이 pagination 패턴의 기능 경계는 여전히 v0.1.7입니다.23
앞으로는 v0.1.10-alpha 라인(2026년 3월 31일 - 5월 18일)이 sqlite-vec를 brute-force KNN 너머로 처음 확장합니다. 이 라인은 approximate-nearest-neighbor index type을 도입합니다. 여기에는 rescore, 기본으로 활성화되지 않는 실험적 ivf(inverted-file) index, 그리고 vector를 메모리에 상주시킬 수 없을 만큼 큰 vault를 위한 disk 기반 DiskANN index가 포함됩니다.23 이런 변화는 매우 큰 vault의 scaling 방식을 바꿀 수 있지만, 0.1.10 라인은 아직 pre-release (alpha) 상태입니다. 따라서 ANN indexing은 실험적인 것으로 보고, stable 0.1.10이 출시되기 전까지 production vault에서는 stable v0.1.9 brute-force KNN 경로를 기반으로 계속 구축하세요.
def _paginated_vector_search(self, query_vec, page_size=20, max_distance=None):
"""Paginate through KNN results using distance constraints."""
packed = _serialize_vector(query_vec)
constraint = f"AND distance < {max_distance}" if max_distance else ""
results = self.db.execute(f"""
SELECT cv.id, cv.distance, c.file_path, c.chunk_text
FROM chunk_vecs cv
JOIN chunks c ON cv.id = c.id
WHERE embedding MATCH ?
AND k = ?
{constraint}
ORDER BY distance
""", [packed, page_size]).fetchall()
# Use last result's distance as cursor for next page
next_cursor = results[-1][1] if results else None
return results, next_cursor
이는 큰 k를 가져온 뒤 Python에서 잘라내던 이전 패턴을 대체하며, 큰 vault에서 탐색적 쿼리를 실행할 때 메모리 사용량을 줄여 줍니다.
vec0 Tables의 DELETE Support
sqlite-vec v0.1.7은 vec0 virtual table에 native DELETE support를 추가했고, v0.1.9는 12자를 초과하는 metadata text 열과 관련된 DELETE 오류 경로를 수정했습니다.1423 이전에는 vector를 제거하려면 테이블을 drop한 뒤 다시 만들어야 했습니다. 이제 indexer의 file-removal 경로는 vector를 직접 삭제할 수 있습니다.
# Before v0.1.7: required workaround (drop + recreate, or mark as inactive)
# After v0.1.7: direct DELETE works
db.execute("DELETE FROM chunk_vecs WHERE id = ?", [chunk_id])
이로써 노트가 삭제되거나 이동될 때 incremental reindexing이 단순해집니다. indexer는 더 이상 shadow “active IDs” 테이블을 유지하거나 batch rebuild를 수행할 필요가 없습니다.
Vector Search가 유리한 경우
Vector search는 특정 단어보다 개념이 더 중요한 쿼리에서 강점을 보입니다.
- Query: “how to handle authentication failures” → “login error recovery”에 대한 노트를 찾습니다(같은 semantic space, 다른 keyword)
- Query: “what patterns exist for caching” → “memoization”, “Redis TTL strategies”, “HTTP cache headers”에 대한 노트를 찾습니다(관련 개념, 다양한 terminology)
- Query: “approaches to testing asynchronous code” → “pytest-asyncio fixtures”, “mock event loops”, “async test patterns”에 대한 노트를 찾습니다(같은 개념이 구현 세부 사항으로 표현됨)
Vector Search가 실패하는 경우
Vector search는 정확한 identifier에 약합니다.
- Query:
_rrf_fuse→ “fusion algorithms”와 “rank merging”에 대한 노트를 반환하지만, 실제 function definition은 개념적 논의보다 낮게 ranking될 수 있습니다 - Query:
PostToolUse→ 특정 hook name보다는 “tool lifecycle hooks”와 “post-execution handlers”에 대한 노트를 반환합니다
Vector search는 structured data에도 약합니다. JSON 설정 파일, YAML block, code snippet은 semantic meaning보다 structural pattern을 포착하는 embedding을 생성합니다. "review": true가 있는 JSON 파일은 code review에 대한 prose 논의와 다르게 embedding됩니다.
Graceful Degradation
sqlite-vec 로드에 실패하면(missing extension, incompatible platform, corrupted library), retriever는 BM25-only search로 fallback합니다.
class VectorIndex:
def __init__(self, db_path):
self.db = sqlite3.connect(db_path)
self._vec_available = False
try:
self.db.enable_load_extension(True)
self.db.load_extension("vec0")
self._vec_available = True
except Exception:
pass # BM25-only mode
@property
def vec_available(self):
return self._vec_available
retriever는 vector query를 시도하기 전에 vec_available을 확인합니다. 비활성화되어 있으면 모든 검색은 BM25만 사용하고, RRF fusion 단계는 건너뜁니다.
Reciprocal Rank Fusion (RRF)
RRF는 점수 보정 없이 순위가 매겨진 두 목록을 병합합니다. 이 섹션에서는 알고리즘, 실제 쿼리 추적 예시, k 매개변수 튜닝, 그리고 RRF를 다른 대안 대신 선택한 이유를 다룹니다. 순위를 편집할 수 있는 인터랙티브 계산기, 시나리오 프리셋, 시각적 아키텍처 탐색기는 hybrid retriever 심층 분석을 참고하세요.
알고리즘
RRF는 각 목록에서 문서가 차지한 순위 위치만을 기준으로 각 문서에 점수를 부여합니다.
score(d) = Σ (weight_i / (k + rank_i))
여기서:
- k는 평활화 상수입니다(60, Cormack et al.3을 따름)
- rank_i는 결과 목록 i에서 문서의 1부터 시작하는 순위입니다
- weight_i는 목록별 선택적 배수입니다(기본값 1.0)
여러 목록에서 높은 순위를 받은 문서는 더 높은 융합 점수를 받습니다. 한 목록에만 나타나는 문서는 해당 단일 소스에서만 점수를 받습니다.
대안 대신 RRF를 선택한 이유
가중 선형 결합은 BM25 점수를 cosine distances와 보정해야 합니다. BM25 점수는 상한이 없고 corpus 크기에 따라 스케일이 달라집니다. Cosine distances는 [0, 2] 범위로 제한됩니다. 둘을 결합하려면 정규화가 필요하며, 정규화 매개변수는 데이터셋에 따라 달라집니다. RRF는 순위 위치만 사용하며, 순위는 점수 산정 방식과 관계없이 항상 1부터 시작하는 정수입니다.
학습된 융합 모델은 라벨링된 학습 데이터, 즉 쿼리-문서 관련성 쌍이 필요합니다. 개인 knowledge base에는 이런 학습 데이터가 없습니다. 유용한 모델을 학습하려면 수백 개의 쿼리-문서 쌍을 직접 판정해야 합니다. RRF는 학습 데이터 없이 작동합니다.
Condorcet voting 방식(Borda count, Schulze method)은 이론적으로는 우아하지만 구현하고 튜닝하기가 더 복잡합니다. 원래 RRF 논문은 TREC 평가 데이터에서 RRF가 Condorcet 방식보다 더 뛰어난 성능을 보인다고 밝혔습니다.3
실제 Fusion
쿼리: “how does the review aggregator handle disagreements”
BM25는 review-aggregator.py를 3위에 배치합니다(“review,” “aggregator,” “disagreements”에 대한 정확한 키워드 일치). 하지만 “review”가 더 두드러지게 일치하는 두 설정 파일을 더 높은 순위에 둡니다. Vector search는 같은 chunk를 1위에 배치합니다(충돌 해결에 대한 의미적 일치). RRF fusion 이후 결과는 다음과 같습니다.
| Chunk | BM25 | Vec | Fused Score |
|---|---|---|---|
| review-aggregator.py “Disagreement Resolution” | #3 | #1 | 0.0323 |
| code-review-patterns.md “Multi-Reviewer” | #4 | #2 | 0.0317 |
| deliberation-config.json “Review Weights” | #1 | — | 0.0164 |
두 목록에서 모두 높은 순위를 받은 chunk가 상위로 올라옵니다. 한 목록에만 나타나는 chunk는 단일 소스 점수만 받아, 두 목록 모두에서 순위가 매겨진 결과보다 아래로 내려갑니다. 실제 의견 불일치 해결 로직이 이기는 이유는 두 방식이 모두 그것을 찾아냈기 때문입니다. BM25는 키워드를 통해, vector search는 의미를 통해 찾아냈습니다.
순위별 RRF 계산을 포함한 전체 단계별 추적은 interactive RRF calculator에서 여러 k 값을 바꿔 보며 확인할 수 있습니다.
구현
RRF_K = 60
def _rrf_fuse(self, bm25_results, vec_results,
bm25_weight=1.0, vec_weight=1.0):
"""Fuse BM25 and vector results using Reciprocal Rank Fusion."""
scores = {}
for rank, r in enumerate(bm25_results, start=1):
cid = r["id"]
if cid not in scores:
scores[cid] = {
"rrf_score": 0.0,
"file_path": r["file_path"],
"section": r["section"],
"chunk_text": r["chunk_text"],
"bm25_rank": None,
"vec_rank": None,
}
scores[cid]["rrf_score"] += bm25_weight / (self._rrf_k + rank)
scores[cid]["bm25_rank"] = rank
for rank, r in enumerate(vec_results, start=1):
cid = r["id"]
if cid not in scores:
scores[cid] = {
"rrf_score": 0.0,
"file_path": r["file_path"],
"section": r["section"],
"chunk_text": r["chunk_text"],
"bm25_rank": None,
"vec_rank": None,
}
scores[cid]["rrf_score"] += vec_weight / (self._rrf_k + rank)
scores[cid]["vec_rank"] = rank
fused = sorted(
scores.values(),
key=lambda x: x["rrf_score"],
reverse=True,
)
return fused
k 튜닝
k 상수는 상위 순위 결과와 하위 순위 결과에 각각 얼마나 많은 가중치를 줄지 제어합니다.
- 낮은 k(예: 10): 상위 순위 결과가 지배합니다. 1위 점수는 1/11 = 0.091이고, 10위 점수는 1/20 = 0.050입니다(1.8배 차이). 개별 ranker가 최상위 결과를 정확히 찾는다고 신뢰할 수 있을 때 좋습니다.
- 기본 k(60): 균형 잡힌 값입니다. 1위 점수는 1/61 = 0.0164이고, 10위 점수는 1/70 = 0.0143입니다(1.15배 차이). 순위 차이가 압축되어 여러 목록에 함께 나타나는 것에 더 많은 가중치가 부여됩니다.
- 높은 k(예: 200): 두 목록에 모두 나타나는지가 순위 위치보다 훨씬 더 중요해집니다. 1위 점수는 1/201이고, 10위 점수는 1/210으로 거의 같습니다. 개별 ranker의 순위가 noisy하지만 목록 간 일치가 신뢰할 만할 때 사용하세요.
k=60으로 시작하세요. 원래 RRF 논문은 이 값이 다양한 TREC 데이터셋에서 견고하다는 점을 확인했습니다. 직접 사용하는 쿼리 분포에서 실패 사례를 측정한 뒤에만 튜닝하세요.
동점 처리
두 chunk의 RRF 점수가 동일한 경우(드물지만 한 목록에서 같은 순위를 받고 다른 목록에는 나타나지 않으면 가능함), 다음 기준으로 동점을 처리합니다.
- 한 목록에만 나타나는 chunk보다 두 목록에 모두 나타나는 chunk를 우선합니다
- 두 목록에 모두 나타나는 chunk끼리는 합산 순위가 더 낮은 chunk를 우선합니다
- 한 목록에만 나타나는 chunk끼리는 해당 목록에서 순위가 더 낮은 chunk를 우선합니다
전체 Retrieval Pipeline
이 섹션에서는 입력부터 출력까지 전체 pipeline을 따라 query가 어떻게 처리되는지 살펴봅니다. BM25 search, vector search, RRF fusion, token budget truncation, context assembly 순서로 이어집니다.
End-to-End 흐름
User query: "PostToolUse hook for context compression"
│
├─ BM25 Search (FTS5)
│ → MATCH "PostToolUse hook context compression"
│ → Top 30 results ranked by BM25 score
│ → 12ms
│
├─ Vector Search (sqlite-vec)
│ → Embed query with Model2Vec
│ → KNN k=30 on chunk_vecs
│ → Top 30 results ranked by cosine distance
│ → 8ms
│
└─ RRF Fusion
→ Merge 60 candidates (may overlap)
→ Score by rank position
→ Top 10 results
→ 3ms
│
└─ Token Budget
→ Truncate to max_tokens (default 4000)
→ Estimate at 4 chars per token
→ Return results with metadata
→ <1ms
총 latency: 약 23ms입니다. Apple M3 Pro hardware에서 49,746개 chunk database를 기준으로 측정했습니다.
Search API
class HybridRetriever:
def search(self, query, limit=10, max_tokens=4000,
bm25_weight=1.0, vec_weight=1.0):
"""
Search the vault using hybrid BM25 + vector retrieval.
Args:
query: Search query text
limit: Maximum results to return
max_tokens: Token budget for total result text
bm25_weight: Weight for BM25 results in RRF
vec_weight: Weight for vector results in RRF
Returns:
List of SearchResult with file_path, section,
chunk_text, rrf_score, bm25_rank, vec_rank
"""
# BM25 search
bm25_results = self._bm25_search(query, limit=30)
# Vector search (if available)
if self.index.vec_available:
vec_results = self._vector_search(query, limit=30)
fused = self._rrf_fuse(
bm25_results, vec_results,
bm25_weight, vec_weight,
)
else:
fused = bm25_results # BM25-only fallback
# Token budget truncation
results = []
token_count = 0
for r in fused[:limit]:
chunk_tokens = len(r["chunk_text"]) // 4
if token_count + chunk_tokens > max_tokens:
break
results.append(r)
token_count += chunk_tokens
return results
Token Budget Truncation
max_tokens parameter는 retriever가 AI tool이 사용할 수 있는 양보다 많은 context를 반환하지 않도록 막아 줍니다. 추정에는 token당 4글자를 사용합니다. 이는 English prose에 적당한 근사치입니다. 결과는 greedy 방식으로 잘립니다. 순위가 높은 결과부터 budget이 소진될 때까지 추가합니다.
이는 보수적인 전략입니다. 더 정교한 접근법이라면 result별 quality score를 고려해 길고 품질이 낮은 결과보다 짧고 품질이 높은 결과를 우선할 수 있습니다. greedy approach는 더 단순하고 실제로도 잘 작동합니다. RRF ranking이 이미 relevance 기준으로 결과를 정렬하기 때문입니다.
Database Schema (전체)
-- Chunk content and metadata
CREATE TABLE chunks (
id INTEGER PRIMARY KEY,
file_path TEXT NOT NULL,
section TEXT NOT NULL,
chunk_text TEXT NOT NULL,
heading_context TEXT DEFAULT '',
mtime_ns INTEGER NOT NULL,
embedded_at REAL NOT NULL
);
CREATE INDEX idx_chunks_file ON chunks(file_path);
CREATE INDEX idx_chunks_mtime ON chunks(mtime_ns);
-- FTS5 for BM25 search (content-synced to chunks table)
CREATE VIRTUAL TABLE chunks_fts USING fts5(
chunk_text, section, heading_context,
content=chunks, content_rowid=id
);
-- sqlite-vec for vector KNN search
CREATE VIRTUAL TABLE chunk_vecs USING vec0(
id INTEGER PRIMARY KEY,
embedding float[256]
);
-- Model metadata for compatibility tracking
CREATE TABLE model_meta (
key TEXT PRIMARY KEY,
value TEXT
);
Graceful Degradation Path
Full pipeline: BM25 + Vector + RRF → Best results
No sqlite-vec: BM25 only → Good results (no semantic)
No model download: BM25 only → Good results (no semantic)
No FTS5: Vector only → Decent results (no keyword)
No database: Error → Prompt user to run indexer
retriever는 초기화 시 capability를 확인하고 query strategy를 조정합니다. component가 없으면 quality는 낮아지지만 error가 발생하지는 않습니다. 유일한 hard failure는 database file이 없는 경우입니다.
Production Stats
16,894개 file, 49,746개 chunk, 83 MB SQLite database, Apple M3 Pro 기준으로 측정했습니다.
| Metric | Value |
|---|---|
| 전체 file | 16,894 |
| 전체 chunk | 49,746 |
| Database size | 83 MB |
| BM25 query latency (p50) | 12ms |
| Vector query latency (p50) | 8ms |
| RRF fusion latency | 3ms |
| End-to-end search latency (p50) | 23ms |
| Full reindex time | 약 4분 |
| Incremental reindex time | <10초 |
| Embedding model | potion-base-8M (256-dim) |
| BM25 candidate pool | 30 |
| Vector candidate pool | 30 |
| Default result limit | 10 |
| Default token budget | 4,000 tokens |
Content Hashing과 Change Detection
indexer는 마지막 index run 이후 어떤 file이 변경되었는지 알아야 합니다. 이 섹션에서는 change detection mechanism과 hashing strategy를 다룹니다.
File Modification Time 비교
indexer는 모든 chunk에 대해 chunks table에 mtime_ns(file modification time, nanoseconds 단위)를 저장합니다. incremental run에서 indexer는 다음을 수행합니다.
- 허용된 folder 안의 모든
.mdfile을 vault에서 scan합니다. - filesystem에서 각 file의
mtime_ns를 읽습니다. - database에 저장된
mtime_ns와 비교합니다. - 세 가지 category를 식별합니다.
- New files: path가 filesystem에는 있지만 database에는 없습니다.
- Changed files: path가 양쪽에 모두 있지만
mtime_ns가 다릅니다. - Deleted files: path가 database에는 있지만 filesystem에는 없습니다.
def get_stale_files(self, vault_mtimes):
"""Find files whose mtime changed or are new."""
stored = dict(self.db.execute(
"SELECT DISTINCT file_path, mtime_ns FROM chunks"
).fetchall())
stale = []
for path, mtime in vault_mtimes.items():
if path not in stored or stored[path] != mtime:
stale.append(path)
return stale
def get_deleted_files(self, vault_paths):
"""Find files in database that no longer exist in vault."""
stored_paths = set(r[0] for r in self.db.execute(
"SELECT DISTINCT file_path FROM chunks"
).fetchall())
return stored_paths - set(vault_paths)
Content Hash가 아니라 mtime을 쓰는 이유
Content hashing(file contents의 SHA-256)은 mtime comparison보다 더 안정적입니다. file이 실제로 바뀌지 않았는데 touched된 경우도 감지할 수 있습니다. 예를 들어 git checkout이 원래 mtime을 복원하는 경우입니다. 하지만 hashing은 incremental run마다 모든 file을 읽어야 합니다. 16,894개 file의 contents를 읽는 데는 2-3초가 걸립니다. filesystem에서 mtime을 읽는 데는 <100ms가 걸립니다.
trade-off는 이렇습니다. mtime comparison은 변경되지 않은 file을 불필요하게 re-indexing하는 경우(false positive)가 가끔 있지만, 실제 변경을 놓치지는 않습니다. false positive는 run마다 embedding call 몇 개가 더 드는 정도입니다. 속도 차이(100ms vs 3초) 때문에 매 AI interaction마다 실행되는 system에서는 mtime이 실용적인 선택입니다.
Deletion 처리
vault에서 file이 삭제되면 indexer는 해당 file의 모든 chunk를 database에서 제거합니다.
def remove_file(self, file_path):
"""Remove all chunks and vectors for a file."""
chunk_ids = [r[0] for r in self.db.execute(
"SELECT id FROM chunks WHERE file_path = ?",
[file_path],
).fetchall()]
for cid in chunk_ids:
self.db.execute(
"DELETE FROM chunk_vecs WHERE id = ?", [cid]
)
self.db.execute(
"DELETE FROM chunks WHERE file_path = ?",
[file_path],
)
DELETE FROM chunk_vecs statement는 sqlite-vec v0.1.7부터 native로 작동하며, v0.1.9에서는 metadata text column이 더 긴 vec0 table에 대한 DELETE operation bug fix가 포함되었습니다.1423 이전 version에서는 workaround가 필요했습니다. 예를 들어 virtual table을 drop한 뒤 다시 만들거나, 외부 “active IDs” set을 유지해야 했습니다. pre-0.1.9 version을 실행 중이라면 metadata-heavy schema에서 direct delete에 의존하기 전에 upgrade하세요.
FTS5 content-sync table은 제거된 각 row에 대해 INSERT INTO chunks_fts(chunks_fts, rowid, ...) VALUES('delete', ?, ...)를 통해 명시적으로 delete해야 합니다. indexer는 file removal process의 일부로 이를 처리합니다.
증분 재색인과 전체 재색인
인덱서는 두 가지 모드를 지원합니다. 증분 모드(빠르고 일상적으로 사용)와 전체 모드(느리고 가끔 사용)입니다. 이 섹션에서는 각 모드를 언제 사용해야 하는지, 멱등성 보장, 손상 복구를 다룹니다.
증분 재색인
사용 시점: 노트를 편집한 뒤 매일 인덱싱할 때 사용합니다. 기본 모드입니다.
수행 작업: 1. 저장소에서 파일 변경 사항을 스캔합니다(mtime 비교) 2. 삭제된 파일의 chunks를 삭제합니다 3. 변경된 파일을 다시 chunking하고 다시 embedding합니다 4. 새 파일의 새 chunks를 삽입합니다 5. FTS5 인덱스를 동기화합니다
일반적인 소요 시간: 16,000개 파일이 있는 저장소에서 하루치 편집분 기준 <10초입니다.
python index_vault.py --incremental
전체 재색인
사용 시점: - embedding 모델을 변경한 뒤(모델 해시 불일치 감지) - 스키마 마이그레이션 뒤(새 컬럼, 변경된 인덱스) - 데이터베이스 손상 뒤(무결성 검사 실패) - 증분 인덱싱이 예상과 다른 결과를 만들 때
수행 작업: 1. 기존 데이터를 모두 삭제합니다(chunks, vectors, FTS5 entries) 2. 전체 저장소를 스캔합니다 3. 모든 파일을 chunking합니다 4. 모든 chunks를 embedding합니다 5. FTS5 인덱스를 처음부터 빌드합니다
일반적인 소요 시간: Apple M3 Pro에서 16,894개 파일 기준 약 4분입니다.
python index_vault.py --full
멱등성
두 모드는 모두 멱등적입니다. 같은 명령을 두 번 실행해도 같은 결과가 나옵니다. 인덱서는 새 chunks를 삽입하기 전에 해당 파일의 기존 chunks를 삭제하므로, 이미 최신 상태인 데이터베이스에 증분 인덱싱을 다시 실행하면 변경 사항이 0개입니다. 전체 인덱싱을 다시 실행하면 동일한 데이터베이스가 생성됩니다.
손상 복구
SQLite 데이터베이스가 손상된 경우(쓰기 중 전원 손실, 디스크 오류, 트랜잭션 도중 프로세스 종료):
# Check integrity
sqlite3 vectors.db "PRAGMA integrity_check;"
# If corruption detected, full reindex rebuilds from source files
python index_vault.py --full
진실의 원천은 항상 데이터베이스가 아니라 저장소 파일입니다. 데이터베이스는 언제든 다시 빌드할 수 있는 파생 산출물입니다. 이는 중요한 설계 속성입니다. 데이터베이스를 백업할 필요가 없습니다.
--incremental 플래그
인덱서가 --incremental로 실행될 때:
- 모델 해시 검사. 저장된 모델 해시를 현재 모델과 비교합니다. 다르면 자동으로 전체 재색인 모드로 전환하고 사용자에게 경고합니다.
- 파일 스캔. 허용된 폴더를 순회하며 파일 경로와 mtimes를 수집합니다.
- 변경 감지. 저장된 데이터와 비교합니다.
- 배치 처리. 변경된 파일을 64개 단위 배치로 다시 chunking하고 다시 embedding합니다.
- 진행 상황 보고. 처리한 파일 수와 경과 시간을 출력합니다.
- 정상 종료. SIGINT를 받으면 현재 파일 처리를 마친 뒤 중지합니다.
자격 증명 필터링과 데이터 경계
개인 노트에는 비밀 정보가 들어 있습니다. API 키, bearer tokens, 데이터베이스 연결 문자열, 디버깅 세션 중 붙여 넣은 private keys 등이 그렇습니다. 자격 증명 필터는 이런 정보가 검색 인덱스에 들어가지 않도록 막습니다.
문제
OAuth 통합을 디버깅하는 노트에는 다음과 같은 내용이 있을 수 있습니다.
The token was: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
I used this curl command:
curl -H "Authorization: Bearer sk-ant-api03-abc123..."
필터링이 없으면 JWT와 API 키가 모두 chunking되고 embedding된 뒤 데이터베이스에 저장됩니다. “authentication”을 검색하면 실제 비밀 정보가 들어 있는 chunk가 반환됩니다. 더 나쁜 경우, retriever가 MCP를 통해 결과를 AI 도구에 전달하면 비밀 정보가 AI의 컨텍스트 창에 들어가고, 잠재적으로 도구 로그에도 남습니다.
패턴 기반 필터링
자격 증명 필터는 저장 전 모든 chunk에서 실행되며, 25개의 벤더별 패턴과 일반 패턴을 매칭합니다.
벤더별 패턴:
| 패턴 | 예시 | Regex |
|---|---|---|
| OpenAI API key | sk-... |
sk-[a-zA-Z0-9_-]{20,} |
| Anthropic API key | sk-ant-api03-... |
sk-ant-api\d{2}-[a-zA-Z0-9_-]{20,} |
| GitHub PAT | ghp_... |
gh[ps]_[a-zA-Z0-9]{36,} |
| AWS Access Key | AKIA... |
AKIA[0-9A-Z]{16} |
| Stripe key | sk_live_... |
[sr]k_(live\|test)_[a-zA-Z0-9]{24,} |
| Cloudflare token | ... |
다양한 패턴 |
일반 패턴:
| 패턴 | 감지 방식 |
|---|---|
| JWT tokens | eyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+ |
| Bearer tokens | Bearer\s+[a-zA-Z0-9_\-\.]+ |
| Private keys | -----BEGIN (RSA\|EC\|OPENSSH) PRIVATE KEY----- |
| 고엔트로피 base64 | 엔트로피가 >4.5 bits/char이고 40자 이상인 문자열 |
| Password assignments | password\s*[:=]\s*["'][^"']+["'] |
필터 구현
def clean_content(text):
"""Scrub credentials from text before indexing."""
result = ScanResult(is_clean=True, match_count=0, patterns=[])
for pattern in CREDENTIAL_PATTERNS:
matches = pattern.regex.findall(text)
if matches:
text = pattern.regex.sub(
f"[REDACTED:{pattern.name}]", text
)
result.is_clean = False
result.match_count += len(matches)
result.patterns.append(pattern.name)
return text, result
주요 설계 선택:
-
embedding 전에 필터링합니다. 정리된 텍스트가 embedding 대상입니다. vector 표현은 자격 증명 패턴을 절대 인코딩하지 않습니다. “API key”를 쿼리하면 실제 키가 들어 있는 노트가 아니라 API key 관리에 대해 다루는 노트가 반환됩니다.
-
삭제하지 않고 대체합니다.
[REDACTED:pattern-name]토큰은 주변 텍스트의 의미적 맥락을 보존합니다. embedding은 자격 증명처럼 보이는 무언가가 이 위치에 있었다는 점을 포착하지만, 자격 증명 자체는 인코딩하지 않습니다. -
값이 아니라 패턴을 기록합니다. 필터는 어떤 패턴이 매칭되었는지 기록합니다(예: “Scrubbed 2 credential(s) from oauth-debug.md [jwt, bearer-token]”). 하지만 자격 증명 값은 절대 기록하지 않습니다.
경로 기반 제외
.indexignore 파일은 경로 기준의 큰 단위 제외를 제공합니다. 자격 증명 필터는 인덱싱되는 파일 내부에서 세밀한 정리를 제공합니다. 둘 다 필요합니다.
.indexignore는 민감한 콘텐츠가 들어 있다고 알고 있는 전체 폴더에 사용합니다(건강 노트, 재무 기록, 커리어 문서)- 자격 증명 필터는 원래는 인덱싱할 수 있는 콘텐츠에 실수로 포함된 비밀 정보에 사용합니다
데이터 분류
다양한 콘텐츠가 들어 있는 저장소라면 민감도에 따라 노트를 분류하는 방식을 고려하세요.
| 수준 | 예시 | 인덱싱? | 필터링? |
|---|---|---|---|
| 공개 | 블로그 초안, 기술 노트 | 예 | 예 |
| 내부 | 프로젝트 계획, 아키텍처 결정 | 예 | 예 |
| 민감 | 급여 데이터, 건강 기록 | 아니요(.indexignore) | N/A |
| 제한 | 자격 증명, private keys | 아니요(.indexignore) | N/A |
MCP 서버 아키텍처
Model Context Protocol (MCP) 서버는 AI 에이전트가 호출할 수 있는 도구로 retriever를 노출합니다. 이 섹션에서는 서버 설계, 기능 범위, 권한 경계를 다룹니다.
프로토콜 선택: STDIO vs HTTP
MCP는 두 가지 전송 모드를 지원합니다.
STDIO — AI 도구가 MCP 서버를 자식 프로세스로 실행하고 stdin/stdout을 통해 통신합니다. 로컬 도구의 표준 모드입니다. Claude Code, Codex CLI, Cursor는 모두 STDIO MCP 서버를 지원합니다.
{
"mcpServers": {
"obsidian": {
"command": "python",
"args": ["/path/to/obsidian_mcp.py"],
"env": {
"VAULT_PATH": "/path/to/vault",
"DB_PATH": "/path/to/vectors.db"
}
}
}
}
HTTP — MCP 서버가 독립 실행형 HTTP 서비스로 실행됩니다. 원격 접근, 다중 클라이언트 구성, 또는 vault가 공유 서버에 있는 팀 구성에 유용합니다.
{
"mcpServers": {
"obsidian": {
"url": "http://localhost:3333/mcp"
}
}
}
권장 사항: 개인 vault에는 STDIO를 사용하세요. 더 단순하고, 더 안전하며(네트워크 노출 없음), 서버 수명 주기를 AI 도구가 관리합니다. 여러 도구나 여러 머신이 같은 vault에 동시에 접근해야 할 때만 HTTP를 사용하세요.
MCP 사양의 발전. 2025년 6월 MCP 사양에는 OAuth 2.1 권한 부여, 구조화된 도구 출력(타입이 지정된 반환 스키마), elicitation(서버가 시작하는 사용자 프롬프트)이 추가되었습니다. 2025년 11월 릴리스에서는 Streamable HTTP가 1급 전송 모드로 제공되었고, 자동 서버 기능 탐색을 위한
.well-knownURL discovery, 도구가 읽기 전용인지 변경 작업을 수행하는지 선언하는 구조화된 도구 주석, SDK 티어 표준화 시스템이 포함되었습니다.79 다음 개정판은 이제 구체화되었습니다. 2026-07-28 사양이 2026년 5월 21일 Release Candidate에 진입했으며, 출시 이후 가장 큰 MCP 개정판입니다. 주요 변경 사항은 상태 없는 프로토콜 코어(initializehandshake와Mcp-Session-Id헤더가 제거되어 서버가 더 이상 연결별 세션 상태를 추적하지 않음), MCP Apps(서버가 sandboxed client iframe에 표시되는 서버 렌더링 HTML를 반환할 수 있음), Tasks가 experimental core에서 공식 확장으로 승격(tasks/get,tasks/update,tasks/cancel로 장기 실행 작업 처리), 강화된 OAuth 2.0 / OIDC 권한 부여, 12개월 기능 지원 중단 수명 주기 정책입니다. 최종 사양은 2026년 7월 28일에 출시됩니다.24 개인 vault 서버에서는 STDIO가 여전히 가장 단순한 경로이며, 상태 없는 코어 덕분에 단일 사용자 STDIO 서버는 더 가벼워집니다. Streamable HTTP 전송,.well-knowndiscovery, MCP Apps는 주로 다중 테넌트 라우팅과 로드 밸런싱이 필요한 엔터프라이즈 HTTP 배포에 이점이 있습니다. 전송 방식 선택에 영향을 주는 업데이트는 MCP roadmap에서 확인하세요.
기능 설계
MCP 서버는 최소한의 도구만 노출해야 합니다.
search — 기본 도구입니다. hybrid retrieval을 실행하고 순위가 매겨진 결과를 반환합니다.
{
"name": "obsidian_search",
"description": "Search the Obsidian vault using hybrid BM25 + vector retrieval",
"parameters": {
"query": { "type": "string", "description": "Search query" },
"limit": { "type": "integer", "default": 5 },
"max_tokens": { "type": "integer", "default": 2000 }
}
}
read_note — 경로로 특정 노트의 전체 내용을 읽습니다. 에이전트가 검색 결과의 전체 맥락을 보고 싶을 때 유용합니다.
{
"name": "obsidian_read_note",
"description": "Read the full content of a note by file path",
"parameters": {
"file_path": { "type": "string", "description": "Relative path within vault" }
}
}
list_notes — 필터(폴더, 태그, 유형, 날짜 범위 기준)와 일치하는 노트를 나열합니다. 에이전트에게 특정 쿼리가 없고 탐색이 필요할 때 유용합니다.
{
"name": "obsidian_list_notes",
"description": "List notes matching filters",
"parameters": {
"folder": { "type": "string", "description": "Folder path within vault" },
"tag": { "type": "string", "description": "Tag to filter by" },
"limit": { "type": "integer", "default": 20 }
}
}
get_context — 검색을 실행하고 결과를 대화에 삽입하기 적합한 맥락 블록 형식으로 구성하는 편의 도구입니다.
{
"name": "obsidian_get_context",
"description": "Get formatted context from vault for a topic",
"parameters": {
"topic": { "type": "string", "description": "Topic to get context for" },
"max_tokens": { "type": "integer", "default": 2000 }
}
}
권한 경계
MCP 서버는 엄격한 경계를 적용해야 합니다.
-
읽기 전용. 서버는 vault와 index database를 읽습니다. 노트를 만들거나 수정하거나 삭제하지 않습니다. 쓰기 작업(새 노트 캡처)은 MCP 서버가 아니라 별도의 hooks나 skills에서 처리합니다.
-
Vault 범위 제한. 서버는 설정된 vault 경로 안의 파일만 읽습니다. 경로 탐색 시도(
../../etc/passwd)는 반드시 거부해야 합니다. -
Credential-filtered output. database에 이미 필터링된 콘텐츠가 있더라도 심층 방어 조치로 출력 단계에서 credential filtering을 적용하세요.
-
토큰 제한 응답. AI 도구가 지나치게 큰 맥락 블록을 받지 않도록 모든 도구 응답에
max_tokens를 적용하세요.
오류 처리
MCP 도구는 AI 도구가 복구하는 데 도움이 되는 구조화된 오류 메시지를 반환해야 합니다.
def search(self, query, limit=5, max_tokens=2000):
if not self.db_path.exists():
return {
"error": "Index database not found. Run the indexer first.",
"suggestion": "python index_vault.py --full"
}
results = self.retriever.search(query, limit, max_tokens)
if not results:
return {
"results": [],
"message": f"No results found for '{query}'. Try broader terms."
}
return {
"results": [
{
"file_path": r["file_path"],
"section": r["section"],
"text": r["chunk_text"],
"score": round(r["rrf_score"], 4),
}
for r in results
],
"count": len(results),
"query": query,
}
Claude Code 통합
Claude Code은 Obsidian 검색 시스템의 주 사용 대상입니다. 이 섹션에서는 MCP 설정, hook 통합, obsidian_bridge.py 패턴을 다룹니다.
MCP 설정
Obsidian MCP 서버를 ~/.claude/settings.json에 추가하세요.
{
"mcpServers": {
"obsidian": {
"command": "python",
"args": ["/path/to/obsidian_mcp.py"],
"env": {
"VAULT_PATH": "/absolute/path/to/vault",
"DB_PATH": "/absolute/path/to/vectors.db"
}
}
}
}
설정을 추가한 뒤 Claude Code을 다시 시작하세요. MCP 서버는 자식 프로세스로 시작됩니다. 실행 중인지 확인하세요.
> What tools do you have from the obsidian MCP server?
Claude Code은 사용 가능한 도구(obsidian_search, obsidian_read_note 등)를 나열해야 합니다.
Hook 통합
Hook은 정의된 수명 주기 지점에서 Claude Code의 동작을 확장합니다. Obsidian 통합에는 2개의 hook이 관련됩니다.
PreToolUse hook — 에이전트가 도구 호출을 처리하기 전에 vault를 쿼리합니다. 관련 컨텍스트를 자동으로 주입합니다.
#!/bin/bash
# ~/.claude/hooks/pre-tool-use/obsidian-context.sh
# Automatically inject vault context before tool execution
TOOL_NAME="$1"
PROMPT="$2"
# Only inject context for code-related tools
case "$TOOL_NAME" in
Edit|Write|Bash)
# Query the vault
CONTEXT=$(python /path/to/retriever.py search "$PROMPT" --limit 3 --max-tokens 1500)
if [ -n "$CONTEXT" ]; then
echo "---"
echo "Relevant vault context:"
echo "$CONTEXT"
echo "---"
fi
;;
esac
PostToolUse hook — 중요한 도구 출력을 vault에 다시 캡처해 나중에 검색할 수 있게 합니다.
#!/bin/bash
# ~/.claude/hooks/post-tool-use/capture-insight.sh
# Capture significant outputs to vault (selective)
TOOL_NAME="$1"
OUTPUT="$2"
# Only capture substantial outputs
if [ ${#OUTPUT} -gt 500 ]; then
python /path/to/capture.py --text "$OUTPUT" --source "claude-code-$TOOL_NAME"
fi
obsidian_bridge.py 패턴
브리지 모듈은 hook과 skill이 호출할 수 있는 Python API를 제공합니다.
# obsidian_bridge.py
from retriever import HybridRetriever
_retriever = None
def get_retriever():
global _retriever
if _retriever is None:
_retriever = HybridRetriever(
db_path="/path/to/vectors.db",
vault_path="/path/to/vault",
)
return _retriever
def search_vault(query, limit=5, max_tokens=2000):
"""Search vault and return formatted context."""
retriever = get_retriever()
results = retriever.search(query, limit, max_tokens)
if not results:
return ""
lines = ["## Vault Context\n"]
for r in results:
lines.append(f"**{r['file_path']}** — {r['section']}")
lines.append(f"> {r['chunk_text'][:500]}")
lines.append("")
return "\n".join(lines)
/capture Skill
인사이트를 vault에 다시 캡처하기 위한 Claude Code skill입니다.
/capture "OAuth token rotation requires both access and refresh token invalidation"
--domain security
--tags oauth,tokens
이 skill은 적절한 frontmatter와 함께 00-inbox/에 새 노트를 만들고, 새 노트를 즉시 검색할 수 있도록 증분 재색인을 트리거합니다.
Custom Command 패턴
Claude Code skill은 vault 작업을 이름 있는 명령으로 감쌀 수 있습니다. 실무자들은 vault를 읽기 소스이자 쓰기 대상으로 다루는 Obsidian 전용 명령 라이브러리를 구축해 왔습니다.
신호 스캔. /scan-intel 명령은 외부 소스를 쿼리하고, 개인 연구 관심사에 비추어 발견 항목에 점수를 매긴 다음, 기준을 통과한 신호를 frontmatter가 포함된 vault 노트로 작성합니다.
/scan-intel --topics "agent infrastructure, security" --lookback 7d
이 명령은 설정된 소스(arXiv, HN, RSS)에서 가져오고, 점수 모델(관련성, 실행 가능성, 깊이, 권위)을 적용한 뒤, 통과한 신호를 주제별 vault 폴더에 작성합니다. vault는 자동화된 인텔리전스 파이프라인의 downstream 소비자가 됩니다.
Captain’s log. /captains-log 명령은 모든 저장소의 일일 git 활동을 집계하고, 구조화된 저널 항목을 vault에 작성하며, 내린 결정, 깨달음, 열린 논의 흐름을 포함합니다.
/captains-log
이 명령은 GitHub에서 커밋 기록을 가져와 저장소별로 그룹화하고, 서사형 저널 항목으로 형식화합니다. 시간이 지나면 일일 로그는 무엇이 배포되었고 왜 그렇게 했는지에 대한 검색 가능한 기록이 됩니다.
Obsidian 캡처. /obsidian-capture 명령은 현재 Claude Code 세션의 인사이트를 가져와 적절한 메타데이터와 함께 vault에 직접 작성합니다.
/obsidian-capture "SAST gates in agent loops increase security degradation"
--folder AI-Tools --tags security,agents
이 패턴은 모든 vault 작업으로 확장됩니다. MOC 생성, 프로젝트 상태 노트 업데이트, 관련 신호 연결, 누적된 일일 로그에서 주간 다이제스트 생성 등이 가능합니다.
커뮤니티 예시. 실무자들은 자신들의 명령 라이브러리를 공개하고 있습니다. 한 개발자는 일일 리뷰, 프로젝트 계획, 연구 캡처, 콘텐츠 워크플로를 다루는 22개의 커스텀 Obsidian + Claude Code 명령을 공유했습니다.1 또 다른 개발자는 코드 분석을 바탕으로 vault에 다이어그램 노트를 생성하는 “Visual Explainer” skill을 만들었습니다.2 명령은 다양하지만 아키텍처는 일관됩니다. 인터페이스는 Claude Code skill, 저장 계층은 vault 노트, 쿼리 엔진은 검색 인프라입니다.
Context Window 관리
통합에서는 Claude Code의 context window를 염두에 두어야 합니다.
- 쿼리당 주입되는 컨텍스트를 1,500-2,000 tokens로 제한하세요. 이보다 많으면 에이전트의 작업 메모리와 경쟁하게 됩니다.
- 출처 표시를 포함하세요. 에이전트가 출처를 참조할 수 있도록 파일 경로와 섹션 제목을 항상 포함하세요.
- Chunk 텍스트를 잘라내세요. 긴 chunk는 완전히 생략하지 말고
...로 잘라내야 합니다. 보통 처음 300-500자에 핵심 정보가 들어 있습니다. - 모든 도구 호출에 주입하지 마세요. PreToolUse hook은 호출되는 도구에 따라 선택적으로 컨텍스트를 주입해야 합니다. 읽기 작업에는 vault 컨텍스트가 필요하지 않습니다. Write 및 Edit 작업은 vault 컨텍스트가 있으면 도움이 됩니다.
Codex CLI 통합
Codex CLI는 config.toml을 통해 MCP 서버에 연결합니다. 통합 패턴은 설정 문법과 지침 전달 방식에서 Claude Code과 다릅니다.
MCP 설정
.codex/config.toml 또는 ~/.codex/config.toml에 추가하세요.
[mcp_servers.obsidian]
command = "python"
args = ["/path/to/obsidian_mcp.py"]
[mcp_servers.obsidian.env]
VAULT_PATH = "/absolute/path/to/vault"
DB_PATH = "/absolute/path/to/vectors.db"
AGENTS.md 패턴
Codex CLI는 프로젝트 수준 지침을 위해 AGENTS.md를 읽습니다. vault 검색 지침을 포함하세요.
## Available Tools
### Obsidian Vault (MCP: obsidian)
Use the `obsidian_search` tool to find relevant context from the knowledge base.
Search the vault when you need:
- Background on a concept or pattern
- Prior decisions or rationale
- Reference material for implementation
Example queries:
- "authentication patterns in FastAPI"
- "how does the review aggregator work"
- "sqlite-vec configuration"
Claude Code과의 차이점
| 기능 | Claude Code | Codex CLI |
|---|---|---|
| MCP 설정 | settings.json |
config.toml |
| Hook | ~/.claude/hooks/ |
지원되지 않음 |
| Skill | ~/.claude/skills/ |
지원되지 않음 |
| 지침 파일 | CLAUDE.md |
AGENTS.md |
| 승인 모드 | --dangerously-skip-permissions |
suggest / auto-edit / full-auto |
핵심 차이: Codex CLI는 hook을 지원하지 않습니다. 자동 컨텍스트 주입 패턴(PreToolUse hook)은 사용할 수 없습니다. 대신 AGENTS.md에 명시적인 지침을 포함해, 작업을 시작하기 전에 에이전트가 vault를 검색하도록 안내하세요.
Cursor 및 기타 도구
MCP를 지원하는 Cursor와 기타 AI 도구는 동일한 Obsidian MCP 서버에 연결할 수 있습니다. 이 섹션에서는 일반적인 도구의 설정을 다룹니다.
Cursor
프로젝트 루트의 .cursor/mcp.json에 추가하세요.
{
"mcpServers": {
"obsidian": {
"command": "python",
"args": ["/path/to/obsidian_mcp.py"],
"env": {
"VAULT_PATH": "/absolute/path/to/vault",
"DB_PATH": "/absolute/path/to/vectors.db"
}
}
}
}
Cursor의 .cursorrules 파일에는 vault를 사용하라는 지침을 포함할 수 있습니다.
When working on implementation tasks, search the Obsidian vault
for relevant context before writing code. Use the obsidian_search
tool with descriptive queries about the concept you're implementing.
호환성 매트릭스
| 도구 | MCP 지원 | 전송 방식 | 설정 위치 |
|---|---|---|---|
| Claude Code | 전체 | STDIO | ~/.claude/settings.json |
| Codex CLI | 전체 | STDIO | .codex/config.toml |
| Cursor | 전체 | STDIO | .cursor/mcp.json |
| Windsurf | 전체 | STDIO | .windsurf/mcp.json |
| Continue.dev | 부분 | HTTP | ~/.continue/config.json |
| Zed | 진행 중 | STDIO | Settings UI |
| Claudian (Obsidian plugin) | 해당 없음(내장) | Claude Code CLI | Obsidian plugin 설정 |
| Agent Client (Obsidian plugin) | 해당 없음(내장) | ACP | Obsidian plugin 설정 |
MCP를 지원하지 않는 도구를 위한 대안
MCP를 지원하지 않는 도구의 경우, retriever를 CLI로 감쌀 수 있습니다.
# Search from command line
python retriever_cli.py search "query text" --limit 5
# Output formatted for copy-paste into any tool
python retriever_cli.py context "query text" --format markdown
CLI는 구조화된 텍스트를 출력하며, 이를 어떤 AI 도구의 입력에도 직접 붙여 넣을 수 있습니다. MCP 통합만큼 세련되지는 않지만 어디서나 작동합니다.
구조화된 노트로 Prompt Caching 사용하기
vault의 구조화된 노트는 AI 상호작용 전반에서 토큰 사용량을 줄이는 재사용 가능한 컨텍스트 블록으로 활용할 수 있습니다. 이 섹션에서는 캐시 키 설계와 토큰 예산 관리를 다룹니다.
패턴
매번 상호작용할 때마다 컨텍스트를 검색하는 대신, 잘 구조화된 vault 노트에서 컨텍스트 블록을 미리 만들고 캐시하세요.
# cache_keys.py
CONTEXT_BLOCKS = {
"auth-patterns": {
"vault_query": "authentication patterns implementation",
"max_tokens": 1500,
"ttl_hours": 24, # Rebuild daily
},
"api-conventions": {
"vault_query": "API design conventions REST patterns",
"max_tokens": 1000,
"ttl_hours": 168, # Rebuild weekly
},
"project-architecture": {
"vault_query": "current project architecture decisions",
"max_tokens": 2000,
"ttl_hours": 12, # Rebuild twice daily
},
}
캐시 무효화
캐시 무효화는 두 가지 신호를 기준으로 합니다.
- TTL 만료. 각 컨텍스트 블록에는 유효 시간이 있습니다. TTL이 만료되면 vault를 다시 쿼리해 블록을 다시 만듭니다.
- Vault 변경 감지. indexer가 캐시된 컨텍스트 블록에 기여한 파일의 변경 사항을 감지하면, 해당 블록은 즉시 무효화됩니다.
토큰 예산 관리
세션은 전체 컨텍스트 예산으로 시작합니다. 캐시된 블록은 해당 예산의 일부를 사용합니다.
Total context budget: 8,000 tokens
├─ System prompt: 1,500 tokens
├─ Cached blocks: 3,000 tokens (pre-loaded)
├─ Dynamic search: 2,000 tokens (on-demand)
└─ Conversation: 1,500 tokens (remaining)
캐시된 블록은 세션 시작 시 로드됩니다. 동적 검색 결과는 쿼리별로 남은 예산을 채웁니다. 이 hybrid 접근 방식은 agent에 자주 필요한 컨텍스트의 기준선을 제공하면서도, 특정 쿼리를 위한 예산을 남겨 둡니다.
캐싱 전후의 토큰 사용량
캐싱 없음: 관련 쿼리마다 vault 검색이 발생하며, 1,500-2,000 토큰의 컨텍스트를 반환합니다. 한 세션에서 10개의 쿼리를 실행하면 agent는 vault 컨텍스트로 15,000-20,000 토큰을 사용합니다.
캐싱 사용: 미리 만든 컨텍스트 블록 3개가 총 4,500 토큰을 사용합니다. 추가 검색은 고유 쿼리마다 1,500-2,000 토큰을 더합니다. 10개의 쿼리 중 6개가 캐시된 블록으로 처리되는 경우, agent는 4,500 + (4 * 1,500) = 10,500 토큰을 사용하며, 이는 캐싱하지 않았을 때의 대략 절반입니다.
컨텍스트 압축을 위한 PostToolUse Hooks
도구 출력은 스택 트레이스, 파일 목록, 테스트 결과처럼 장황할 수 있습니다. PostToolUse hook은 이러한 출력이 컨텍스트 창 공간을 사용하기 전에 압축할 수 있습니다.
문제
테스트를 실행하는 Bash 도구 호출은 다음과 같은 결과를 반환할 수 있습니다.
PASSED tests/test_auth.py::test_login_success
PASSED tests/test_auth.py::test_login_failure
PASSED tests/test_auth.py::test_token_refresh
PASSED tests/test_auth.py::test_session_expiry
... (200 more lines)
FAILED tests/test_api.py::test_rate_limit_exceeded
전체 출력은 5,000 토큰이지만, 신호는 2줄에 있습니다. 200개 통과, 1개 실패입니다.
Hook 구현
#!/bin/bash
# ~/.claude/hooks/post-tool-use/compress-output.sh
# Compress verbose tool outputs to preserve context window
TOOL_NAME="$1"
OUTPUT="$2"
OUTPUT_LEN=${#OUTPUT}
# Only compress large outputs
if [ "$OUTPUT_LEN" -lt 2000 ]; then
exit 0 # Pass through unchanged
fi
case "$TOOL_NAME" in
Bash)
# Compress test output
if echo "$OUTPUT" | grep -q "PASSED\|FAILED"; then
PASSED=$(echo "$OUTPUT" | grep -c "PASSED")
FAILED=$(echo "$OUTPUT" | grep -c "FAILED")
FAILURES=$(echo "$OUTPUT" | grep "FAILED")
echo "Tests: $PASSED passed, $FAILED failed"
if [ "$FAILED" -gt 0 ]; then
echo "Failures:"
echo "$FAILURES"
fi
fi
;;
esac
재귀 트리거 방지
출력을 내보내는 압축 hook은 보호 장치가 없으면 자기 자신을 다시 트리거할 수 있습니다.
# Guard against recursive invocation
if [ -n "$COMPRESS_HOOK_ACTIVE" ]; then
exit 0
fi
export COMPRESS_HOOK_ACTIVE=1
압축 휴리스틱
| 출력 유형 | 감지 | 압축 전략 |
|---|---|---|
| 테스트 결과 | PASSED / FAILED 키워드 |
통과/실패 개수를 세고 실패만 표시 |
| 파일 목록 | 명령의 ls 또는 find |
처음 20개 항목 + 개수로 자르기 |
| 스택 트레이스 | Traceback 키워드 |
첫 번째와 마지막 프레임 + 오류 메시지만 유지 |
| Git 상태 | modified: / new file: |
상태별 개수 요약 |
| 빌드 출력 | warning: / error: |
정보 라인을 제거하고 경고/오류만 유지 |
신호 수집 및 분류 파이프라인
수집 계층은 무엇이 볼트에 들어갈지 결정합니다. 선별 과정이 없으면 볼트에는 잡음이 쌓입니다. 이 섹션에서는 신호를 도메인 폴더로 라우팅하는 점수화 파이프라인을 다룹니다.
소스
신호는 여러 채널에서 들어옵니다.
- RSS 피드: 기술 블로그, 보안 권고, 릴리스 노트
- Web Clipper를 통한 북마크: 공식 Obsidian Web Clipper 확장 프로그램(Chrome, Firefox, Safari)은 브라우저에서 캡처할 때 가장 충실도가 높은 수집 경로입니다. 2026년 4월 릴리스 주기에는 AI 워크플로에 실질적으로 더 유용해졌습니다:22
- 1.4.0 (4월 9일): 대화형 YouTube transcript UI — 동영상을 고정하고, transcript를 훑어보고, 자동 스크롤하며, 현재 위치를 강조 표시합니다. 여기에 원클릭 캡처를 Reader 모드로 바로 보내는 “Open in Reader” 기본값이 추가되었습니다.
- 1.5.0–1.5.1 (4월 15일): Highlights viewer — 볼트 전체에서 캡처한 하이라이트를 탐색하고 검색합니다. Reader로 전환될 때 페이드인 전환이 적용됩니다. YouTube 재생/일시정지가 더 부드러워졌습니다. 1.5.1에서는 webpack 컴파일 회귀가 수정되었습니다.
- 1.6.0–1.6.2 (4월 21–23일): 모바일 지원을 포함한 Highlighter UX 전면 개편. Defuddle 0.18은 LinkedIn, Threads, Bluesky, Discourse, Medium을 위한 소스별 추출기를 추가합니다. 1.6.2는 Safari embedded-mode 클립보드 회귀를 수정합니다. 소스 도메인별로 템플릿을 설정해 YouTube transcripts, GitHub READMEs, 장문 글이 각각 아래 점수화 파이프라인에 맞는 frontmatter를 갖춘 적절한 이름의 노트로 들어가게 하세요.
- 뉴스레터: 이메일 뉴스레터의 핵심 발췌문
- 수동 캡처: 읽기, 대화, 조사 중에 작성한 노트
- 도구 출력: hooks를 통해 캡처한 중요한 AI 도구 출력
- iOS Share Extension: Obsidian의 iOS 앱(2026년 초 업데이트)에는 Safari, 소셜 네트워크, 다른 앱의 콘텐츠를 Obsidian을 열지 않고도 볼트에 직접 저장하는 Share Extension이 포함되어 있습니다.19 덕분에 마찰이 적은 모바일 수집 경로가 생깁니다. Safari에서 글을 공유하면 점수화할 준비가 된 볼트 노트로 도착합니다.
- Obsidian CLI: Shell scripts와 hooks는
obsidian file create로 노트를 만들거나obsidian file append로 기존 노트에 추가할 수 있어 데스크톱에서 자동 수집 파이프라인을 구현할 수 있습니다.
점수화 기준
각 신호는 4가지 기준으로 점수를 매깁니다(각각 0.0에서 1.0).
| 기준 | 질문 | 낮은 점수 (0.0-0.3) | 높은 점수 (0.7-1.0) |
|---|---|---|---|
| 관련성 | 이 내용이 내 활성 도메인과 관련이 있나요? | 곁가지에 가깝고 범위 밖임 | 현재 작업과 직접 관련 있음 |
| 실행 가능성 | 이 정보를 활용할 수 있나요? | 순수 이론이며 적용점 없음 | 적용할 수 있는 구체적인 기법이나 패턴 |
| 깊이 | 콘텐츠가 얼마나 충실한가요? | 헤드라인, 얕은 요약 | 예시가 포함된 상세 분석 |
| 권위 | 소스가 얼마나 신뢰할 만한가요? | 익명 블로그, 검증되지 않음 | 1차 소스, 동료 심사, 인정받은 전문가 |
종합 점수와 라우팅
composite = (relevance * 0.35) + (actionability * 0.25) +
(depth * 0.25) + (authority * 0.15)
| 점수 범위 | 작업 |
|---|---|
| 0.55+ | 도메인 폴더로 자동 라우팅 |
| 0.40 - 0.55 | 수동 검토 대기열에 추가 |
| < 0.40 | 삭제(저장하지 않음) |
도메인 라우팅
0.55를 넘는 신호는 키워드 매칭과 주제 분류를 기준으로 12개 도메인 폴더 중 하나로 라우팅됩니다.
05-signals/
├── ai-tooling/ # Claude, LLMs, AI development tools
├── security/ # Vulnerabilities, auth, cryptography
├── systems/ # Architecture, distributed systems
├── programming/ # Languages, patterns, algorithms
├── web/ # Frontend, backends, APIs
├── data/ # Databases, data engineering
├── devops/ # CI/CD, containers, infrastructure
├── design/ # UI/UX, product design
├── mobile/ # iOS, Android, cross-platform
├── career/ # Industry trends, hiring, growth
├── research/ # Academic papers, whitepapers
└── other/ # Signals that don't fit a domain
운영 통계
14개월 넘게 운영한 결과:
| 지표 | 값 |
|---|---|
| 처리한 총 신호 수 | 7,771 |
| 자동 라우팅됨 (>0.55) | 4,832 (62%) |
| 검토 대기열에 추가됨 (0.40-0.55) | 1,543 (20%) |
| 삭제됨 (<0.40) | 1,396 (18%) |
| 활성 도메인 폴더 | 12 |
| 일평균 신호 수 | ~18 |
Knowledge Graph 패턴
Obsidian의 wiki-link 그래프는 노트 간 관계를 인코딩합니다. 이 섹션에서는 링크 의미론, 컨텍스트 확장을 위한 그래프 탐색, 그래프 품질을 떨어뜨리는 안티패턴을 다룹니다.
Backlink 의미론
모든 wiki-link는 그래프에 방향성 edge를 만듭니다. Obsidian은 forward link와 backlink를 모두 추적합니다.
- Forward link: 노트 A에
[[Note B]]가 포함됨 → A가 B로 링크함 - Backlink: 노트 B는 노트 A가 자신을 참조한다고 표시함
그래프는 문맥에 따라 서로 다른 관계 유형을 인코딩합니다.
| 링크 패턴 | 의미 | 예시 |
|---|---|---|
| 인라인 링크 | “관련 있음” | “자세한 내용은 [[OAuth Token Rotation]]을 참고하세요” |
| 헤더 링크 | “하위 주제가 있음” | ”## Related\n- [[Token Rotation]]\n- [[Session Management]]” |
| 태그형 링크 | “다음으로 분류됨” | ”[[type/reference]]” |
| MOC 링크 | “일부임” | 관련 노트를 나열하는 Map of Content 노트 |
Maps of Content (MOCs)
MOC는 관련 노트를 탐색 가능한 구조로 정리하는 인덱스 노트입니다.
---
title: "Authentication & Security MOC"
type: moc
domain: security
---
## Core Concepts
- [[OAuth 2.0 Overview]]
- [[JWT Token Anatomy]]
- [[Session Management Patterns]]
## Implementation Patterns
- [[OAuth Token Rotation]]
- [[Refresh Token Security]]
- [[PKCE Flow Implementation]]
## Failure Modes
- [[Token Expiry Handling]]
- [[Session Fixation Prevention]]
- [[CSRF Defense Strategies]]
MOC는 검색에 두 가지 방식으로 도움이 됩니다.
- 직접 일치. “authentication overview”를 검색하면 MOC 자체가 매칭되어, 에이전트에게 관련 노트의 선별된 목록을 제공합니다.
- 컨텍스트 확장. 특정 노트를 찾은 뒤 retriever는 해당 노트가 어떤 MOC에 포함되어 있는지 확인하고 결과에 MOC의 구조를 포함할 수 있습니다. 이렇게 하면 에이전트가 더 넓은 주제의 지도를 얻습니다.
컨텍스트 확장을 위한 그래프 탐색
retriever의 향후 개선 사항: 상위 결과를 찾은 뒤 링크를 따라가며 컨텍스트를 확장합니다.
def expand_context(results, depth=1):
"""Follow wiki-links from top results to find related context."""
expanded = set()
for result in results:
# Parse wiki-links from chunk text
links = extract_wiki_links(result["chunk_text"])
for link_target in links:
# Resolve link to file path
target_path = resolve_wiki_link(link_target)
if target_path and target_path not in expanded:
expanded.add(target_path)
# Include target's most relevant chunk
target_chunks = get_chunks_for_file(target_path)
# ... rank and include best chunk
return results + list(expanded_results)
이는 현재 retriever에는 구현되어 있지 않지만, 그래프 구조의 자연스러운 확장 방향을 보여줍니다.
안티패턴
고립 클러스터. 서로 링크되어 있지만 볼트의 나머지 부분과 연결되지 않은 노트 그룹입니다. Obsidian의 그래프 패널에서는 이런 그룹이 연결되지 않은 섬처럼 보입니다. 고립 클러스터는 MOC가 없거나 도메인 간 링크가 부족하다는 신호입니다.
태그 난립. 태그를 일관성 없이 사용하거나 지나치게 세분화된 태그를 너무 많이 만드는 경우입니다. 5,000개 노트에 고유 태그가 500개인 볼트는 태그 10개당 노트 1개꼴입니다. 이런 태그는 필터링에 유용하지 않습니다. 도메인 폴더에 매핑되는 상위 수준 태그 20-50개로 통합하세요.
링크는 많고 내용은 적은 노트. prose 없이 wiki-link만으로 구성된 노트입니다. chunker가 임베딩할 텍스트가 없기 때문에 이런 노트는 인덱싱 품질이 낮습니다. 링크된 노트들이 왜 관련되어 있는지 설명하는 컨텍스트를 최소 한 단락 추가하세요.
모든 것에 양방향 링크 사용. 모든 참조가 wiki-link일 필요는 없습니다. “OAuth”를 지나가듯 언급한다고 해서 [[OAuth 2.0 Overview]]가 필요한 것은 아닙니다. wiki-link는 클릭했을 때 유용한 컨텍스트를 제공하는 의도적이고 탐색 가능한 관계에만 사용하세요.
개발자 Workflow Recipes
Vault 검색을 일상적인 개발 작업과 결합하는 실용적인 workflow입니다.
아침 Context 로드
관련 context를 불러오며 하루를 시작하세요.
Search my vault for notes about [current project] updated in the last week
Retriever는 현재 작업 중인 프로젝트에 대한 최근 노트를 반환해, 어디까지 진행했는지 빠르게 다시 파악할 수 있게 해줘요. 어제의 commit 메시지를 다시 읽는 것보다 더 효과적입니다.
Coding 중 Research 캡처
기능을 구현하는 동안 editor를 벗어나지 않고 insight를 캡처하세요.
/capture "FastAPI dependency injection with async generators requires yield,
not return. The generator is the dependency lifecycle."
--domain programming
--tags fastapi,dependency-injection
캡처된 insight는 즉시 색인되어 나중에 검색할 수 있어요. 몇 달이 지나면 이런 작은 캡처들이 구현에 특화된 지식 corpus로 쌓입니다.
Project 시작
새 프로젝트나 기능을 시작할 때는 다음을 수행하세요.
- Vault를 검색하세요. “[technology/pattern]에 대해 내가 알고 있는 것은 무엇인가?”
- 상위 5개 결과를 검토해 이전 결정과 주의할 점을 확인하세요
- 해당 domain에 MOC가 있는지 확인하고, 없다면 새로 만드세요
- 실패 mode를 검색하세요. “[technology]의 문제점”
Vault Search로 Debugging하기
오류나 예상치 못한 동작을 만났을 때는 다음을 사용하세요.
Search my vault for [error message or symptom]
이전 debugging 노트에는 root cause와 fix가 들어 있는 경우가 많아요. 프로젝트 전반에서 반복되는 문제에 특히 유용합니다. Vault는 사용자가 잊은 내용을 기억해요.
Code Review 준비
PR을 review하기 전에 다음을 실행하세요.
Search my vault for patterns and conventions about [module being changed]
Vault는 review 대상 code와 관련된 이전 결정, architecture 제약, coding standards를 반환해요. Review는 단순히 diff만 보는 것이 아니라 조직의 축적된 지식을 바탕으로 진행됩니다.
Performance Tuning
이 섹션에서는 다양한 vault 크기와 사용 패턴에 맞는 최적화 전략을 다뤄요.
Index 크기 관리
| Vault 크기 | Chunks | DB 크기 | 전체 Reindex | 증분 |
|---|---|---|---|---|
| 500개 노트 | ~1,500 | 3 MB | 15초 | <1초 |
| 2,000개 노트 | ~6,000 | 12 MB | 45초 | 2초 |
| 5,000개 노트 | ~15,000 | 30 MB | 2분 | 4초 |
| 15,000개 노트 | ~50,000 | 83 MB | 4분 | <10초 |
| 50,000개 노트 | ~150,000 | 250 MB | 15분 | 30초 |
50,000개 이상의 노트에서는 다음을 고려하세요. - 더 빠른 embedding을 위해 batch size를 64에서 128로 늘리기 - 동시 access를 위해 WAL mode(기본값) 사용하기 - 사용량이 적은 시간에 전체 reindex 실행하기
Query 최적화
WAL mode. SQLite의 Write-Ahead Logging mode는 indexer가 쓰는 동안에도 동시에 읽을 수 있게 해줘요.
db.execute("PRAGMA journal_mode=WAL")
Indexer가 증분 update를 실행하는 동안 MCP 서버가 query를 처리해야 할 때 이 설정은 중요해요.
Connection pooling. MCP 서버는 query마다 새 connection을 여는 대신 database connection을 재사용해야 해요. WAL mode를 사용하는 하나의 오래 유지되는 connection만으로도 동시 read를 지원할 수 있습니다.
# MCP server initialization
db = sqlite3.connect(DB_PATH, check_same_thread=False)
db.execute("PRAGMA journal_mode=WAL")
db.execute("PRAGMA mmap_size=268435456") # 256 MB mmap
Memory-mapped I/O. mmap_size pragma는 SQLite에 database 파일에 memory-mapped I/O를 사용하라고 지시해요. 83 MB database라면 전체 파일을 memory에 mapping해 대부분의 disk read를 제거할 수 있습니다.
FTS5 최적화. 전체 reindex 후에는 다음을 실행하세요.
INSERT INTO chunks_fts(chunks_fts) VALUES('optimize');
이 작업은 FTS5의 내부 b-tree segment를 병합해 이후 search의 query latency를 줄여요.
Scaling Benchmark
Apple M3 Pro, 36 GB RAM, NVMe SSD에서 측정했습니다.
| Operation | 500개 노트 | 5K 노트 | 15K 노트 | 50K 노트 |
|---|---|---|---|---|
| BM25 query | 2ms | 5ms | 12ms | 25ms |
| Vector query | 1ms | 3ms | 8ms | 20ms |
| RRF fusion | <1ms | <1ms | 3ms | 5ms |
| Full search | 3ms | 8ms | 23ms | 50ms |
모든 benchmark에는 database access, query execution, result formatting이 포함돼요. MCP STDIO 통신의 network latency는 1-2ms가 추가됩니다.
Troubleshooting
Index Drift
증상: Search가 오래된 결과를 반환하거나 최근 추가된 노트를 찾지 못합니다.
원인: 노트를 추가한 뒤 incremental indexer가 실행되지 않았거나, 파일의 mtime이 update되지 않았습니다. 예를 들어 timestamp를 유지한 채 다른 machine에서 sync된 경우입니다.
해결: 전체 reindex를 실행하세요. python index_vault.py --full
Embedding Model 교체
증상: Embedding model을 변경한 뒤 vector search가 말이 안 되는 결과를 반환합니다.
원인: 이전 model의 old vector가 새 query vector와 비교되고 있습니다. Dimension이나 vector space semantics가 호환되지 않습니다.
해결: Indexer가 model hash mismatch를 감지해 자동으로 전체 reindex를 트리거해야 해요. 그렇지 않다면 database를 수동으로 지우고 reindex하세요.
rm vectors.db
python index_vault.py --full
FTS5 유지 관리
증상: 많은 incremental update 이후 FTS5 query가 부정확하거나 불완전한 결과를 반환합니다.
원인: 많은 작은 update 후 FTS5 내부 segment가 fragmented 상태가 될 수 있습니다.
해결: Rebuild하고 optimize하세요.
INSERT INTO chunks_fts(chunks_fts) VALUES('rebuild');
INSERT INTO chunks_fts(chunks_fts) VALUES('optimize');
MCP Timeout
증상: AI tool이 MCP 서버 timeout을 보고합니다.
원인: 첫 query가 model loading(lazy initialization)을 트리거하며, 이 작업은 2-5초가 걸립니다. AI tool의 기본 MCP timeout이 이보다 짧을 수 있습니다.
해결: Server startup 시 model을 미리 warm-up하세요.
# In MCP server initialization
retriever = HybridRetriever(db_path, vault_path)
retriever.search("warmup", limit=1) # Trigger model load
SQLite File Locks
증상: SQLITE_BUSY 또는 SQLITE_LOCKED 오류가 발생합니다.
원인: 여러 process가 동시에 database에 쓰고 있습니다. WAL mode는 동시 read를 허용하지만 writer는 하나만 허용합니다.
해결: 하나의 process(indexer)만 database에 쓰도록 하세요. MCP 서버와 hooks는 read만 해야 합니다. Concurrent write가 필요하다면 WAL mode를 사용하고 busy timeout을 설정하세요.
db.execute("PRAGMA busy_timeout=5000") # Wait up to 5 seconds
sqlite-vec가 로드되지 않음
증상: Vector search가 disabled되고 retriever가 BM25-only mode로 실행됩니다.
원인: sqlite-vec extension이 설치되지 않았거나, library path에서 찾을 수 없거나, SQLite version과 호환되지 않습니다.
해결:
# Install via pip
pip install sqlite-vec
# Or compile from source
git clone https://github.com/asg017/sqlite-vec
cd sqlite-vec && make
Extension이 로드되는지 확인하세요.
import sqlite3
db = sqlite3.connect(":memory:")
db.enable_load_extension(True)
db.load_extension("vec0")
print("sqlite-vec loaded successfully")
대형 Vault Memory 문제
증상: 대형 vault(50,000개 이상 노트)를 전체 reindex하는 동안 out-of-memory 오류가 발생합니다.
원인: Embedding batch size가 너무 크거나, 모든 file content가 동시에 memory에 로드됩니다.
해결: Batch size를 줄이고 file을 incremental하게 처리하세요.
BATCH_SIZE = 32 # Reduce from 64
또한 indexer가 모든 file을 memory에 로드하는 대신, file을 하나씩 처리하는지 확인하세요. 즉, 각 file을 읽고, chunking하고, embedding한 뒤 다음 file로 넘어가야 합니다.
Migration Guide
Apple Notes에서 이전하기
- “Export All” option(macOS)으로 Apple Notes를 export하거나
apple-notes-liberator같은 migration tool을 사용하세요 markdownify또는pandoc을 사용해 HTML exports를 markdown으로 변환하세요- 변환된 file을 vault의
00-inbox/폴더로 이동하세요 - 각 노트를 review하고 frontmatter를 추가하세요
- 노트를 적절한 domain 폴더로 이동하세요
Notion에서 이전하기
- Notion에서 export하세요. Settings → Export → Markdown & CSV
- Export 파일의 압축을 vault의
00-inbox/폴더에 푸세요 - Notion 전용 markdown artifact를 수정하세요.
- Notion은 checklist에
- [ ]를 사용합니다. 이는 standard markdown입니다 - Notion은 property table을 HTML으로 포함합니다. 이를 YAML frontmatter로 변환하세요
- Notion은 image를 relative path로 embed합니다. Image를 attachments folder로 복사하세요
- Standard frontmatter(
type,domain,tags)를 추가하세요 - Notion page link를 Obsidian wiki-links로 바꾸세요
Google Docs에서 이전하기
- Google Takeout을 사용해 모든 document를 export하세요
.docxfile을 markdown으로 변환하세요.pandoc -f docx -t markdown input.docx -o output.md- Batch convert를 실행하세요.
for f in *.docx; do pandoc -f docx -t markdown "$f" -o "${f%.docx}.md"; done - Vault로 이동하고, frontmatter를 추가한 뒤 folder로 정리하세요
일반 Markdown에서 이전하기(Obsidian 없음)
이미 markdown file directory가 있다면 다음을 수행하세요.
- 해당 directory를 Obsidian vault로 여세요. Obsidian → Open Vault → Open folder
- Directory가 version-controlled 상태라면
.obsidian/을.gitignore에 추가하세요 - Frontmatter template을 만들고 기존 file에 적용하세요
- 읽고 정리하면서
[[wiki-links]]로 노트를 연결하기 시작하세요 - Indexer를 즉시 실행하세요. Retrieval system은 첫날부터 작동합니다
다른 Retrieval System에서 이전하기
다른 embedding/search system에서 migration하는 경우:
- Vector를 migration하려고 하지 마세요. 서로 다른 model은 호환되지 않는 vector space를 생성합니다. 새 model로 전체 reindex를 실행하세요.
- Index가 아니라 content를 migration하세요. Vault file이 source of truth입니다. Index는 파생 artifact입니다.
- Migration 후 verify하세요. 답을 알고 있는 query 10-20개를 실행하고, 결과가 기대와 일치하는지 확인하세요.
변경 내역
| 날짜 | 변경 사항 |
|---|---|
| 2026-07-07 | 정확도 수정. MCPVault가 자체 프로젝트(npm @bitbonsai/mcpvault, repo bitbonsai/mcpvault)라는 점을 명확히 했으며, 현재 v0.12.1이고 중간 심각도 경로 필터 권고 2건(GHSA-9c83-rr99-vfwj, GHSA-j99q-93c9-h869)을 포함합니다. 이전 [^24] 링크는 잘못된 repo(MarkusPfundstein/mcp-obsidian)를 가리키고 있었습니다. MarkusPfundstein/mcp-obsidian의 상태를 수정했습니다. 이 프로젝트는 활발히 유지관리 중이며(2026년 5월 15일까지 커밋, search_by_tag/get_frontmatter 추가), “2025년 6월 이후 휴면” 상태가 아닙니다. 다만 여전히 태그가 지정된 릴리스는 없습니다. GitHub 커밋 기록, GitHub Security Advisories, npm을 기준으로 확인했습니다. |
| 2026-07-06 | 찾기 쉽게 편집 구조를 조정했습니다. “Quick Start: First AI-Connected Vault”의 제목을 Obsidian MCP Setup(앵커 #obsidian-mcp-setup)으로 바꾸고, MCP Server Architecture 섹션에서 가져온 “연결 후 Claude가 할 수 있는 일” 기능 요약(검색, 읽기, 목록 조회, 형식화된 컨텍스트, 쓰기는 hooks가 처리하는 읽기 전용 경계)을 추가했습니다. 새로운 사실은 없으며, 내부 링크를 업데이트했습니다. |
| 2026-06-10 | 버전 최신성 업데이트. Obsidian 1.13.1 desktop이 공개 채널에 도달했습니다(2026년 6월 9일). 1.13.0 대비 설정 UX와 CodeMirror 업그레이드가 포함되었지만, 주요 AI/자동화 변경은 없습니다. 본문의 현재 버전 참조를 1.13.0에서 1.13.1(공개, 2026년 6월 9일)로 옮겼습니다. |
| 2026-06-09 | 생태계 새로고침. MCP 2026-07-28 사양이 Release Candidate에 진입했습니다(2026년 5월 21일 발표). 출시 이후 가장 큰 MCP 개정으로, 상태 비저장 프로토콜 코어(initialize handshake와 Mcp-Session-Id 제거), MCP Apps(샌드박스 iframe의 서버 렌더링 HTML), Tasks의 실험적 코어에서 공식 확장으로의 승격, OAuth 2.0/OIDC 강화, 12개월 사용 중단 수명 주기 정책(최종 사양 2026년 7월 28일)이 포함됩니다. MCP Spec Evolution 노트의 추측성 “2026년 중반 잠정” 로드맵 표현을 구체적인 RC로 교체했습니다. sqlite-vec v0.1.10-alpha(2026년 3월 31일-5월 18일)는 brute-force KNN을 넘어서는 근사 최근접 이웃 인덱스 유형(rescore, 실험적 ivf, 디스크 기반 DiskANN)을 추가합니다. 0.1.10 라인이 아직 사전 릴리스이므로 곧 다가올/실험적 기능으로 표시했습니다. Obsidian 1.13.0 desktop(early access, 2026년 5월 28일)을 본문 전체의 현재 버전으로 업데이트했습니다. 이는 UX/보안/개발 도구 릴리스이며 새로운 AI/자동화 기능은 없습니다. |
| 2026-06-08 | 유지관리 확인. Model2Vec v0.8.2(2026년 5월 29일) 릴리스: 학습용 frozen-weights 옵션, multiword-token 수정, 학습 리팩터링, 비양자화 가중치 처리 수정이 포함된 유지관리 릴리스입니다. 각주를 업데이트했습니다. 기존 기준보다 더 최신인 항목은 없습니다. Obsidian 최신 버전은 1.13.0(5월 28일, 아래에 이미 문서화됨)으로 유지되고, sqlite-vec 안정 버전은 v0.1.9(v0.1.10은 여전히 alpha)로 유지되며, MCP 사양은 2025-11-25 개정판으로 유지됩니다. Model2Vec 버전 노트 외 본문 변경은 없습니다. |
| 2026-05-28 | Obsidian 1.13.0 desktop + 1.13.0 mobile(Catalyst early-access) 릴리스. Desktop: 자체 창에서 열리고 내장 검색과 키보드 탐색을 제공하는 개편된 Settings 패널, Obsidian URI가 작업 실행 전에 확인 대화상자를 표시하도록 변경, 네트워크 드라이브에서 HTML 리소스를 로드하기 전 새 경고 추가, Bookmarks 보기의 Search 추가, 향상된 에디터 이미지 처리, File Explorer / Properties / Sync 개선, 다수의 개발자 API 및 버그 수정. Mobile: 설정 가능한 대상 위치를 지원하는 새 iOS Share Sheet, 탭 전환기에서 탭 순서 변경, 분할 화면과 고정 사이드바 크기를 조정하는 태블릿 길게 누르기 제스처, Bases의 테이블 보기 열 크기 조정 메뉴 항목 추가, iOS 및 검색 버그 수정. AI 워크플로에 대한 의미: Obsidian URI 확인 대화상자는 URI 기반 MCP/agent 통합에 의도적인 게이트를 추가합니다. Bases 열 크기 조정 메뉴는 agents가 쿼리하는 vault 전면 인덱스로 Bases를 더 사용하기 쉽게 만듭니다. iOS Share Sheet의 설정 가능한 대상은 이미 주요 입력 경로로 문서화된 iPhone 캡처 경로를 Claude/Codex 파이프라인에 더 빠르게 연결할 수 있게 합니다. |
| 2026-05-06 | 소스 검증 기반 최신성을 새로고침했습니다. Smart Connections v4.5.0은 footer connections를 Core로 옮겼습니다. sqlite-vec v0.1.8/v0.1.9 안정 릴리스는 패키징과 DELETE 동작을 업데이트했습니다. Model2Vec v0.8.x는 tokenizer/영속성 내부 구조와 벤치마크 표를 업데이트했습니다. Obsidian CLI 연대기를 “1.12.7에서 CLI 도입”에서 “1.12.0에서 CLI 도입, 1.12.7에서 설치/런타임 패키징 개선”으로 바로잡았습니다. |
| 2026-04-27 | Web Clipper 4월 주기: 1.4.0(대화형 YouTube transcript UI + Reader에서 열기 기본값), 1.5.0(Highlights viewer), 1.6.0(Highlighter UX 전면 개편 + LinkedIn/Threads/Bluesky/Discourse/Medium용 Defuddle 0.18 소스 추출기), 1.6.1 + 1.6.2(Reader 및 Safari 수정). Web Clipper를 단순한 북마크 언급이 아니라 AI 워크플로의 기본 브라우저 측 입력 경로로 재구성했습니다. 해당 기간에 Obsidian desktop, Sync, Bases 릴리스는 없습니다. |
| 2026-04-16 | Smart Connections v4.3.0(graph view, 설정 가능한 dock, block-embedding 복구, Substrate cross-plugin env). 2026년 4월 AI-native plugin 흐름(Cortex, VaultSearch, LLM Wiki, Drift, EngramQuest, Hybrid Search MCP)을 문서화했습니다. MarkusPfundstein/mcp-obsidian을 maintenance-mode(마지막 커밋 2025년 6월)로 표시했습니다. Dataview는 휴면 상태이며, 새 작업에는 Bases가 후속 도구입니다. Obsidian CLI 1.12.7은 AI assistants를 위한 선호 브리지로 계속 유지됩니다. |
| 2026-04-01 | Obsidian CLI 섹션(v1.12의 AI 워크플로용 명령)을 추가했습니다. agent plugin 섹션(Claudian, Agent Client)을 추가했습니다. vault 구성을 위한 Bases core plugin을 문서화했습니다. plugin 수를 2,500+로 업데이트했습니다. iOS Share Extension을 입력 소스로 추가했습니다. 내장 agent plugins를 포함해 호환성 매트릭스를 업데이트했습니다. |
| 2026-03-30 | MCPVault v0.11.0: list_all_tags 도구, .base/.canvas 지원, @bitbonsai/mcpvault로 이름 변경. Obsidian Desktop v1.12.7은 더 빠른 터미널 상호작용을 위해 CLI 바이너리를 번들로 제공합니다. |
| 2026-03-23 | sqlite-vec v0.1.7 안정 버전을 문서화했습니다. vec0 테이블의 DELETE 지원, 페이지네이션을 위한 KNN 거리 제약이 포함됩니다. DiskANN 근사 최근접 이웃 인덱스가 향후 릴리스로 발표되었습니다. |
| 2026-03-07 | embedding model 비교에 potion-multilingual-128M(101개 언어, 2025년 5월)을 추가했습니다. sqlite-vec은 v0.1.7-alpha.10(CI/CD 수정, 기능 변경 없음)입니다. MCP 사양과 retrieval 기법이 최신임을 확인했습니다. |
| 2026-03-03 | MCP 사양 발전 내용을 업데이트했습니다(2025년 11월 출시: Streamable HTTP, .well-known, tool annotations). Model2Vec fine-tuning 및 BPE/Unigram tokenizer 지원을 추가했습니다. 커뮤니티 MCP server 비교 표를 추가했습니다. Smart Connections를 v4로 업데이트했습니다. |
| 2026-03-02 | 모델 비교에 potion-base-32M과 potion-retrieval-32M을 추가했습니다. 양자화/차원 축소 섹션을 추가했습니다. MCP 사양 발전 노트를 추가했습니다. |
| 2026-03-01 | 최초 릴리스 |
참고 문헌
-
Internet Vin, “22 commands I use with Obsidian and Claude Code,” 2026년 3월, x.com/internetvin/status/2026461256677245131. ↩
-
Nicopreme, “Visual Explainer” agent skill with slash commands, x.com/nicopreme/status/2023495040258261460. ↩
-
Cormack, G.V., Clarke, C.L.A., and Buettcher, S. Reciprocal Rank Fusion outperforms Condorcet and individual Rank Learning Methods. SIGIR, 2009. 순위 목록을 결합하는 매개변수 없는 방법으로 k=60을 사용하는 RRF를 소개합니다. ↩↩↩
-
OpenAI Embeddings Pricing. text-embedding-3-small: 100만 토큰당 $0.02. 전체 재색인 1회당 예상 vault 비용: 약 $0.30. ↩
-
van Dongen, T. et al. Model2Vec: Turn any Sentence Transformer into a Small Fast Model. arXiv, 2025. sentence transformer에서 정적 embeddings를 생성하는 증류 접근 방식을 설명합니다. ↩
-
potion-base-8M Model Card 및 Model2Vec results. 현재 공개된 표에서는 potion-base-8M이 51.32 Avg (All) / 51.08 Avg (MTEB)로 보고되며, all-MiniLM-L6-v2의 55.80 Avg (All) / 55.93 Avg (MTEB)와 비교하면 전체 작업 점수에서 약 92%를 유지합니다. ↩
-
Model Context Protocol Specification. AI 도구를 데이터 소스에 연결하기 위한 MCP 표준입니다. ↩
-
Model2Vec Potion Models, potion-base-32M, 및 potion-retrieval-32M. 현재 model card에서는 potion-base-32M이 52.83 Avg (All), potion-retrieval-32M이 검색 표에서 35.06으로 보고됩니다. ↩↩↩
-
Update on the Next MCP Protocol Release. 2025년 11월 릴리스에는 Streamable HTTP 전송, .well-known URL 탐색, 구조화된 도구 주석, SDK 계층 표준화가 포함되었습니다. 다음 릴리스는 2026년 중반으로 잠정 예정되어 있으며, 비동기 작업, 도메인별 확장, agent-to-agent 통신이 포함됩니다. ↩
-
Model2Vec Releases. v0.4.0 (2025년 2월): 학습/파인튜닝 지원. v0.5.0 (2025년 4월): 백엔드 재작성, 양자화, 차원 축소. v0.7.0 (2025년 10월): 어휘 양자화, BPE/Unigram tokenizer 지원. v0.8.0/v0.8.1 (2026년 3월): tokenizer 및 영속성 리팩터링, Python 3.9 지원 중단, MTEB V2 결과 업데이트, Windows 경로 호환성. v0.8.2 (2026년 5월 29일): 학습용 frozen-weights 옵션을 추가하고, multiword-token 수정, 학습 리팩터링, 비양자화 가중치 처리 수정을 포함한 유지보수 릴리스입니다. ↩↩
-
Smart Connections for Obsidian. Smart Connections v4: 로컬 우선 AI embeddings이며, 초기 색인 후 semantic search가 오프라인에서 작동합니다. ↩
-
potion-multilingual-128M. Minish Lab, 2025년 5월. 101개 언어 정적 embedding 모델이며, 다국어 정적 embeddings 중 가장 뛰어난 성능을 보입니다. 다른 potion 모델과 동일하게 numpy만 의존합니다. ↩
-
MCPVault —
bitbonsai/mcpvault. npm@bitbonsai/mcpvault, 최신 v0.12.1(2026-06-23 게시);MarkusPfundstein/mcp-obsidian과는 별개의 프로젝트이며, 이름이 바뀐 것이 아닙니다. v0.11.0(2026년 3월)은 frontmatter와 hashtags를 개수와 함께 스캔하는list_all_tags도구를 추가했고, dotted-folder 처리와.base/.canvas파일 지원을 개선했습니다. 중간 심각도의 GitHub Security Advisories 2건이 이 프로젝트의 경로 필터에 영향을 줍니다. GHSA-9c83-rr99-vfwj(제한 디렉터리가 vault 루트에서만 거부되고 중첩 위치에서는 거부되지 않음) 및 GHSA-j99q-93c9-h869(대소문자 및 끝의 점/공백 동등성을 이용한 deny-list 우회)입니다. v0.12.1 이상을 실행하세요. ↩ -
sqlite-vec v0.1.7 Release. 2026년 3월 17일. 안정 릴리스: vec0 가상 테이블의 DELETE 지원, 페이지네이션을 위한 KNN 거리 제약, fuzz testing 개선. DiskANN approximate nearest neighbor indexing은 향후 릴리스로 발표되었습니다. ↩↩↩
-
Introduction to Bases. Obsidian core plugin은 v1.9.10에서 도입되었습니다. frontmatter 속성을 필드로 사용해 vault 파일 위에 데이터베이스 같은 보기(표, 갤러리, 캘린더, kanban boards)를 제공합니다. 파일은
.base형식으로 저장됩니다. ↩ -
Obsidian Desktop v1.12.0 Changelog 및 Obsidian Desktop v1.12.7 Changelog. v1.12.0은 터미널 기반 vault 자동화를 위한 CLI를 도입했고, v1.12.7은 standalone binary, TUI, socket-file 동작으로 설치/런타임 패키징을 개선했습니다. CLI 문서도 참고하세요. ↩↩
-
Claudian. Claude Code을 vault 안의 AI 협업자로 임베드하는 Obsidian plugin입니다. sidebar chat, context-aware prompts, vision support, slash commands, permission modes를 제공합니다. ↩
-
Agent Client. Agent Client Protocol (ACP)을 통해 Claude Code, Codex CLI, Gemini CLI용 통합 인터페이스를 제공하는 Obsidian plugin입니다. note mentions, shell execution, action approval을 지원합니다. ↩
-
Obsidian iOS Changelog. 2026년 초 업데이트에는 다른 앱의 콘텐츠를 vault에 직접 저장하는 Share Extension, Daily Note 및 Bookmark widget 수정, View Note widget refresh 개선이 포함됩니다. ↩
-
MarkusPfundstein/mcp-obsidian. 활발히 유지보수되고 있습니다. 2026년 5월 15일까지 커밋이 있으며, 최근 작업으로
search_by_tag,get_frontmatter등의 도구 추가와 테스트 커버리지 확대가 포함되었습니다(저장소의 커밋 이력과tools.py기준으로 확인). 아직 태그된 릴리스는 없으므로 pinned commit에서 설치하세요. Local-REST-API 기반입니다. 포럼 논의(2026년 4월)에서는 새 설정에 대해 커뮤니티가 1급 Obsidian CLI bridge(1.12.x)로 이동하는 흐름이 보고되지만, mcp-obsidian은 기존 REST-API 배포에서 여전히 작동하고 업데이트되는 선택지입니다. ↩↩ -
Smart Connections v4.5.0 Release. 2026년 5월 5일. footer connections가 Core 기능이 되었습니다. 최근 v4 릴리스에는 connection list용 graph views, 설정 가능한 connection-panel 위치, 개선된 block-embedding 복구, Substrate cross-plugin state, transformer fallback 수정, 중복 connection 계산 감소도 포함됩니다. ↩
-
obsidianmd/obsidian-clipper releases — Web Clipper 버전-기능 매핑의 1차 출처입니다. 2026년 4월 주기: 1.4.0(4월 9일, YouTube transcript UI + Open in Reader 기본값), 1.5.0(4월 15일, Highlights viewer + Reader fade-in), 1.5.1(4월 15일, webpack compilation fix), 1.6.0(4월 21일, Highlighter UX + Defuddle 0.18 with LinkedIn/Threads/Bluesky/Discourse/Medium extractors), 1.6.1(4월 22일, Reader outline fixes + highlights search), 1.6.2(4월 23일, Safari embedded-mode clipboard fix). Mozilla Add-ons store와 Chrome Web Store에도 함께 등재되어 있습니다. ↩
-
sqlite-vec v0.1.8, sqlite-vec v0.1.9, sqlite-vec v0.1.10-alpha.3, 및 sqlite-vec v0.1.10-alpha.4. v0.1.8은 npm 패키징을 수정했고, v0.1.9는 12자보다 긴 metadata text columns에 대한 DELETE 버그를 수정했습니다. v0.1.10-alpha.3은 적절한
INSERT OR REPLACE INTO지원을 추가합니다. v0.1.10-alpha.4(2026년 5월 18일)는 새 ivf/diskann 기능을 사용하는vec0테이블에서ALTER TABLE RENAME이 실패하는 문제와 DiskANN의 cached-statement cleanup 버그를 수정합니다. 0.1.10 라인은 아직 prerelease입니다. ↩↩↩↩ -
MCP 2026-07-28 Specification Release Candidate. 2026년 5월 21일 발표되었으며, 최종 사양은 2026년 7월 28일에 공개됩니다. 출시 이후 가장 큰 MCP 개정입니다. stateless protocol core(
initializehandshake와Mcp-Session-Idheader 제거), MCP Apps(sandboxed client iframes 안에서 server-rendered HTML), Tasks가 experimental core에서 공식 확장(tasks/get,tasks/update,tasks/cancel)으로 승격, OAuth 2.0 / OIDC authorization hardening, 12개월 feature-deprecation lifecycle policy가 포함됩니다. ↩ -
Obsidian Desktop v1.13.0 Changelog. Early access, 2026년 5월 28일. UX/보안/developer-tooling 릴리스입니다. 검색과 키보드 탐색을 갖추고 별도 창에서 열리는 개편된 Settings panel, Obsidian URIs 실행 전 확인 대화상자, plugin developers를 위한 새 Settings API, flatpak 설치용 CLI 수정이 포함됩니다. 1.12.x CLI 표면을 넘어서는 주요 신규 AI/자동화 기능은 없습니다. ↩↩
-
Obsidian Changelog. Obsidian 1.13.1 desktop은 2026년 6월 9일 public channel에 도달했습니다. 1.13.0 대비 settings-UX 개선과 CodeMirror 업그레이드가 포함되었으며, 신규 AI/자동화 기능은 없습니다. ↩↩