
LangChain을 사용하여 LLM 애플리케이션이나 Agent를 개발하다 보면, 우리는 자연스럽게 SystemMessage, HumanMessage, AIMessage와 같은 객체들을 레고 블록처럼 다루게 됩니다.
이러한 추상화(Abstraction)는 개발 생산성을 높여주지만, "내가 작성한 이 객체가 실제로 모델 내부에서 어떻게 처리되는가?"에 대한 본질적인 작동 원리를 이해하기 어렵게 만듭니다.
특히 복잡한 Multi-Agent 시스템에서는, 종종 표준 Role(User/Assistant) 외에 Reviewer나 Summarizer 같은 커스텀 Role(Custom ChatMessage)을 정의하여 모델에게 특정 역할을 강제하려는 시도를 하는 경우가 있습니다. JSON 구조상으로는 그럴듯해 보이지만, 결론부터 말하자면 이는 LLM의 작동 원리에 위배되는 안티 패턴(Anti-pattern)이며, 의도치 않은 환각(Hallucination)이나 성능 저하의 원인이 될 수 있습니다.
이번 포스팅에서는 LangChain의 Message 객체가 API의 JSON Payload를 거쳐, 최종적으로 모델이 이해하는 ChatML(특수 토큰) 형태로 변환되는 전체 파이프라인을 'Under the Hood' 관점에서 해부해 봅니다. 이 과정을 통해 LLM이 실제로 프롬프트를 인식하는 메커니즘을 이해하고, 왜 "정해진 Role"을 준수하도록 권장되는지 그 기술적 배경을 소개합니다.
개발자가 LangChain으로 Agent를 빌드하는 코드 단계입니다. 여기서 SystemMessage, HumanMessage는 클래스 객체입니다. Agent는 이 객체들을 리스트(List[BaseMessage])로 관리하며 대화 기록과 도구 실행 결과를 누적합니다.
이러한 방식 덕분에 개발자는 API 명세를 신경쓰지 않고 객체 지향적으로 대화 흐름과 로직을 설계합니다.
messages = [
SystemMessage(content="너는 비서다."),
HumanMessage(content="안녕?"),
]
Agent가 invoke되거나 모델을 호출하는 순간, LangChain은 내부의 메세지 객체들을 타겟 모델의 API 명세에 맞는 JSON 포맷으로 직렬화하여 전송합니다.
SystemMessage → "role":"system"HumanMessage → "role":"user"AIMessage → "role":"assistant"[
{"role": "system", "content": "너는 비서다."},
{"role": "user", "content": "안녕?"}
]
하지만, 엄밀히 말하면 모델은 JSON을 입력받을 수 없습니다. 따라서 API 서버는 수신된 JSON을 텍스트로 변환하여 모델에 입력해줘야 합니다.
특히 대부분의 경우 모델이 학습한 형태인 ChatML(Chat Markup Language) 포맷이 활용됩니다. 이때, role:"system"은 단순 문자열이 아닌, 모델에 미리 학습된 특수 토큰으로 변환되어 주입됩니다.
<|im_start|>system
너는 비서다.<|im_end|>
<|im_start|>user
안녕?<|im_end|>
<|im_start|>assistant
LangChain Messages의 role은 결국 ChatML에서 모델에 미리 학습된 특수 토큰으로 변환되어 입력됩니다. 쉽게 말해, role이란 단순한 메타데이터가 아니라, 입력된 content를 어떻게 처리할지 결정하는 하드코딩된 명령어와 같습니다.
따라서 개발자가 임의의 Role(reviewer 등)을 사용하면, 이에 매핑될 특수 토큰이 없기 때문에 모델은 이를 단순 텍스트로 인식하거나, 학습되지 않은 패턴으로 간주하여 의도한 컨텍스트를 전달할 수 없게 됩니다.
따라서 Agent에게 다양한 페르소나 혹은 역할을 부여하고 싶다면, 본인이 만들어낸 role을 사용하는 대신, content에 구체적인 프롬프트를 작성하는 방식이 더 확실하고 공학적인 접근이라고 생각됩니다.
사실 최근엔 너무 많은 모듈과 프레임워크가 나오면서 기능을 DocString이나 간단한 테스트로 '에러 없이 돌아가게만' 파악하고 사용해왔던 것 같습니다. 그런데 이번에 LangChain의 추상화된 Messages의 동작 원리를 파악해보면서 '이해 기반 개발의 중요성'을 다시 한 번 깨달았습니다.
특히, LLM 기반 애플리케이션은 프로토콜을 준수하는게 매우 중요한 것 같습니다.