
Microsoft Agent Framework 1.10을 활용한 멀티 에이전트 PR 리뷰어 구축
요약
Microsoft Agent Framework(MAF) 1.10을 사용하여 GitHub PR을 자동으로 분석하는 멀티 에이전트 시스템 구축 방법을 소개합니다. 보안, 성능, 스타일 전문가 에이전트가 동시에 분석을 수행하고 결과를 통합하는 워크플로우를 구현했습니다.
핵심 포인트
- MAF의 ConcurrentBuilder를 활용한 효율적인 작업 분산(Fan-out) 구현
- 보안, 성능, 스타일 등 전문 에이전트 기반의 멀티 에이전트 아키텍처
- GPT-4o-mini를 사용하여 저비용(약 $0.004)으로 빠른 분석 가능
- MAF 1.10 API 사용 시 문서와 실제 구현 간의 차이점 주의 필요
Microsoft Agent Framework (MAF)는 2026년 4월 3일에 1.0 GA(General Availability)에 도달했습니다. 저는 단순한 'hello-world' 데모보다는 작고 유용하며 제 자신의 워크플로우에 솔직하게 적용할 수 있는 무언가에 이를 시도해보고 싶었습니다. 그래서 pr-triage-agent를 구축했습니다. 이는 GitHub PR URL을 입력받아 세 명의 전문 에이전트(Security, Performance, Style)를 동시에 실행한 다음, 그들의 분석 결과를 구조화된 마크다운(markdown) 보고서를 생성하는 Consolidator 에이전트에게 전달하는 CLI(Command Line Interface)입니다.
Repo: https://github.com/sumesh-ramasamy/pr-triage-agent
이 포스트에서는 이 도구가 무엇을 하는지, 왜 MAF가 자연스러운 선택이었는지, 아키텍처(architecture), 그리고 설치된 1.10 API가 문서와 달라 제가 상황을 파악하기 전까지 한 시간을 허비하게 만들었던 네 가지 차이점에 대해 설명합니다.
작동 방식
pr-triage-agent는 다음과 같이 실행됩니다:
python triage.py https://github.com/pallets/flask/pull/6013
이 도구는 PR diff(차이점)를 가져와서 MAF의 ConcurrentBuilder를 통해 세 명의 전문 에이전트에게 동시에 배포(fan out)한 다음, 그들의 JSON 분석 결과를 rich로 출력되는 단일 마크다운 보고서로 통합합니다. 전체 실행에는 약 10초가 소요되며, gpt-4o-mini 사용 시 비용은 약 $0.004입니다.
출력 예시:
왜 Microsoft Agent Framework인가
저는 제 개인 저장소와 직장의 코드베이스 모두에 끊임없이 작은 PR을 제출합니다. 명백한 문제들, 하드코딩된 비밀번호(secrets), N+1 쿼리(N+1 queries), 분리되었어야 할 함수 등을 분류(triaging)하는 작업은 제가 LLM(Large Language Model)이 구조화된 멀티 에이전트(multi-agent) 방식으로 처리하는 것을 확인해보고 싶었던 바로 그 패턴 매칭(pattern-matching) 작업입니다.
제가 특히 MAF에 끌린 데에는 두 가지 이유가 있습니다. 첫째, 동시 워크플로 프리미티브(concurrent workflow primitive)가 PR 분류(triage) 문제의 형태와 정확히 일치합니다. 즉, N명의 전문가에게 작업을 분산(fan out)하고, 그들의 출력을 수집하여, 통합기(consolidator)에게 전달하는 방식입니다. 이러한 프리미티브가 없는 프레임워크에서는 비동기(async) 패턴을 직접 하나씩 이어 붙여야 합니다. 둘째, MAF는 Microsoft의 에이전트용 퍼스트 파티(first-party) 프레임워크이며, 생태계가 북적이기 전인 지금 이 기술에 익숙해지고 싶었습니다.
또한 단순히 문서를 읽기보다는 그 안에서 무언가를 직접 만들어보고 싶었습니다. 읽는 것과 만드는 것은 이해의 수준이 매우 다릅니다.
아키텍처 (Architecture)
GitHub PR URL
│
▼
...
각 전문가는 심각도(severity), 발견 사항(findings), 요약(summary)이 포함된 JSON 블록을 반환합니다. 통합기(Consolidator)는 이 세 개의 JSON을 하나의 마크다운(markdown) 보고서로 병합합니다. 전체 파이프라인 코디네이터(pipeline coordinator)는 ConcurrentBuilder가 핵심적인 작업을 수행하기 때문에 100줄 미만으로 구성됩니다.
MAF 1.10에서 놀랐던 점
이 섹션에 가장 많은 시간을 할애하고 싶습니다. 왜냐하면 이 문제로 인해 약 한 시간을 허비했고, 해결 방법이 문서에 명확히 나와 있지 않았기 때문입니다.
저는 Microsoft Learn의 공식 퀵스타트(quickstart)를 따르는 것으로 시작했습니다. 하지만 문서의 임포트(import) 문은 실패했습니다. 문서에 나온 클래스 이름들은 설치된 패키지에 존재하지 않았습니다. agent-framework==1.10.0을 직접 조사(introspect)해 본 결과 실제 API 표면(API surface)을 찾을 수 있었고, 이는 문서가 제안하는 것보다 더 깔끔했지만, 샘플 코드를 복사해서 붙여넣기에는 차이가 꽤 컸습니다.
제가 겪은 네 가지 구체적인 차이점과 그 해결책은 다음과 같습니다:
1. Agent 클래스 이름. 문서에는 ChatAgent로 표시되어 있습니다. 하지만 설치된 1.10.0 패키지는 Agent를 노출합니다:
# 문서 (Docs)
from agent_framework import ChatAgent
agent = client.create_agent(...)
...
2. OpenAI 채팅 클라이언트 모델 키워드 인자(kwarg). 문서에는 model_id라고 되어 있습니다. 설치된 패키지에서 이를 사용하면 TypeError가 발생합니다. 실제 키워드 인자는 model입니다:
# 문서 (Docs)
OpenAIChatClient(api_key=..., model_id="gpt-4o")
...
3. ConcurrentBuilder 위치. 패키지 루트(root)에 있지 않습니다. agent_framework.orchestrations에 위치합니다:
from agent_framework.orchestrations import ConcurrentBuilder
4. Aggregator 응답 형태 (response shape). 문서에는 r.agent_run_response.messages[-1].text로 표시되어 있습니다. 하지만 설치된 버전은 각 요소가 .agent_response.text를 가지는 list[AgentExecutorResponse]를 반환합니다.
제가 이를 찾아낸 방법은 dir()을 사용하여 설치된 패키지를 조사(introspecting)하고, microsoft/agent-framework 리포지토리의 열려 있는 몇 가지 GitHub 이슈를 읽는 것이었습니다. 문서(docs)를 신뢰하는 것을 멈추고 설치된 패키지를 신뢰하기 시작하자, 마찰(friction)이 제로로 줄어들었습니다.
이것이 GA(General Availability) 출시 후 3개월이 지난 1.0 프레임워크를 다루는 모습입니다. 형태(shape)는 맞고, API는 구축하기에 충분히 안정적이지만, 표면(surface)은 여전히 문서 사이트보다 빠르게 움직이고 있습니다. 이는 비판이 아니라, 초기 단계에서 겪는 정상적인 트레이드오프(tradeoff)입니다. 저는 문서의 모든 구석이 따라잡을 때까지 기다리기보다 지금 유창함(fluency)을 쌓아가는 쪽을 택하겠습니다.
다른 사람들도 동일한 마찰을 겪을 경우를 대비해, 리포지토리의 MAF 1.10.0 API notes 섹션에 이 네 가지 사항을 모두 기록해 두었습니다.
다르게 했을 점
시간이 더 있었다면 바꿨을 두 가지가 있습니다.
구조화된 출력 (Structured output). MAF는 실행(run) 호출 시 네이티브 Pydantic 타입의 구조화된 출력을 위한 response_format을 지원합니다. 저는 지침(instructions)에서 모델에게 "JSON만 반환하세요"라고 요청하는 대신 이를 사용하는 것을 선호합니다. 하지만 1.0.0b260107 이후 버전에서 response.value가 None을 반환하는 열린 이슈 #3325 때문에 이를 건너뛰었습니다. 저의 해결책(workaround)은 마크다운 구분 기호가 있는 JSON(markdown-fenced JSON)과 서문이 포함된 JSON(JSON-with-preamble)을 모두 처리할 수 있는 작은 관대한 JSON 파서(tolerant JSON parser)를 사용하는 것이었습니다. 이는 안정적으로 작동하지만, 임시 방편입니다. 업스트림(upstream) 수정 사항이 반영되면, 네이티브 구조화된 출력으로 교체하고 마이그레이션에 대해 작성하겠습니다.
전문가 프롬프트 (Specialist prompts). 세 가지 전문가 프롬프트 (Security, Performance, Style)는 강력한 의견을 담고 있지만 튜닝(tuned)되지는 않았습니다. 적절한 다음 반복(iteration) 단계에서는, 전문가들이 무엇을 지적해야 하는지 이미 알고 있는 일련의 골든 PR (golden PRs) 세트를 사용하여 소규모 평가 하네스 (evaluation harness)를 구축한 다음, 이를 바탕으로 프롬프트를 조정할 것입니다. 현재 프롬프트는 측정(measurement)이 아닌 직관에 의해 형성되었습니다. 첫 번째 버전으로서는 괜찮지만, 확장(scale)되지는 못할 것입니다.
또한 범위를 대폭 축소했습니다. 이것은 단 하룻밤 만에 만든 빌드이며, 프로덕션 도구가 아닙니다. GitHub Action 통합, 웹훅 (webhook) 모드, 그리고 멀티 리포 (multi-repo) 지원은 모두 로드맵에 포함되어 있지만 v0.1에는 포함되지 않았습니다.
다음 단계
리포지토리(repo)의 공개 로드맵:
- GitHub Models 지원 (PR 가져오기와 LLM 추론 모두를 위한 하나의 PAT)
- Azure OpenAI + Foundry 변형
- GitHub Action 모드 (분류 후 자동으로 PR 댓글로 게시)
response_format을 통한 네이티브 구조화된 출력 (issue #3325에 대한 업스트림 수정 대기 중)- 플러그인 설정을 통한 커스텀 전문가 에이전트
- 골든 PR (golden PRs)을 활용한 평가 하네스 (evaluation harness)
각각의 항목은 작은 커밋(commit)과 작은 후속 포스트로 이어질 것입니다.
직접 시도해 보세요
리포지토리: github.com/sumesh-ramasamy/pr-triage-agent
빠른 시작 (Quickstart):
git clone https://github.com/sumesh-ramasamy/pr-triage-agent
cd pr-triage-agent
python3.13 -m venv .venv && source .venv/bin/activate
...
LLM 호출 없이 비용 미리보기:
python triage.py <URL> --dry-run
만약 MAF를 기반으로 구축하면서 문서와 설치된 API 사이에서 유사한 마찰을 겪고 있다면, 의견을 듣고 싶습니다. 이슈(Issues)와 PR은 언제나 환영합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기