GitHub 트렌딩 딥다이브 · 2026-06-24 · TrendShift 라이브 멘션

GraphIQ 딥다이브
— 레포를 '구조 그래프'로 인덱싱하고, 임베딩·LLM·네트워크 없이 BM25+그래프로 검색하는 로컬 코드 인텔리전스 엔진

aaf2tbz/graphiq. 저장소를 통째로 읽어 함수·타입·호출·import·상수·타입 흐름을 '구조 그래프(structural graph)' 하나로 인덱싱하고, 그것을 BM25 전문검색 + 그래프 워크 재랭킹으로 검색하는 코드 인텔리전스 엔진이다. 핵심 주장은 강렬하다 — 임베딩도, LLM도, 네트워크 호출도 없다(no embeddings, no LLM, no network). 모든 것이 단일 SQLite 파일 안에 들어간다. Rust로 작성됐고, CLI · MCP 서버 · 데스크탑 앱 세 창구로 노출되어 Claude Code·Codex·OpenCode·Cursor 같은 AI 코딩 에이전트가 "파일을 통째로 읽는" 대신 랭킹된 심볼만 받게 한다. (저장소: aaf2tbz/graphiq · 언어 Rust · 워크스페이스 4개 크레이트 + 데스크탑 앱 · graphiq-core v4.3.3 / 아키텍처 v3 · 라이선스 MIT · TensorFlow 6.46M 라인 벤치마크에서 grep 대비 NDCG@10 +63% · ★ 급상승 신생 — TrendShift 라이브 멘션)

목차
  1. 프로젝트 한줄 요약
  2. 왜 주목받는가
  3. 기술 스택 전체 지도
  4. 아키텍처 심화 분석 (코드 한 줄이 검색결과가 되기까지)
  5. 디렉토리 구조 해부
  6. 학습 포인트 (기술별)
  7. 하드웨어 / 시스템 요구사항
  8. 직접 해볼 수 있는 실습 과제
  9. 관련 기술 심화 학습 로드맵
  10. 핵심 키워드 사전
  11. 참고 링크

1프로젝트 한줄 요약

"AI에게 '파일 더미'를 던지는 대신, 미리 만들어 둔 '코드 색인'에서 가장 관련 있는 함수만 순위로 뽑아 준다 — 그것도 AI 모델 없이."

한 줄로

GraphIQ는 레포 전체를 구조 그래프 + 전문검색 색인으로 미리 만들어 두고, "이 코드 어디 있어?"를 임베딩·LLM 없이 정밀 랭킹으로 답하는 '코드 검색 두뇌'다.

요즘 코드 검색 도구는 대개 임베딩(embedding)에 의존합니다 — 코드를 AI 모델로 숫자 벡터로 바꿔 '의미가 비슷한' 조각을 찾죠. 하지만 그건 GPU·모델·네트워크가 필요하고, 결과가 왜 그렇게 나왔는지 설명하기 어렵고, 토큰/비용이 듭니다. GraphIQ는 정반대로 갑니다. tree-sitter로 코드를 구문 분석해 심볼(함수·타입·변수)을 점, 관계(호출·import·타입 흐름)를 선으로 잇는 그래프를 만들고, 검증된 검색 수학인 BM25로 1차 후보를 뽑은 뒤 그래프를 따라 걸으며(graph walk) 순위를 다시 매깁니다. AI 모델은 한 번도 부르지 않습니다. 전부 SQLite 파일 하나에서, 오프라인으로, 결정론적으로 돕니다.

용어
코드 인텔리전스 엔진 (code intelligence engine)
소스 코드를 단순 텍스트가 아니라 "구조를 이해한 데이터"로 바꿔, "이 이름이 정의된 곳", "이 함수의 호출 관계", "이 동작을 하는 코드"를 프로그램이 질의할 수 있게 만드는 도구. VS Code의 "정의로 이동 / 참조 찾기"를 떠올리되, 그걸 사람이 아니라 AI 코딩 에이전트가 API로 호출한다고 생각하면 됩니다. GraphIQ는 여기에 자연어 검색("rate limit 미들웨어")까지 얹습니다.
용어
구조 그래프 (structural graph)
코드를 점(노드)선(엣지)의 그물망으로 표현한 것. GraphIQ에서 노드는 함수·메서드·클래스·구조체·열거형·인터페이스·트레이트·변수·import이고, 엣지는 calls(호출)·imports·references(참조)·contains(포함)·extends(상속)·implements(구현) 같은 구문적 관계와, 한 발 더 나아가 shares_type(같은 타입 사용)·shares_error_type(같은 에러 처리)·shares_data_shape(같은 필드 접근)·shares_constant(같은 상수 사용) 같은 의미적(deep) 관계까지 포함합니다.

좀 더 구체적으로, GraphIQ는 Rust로 만든 명령줄 도구이자 서버입니다. graphiq index /프로젝트로 한 번 색인하면 .graphiq/graphiq.db라는 SQLite 파일이 생기고, 이후 graphiq search "rate limit middleware"처럼 이름·자연어·파일 경로·에러 메시지 무엇으로든 검색할 수 있습니다. 같은 엔진을 ① 터미널에서 직접(CLI), ② AI 에이전트가 표준 규격으로(MCP 서버), ③ 데스크탑 앱에서 시각적으로 쓸 수 있습니다. 자랑거리는 "임베딩·LLM·네트워크 0, 그런데도 grep보다 의미 검색이 훨씬 정확하다"는 것 — 즉 'AI 없이 AI급 검색'이 이 프로젝트의 진짜 상품입니다.

이 문서가 파고드는 건 사용법만이 아니라 그 안의 설계입니다 — "AI 모델 없이 어떻게 자연어 질문을 이해하나(질의 패밀리 분류)", "BM25가 놓친 코드를 그래프가 어떻게 주워 오나(graph walk)", "왜 임베딩을 버렸나(29단계 실험기)". 검색·정보검색(IR)·코드 분석·에이전트 도구를 처음 공부하는 사람에게 드물게 솔직한 실전 교본입니다.

2왜 주목받는가

"모두가 임베딩(벡터 검색)으로 달려갈 때, GraphIQ는 'BM25면 충분하다'며 정반대로 걸어가 더 좋은 점수를 냈다."

