AI/LLM

대규모 코드베이스를 정복하는 AI 코딩 에이전트: Confucius Code Agent 깊이 파헤치기

Tech코알라 2026. 1. 13. 08:13

 

이번에는 Meta와 Harvard가 공동으로 발표한 소프트웨어 엔지니어링 에이전트 논문에 대해 다루어 보고자 합니다.

실제 프로덕션 환경에서 수백만 줄의 코드를 다루는 AI 에이전트를 만드는 것은 쉽지 않습니다. 작은 데모 프로젝트에서는 잘 작동하던 에이전트들이 실제 대규모 코드베이스에서는 맥을 못 추는 경우가 많죠. Confucius Code Agent(CCA)는 이러한 한계를 극복하기 위해 설계된 프레임워크입니다.

 

이 글에서는 CCA의 핵심 아이디어와 구현 전략을 살펴보고, 실제 코드 예제를 통해 어떻게 동작하는지 알아보겠습니다.

 

왜 기존 에이전트들은 실패했을까?

연구진은 실제 소프트웨어 엔지니어링에서 AI 에이전트가 직면하는 두 가지 핵심 도전 과제를 정의했습니다:

1. 장문맥 추론(Long-context Reasoning)

  • 거대한 저장소에서 관련 코드를 효율적으로 찾아야 함
  • 여러 파일과 모듈에 걸친 다단계 추론 필요
  • 긴 도구 실행 흔적과 깊은 실행 히스토리 관리

2. 장기 메모리(Long-term Memory)

  • 세션과 태스크를 넘나들며 지속적인 지식 축적
  • 재사용 가능한 패턴과 실패 모드 캡처
  • 과거 실수를 반복하지 않기

CCA의 놀라운 성과

SWE-Bench-Pro 벤치마크에서 CCA는 기존 연구 기반 에이전트들을 크게 앞서는 성능을 보여줍니다:

에이전트모델해결률
SWE-agent Claude Haiku 4.5 39.5%
SWE-agent Claude Sonnet 4.5 43.7%
Live-SWE-agent Claude Sonnet 4.5 45.8%
CCA Claude Opus 4.5 54.3% 

더 흥미로운 점은, 약한 모델 + 강한 스캐폴딩강한 모델 + 약한 스캐폴딩보다 성능이 좋다는 것입니다. 이는 에이전트 아키텍처의 중요성을 강조합니다.

핵심 설계 철학: AX, UX, DX

대부분의 에이전트 프레임워크는 한 가지 관점만 최적화합니다. CCA는 세 가지 관점을 모두 고려합니다:

Agent Experience (AX): AI의 관점

AI가 보는 정보는 간결하고 구조화되어야 합니다. 불필요한 노이즈는 모델을 혼란스럽게 만듭니다.

# UX (사용자가 보는 화면):
"""
Creating file at config.py
File created successfully at config.py
Here is the diff:
+ PORT=8080
+ DEBUG=true
+ MAX_CONNECTIONS=100
"""

# AX (에이전트가 받는 입력):
"""
Human: [previous user message]
AI: <file_edit type="create" file_path="config.py">...</file_edit>
Human: <result>File created successfully</result>
"""

 

사용자에게는 풍부한 정보를, 에이전트에게는 압축된 요약만 제공합니다. 이렇게 하면 컨텍스트 윈도우를 효율적으로 사용할 수 있습니다.

 

User Experience (UX): 개발자의 관점

 

투명성과 해석 가능성이 핵심입니다. 개발자는 에이전트가 무엇을 하고 있는지 명확히 볼 수 있어야 합니다.

 

Developer Experience (DX): 시스템 구축자의 관점

 

에이전트를 개발하고 개선하는 사람들을 위한 관찰 가능성, 평가, 모듈성이 중요합니다.

 

CCA의 4가지 핵심 메커니즘

 

F1: 계층적 컨텍스트 관리

 

긴 디버깅 세션에서도 중요한 정보를 잃지 않도록 계층적 메모리 구조를 사용합니다:

# 계층적 메모리 구조 예시
"""
+-- instance_qutebrowser__qutebrowser-c09e1439...
    +-- hierarchical_memory_3a7488c6-bf8c-11f0-8236-cfd9fd0d56b4
        +-- qutebrowser_process_cleanup
        |   |-- analysis.md
        |   |-- implementation_summary.md
        +-- todo.md
"""
 
 

