본문으로 건너뛰기
김성연
AI Research Engineer, Braincrew
모든 저자 보기

jikji: 에이전트의 파일 탐색 비용을 줄이는 로컬 인덱스

· 약 15분
김성연
AI Research Engineer, Braincrew

Jikji Architecture: 로컬 파일 탐색 맵의 준비·검색·응답 3단계 구조

에이전트에게 로컬 폴더를 붙이면 가장 먼저 하는 일은 탐색이다. ls로 트리를 훑고, cat으로 파일을 열고, grep으로 내용을 뒤진다. 원하는 파일 하나를 찾을 때까지 이 과정을 몇 번씩 반복한다. 잘 정리된 코드베이스라면 몇 번의 명령으로 끝나지만, 사람의 다운로드 폴더나 회사 공유 드라이브처럼 파일명이 제각각이고 비슷한 자료가 여러 곳에 흩어진 곳에서는 이 탐색만으로 컨텍스트 예산이 줄줄 샌다. 그리고 이 낭비는 매 요청마다, 매 파일마다 다시 발생한다. Hermes 같은 에이전트에 LLM을 붙여 실제로 파일을 찾게 해본 적이 있다면 알 것이다. 파일 하나 찾을 때마다 토큰이 녹는다.

문제는 두 방향으로 더 나빠진다. 첫째, 에이전트가 쓰는 검색 도구가 grep·find 같은 기본 CLI라서, HWP나 PDF처럼 바이너리로 감싸인 문서는 내용 탐색이 아예 안 되거나 극단적으로 비효율적이다. 둘째, 이제는 문서만이 아니라 이미지·영상도 내용 기반으로 찾아야 하는데, 그러자고 임베딩 모델을 얹고 벡터 DB로 RAG를 세우는 건 구축이 까다롭고 컴퓨팅 자원도 낭비다.

이전 글에서 다룬 agentdir는 이 문제의 절반, 즉 "에이전트가 보기 좋은 파일 구조를 만드는" 쪽을 담당했다. 하지만 agentdir는 README에서 파일 파싱, 인덱싱, 검색을 명시적 비범위로 선을 그었다. 정확히 그 빈칸을 채우는 도구가 같은 팀(NomaDamas)이 만든 jikji다.

  • GitHub: NomaDamas/jikji
  • 역할: 에이전트가 반복 탐색 없이 로컬 파일을 찾도록, 미리 검색 인덱스와 파일 맵을 만들어두는 도구
  • 핵심 명령: jikji prepare ROOT (인덱싱) → jikji find ROOT "clue" --json (한 번의 탐색 호출)

jikji의 자기 소개는 이렇다.

"Non-destructive local file maps and instant search indexes for AI agents." 파일 하나 찾을 때마다 38,650 토큰, 57초, 11.7회 LLM 호출을 쓰던 raw agent 탐색을, 파일당 447 토큰·2.1초·1회 호출로 줄이는 비파괴 로컬 탐색 스킬.

이 글은 위 아키텍처 그림(레포 스냅샷 9ecc942 기준)을 따라가며 jikji가 무엇을 하는지 보고, 실제로 설치해 돌려본 결과를 정리한다.

jikji가 하는 일

jikji의 동작은 그림처럼 세 단계다.

  1. Prepare folder: jikji prepare ROOT. 폴더를 읽어 문서(PDF, HWP/HWPX, Office, 텍스트 등)를 파싱하고, 파일 카드·폴더 프로파일·에이전트 맵·각종 캐시를 만든다. 이미지·오디오·비디오는 opt-in 미디어 브리지로 처리한다.
  2. Build search map: 이 준비 과정이 만들어내는 산출물은 전부 .jikji/ 아래에 쌓인다. 파일명·경로 단서는 메타데이터 라우트로, 문서 내용 단서는 문서 텍스트 캐시로, 폴더 맥락은 에이전트 맵으로, 연관어·힌트는 위키 그래프로 각각 분리해서 인덱싱한다.
  3. Agent finds files: jikji find. 에이전트는 자연어 단서 하나를 던지고, 후보 슬레이트(candidate slate)·정답 경로(answer paths)·근거 팩(evidence pack)을 한 번에 받는다. 그리고 그 결과를 그대로 쓰거나(direct use), 실패 시 한 번 재시도하는 식으로 핸드오프한다.

핵심은 비파괴다. jikji는 사용자 파일을 옮기거나 이름을 바꾸거나 지우거나 재배치하지 않는다. 생성물은 전부 .jikji/ 디렉토리와 .jikji_agent_map.md로 격리된다. 그림 1단계에 non-destructive 뱃지가 붙은 이유가 이것이다.

비용 절감의 핵심

jikji가 빠르고 저렴한 이유는 단순하다. 비싼 탐색 작업을 에이전트가 물어보기 전에 미리 끝내두기 때문이다.

에이전트가 실시간으로 파일을 찾는 방식은 매번 같은 비용을 다시 치른다. 문서를 열 때마다 그 자리에서 파싱하고, 경로를 모르니 트리를 헤매고, 쿼리를 추측으로 바꿔가며 여러 번 검색한다. 이 모든 단계가 LLM 호출과 토큰으로 환산된다.

jikji는 이 비용을 prepare 시점으로 앞당긴다.

  • 탐색 시점 파싱 루프 제거: 문서는 prepare.jikji/doc_text/에 텍스트로 캐시된다. 검색 때 다시 열지 않는다.
  • 경로 방황 제거: 폴더 프로파일, 파일 카드, 중복 힌트, 라우트 행이 지저분한 트리를 에이전트가 읽을 수 있는 파일 맵으로 바꾼다.
  • 필드 분리 검색: 경로·파일명·폴더·확장자·본문·메타데이터를 따로 인덱싱해서, 명백한 경로 단서와 본문에만 있는 단서가 둘 다 잘 랭킹된다.
  • LLM 위키·지식 그래프: 소스마다 짧은 위키 요약 페이지를 만들어 두어, 에이전트가 큰 원본을 여는 대신 요약만 보고 판단할 수 있다. 노드(소스·폴더·용어·의도·중복)는 knowledge_graph.jsongraph_routes.jsonl로 미리 라우팅된다.

결과적으로 jikji find 한 번은, 여러 라우트에서 모은 후보를 하나의 슬레이트로 합쳐 에이전트에게 넘긴다. 에이전트는 이 top-k 슬레이트에 대해 한 번의 판단만 하면 되고, ls/find/grep/문서 열기/쿼리 추측에 채팅 턴을 쓰지 않는다.

결정론적 검색과 멀티미디어

인트로에서 짚은 두 번째 고민, "시맨틱 검색을 하려면 결국 임베딩과 벡터 DB가 필요하지 않나"에 대한 jikji의 대답이 흥미롭다. jikji는 임베딩 모델도, 벡터 DB도, 클라우드 RAG 스택도 쓰지 않는다. README의 표현을 그대로 옮기면 이렇다.

This is RAG-style retrieval context, not a mandatory vector DB or cloud RAG stack. Jikji's default index is local and deterministic: no embeddings, cloud parser, or LLM call is required to prepare or search.

대신 prepare 단계에서 결정론적 시맨틱 용어(deterministic semantic terms)를 뽑아 필드로 인덱싱한다. 랭킹도 임베딩 유사도가 아니라 필드별로 가중치를 둔 BM25 역색인(SQLite 기반, FTS5도 벡터도 아니다)으로 이뤄진다. 앞서 직접 돌려본 find 응답에서 후보가 뽑힌 이유(why)에 intent-tag, contextual-anchor 같은 항목이 섞여 있던 것이 이 계층이다. 순수 문자열 매칭을 넘어선 의도·맥락 기반 랭킹을, LLM 추론이나 임베딩 없이 결정론적으로 만들어낸다는 것이 핵심 주장이다. 준비·검색 과정에서 LLM은 전혀 호출되지 않고, LLM은 오직 에이전트가 반환된 슬레이트에서 최종 선택을 할 때만 쓰인다.

멀티미디어도 같은 틀 위에 있다. PDF·HWP/HWPX·Office·텍스트·자막·HTML·JSON/YAML·아카이브는 기본으로 파싱·인덱싱되고, 이미지·오디오·비디오는 opt-in OCR/ASR로 내용까지 인덱싱할 수 있다. 별도 임베딩 학습 없이, 미디어 브리지가 콘텐츠를 텍스트로 뽑아 같은 검색 인덱스에 태우는 방식이다.

# 이미지·오디오·비디오 내용 인덱싱은 opt-in (CPU/RAM을 쓰므로 기본 비활성)
pip install "jikji[media]"
jikji prepare ROOT --enable-media-index --media-index-max-mb 25

이 절의 내용(결정론적 시맨틱 랭킹의 품질, 멀티미디어 OCR/ASR 검색)은 레포의 설계·주장이며, 아래 "직접 돌려본 결과"에서 내가 검증한 범위와는 구분해서 읽어달라. 나는 텍스트 문서 탐색까지만 직접 확인했다.

기본 사용법

jikji가 노리는 사용 방식은 CLI를 직접 두드리는 게 아니라, 에이전트에게 스킬로 붙여두고 에이전트가 알아서 jikji find를 쓰게 하는 것이다. 저자가 공개한 안내처럼, Claude Code·Hermes 같은 에이전트에 이렇게 자연어로 시키면 된다.

GitHub 저장소 https://github.com/NomaDamas/jikji 에서 Jikji를 설치하고,
내 CLI 에이전트들이 jikji find를 바로 쓰도록 Jikji skill까지 연결해줘.

이 자연어 지시가 실제로 매핑되는 명령은 스킬 설치 계열이다. agent-skill-install은 공용 에이전트들에 jikji 스킬 지시문을 설치하고, 필요하면 특정 에이전트로 내보낼 수도 있다.

jikji agent-skill-install --agent all --json   # 공용 에이전트에 스킬 설치
jikji hermes-skill-install --json # Hermes 전용
jikji skill-export --dest /path/to/agent/skills/jikji/SKILL.md --json