먼저, 'AI 코드 검색'의 표준 레시피와 그 약점

2026년 코드 검색 도구의 8할은 임베딩 + 벡터 DB 조합입니다. 코드를 AI 임베딩 모델로 벡터로 바꿔 저장하고, 질문도 벡터로 바꿔 '가까운' 것을 찾죠(시맨틱 검색). 강력하지만 비용이 큽니다 — 임베딩 모델을 돌릴 GPU나 API, 벡터를 담을 DB, 그리고 코드가 바뀔 때마다 다시 임베딩해야 합니다. 게다가 결과가 왜 1등인지 설명하기 어렵고(블랙박스), 회사 코드를 외부 API로 보내야 하는 프라이버시 문제도 생깁니다.

함정
"의미 검색 = 무조건 임베딩"이라는 통념

임베딩은 멋지지만, 코드 검색에서 진짜 신호의 상당수는 구조에 있습니다 — "이 함수를 누가 부르나", "같은 에러 타입을 다루는 함수들", "같은 상수를 공유하는 코드". 이건 벡터 유사도가 아니라 그래프 관계로 풀어야 정확합니다. 임베딩에만 올인하면 비용은 비용대로 쓰면서 정작 구조 신호를 놓칩니다.

해결
검증된 BM25로 후보를 뽑고, 그래프 수학으로 다시 줄 세운다

GraphIQ의 공식은 "BM25가 검색하고(retrieve), 구조가 재랭킹한다(rerank)"입니다. 1차로 SQLite의 FTS5 BM25 전문검색이 후보를 뽑고, 2차로 그래프 워크가 "후보와 호출·타입·에러를 공유하는 이웃"을 따라가 순위를 보정합니다. 임베딩이 필요 없고, 결과가 왜 그 순위인지 explain 툴로 설명할 수 있으며, 회사 코드는 한 줄도 밖으로 나가지 않습니다.

숫자로 증명: TensorFlow 646만 라인에서 grep을 이기다

주장만 있는 게 아닙니다. README에는 TensorFlow 저장소(646만 라인, 심볼 433,898개, 엣지 726,439개, 5개 언어 — GraphIQ가 벤치마크한 역대 최대 규모)에서 grep과 100문항씩 맞붙은 결과가 실려 있습니다.

지표GraphIQGrep차이
NDCG@100.2010.123+63%
MRR@100.5580.343+63%
Hit@100.850.52+33%p
이긴 카테고리8 / 102 / 10
용어
NDCG@10 · MRR@10 · Hit@10
검색 품질을 재는 정보검색(IR)의 표준 채점표입니다. Hit@10은 "정답이 상위 10개 안에 한 번이라도 들었나"(들면 1). MRR@10(평균 역순위)은 "첫 정답이 몇 등에 나왔나"를 1/등수로 환산해 평균 — 1등에 나오면 1.0, 2등이면 0.5. NDCG@10은 "여러 정답을 얼마나 위쪽에 잘 모았나"를 이상적 순서 대비 비율로 잰 것. 셋 다 높을수록 좋고, GraphIQ가 grep을 큰 폭으로 앞섭니다.

차별점이 또렷한 신생 프로젝트

관점GraphIQ전형적 임베딩 검색
의미 이해 방식BM25 + 구조 그래프 (규칙·수학)임베딩 벡터 유사도 (AI 모델)
외부 의존0 — 모델·GPU·네트워크·DB 불필요임베딩 모델 + 벡터 DB
저장단일 SQLite 파일벡터 인덱스 + 메타 저장소
프라이버시100% 로컬, 코드 유출 0외부 API 전송 가능성
설명가능성explain/why로 순위 근거 제시대체로 블랙박스
증분 갱신콘텐츠 해시로 바뀐 파일만 재색인바뀐 코드 재임베딩 필요
속도웜 ~50ms / MCP 인프로세스 ~18µs벡터 검색 + 모델 지연
비유

임베딩 검색 = 모든 책을 통째로 외운 천재 사서. 비슷한 내용을 잘 찾지만, 외우는 데 시간·돈(GPU)이 들고 "왜 이 책을 골랐냐" 물으면 설명을 못 합니다.

GraphIQ = 색인 카드(BM25)와 상호참조 지도(그래프)를 든 베테랑 사서. AI 기억력은 없지만, 잘 만든 색인과 "이 책은 저 책을 인용한다"는 관계망으로 더 정확히, 더 빨리, 그리고 근거를 대며 찾아 줍니다.

진짜 흥미로운 건 '버린 것들' — 29단계 실험기

이 레포가 개발자들 사이에서 회자되는 이유는 성능뿐 아니라 드물게 솔직한 개발 회고 때문입니다. 현재 아키텍처(v3)는 29단계의 실험 끝에 나왔는데, README가 실패를 그대로 공개합니다.

함정
v1의 화려한 수학 5,087줄 → 18GB RAM, 효과는 반올림 오차

초기 버전은 스펙트럴/홀로그래픽/예측 수학을 잔뜩 넣었습니다 — 체비쇼프 열확산, FFT 홀로그래픽 인코딩, KL-발산 예측 모델, 리치 곡률(Ricci curvature), MDL 설명 집합. 코드 5,087줄에 RAM 18GB를 먹었는데, NDCG 개선은 고작 +0.02~0.05. 개발자의 표현으로 "반올림 오차를 리파이낸싱하고 있었다".

해결
v3는 그걸 전부 삭제하고 오히려 더 좋아졌다

v3는 그 복잡한 수학을 전부 들어내고, 그래프 워크(구조 신호) + BM25(어휘 신호)만 남겼습니다. 결과는 더 단순하고, 더 가볍고, 설명 이름이 좋은 코드베이스에선 오히려 정확도가 올랐습니다. 핵심 교훈 7가지 중 첫째가 "BM25는 이기기 어렵다 — 늘 BM25가 검색하고 구조가 재랭킹하는 패턴이 승리한다", 둘째가 "단순한 게 낫다(Simpler is better)"입니다.

