요약: 에이전트가 코드베이스를 조사하도록 할 때, 저는 결정론적 도구(ripgrep, ast-grep, Serena, CodeQL)의 출력을 증거로 인용하게 하고, LLM 자체 분석은 보고서에서 명확히 분리합니다.
"이 시스템의 외부 인터페이스가 변경되면 어디를 수정해야 하나요?"
저는 모르는 코드베이스에 직면했을 때 이런 질문에 답할 수 있기를 원했습니다.
그래서 에이전트에게 조사를 맡기려고 합니다. 만약 모든 것을 읽게만 한다면 컨텍스트 오버플로우가 발생합니다 (이미 많은 분들이 보셨을 겁니다?). 또한 결과가 재현 가능하기를 바랍니다.
그래서 저는 에이전트가 정적 분석 도구의 출력을 증거로 인용하게 할 수 있는 도구를 찾았습니다. 저는 정적 분석과 그 도구 분야를 정리하고, 채택하기 쉬운 정도와 커버리지 면에서 좁혔습니다.
초기 방향성
조금 조사한 결과, 저의 주요 정책은 다음과 같았습니다:
- 이전에 수행했던 조사를 바탕으로 조사 단계를 결정합니다.
- 해당 단계에 대해 결정론적 계산을 수행하는 도구를 준비하고, 가능한 한 그 출력에 기반하여 조사를 진행합니다.
제가 선택한 정적 분석 도구는 ripgrep, ast-grep, Serena, 그리고 CodeQL이었습니다.
조사 흐름 및 제가 선택한 도구들
먼저, 제가 직접 어떻게 조사할지 생각해 보았습니다. 몇 가지 경우가 있는 것 같았습니다: 큰 그림에서 세부 사항으로 이동하기, 일치하는 모든 문자열 찾기, 무언가를 참조하는 코드를 나열하기.
생각해 본 끝에, 이 조합이 상당히 넓은 범위까지 커버할 수 있을 것이라고 결정했습니다.
- ripgrep: grep의 빠르고 유용한 버전으로, 프로그래머의 가장 친한 친구입니다. 이름과 문자열을 철저하게 찾는 데 능숙하며, 다른 도구의 결과를 재확인하는 데도 좋습니다. 한편으로는 주석(comments)과 문자열 리터럴(string literals)을 코드와 구분해야 할 때 약간 까다롭습니다. 문법으로 범위를 좁히고 싶을 때는 ast-grep을 사용합니다. 타입이 필요할 때는 Serena를 사용합니다.
- ast-grep: AST(abstract syntax tree, 추상 구문 트리)의 노드를 검색합니다. 검색 쿼리를 유연하게 구축할 수 있습니다. grep으로도 충분할 수 있지만, 구조적 매칭(structural matching)을 사용하면 외부 통신 같은 것들을 나열하기가 쉬워져서 포함했습니다. 'AST'라는 말을 들으면 변환 과정에서 정보 손실(주석, 매크로, 전처리기 등)이 있을까 봐 걱정할 수 있습니다. 하지만 이 도구는 실제로는 CST(concrete syntax tree, 구체 구문 트리)를 내부적으로 사용하므로 주석을 문제없이 검색할 수 있습니다. 또한 매크로 및 전처리기 정의도 추출할 수 있지만, 이를 확장하지는 않습니다. 밑단에서 사용하는 파서 라이브러리인 Tree-sitter로 파서를 구축하면 다른 언어도 지원할 수 있을 것 같습니다 (검증되지 않음).
- Serena: MCP를 통해 IDE의 '정의로 이동(Go to Definition)' 및 '사용처 찾기(Find Usages)' 기능 뒤에서 실행되는 언어 서버(Language Server, LSP) 기능을 제공합니다. 심볼 수준의 작업들을 에이전트에게 전달합니다. 저는 이것을 특정하고 작은 코드 조각에서 더 깊이 파고드는 도구라고 생각합니다. 장점은 타입이 해결된 정확한 참조를 따라갈 수 있다는 것이지만, 인덱스를 구축하는 데 시간이 조금 걸립니다.
- CodeQL: 코드를 데이터베이스로 변환하여 여러 종류의 분석을 제공합니다. 주로 취약점과 오류를 찾는 데 사용되는 것 같지만, 데이터 흐름 분석(data flow analysis)을 수행하고 코드베이스의 핵심 지표를 계산할 수도 있습니다. 전체적인 그림을 파악하기 위해 포함했습니다. 한편으로는 네 가지 도구 중 가장 많은 준비가 필요합니다.
하지만 각 도구는 서로 다른 언어를 지원하며, 오래된 언어들은 다루기 어렵습니다.
제 경우, 어떤 시스템은 모든 도구를 사용할 수 있고, 어떤 시스템은 ripgrep만 사용할 수 있습니다. 후자의 경우, 실험 삼아 ripgrep 하나만으로 얼마나 멀리 갈 수 있는지 확인해 보았습니다.
시도하며 배운 점
이것은 작은 시험일 뿐입니다 (아직 CodeQL을 사용해 볼 수는 없었지만), 제가 배운 것은 다음과 같습니다.
도구 없이도 AI가 자체 도구를 구축했습니다. 오래된 언어를 조사하면서, 저는 AI에게 결정론적 계산에 대한 보고서를 가능한 한 많이 작성하도록 지시했습니다. 그러자 AI는 자신만의 분석 스크립트를 작성하고 실행했으며, 그 결과를 증거로 사용했습니다. 물론 생각해보면 그렇게 할 수 있다는 것은 알지만, 놀라웠습니다. 다만, AI가 구축한 도구 역시 검토가 필요하다는 점에 유의해야 합니다.
ripgrep과 ast-grep 사이의 차이는 제가 예상했던 바와 달랐습니다. 저는 두 가지를 모두 사용하여 외부 통신을 감지했고, 결과가 달랐습니다. 자세히 살펴보니, ripgrep은 주석까지 계산하고 있었습니다. 저는 ast-grep의 강점을 배웠고, 이로 인해 여러 도구로 검증하는 것이 얼마나 중요한지에 대해서도 생각하게 되었습니다.
Serena는 보조적인 역할을 수행했습니다. 이 간단한 시험에서, 저는 그것을 ast-grep과 교차 확인하고 결과 검증을 보완하는 용도로만 사용했습니다. 문자 인코딩 변환이 필요하거나 외부 라이브러리의 타입 정보가 누락되는 등의 몇 가지 문제에 부딪혔습니다. 그럼에도 불구하고, 제가 아직 시도해보지 않은 영역에서 Serena가 어떻게 도움이 될 수 있을지에 대한 아이디어를 얻었습니다.
- ast-grep이 플래그를 지정한 항목들에 대한 엄격한 확인 (예: 아마 호출되지 않을 메서드).
- 타입 정보를 사용하여 무언가가 실제로 사용되는지 여부 확인 (예: Java에서 인터페이스 구현을 해결하는 것).
- 원래 질문(가장 위에 있는)에 대한 영향 분석: 이 코드가 변경되면 무슨 일이 발생하는가?
저는 Serena가 이러한 영역들에서 상당히 효과적일 것이라고 생각합니다.
LLM은 읽기에도 유용합니다. 처음에는 제가 LLM에게 결정론적으로 생성된 결과만 해석하도록 했습니다. 하지만 그러다 보니 보고서의 가치가 떨어졌습니다. 코드의 실제 의미, 문서화되지 않은 규칙, 그리고 전체 프로그램의 흐름을 LLM이 해석하는 것이 값지다는 것을 깨달았습니다. 출력물에 검토가 필요하지만, 그 비용보다 얻는 가치가 훨씬 크다고 판단했습니다.
조사 대상마다 도구를 다시 선택하는 것이 고정된 도구 세트를 사용하는 것보다 낫습니다. 소스 코드와 데이터는 시스템마다 다릅니다. Java를 읽을 때는 Java 도구가 필요하고, JSON을 읽을 때는 JSON 도구가 필요합니다. 가장 적합한 조합이 계속 바뀝니다. 따라서 고정된 도구 세트만으로는 충분하지 않다고 느꼈고, 더 광범위한 범위의 도구를 조사해야 할 것 같습니다. 제가 확실치 않은 부분은 이 '도구 조사' 과정까지 얼마나 동적으로 만들 수 있는지입니다. 하지만 지금까지 LLM을 사용해 본 경험으로 미루어 볼 때, 저의 직감은 '도구 찾기 단계'와 '도구 사용 단계'를 분리해야 한다는 것입니다. 에이전트에게 몇 개의 도구를 제공할지 역시 앞으로 더 살펴봐야 할 부분입니다.
이러한 발견을 바탕으로 현재 제 정책은 다음과 같습니다.
- LLM이 보고서를 작성하도록 하되, 결정론적 계산을 수행하는 도구의 결과를 가능한 한 많이 인용하게 합니다.
- 추가적으로 LLM 자체의 에이전트 코드 분석 내용을 보고서에 포함하되, 위 내용과 명확히 분리합니다.
- AI가 구축한 도구의 경우, 해당 도구가 무엇을 하는지 소스 코드를 포함하여 보고서에 포함합니다.
- 조사 대상마다 도구를 다시 선택합니다. 도구 조사와 도구 사용은 분리하고, 조사를 시작하기 전에 '어떤 목적에 어떤 도구를 사용할지'를 정의합니다.
- AI 자체의 도구가 사용될 경우, 동일한 작업을 수행하는 다른 도구를 찾아보고 도구 정의 업데이트를 고려합니다.
생각 (Thoughts)
저는 이전에 RAG 시스템을 구축해 본 적이 있어서 증거와 인용(citation)이 얼마나 중요한지 알고 있다고 생각했습니다. 하지만 LLM에게 결정론적 계산을 도구로 제공하여 답변의 모호성을 좁히는 아이디어는 비로소 지금 깨닫게 된 것입니다.
지금까지 이 방향으로 논의가 진행될 때면, 저는 종종 'LLM 단계로 구성된 워크플로우가 최적이다'라고 생각했습니다. 하지만 이번 경우에는 에이전트가 스스로 도구를 선택하고 더 깊이 파고들기를 정말 원합니다. 다만, 결코 건너뛰게 하고 싶지 않은 단계들이 있습니다. 따라서 필요한 단계들이 먼저 실행되고, 그 결과가 에이전트에게 전달되며, 이후에 에이전트가 자체적으로 조사하는 프로세스가 필요할 것입니다. 이는 내부의 에이전트를 가진 워크플로우입니다.
반면에, 이것이 과도한 엔지니어링(over-engineering)일 수도 있다는 느낌도 듭니다. 제 인상으로는 최근의 적당히 똑똑한 모델들이 인간보다 지침을 더 잘 따른다는 것입니다. 더 많은 사람들이 이렇게 느끼면서, 확률적 답변에 대한 우려는 점차 변할 것이라고 생각합니다. 제가 이전 게시물에서 썼듯이, 얼마나 많은 오류를 허용할지에 대한 산업 표준이 향후 2~3년 안에 나타날 것이라고 생각합니다. 그럼에도 불구하고, 이 전환기 동안에는 안전한 쪽으로 기울이는 것이 최선의 관행입니다.
앞으로는 AI 출력을 설득력 있게 만드는 방법이 그 유용성의 핵심이 될 가능성이 높습니다. 이것이 어떻게 발전할지는 비용을 지켜보면서 실험을 통해 계속 생각해 나갈 것입니다.
그 위에, 저는 여전히 신뢰를 구축하는 첫 단계는 출처를 명확히 밝히는 것이라고 생각합니다.
[출처] ChatGPT
원래 내 블로그에 게시됨.