컨텍스트가 임계값에 도달하면, Architect 에이전트가 자동으로 요약을 생성합니다:

 
# Algorithm 1: Confucius Orchestrator Loop (단순화)
def orchestrator_loop():
    initialize_session_context()
    
    while iteration < max_iters:
        # LLM 호출
        llm_output = invoke_llm(system_prompt + memory)
        
        # 출력 파싱
        actions = parse_llm_output(llm_output)
        
        # 각 액션 실행
        for action in actions:
            extension = route_to_extension(action)
            result = execute_extension(extension)
            update_memory(result)
            
            if extension.signals_continuation():
                add_observations_to_memory(result)
                continue
        
        # 완료 확인
        if is_complete():
            break
    
    return final_output_and_artifacts

F2: 실패로부터 배우는 노트 테이킹

CCA의 가장 독특한 특징 중 하나는 Hindsight Notes입니다. 성공한 솔루션뿐만 아니라 실패 사례도 기록합니다:

---
id: escaping_wildcards_in_infobase_queries
title: Escaping Wildcards in Infobase Queries
keywords:
- infobase
- queries
- wildcards
- escaping
---

# Escaping Wildcards in Infobase Queries

## Problem Context
When searching for authors by name in OpenLibrary's Infobase, 
asterisk characters ('*') in author names cause unexpected behavior 
because they are treated as wildcards.

## The Solution
Escape asterisks in name fields using a backslash when performing 
exact matches, but preserve wildcards for surname matching.