이렇게 붙여두면 에이전트는 파일을 찾을 때 grep·find로 헤매는 대신 jikji find를 호출하고, 앞서 본 근거 팩과 핸드오프 정책을 그대로 받아 판단한다. (이 스킬 설치 명령들은 사용자의 실제 에이전트 설정 디렉토리에 파일을 쓰므로, 이 글에서는 동작만 소개하고 직접 실행하지는 않았다.)

직접 돌려본 결과

이 도구가 실제로 약속을 지키는지 보려고, 어지러운 코퍼스를 하나 만들어 macOS에서 돌려봤다. 현재 레포는 Rust 포팅 중인 모노레포라 crates/의 Rust CLI가 정식 구현이지만, python/jikji/가 동일 동작의 레퍼런스 구현으로 남아 있다. 이 환경엔 Rust 툴체인이 없어 Python 레퍼런스 구현(0.1.0)을 소스에서 설치해 측정했다. (주의: PyPI의 jikji 패키지는 전혀 다른 Flask 정적 사이트 생성기다. 이 도구가 아니다. 반드시 레포에서 설치해야 한다.)

코퍼스는 다운로드 폴더처럼 흩어진 파일 6개로 합성했다. 회의록_최종_진짜최종.md, report_v2_draft.txt, 고객사자료/제안서.md, random_notes.txt, temp/build_log.txt, 그리고 30MB짜리 더미 dataset.csv를 여러 하위 폴더에 뿌렸다.

1. prepare는 비파괴적이고 빠르다

$ jikji prepare ./corpus
Jikji prepared: .../corpus
- files=6 folders=4 deleted=0
- docs parsed/reused/failed=0/0/0

파일 6개 기준 약 0.17초에 끝났고, deleted=0이며 내가 만든 원본 6개는 그대로였다. 그림 2단계에 나온 산출물이 실제로 .jikji/ 아래에 생성됐다. search_index.sqlite, doc_text/, file_cards.jsonl, folder_profile.jsonl, knowledge_graph.json, graph_routes.jsonl, wiki/ 가 모두 확인됐다.

한 가지 그림에 안 나온 사실도 있다. prepare는 .jikji/ 외에 코퍼스 루트에 에이전트 포인터 파일도 쓴다. 내 경우 .jikji_agent_map.md 말고도 AGENTS.md, CLAUDE.md, .cursorrules가 루트에 새로 생성됐다. 원본을 수정·삭제하진 않지만, "생성물은 .jikji/에만"이라는 요약보다는 루트에 몇 개 파일이 더 놓인다는 점을 알고 쓰는 게 좋다.

2. find는 근거가 붙은 후보 슬레이트를 돌려준다

"분기 보고서 핵심 지표 토큰"이라는 자연어 단서로 --json을 붙여 호출했더니, 정답인 downloads/report_v2_draft.txt를 1순위로 랭킹하면서 이런 근거 팩을 돌려줬다.

{
"path": "downloads/report_v2_draft.txt",
"why": ["multi-token-overlap", "doc-type-match", "intent-tag",
"contextual-anchor", "body-coverage"],
"matched_terms": ["분기", "보고서", "핵심", "지표", "토큰"],
"evidence": ["핵심 지표: Hit@1 정확도, LLM 호출 수, 총 토큰 사용량"],
"next_read": { "kind": "original", "path": "downloads/report_v2_draft.txt" }
}

주목할 부분은 이 페이로드가 단순히 경로 목록이 아니라는 점이다. 왜 이 파일이 뽑혔는지(why), 어떤 용어가 맞았는지(matched_terms), 본문 어디에 근거가 있는지(evidence), 다음에 뭘 읽어야 하는지(next_read)까지 들어 있다. 에이전트는 이걸 보고 원본을 열지 않고도 판단할 수 있다.

3. 페이로드가 추가 탐색을 막는다

find --json의 전체 응답에서 가장 인상적이었던 건 핸드오프 정책 필드였다. 응답에는 이런 것들이 함께 들어온다.

"handoff_action": "direct_use",
"answerability": "answerable_from_payload",
"tool_call_policy": {
"stop_after_find": true,
"forbidden_tools": ["read_file", "search", "grep", "rg",
"find", "fd", "ls", "cat", "tree", "glob"],
"rerank_locked": true
},
"allowed_agent_tool_calls": 0,
"allowed_llm_calls": 0

즉 jikji는 "여기 답이 있으니, grep·ls·cat 같은 도구를 더 쓰지 말고 이 페이로드로 끝내라"고 에이전트에게 명시적으로 지시한다. 토큰 절감의 메커니즘이 여기 있다. 절감은 인덱스가 빨라서가 아니라, 에이전트의 추가 탐색 루프 자체를 막아서 나온다. 이 전체 응답은 약 5.3KB(대략 1,300 토큰 상당)였다. 파일 하나 찾자고 트리를 반복해서 훑는 것과 비교하면 작다.

4. 코퍼스를 키우면 차이가 벌어진다

위 6개짜리 코퍼스는 너무 작아서 raw와 jikji가 둘 다 쉽게 맞혔다(뒤의 벤치마크 절 참고). 그래서 지저분한 작업 폴더를 좀 더 크게 흉내 낸 합성 코퍼스(파일 530개, 폴더 42개)를 만들고, 정답을 미리 아는 자연어 질의 10개로 같은 결정론적 비교(jikji 인덱스를 쓰지 않는 raw 베이스라인 vs jikji)를 다시 돌렸다. 모델 없이 순수 검색 품질만 본 결과다.

지표raw (jikji 인덱스·파서 미사용)jikji
Hit@10.601.00
Hit@100.801.00
MRR0.6671.000

같은 폴더, 같은 질의에서 jikji가 정답 파일을 상위로 올리는 능력이 분명히 좋았다. 다만 이 표는 검색 품질의 재현이지 비용·시간의 근거는 아니다. 소형 코퍼스에서는 CLI 실행이나 함수 호출 오버헤드가 시간 지표를 왜곡하므로 시간은 비교하지 않았고, 단일 합성 코퍼스에 소표본(질의 10개)이라 절대값보다 방향으로 읽어야 한다. 또 raw 쪽이 파서 캐시를 쓰지 않아 바이너리 문서 본문을 못 읽는 비대칭이 있어, 이 차이의 상당 부분은 "미리 파싱해 둔 인덱스가 있느냐"에서 온다. 바꿔 말하면 그 인덱스가 바로 jikji가 파는 것이기도 하다.

재현 메모: macOS에서 jikji Python 레퍼런스(0.1.0)를 레포 소스(python/jikji)에서 설치해 측정했다. 1)~3)의 근거 팩 데모는 흩어진 파일 6개(더미 30MB CSV 포함) 코퍼스에서, 4)의 검색 품질 재현은 파일 530개 코퍼스에서 각각 돌렸다. 위 수치들은 단일 데모 기준이며, 절대값보다 동작의 성격에 주목해 달라. 1)~3)의 wc -c 기반 바이트→토큰 환산(≈4바이트/토큰)은 정밀 토크나이저 측정이 아니라 대략적 크기 감이다.

벤치마크 결과: 모델링과 실측

그림 오른쪽 아래의 "38,650 → 447 tokens", "56.7s → 2.1s"는 레포가 제시하는 벤치마크 수치다. 이 부분은 내가 직접 재현하지 못했다. 이 숫자는 실제 LLM 에이전트(Hermes)를 붙여 raw 탐색과 jikji 탐색을 비교하는 벤치마크에서 나오는데, 그러려면 LLM 에이전트 루프가 필요하다. 이 환경에서 돌린 내장 bench-run은 LLM이 없는 결정론적 렉시컬 베이스라인이라, 6개짜리 작은 코퍼스에서는 raw와 jikji가 둘 다 Hit@1=1.0으로 나와 토큰/호출 차이를 만들어내지 못했다. 즉 "왜 답이 뽑히는가"는 위에서 직접 확인했지만, "얼마나 싸지는가"의 절대 수치는 레포의 주장으로 받아들여야 한다.

레포가 밝힌 벤치마크(HippoCamp Fullset, 551건, 동일 Hermes 태스크 범위)는 다음과 같다. 아래 표와 배수는 모두 레포의 자체 측정치다.

지표raw HermesJikji find개선
케이스 수551551-
Hit@10.66970.7949상승
Hit@100.77860.7949상승
LLM 호출6,42055111.65× 감소
총 토큰21,296,278246,31686.46× 감소
벽시계 시간31,231.9s1,164.2s26.83× 감소

케이스당 평균으로 보면 raw는 11.7회 호출·38,650 토큰·56.7초를 썼고, jikji find는 1회 호출·447 토큰·2.1초를 썼다(그림의 그 숫자다). 정확도가 떨어지는 대가로 비용을 줄인 게 아니라, Hit@1이 0.67 → 0.79로 오히려 올랐다는 점이 이 벤치마크의 핵심 주장이다.

다만 이 표를 인용할 때 한 가지는 갈라 읽는 게 정확하다. 위 "Jikji find" 행은 레포가 실제로 측정한 라이브 실행이 아니라, 반환된 후보 슬레이트를 두고 LLM이 한 번에 정답을 고른다고 가정한 모델링 값이다. 레포의 docs/jikji-value-report.json에서 확인했다. 이 행은 정답 파일이 상위 후보 안에 있으면 judge가 항상 맞힌다고 보기 때문에 Hit@1과 Hit@10이 0.7949로 같고, 토큰은 하드코딩 상수와 길이 휴리스틱으로 추정하며(리포트 자신이 "실제 프로바이더 사용량이 아니라 추정치"라고 라벨링한다), 시간은 answer-pack 검색 시간(실측)에 호출당 1.5초를 가정해 더한 값이다.

같은 리포트에서 실제로 LLM 에이전트를 붙여 측정한 라이브 실행(jikji-discover)은 더 겸손하지만 그래도 분명한 승리다. Hit@1 0.688(raw 0.670), Hit@10 0.800(raw 0.779)에, 토큰은 약 2.8배(2,130만 → 760만), LLM 호출은 약 2.8배, 벽시계 시간은 약 2배(31,232s → 15,603s) 줄었다. 반대로 LLM을 전혀 쓰지 않는 순수 검색 모드(jikji-answer-pack)의 Hit@1은 0.514로 raw(0.670)보다 낮다. 정확도 상승은 후보 slate를 두고 한 번 판단하는 LLM 호출이 있어야 생긴다는 뜻이다. 정리하면 헤드라인의 "86배·27배·Hit@1 0.79"는 이 도구를 가장 이상적으로 썼을 때의 천장으로 읽고, 붙이면 바로 나오는 실측 절감은 토큰·시간 약 2~3배에 정확도 동률 수준으로 읽는 게 정확하다. 그리고 이 수치들은 NomaDamas가 단일 모델로 한 번 돌린 self-run이며 제3자 독립 재현은 아직 없다.

