46개 저장소를 아우르는 단일 지식 그래프 구축하기

저는 airCloset의 CTO인 Ryan입니다.

저는 3개월 동안 code-graph를 구축하는 데 시간을 보냈습니다. 이는 여러 서비스에 걸친 46개의 저장소를 통합하는 단일 지식 그래프입니다.

많은 사람들이 모든 코드를 AI에게 건네주고 질문만 하면 된다고 생각합니다. 하지만 여기에는 두 가지 이유로 한계가 있습니다.

  • 컨텍스트 윈도우(Context windows): 46개 저장소에 담긴 수년간의 코드를 하나의 프롬프트에 모두 담을 수는 없습니다.
  • 환각(Hallucination): AI는 관계를 추론하려고 할 때 실수를 합니다. 연결 고리를 놓치기도 합니다.

이를 해결하기 위해, 저는 정적 분석(static analysis)을 사용하여 신뢰할 수 있는 단일 출처(source of truth)를 구축했습니다.

도전 과제: 경계를 넘나들기

대규모 코드베이스는 복잡합니다. 하나의 API가 다섯 개의 서로 다른 저장소에서 호출될 수 있습니다. 하나의 데이터베이스 테이블이 세 개의 서로 다른 서비스에서 사용될 수 있습니다.

하나의 저장소만 본다면 전체 그림을 놓치게 됩니다. 이는 위험합니다. 코드를 변경할 때 실제 영향 범위(blast radius)를 파악하지 못하면 시스템이 망가질 수 있습니다.

저의 방식은 tree-sitter를 사용하여 코드를 구문 트리(syntax trees)로 파싱하는 것입니다. 하지만 tree-sitter만으로는 저장소 간의 경계를 넘나들 수 없습니다.

이를 해결하기 위해 저는 경계 노드(boundary nodes)를 구축했습니다.

작동 방식:

  • tree-sitter를 사용하여 저장소 내의 관계를 추출합니다.
  • TypeScript Compiler API를 사용하여 타입과 변수를 해석합니다.
  • 도구가 놓치는 동적인 케이스를 처리하기 위해 Gemini를 사용합니다.

AI에게 추측하게 하는 대신 사실을 제공합니다. "이 API는 Repo X에서도 호출됩니다"라고 알려주는 식입니다. 이를 통해 환각 현상을 방지합니다.

어려운 점: 프레임워크의 숲

진짜 싸움은 이러한 경계를 추출하는 것이었습니다. 프레임워크마다 경계를 작성하는 방식이 모두 다릅니다.

어떤 팀은 NestJS 데코레이터를 사용하고, 어떤 팀은 Express 라우트를 사용하며, 또 다른 팀은 순수 jQuery를 사용합니다. 각각의 방식은 코드 내에 서로 다른 구조를 만듭니다.

이를 구현하기 위해 다음과 같은 항목에 대한 커스텀 파서를 구축해야 했습니다:

  • NestJS 및 TypeORM
  • Express 및 Fastify
  • AngularJS 및 Redux
  • 다양한 경로 별칭(path-alias) 방식

우리는 99%의 정확도를 목표로 해야 했습니다. 연결률이 90%에 불과하다면, AI는 연결의 10%를 놓치게 됩니다. 운영 환경(production system)에서 그 10%는 바로 버그가 숨어 있는 곳입니다.

현재는 매일 점검을 수행합니다. 연결률이 5% 이상 떨어지면 알림을 받습니다. 이를 통해 새로운 코드 패턴이 파서를 망가뜨리는 경우를 잡아낼 수 있습니다.

현재의 한계점

그래프가 완벽하지는 않습니다.

  • 검색이 어렵습니다. 검색을 시작하려면 종종 함수 이름을 알고 있어야 합니다.
  • 노드 폭발(Node explosion). 경로를 따라가다 보면 수천 개의 작고 쓸모없는 헬퍼 함수들이 딸려 들어올 수 있습니다.
  • 유지보수. 새로운 프레임워크가 스택에 추가될 때마다 새로운 파서를 작성해야 합니다.

이것은 파트 1입니다. 파트 2에서는 이러한 간극을 메우기 위해 구축한 service-product-graph (SPG) 레이어에 대해 논의하겠습니다.

Source: https://dev.to/ryantsuji/building-one-knowledge-graph-across-46-repositories-with-static-analysis-part-1-egm

Optional learning community: https://t.me/GyaanSetuAi