멀티 에이전트 코스 빌더
한 줄 정의
멀티 에이전트 코스 빌더는 실라버스 설계, 본문 집필, 평가 문항 출제, 슬라이드 덱 생성 단계를 개별 에이전트와 휴먼 검토 루프(Revision Loop)로 구성하여, 비결정론적 모델의 맥락 분산 문제를 극복하는 프로덕션급 콘텐츠 생성 파이프라인 아키텍처다.
핵심 요지
- 단일 프롬프트의 한계 극복: 긴 콘텐츠(예: 12개 강의가 포함된 전체 코스)를 단일 컨텍스트와 하나의 거대한 프롬프트로 처리할 때 나타나는 글의 산포(drift), 문체 일관성 상실, 학습 목표 모순 등의 실패 패턴을 차단한다.
- 역할 분할 및 모델 최적화: ADDIE 교수 설계 프레임워크와 유사하게 기획(실라버스), 집필(본문), 설계(평가), 빌드(프로덕션) 단계를 나누고, 각 단계에 맞는 모델(기획 단계에는 고성능 Reasoning 모델, 본문 작성에는 저렴하고 빠른 모델 등)을 차등 매칭한다.
- 공유 상태 기반 데이터 규약: 모든 에이전트가 LangGraph 내에서 하나의 Pydantic 상태 스키마(State Schema)를 함께 읽고 쓰며, API 계약 구조(Data Contract)를 명확히 유지한다.
- 병렬 팬아웃(Parallel Fan-out) 실행: 실라버스 확정 이후 개별 강의 본문 및 퀴즈 출제 작업을 독립된 세션으로 병렬 처리함으로써, 실행 시간을 대폭 단축(예: 15분에서 90초로 단축)하고 에이전트 대화 맥락 간의 상호 오염을 원천 제거한다.
- 적대적 검증(Anti-slop Reviewer): 작업 에이전트가 완성한 결과물을 받아 regex 프리체크와 평가 가이드를 기반으로 상투적 미사여구(AI Slop), 코드 결여 등을 교차 검증하고 반려(Verdict) 시 구체적 방향을 제공하는 특화 노드를 둔다.
- 결정론적 최종 빌드 (Deterministic Production): 디자인이나 레이아웃이 붕괴하기 쉬운 영역(슬라이드 덱, PDF 등)은 LLM의 임의 생성에 맡기지 않고, 마크다운(Marp CLI 등) 컴파일러를 실행하는 규칙 기반 코드로 처리한다.
상세
1. 4단계 멀티 에이전트 역할 구조
- 커리큘럼 에이전트 (Curriculum Agent): 한 줄의 자연어 입력으로부터 대주제, 모듈 구분, 모듈별 학습 목표(Learning Objectives), 강의 아웃라인이 담긴 실라버스를 기획한다.
- 콘텐츠 에이전트 (Content Agent): 확정된 아웃라인을 바탕으로 실제 본문 텍스트를 채운다. 이 단계는 각 강의별로 독립적으로 병렬 구동된다.
- 평가 에이전트 (Evaluation Agent): 각 모듈의 본문과 학습 목표를 읽고 이에 부합하는 객관식 퀴즈(JSON 규격)와 실무 과제 가이드를 제작한다.
- 프로덕션 에이전트 (Production Agent): 최종 본문과 퀴즈, 과제를 취합하여 규칙 기반 컴파일러 코드를 구동하고 정적 슬라이드 PDF를 컴파일한다.
2. 제어 흐름과 개입 게이트
- 휴먼 승인 게이트 (Human Approval Gate): 비결정론적 기획 단계인 커리큘럼 에이전트 작동 완료 직후, 파이프라인은 상태를 보존한 채 실행을 정지(
interrupt)한다. 사용자는 실라버스를 확인하여 필요시 방향성 수정을 지시할 수 있으며, 이 Revision Loop를 거쳐 최종 승인한 경우에만 대규모 집필 작업(Content/Evaluation)으로 진입한다. - 안티 슬롭 검토기 (Anti-slop Reviewer): 콘텐츠 에이전트가 본문을 작성할 때마다 호출되어 “AI slop” 필터를 적용한다. 이 노드는 주 기획 노드 대비 저렴한 모델을 활용하며, 통과 실패 시 에이전트에게 반려 피드백을 전달하여 재생성을 유도한다.
예시
LangGraph 상태 스키마 정의 (Pydantic 규약)
from typing import Annotated, Literal
from pydantic import BaseModel, Field
BloomLevel = Literal[
"remember", "understand", "apply", "analyze", "evaluate", "create"
]
class LearningObjective(BaseModel):
id: str = Field(description="안정적인 식별자 (예: 'lo-1-2')")
text: str = Field(description="측정 가능하며 학습자 중심적인 동사구 목표")
bloom: BloomLevel
module_id: str
class Lesson(BaseModel):
id: str
module_id: str
title: str
objectives: list[str] = Field(description="이 강의에서 다루는 LearningObjective ID 목록")
class CourseState(BaseModel):
topic: str
syllabus_approved: bool = False
objectives: list[LearningObjective] = []
lessons: list[Lesson] = []
content: dict[str, str] = {} # lesson_id -> markdown content
quizzes: list[dict] = []아키텍처 제어 흐름
graph TD Start([입력: 한 줄 주제]) --> Curriculum[커리큘럼 에이전트] Curriculum --> Interrupt[Human Approval Gate] Interrupt -- 수정 요구 --> Curriculum Interrupt -- 승인 --> FanOut{병렬 팬아웃} FanOut --> Content1[콘텐츠 에이전트: 강의 1] FanOut --> Content2[콘텐츠 에이전트: 강의 2] FanOut --> Eval[평가 에이전트: 퀴즈/과제] Content1 --> Reviewer1[Anti-slop 검토기] Reviewer1 -- 반려 --> Content1 Reviewer1 -- 승인 --> Join{Join} Content2 --> Reviewer2[Anti-slop 검토기] Reviewer2 -- 반려 --> Content2 Reviewer2 -- 승인 --> Join Eval --> Join Join --> Production[프로덕션 에이전트: Marp CLI] Production --> Output([최종 PDF / 마크다운 묶음])
충돌
현재 확인된 충돌 없음.