시리즈와 AutoRAG의 관계 (Braincrew의 해석)

같은 팀의 두 도구를 나란히 놓으면 분업이 선명하다.

  • agentdir (이전 글): 원본을 건드리지 않고 에이전트가 보기 좋은 파일 레이아웃(구조)을 만든다. 파싱·인덱싱·검색은 비범위.
  • jikji: 원본을 건드리지 않고 에이전트가 파일을 찾을 검색 인덱스·맵(내용)을 만든다. 구조 재배치는 하지 않는다.
  • MinSync (직전 글): 임베딩 벡터 인덱스를 운영 중에 신선하게 유지한다. 바뀐 청크만 다시 임베딩한다.

셋 다 "원본 비파괴 + 에이전트용 뷰를 따로 생성"이라는 같은 철학 위에 있지만 맡는 층이 다르다. agentdir는 배치, jikji는 발견, MinSync는 의미 인덱스의 신선도다. 특히 jikji와 MinSync는 이름이 비슷해 헷갈리기 쉬운데 겨냥하는 곳이 다르다. jikji는 임베딩 없이 결정론적으로 "어느 파일인지"를 싸게 좁히고, MinSync는 임베딩 벡터 인덱스를 최신으로 유지한다. 둘은 경쟁이 아니라 이어 붙는 관계에 가깝다. jikji가 지저분한 폴더를 몇 개 후보 파일로 좁히면, MinSync로 신선하게 유지한 벡터 RAG가 바로 그 몇 개 안에서 의미 기반 심층 검색을 하는 식이다. RAG/에이전트 파이프라인 관점에서 보면 jikji는 인제스트 이전의 탐색·후보 선정 레이어로 읽힌다. 에이전트가 코퍼스에서 관련 문서를 찾는 단계를, 매번 LLM으로 헤매는 대신 결정론적 로컬 인덱스로 대체하는 셈이다. 실제로 prepare 산출물에는 autorag_manifest.json(AutoRAG 연동 계약), chunk_map.jsonl 같은 RAG 친화적 아티팩트도 포함돼 있었다. 즉 jikji가 AutoRAG를 향해 설계되고 있다는 신호는 저장소에서 확인된다. 개발팀에 따르면 jikji는 앞으로의 AutoRAG 2.0에 포함될 계획이다.

다만 이 RAG 연결은 같은 팀이 만든 도구라는 사실과 각 도구의 설계 의도에 근거한 Braincrew의 해석이지, jikji가 스스로 주장하는 통합은 아니다. jikji README는 자신을 "AI 에이전트를 위한 로컬 파일 탐색 스킬"로 소개한다. 그리고 이 글을 쓰는 시점에 "AutoRAG 2.0"이라는 공개 릴리스나 로드맵이 발표된 것은 아니다. 확실한 것은 jikji가 AutoRAG 연동 계약을 이미 산출물로 emit한다는 사실이고, 통합의 출시는 아직 예고된 방향이다.

한계와 주의점

직접 써보며, 혹은 문서를 읽으며 확인한 지점들이다.

  • 정식 구현은 Rust CLI, 내가 돌린 건 Python 레퍼런스다. 두 구현은 parity 테스트로 맞춰지지만, 세부 동작이나 성능은 다를 수 있다. Rust CLI(cargo)로 쓰면 이 글의 설치 과정과 달라진다.
  • 벤치마크 수치는 재현하지 못했다. 앞서 밝혔듯 토큰/호출 절감의 절대값은 LLM 에이전트 벤치마크에서 나오며, 이 글에서는 레포 주장으로 인용했다.
  • prepare는 루트에도 파일을 쓴다. .jikji/뿐 아니라 AGENTS.md, CLAUDE.md, .cursorrules, .jikji_agent_map.md가 루트에 생성됐다. 원본은 안 건드리지만, 버전 관리 중인 폴더라면 .gitignore 처리를 고려해야 한다.
  • 인덱스는 스냅샷이다. 원본이 바뀌면 refresh로 다시 준비해야 하고, jikji는 인덱스가 낡았는지(freshness) 알려주되 검색 중에 몰래 재인덱싱하지는 않는다.
  • jikji는 두뇌가 아니다. 무엇을 찾을지 판단하는 건 여전히 에이전트다. jikji는 후보 슬레이트와 근거를 줄 뿐이고, 최종 선택은 에이전트의 몫이다.

정리

에이전트에게 로컬 폴더를 주면 매번 탐색부터 다시 시작한다. jikji는 그 비싼 탐색을 prepare 한 번으로 앞당겨 갚아두고, 이후에는 find 한 번으로 근거가 붙은 후보 슬레이트를 돌려준다.

직접 돌려 확인한 건 세 가지다. prepare는 원본을 건드리지 않고 그림 속 산출물을 실제로 만들어냈고(단, 루트에 포인터 파일 몇 개를 추가한다), find --json은 경로가 아니라 왜·무엇이·어디서 맞았는지가 담긴 근거 팩을 돌려줬으며, 그 페이로드는 에이전트에게 grep·ls·cat더 쓰지 말라고 명시적으로 지시했다. 토큰 절감의 정체는 빠른 인덱스가 아니라 바로 이 "그만 찾아라" 신호였다. 반면 "38,650 → 447 토큰" 같은 절대 수치는 이 환경에서 재현하지 못했고, 레포의 자체 벤치마크 주장으로 남겨둔다.

agentdir가 에이전트에게 정돈된 파일 구조를 줬다면, jikji는 그 위에서 파일을 찾는 비용을 미리 갚아주는 인덱스 레이어다. 둘 다 원본은 그대로 둔 채로.

References

MinSync: 파일이 바뀐 만큼만 다시 인덱싱하고, 청커로 그 비용을 좌우하기

· 약 16분
김성연
AI Research Engineer, Braincrew

MinSync GitHub repository screenshot

AI 에이전트나 RAG 시스템을 만들다 보면 반복해서 마주치는 문제가 있다.

처음에는 문서를 벡터 DB에 넣는 일이 어렵지 않다. 폴더를 읽고, 텍스트를 청크로 나누고, 임베딩을 만들고, 벡터 스토어에 저장하면 된다.

문제는 그다음이다. 파일이 하나 바뀌었을 때 어떻게 할 것인가?

전체를 다시 인덱싱하면 간단하지만 비효율적이다. 파일이 많아질수록 비용도 커지고, 시간도 오래 걸린다. 반대로 바뀐 파일만 처리하려면 변경 감지, stale 청크 삭제, manifest 관리, cursor 관리 같은 귀찮은 문제가 생긴다.

MinSync는 이 문제를 해결하기 위한 도구다.

  • GitHub: NomaDamas/MinSync
  • 역할: 로컬 텍스트 폴더를 계속 최신 벡터 인덱스로 유지하는 CLI
  • 핵심 상황: "문서는 계속 바뀌는데, 에이전트 검색 인덱스는 stale해지면 안 된다"

MinSync는 텍스트 파일을 대상으로 하는 manifest 기반 증분 벡터 인덱싱 CLI다. 쉽게 말하면, 디렉토리 안의 파일 변화를 추적하고, 바뀐 파일만 다시 청킹·임베딩해서 로컬 LanceDB 인덱스를 최신 상태로 유지해준다. 별도 벡터 DB 서버나 Python 런타임 없이 단일 Rust 바이너리로 동작하고, 라이선스는 MIT다.

이 글에서 다루는 MinSync는 Markr.AI 산하 오픈소스 조직 NomaDamas가 공개한 도구다. NomaDamas는 RAG AutoML 프레임워크 AutoRAG를 만든 팀이기도 하다. Braincrew는 RAG를 많이 다루는 입장에서 이 도구를 직접 뜯어보고 실측했다.

인덱스는 조용히 낡는다

RAG나 에이전트 memory 시스템에서 "인덱스 최신성"은 생각보다 중요하다.

전통적인 RAG 파이프라인에서 문서는 한 번 청킹되고 임베딩되어 벡터 DB에 들어가면 그대로 멈춰 있다. 그런데 원본 문서는 계속 바뀐다. 약관이 개정되고, 제품 사양이 업데이트되고, 회의록이 수정된다. 이때 임베딩이 갱신되지 않으면 인덱스는 과거의 문서를 가리킨 채로 남는다.

이 실패가 위험한 이유는 조용하기 때문이다. 코사인 유사도에는 "이 청크가 최신인가"라는 개념이 없다. 낡은 청크도 쿼리와 의미가 비슷하면 높은 점수로 상위에 올라온다. 검색은 정상으로 보이고, 지연도 없고, 에러 로그도 찍히지 않는다. 단지 사용자가 틀린(혹은 철 지난) 근거로 답을 받을 뿐이다.

특히 에이전트 작업 공간에서는 파일 변화가 자주 일어난다.

  • 문서가 계속 추가된다.
  • 에이전트가 중간 산출물을 만든다.
  • 로그나 요약 파일이 갱신된다.
  • 사용자가 README, 메모, 스펙 문서를 수정한다.
  • 생성된 작업 공간은 git으로 관리되지 않을 수도 있다.

가장 단순한 대응은 주기적으로 전체를 다시 임베딩하는 것이다. 그러나 코퍼스가 커질수록 이 방식은 느리고, 별도 인덱스 교체 없이 제자리에서 재인덱싱하면 그동안 검색 품질이 흔들릴 수 있으며, 임베딩 API 비용이 그대로 청구된다. 보통 실제로 바뀐 건 전체의 일부인데도 100%를 다시 계산하는 셈이다.

