지난 몇 달 동안 나는 조금은 이단적인 방식의 지식 베이스 에이전트를 만들어 왔다. 벡터 스토어를 세우고, 문서를 청크로 쪼개고, 질의할 때마다 유사도 검색을 돌리는 대신, 에이전트가 원문을 한 번 읽고 위키 형태로 다시 정리하는 방식이다. 결과물은 YAML frontmatter가 붙은 마크다운 파일들의 디렉터리다. 문서끼리는 링크로 연결되어 있고, 인덱스와 변경 로그도 함께 둔다. 이후 다른 에이전트가 질문에 답해야 할 때는, 사람처럼 그 위키를 읽는다. 인덱스를 열고, 링크를 따라가고, 필요한 페이지를 읽는 식이다.
이 방식은 꽤 잘 작동했다. 내가 다루던, 자주 바뀌지 않으면서도 구조가 중요한 지식에는 이전의 RAG 파이프라인보다 오히려 더 잘 맞았다. 그런데도 마음 한편이 계속 불편했다. 사람들이 블로그에서 이야기하는 건 늘 벡터 RAG였고, 대부분의 프레임워크도 그걸 기본 전제로 삼고 있었기 때문이다. 내가 만든 마크다운 위키 방식은 괜히 구석에서 혼자 만든 장난감 같았고, 진지한 팀이라면 채택하지 않을 영리한 꼼수처럼 느껴질 때도 있었다. 언젠가 누군가가 “그건 잘못된 방향”이라고 말할 것만 같았다.
그런데 구글이 Open Knowledge Format, 즉 OKF를 발표했다.
OKF가 실제로 무엇인가
OKF는 Google Cloud의 knowledge-catalog 프로젝트에서 나온 초안(draft) 스펙이다. 한 줄로 요약하면 이렇다. 지식 코퍼스는 YAML frontmatter가 붙은 마크다운 파일들의 디렉터리면 된다. 그게 전부다. 스키마 레지스트리도 없고, 중앙 권한 체계도 없고, 필수 SDK도 없다. 스펙의 표현을 그대로 옮기면, “cat으로 파일을 읽을 수 있으면 OKF도 읽을 수 있고, git clone으로 저장소를 가져올 수 있으면 배포도 할 수 있다.”
구조는 놀랄 만큼 단순하다.
- 하나의 개념(concept)은 하나의 마크다운 파일로 표현한다. 파일 경로에서
.md를 뺀 값이 그 개념의 ID다. - 각 파일에는 frontmatter 블록이 있다. 필수 필드는 단 하나,
type뿐이다. 그 외title,description,resource,tags,timestamp같은 필드는 권장되지만 선택 사항이다. index.md는 디렉터리 목록을 위한 예약 파일명이고,log.md는 변경 이력을 위한 예약 파일명이다. 둘 다 있어도 되고 없어도 된다.- 개념 간 연결은 그냥 일반적인 마크다운 링크로 표현한다. A에서 B로 링크가 있으면 “둘은 관련이 있다”는 정도만 의미한다. 관계의 종류는 주변 문맥이 설명한다. 소비자는 모든 링크를 타입 없는 방향성 엣지(untyped directed edge)로 다룬다.
- 깨진 링크도 허용된다. 아직 작성되지 않은 지식을 미리 가리킬 수도 있기 때문이다.
포맷 자체는 이게 전부다. conformance 규칙도 마찬가지로 매우 관대하다. 예약어가 아닌 마크다운 파일들에 파싱 가능한 frontmatter가 있고, 모든 frontmatter에 비어 있지 않은 type이 들어 있으면 그 번들은 conformant로 본다. 선택 필드가 빠져 있거나, 알 수 없는 타입이나 키가 있거나, 링크가 깨져 있거나, index.md가 없다는 이유만으로 소비자가 번들을 거부해서는 안 된다고 스펙에 명시돼 있다. 즉, 지식이 아직 덜 써졌고 일부는 기계가 생성한 상태여도 계속 쓸모가 있도록 설계된 포맷이다. 실제 지식이 대개 그런 상태로 존재한다는 점을 잘 반영하고 있다.
그리고 흥미로운 건, 이 스펙이 자기 영감의 원천을 꽤 솔직하게 적어 두고 있다는 점이다. 섹션 10에는 “LLM wiki repos”와 “metadata as code”가 OKF가 의도적으로 가깝게 둔 패턴으로 나온다. 그 문장을 읽는 순간 바로 자세를 고쳐 앉게 됐다. 내가 조용히 만들고 있던 것이, 떠오르는 오픈 표준의 대표적 참조 패턴 중 하나로 이름까지 붙어 있었기 때문이다.
왜 어떤 문제에서는 파일 디렉터리가 벡터 스토어보다 낫나
2026년의 기본 감각은 대체로 이렇다. RAG라고 하면 임베딩과 벡터 데이터베이스를 떠올린다. 실제로 많은 문제에서는 그게 맞다. 코퍼스가 엄청나게 크고, 구조화되어 있지 않으며, 수백만 개의 청크를 대상으로 느슨한 유사 검색이 필요하다면 벡터 스토어가 적절하다.
하지만 벡터 RAG는 질의가 들어올 때마다 지식을 매번 처음부터 다시 찾아낸다. 질문과 비슷한 top-k 청크를 가져와 모델에 넣고, 모델은 그때그때 답을 다시 합성하고, 충돌하는 내용을 다시 조정하고, 관계를 다시 추적해야 한다. 지식이 누적되지 않는다. 게다가 검색 품질은 결국 청킹 품질에 묶이는데, 그 청킹 과정이 원래 문서가 가지고 있던 구조와 맥락을 무너뜨리는 경우가 많다.
반면, 컴파일된 위키는 이 과정을 뒤집는다. 합성 비용을 쓰는 시점에 한 번만 내면 된다. 교차 참조는 이미 문서 안에 들어가 있고, 모순되는 내용도 페이지를 작성한 사람(혹은 에이전트)이 미리 표시하고 정리해 둘 수 있다. 개념 간 관계도 코사인 유사도로 그때그때 복원하는 것이 아니라 링크 그래프 안에 직접 담겨 있다. 이 위키를 읽는 에이전트는 “가까워 보이는 조각 몇 개”가 아니라, 이미 한 번 정리된 관점 자체를 받는다.
그래서 이 선택은 “RAG냐 아니냐”의 문제가 아니라, 다루는 지식의 성격이 무엇이냐의 문제에 가깝다.
- 변화 속도가 느리고, 구조가 중요하고, 관계가 많은 지식(데이터 모델, 내부 시스템 묶음, 도메인 온톨로지 등)에는 컴파일된 위키가 유리하다. 여기서는 구조 자체가 가치이고, 위키는 그 구조를 보존한다.
- 규모가 크고, 자주 바뀌고, 비구조적인 코퍼스(고객 지원 티켓 기록, 방대한 문서 더미 등)에는 벡터 RAG가 유리하다. 백만 개의 티켓을 손으로 컴파일할 수는 없고, 그 사이의 관계도 굳이 모두 모델링할 필요가 없다.
- 많은 내부 도구의 현실적인 답은 사실 둘 다다. 핵심이 되는 구조화된 지식은 작고 잘 정리된 위키로 두고, 그 밖의 긴 꼬리(long tail)는 벡터 검색으로 처리하는 식이다.
이 위키 방식에는 생각보다 더 중요한 장점이 하나 더 있다. 사람이 직접 읽을 수 있다는 점이다. 그냥 git 안에 들어 있는 마크다운이기 때문에, diff도 볼 수 있고, 누가 무엇을 바꿨는지 추적할 수 있고, 코드 리뷰도 가능하다. Obsidian으로 열어서 그래프를 훑어볼 수도 있다. 반면 벡터 스토어는 결국 불투명한 숫자 덩어리에 가깝다. 에이전트가 이상한 답을 했을 때, 위키 방식은 해당 페이지를 열어 “얘가 정확히 뭘 읽었는지”를 바로 확인할 수 있다.
conformant 상태가 되는 데 든 비용은 사실상 0에 가까웠다
내가 느끼던 찜찜함이 가장 생산적으로 바뀐 지점이 바로 여기였다. 기존에 만들어 둔 위키들을 OKF의 conformance 규칙에 맞춰 비교해 보면서, 내가 얼마나 멀리 떨어져 있는지를 확인해 봤다.
결론은, 거의 두 줄 차이였다.
내 위키 생성기는 이미 YAML frontmatter가 붙은 마크다운 파일을 디렉터리 트리 형태로 만들고 있었고, 문서 간 교차 링크도 있었고, 기계가 읽을 수 있는 인덱스도 따로 생성하고 있었다. 스펙이 엄격하게 요구하는 것 중 내가 빠뜨리고 있던 건 frontmatter의 type 필드 하나뿐이었다. 생성기에서 frontmatter 딕셔너리에 한 줄 추가하면 끝나는 수준이었다. 나머지 차이는 사실상 외형적인 부분이었다. 나는 기계용 index.json은 가지고 있었지만, 스펙에서 말하는 index.md는 없었다. 그래서 이제는 둘 다 생성하게 만들었다. 인덱스를 소비하는 내 쿼리 라이브러리는 이 변화를 거의 의식하지도 못했다. index.md와 type은 내 런타임 동작을 바꾸는 요소가 아니라, 외부 소비자에게 노출되는 표면을 조금 넓혀 주는 정도였기 때문이다.
그 밖의 설계는 거의 자연스럽게 맞아떨어졌다. 내 교차 링크는 스펙이 예시로 드는 [text](/path.md) 형태가 아니라 Obsidian 스타일의 [[wikilink]]였지만, OKF는 링크 형식을 엄격하게 강제하지 않고 링크를 느슨한 가이드 정도로 보기 때문에, 기존 wikilink를 그대로 유지해도 번들은 여전히 conformant하다. 타입 없는 엣지, 관대한 소비 방식, 문서마다 frontmatter를 둔다는 발상까지. 결국 나는 같은 문제를 풀다가 비슷한 설계 판단에 도달해 있었던 셈이다.
실제로 운영하면서 부딪힌 문제들
이 패턴을 실제로 굴려 보니, 스펙만 읽어서는 잘 드러나지 않는 함정도 몇 가지 있었다.
- 중복 페이지가 가장 흔한 실패 모드다. 에이전트가 새 문서를 쓰기 전에 인덱스를 먼저 확인하지 않으면, 이미 존재하는 개체를 이름만 조금 다르게 바꿔서 새 페이지로 또 만들어 버린다. 해결책은 단순하지만 강하게 강제해야 한다. 먼저 현재 구조를 파악하고, 인덱스를 검색한 뒤, 그 다음에 써야 한다. 이 순서를 건너뛰는 순간 위키는 금방 중복 문서 더미가 된다.
- 고아 페이지(orphan page)는 사실상 보이지 않는다. 다른 문서에서 들어오는 링크가 하나도 없는 페이지는 존재하지 않는 것이나 다름없다. 아무도 거기까지 도달하지 못하기 때문이다. 새 페이지를 만들었다면 기존 페이지들로부터 최소 몇 개의 inbound link는 연결해 두는 편이 좋다.
- 인덱스와 로그는 단순한 부기(bookkeeping)가 아니라 탐색의 뼈대다. 업데이트를 건너뛰고 싶어질 때가 있는데, 그러면 안 된다. 인덱스는 문서를 찾는 기본 경로이고, 오래된 인덱스는 실제 문서를 사실상 도달 불가능하게 만든다.
- 깨진 링크는 허용되지만, 그렇다고 방치해도 된다는 뜻은 아니다. 스펙이 말하듯 깨진 링크는 “아직 쓰지 않은 문서”를 의미할 수도 있다. 하지만 의도된 선행 참조와 단순 오타를 구분해 주는 lint 단계는 여전히 필요하다. 그렇지 않으면 링크 그래프가 서서히 썩어 간다.
그래서 내가 실제로 얻은 것
실질적인 이득은 작지만 분명했다. 생성기에 몇 줄 더하는 것만으로, 내 지식 베이스를 더 이상 “나만의 bespoke한 해킹”이 아니라 “OKF-conformant한 시스템”이라고 부를 수 있게 됐다. 이제 누군가에게 이 설계를 설명할 때도 가리킬 수 있는 기준 문서가 생겼다.
하지만 더 큰 변화는 감정 쪽에 있었다. 나는 한동안 마크다운 위키 방식이 혹시 장난감 수준의 아이디어는 아닐까 하는, 낮지만 끈질긴 의심을 품고 있었다. 모두가 벡터 쪽으로 가는 것 같을 때, 나만 잘못된 길로 새는 건 아닐까 싶었다. 그런데 돌아보니 그 직감이 향하던 방향이, 지금 구글이 표준으로 만들려는 방향과 같았다. 내가 하던 방식은 편법이 아니었다. 언젠가 스펙으로 정리될 무언가의 초기 구현에 더 가까웠다.
이건 그냥 개인 메모로라도 남겨 둘 가치가 있다고 느꼈다. 단순한 설계가 계속 잘 작동하는데도, 그에 딱 들어맞는 표준을 아직 찾지 못했다면, 가끔은 그 표준이 아직 세상에 쓰이지 않았을 뿐일 수 있다. 그럴 때는 일단 단순한 것을 만들어라. 그리고 표준이 따라오기를 기다리면 된다.