
이번 글에서는 2차 미니 프로젝트로 진행한 AI 강사 Agent 구축 과정에 대해 정리해보려고 한다. PPT에 내용을 슬라이드별로 데이터를 분석하고 이를 음성 및 영상과 연결하여 하나의 완성된 강의 콘텐츠를 만들어내는 에이전트를 개발하는 것이 이번 프로젝트의 핵심이었다. 나는 이 과정에서 각 기능을 담당하는 노드들을 설계하고 이들을 연결하는 그래프 관리자 역할을 맡았다.
이번 프로젝트의 목표는 영상 제작 시간을 단축하고 품질을 균일화하여 강의 자료 학습의 효율성을 극대화하는 것이었다. 우리 조는 단순히 텍스트를 읽어주는 기능을 넘어, 교안의 전체 맥락을 이해하고 학습을 돕는 부가 콘텐츠(퀴즈, 만화 등)까지 생성하는 에이전트를 설계했다.

전체 워크플로우는 다음과 같은 구조로 진행된다.
우리 조가 설계한 그래프의 각 노드는 독립적인 기능을 수행하면서도 State를 통해 긴밀하게 데이터를 주고받는다. 코드 로직을 바탕으로 정리한 주요 노드의 역할은 다음과 같다.
overall_script를 참조하여 전체적인 톤앤매너를 유지한다.그래프 관리자 역할을 맡고 내가 신경 쓴 부분은 비동기적인 작업 분기와 루프 제어의 정확성이었다. 아래는 LangGraph를 활용해 구축한 우리 조의 에이전트 구조이다.

from langgraph.graph import StateGraph, END
# 그래프 구축
w = StateGraph(State)
# 노드 추가
w.add_node("parse_all", node_parse_all)
w.add_node("gen_overall_script", node_gen_overall_script)
w.add_node('gen_comic', node_gen_comic)
w.add_node("gen_outro", node_gen_outro)
w.add_node("search_index", node_search_index)
w.add_node("gen_content", node_gen_content)
w.add_node("gen_script", node_gen_script)
w.add_node("tts", node_tts)
w.add_node("gen_video", node_gen_video)
w.add_node("update_index", node_update_slide_index)
w.add_node("concat_videos", node_concat_videos)
w.add_node("gen_quiz", node_gen_quiz)
w.add_node("gen_subtitles_video", node_gen_subtitles_video)
# 엣지 및 흐름 설계
w.set_entry_point("parse_all")
w.add_edge("parse_all", "gen_overall_script")
# 분기 1: 만화 및 아웃트로 생성
w.add_edge("gen_overall_script", "gen_comic")
w.add_edge("gen_comic", "gen_outro")
w.add_edge("gen_outro", "concat_videos")
# 분기 2: 슬라이드별 콘텐츠 제작 루프
w.add_edge("gen_overall_script", "search_index")
w.add_edge("search_index", "gen_content")
w.add_edge("gen_content", "gen_script")
w.add_edge("gen_script", "tts")
w.add_edge("gen_script", "gen_quiz")
w.add_edge("gen_quiz", "gen_script")
w.add_edge("tts", "gen_video")
w.add_edge("gen_video", "update_index")
# 조건부 분기: 모든 슬라이드 완료 여부 체크
w.add_conditional_edges(
"update_index",
node_check_finish,
{
"continue": "search_index",
"end": "concat_videos"
}
)
w.add_edge("concat_videos", "gen_subtitles_video")
w.add_edge("gen_subtitles_video", END)
app = w.compile()
이번 프로젝트는 단순히 기술적인 구현보다는 에이전트 시스템을 설계하고 협업하는 경험을 한거같다.
첫째로, 상태(State) 관리의 중요성을 깨달았다. LangGraph 내에서 데이터가 노드 사이를 흐를 때, 어떤 형식으로 저장되고 업데이트되는지 명확히 정의하지 않으면 전체 워크플로우가 꼬이기 쉽다. 특히 여러 팀원이 만든 노드를 통합할 때 공통된 데이터 규격을 유지하는 인터페이스 설계 능력이 필수적임을 느꼇다.
둘째로, 맥락(Context) 유지의 힘이다. 초기 모델에서는 슬라이드별로 대본을 따로 쓰다 보니 내용이 겹치거나 말투가 달라지는 문제가 있었다. 이를 해결하기 위해 Summary Context를 공유하도록 설계함으로써 전체 영상의 일관성을 확보할 수 있었다.
마지막으로, 협업의 가치이다. 팀원들이 각자의 노드에 역할을 맡아 개발을 진행해 만든 노드들을 최적의 경로로 엮어냈을 때, 혼자서라면 진행하기 힘들었던 부분들도 완성되는 과정에서 큰 보람을 느꼈다.
마감시간 전까지 프로젝트를 무사히 제출하고, 다른 조들의 최종 발표를 들으며 참 많은 생각이 들었다. 프로젝트 초기 기획 단계부터 '이 에이전트를 누가, 어떻게 사용할 것인가'에 대한 타겟 유저를 명확히 정의하고 출발한 팀들도 있었고, 퀴즈나 만화 같은 보편적인 기능을 넘어 노드 자체에 참신한 아이디어를 녹여낸 팀들도 보였다.
우리 조의 경우 R&R을 나누고 각자 맡은 기능 구현에 몰두하느라, 프로젝트의 본질적인 주제나 기획의 방향성에 대해 깊게 토론할 시간이 부족했다. 서로 의견을 나누며 전체적인 그림을 다듬기보다 각자의 역할에만 집중했던 것 같아 결과물에 아쉽다..
아쉬운점도 남지만 오히려 시스템을 설계해 보면서, 아키텍처 관점에서 시스템을 더 고도화하고 싶은 생각이 들었다. 이번 경험을 발판 삼아 앞으로 다음 두 가지 주제를 더 깊이 파고들어 보려 한다.
현재 구조는 추출된 텍스트, 요약본, 그리고 파일 경로들이 모두 하나의 State 딕셔너리에 담겨 노드 사이를 이동한다. 작은 규모의 프로젝트에서는 당장 문제가 없지만, 처리해야 할 슬라이드가 많아지거나 데이터의 크기가 커지면 메모리 효율성이 크게 떨어질 수밖에 없다. (우리는 3p 슬라이드만 실험을 했기때문에 후에 진행 해봐야겠다.)
이를 해결하기 위해 매번 전체 데이터를 복사해서 넘기는 대신, 필요한 정보만 선별하여 전달하는 방식을 고민해야 한다. 더 나아가 메타데이터(ID) 식별자만 State에 남기고, 실제 무거운 데이터는 외부 DB와 연동하여 필요할 때만 불러오는 효율적인 구조를 설계해봐야겠다.
현재 우리 에이전트는 부족한 내용을 채우기 위해 단순한 웹 검색 노드(TavilySearch)를 활용하고 있다. 하지만 타겟 사용자가 명확하게 정해진 교육용 에이전트로 거듭나려면 이를 넘어 벡터 데이터베이스를 활용해야 한다.
사내 문서나 전공 서적, 특정 도메인의 전문 지식을 청크 단위로 벡터화하여 저장해 두고, 스크립트를 작성할 때 이를 정확하게 검색해오는 고급 RAG 파이프라인으로 발전시키고 싶다. (공모전에서 사용할 수 있다면 해봐야겠다.)
02miniproject, 2번째 미니로젝트 회고록을 마무리한다.
협업은 언제나 재밌다.