그래서 "바뀐 것만 다시 임베딩"이 필요하다. 문제는 이걸 깔끔하게 해주는 도구가 대부분 전제 조건을 요구한다는 점이다.

  • git diff 기반 파이프라인: 정확하고 빠르지만 git 저장소가 있어야 한다. 에이전트가 생성한 작업 폴더, 마운트된 데이터 디렉토리, 커밋 경계가 없는 환경에서는 쓸 수 없다.
  • LangChain Indexing API / LlamaIndex docstore: 콘텐츠 해시로 중복 임베딩을 막아주지만, 해당 프레임워크의 객체 모델 안에서 살아야 하고 상태를 저장할 별도 백엔드(record manager, docstore)가 필요하다.
  • cocoindex 같은 데이터플로 프레임워크: 증분 처리를 잘 해주지만, Python 프로젝트 설정과 자체 상태 스토어가 필요해 폴더 하나를 가볍게 동기화하려는 용도에는 셋업 부담이 더 크다.

MinSync는 이 빈칸을 노린다. git이 없고, 무거운 프레임워크는 끌어들이고 싶지 않은, 가벼운 로컬 폴더의 증분 인덱싱이다.

MinSync가 하는 일

MinSync는 특정 디렉토리를 스캔하면서 각 파일의 메타데이터와 청크 상태를 .minsync/ 디렉토리에 기록한다. 이후 minsync sync를 실행하면 이전 상태와 현재 상태를 비교해서, 바뀐 파일과 그 청크만 다시 처리하고 삭제된 파일의 stale 벡터는 정리한 뒤 로컬 LanceDB 인덱스를 최신 상태로 유지한다. 자세한 동작은 다음 절에서 단계별로 본다.

기록하는 정보는 대략 다음과 같다.

  • 파일 경로
  • 수정 시간 mtime
  • 파일 크기
  • SHA-256 content hash
  • 청크 상태
  • 처리 cursor

이 정보는 .minsync/ 디렉토리에 저장된다.

.minsync/
config.toml
manifest.json
cursor.json
txn.json
lock

각 파일의 역할은 다음과 같다. manifest.json은 마지막으로 확인한 파일 메타데이터와 content hash를 들고 있고, cursor.json은 마지막으로 완료된 처리 지점을 기록한다. txn.json은 진행 중인 transaction marker이고, lock은 동시에 여러 프로세스가 같은 인덱스를 건드리는 일을 막는다.

아래 그림은 MinSync의 처리 흐름과 전체 재인덱싱 대비 증분 동기화의 차이를 한 장으로 요약한 것이다.

MinSync incremental indexing overview

동작 구조

내부 동작은 다섯 단계로 정리된다.

  1. git-free 변경 감지: .minsync/ 디렉토리에 파일별 메타데이터(크기, mtime, SHA-256 콘텐츠 해시)를 manifest로 기록한다. 다음 스캔에서 크기와 mtime이 그대로면 해시 계산을 건너뛰고, 달라졌으면 그 파일만 다시 해싱한다. 큰 트리에서도 변경 감지가 싸게 끝난다. (mtime과 파일 크기가 함께 보존되는 복사·동기화 환경에서는 변경을 놓칠 수 있어, 그럴 땐 verify --fix나 전체 동기화로 보정한다.)
  2. 결정적 청크 ID: 청크마다 sha256(source_id, 경로, 청크 스키마, 청크 타입, 콘텐츠 해시, 중복 인덱스)로 ID를 만든다. 콘텐츠가 같으면 ID가 같으므로, 이미 존재하는 청크인지 즉시 판별된다. 벡터 DB에서 청크 ID가 계속 바뀌면 사실상 매번 새 데이터를 넣는 것과 비슷해지는데, MinSync는 이 문제를 정면으로 다룬다.
  3. 바뀐 청크만 임베딩: 이미 존재하는 청크는 메타데이터만 갱신하고 재임베딩하지 않는다. 콘텐츠 해시가 실제로 바뀐 청크만 임베더로 보낸다.
  4. mark-and-sweep로 stale 제거: 이번 실행에서 살아남은 청크에 실행 토큰을 찍고, 토큰이 찍히지 않은 옛 청크는 삭제한다. 문서에서 특정 정책 내용이 삭제됐는데 벡터 DB에 예전 청크가 남아 있으면 에이전트는 삭제된 정책을 계속 참조할 수 있는데, 이 방식이 그런 stale 벡터를 정리한다. 문서가 수정되거나 삭제돼도 인덱스가 현재 상태로 수렴한다.
  5. 크래시 안전성: 잠금 → 트랜잭션 마커 → 처리 → 벡터 스토어 flush → 커서 기록 → manifest 저장 순서로 진행한다. 커서와 manifest를 마지막에 커밋하므로, 중간에 죽어도 다음 minsync sync가 마지막으로 성공한 상태에서 안전하게 다시 시작한다. 임베딩 API 오류, 네트워크 timeout, rate limit, 프로세스 종료 같은 실패가 상태를 어중간하게 망가뜨리지 않는다.

저장소는 임베디드 LanceDB(IVF-HNSW-SQ ANN 인덱스)이고, 거리 메트릭은 cosine을 쓴다. 임베더는 OpenAI 호환(openai:)과 로컬 Hugging Face TEI(tei:) 두 가지다. 별도 벡터 DB 서버를 띄울 필요가 없다.

청커는 세 가지를 제공한다. 이 부분이 뒤에서 핵심이 되므로 정확히 짚고 간다.

  • recursive (기본값): 문단에서 문장, 라인 순으로 쪼갠 뒤 크기 예산까지 병합
  • chonkie: 구분자/크기 기반 chonkie-core 청킹
  • cdc: FastCDC 스타일 롤링 해시로 콘텐츠 기준 경계를 잡는 content-defined chunking

recursivechonkie는 문서 상단을 수정하면 하위 청크 경계가 밀릴 수 있고, cdc는 경계를 위치가 아닌 콘텐츠로 정하기 때문에 작은 수정이 보통 인접 청크에만 영향을 준다고 문서는 설명한다. 이 차이가 실제로 얼마나 나는지 직접 측정했다.

테스트 결과: 청커가 비용을 가른다

측정은 완전 오프라인으로 했다. OpenAI 호환 형식으로 응답하는 로컬 mock 임베딩 서버를 띄우고 MinSync의 base_url을 그쪽으로 연결해, API 키 없이 재임베딩량만 관찰했다. 측정 지표는 **편집 후 임베더로 실제로 다시 보낸 청크 수(재임베딩량)**다. 청킹과 청크 ID 계산은 임베더 호출 전에 끝나므로, 임베더가 mock이든 실제든 재임베딩 대상 청크 수는 동일하다.

코퍼스는 길이가 제각각인 문장 120개로 만든 단일 문서이고, 청크당 여러 문장이 묶이도록 max_chunk_size를 800으로 두었다. 세 가지 편집(끝에 한 문장 추가, 상단에 한 문장 삽입, 문서 중간 한 문장 수정)을 기본 청커와 CDC 청커에서 각각 측정했다. 단일 합성 문서 기준이므로, 절대 수치보다 청커 간 상대 차이에 주목해 달라.

편집 종류recursive (기본)cdc
끝에 추가 (append)11%9%
상단 삽입 (insert top)111%27%
중간 수정 (modify mid)67%9%

(재임베딩 비율 = 다시 임베딩한 청크 수 / 편집 전 청크 수. 그래서 새 청크가 추가되면 100%를 넘을 수 있고, 상단 삽입의 111%는 편집 전 9청크를 전부 다시 임베딩한 위에 새 청크 1개가 더해진 결과(10/9)다. 분모인 편집 전 청크 수는 청커마다 다르므로, 같은 편집이라도 비율의 기준점이 청커별로 다르다는 점에 유의해 달라.)

여기서 몇 가지가 분명해졌다.

첫째, 끝에 추가하는 편집은 어느 청커든 싸다(약 한 청크). 문제는 실제 문서가 끝에만 추가되지 않는다는 점이다. 약관 개정은 문서 중간을 고치고, 머리말은 상단에 들어간다. 진짜 비용 차이는 바로 그 흔한 편집(중간 수정 recursive 67% vs cdc 9%, 상단 삽입 recursive 111% vs cdc 27%)에서 났다. MinSync 문서가 설명하는 "상단을 수정하면 하위 청크 경계가 밀린다"가 기본 청커에서 글자 그대로 재현된 셈이다.

둘째, recursive의 드리프트는 예측이 안 된다. 같은 상단 삽입이라도 청크 크기를 250으로 줄이면 재임베딩이 6%로 작았다가(별도 설정 실측), 더 긴 코퍼스에서는 111%까지 튀었다. CDC는 현실적인 길이의 코퍼스에서는 편집 종류와 설정을 바꿔도 대체로 2~33% 범위에 머물렀다(별도 설정 실측, 아래에서 보듯 아주 짧고 균일한 코퍼스에서는 이 이점이 사라진다). RAG 운영에서 의미 있는 건 평균값이 아니라 "한 번의 수정이 최악의 경우 얼마까지 비용을 일으키는가"의 상한이다.

셋째, 그래서 비용을 좌우한 건 증분 기능 자체가 아니라 청커 선택이었다. 기본값인 recursive로만 쓰면 증분 인덱싱의 가치를 절반밖에 누리지 못할 수 있다. 청커는 minsync init --chunker cdc로 지정하거나 .minsync/config.toml[chunker] id를 바꿔 고르는데, CDC가 비용을 실제로 눌러주는데도 README에서 강조되지 않아 직접 돌려봐야 드러나는 지점이었다. 단, 청커를 바꾸면 청크 스키마가 달라지므로 한 번은 minsync sync --full로 전체 재인덱싱이 필요하다. CDC 전환 자체는 1회성 전체 재임베딩 비용을 동반하고, 절감은 그 이후 증분 갱신에서 나타난다. 또 CDC는 콘텐츠 기준으로 경계를 잡아 문장이나 의미 단위와 어긋날 수 있으니, 비용과 별개로 retrieval 품질은 도입 전에 따로 확인하는 게 안전하다.

