의도 분류(Intent Classification)를 통한 분기 처리
RAG 챗봇을 만들때 주의 할 점은 의도하지 않는 답변들에 대한 처리입니다.
가령 "안녕하세요" 라고 물었을 경우 이 텍스트를 이용하여 임베팅 벡터를 생성하고 기존 입력된 데이타중 가중 유사한 벡터를 찾으려고 합니다.
그리고 가장 유사한 벡터에 대한 문서를 가져와서 "아름답게 말을 지어내죠"
이련 경우를 대비하여 우리는 의도 분류를 통해 먼저 질문의 의도를 먼저 알아보고 다음 단계(실제 답변 준비)로 진행하게 만들어야 합니다.
아래는 3개 해결방안을 설명해 두었지만 추천 하는 것은 "의도분류를 통한 분기 처리"를 추천 드립니다.
의도 분류(Intent Classification)를 통한 분기 처리 (가장 추천)
LLM의 강력한 언어 이해 능력을 사용하여, RAG를 실행하기 전에 먼저 사용자의 질문이 어떤 종류인지 '분류'하게 하는 방식입니다.
// app/Services/RagService.php 에 추가
/**
* 사용자의 질문 의도를 몇 가지 핵심 카테고리로 분류합니다.
*/
private function classifyIntent(string $question): string
{
$chatModel = env('OLLAMA_CHAT_MODEL');
// 의도 분류를 위한 전용 프롬프트
$prompt = <<<PROMPT
사용자의 질문을 아래 카테고리 중 가장 적합한 것 하나로 분류하고, 다른 말은 붙이지 말고 오직 카테고리 키워드만 반환해.
# 카테고리:
- GREETING: "안녕하세요", "안녕", "하이" 와 같은 단순 인사
- FAREWELL: "안녕히 계세요", "수고하세요", "나중에 봐요" 와 같은 작별 인사
- INFORMATION_QUERY: 무언가에 대해 물어보는 모든 정보성 질문 (예: "비용 알려줘", "어떻게 가?")
# 사용자 질문: {$question}
# 분류 결과:
PROMPT;
$response = Http::timeout(10)->post("{$this->ollamaUrl}/api/generate", [
'model' => $chatModel,
'prompt' => $prompt,
'stream' => false,
'options' => ['temperature' => 0.0] // 일관된 결과를 위해 온도를 0으로 설정
]);
// LLM이 반환한 텍스트에서 정확히 키워드만 추출 (공백, 줄바꿈 등 제거)
return trim($response->json('response', 'INFORMATION_QUERY'));
}
public function handleQuery(string $question, array $userInfo): string
{
// 1. 사용자 질문의 의도를 먼저 분류합니다.
$intent = $this->classifyIntent($question);
// 2. 의도에 따라 분기 처리합니다.
if ($intent === 'GREETING') {
return "안녕하세요! 무엇을 도와드릴까요?";
}
if ($intent === 'FAREWELL') {
return "네, 감사합니다. 좋은 하루 보내세요!";
}
// 3. 정보성 질문일 경우에만 기존 RAG 파이프라인을 실행합니다.
$questionEmbedding = $this->getEmbedding($question);
$contextDocuments = $this->searchChroma($questionEmbedding);
$context = implode("\n---\n", $contextDocuments);
return $this->generateAnswer($question, $context);
}
키워드 기반 필터링 (간단한 방법)
간단한 인사말 목록을 미리 만들어두고, 사용자의 질문이 그 목록에 포함되는지 확인하는 단순한 규칙 기반 방식입니다.
- 장점: 매우 빠르고 간단하게 구현할 수 있습니다.
- 단점: 목록에 없는 표현("안뇽", "ㅎㅇ" 등)에는 대응하지 못하는 한계가 있습니다.
public function handleQuery(string $question, array $userInfo): string
{
$greetings = ['안녕하세요', '안녕', '하이', 'hello', 'hi'];
if (in_array(strtolower(trim($question)), $greetings)) {
return "안녕하세요! 돌담요양병원 챗봇입니다. 무엇을 도와드릴까요?";
}
// 정보성 질문일 경우에만 기존 RAG 파이프라인을 실행
$questionEmbedding = $this->getEmbedding($question);
// ... 이하 동일
}
최종 프롬프트 수정 (비추천)
generateAnswer 메소드의 프롬프트에 "만약 질문이 단순 인삿말이면, 내부 지식을 무시하고 인사만 해줘" 라는 규칙을 추가하는 방법입니다.
단점: 이 방법은 LLM에게 혼란을 줄 수 있어 매우 불안정하고 추천하지 않습니다. 컨텍스트에 자원봉사 내용이 들어온 이상, LLM은 그 내용을 사용하려는 경향이 매우 강하기 때문입니다.
결론적으로, 가장 전문적이고 확장성 있는 방법은 "전략 1: 의도 분류" 입니다. 이 방식을 도입하면 챗봇의 수준을 한 단계 높일 수 있습니다.