이 글은 Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady에 제출하는 글입니다.
제가 만든 것 (What I Built)
커밋 메시지를 작성하는 것은 개발자들이 끊임없이 하지만 잘 즐기지 않는 작은 작업 중 하나입니다. 이슈를 디버깅하거나 기능을 구현한 후, 변경 사항을 명확하게 설명하는 것이 불필요한 마찰처럼 느껴질 수 있습니다.
GAI는 외부 LLM API에 의존하지 않고 개발 워크플로우의 이 부분을 자동화하려는 저의 시도입니다.
GAI는 스테이징된 Git 변경 사항을 분석하고, 히스토리 커밋 데이터에서 유사한 커밋을 검색하며, 로컬에서 실행되는 언어 모델(language model)을 사용하여 리포지토리에 존재하는 기존 태깅 규칙을 따르는 커밋 메시지를 생성합니다.
단순히 전체 Git diff를 모델에 보내는 대신, GAI는 두 가지 접근 방식을 결합합니다:
- 검색 증강 생성 (Retrieval-augmented generation, RAG): 임베딩(embeddings)과 FAISS를 사용하여 역사적으로 유사한 변경 사항을 찾습니다.
- 변경 사항 중심 diff 파싱 (Change-focused diff parsing): 파일 경로, 덩어리(hunks), 추가/삭제 내용 및 관련 컨텍스트를 추출하여 생성 모델이 변경 사항을 더 쉽게 이해하도록 만듭니다.
저는 커밋 메시지 작성의 마찰을 줄이기 위해 제 개발 워크플로우에서 GAI를 사용합니다.
생성된 메시지는 자동으로 커밋되지 않습니다. 저는 이를 수락하거나, 편집하거나, 다시 생성하거나, 작업을 취소할 수 있습니다. 커밋하기 전에 GAI는 또한 스테이징된 변경 사항이 메시지가 생성된 이후로 변경되지 않았는지 확인합니다.
핵심 아이디어는 간단합니다: 커밋 메시지 작성에 쓰는 시간을 줄이고 코드 작성에 더 많은 시간을 쓰자는 것입니다.
워크플로우 (Workflow)
flowchart TD
A([Run gai]) --> AZ[Invokes src/cli.py]
AZ --> B[Validate Git repository]
...
데모 (Demo)
GAI는 로컬 명령줄 도구(command-line tool)이므로 개발자의 환경에서 직접 실행됩니다. 제가 제 코드 변경 사항에 GAI를 사용하는 방법은 다음과 같습니다.
1. 변경 사항 스테이징하기 (Stage the changes)
git add README.md
git diff --cached --stat
⚠️ Staged git diff from VS code UI
2. 커밋 메시지 생성하기
gai terminal execution -- formatted-diff
gai terminal execution -- generating the commit using gemma
GAI는 유사한 과거 커밋을 검색하고, 메시지를 생성하며, 제가 커밋하기 전에 검토할 수 있도록 합니다.
3. 커밋 확인 (선택 사항)
Git log output showing the commit created using GAI's generated message
코드
GitHub 저장소: https://github.com/kira14102005/gai
GAI는 Python으로 구현되었으며, Python CLI를 통해서도 호출할 수 있습니다.
검색할 과거 예시의 개수를 제어할 수 있습니다:
python -m src.cli --top-k 3
저장소 README에는 설정 지침, FAISS 인덱스 준비 단계 및 현재 워크플로우에 대한 세부 정보가 포함되어 있습니다.
구현 방법
현재 구현은 Ollama, FAISS, EmbeddingGemma, 그리고 Gemma4:e2b를 사용합니다.
저는 태그, 메시지, diff를 포함하는 과거 커밋 데이터로 시작했습니다. 인덱싱 파이프라인은 EmbeddingGemma를 사용하여 사용 가능한 과거 diff에 대한 임베딩을 생성하고 이를 FAISS IndexFlatL2 인덱스에 저장합니다. 해당 커밋 메타데이터는 별도로 저장되어 검색된 벡터가 원래의 메시지 및 태그로 매핑될 수 있도록 합니다.
GAI를 실행하면 스테이징된 Git diff를 읽습니다. 원본 diff는 검색(retrieval)에 사용되며, 파싱되고 형식화된 표현은 생성 모델에게 실제 변경 사항을 더 명확하게 보여줍니다.
FAISS는 가장 관련성 높은 과거 커밋들을 검색하고, 그 메시지와 태그들은 형식화된 diff와 함께 생성 프롬프트에 포함됩니다. 이후 Gemma가 Ollama를 통해 후보 메시지를 생성합니다.
CLI(Command Line Interface)가 최종 검토를 처리합니다. 저는 커밋이 생성될지 여부를 계속 통제하며, GAI는 커밋하기 전에 스테이징된 diff가 변경되지 않았는지 확인합니다.
제가 해결하는 데 관심 있는 한 가지 과제는 작은 로컬 모델들이 큰 diff를 어떻게 처리하느냐입니다. 단순히 컨텍스트 윈도우(context window)를 늘리는 것이 항상 최선의 해결책은 아니며, 모델에게 더 구조화된 정보를 제공하는 것이 더 유용할 수 있습니다.
제가 계획하고 있는 다음 단계는 두 가지 생성 경로를 가진 병렬 LangGraph 워크플로우입니다:
- 현재 구현과 같이 과거 커밋을 사용하는 검색 기반 체인(retrieval-based chain).
- 파일을 단위로, 그리고 덩어리(hunk)별로 큰 변경 사항을 요약하여 또 다른 후보를 생성하는 diff 이해 체인(diff-understanding chain).
평가기(evaluator)는 두 후보 모두에 대해 정확성(correctness), 관련성(relevance), 구체성(specificity), 그리고 태그 유효성을 평가할 것입니다. 이후 축소기(reducer)는 더 강력한 후보를 선택하거나 필요할 때 더 나은 하나로 합성할 것입니다.
이러한 LangGraph 체인, 평가기, 그리고 축소기는 향후 개선 사항이며, 아직 현재 구현의 일부는 아닙니다.
Open Innovation이 중요한 이유
저는 GAI가 코드 변경에 대한 일반적인 설명 대신, 제가 실제로 커밋하는 방식이 반영된 커밋 메시지를 생성하기를 원했습니다.
그렇기 때문에 로컬 추론(local inference)과 과거 커밋 검색을 결합했습니다.
- 기존 관행 학습: FAISS는 유사한 과거 diff와 해당 커밋 메시지를 검색하여 Gemma에게 저장소의 태그 지정 관행과 작성 스타일의 구체적인 예시를 제공합니다.
- 코드 로컬 유지: Git diff에는 독점적인 구현 세부 정보가 포함될 수 있습니다. 임베딩 모델과 생성 모델을 모두 Ollama를 통해 실행하면 GAI가 외부 LLM API에 의존하지 않고 이러한 변경 사항을 로컬에서 처리할 수 있게 합니다.
- 구속받지 않고 실험: 특정 호스팅 AI 제공업체 주변으로 애플리케이션을 재설계할 필요 없이 생성 모델을 변경하거나, 프롬프트를 개선하거나, 검색 전략을 실험할 수 있습니다.
흥미로운 부분은 단순히 LLM을 로컬에서 실행하는 것이 아닙니다. 오픈 모델과 저장소 자체의 기록을 결합하여 해당 개발자가 작업하는 방식에 맞춰진 커밋 메시지 생성기를 구축하는 것입니다.
게으른 보상 (The Lazy Payoff)
목표는 반복적인 작업을 개발 루프에서 제거하는 것입니다.
커밋 메시지의 문구를 생각하는 것과 코딩을 전환하는 대신, 변경 사항을 스테이징하고 gai를 실행한 다음, 제안을 검토하고 계속할 수 있습니다.
인간의 검토 단계는 의도적입니다. 지루한 부분을 자동화한다고 해서 최종 커밋에 대한 통제권을 포기해야 한다는 의미는 아닙니다.
다음 계획 (What's Next?)
저는 생성된 메시지의 품질을 개선하기 위해 병렬 생성 체인, 계층적 diff 요약, 그리고 평가자/축소자(evaluator/reducer) 워크플로우를 실험할 계획입니다.
다른 분들이 커밋 메시지 생성을 어떻게 접근하는지, 특히 더 작은 로컬 모델을 사용할 때 궁금합니다. 이 파이프라인에서 어떤 부분을 개선하면 좋을까요?
시상 부문 (Prize Categories)
- Gemma 활용 최우수: GAI는 Ollama를 통해 _gemma4:e2b_를 사용하여 커밋 메시지를 로컬에서 생성하며, 오픈 가중치 AI와 과거 Git 커밋 검색을 결합합니다.