사실 첫 측정에서는 짧고 균일한 코퍼스를 써서 recursive와 cdc가 둘 다 약 90%로 나왔고, "CDC 효과가 없네"라는 잘못된 결론에 닿을 뻔했다. 코퍼스를 더 길고 다양하게 바꿔 다시 재면서야 위 표의 차이가 드러났다. 즉 CDC의 이점은 코퍼스 특성(문서 길이, 청크 크기)에 민감하므로, 단일 코퍼스 결과만으로 일반화하지 않는 것이 안전하다. 더 긴 코퍼스를 대표값으로 본 이유는 실제 문서가 대체로 그 길이에 가깝기 때문이고, 이 판단을 뒤집을 데이터(짧은 문서가 다수인 코퍼스)가 있다면 결론은 달라질 수 있다.

그럼 대규모 수정은? 자연스럽게 따라오는 질문인데, 한 파일에서 여러 청크가 한꺼번에 바뀌면 당연히 그 청크들은 모두 다시 임베딩된다. MinSync의 역할은 바뀌지 않은 청크를 불필요하게 다시 임베딩하지 않는 것까지다.

기본 사용법

설치는 install script를 사용하는 방식이 권장된다. Cargo로 직접 설치하거나 GitHub Releases의 프리빌트 바이너리를 받아 써도 된다.

curl -fsSL https://raw.githubusercontent.com/NomaDamas/MinSync/main/scripts/install.sh | sh
# 또는
cargo install minsync

OpenAI 임베딩을 사용할 경우 API key를 설정한다.

export OPENAI_API_KEY="..."

기본 사용 흐름은 init → sync → query로 이어진다. 인덱싱할 디렉토리에서 초기화한 뒤, 처음 한 번 전체를 만들고, 이후에는 변경분만 반영하면서 검색하는 식이다.

agent-workspace를 semantic search 가능하게 만들기
# 1. 에이전트가 작업하는 폴더로 이동
cd ~/projects/customer-research-agent

# 2. MinSync 초기화
minsync init

# 3. 처음 한 번 전체 인덱싱
minsync sync --full

# 4. 에이전트가 만든 산출물을 검색
minsync query "지난 회의에서 결정된 보안 요구사항" --k 5

# 5. 이후에는 변경분만 반영
minsync sync

예를 들어 에이전트가 notes/meeting.md에 회의 요약을 추가하고, spec/security.md에서 인증 정책을 수정했다고 하자. 다음 minsync sync에서는 전체 프로젝트를 다시 임베딩하지 않고, 바뀐 파일과 그 청크만 다시 처리한다. 그 뒤에는 바로 이렇게 물어볼 수 있다.

minsync query "인증 정책에서 MFA는 필수인가?" --k 3

이 흐름이 좋은 이유는 단순하다. 에이전트가 파일을 계속 만들고 고치는 환경에서도, 검색 인덱스를 매번 처음부터 다시 만들 필요가 없다.

이 밖에 파일 변경을 계속 감지하려면 minsync watch, 인덱스 상태를 보려면 minsync status, 일관성 검증과 복구는 minsync verify --fix를 쓴다.

로컬 임베딩

OpenAI 임베딩을 사용할 수도 있지만, 로컬 임베딩 서버를 붙일 수도 있다. 문서도 쿼리도 외부로 나가지 않는 파이프라인이 필요할 때 유용하고, 데이터 반출이 제한되는 온프렘 환경에서 특히 잘 맞는다.

예를 들어 Hugging Face Text Embeddings Inference, TEI를 사용할 수 있다.

brew install text-embeddings-inference
text-embeddings-router --model-id intfloat/multilingual-e5-small --port 8080 --dtype float32

서버 상태를 확인한다.

curl http://localhost:8080/health

MinSync를 TEI 임베더로 초기화한다.

minsync init --embedder tei:intfloat/multilingual-e5-small

설정 파일에서 dimension과 prefix를 맞춘다.

[embedder]
id = "tei:intfloat/multilingual-e5-small"
base_url = "http://localhost:8080"
query_prefix = "query: "
passage_prefix = "passage: "

[vectorstore.options]
dimension = 384

이후 전체 sync와 query를 실행한다.

minsync sync --full
minsync query "검색어" --k 5

bge-m3, multilingual-e5 같은 모델을 연결하면 되고, UTF-8 텍스트라면 한국어, 일본어, 중국어 모두 그대로 인덱싱된다.

활용 예시

써본 기준으로, MinSync가 잘 맞는 상황은 다음과 같다.

1. 에이전트 작업 폴더와 개인 문서 검색

에이전트가 logs/, decisions/, drafts/ 같은 폴더에 작업 기록을 계속 남기거나, 팀 문서·개인 메모가 Markdown/Text 중심으로 쌓이는 구조라면 MinSync를 가벼운 로컬 검색 레이어로 붙일 수 있다. 둘은 본질적으로 같은 사용법이다. git 히스토리가 없어 git diff 방식이 통하지 않는 작업 폴더라도, 무거운 프레임워크 없이 코드 0으로 처리한다는 점이 강점이다.

cd ~/agent-workspaces/skt-tax-agent
minsync init --chunker cdc
minsync watch

이후 별도 RAG 서버 없이도 로컬에서 바로 질문할 수 있다.

minsync query "세무 신고 자동화에서 홈택스 연동 리스크는 뭐였지?" --k 5

개인 메모 폴더라면 watch 대신 한 번 전체 인덱싱한 뒤 필요할 때 검색하는 식으로 써도 된다.

cd ~/Documents/braincrew-notes
minsync init
minsync sync --full
minsync query "에이전트 평가 지표 관련해서 전에 적어둔 내용" --k 5

2. CI나 cron에서 주기적으로 최신화

항상 watch를 켜두기 어렵다면 cron이나 CI에서 주기적으로 sync만 실행해도 된다. 크래시 세이프 커서 덕분에 실패한 실행은 그냥 다시 돌리면 된다.

*/30 * * * * cd /path/to/workspace && minsync sync

이렇게 하면 "검색은 빠르게 하되, 임베딩 비용은 변경분에만 쓰는" 구조를 만들 수 있다.

3. 로컬/온프렘 한국어 RAG

tei: 백엔드로 로컬 임베딩을 붙이면 문서도 쿼리도 외부로 나가지 않는 파이프라인이 된다. 데이터 반출이 제한되는 환경에서 로컬 semantic search를 붙이고 싶을 때 적합하다. 연결 방법과 다국어 인덱싱은 앞의 "로컬 임베딩" 절에 정리해 두었다.

CocoIndex와 비교

CocoIndex GitHub repository screenshot

비슷한 문제의식으로 볼 수 있는 도구로 CocoIndex가 있다. 참고로 CocoIndex는 직접 실측하지 않고 공식 문서 기준으로 정리했다.

CocoIndex는 같은 문제를 더 큰 범위에서 다룬다. codebase, meeting notes, inbox, Slack, PDF, video 같은 여러 source를 AI 에이전트가 사용할 수 있는 최신 context로 바꾸는 프레임워크다. 핵심은 "target은 source의 함수"라는 관점이다.

대략 이런 모델이다.

TargetState = Transform(SourceState)

개발자는 source를 읽고, 변환하고, target state를 선언한다. 그러면 CocoIndex가 어떤 입력이 바뀌었는지, 어떤 처리 결과를 재사용할 수 있는지, target에서 무엇을 insert/update/delete 해야 하는지를 추적한다.

문서에서 자주 나오는 비유는 React와 spreadsheet다. React에서 UI를 직접 조작하기보다 state에 대한 함수로 선언하듯, CocoIndex에서는 target DB를 직접 한 줄씩 맞추기보다 source state에 대한 결과를 선언한다. 파일이 추가되면 해당 파일의 processing component가 생기고, 파일이 수정되면 영향을 받은 target state만 바뀌고, 파일이 삭제되면 연결된 target state도 제거된다.

CocoIndex가 MinSync와 크게 다른 지점은 코드 변경까지 증분 처리의 대상으로 본다는 점이다. 함수에 memo=True를 붙이면 입력과 로직의 fingerprint를 기준으로 결과를 재사용할 수 있다. 파서 옵션을 바꾸거나 변환 함수를 수정했을 때도 모든 것을 무작정 다시 돌리는 대신, 어떤 계산을 다시 해야 하는지 판단하는 쪽에 가깝다.

운영 모드도 나뉜다. 기본 업데이트는 catch-up 방식으로 한 번 실행되고 끝난다. 반면 live update는 프로세스를 계속 띄워두고 source의 변경을 감지한다. 주기적으로 source를 확인할 수도 있고, S3 알림이나 Google Drive 변경 polling처럼 source별 change capture를 활용할 수도 있다. "밤마다 한 번 최신화"가 아니라 "바뀌면 따라간다"에 가까운 구조다.

지원 범위도 넓다. local filesystem, S3, Google Drive 같은 source와 Postgres, LanceDB, Qdrant, Neo4j 같은 target을 연결할 수 있다. 그래서 CocoIndex는 단일 폴더 검색 도구라기보다, AI 제품을 위한 증분 데이터 레이어에 가깝다.

정리하면 이렇게 볼 수 있다.

구분MinSyncCocoIndex
형태그냥 실행하는 단일 CLI 도구위에 코드를 얹는 프레임워크
소스로컬 텍스트 폴더 1개local fs, S3, Google Drive 등 다중
타깃임베디드 LanceDBPostgres, LanceDB, Qdrant, Neo4j 등 다중
상태.minsync/ 파일 (git, 외부 DB 불필요)자체 내부 상태 스토어
셋업바이너리 하나, 코드 0Python 프로젝트 + 파이프라인 작성
적합한 경우에이전트 작업 공간, 로컬 문서 검색, 빠른 semantic index다양한 source/target, live update, knowledge graph

흥미로운 건 차이만이 아니라 공통점이다. 둘 다 콘텐츠 해시로 변경을 감지하고, 둘 다 바뀐 부분만 재처리하고, 둘 다 크래시 세이프하다. 증분 인덱싱의 설계가 비슷하게 수렴하고 있다는 신호로 읽힌다.

그래서 둘은 경쟁 관계라기보다 크기와 추상화 수준이 다른 도구다. 여러 소스를 다양한 타깃으로 흘려보내며 live 갱신까지 필요한 플랫폼을 만든다면 CocoIndex가, 폴더 하나를 코드 없이 가볍게 동기화하고 싶다면 MinSync가 맞다. 둘을 이어서 쓸 수도 있다. 예를 들어 CocoIndex로 PDF와 사내 문서를 Markdown이나 구조화된 text로 변환하고, 특정 팀의 로컬 작업 공간에서는 그 결과물을 MinSync로 빠르게 검색하게 할 수 있다.