여기에 모든 주요 에이전트와 호환(Claude Code·Codex·OpenCode·Cursor·Pi)된다는 실용성, 단일 바이너리 + SQLite라는 배포 단순함이 더해져 빠르게 입소문을 탔습니다. 다만 솔직히 짚자면, GraphIQ는 아직 신생 프로젝트입니다(Live Mentions로 떠오른 초기 단계). 벤치마크 수치는 개발자가 제시한 값이고, NDCG@10 절대값(0.2대)이 보여주듯 코드 검색 자체가 본질적으로 어려운 문제라 "grep보다 낫다"는 상대 비교로 읽는 게 정확합니다.

3기술 스택 전체 지도

"전부 Rust. 그리고 핵심은 딱 두 외부 기술 — 코드를 뜯는 tree-sitter, 검색·저장을 맡는 SQLite(FTS5)."

GraphIQ는 화면이 거의 없는 엔진 중심 프로젝트입니다. 무게중심이 graphiq-core 크레이트에 쏠려 있고, 외부 인프라(DB 서버, 벡터 스토어, 모델 서버)는 하나도 없습니다. 의존성 철학이 곧 셀링 포인트라, 의존성 목록을 보는 것만으로 설계 의도가 읽힙니다.

① 백엔드 코어 (파싱 · 그래프 · 검색)

