EN
CASE 08Pilltip / AI Agent, 개인정보 유출 방지

복약 챗봇 - 건강 프로필을 외부 AI 입력에서 분리

Pilltip은 건강 상태에 맞춰 복약 정보를 안내해야 하지만, 그 건강 정보는 가장 민감한 개인정보라고 생각했습니다. 그래서 RAG 기반 AI 오케스트레이션으로 복약 정보를 안내하는 챗봇을 만들되, 사용자 증상에 맞는 의약품 탐색과 프로필에 따른 판단을 나눴습니다. 모델의 언어 해석 능력은 활용하면서, 민감한 프로필을 사용하는 범위는 코드로 관리했습니다.

2025.09

Spring AIWeaviateRAGAI Orchestration

01 배경

사용자는 일상적인 말투로 증상을 설명하지만, 이는 약품과 건강기능식품 효능에 쓰인 단어와 달라 단어 검색으로는 맞는 제품을 찾기 어려웠습니다. 또한, 개인화에는 임신 여부, 복용약, 기저질환을 통한 연산이 필요했는데, 이를 외부 LLM에 보내면 민감정보가 나가는 범위를 통제하기 어려웠습니다.

해결하면 얻는 것

개인 건강 정보를 외부 AI로 보내면 유출 위험과 규제 부담을 지고, 보내지 않으면 개인 맞춤이라는 핵심 기능을 잃습니다. 판단을 내부에서 하면 두 가지를 함께 지킬 수 있습니다.

02 설계와 구현 과정

  1. 증상과 약품 효능을 연결하는 의미 검색 구현

    문제 상황 사용자는 자신의 말투로 증상을 설명합니다. 그래서 제품에 적힌 사전적인 단어와 겹치지 않는 경우가 많았고, 이로인해 알맞은 제품을 찾아오지 못하는 문제가 있었습니다. 따라서 관련 정보를 이어 줄 방법이 필요했습니다.

    판단 처음에는 증상 동의어 사전을 만들기로 하고 코드까지 작성했습니다. 하지만 다뤄야 할 증상이 너무 많았고, 증상을 여러 개 말하면 사전 항목들이 서로 겹쳤습니다. 그중 그 사람에게 맞는 약을 골라야 하는데, 약품 설명이 모호하면 거의 모든 증상에 걸렸습니다. 그래서 자연어 증상과 약품 효능을 잇는 일은 의미 검색에 맡기고, 질문에 필요한 정보를 주도록 처리 경로를 나눴습니다.

    구현 벡터 DB는 무료이면서 하이브리드 검색이 되는 후보 중 Milvus를 먼저 시험했지만 메모리를 많이 차지해, 비용적으로 이득이 없다고 판단했습니다. 그래서 상대적으로 경량인 Weaviate에 약품 효능을 저장해 증상과 뜻이 가까운 정보를 찾게 했습니다. 백엔드가 Spring이라 Python 컨테이너를 따로 띄우는 부하를 피하려고, 오케스트레이션도 Spring AI로 같은 서버에서 처리했습니다. 질문에 따라 약품 탐색, 복약 위험, 섭취량 정보를 처리하는 경로를 나눴습니다.

    확인한 결과 사용자 시나리오 기반 E2E 테스트에서, 자연어로 증상을 말하면 질문에 맞는 처리 경로(약품 탐색, 복약 위험, 섭취량)로 이어지는 흐름을 확인했습니다. 증상별 정확도를 수치로 평가한 기록은 증상의 종류가 너무 많아 남기지 못했습니다.

  2. 건강 프로필의 내부 처리와 외부 AI 입력 분리

    문제 상황 개인화에는 임신 여부, 복용약, 기저질환을 활용한 연산이 필요했지만, 이 프로필을 외부 LLM에 넘기면 민감정보가 새어 나갈 수 있었습니다. 이는 보안적으로 치명적인 문제라고 판단해, 내부에서 이를 처리할 방법을 고안해야 했습니다.

    판단 처음에는 건강 프로필을 외부 AI에 보내되 민감한 내용만 걸러내는 방식을 검토했습니다. 하지만 걸러야 할 표현과 경우가 너무 많아 코드로 모두 막기는 어렵다고 판단했습니다. 내부 서버에 AI 모델을 작동시키는 방법도 있었지만, 온프레미스 환경이여서 서버에 부하가 갈 수 있었습니다. 그래서 증상 표현을 이해하는 일과 건강 프로필의 조건을 판단하는 일을 나누고, 프로필을 쓰는 판단은 입력과 규칙을 직접 관리할 수 있는 내부 코드에 두었습니다.

    구현 임신 여부, 복용약, 기저질환 필드는 외부 LLM 입력에서 뺐습니다. 내부 코드의 프로필 판단을 증상 검색 결과와 이어, 개인화 정보를 주도록 구현했습니다.

    확인한 결과 임신 여부, 복용약, 기저질환은 외부 LLM 호출 입력에 넣지 않고, 내부 Tool이 이 정보로 금기, 중복, 상호작용을 판단해 해당 약을 걸러 내도록 처리 경로를 나눴습니다. 약 상세 화면의 AI 맞춤 안내에도 이 세 가지 정보는 보내지 않았습니다.

03 개선한 처리 흐름

ARCHITECTUREMermaid diagram
flowchart TD
  S[자연어 증상] --> V[Weaviate, 효능 의미 탐색]
  P[임신 여부, 복용약, 기저질환] --> T[내부 Tool, 프로필 판단]
  V --> R[개인화 정보 제공]
  T --> R
  B[외부 LLM 입력 경계] -.-> X[해당 프로필 필드 제외]
  classDef accent fill:#e5edff,stroke:#4164c8,color:#142b60;
  class T,B,X accent;

책임과 데이터 경계를 설명하는 개념도입니다. 실제 호출 순서나 모든 데이터의 외부 유출 방지를 나타내지 않습니다.

Mermaid 원문 보기
flowchart TD
  S[자연어 증상] --> V[Weaviate, 효능 의미 탐색]
  P[임신 여부, 복용약, 기저질환] --> T[내부 Tool, 프로필 판단]
  V --> R[개인화 정보 제공]
  T --> R
  B[외부 LLM 입력 경계] -.-> X[해당 프로필 필드 제외]
  classDef accent fill:#e5edff,stroke:#4164c8,color:#142b60;
  class T,B,X accent;

04 결과와 배운 점

증상 의미 검색과 건강 프로필의 내부 판단 분리

자연어 증상으로 약품 정보를 찾으면서도, 임신 여부, 복용약, 기저질환 필드는 외부 LLM 입력에서 뺐습니다. 민감정보를 다루는 서비스에서 모델에 맡길 판단과 코드가 책임질 판단의 경계를 먼저 정하고 구현했습니다.

결과의 범위와 트레이드오프

프로필 조건이 바뀌면 내부 코드도 수정해야 하는 단점이 있지만, 민감한 건강 정보가 외부 모델로 나가는 것을 막는 게 우선이었기 때문에 이 판단은 AI에게 맡기지 않고 코드에서 처리했습니다. 특정 프로필 항목을 외부 입력에서 뺀 것이며, 모든 경로의 유출 차단을 측정한 결과는 아닙니다. 사용자가 늘면 AI 처리를 별도 서비스로 분리하는 것이 맞다고 봅니다.