AutoRAG와의 관계 (Braincrew의 해석)

이 도구가 어디에서 왔는지도 짚어둘 만하다. MinSync를 만든 NomaDamas는 RAG AutoML 프레임워크 AutoRAG를 만든 팀이기도 하다. AutoRAG 저장소는 Marker-Inc-Korea/AutoRAG이고, NomaDamas/Markr.AI 진영에서 만든 RAG AutoML 프레임워크로 현재 수천 stars 규모다. AutoRAG는 본질적으로 오프라인 도구다. 내 데이터로 수많은 RAG 구성을 평가해 "이 코퍼스에는 이 청커, 이 임베더, 이 인덱스가 최적"이라는 정답을 찾아준다.

그런데 그 최적 구성을 찾은 뒤, 운영 환경에서 문서가 계속 바뀔 때 인덱스를 어떻게 신선하게 유지할지는 AutoRAG의 범위 밖이다. AutoRAG가 코퍼스에 맞는 최적 구성을 골라준다면, MinSync는 그 구성으로 만든 인덱스를 운영 중에 신선하게 유지하는 쪽이다. 둘의 범위가 자연스럽게 맞물릴 여지가 있다.

한 가지 분명히 해둘 점이 있다. MinSync 저장소는 AutoRAG를 직접 언급하지 않으며, 위 연결은 같은 팀이 공개한 방향성과 각 도구의 설계 의도에 근거한 Braincrew의 해석이다. 두 도구가 어떻게 맞물릴지는 앞으로의 릴리스에서 더 구체화될 영역이다.

한계와 주의점

운영에서 미리 알아둘 한계는 다음과 같다.

  • 기본 청커(recursive)는 상단/중간 편집에서 재임베딩 비용이 크다. 비용이 중요하면 CDC 청커를 고려하되, retrieval 품질은 별도로 검증한다.
  • 짧고 균일한 코퍼스에서는 CDC 이점이 거의 없다. 코퍼스가 그런 특성이면 굳이 CDC로 바꿀 이유가 적다.
  • mtime과 파일 크기가 함께 보존되는 복사·동기화 환경에서는 변경을 놓칠 수 있다. 이럴 땐 verify --fix로 보정한다.
  • 텍스트 전용이다. PDF, DOCX, XLSX, 이미지 같은 바이너리 파일에서는 내용을 추출하지 않는다.

이런 파일들은 .minsyncignore에 넣는 것이 좋다.

target/
*.png
*.pdf
*.docx
*.xlsx

검색 대상에서 PDF나 DOCX가 큰 비중을 차지한다면 MinSync 단독으로는 부족하고, 별도의 extraction pipeline으로 텍스트를 먼저 뽑은 뒤 그 결과물을 MinSync가 인덱싱하도록 구성해야 한다. MinSync는 작업 디렉토리나 에이전트 sandbox 안의 텍스트 지식, 개발 문서, Markdown 노트, 코드 주변 설명, 에이전트 memory처럼 자주 바뀌지만 구조가 단순한 텍스트를 빠르게 검색 가능하게 만드는 데 초점이 맞춰져 있다.

정리

테스트하며 가장 크게 남은 발견은, 재임베딩 비용을 좌우한 게 청커 선택이었다는 점이다. CDC 청커가 비용을 실제로 눌러주는데도 README나 소개만 읽어서는 이 점이 잘 드러나지 않았고, 직접 측정하고 나서야 보였다.

도구의 동작은 단순하다. 파일 변경을 manifest로 추적해 git 없이도 바뀐 텍스트만 다시 처리하고, mark-and-sweep로 stale 벡터를 정리하며, 크래시 세이프 커서로 실패한 sync를 안전하게 재시도한다.

언제 쓰면 좋은가로 정리하면 이렇다. 복잡한 production RAG 파이프라인이나 여러 source/target, live 갱신이 필요하면 CocoIndex 같은 프레임워크가 더 적합하다. 반면 로컬 문서 폴더, 에이전트 작업 공간, 텍스트 기반 지식 저장소를 빠르게 semantic search 가능하게 만들고 싶다면 MinSync가 훨씬 가볍고 직접적인 선택지다. 그리고 RAG 인덱스의 최신성을 고민하는 팀이라면, 기본값 대신 CDC 청커로 한 번 측정해보길 권한다.

References

agentdir: 원본은 그대로 두고 에이전트에게 작업하기 좋은 파일 구조를 따로 만들어주기

· 약 16분
김성연
AI Research Engineer, Braincrew

agentdir GitHub repository screenshot

AI 에이전트를 사용할 때 자주 마주치는 문제가 있다. 파일은 많은데 정리가 되어 있지 않고, 폴더 구조는 사람이 과거에 일하던 방식 그대로 남아 있다. 그런데 우리는 그 상태 그대로 에이전트에게 "이 문서들 보고 정리해줘", "이 자료 기반으로 분석해줘", "관련 파일 찾아서 작업해줘"라고 요청한다.

Jeffrey Kim의 LinkedIn 글 표현처럼, 에이전트도 정리가 안 된 파일과 폴더를 보고 싶어하지 않는다. 정확히는, 그런 곳에서는 일을 잘하기 어렵다. 인간도 어지러운 바탕화면에서 원하는 파일을 찾는 데 시간을 쓰듯이, 에이전트도 지저분한 작업 공간에서는 탐색과 추론 비용을 낭비한다. 파일명이 제각각이고 비슷한 자료가 여러 위치에 흩어져 있으면, 에이전트는 컨텍스트를 잡기 전에 탐색에만 많은 토큰과 시간을 쓴다.

이때 사용할 수 있는 도구가 agentdir이다. Markr.AI 산하 오픈소스 조직 NomaDamas가 공개한 Rust 기반 프로젝트인데, NomaDamas는 RAG AutoML 프레임워크 AutoRAG를 만든 팀이기도 하다.

  • GitHub: NomaDamas/agentdir
  • 소개 글: Jeffrey Kim LinkedIn post
  • 역할: 원본 폴더는 건드리지 않고 에이전트용 파일 구조를 따로 만드는 도구
  • 핵심 상황: "사람에게 편한 폴더 구조"와 "에이전트가 일하기 좋은 폴더 구조"가 다를 때

agentdir는 원본 파일은 그대로 둔 채, 에이전트가 보기 좋은 별도의 파일 트리를 만들어주는 도구다. 사람이 쓰는 폴더 구조와 에이전트가 쓰는 폴더 구조를 분리하는 인프라라고 보면 된다.

agentdir가 하는 일

agentdir는 기존 파일을 실제로 옮기지 않고, 특정 작업에 맞는 가상의 폴더 구조를 만들어준다.

team-files/
2023/
2024/
screenshots/
회의록_최종진짜최종.md
report_v2.pdf
고객사자료/
temp/

사람 입장에서는 익숙한 구조일 수 있다. 하지만 에이전트에게는 "무엇이 중요한 파일인지", "분석 대상은 어디인지", "보고서와 회의록은 어떻게 연결되는지"를 파악하기 어렵다.

agentdir를 쓰면 같은 파일들을 에이전트 작업 목적에 맞춰 이렇게 노출할 수 있다.

workspace/
reports/
q1-report.pdf
meeting-notes/
customer-meeting.md
customer-materials/
proposal.pdf
references/
screenshots/

중요한 점은 원본 파일은 움직이지 않는다는 것이다. 에이전트가 보는 구조만 새로 만들어진다. 단순 복사본을 만드는 방식도 아니기 때문에, 지원되는 파일시스템에서는 대용량 파일을 중복해서 들고 있을 필요가 없다. 내 바탕화면이나 공유 폴더는 그대로 복잡해도, 에이전트에게는 잘 정리된 작업 공간만 보여줄 수 있다.

아래 그림은 원본 폴더, 매핑, 읽기 전용 작업 뷰가 어떻게 분리되는지 요약한 것이다.

agentdir workspace views overview

즉, agentdir는 다음과 같은 일을 한다.

  • 원본 디렉토리를 가상 파일 트리에 매핑한다.
  • 에이전트가 읽기 좋은 구조로 파일을 재배치한다.
  • 원본 파일은 그대로 유지하고, 가상 뷰는 읽기 전용으로 만든다.
  • 변경 사항을 감지해 작업 공간에 반영한다.
  • macOS, Linux, Windows에서 동작한다.
  • APFS, Btrfs, XFS 같은 CoW 지원 파일시스템에서는 대용량 파일도 중복 복사 비용 없이 다룰 수 있다.

왜 필요한가

에이전트를 쓸 때 많은 사람들이 모델 성능에만 집중한다. 하지만 실제 작업 품질은 모델만으로 결정되지 않는다.

에이전트가 어떤 파일을 보고, 어떤 순서로 탐색하고, 어떤 구조 안에서 판단하는지가 결과에 큰 영향을 준다. 에이전트를 실제 프로젝트나 데이터 폴더에 붙이면 처음 하는 일이 탐색이다. 깊은 트리를 ls로 훑고, 파일을 cat으로 열고, grep으로 검색한다. 잘 정리된 코드베이스라면 괜찮지만, 사람의 다운로드 폴더처럼 빌드 산출물, 대용량 데이터, 임시 파일이 뒤섞인 곳에서는 탐색만으로도 컨텍스트 예산이 줄줄 샌다. 정작 필요한 파일은 그 잡음 속 어딘가에 있다.

두 번째 문제는 덜 이야기되지만 더 위험하다. 에이전트가 탐색하는 그 폴더는 보통 에이전트가 수정도 할 수 있는 폴더다. 잘못된 writerm 한 번이 사용자의 원본을 건드린다. 사람이 쌓아온 디렉토리 구조나 파일을 에이전트가 마음대로 바꾸길 원하는 경우는 드물다. 파일명 변경, 폴더 이동, 삭제 같은 작업은 한 번 잘못되면 되돌리기 어렵고, 공유 드라이브나 고객사 자료라면 더더욱 조심해야 한다.