기술역할한 줄 설명
Rust 2021전체 언어단일 바이너리·메모리 안전·빠른 속도. 워크스페이스(resolver 3)로 4개 크레이트를 묶는다.
rusqlite
(bundled)
저장 + 검색SQLite를 내장 컴파일(bundled)해 외부 DB가 필요 없다. FTS5 가상 테이블로 BM25 전문검색을 공짜로 얻는다.
tree-sitter 0.24소스 파싱13개 문법(typescript·rust·python·go·java·c·cpp·ruby·json·yaml·toml·html·css)으로 16개 언어 변종을 같은 방식의 구문 트리(AST)로 만든다.
rayon병렬 처리인덱싱(파일 파싱·엣지 계산)을 멀티코어로 병렬화. 데이터 병렬의 표준 크레이트.
ignore파일 순회.gitignore를 존중하며 소스 파일을 걷는다. ripgrep과 같은 크레이트라 빠르고 정확하다.
zstd · bincode캐시 직렬화인메모리 색인(CruncherIndex)을 cruncher.bin.zst로 압축 저장(20K 심볼에 ~6.5MB)해 재실행을 빠르게 한다.
sha2증분 색인파일 내용 해시로 "바뀐 파일만" 다시 파싱(content-hash incremental).
dashmap · lru · regex · thiserror · serde유틸동시성 해시맵, LRU 캐시, 정규식, 에러 타입, 직렬화 — 흔하지만 탄탄한 기반.
용어
BM25 · FTS5
BM25는 1990년대에 정립된 전문검색 랭킹 공식입니다. "질의 단어가 문서에 몇 번 나오나(TF)"와 "그 단어가 전체에서 얼마나 희귀한가(IDF)"를 결합하되, 흔한 단어와 긴 문서를 깎아내려 균형을 맞춥니다. 구글 이전부터 검색의 주력이었고, 지금도 강력합니다. FTS5는 SQLite에 내장된 전문검색 엔진으로, BM25를 기본 제공합니다. GraphIQ는 별도 검색엔진(Elasticsearch 등) 없이 SQLite 한 파일로 BM25를 돌립니다.
용어
tree-sitter / AST(구문 트리)
tree-sitter는 소스 코드를 구문 트리(AST)로 바꿔 주는 파서입니다. 언어마다 문법(grammar)이 따로 있어 여러 언어를 같은 인터페이스로 파싱하는 게 장점입니다. GitHub이 코드 하이라이팅·네비게이션에 쓰는 바로 그 기술이며, 증분 파싱이 빨라 인덱서에 잘 맞습니다. GraphIQ는 16개 언어는 tree-sitter로 심볼까지 추출하고, 나머지 20여 개(Kotlin·Swift·C#·PHP·Zig·SQL·Dockerfile 등)는 파일 단위로만 추적합니다.

② 인터페이스 계층 (세 개의 창구 + 벤치마크)

크레이트/창구대상설명
graphiq-mcpAI 에이전트핵심 상품. stdio(JSON-RPC)로 에이전트에 물리는 MCP 서버. search·context·blast·briefing 등 툴을 노출.
graphiq-cli사람(터미널)graphiq search/context/blast… 처럼 사람이 직접 질의. main.rs가 184KB에 이르는 가장 큰 단일 파일.
apps/desktop브라우저/데스크탑색인을 시각적으로 둘러보는 인덱스 브라우저 앱.
graphiq-bench
+ benches/
품질 측정NDCG/MRR 벤치마크 하네스와 질의 세트. "grep 대비 +63%"가 나온 곳.
용어
MCP (Model Context Protocol)
AI 에이전트와 외부 도구를 잇는 표준 콘센트 규격. "이 서버는 이런 툴(함수)을 제공한다"를 약속된 형식으로 알려 주면, Claude Code 같은 호스트가 그 툴을 직접 호출합니다. USB가 기기 종류와 무관하게 꽂히듯, MCP는 도구가 무엇이든 에이전트에 똑같이 꽂힙니다. GraphIQ는 자신을 코드 검색·영향분석 툴 묶음을 가진 MCP 서버로 내놓으며, 표면(native/Signet/Pi)에 따라 툴 이름 접두사가 달라집니다.

③ 반전: '임베딩 없음'인데 임베딩 코드가 있다 (단, 꺼져 있음)

흥미로운 디테일입니다. Cargo.toml을 보면 candle(Rust 딥러닝), llama-cpp-2(로컬 LLM), wgpu(GPU) 의존성이 선택적(optional) 기능으로 들어 있고, 기본값은 전부 꺼짐(default = [])입니다. 소스에도 embed.rs·gpu_compute.rs가 존재하지만 피처 게이트로 막혀 있습니다.

피처켜면 추가되는 것기본값
embed-candlecandle-core/nn/transformers, tokenizers, hf-hub (임베딩)꺼짐
embedllama-cpp-2, ureq (로컬 LLM/다운로드)꺼짐
gpuwgpu, pollster (GPU 가속)꺼짐

즉 GraphIQ는 "임베딩을 할 줄 모르는 게 아니라, 안 켜는 것을 기본으로 택한" 프로젝트입니다. 29단계 실험에서 "BM25+구조만으로 충분하더라"는 결론을 내렸기에, 무거운 경로는 옵션으로만 남겨 둔 것이죠. "기본은 가볍게, 필요하면 켜라"는 성숙한 설계 태도입니다.

④ 인프라 · 빌드 · 배포

항목내용
배포 형태단일 바이너리 + SQLite 내장. 런타임 의존성·외부 서비스 0.
설치Homebrew, install.sh 스크립트, 또는 소스 빌드. rust-toolchain.toml로 툴체인 고정.
CIGitHub Actions linux-smoke 워크플로 — 핵심 동작 회귀 테스트.
문서docs/가 매우 충실 — 동작 원리(18KB), 연구 노트(40KB, 29단계), 벤치마크, 신뢰성, 하드닝 로드맵까지.
에이전트 통합graphiq setup/sync/discover로 Claude Code·Codex·Cursor·Pi 등에 자동 연결. skills/graphiq/SKILL.md 동봉.

4아키텍처 심화 분석 (코드 한 줄이 검색결과가 되기까지)

"두 개의 큰 막 — 색인(그래프를 만든다)과 검색(5단계 파이프라인으로 순위를 매긴다)."

전체 데이터 흐름 한 장

── 색인(INDEX) ───────────────────────────────────── [ 파일 시스템 ] │ walk_project (ignore: .gitignore 존중) ▼ ┌────────────────┐ tree-sitter 파싱 (16개 언어) │ 심볼 추출 │ 함수·타입·변수·import + 시그니처/주석/본문 └───────┬────────┘ ▼ ┌────────────────┐ 구조 엣지: calls/imports/contains/extends… │ 엣지 추출 │ deep 엣지: shares_type/error/data_shape/constant └───────┬────────┘ ▼ ┌────────────────┐ 이름·호출·경로로 역할 태그 19 + 모티프 8 추론 │ hints 컬럼 │ → BM25가 '역할 어휘'를 질의시점 비용 0으로 매칭 └───────┬────────┘ ▼ ╔══════════════════════════════╗ ┌────────────────────┐ ║ SQLite (.graphiq/graphiq.db) ║ │ CruncherIndex ║ ║ symbols · edges · files · ║ ──▶ │ (인메모리: 인접리스트 │ ║ symbols_fts (FTS5/BM25) ║ │ ·IDF·이웃항) │ ╚══════════════════════════════╝ │ → cruncher.bin.zst │ └────────────────────┘ ── 검색(SEARCH) 5단계 ─────────────────────────────── 질의 ──▶ ① 패밀리 분류(8종) ──▶ ② 시드 생성(BM25+확장) ──▶ ③ 그래프 워크(BFS) ──▶ ④ 스코어링(패밀리별) ──▶ ⑤ 후처리(BM25잠금·다양성) ──▶ 랭킹 결과

색인은 파일을 걷고 → tree-sitter로 뜯어 심볼을 뽑고 → 엣지를 잇고 → 역할 힌트를 붙여 → SQLite와 인메모리 색인에 적재하는 흐름입니다. 검색은 5단계 파이프라인으로, 한 질의가 분류 → 후보 → 확장 → 채점 → 정리를 거쳐 순위가 됩니다. 아래에서 설계 포인트를 하나씩 봅니다.

설계 ① — 저장을 둘로: 디스크의 SQLite + 메모리의 CruncherIndex

GraphIQ는 같은 데이터를 두 군데에 둡니다. SQLite(4개 테이블: symbols·edges·files·symbols_fts)는 영속 저장과 BM25 검색을 맡고, CruncherIndex는 그래프 워크를 위해 인접 리스트·IDF 가중치·이웃 용어·구조 차수를 전부 메모리에 미리 계산해 둡니다. 후자는 cruncher.bin.zst로 압축 캐시되어, 두 번째 실행부터는 ~50ms 만에 뜹니다.

비유

SQLite는 도서관 서고(모든 책이 안전하게 보관·검색됨), CruncherIndex는 사서 책상 위에 펼쳐 둔 색인 카드와 상호참조 지도입니다. 매번 서고를 뒤지지 않고 책상 위 지도로 즉답하되, 지도는 한 번 만들어 두면 압축해 보관했다가 다음에 그대로 펴면 됩니다.

설계 ② — hints 컬럼: '이름에 없는 역할'을 색인에 미리 새긴다

가장 영리한 트릭입니다. 색인 시점에 GraphIQ는 심볼 이름·호출 패턴·파일 경로를 보고 19개 역할 태그(validator, cache, handler, retry, auth-gate…)와 8개 구조 모티프(connector, orchestrator, hub, guard, transform, sink, source, leaf)를 추론해 FTS의 hints 컬럼에 적어 둡니다. 그러면 함수 이름엔 없는 단어로도 검색이 됩니다 — 예: ensureFreshness라는 캐시 검증 함수에 "cache validate check verify" 힌트가 박혀, "validate cache entry" 질의가 이 함수를 찾아냅니다. 질의 시점 비용은 0(이미 색인돼 있으니).

설계 ③ — 질의 패밀리: '의도'가 아니라 '권한 경계'를 정한다

검색 1단계에서 모든 질의는 8개 패밀리 중 하나로 분류됩니다.

패밀리감지 신호예시
SymbolExactPascalCase, snake_case, ::RateLimiter
FilePath경로 구분자, 확장자scheduler/worker.rs
ErrorDebugerror/panic/timeout/crash"timeout in channel send"
NaturalDescriptive동작 서술(기본 자연어)"encode a value in VLQ"
NaturalAbstract"how does…", "what controls…""how does auth work"
CrossCuttingSet"all", "every", 복수형"all connector implementations"
Relationship"what calls", "callers of""RateLimiter vs TokenBucket"

핵심 통찰은 이것입니다 — 분류기는 "사용자 의도"를 맞히는 게 아니라, "어떤 신호를 켜도 되는지" 권한 경계를 정한다. 각 패밀리는 RetrievalPolicy(BM25 잠금 강도·다양성·증거 가중)와 ScoreConfig(채점 가중치·기능 게이트)를 내놓습니다. 심볼 검색은 BM25를 믿고, 추상 질문은 구조 확장에 기대고, "전부(all)" 질의는 다양성을 최대화 — 질의 종류마다 신호 조합이 다르기 때문입니다.

설계 ④ — 시드 생성: BM25를 중심으로 다섯 갈래 확장

2단계는 후보(seed)를 모읍니다. 모든 질의는 FTS5 BM25로 시작하는데, 컬럼마다 가중치가 다릅니다(이름이 본문보다 10배 중요).

FTS 컬럼가중치내용
name10.0심볼 이름
decomposed8.0식별자 분해(RateLimiter → rate, limiter)
qualified · hints · sig6 · 5 · 4정규화 이름 · 역할 힌트 · 시그니처
file_path · doc · source3.5 · 3 · 1경로 · 문서주석 · 소스 본문

자연어 질의에선 여기에 네 갈래가 더해집니다 — per-term 확장(어간추출·동의어), numeric bridge("30초 후" 같은 숫자로 shares_constant 엣지 탐색), source scan(에러 질의 한정, 소스 본문 직접 매칭), graph-aware 확장(상위 시드에서 의미 엣지를 타고 확장). 패밀리별로 어떤 갈래를 켤지 시드 활성화 매트릭스가 정해 줍니다(심볼 질의는 BM25만, 자연어는 전부).

설계 ⑤ — 그래프 워크: BM25가 놓친 것을 구조가 줍는다

3단계가 GraphIQ의 가장 강한 구조 신호입니다. 상위 8개 시드에서 출발해 구조 그래프를 BFS로 최대 깊이 2까지 걷되, 이웃마다 두 관문을 통과해야 합니다.

IDF 게이트이웃이 희귀한(중간값 이상 IDF) 질의어를 하나라도 매칭 — 흔한 유틸 함수 거름
커버리지 게이트이웃이 자기 텍스트로도 질의어를 하나는 매칭 — 순수 구조만으로의 오답 방지

걷기 증거는 커버리지 × 0.5^깊이(근접 감쇠) × 엣지 가중치로 계산합니다. 이 덕에 BM25가 텍스트로는 못 찾지만 구조로 연결된 심볼을 주워 옵니다 — README는 관계(Relationship) 질의에서 grep 대비 3.7배 우수하다고 말합니다(타입 흐름·에러 표면·데이터 모양을 공유하는 심볼을, 질의와 글자가 안 겹쳐도 찾기 때문).

설계 ⑥ — 스코어링과 후처리: 더하기는 승격, 곱하기는 정돈, 그리고 잠금

4단계 채점은 패밀리별 가중치(BM25 w / 커버리지 w / 이름 w / 워크 w)로 베이스 점수를 만든 뒤, 가산(additive) 보정을 더합니다 — 이름 겹침이웃 지문(1-hop 이웃 용어와의 일치)인데, 둘 다 신뢰 게이트(threshold) 아래면 기여가 정확히 0입니다. 이어 곱셈(multiplicative) 보정으로 커버리지·종류(함수/타입↑, 변수/import↓)·테스트 페널티를 반영합니다. 5단계 후처리는 BM25 신뢰 잠금(1등이 2등보다 1.2배 이상 앞서고 이름이 질의어를 포함하면 1등으로 고정)과 파일 다양성 상한(한 파일이 결과를 독식하지 못하게 1~5개로 제한)입니다.

설계 철학 7원칙 (연구 노트 요약)

"BM25는 이기기 어렵다. 단순한 게 낫다. 신호엔 게이트를 걸어라."

BM25는 이기기 어렵다 — 늘 'BM25 검색 + 구조 재랭킹'이 승리. 단순한 게 낫다 — v1의 18GB 수학을 지우니 더 좋아짐. 신뢰가 중요 — BM25 잠금이 명백한 어휘 일치를 구조 신호가 뒤엎지 못하게. 가산이 곱셈을 이긴다 — 곱셈은 순서만 섞지만 가산은 후보를 진짜 끌어올림(단, 게이트 필수). 신호에 게이트를 — 이름 겹침 임계값이 코드베이스에 자동 적응. 패밀리 라우팅이 맞는 추상화. 기하는 계산하되 점수화하진 말라 — 리치 곡률 같은 토폴로지는 인프라일 뿐, 랭킹 신호는 커버리지·이름·워크 증거.

5디렉토리 구조 해부

"Cargo 워크스페이스 하나에 4개 크레이트 + 데스크탑 앱. 무게는 graphiq-core의 35개 모듈에 쏠려 있다."

최상위 한눈에

graphiq/ ├── Cargo.toml ← 워크스페이스(resolver 3): 4개 크레이트 묶음 ├── rust-toolchain.toml ← Rust 툴체인 고정 ├── install.sh ← 설치 스크립트 ├── crates/ │ ├── graphiq-core/ ← 엔진 본체 (라이브러리) — 모듈 35개 │ │ └── src/ index.rs(71KB)·db.rs(41KB)·cruncher.rs·… │ ├── graphiq-cli/ ← CLI 실행파일 (main.rs 184KB) │ ├── graphiq-mcp/ ← MCP 서버 (main.rs 123KB) │ └── graphiq-bench/ ← 벤치마크 하네스 ├── apps/desktop/ ← 시각적 인덱스 브라우저 앱 ├── benches/queries/ ← 벤치마크 질의 세트(NDCG/MRR 측정) ├── integrations/ ← 에이전트 하네스 연동 ├── signet-plugin/ ← Signet용 관리형 플러그인 ├── skills/graphiq/ ← 에이전트 스킬(SKILL.md) └── docs/ ← 동작원리·연구노트(40KB)·벤치마크·신뢰성
용어
Cargo 워크스페이스 / 크레이트(crate)
Rust에서 크레이트는 컴파일 단위(라이브러리 또는 실행파일)입니다. 워크스페이스는 여러 크레이트를 한 저장소에서 함께 빌드·의존성 공유하게 묶는 구조입니다. GraphIQ는 graphiq-core(엔진 로직 라이브러리)를 가운데 두고, graphiq-cli·graphiq-mcp·graphiq-bench가 그것을 가져다 쓰는 형태입니다. "로직은 한 번 짜고, 창구는 여러 개"라는 깔끔한 분리입니다.

graphiq-core의 35개 모듈을 기능별로 묶어 보기

모듈이 많아 보이지만, 역할로 묶으면 "검색엔진의 부품 목록"이 또렷해집니다.

묶음대표 모듈하는 일
색인·추출index.rs(71KB), files.rs, symbol.rs, edge.rs, languages/, manifest.rs, chunker.rs파일을 걷고 → tree-sitter로 심볼/엣지 추출 → 파일 메타·청크 관리
그래프·딥엣지graph.rs, deep_graph.rs, edge_evidence.rs, calls.rs, numeric_bridges.rs구조 그래프 본체 + 의미 엣지(타입/에러/데이터모양/상수) 계산
인메모리 색인cruncher.rs(34KB), cache.rs인접리스트·IDF·이웃항을 메모리에 미리 계산(CruncherIndex) + 캐시
검색 파이프라인query_family.rs, seeds.rs, pipeline.rs, search.rs, scoring.rs, rerank.rs(24KB), fts.rs분류 → 시드 → 워크 → 채점 → 재랭킹의 5단계 + FTS5 연동
텍스트·역할tokenize.rs, decompose.rs(27KB), roles.rs, motifs.rs, behavioral.rs식별자 분해·토큰화·역할 태그 19종·모티프 8종·행동 힌트 추론
분석·영향impact.rs(34KB), subsystems.rs(47KB), structural_alias.rs(27KB), dead_code.rs, trace.rs변경 영향(blast)·서브시스템 구조·구조적 별칭·죽은코드·추적
저장·선택기능db.rs(41KB), embed.rs, gpu_compute.rs(31KB)SQLite 스키마/질의 + (기본 꺼짐) 임베딩·GPU 경로
비유

검색엔진 공장으로 보면, 색인·추출은 원자재 가공 라인, 그래프·딥엣지는 부품 간 배선도, 인메모리 색인은 작업대 위 즉시조회 선반, 검색 파이프라인은 주문을 받아 처리하는 5단계 컨베이어, 분석·영향은 품질·위험 검사실입니다. db.rs 한 파일이 41KB인 건 SQLite 스키마·질의가 그만큼 엔진의 중심이라는 뜻입니다.

가장 큰 두 파일이 말해 주는 것

index.rs(71KB)가 코어에서 가장 크다는 건 "색인의 정확함이 검색의 전부"라는 철학을 보여 줍니다(엣지·힌트를 잘 만들어 둬야 검색이 산다). 그리고 graphiq-cli/main.rs(184KB)·graphiq-mcp/main.rs(123KB)가 거대한 건, 사람용·에이전트용 표면(surface)에 명령·툴·출력 포맷이 그만큼 촘촘히 들어 있다는 뜻입니다.

6학습 포인트 (기술별 — 배울 것 + 실습 아이디어)

"GraphIQ 한 레포 안에 정보검색(IR)·컴파일러 프런트엔드·그래프 알고리즘·Rust 시스템·도구 철학이 다 들어 있다."

① 정보검색(IR) 기초 — BM25 · FTS5 · IDF

배울 것: 임베딩 없이도 강력한 고전 검색의 핵심. TF-IDF에서 BM25로 이어지는 발상, 그리고 SQLite FTS5만으로 전문검색을 돌리는 법. "왜 이름 컬럼에 10배 가중을 주나" 같은 가중 컬럼 설계도.

실습: SQLite로 FTS5 가상 테이블을 만들어 작은 코드 모음을 색인하고, bm25() 함수로 "rate limit" 검색 결과를 점수순으로 출력해 보기. 컬럼 가중치를 바꿔 순위가 어떻게 변하는지 관찰.

② tree-sitter · AST 멀티언어 추출

배울 것: 소스를 텍스트가 아니라 구문 트리로 다루는 법. 언어가 달라도 같은 노드/엣지 스키마로 환원하는 디스패치 구조(languages/), 함수·호출·import를 트리에서 잡아내는 쿼리.

실습: tree-sitter로 작은 Python 파일을 파싱해 함수 이름을 전부 출력하고, 그다음 "함수 호출(call)" 노드를 잡아 호출 목록을 뽑아 보기 = GraphIQ calls 엣지의 축소판.

③ 그래프 모델링 + BFS 워크

배울 것: 심볼=노드/관계=엣지 스키마를 안정 ID로 설계하고, 빠른 조회를 위한 인접 리스트(정방향·역방향)를 만드는 법. 그리고 근접 감쇠(0.5^깊이)를 곁들인 BFS로 이웃을 점수와 함께 확장하는 패턴.

실습: map[심볼][]엣지 두 개(정/역방향)로 미니 호출 그래프를 만들고, 시드에서 깊이 2까지 BFS하며 0.5^깊이 × 엣지가중으로 이웃 점수를 누적해 보기.

④ AI 없는 질의 이해 — 규칙기반 분류

배울 것: LLM 없이 자연어 질의를 패밀리로 분류하고, 분류가 "의도"가 아니라 "어떤 신호를 켤지 권한"을 정하게 만드는 설계. 우선순위 캐스케이드로 오분류를 막는 법(예: "all error types"는 cross-cutting이지 error-debug가 아님).

실습: 정규식 몇 개로 질의를 SymbolExact / FilePath / Natural 셋으로 나누는 분류기를 짜고, 각 분류마다 "BM25만" vs "BM25+그래프"를 켜는 정책 객체를 반환해 보기.

⑤ 랭킹 평가 — NDCG · MRR · Hit@K

배울 것: "검색이 좋아졌다"를 숫자로 증명하는 법. 정답 세트를 만들고, 상위 K 결과로 Hit/MRR/NDCG를 계산해 grep 같은 베이스라인과 비교하는 벤치마크 설계(benches/queries/가 본보기).

실습: 질의 10개와 정답을 손으로 정해 두고, 내 검색기와 단순 grep의 결과로 Hit@10·MRR@10을 계산해 표로 비교. "어느 질의 유형에서 격차가 큰가"를 분석.

⑥ Rust 시스템 프로그래밍

배울 것: Cargo 워크스페이스로 라이브러리/CLI/서버를 분리하기, rayon으로 색인을 병렬화하기, bincode+zstd로 인메모리 구조를 압축 캐시하기, 콘텐츠 해시로 증분 색인하기 — "혼자서도 배포 가능한 빠른 도구"의 전형.

실습: 작은 Rust 워크스페이스(core 라이브러리 + cli)를 만들고, 파일 목록을 rayon으로 병렬 해시(sha2)한 뒤 결과 구조체를 bincode+zstd로 저장/복원해 보기.

⑦ 도구 철학 — 단순함 · 게이팅 · 가산>곱셈

배울 것: 이 레포 전체를 관통하는 사상. "복잡한 수학이 항상 이기는 건 아니다 — 검증된 베이스라인(BM25)에 잘 게이트된 신호 몇 개를 더하는 게 낫다"는 관점. docs/research.md(40KB)는 그 자체로 훌륭한 엔지니어링 에세이입니다.

실습: docs/research.md를 읽고 "삭제된 기능 5가지와 그 이유"를 한 페이지로 요약해 보기. 내 프로젝트에서 "효과 대비 복잡한" 부분을 하나 찾아 제거 실험을 설계.

7하드웨어 / 시스템 요구사항

"GPU·클라우드·DB·모델 전부 필요 없다. 드는 건 'Rust 빌드 한 번'과 '심볼 수에 비례하는 약간의 RAM'뿐."

항목요구사항 / 메모
언어/런타임Rust 툴체인으로 빌드(또는 Homebrew·install.sh로 설치). 결과는 단일 바이너리라 런타임 의존성 0.
외부 서비스없음. 데이터베이스·벡터DB·클라우드 API·임베딩 모델 전부 불필요. SQLite가 내장(bundled)이라 별도 설치도 없음.
GPU불필요. gpu·임베딩 피처는 기본 꺼짐. 기본 동작은 100% CPU·오프라인.
네트워크불필요. 색인·검색에 네트워크 호출이 없다 = 에어갭 환경·사내 코드에 안전.
메모리(RAM)심볼 수에 비례. 인메모리 CruncherIndex가 핵심 — 20K 심볼 캐시가 ~6.5MB. 거대 모노레포는 더 들지만, 임베딩 방식보다 훨씬 가볍다.
CPU 제어GRAPHIQ_MAX_THREADS / RAYON_NUM_THREADS로 스레드 상한. 세션/백그라운드 모드는 기본 4스레드, 포그라운드 색인은 전체 코어 사용.
디스크.graphiq/graphiq.db(SQLite) + cruncher.bin.zst(압축 인메모리 캐시). 프로젝트 폴더 안에 생긴다.
속도콜드(첫 실행) ~5–10초(캐시 빌드), 웜 CLI ~50ms, MCP 인프로세스 ~18µs.
OS크로스 플랫폼(Linux CI smoke 테스트 운영). 백그라운드 데몬은 세션 종료 시 자동 정리(dormancy).
신뢰성 디테일
"백그라운드에서 조용히, 망가지지 않게"

GraphIQ는 에이전트가 띄운 데몬으로 상주할 수 있어 크래시 안전성을 신경 씁니다 — 콘텐츠 해시 증분 색인(바뀐 파일만 재파싱), 0-심볼 색인도 크래시 없이 "결과 없음"으로 우아하게 처리, 출력을 head로 받다 끊겨도 패닉 대신 SIGPIPE(exit 141)로 종료(ripgrep·fd와 같은 정석). 락파일·256KB 초과 JSON/YAML/TOML은 심볼 추출에서 제외해 그래프가 잡음으로 오염되지 않게 합니다.

함정
거대 모노레포 = 인메모리 색인이 RAM을 먹는다

모든 그래프 데이터를 메모리에 올리는 설계라, 수십만 심볼 규모(예: TensorFlow 433K 심볼)에선 RAM 사용이 올라갑니다. 완화책으로 백그라운드 저메모리 프로파일(GRAPHIQ_INDEX_MODE=background)과 소스 토큰 창 축소(GRAPHIQ_SOURCE_TERM_LIMIT)가 있지만, 초대형 레포에서는 색인 시간·메모리를 미리 가늠하는 게 좋습니다.

8직접 해볼 수 있는 실습 과제

"색인해서 검색해 보기 → SQLite 속을 들여다보기 → 에이전트에 물리기 → 파이프라인을 읽고 → 미니 엔진을 만들기."

과제 1난이도 ★☆☆☆☆ · 입문

내 레포를 색인하고 검색해 보기 (콜드 vs 웜 체감)

설치 후 graphiq index /내/프로젝트로 색인 → graphiq search "rate limit middleware"처럼 자연어로 검색. 같은 검색을 두 번 돌려 첫 실행(캐시 빌드)과 두 번째(웜)의 속도 차이를 느껴 본다.

목표: "이름이 아니라 동작 설명으로도 코드가 찾아진다"를 체감. 같은 질의를 grep -r로도 해 보고 결과 품질을 눈으로 비교.

과제 2난이도 ★★☆☆☆ · 초급

SQLite 색인 속을 직접 열어 보기

sqlite3 .graphiq/graphiq.db로 접속해 .tables로 4개 테이블을 확인하고, symbols·edges 행을 들여다본다. symbols_ftsbm25() 질의를 직접 날려 본다.

목표: "검색엔진이 별게 아니라 잘 설계된 SQLite 테이블"임을 깨닫기. edges에서 shares_error_type 같은 deep 엣지를 찾아 "이게 왜 연결됐지?"를 추적.

과제 3난이도 ★★★☆☆ · 중급

Claude Code에 MCP로 물리고 8개 질의 패밀리 실험

graphiq setup으로 에이전트에 연결한 뒤, 심볼·파일경로·에러·자연어·관계 등 서로 다른 유형의 질의를 던져 본다. explain/why 툴로 "왜 이 결과가 1등인지" 근거를 받아 본다.

목표: 같은 엔진이 질의 유형마다 다른 신호를 켜는 것을 관찰. "what calls X"(관계)와 "RateLimiter"(심볼)에서 결과 성격이 어떻게 달라지나 비교.

과제 4난이도 ★★★★☆ · 중상

한 질의가 5단계를 통과하는 길 추적하기

crates/graphiq-core/srcquery_family.rsseeds.rspipeline.rsscoring.rs를 순서대로 읽으며, 자연어 질의 하나가 분류 → 시드 → 워크 → 채점을 어떻게 지나는지 손으로 따라가 본다.

목표: docs/how-graphiq-works.md의 설명과 실제 코드를 대조. "왜 ErrorDebug만 source_scan을 켜나" 같은 게이팅 결정을 코드에서 확인.

과제 5난이도 ★★★★★ · 고급

'미니 GraphIQ' 직접 만들기

좋아하는 언어로 작은 코드 검색기를 만든다 — tree-sitter로 함수 추출 → SQLite FTS5에 색인 → BM25로 검색 → calls 엣지로 1-hop 재랭킹. GraphIQ의 "BM25 검색 + 구조 재랭킹" 패턴을 축소 재현.

목표: 질의 세트로 BM25 단독 vs BM25+1-hop 재랭킹의 NDCG@10을 비교해, 구조 신호가 실제로 순위를 끌어올리는지 수치로 확인. 이게 GraphIQ 논지의 핵심 검증.

9관련 기술 심화 학습 로드맵 (6주 플랜)

"Rust 기초에서 출발해 정보검색·파싱·그래프·랭킹 평가까지, GraphIQ를 길잡이 삼아."

주차주제핵심 학습 + GraphIQ 연결점
1주Rust 기초 + 워크스페이스소유권·에러처리·Cargo 워크스페이스로 라이브러리/CLI 분리 → Cargo.toml·crates/graphiq-core·cli
2주정보검색(IR) 기초TF-IDF → BM25, SQLite FTS5로 전문검색, 가중 컬럼 설계 → fts.rs·symbols_fts
3주파싱 · AST · tree-sitter구문 트리에서 심볼/호출/import 추출, 멀티언어 디스패치 → index.rs·languages/
4주그래프 모델링 + 순회인접 리스트(정/역방향), BFS 워크, IDF·근접 감쇠 → graph.rs·cruncher.rs·pipeline.rs
5주질의 이해 + 채점 + 평가규칙기반 패밀리 분류, 가산/곱셈 채점, NDCG/MRR 벤치마크 → query_family.rs·scoring.rs·benches/
6주MCP 서버 + 도구 철학에이전트 툴 표면 설계, 신뢰성/도먼시, 연구 회고 정독 → graphiq-mcp + docs/research.md
비유

이 로드맵은 "정보검색 + 컴파일러 프런트엔드(파싱) + 그래프 알고리즘 + 시스템/도구화"를 한 줄로 꿴 코스입니다. 특히 GraphIQ는 "화려한 AI 없이 검증된 기법을 잘 조합하면 어디까지 가는가"를 보여주는 살아 있는 사례라, 검색·랭킹을 공부하는 사람에게 이론과 실전을 동시에 잡게 해 줍니다.

10핵심 키워드 사전

"이 문서와 저장소에서 반복되는 용어를 한곳에 모았다."

용어의미
코드 인텔리전스 엔진소스를 구조 데이터로 바꿔 '정의·참조·호출' 같은 코드 내비게이션과 자연어 검색을 API로 제공하는 도구
구조 그래프심볼=노드, 관계=엣지로 코드를 표현한 그물망. GraphIQ의 본체
BM25TF(빈도)와 IDF(희귀도)를 결합한 고전 전문검색 랭킹 공식. 임베딩 없이도 강력
FTS5SQLite 내장 전문검색 엔진. BM25를 기본 제공 — 별도 검색서버가 필요 없음
IDF (역문서빈도)흔한 단어는 깎고 희귀한 단어를 키우는 가중치. GraphIQ는 그래프 워크의 'IDF 게이트'에도 씀
NDCG@10 / MRR@10 / Hit@10검색 품질 채점표. 정답을 얼마나 위쪽에 잘 모았나(NDCG)·첫 정답 순위(MRR)·상위10 적중(Hit). 높을수록 좋음
임베딩 (embedding)코드를 AI 모델로 벡터화해 의미 유사도로 검색하는 방식. GraphIQ는 일부러 안 씀(코드는 있으나 기본 꺼짐)
tree-sitter / AST여러 언어를 같은 방식으로 구문 트리(AST)로 파싱하는 파서. 16개 언어 심볼 추출에 사용
CruncherIndex인접리스트·IDF·이웃항·구조차수를 메모리에 미리 계산한 인메모리 색인. cruncher.bin.zst로 압축 캐시
질의 패밀리 (query family)질의를 8종(SymbolExact·FilePath·ErrorDebug·NaturalDescriptive·NaturalAbstract·CrossCuttingSet·Relationship 등)으로 분류해 신호를 다르게 켬
RetrievalPolicy / ScoreConfig패밀리가 내놓는 '권한 경계' — BM25 잠금 강도·다양성·채점 가중·기능 게이트를 정의
구조 엣지 / deep 엣지구조: calls·imports·contains·extends·implements / deep: shares_type·shares_error_type·shares_data_shape·shares_constant·comment_ref
hints 컬럼색인 시점에 추론한 역할 태그 19종 + 모티프 8종을 FTS에 새겨, 이름에 없는 단어로도 검색되게 함
그래프 워크 (graph walk)시드에서 BFS로 이웃을 확장하는 재랭킹 단계. 커버리지 × 0.5^깊이 × 엣지가중으로 증거 누적
시드 생성 (seed)BM25 + per-term 확장 + numeric bridge + source scan + graph-aware로 1차 후보를 모으는 단계
이웃 지문 (neighbor fingerprint)후보의 1-hop 이웃 용어와 질의어의 겹침. 동명이의 일반 이름을 문맥으로 구별하는 가산 신호
BM25 신뢰 잠금BM25 1등이 2등보다 1.2배 앞서고 이름이 질의어를 포함하면 1등으로 고정 — 구조 신호의 과잉승격 방지
파일 다양성 상한한 파일이 상위 결과를 독식하지 못하게 1~5개로 제한(패밀리별)
blast radius (영향 반경)한 심볼을 바꿀 때 영향받는 범위. 의존 엣지를 역방향으로 따라가 계산(blast 툴)
콘텐츠 해시 증분파일 내용 해시로 바뀐 파일만 다시 파싱하는 색인 방식(sha2)
specificity (특이도)질의어 중 IDF가 높은(희귀한) 비율. 높으면 BM25 가중↑, 낮으면 커버리지 가중↑로 동적 조정
rayon / zstd / bincode병렬 처리 / 압축 / 직렬화 — 인덱싱 병렬화와 인메모리 캐시 저장에 사용
MCP에이전트와 도구를 잇는 표준 규격. GraphIQ는 코드검색 툴을 가진 MCP 서버로 동작

11참고 링크