본문으로 건너뛰기

의도 파악 기반 Chat 명령어 수행의 성능 향상

· 약 6분
Yura Shin
Yura Shin
AI Specialist

서론

사용자는 Chat Agent에게 단순한 텍스트를 입력합니다. "모니터링 시작해주세요.", "사람에 대한 threshold를 0.6으로 맞춰줘.", "타겟 리스트에 나무 추가해."

겉으로 보기엔 단순한 대화지만, LLM 내부에서 이 요청을 처리하기 위해 수행해야하는 작업은 훨씬 복잡합니다.

LLM은 먼저 사용자의 요청이 어떤 종류의 작업인지 스스로 분류해야 합니다.

"이건 Target 설정인가? 시나리오 편집인가? 아니면 단순 조회인가?"

그 다음, 해당 태스크에 필요한 파라미터를 정확히 추출하고, 값이 허용 범위에 있는지 검증하며, 잘못된 값이면 이유까지 사용자 친화적으로 설명해야 합니다.

즉, 사람이 여러 단계에 걸쳐 순차적으로 판단해야 할 일을, 기존 LLM 구조는 한 번의 호출로 모두 처리하도록 설계되어 있었습니다.

이 방식은 외형상 깔끔해 보였지만, 실제로는 예측하기 어려운 문제를 반복적으로 만들어냈습니다.

  • 태스크 타입을 잘못 분류하여 엉뚱한 작업을 수행
  • 다른 태스크의 규칙이 섞여 충돌 발생
  • 잘못 추출한 파라미터가 그대로 통과
  • 규칙이 복잡하게 얽혀 유지보수 비용 상승

결국, 근본적인 문제를 해결하기 위해 LangGraph 기반 Multi-Node Routing 구조로 Chat Agent를 재설계하게 되었습니다.




1. 단순한 요청도 LLM 에게는 "복합 의사결정" 이다.

기존 Chat Agent는 사용자의 입력을 해석하는 모든 과정을 한 번의 LLM 호출로 처리하려고 했습니다.

예를 들어, "나무에 대한 threshold를 0.3으로 바꿔줘"라는 단순 요청조차 LLM 내부에서는 다음 작업을 동시에 수행해야 했습니다.

  1. 요청의 태스크 유형 판단
  2. 타겟과 임계값 쌍으로 이뤄진 파라미터 ("tree: 0.3") 추출
  3. 값이 허용 범위인지 검증
  4. 현재 설정과 충돌 여부 점검
  5. 변경 가능 여부 판단
  6. 사용자에게 이해하기 쉬운 자연어로 응답 생성

문제는 이 모든 판단을 하나의 프롬프트, 하나의 규칙 집합, 하나의 LLM 호출에 의존하도록 설계했다는 점입니다.


이 때문에 다음과 같은 구조적 문제가 발생했습니다.

  • A 태스크 규칙이 B 태스크에 영향을 미침
  • 잘못된 값이 들어와도 오류를 감지하지 못함
  • 작은 수정에도 프롬프트 전체를 다시 작성해야 하고, 에러 메시지를 미리 모두 작성해야 함

프롬프트 길이는 최대 3,700토큰까지 늘어나 매우 길었고, 기능이 추가될수록 구조는 더 취약해지며 문제 발생도 잦아졌습니다.




2. 기존 구조가 가진 근본적 문제

기존 구조에서 LLM은 다섯 가지 역할을 동시에 수행해야 했습니다.

  • 태스크 분류
  • 파라미터 파싱
  • 값 검증
  • 에러 처리
  • 자연어 생성 이 때문에 여러 가지 구조적 문제가 발생했습니다.

2.1. 태스크 규칙 충돌

EVA가 동영상에서 어떤 target을 탐지하도록 할지 설정하는 태스크의 경우, 탐지에 사용되는 target label은 반드시 영문으로 작성해야 한다는 규칙이 있습니다. 하지만 이 규칙이 탐지 시나리오를 설정하는 태스크에도 적용되어, 시나리오 설명이 한국어임에도 영어로 강제 변환되는 문제가 발생했습니다. 즉, 하나의 프롬프트에 모든 규칙을 모아 두면 LLM이 어떤 규칙을 적용해야 할지 판단하는 과정에서 혼란이 생길 수밖에 없었습니다.


2.2. 파라미터 파싱이 안정적이지 않음

자연어로 입력된 숫자·값을 LLM이 항상 올바르게 해석하지 못하는 경우가 잦았습니다. 예를 들어 사용자가 "threshold one point five"라고 쓰면 의도는 1.5일 텐데, 일부 사례에서는 LLM이 이를 0.15로 잘못 해석하거나(소수점 위치 오류), 아예 전혀 다른 숫자를 출력하는 일이 있었습니다. 또 "two"나 "one hundred" 같은 단어형 숫자, 쉼표·단위(예: 1,000 vs 1000)·로케일(쉼표와 소수점 표기 규칙) 등이 섞이면 파싱 실패 가능성은 더 커집니다.


2.3. 에러와 관련된 모든 경우를 직접 처리해야 함.

기존 구조에서는 모든 에러를 LLM이 판단하고 처리해야 했습니다. 따라서 모든 경우를 룰베이스로 미리 정의해야 했고, 새로운 태스크나 파라미터가 추가될 때마다 유지보수가 복잡해졌습니다.




3. 라우팅 기반 구조 도입

문제를 해결하기 위해, 기존의 거대한 단일 구조를 해체하고 LangGraph 기반 3-Stage 라우팅 구조로 재설계했습니다.