그렇다고 정리된 사본을 새로 복사하면, 멀티 GB짜리 PDF나 데이터셋이 레이아웃 수만큼 그대로 중복된다. 결국 "에이전트에게 깔끔하고 안전한 뷰를 주고 싶다"와 "원본을 옮기거나 복제하기는 싫다"가 충돌한다.

그래서 agentdir는 원본을 정리해주는 대신, 에이전트가 볼 수 있는 별도의 정리된 작업 공간을 만든다. 정리되지 않은 폴더를 그대로 에이전트에게 주면 다음 문제가 생긴다.

  1. 탐색 비용이 커진다. 에이전트가 관련 파일을 찾기 위해 불필요한 디렉토리를 뒤진다.
  2. 중요 파일을 놓치기 쉽다. 파일명이나 위치가 애매하면 에이전트가 핵심 자료를 발견하지 못할 수 있다.
  3. 컨텍스트가 흐려진다. 작업과 무관한 파일까지 섞이면 에이전트가 목표를 잘못 이해할 수 있다.
  4. 사람용 폴더 구조와 에이전트용 구조가 다르다. 사람은 날짜, 부서, 고객사 기준으로 정리하고 싶을 수 있지만 에이전트에게는 "분석 대상", "참고자료", "출력물", "검토 대상"처럼 작업 중심 구조가 더 유리하다.

agentdir의 핵심 가치는 여기에 있다.

원본 파일 구조는 유지하면서, 에이전트에게는 작업 목적에 맞는 깨끗한 파일 구조를 제공한다. 그리고 그 뷰를 통해서는 원본에 쓰지 못하게 막는다.

이 접근은 특히 문서, 리서치 자료, 고객사 파일, PDF, 이미지, 데이터셋처럼 코드가 아닌 파일을 다룰 때 유용하다.

기본 사용법

기본 사용 흐름은 간단하다. 먼저 CLI를 설치한다.

cargo install agentdir-cli

Python 패키지나 Node.js 패키지로도 사용할 수 있다.

pip install agentdir
npm install @nomadamas/agentdir

그다음 작업 공간을 만든다.

agentdir init ./workspace

원본 디렉토리를 가상 트리에 매핑한다.

agentdir -w ./workspace map ./team-files /files

이제 에이전트는 ./team-files 원본을 직접 뒤지는 대신, ./workspace 안의 정리된 구조를 기준으로 작업할 수 있다.

파일을 에이전트용 구조로 옮기고 싶다면 virtual namespace 안에서 이동하면 된다.

agentdir -w ./workspace mv /files/q1-report.pdf /reports/q1-report.pdf

여기서 중요한 점은 이 명령이 원본 파일을 실제로 옮기는 것이 아니라는 점이다. 에이전트가 보는 가상 구조만 바뀐다.

원본 파일 변경 사항을 반영하려면 refresh를 실행한다.

agentdir -w ./workspace refresh

계속 동기화하고 싶다면 watcher를 실행할 수 있다.

agentdir -w ./workspace watch --interval 60

이렇게 하면 원본 디렉토리의 변경 사항이 작업 공간에 반영된다.

에이전트 워크플로가 꼭 Rust나 CLI에서만 만들어지는 것은 아니다. 데이터 분석 스크립트는 Python에 있고, 자동화 도구는 Node.js에 있을 수 있다. 그래서 Python 바인딩에서는 같은 흐름을 이렇게 쓸 수 있다.

from agentdir import Workspace

ws = Workspace.init("./workspace")
ws.map("./team-files", "/files")
ws.mv("/files/q1-report.txt", "/reports/q1-report.txt")
ws.refresh()

여기서 핵심은 동작 방식이다. 이 흐름은 파일을 옮기는 것이 아니라 보여주는 경로를 바꾸는 식으로 동작한다.

실제 사용 예시는 조금 더 구체적으로 보면 이해가 쉽다.

고객사 자료를 에이전트 리뷰용 구조로 재배치하기
# 원본은 회사 공유 폴더 그대로 둔다
agentdir init ./agent-workspace
agentdir -w ./agent-workspace map ./customer-dropbox /raw

# 에이전트가 볼 구조를 작업 중심으로 만든다
agentdir -w ./agent-workspace mv /raw/회의록_최종진짜최종.md /inputs/meeting-notes/customer-kickoff.md
agentdir -w ./agent-workspace mv /raw/report_v2.pdf /inputs/reports/current-report.pdf
agentdir -w ./agent-workspace mv /raw/고객사자료/proposal.pdf /references/customer-proposal.pdf

# 이후 에이전트에게는 ./agent-workspace만 넘긴다

에이전트에게 전달하는 프롬프트도 훨씬 단순해진다.

./agent-workspace를 기준으로 고객사 제안서와 회의록을 검토해줘.
- /inputs: 분석 대상
- /references: 참고자료
- /outputs: 결과물을 저장할 위치

원본 폴더가 지저분하더라도 에이전트는 /inputs, /references, /outputs처럼 작업 의도가 드러나는 구조만 보면 된다. 이게 agentdir의 실용적인 포인트다.

사용하며 알게 된 점

도구가 실제로 약속을 지키는지 보려고, 어지러운 코퍼스를 하나 만들어 macOS(APFS)에서 측정했다. 300MB짜리 데이터 파일 하나와 다운로드 폴더처럼 흩어진 문서 여섯 개(PDF, 스크린샷, 계약서 초안, node_modules 잡파일 등)를 가상 트리에 매핑한 뒤, 같은 300MB 데이터셋을 다섯 개의 서로 다른 레이아웃에 동시에 노출했다.

측정 항목결과
원본 전체를 가상 트리에 매핑30ms (파일 7개 + 디렉토리 8개)
materialize 방식reflinked 7 / copied 0 (전부 CoW 클론)
같은 300MB를 5개 레이아웃에 노출du 논리 합계 1.5GB, 실제 추가 디스크 거의 0
대조: 같은 1.5GB를 일반 복사0.23초 + 실제 디스크 1.5GB 사용
가상 뷰 파일 권한 (reflink 전략)0o444, 그 경로로 쓰기 시도 차단됨. 원본은 쓰기 가능 유지
스냅샷 격리스냅샷에 쓴 내용이 베이스 작업 공간으로 전파되지 않음
추적성가상 경로 → 실제 원본 경로 역매핑 동작 확인 (export_mapping)

(단일 데모 기준이다. 절대 수치보다 동작의 성격에 주목해 달라. reflink 동작은 파일시스템에 의존하므로 APFS/Btrfs/XFS에서만 이렇게 나오고, ext4나 NTFS에서는 byte-copy로 폴백한다.)

이 데모가 확인한 건 측정 대상이 곧 도구의 약속이라는 점이다. "복사 없이 여러 레이아웃"은 reflinked 7/copied 0으로, "가상 뷰로는 원본에 못 쓴다"는 0o444 쓰기 차단으로, "스냅샷 쓰기가 베이스로 새지 않는다"는 스냅샷 격리로 확인된다. 반대로 이 데모가 측정하지 않은 것도 있다. 탐색 시 토큰/컨텍스트 절감, 실제 에이전트 루프에서의 동작, 동시 실행 충돌은 별도 검증이 필요한 영역이다. 참고로 위 표의 0o444 쓰기 차단은 reflink 전략에서 만든 가상 뷰에 한정된다.

규모를 키우면 차이가 더 분명하다. 50GB 코퍼스를 여덟 개 task-view에 노출하면 일반 복사로는 400GB가 들지만, reflink로는 추가 디스크가 거의 들지 않는다(이 50GB/400GB는 위 데모를 외삽한 추정치이며 직접 측정값은 아니다).

재현 메모: 측정은 agentdir 0.1.x의 Python 바인딩으로 짠 약 90줄 스크립트로, macOS(APFS)에서 돌렸다. 코퍼스는 300MB 더미 데이터 1개와 흩어진 문서 6개로 합성해 다섯 레이아웃에 노출했다. "추가 디스크 거의 0"은 du(논리 크기)가 아니라 reflinked=7/copied=0(CoW 블록 공유)과 동일 데이터를 일반 복사했을 때 1.5GB가 실제로 기록되는 대조로 확인한 값이며, df 단위의 정밀 측정은 아니다. 스냅샷·rglob 등 일부 기능은 현재 CLI가 아니라 바인딩에만 있어 측정도 바인딩으로 했다.

이 가상 뷰에는 원본으로 가는 쓰기 경로가 없다. 수정이 필요하면 에이전트가 아니라 신뢰된 오케스트레이터가 export_mapping으로 원본 실경로를 받아 거기서 고치는 식으로 역할을 분리한다.

동작 구조

레포 구조는 단순하다.

crates/
agentdir/ Core Rust library
agentdir-cli/ CLI
bindings/
python/ PyO3 + maturin
node/ NAPI-RS

코어는 Rust로 짜여 있고, 동일한 로직을 Rust 크레이트, Python(PyO3), Node(NAPI-RS) 세 채널로 배포한다. 핵심 컴포넌트는 Workspace, Catalog, Materializer, Reconciler로 나뉜다.

  • 가상 네임스페이스 (Catalog): Catalog는 가상 경로와 실제 소스 경로의 매핑을 들고 있다. 내부적으로는 가상 경로 문자열을 키로 하는 해시맵 인덱스라 경로 조회가 평균 O(1)이고, 가상 경로는 OS와 무관하게 항상 /를 쓴다.
  • CoW materialize (Materializer): Materializer는 그 매핑을 실제 디스크 위에 드러내는 역할을 한다. 기본 전략은 reflink다(APFS/Btrfs/XFS에서 블록 공유 클론, 그 외에는 byte-copy 폴백). 이 밖에 symlink, metadata만 두는 virtual 전략을 고를 수 있다.
  • 읽기 전용 보장: reflink/byte-copy로 materialize된 파일은 0o444(Windows에서는 읽기 전용 속성)로 설정된다(symlink 전략은 이 보장에서 예외다). 쓰기는 원본 파일을 통해 이뤄져야 하고, 가상 트리는 탐색과 소비를 위한 뷰로 남는다.
  • 변경 추적 (Reconciler): 기본은 mtime + 파일 크기 비교(빠름)이고, 옵션으로 SHA-256 검증을 켜면 mtime/size가 그대로인 조용한 수정까지 잡는다. 동기화는 on-demand refresh 또는 장기 실행 watch(파일 이벤트 약 500ms 디바운스 + 기본 60초 주기 전체 재스캔)로 한다.
  • 원자적 manifest: 상태는 .agentdir/manifest.json에 저장되는데, 임시 파일에 쓰고 fsync 후 rename하는 방식을 쓴다. 중간에 프로세스가 죽어도 반쯤 쓰인 JSON이 남지 않게 하려는 선택이다.

