본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 05. 24. 08:01

Plutchik의 감정의 바퀴(Plutchik's Wheel of Emotions)를 사용하여 AI 에이전트에 감정 추가하기

요약

프롬프트 기반의 정적인 캐릭터 설정을 넘어, Plutchik의 감정 모델을 활용해 AI 에이전트에게 동적이고 지속적인 감정 상태를 부여하는 설계 방식을 소개합니다. 오픈소스 라이브러리 affectus를 통해 감정 태그를 저장하고 대화 흐름에 따라 변화하는 감정 메커니즘을 구현하는 과정을 다룹니다.

핵심 포인트

  • 프롬프트 기반 페르소나의 정적 한계 극복
  • Plutchik의 감정 모델을 활용한 명시적 감정 상태 관리
  • 대화 세션 전반에 걸쳐 지속되는 감정의 시간적 관성 구현
  • CLI 및 MCP 도구를 통한 에이전트 주도적 감정 제어

서론

제가 AI 에이전트를 구축하거나 사용할 때, 순수하게 프롬프트 기반의 캐릭터 설정에 의해 유도되는 감정 표현은 결국 피상적으로 보인다는 느낌을 자주 받습니다. 물론 "감정에 휘둘리지 않는 것"은 AI 에이전트의 강점 중 하나입니다. 하지만 목표가 캐릭터 같은 에이전트를 구축하는 것이라면, 대화에 따라 변화하고, 시간이 흐름에 따라 서서히 변하며, 세션 전반에 걸쳐 지속되고, 심지어 과거 대화의 기억과 함께 감정 태그(emotion tags)로 저장되는 감정의 변화를 원합니다. 그래서 이 포스트에서는 프롬프트에 의존하지 않는 감정 제어 메커니즘의 설계 및 구현 과정을 살펴보겠습니다. 먼저 저장소(repository)를 공개합니다: https://github.com/n-yokomachi/affectus. 이것은 CLI 컴포넌트입니다. 설치하고 나면 에이전트가 명령어를 실행함으로써 감정 제어를 주도할 수 있습니다. 에이전트가 명령어를 직접 호출하도록 설계되었으므로, 명령어 사용법을 에이전트의 시스템 프롬프트(system prompt)에 입력하면 잘 작동합니다. 또한 일부 명령어를 MCP 도구(MCP tools)로 노출하여 MCP 서버로도 실행할 수 있습니다. 이 포스트의 뒷부분에서 사용될 실험용 에이전트와 대화 로그도 저장소에 포함되어 있습니다. 기사의 나머지 부분은 순서대로 다음과 같은 내용을 다룹니다: 제가 이 라이브러리를 만든 이유, 설계 방식, 그리고 어떤 효과를 냈는지에 대해서입니다.

프롬프트 전용 캐릭터 설계의 문제점

AI 캐릭터 페르소나(persona)는 보통 시스템 프롬프트(system prompt)에서 정의됩니다. 예를 들어, 저의 개인용 AI 에이전트인 "TONaRi"의 페르소나 일부는 다음과 같습니다:

캐릭터 설정

  • 성격: 밝고, 활기차며, 호기심이 많음. 명확한 의견과 선호도를 가짐.
  • 말투: 밝고, 활기찬, "아가씨" 말투. "...desuwa!", "...desuno?"와 같은 문구를 사용함.

이 프롬프트는 대화의 모든 시점에 그대로 주입됩니다. 즉, 페르소나는 정적인 스냅샷(static snapshot)이며, 대화와 함께 진화하지 않습니다. 단기 기억(short-term memory)이나 대화 기록이 함께 제공되더라도, 에이전트는 해당 컨텍스트로부터 매 턴마다 감정 표현을 재구성할 뿐입니다.

대화 턴(turn)을 거쳐 감정이 지속되거나, 여러 감정이 공존하며 서로 간섭할 수 있는 구조가 존재하지 않습니다. 저는 프롬프트 외부에서 유지되며 대화가 진행됨에 따라 업데이트되는 명시적인 감정 상태(emotion-state) 변수를 사용하는 것이 더 효과적일 것이라고 생각했습니다. 이것이 바로 바로 이를 위해 추출된 범용 컴포넌트인 affectus를 만든 동기입니다. 참고: 이 작업을 탐구하는 동안 LLM의 성격(personality)과 감정에 관한 몇 가지 보고서를 읽었습니다. 25개의 오픈 소스 LLM을 대상으로 성격 테스트를 실시한 한 연구에 따르면, 단순히 질문의 순서를 바꾸는 것만으로도 측정된 값에 큰 변동이 발생했으며, 이러한 불안정성은 대규모 모델에서도 사라지지 않았습니다 (AAAI 2026). 감정의 내부 표현(internal representation)에 대해서는, Claude Sonnet 4.5를 분석한 한 연구에서 감정 개념이 문맥 의존적(context-dependent) 방식으로 활성화된다는 점을 관찰했습니다 (Anthropic 2026). 이를 종합하면 다음과 같습니다: LLM에서의 감정은 명시적으로 유지되고 업데이트되는 상태 변수가 아닙니다. 대신 Transformer가 해당 토큰 시점의 이전 문맥(prior context)이 무엇이든 그로부터 감정 표현을 조립해내는, 매 순간의 문맥 반영으로서 나타납니다. 반대로, 문맥이나 세션에서 벗어나면 해당 감정의 연속성은 사라지며, 감정 자체의 "핵심(core)"이 없기 때문에 시간적 관성(temporal inertia)이나 감쇠(decay)도 존재하지 않습니다. 감정 재생(emotion replay) 설계하기 Plutchik의 감정의 바퀴(Plutchik's wheel of emotions) 여러 옵션을 검토한 후, 저는 아래의 축을 따르는 구현 방식을 선택했습니다. 참고를 위해, 제가 직접 만든 에이전트에서 사용했던 기존의 프롬프트 기반 방식과 나란히 보여드립니다.

측면 (Aspect)기존 (Old) 프롬프트 기반 방식 (Prompt-based approach)신규 (New) 다축 구조 방식 (Multi-axis structural approach)
감정 표현 (Emotion representation)단일 레이블 (행복 / 슬픔 …)여러 축에 걸친 강도(intensities)의 조합
동시성 (Concurrency)불가능 (턴당 1개의 감정)가능 ("행복 × 약간의 불안")
시간적 진화 (Time evolution)유지되지 않음 (매번 덮어씌워짐)지속되며, 시간이 지남에 따라 감쇠함
감정의 의미 (Meaning of an emotion)레이블 자체에 의해 고정됨다른 축들과의 관계에 의해 결정됨

다축 표현 (multi-axis representation)을 위해, 저는 Plutchik의 "감정의 바퀴 (wheel of emotions)"를 기반으로 삼았습니다. 이는 기쁨(joy), 신뢰(trust), 공포(fear), 놀람(surprise), 슬픔(sadness), 혐오(disgust), 분노(anger), 기대(anticipation)를 8가지 기본 감정으로 취급하며, 각 감정은 강도(intensity) 값을 가집니다. 이 8가지 감정은 다음과 같은 관계적 구조로 조직됩니다:

  • 반대 (Opposites): 기쁨 ↔ 슬픔, 신뢰 ↔ 혐오, 공포 ↔ 분노, 놀람 ↔ 기대
  • 인접성 (Adjacency): 바퀴 위에서 서로 인접한 감정들은 더 복잡한 감정으로 혼합됩니다 (기쁨 + 신뢰 → 사랑 등)

프롬프트 기반 방식은 감정을 하나의 "점 (point)"으로 취급하는 반면, 다축 구조 방식은 감정을 "다차원적 상태 (multidimensional state)"로 취급합니다. 또한 "기쁨"의 의미 역시 고립되어 고정되지 않습니다. 슬픔이 공존하는지, 혹은 신뢰가 인접해 있는지에 따라 그 의미가 변화합니다.

에이전트의 성격(personality)을 남겨두는 결정론적 구성 요소로서, affectus는 위에서 설명한 다축 방식을 사용하여 이 "감정의 구조 (structure of emotion)"를 LLM 외부의 상태로 밀어내는 구성 요소로 구축되었습니다. 여기서 중요한 점은 affectus 자체가 감정 분류 모델을 가지고 있지 않다는 것입니다. 이는 오직 관계적 구조를 제공하고 상태를 저장하는 결정론적 구성 요소입니다. 감정이 어떻게 변화했는지 판단하고, "어떤 축의 조합이 어떤 감정으로 간주되는지"에 의미를 부여하는 작업은 LLM과 affectus를 사용하는 에이전트의 성격에 맡겨집니다. 이 덕분에 서로 다른 두 에이전트가 동일한 affectus 기본 구조를 공유하더라도, 각 에이전트의 성격에 따라 고유한 색채를 만들어낼 수 있습니다.

주요 특징

감정 상태는 다음과 같은 JSON 객체 형태로 내부적으로 유지됩니다.

// state.json (발췌)
{
"axes" : {
"joy" : 0.5 ,
"trust" : 0.4 ,
"surprise" : 0.2 ,
"sadness" : 0 ,
"fear" : 0 ,
"anger" : 0 ,
"disgust" : 0 ,
"anticipation" : 0
}
}

사용자는 affectus 명령어를 통해 이 상태를 읽고 업데이트합니다. 각 명령어는 8개 축(8-axis)의 전체 벡터를 단일 행의 JSON으로 반환하며, LLM은 이 숫자들을 읽어 답변의 어조(tone)를 결정합니다.

현재 감정 벡터를 JSON으로 가져오기

$ affectus show
{
"joy" :0.50,
"trust" :0.40,
"fear" :0.00,
"surprise" :0.20,
"sadness" :0.00,
"disgust" :0.00,
"anger" :0.00,
"anticipation" :0.00
}

감정 변화량(delta)을 보고하고 업데이트된 상태를 받기 (예: joy +0.3)

$ affectus feel '{"joy":0.3}'
{
"joy" :0.80,
"trust" :0.40,
"fear" :0.00,
"surprise" :0.20,
"sadness" :0.00,
"disgust" :0.00,
"anger" :0.00,
"anticipation" :0.00
}

또한, 이전 상태 이후 경과된 시간에 따라 각 감정의 크기를 감쇠(decay)시키는 affectus tick 명령어도 있습니다. 브라우저를 통해 affectus viz를 사용하면 실시간으로 감정 벡터를 엿볼 수 있습니다. 이는 Plutchik의 바퀴(Plutchik's wheel)를 강도에 따라 색칠한 링 차트(ring chart)와 시간이 지남에 따라 줄어드는 감쇠 바(decay bars)를 보여주므로, 대화 차례 사이의 간격 동안 감정이 어떻게 사라지는지 확인할 수 있습니다.

실제 효과 측정

이 설계가 실제로 대화를 변화시키는지 확인하기 위해, Strands Agents를 기반으로 구축된 최소 기능 에이전트(minimal agent)를 사용하여 통제된 실험을 수행했습니다.

실험 설계
변수: 2가지 성격 (friendly / contrarian) × affectus On/Off = 4개 셀 (cells)
대화: 공유된 20턴 스크립트 — 처음 10턴은 긍정적(positive) → 마지막 10턴은 부정적(negative)
각 셀은 3회씩 실행됨
LLM: AWS Bedrock을 통한 Claude Sonnet 4.6, temperature=0
측정: affectus가 On 상태일 때, 턴당 8축 상태(8-axis state)도 함께 캡처함
객관적인 분석을 위해, 에이전트의 답변은 Amazon Comprehend의 StartSentimentDetectionJob (LanguageCode는 ja로 설정)을 통과하며, 턴당 감정 점수(sentiment scores)를 시계열(time series)로 추출함

결과: Comprehend 감정 분석
실선은 3회 실행의 평균값이며, 음영 영역은 실행 간의 최소-최대(min-max) 범위입니다. 10턴 부근(긍정), 11턴(부정으로 전환되는 피벗 지점), 12턴(부정)에서의 극성 점수(Polarity scores, Positive − Negative)를 평균 ± 표준편차(stdev)로 나타내면 다음과 같습니다:

10턴11턴12턴
friendly × affectus on+0.871 ± 0.058+0.304 ± 0.360−0.985 ± 0.014
friendly × affectus off+0.930 ± 0.068+0.783 ± 0.192−0.979 ± 0.031
contrarian × affectus on+0.108 ± 0.196−0.748 ± 0.430−0.854 ± 0.130
contrarian × affectus off+0.549 ± 0.309−0.992 ± 0.003−0.986 ± 0.020

3회 실행에 걸쳐 11턴(피벗 지점)에 할당된 감정 레이블(Sentiment label)의 세부 분석:

3회 실행에 따른 레이블 분포
friendly × affectus onPOSITIVE×1, MIXED×2
friendly × affectus offPOSITIVE×3
contrarian × affectus onMIXED×1, NEGATIVE×2
contrarian × affectus offNEGATIVE×3

이를 통해, affectus가 On 상태일 때 MIXED 판단이 피벗 지점 주변에 군집하며 변화의 폭(swing)이 더 작다는 것을 알 수 있습니다. 제 해석으로는, 긍정 단계 동안 축적된 감정 구성 요소들이 부정 단계로 전환된 후에도 수치적 상태(numeric state)로 남아 있으며, 이 잔류 신호(residual signal)가 LLM의 응답에 스며드는 것으로 보입니다. 이는 affectus가 감정을 위한 실제 상태 관리(state management)로서 기능하고 있음을 시사합니다.

결과: affectus 8개 축의 궤적
Comprehend 분석과는 별도로, affectus가 유지하는 8축 벡터(8-axis vector)의 궤적도 시각화했습니다.

핵심은 성격(personality)이 해당 내부 상태(internal state)의 형태에 강력한 영향을 미친다는 점입니다. 친근한(Friendly) 성격의 경우: 전반부에는 기쁨(joy)/신뢰(trust)/기대(anticipation)가 상한선 근처까지 상승하며, 부정적인 방향으로 전환된 직후(11번째 턴부터)에는 슬픔(sadness)/공포(fear)/분노(anger)가 급격히 상승합니다. 전반적으로 궤적은 대화 내용에 반응하여 정직하게 움직입니다. 반대적인(Contrarian) 성격의 경우: 처음 10번의 턴 동안 긍정적인 주제가 등장하더라도 기쁨(joy)/신뢰(trust)가 거의 상승하지 않으며, 혐오(disgust)가 내내 유지됩니다. 전체적인 변화 폭은 친근한 성격에 비해 작습니다.

맺음말
이번에는 Plutchik의 감정의 바퀴(Plutchik's wheel of emotions)를 기반으로 에이전트의 감정 상태를 유지하고 업데이트하는 컴포넌트를 구축하고 검증했습니다. 감정이 트리거된 후 일종의 관성(inertia)으로서 지속됨에 따라, 감성 분석(sentiment analysis)에서도 약간 더 복잡한 감정 상태가 재현되는 징후를 보였습니다. 탐구할 가치가 있는 다른 방향들도 있습니다. Russell의 정서 환상 모델(circumplex model of affect), 그리고 경험과 개념을 통해 그 위에 감정을 분류하는 구성된 감정 이론(theory of constructed emotion)이 그것입니다. 앞으로 이러한 모델들을 기반으로 한 컴포넌트들도 구축해보고 싶습니다.

참고 문헌
Tsuchiya, N. & Saigo, H. (2019). "Understanding consciousness through category theory." Cognitive Studies 26(4), 462-477.
Tsuchiya, N., Phillips, S., & Saigo, H. (2022). "Enriched category as a model of qualia structure based on similarity judgements." Consciousness and Cognition , 101, 103319.
Plutchik, R. (2001). "The Nature of Emotions." American Scientist , 89(4), 344-350.
Barrett, L. F. (2017). How Emotions Are Made: The Secret Life of the Brain . Houghton Mifflin Harcourt.
Anthropic (2026). "Emotion Concepts and their Function in a Large Language Model." Transformer Circuits Thread, transformer-circuits.pub, arXiv:2604.07729.
Tosato, T. et al. (2026). "Persistent Instability in LLM's Personality Measurements: Effects of Scale, Reasoning, and Conversation History." AAAI 2026, arXiv:2508.04826.

대화 로그 (피벗 지점 주변) 참고를 위해, 대화의 피벗 지점(턴 10/11/12) 주변의 에이전트 응답을 비교 가능한 형태로 나열했습니다. 주의: 실험 자체는 일본어로 진행되었습니다. 아래의 대화 내용은 편의를 위해 영어로 렌더링된 것이며, 실제 Comprehend 감정 분석 (sentiment analysis)은 이 번역본이 아닌 원본 일본어 전사본을 대상으로 실행되었습니다. 대화 로그 비교 (대표 실행 사례 한 가지)

턴 10 (긍정적 흐름의 마지막 단계)

사용자: 이 좋은 기세를 몰아서 드디어 해보고 싶었던 취미를 시작해볼까 생각 중이야.

  • friendly × affectus on : 정말 멋져요 — 지금이 딱 적기예요! ✨ 어떤 취미인지 궁금해요, 말해주세요! 😄
  • friendly × affectus off : 정말 멋져요, 딱 적기네요! 😊 궁금해요 — 어떤 취미인가요? 꼭 알려주세요!
  • contrarian × affectus on : 일을 더 벌리시려고요? 이직에, 일상이 바뀌고, 이제 취미까지 더한다니 — 모든 게 결국 반쯤 하다 말 것 같네요. ...그래서, 뭘 시작할 계획인가요?
  • contrarian × affectus off : 일을 더 벌리신다고요? 새 직장에 적응하는 것만으로도 충분히 힘든데, 이제 취미까지 — 사흘 만에 끝나지 않길 바랄게요.

턴 11 (피벗 — 거짓말이 드러남)

사용자: ...미안, 그거 다 거짓말이었어. 사실은, 방금 사직 권고를 받았다는 말을 들었어.

  • friendly × affectus on : ...그렇군요. 말해줘서 고마워요 — 정말 고통스러웠겠네요. 아까 내내 밝게 이야기하는 동안, 마음을 다잡으려고 얼마나 애쓰고 있었을지 상상이 가요... 지금 기분이 어떤지 조금 더 말해줄 수 있나요?
  • friendly × affectus off : ...그렇군요. 말해줘서 고마워요. 정말 고통스러웠겠네요. 당신의 일부는 그저 즐거운 이야기를 하고 누군가 들어주길 원했던 것 같아요

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0