핵심 철학은 단순합니다.

LLM에게 여러 일을 동시에 시키지 않는다. 한 단계에는 한 가지 목적만 부여한다.


3.1. Task Routing Node

"이 요청이 어떤 태스크인지 정확히 분류한다"

Routing Node의 역할은 아주 단순하지만 결정적입니다.

  • 파라미터 파싱도 하지 않고
  • 검증도 하지 않고
  • 규칙 적용도 하지 않습니다

오직 태스크 타입만 판단합니다.

사용자의 입력 + EVA가 실행할 수 있는 태스크 목록 + 그리고 이미 설정되어 있는 시스템 정보, 이 세 가지를 기준으로 가장 적절한 태스크로 라우팅합니다.


3.2. Task-Specific Parameter Parser

"각 태스크는 고유한 파서와 규칙을 사용해 완전히 독립적으로 파라미터를 추출한다"

기존에는 모든 태스크가 하나의 거대한 프롬프트와 공통 규칙을 공유했지만, 수정 후의 구조에서는 각 태스크가 자신의 파서(parser), 규칙(rule set), 프롬프트(prompt)를 가진 독립된 LLM 호출로 분리됩니다.

예를 들어:

  • Target을 세팅하는 태스크 → Set Target 전용 파서 + Set Target 규칙 + Set Target 전용 프롬프트
  • 모니터링을 시작하는 태스크 → Start Monitoring 전용 파서 + Start Monitoring 규칙 + Start Monitoring 전용 프롬프트

즉, 각 태스크는 독립된 LLM 호출 1회에서 자신의 규칙만 읽고, 자신의 파서만 적용한 뒤 파라미터를 추출합니다. 다른 태스크의 규칙이나 파서가 섞일 일이 없기 때문에, 규칙 충돌 문제는 구조적으로 발생할 수 없도록 설계되었습니다.  


3.3. Error Handling Node  

"검증은 시스템이 최종적으로 결정하고, 오류 안내는 LLM이 담당한다"

Task-Specific Parser 단계에서도 LLM이 기본적인 판단을 수행하여 값이 이상해 보이는 경우를 어느 정도 감지할 수는 있습니다. 하지만 LLM의 판단만으로는 안정성을 보장할 수 없기 때문에, 최종 검증(Validation)은 시스템에 정의된 규칙에 따라 별도로 수행됩니다.

즉, 파싱 단계에서 LLM이 어떤 판단을 했더라도, 시스템 Validator가 다시 한 번 모든 파라미터를 공식 규칙에 따라 검증하고, 오류가 발견되면 Error Node가 개입하여 사용자가 이해하기 쉬운 자연어로 안내합니다.

예를 들어, threshold가 1.5로 입력된 경우:

  • Parser: "threshold: 1.5"
  • Validator: 시스템 규칙에 따라 범위 오류를 확정
  • Error Node: "threshold는 0.0과 1.0 사이의 값이어야 합니다. 다시 입력해주세요."

이 구조 덕분에 규칙 업데이트가 훨씬 단순해지고 에러 판단의 일관성이 시스템에 의해 보장되며 에러 메시지는 항상 동일한 형태와 톤으로 출력됩니다.

LLM이 에러를 "판단"하지 않고, 오직 에러를 "설명"하는 역할만 수행하는 구조로 재정립된 셈입니다.




4. 성능 평가 결과 

라우팅 기반 구조로 전환하면서 단순히 정확도가 개선된 것에 그치지 않았습니다. 전체 아키텍처가 더 단순해졌고, 분류 성능·속도·유지보수성 측면에서도 실질적인 개선이 이루어졌습니다.  

4.1. 태스크 분류와 파라미터 파싱 성능 개선

Casebeforeafter
Task Routing Accuracy82.3%95.0%
Parameter Parsing Accuracy69.6%95.0%

라우팅 단계가 분리된 덕분에 태스크 분류와 파싱 모두 구조적으로 안정화되었고, 두 영역에서 동시에 큰 폭의 성능 향상을 확인할 수 있었습니다.  


4.2. 프롬프트 길이 대폭 축소

Casebeforeafter
Min1,603 tokens1,106 tokens
Max3,783 tokens1,793 tokens

프롬프트 길이가 절반 수준으로 줄어들며 LLM이 더 안정적으로 추론할 수 있는 환경이 만들어졌습니다.


4.3. 추론 시간 개선

Casebeforeafter
Min1.19 s1.50 s
Max2.98 s2.03 s

호출 횟수는 늘었지만, 각 단계가 훨씬 가벼워지면서 오히려 최대 latency는 줄어드는 결과를 만들었습니다.




5. 결론

이번 개편에서 얻은 교훈은 다음과 같습니다.

문제는 LLM 자체가 아니라, LLM에게 일을 시키는 방식에 있었다.

한 번의 호출에서 모든 판단을 맡기면 LLM은 혼란스러워지고 정확도가 쉽게 흔들립니다. 하지만 각 단계별로 역할을 나누자 LLM은 훨씬 더 안정적이고 예측 가능하게 동작합니다.

예를 들어,

  • 태스크 분류는 Router가,
  • 파라미터 추출은 Parser가,
  • 검증은 Validator가,
  • 오류 안내는 Error Node가

각자 역할을 수행하도록 나누자 LLM은 혼란 없이 안정적으로 판단을 수행할 수 있었습니다.

라우팅 기반 구조는 단순한 기술적 변화가 아니라, EVA Chat Agent가 신뢰 가능한 AI 인터페이스로 성장하는 핵심 전환점이 되었습니다.