동기화는 자동 마법이 아니라 명시적(refresh / watch)이고, 원본 → 가상 한 방향이다. 무엇을 어떤 구조로 보여줄지는 여전히 사람이나 상위 오케스트레이터가 결정한다. 화려하진 않지만 이런 구현이 파일 시스템 도구에서는 꽤 중요하다. 에이전트가 읽는 작업 트리가 깨지면 다음 단계의 추론도 같이 흔들리기 때문이다.

한계와 주의점

사용하며 마주친 한계들이다.

  • read-only 보장은 가상 뷰에 한정된다. 0o444는 뷰 경로에 대한 것이고, 에이전트 런타임이 원본 경로에 접근 가능하면 별도 sandbox/접근 스코핑이 필요하다.
  • 비CoW 파일시스템에서는 디스크가 중복된다. reflink가 안 되는 ext4/NTFS/exFAT에서는 byte-copy로 폴백하므로, "복사 없이"의 이점은 APFS/Btrfs/XFS에 한정된다. Linux의 FUSE, Windows의 ProjFS 같은 대안은 가능하지만 장기 실행 프로세스나 OS 기능 의존성이 커져 현재 범위 밖에 둔다.
  • 스냅샷은 reflink가 아니라 symlink로 구현돼 있다. README는 스냅샷을 "CoW fork"로 소개하지만, 실제 구현은 베이스 작업 공간의 파일을 심볼릭 링크로 가리키다가 쓰기 시점에 실제 파일로 분리하는 방식이다. 쓰기가 베이스로 전파되지 않는 것은 확인했지만, 동시 실행 충돌 같은 더 넓은 격리는 별도 검증이 필요하다.
  • 변경 감지가 모든 수정을 잡지는 않는다. 기본 mtime/size 비교는 mtime/size가 보존되는 복사나 동기화 환경의 조용한 수정을 놓칠 수 있어, 그럴 땐 SHA-256 검증을 켜야 한다(전체 재해시 비용이 든다).
  • 일부 기능은 바인딩 전용이다. 스냅샷(snapshot), rglob, exists 등은 현재 Python/Node 바인딩에는 있지만 CLI에는 없다.
  • 이 도구는 두뇌가 아니다. 무엇을 어떤 구조로 재배치할지 결정하는 오케스트레이터, 파일 파싱, 인덱싱, 검색은 모두 명시적 비범위다.

기존 방식과 비교

에이전트에게 파일을 주는 방법은 이미 여럿 있다. agentdir의 자리는 그 사이의 빈칸을 보면 드러난다.

접근해결하는 것데몬/root/마운트한계
MCP 파일시스템 서버접근 폴더 스코핑프로세스원본 트리 그대로 노출(정리 안 됨), read-only는 서버 정책
OverlayFS쓰기 격리(리눅스 CoW)root + 마운트리눅스 전용, 구조 재배치 아님
git worktree코딩 에이전트 격리없음(git 필요)git 저장소 전용, 여전히 수정 가능한 전체 트리
FUSE 기반 AgentFS류에이전트 전체 상태/메모리 substrate데몬 + DB + 마운트무거운 스택
reflink/cp --reflink대용량 파일 무복제 클론없음복사 프리미티브일 뿐, 레이아웃·안전 경계 아님
agentdir정돈된 읽기 전용 뷰 + 무복제없음어린 프로젝트, 일부 기능 바인딩 전용

agentdir가 메우는 칸은 "정돈된 읽기 전용 뷰 + 무복제"다. 마운트도, root도, 상시 데몬도 없는 유저스페이스 도구이고, reflink/byte-copy 전략에서는 가상 트리가 실제 일반 파일이라 심볼릭 링크를 무시하는 도구도 내용을 본다. 그리고 가상 뷰의 read-only가 서버 정책이 아니라 파일 권한이라는 점이 MCP 서버의 ro 플래그와 갈라지는 지점이다.

활용 예시

agentdir는 다음과 같은 상황에서 특히 유용하다.

1. 에이전트에게 문서 묶음을 넘겨야 할 때

회의록, PDF, 발표자료, 스프레드시트, 이미지가 한 폴더에 섞여 있다면 에이전트가 보기 좋은 구조로 재배치할 수 있다.

/inputs
/references
/reports
/customer-materials
/outputs

이런 식으로 정리하면 에이전트가 작업 목적을 훨씬 빨리 이해한다.

2. 공유 문서 더미를 복사 없이 재배치할 때

회사 공유 드라이브, 고객사 자료, 기존 프로젝트 폴더는 임의로 구조를 바꾸기 어렵다. 하지만 에이전트 작업을 위해서는 구조를 바꾸고 싶다. agentdir는 이 둘을 분리한다.

  • 사람과 조직은 기존 폴더 구조를 계속 사용한다.
  • 에이전트는 별도의 정리된 작업 공간을 사용한다.

멀티 GB 코퍼스도 CoW 파일시스템 기준으로는 복사 없이 목적별 레이아웃으로 보여줄 수 있다.

3. 여러 에이전트에게 서로 다른 관점을 제공하고 싶을 때

같은 파일 묶음이라도 에이전트 역할에 따라 필요한 구조가 다를 수 있다. 예를 들어 하나의 고객사 자료 폴더를 두고, 분석 에이전트와 문서 작성 에이전트에게 서로 다른 뷰를 줄 수 있다.

analysis-workspace/
inputs/
data/
evidence/
outputs/

writing-workspace/
source-materials/
outline/
drafts/
references/

이렇게 나누면 분석 에이전트는 근거 수집에 집중하고, 작성 에이전트는 초안 작성 흐름에 집중한다. 원본 파일을 복사해서 여기저기 흩뿌리는 대신, 에이전트별 작업 공간만 다르게 구성하는 방식이다.

4. 세션·실험마다 격리가 필요할 때

스냅샷으로 트라이얼마다 격리된 작업 뷰를 만들 수 있다. CoW 파일시스템에서는 이 비용도 작게 가져갈 수 있다. 다만 현재 스냅샷은 바인딩 전용이고 symlink 기반임에 유의한다.

이럴 때는 권하지 않는다

  • 주력 환경이 ext4나 NTFS면 reflink가 byte-copy로 폴백하므로 무복제 이점이 사라진다. 대용량 코퍼스를 여러 레이아웃에 노출하려는 목적이라면 다른 선택지를 본다.
  • 에이전트가 원본 경로에 직접 접근할 수 있는 구성이면 0o444만으로는 부족하다. 이 경우 가상 뷰 권한보다 샌드박스나 접근 스코핑이 먼저다.
  • 대상이 이미 잘 정리된 코드베이스면 git worktree로 충분하다.

agentdir가 하지 않는 것

agentdir는 파일을 "이해"하거나 "자동 분류"하는 AI 도구는 아니다. README에서도 명확히 밝히듯이, 이 도구의 목표는 다음이 아니다.

  • LLM 통합
  • 의미 기반 파일 분류
  • 전문 검색
  • 파일 포맷 변환
  • 문서 내용 파싱
  • 에이전트가 어떤 구조로 정리해야 하는지 판단하는 일

즉, agentdir는 지능형 분류기가 아니라 에이전트가 쓰기 좋은 파일 레이아웃을 만들기 위한 파일 시스템 인프라다. 검색 엔진도 아니고, 임베딩 인덱스도 아니고, LLM 통합 레이어도 아니다. 그 선 덕분에 오히려 역할이 명확해진다. 에이전트나 사람이 "어떤 구조가 좋은지" 결정하고, agentdir는 그 구조를 안전하게 materialize한다.

RAG·AutoRAG 관점에서 (Braincrew의 해석)

RAG/에이전트 파이프라인 관점에서 agentdir는 인제스트 직전의 코퍼스 뷰 레이어로 읽힌다. 파싱·인덱싱 전에 에이전트에게 정돈된 코퍼스를 보여주고, 평가 트라이얼마다 격리 스냅샷으로 재현 가능한 환경을 구성하는 식이다. 또 가상 경로 기준으로 인덱싱한 청크를 export_mapping으로 실제 원본 파일까지 되돌리면 인용 출처 추적에도 쓸 수 있을 것으로 보인다. AutoRAG가 코퍼스에 맞는 최적 RAG 구성을 오프라인에서 찾는 도구라면, 그 입력이 되는 코퍼스를 정리해 보여주는 인프라가 agentdir라는 분업으로 이해할 수 있다.

다만 agentdir 저장소는 RAG도 AutoRAG도 전혀 언급하지 않는다. README는 "AI 에이전트, 스크립트, 사람"을 위한 범용 가상 파일트리로 소개하고, AI/LLM 통합과 검색·인덱싱을 명시적 비범위로 둔다. 위 RAG 연결은 같은 팀이 만든 도구라는 사실과 각 도구의 설계 의도에 근거한 Braincrew의 해석이지, 도구가 주장하는 통합이 아니다.

정리

agentdir는 기존 파일을 건드리지 않으면서 에이전트에게 더 좋은 작업 공간을 제공한다. 정리되지 않은 폴더를 그대로 에이전트에게 던지는 대신, 목적에 맞는 가상 폴더 구조를 만들어주고, 그 뷰를 통해서는 원본에 쓰지 못하게 막는다.

직접 돌려본 결과로 남은 건 두 가지다. CoW 파일시스템(APFS/Btrfs/XFS)에서는 같은 데이터셋을 여러 레이아웃에 노출해도 reflink 덕분에 추가 디스크가 거의 들지 않았고, reflink 전략으로 만든 가상 뷰 파일은 0o444로 잠겨 그 경로로는 원본을 건드릴 수 없었다. agentdir는 에이전트에게 정돈된 읽기 전용 뷰를 무복제로 제공하는 파일 시스템 인프라다.

References