### Code Example
```python
def find_author():
    # 정확한 이름 매칭을 위해 애스터리스크 이스케이프
    escaped_name = author["name"].replace("*", r"\*")
    queries = [
        {"type": "/type/author", "name~": escaped_name},
        {"type": "/type/author", "alternate_names~": escaped_name},
    ]
    
    # 성(surname) 매칭에는 의도적으로 와일드카드 사용
    if birth_year and death_year:
        surname = author['name'].split()[-1]
        queries.append({
            "type": "/type/author",
            "name~": f"* {surname}",  # 의도적인 와일드카드
            "birth_date~": f"*{birth_year}*",
            "death_date~": f"*{death_year}*",
        })

Key Insights

 

1. 상황 의존적 이스케이핑: 와일드카드는 어떤 쿼리 컨텍스트에서는 이스케이프해야 하지만, 다른 컨텍스트에서는 보존해야 함

 

2. 백슬래시 이스케이핑: Infobase 쿼리에서는 `r"\*"`를 사용

 

3. 쿼리 순서의 중요성: 정확한 매치를 먼저 시도하고, 그 다음 대체 이름, 마지막으로 성 매칭

 

이러한 노트는 다음 실행에서 자동으로 검색되어 같은 실수를 반복하지 않게 합니다.

실제 성능 개선:

실행평균 턴 수평균 토큰 비용해결률
Run 1 (처음) 64 104k 53.0%
Run 2 (노트 사용) 61 (-3) 93k (-11k) 54.4% (+1.4%)

 

F3: 모듈식 확장 시스템

 

모든 도구 사용과 동작은 확장(Extension)을 통해 관리됩니다:

# Extension 인터페이스 예시
class Extension:
    """확장의 기본 구조"""
    
    def on_input_messages(self, messages, context):
        """입력 메시지 전처리"""
        pass
    
    def on_llm_output(self, output, context):
        """LLM 출력 후처리"""
        pass
    
    def on_tag(self, tag_name, tag_content, context):
        """특정 태그 처리 (예: <bash>...</bash>)"""
        pass

# 파일 편집 확장 예시
class FileEditExtension(Extension):
    def on_tag(self, tag_name, tag_content, context):
        if tag_name == "file_edit":
            # XML 태그 파싱
            file_path = extract_file_path(tag_content)
            edit_type = extract_edit_type(tag_content)
            
            # 안전성 검증
            if not is_safe_path(file_path):
                return error_response("Unsafe file path")
            
            # 파일 수정 실행
            result = perform_file_edit(file_path, edit_type, tag_content)
            
            # 메모리 업데이트
            context.memory.add_result(result)
            
            return success_response(result)

# Bash 명령 확장 예시
class BashExtension(Extension):
    ALLOWED_COMMANDS = ["ls", "grep", "cat", "find", "git"]
    
    def on_tag(self, tag_name, tag_content, context):
        if tag_name == "bash":
            command = extract_command(tag_content)
            
            # 명령어 검증
            if not self.is_allowed_command(command):
                return error_response("Command not allowed")
            
            # 실행
            output = execute_bash(command)
            
            # 결과 저장
            context.memory.add_observation(output)
            
            return success_response(output)
    
    def is_allowed_command(self, command):
        cmd_name = command.split()[0]
        return cmd_name in self.ALLOWED_COMMANDS
 
 

이 모듈식 설계 덕분에 새로운 도구를 추가하거나 기존 동작을 수정하기가 매우 쉽습니다.

F4: 메타 에이전트 - 에이전트가 에이전트를 만든다

가장 혁신적인 부분은 메타 에이전트입니다. 이는 다른 에이전트를 자동으로 생성하고 개선합니다:

 

# 메타 에이전트의 Build-Test-Improve 루프
class MetaAgent:
    def create_agent(self, specification):
        """
        1. BUILD: 에이전트 구성 생성
        """
        config = self.generate_config(specification)
        agent = self.instantiate_agent(config)
        
        """
        2. TEST: 평가 태스크로 테스트
        """
        test_results = []
        for task in specification.eval_tasks:
            result = agent.run(task)
            test_results.append(result)
        
        """
        3. IMPROVE: 실패 분석 및 개선
        """
        failures = self.analyze_failures(test_results)
        
        if failures:
            improvements = self.propose_improvements(failures)
            updated_config = self.apply_improvements(config, improvements)
            
            # 재귀적으로 다시 테스트
            return self.create_agent_with_config(updated_config)
        
        return agent
    
    def propose_improvements(self, failures):
        """실패 패턴 분석 및 개선안 제시"""
        improvements = []
        
        for failure in failures:
            if failure.type == "brittle_tool_selection":
                improvements.append({
                    "type": "prompt_modification",
                    "target": "tool_selection_prompt",
                    "change": "Add examples of correct tool usage"
                })
            
            elif failure.type == "incorrect_file_edit":
                improvements.append({
                    "type": "extension_config",
                    "target": "file_edit_extension",
                    "change": "Enable stricter validation"
                })
        
        return improvements
 

실제로 CCA 자체가 메타 에이전트를 통해 개발되었습니다!

 

실전 예제: PyTorch 버그 수정 비교

 

연구진은 실제 PyTorch 저장소의 이슈들로 CCA를 테스트했습니다. 한 가지 흥미로운 케이스를 살펴보겠습니다:

이슈: CUDA 메모리 체크포인트에서 curr_block->next == nullptr 어설션 실패

 

# 문제 재현 코드 (간략화)
import torch

pool_id = torch.cuda.graph_pool_handle()
com_stream = torch.cuda.Stream()
com_device = torch.cuda.current_device()

# 작은 메모리 상태 저장
graph1, outputs1 = cudagraphify(foo, [inp, 1], pool=pool_id)
small_state = torch._C._cuda_getCheckpointState(com_device, pool_id)

# 큰 메모리 상태로 전환
torch._C._cuda_setCheckpointPoolState(com_device, original_mem_state, [], [])
graph2, outputs2 = cudagraphify(foo, [inp, 2], pool=pool_id)

# 다시 작은 상태로 복원 시 에러 발생!
torch._C._cuda_setCheckpointPoolState(com_device, small_state, [], output1_new_storage)
# RuntimeError: Expected curr_block->next == nullptr to be true

 

CCA의 솔루션:

  • 문제 분석: 어설션이 너무 엄격함을 파악
  • 최소 개입: 문제가 되는 체크만 제거 (-2 줄)
  • 결과: PyTorch 팀의 최종 수정사항과 일치!

Claude Code의 솔루션:

  • 더 포괄적인 접근: 블록 분할 로직 수정 (+7 줄)
  • 어설션은 유지하되 구조를 맞춤
  • 결과: 더 복잡하지만 아키텍처적으로 견고

두 접근 방식 모두 장단점이 있지만, CCA의 "원칙적 엔지니어링" 스타일이 실제 수정사항과 일치했다는 점이 흥미롭습니다.

성능 분석

컨텍스트 관리의 효과

모델컨텍스트 관리도구 사용해결률
Claude 4 Sonnet 기본 42.0%
Claude 4 Sonnet 고급 48.6%
Claude 4.5 Sonnet 기본 44.0%
Claude 4.5 Sonnet 고급 51.0%
Claude 4.5 Sonnet 고급 51.6%

컨텍스트 관리만으로도 6.6%p 향상!

멀티 파일 편집 견고성

수정 파일 수해결률샘플 수
1-2 files 57.8% 294
3-4 files 49.2% 203
5-6 files 44.1% 86
7-10 files 52.6% 38
10+ files 44.4% 18

파일 수가 늘어나도 안정적인 성능을 유지합니다.

실제 구현해보기

CCA의 핵심 아이디어를 간단한 코드로 구현해볼 수 있습니다:

 

class SimpleConfuciusAgent:
    def __init__(self, llm_client):
        self.llm = llm_client
        self.memory = HierarchicalMemory()
        self.extensions = []
        self.notes = NoteStore()
    
    def add_extension(self, extension):
        """확장 등록"""
        self.extensions.append(extension)
        extension.register(self)
    
    def run(self, task):
        """메인 오케스트레이터 루프"""
        self.memory.add_user_message(task)
        
        for iteration in range(self.max_iterations):
            # 컨텍스트 압축 필요 시
            if self.memory.needs_compression():
                self.compress_context()
            
            # 과거 노트 검색
            relevant_notes = self.notes.search(task)
            
            # LLM 호출
            context = self.memory.get_context() + relevant_notes
            response = self.llm.generate(context)
            
            # 확장 시스템을 통한 액션 처리
            actions = self.parse_response(response)
            results = self.execute_actions(actions)
            
            # 메모리 업데이트
            self.memory.add_ai_response(response)
            self.memory.add_results(results)
            
            # 완료 확인
            if self.is_complete(results):
                break
        
        # 세션 노트 생성
        self.notes.create_session_notes(self.memory)
        
        return self.memory.get_final_output()
    
    def compress_context(self):
        """Architect 에이전트를 통한 컨텍스트 압축"""
        summary = self.llm.generate({
            "role": "architect",
            "task": "Summarize conversation preserving key decisions"
        })
        self.memory.replace_with_summary(summary)
    
    def execute_actions(self, actions):
        """확장을 통한 액션 실행"""
        results = []
        for action in actions:
            # 적절한 확장 찾기
            extension = self.find_extension_for(action)
            if extension:
                result = extension.execute(action)
                results.append(result)
        return results

 

이 코드는 실제 CCA의 단순화된 버전이지만, 핵심 아이디어를 잘 보여줍니다.

결론: 스캐폴딩이 전부다

이 연구가 우리에게 주는 가장 중요한 교훈은 에이전트 스캐폴딩의 중요성입니다:

  1. 모델만으로는 부족하다: 더 강력한 LLM을 사용하는 것보다 더 나은 아키텍처가 중요할 수 있습니다.
  2. AX/UX/DX 분리: 사용자, 에이전트, 개발자가 보는 것을 분리하면 모두에게 더 나은 경험을 제공할 수 있습니다.
  3. 메모리는 필수: 과거로부터 배우지 못하는 에이전트는 계속 같은 실수를 반복합니다.
  4. 모듈성이 승리한다: 확장 시스템을 통해 새로운 기능을 쉽게 추가하고 테스트할 수 있습니다.
  5. 메타 에이전트: 에이전트 개발 자체를 자동화하면 훨씬 빠른 개선이 가능합니다.

CCA는 단순히 높은 벤치마크 점수를 달성한 것이 아니라, 대규모 소프트웨어 엔지니어링을 위한 에이전트 설계의 새로운 패러다임을 제시했습니다. 앞으로 강화학습을 통한 추가 개선도 계획되어 있다고 하니, 더욱 기대가 됩니다!


참고 자료:

  • 논문: "Confucius Code Agent: Scalable Agent Scaffolding for Real-World Codebases"
  • arXiv: 2512.10398v5
  • 저자: Meta & Harvard 공동 연구

이 글의 코드 예제들은 논문 내용을 바탕으로 작성한 것으로, 실제 구현과는 차이가 있을 수 있습니다.