1. 처음에는 프롬프트 하나면 될 줄 알았다
1편에서 정리한 문제는 단순해 보였습니다. 학생 질문이 모호하면 바로 답하지 말고, 먼저 질문을 더 명확하게 만들도록 돕자는 것이었습니다.
그래서 처음에는 하나의 프롬프트에 모든 역할을 넣어 보려 했습니다.
"학생 수준을 파악하고, Bloom 지식 차원을 분류하고, 질문이 모호하면 되묻고, 답변까지 생성해 줘. 가능하면 교과서 용어도 지켜 줘."
문장으로 쓰면 그럴듯했습니다. 하지만 실제 대화에서는 역할이 서로 충돌했습니다. 어떤 질문은 더 물어봐야 하는데 AI가 바로 풀이를 시작했습니다. 반대로 이미 충분히 구체적인 질문에도 다시 질문을 던지는 경우가 있었습니다.
문제는 모델이 똑똑하지 않아서가 아니었습니다. 판단의 종류가 달랐기 때문입니다. 질문을 판별하는 일, 되묻는 일, 답변을 만드는 일, 대화 흐름을 관찰하는 일은 서로 다른 책임이었습니다.
그래서 MAICE는 프롬프트를 더 길게 만드는 대신, 역할을 나누기로 했습니다.
이 글의 핵심은 에이전트 이름을 외우는 것이 아닙니다. "질문을 판단하는 일"과 "답을 만드는 일"을 왜 분리해야 했는지 이해하는 것입니다.
2. 다섯 개의 역할로 나누기
MAICE에서 실제로 실행되는 핵심 에이전트는 다섯 개입니다. 독자가 먼저 이해해야 할 것은 파일명이 아니라 역할입니다.
| 역할 | 하는 일 | 교육적 의미 |
|---|---|---|
| 질문 분류 | 질문이 무엇을 요구하는지 판단 | 답변 전에 질문의 성격을 확인 |
| 질문 명료화 | 질문이 모호하면 다시 묻기 | Dewey의 문제 정의 단계를 대화로 구현 |
| 답변 생성 | 충분히 명확한 질문에 답변하기 | 학생 수준과 맥락에 맞춘 설명 생성 |
| 학습 관찰 | 세션 흐름을 관찰하기 | 어떤 대화가 일어났는지 추적 |
| Freepass 응답 | 명료화 없이 바로 답하기 | 비교 조건과 즉시 응답 흐름 처리 |
개발 메모로 남기면, 실제 코드 이름은 각각 QuestionClassifier, QuestionImprovement, AnswerGenerator, Observer, FreeTalker입니다. /Users/hwansi/project/01_MAICE/MAICE/agent/worker.py는 이 다섯 에이전트를 별도 프로세스로 띄우고, 죽은 프로세스가 있으면 다시 시작합니다.
이 글에서는 실제 논문 실험의 활성 파이프라인에 들어간 다섯 에이전트만 다룹니다. 코드에 남아 있더라도 실험과 효과 검증에 쓰이지 않은 흔적은 결과 설명과 연결하지 않습니다.
3. 학생 질문은 어디로 흘러가는가
학생이 질문을 입력하면 MAICE는 먼저 답변을 만들지 않습니다. 질문이 답변 가능한 상태인지부터 확인합니다.
예를 들어 "수학적 귀납법 증명 어떻게 해요?"라는 질문은 절차를 묻는 질문으로 볼 수 있습니다. 아직 거칠지만, 적어도 어떤 단원과 어떤 활동을 묻는지는 보입니다.
반면 "이거 모르겠어요"는 다릅니다. 어떤 개념이 막혔는지, 어떤 풀이를 시도했는지, 무엇을 설명받고 싶은지 알 수 없습니다. 이 질문에 바로 풀이를 주면 학생은 자신의 막힌 지점을 다시 보지 못합니다.
그래서 MAICE는 이런 분기점을 둡니다.
이 구조의 핵심은 "AI가 답을 잘하느냐"만 보는 것이 아닙니다. 답하기 전에 질문이 충분히 준비되었는지를 한 번 더 보는 것입니다.
4. 에이전트들이 서로 말을 주고받는 방식
역할을 나누면 새로운 문제가 생깁니다. 이제 각 에이전트가 판단한 내용을 서로 안전하게 주고받아야 합니다.
단순히 함수 하나를 호출하는 구조라면 쉽습니다. 하지만 MAICE에서는 답변이 스트리밍되고, 에이전트 프로세스가 따로 돌고, 세션 로그도 남아야 했습니다. 배포 중이거나 프로세스가 재시작되는 상황도 고려해야 했습니다.
그래서 MAICE는 메시지 전달에 Redis Streams를 사용했습니다. Redis Streams는 여러 작업자가 같은 흐름을 나누어 처리하면서도, 누가 어떤 메시지를 처리했는지 남길 수 있는 메시지 통로입니다.
교실에 비유하면 이렇습니다. 한 학생의 질문지가 교무실 책상 위에 아무렇게나 놓이는 것이 아니라, 담당 교사가 가져가고, 처리했는지 표시하고, 필요하면 다음 담당자에게 넘기는 업무함에 가깝습니다.
개발 메모로 보면 agent/utils/redis_streams_client.py에 이 흐름이 모여 있습니다. 백엔드가 에이전트에게 보내는 maice:backend_to_agent_stream, 에이전트가 백엔드로 돌려보내는 maice:agent_to_backend_stream, 세션별 응답을 분리하는 maice:agent_to_backend_stream_session_{session_id} 같은 스트림이 사용됩니다.
다만 Redis Streams가 모든 문제를 마법처럼 해결하는 것은 아닙니다. 장애나 재시작 상황에서는 메시지가 늦게 처리되거나 다시 처리될 가능성도 있습니다. 그래서 세션 상태, 처리 여부, 같은 요청을 다시 처리해도 대화가 망가지지 않게 하는 설계가 함께 필요합니다.
5. 실제로 막혔던 부분
에이전트를 나누면서 가장 조심해야 했던 문제는 순환이었습니다. 질문 분류기는 "명료화가 필요하다"고 판단하고, 명료화 에이전트는 다시 질문을 만들고, 그 질문이 또 다른 판단을 부르는 식으로 흐름이 길어질 수 있습니다.
그래서 코드에서는 책임을 분명히 나누었습니다. QuestionClassifier는 답변 가능한 질문인지 판단하고, 명료화가 필요하면 QuestionImprovement로 보냅니다. 충분히 명확하면 AnswerGenerator로 넘깁니다. QuestionImprovement에는 clarification_count와 max_clarifications: 3이 있어, 되묻기가 끝없이 반복되지 않도록 제한합니다.
또 하나의 문제는 맥락이었습니다. 에이전트가 나뉘면 각 에이전트가 보는 정보가 달라질 수 있습니다. 분류기는 질문만 보고, 답변 생성기는 뒤늦게 요약된 맥락만 보면 답변 일관성이 떨어집니다. 그래서 MAICE는 세션 단위의 공유 맥락을 백엔드에서 관리하고, 각 에이전트가 자기 역할에 필요한 맥락을 함께 받도록 했습니다.
6. 왜 이 구조가 교육적으로 중요한가
멀티에이전트 구조의 목적은 기술적으로 멋있어 보이기 위한 것이 아닙니다. 교육적 개입을 나누어 보고, 어느 단계에서 어떤 일이 일어났는지 관찰하기 위한 구조입니다.
질문 분류는 학생 질문의 상태를 봅니다. 질문 명료화는 학생이 자신의 문제를 다시 정의하도록 돕습니다. 답변 생성은 그 다음에야 이루어집니다. 관찰 에이전트는 이 흐름이 실제 세션에서 어떻게 나타났는지 남깁니다.
이렇게 역할을 나누면 "AI가 좋은 답을 했다"라는 막연한 평가를 넘어, 질문이 명확해졌는지, 답변이 적절했는지, 대화가 학습 과정을 지원했는지 따로 볼 수 있습니다.
하지만 구조가 준비됐다고 해서 학생이 곧바로 좋은 질문을 하는 것은 아닙니다. 수학에서는 질문을 입력하는 순간부터 수식, 사진, 렌더링 문제가 따라옵니다. 다음 글에서는 이 대화가 실제로 시작될 수 있게 만든 인터페이스를 다룹니다.
💬 댓글
이 글에 대한 의견을 남겨주세요