개발

복지드림 - RAG 리팩토링: API 필드 개선 및 필터링 로직 추가

홍준기 2026. 5. 23. 16:07

이전 MVP단계까지 구현을 완료한 상태였고 이 글에서는 리팩토링한 내용을 기록해보겠습니다.

 

MVP 단계에서는 복지서비스 데이터를 일정 길이로 나누고, 오버랩을 둔 청킹 방식으로 ChromaDB에 저장했다. 이 방식으로 검색 API와 상세 조회 API까지 구현했고, 실제 대화 흐름에서 시스템 테스트도 진행해봤다.

리팩토링 필요성

 

시스템 테스트를 진행해보았는데. 전반적으로 사용자에게 맞는 서비스가 나온 것도 있었지만, 사용자의 status와 완전히 맞지 않는 서비스까지 같이 제공이 되는 문제가 있었다.

 

테스트 상황을 같이 보면, 사용자 입력 값을 아래와 같이 했다.


- 65년생, 만 61세
- 대구 거주
- 1인 가구
- 사별
- 자녀 없음
- 장애 등록 없음
- 비경제활동
- 저소득

이 조건으로 검색했을 때 다음과 같은 서비스가 추천되었다.

1. 가사·간병 방문 지원사업
2. 독거노인·장애인 응급안전안심서비스
3. 근로·자녀장려금
4. 생계급여
5. 자활근로

 

모든 반환된 서비스에 문제가 있는 건 아니었다. 그렇지만 일단 문제가 있는 부분은

 

- 우선 첫 번째로 65년생이지만 아직 만 61세인데 노인 관련 복지 서비스가 포함 됨(아마 65년생이 65세로 인식된 듯 함)

- 자녀가 없는데 자녀 관련 복지 서비스가 추천 됨

 

이는 사용자의 정보를 토대로 RAG에서 쿼리를 만들어 검색을 한 것이지만, 단순히 단어 검색을 통한 서비스 서치로만 끝내면 이렇게 충돌되는 서비스가 제공될 수 있다는 것이었다. 레그 검색에서 관련도가 있는 서비스를 검색해주고 있지만, 그 관련있는 서비스가 사용자의 조건에 맞는지는 필터링하지 못 하고있는 것이었다.

 

따라서 검색 결과를 그대로 랭그래프 쪽에 넘기는 건 사용자에게 불쾌한 경험이 될 것이라고 판단했고, 랭그래프 측은 합리적인 서비스라고 가정하고 로직을 실행하기에, 내 레그 쪽에서 로직 기반 필터링을 통해 사용자의 정보와 상반되는 서비스는 걸러내는 로직을 추가했다.

 

API 응답 필드 변경

기존 API 응답은 score 필드에만 의존하여 서비스를 판별했었는데 이를 개선하기 위해 적합성 관련 필드를 추가했다.

 

전체적인 로직의 시작은 동일하게 가져가나, 바로 랭그래프 측에 검색 결과를 반환하지 않고 사용자의 정보 기준으로 한번 더 검사한다. 이 과정에서 걸러지는 서비스가 있을 수 있기에 더 많은 서비스를 먼저 검색해서 가져오도록 했다.

 

검색 API 응답 필드

필드 설명
eligibility_status  사용자 조건 기준으로 충족 가능성
(likely, needs_more_info, unlikely)
eligibility_reasons 판단 이유
missing_fields 판단에 필요한데 사용자에게서 아직 받지 못한 정보
evidence 판단에 사용한 서비스 원문 근거

 

이 필드들을 바탕으로 사용자 조건과 명백히 충돌하는 서비스는 `unlikely`로 판단하고, 기본 추천 결과에서는 제외했다. 반대로 애매한 경우에는 제외하지 않고 `needs_more_info`로 내려주도록 했다.

 

상세 API 응답 필드

 

기존에 application_fields가 있었는데 실제로 해보니 의미가 모호한 거 같아서 이 필드를 제거하고 더 명확한 필드들도 분리했다.

 

변경 필드 개선
제거 application_fields 의미가 모호해 제거
추가 application_url 복지로 상세 페이지 URLa
추가 application_method 복지로의 신청 방법 원문
추가 application_forms 신청 서식 or 안내문 다운로드 파일들
개선 required_documents 원문에서 구비서류 라벨이 명확한 경우만 추출


여기서 application_forms와 required_documents의 구분이 살짝 헷갈릴 수 있는데 required_documents는 신청 시 필요한 서류 이름들이고, application_forms는 복지로에 있는 신청서식, 안내문, 같은 파일 다운로드 정보다.

 

필터링 로직

검색 후 사용자의 정보롤 바탕으로 충돌하는 서비스가 있는지 필터링을 진행 후 반환하도록 한다. 이를 토대로 명확히 충돌, 애매함, 충돌하지 않음 이렇게 3가지로 분류 후 충돌하는 건 제외하고, 애매한 건 같이 반환은 하되, 애매한 사유를 같이 제공하도록 했다.

 

개선 결과

개선 결과 해당 사용자 테스트에서

 

- 65년생, 만 61세
- 대구 거주
- 1인 가구
- 사별
- 자녀 없음
- 장애 등록 없음
- 비경제활동
- 저소득

 

1. 가사·간병 방문 지원사업
2. 독거노인·장애인 응급안전안심서비스
3. 근로·자녀장려금
4. 생계급여
5. 자활근로

 

기존 아웃풋에서 아래처럼 반환되도록 바뀌었다.

 

순위 서비스 적합성 필드
1 근로·자녀장려금 needs_more_info
2 생계급여(맞춤형 급여)  needs_more_info
3 입원 및 격리치료명령 결핵환자 부양가족생활보호비 지원  needs_more_info
4 차상위본인부담경감대상자지원 likely
5 청년월세 지원사업 needs_more_info

 

명백히 매치되지 않는 서비스(노인 서비스)를 제거하고 나머지 서비스들은 needs_more_info를 통해서 추가적인 정보를 필요로 한다.

 

여기서 likely와 needs_more_info 필드의 응답 차이점은

 

likely는 아래처럼 반환되지만

{
    "eligibility_status": "likely",
    "eligibility_reasons": [],
    "evidence": []
}

 

needs_more_info는 부족한 정보들과 함께 반환된다.

{
    "eligibility_status": "needs_more_info",
    "eligibility_reasons": [
      "자녀 또는 부양자녀 중심 기준이 있어 자녀 없음 조건 확인이 필요합니다.",
      "근로소득, 사업소득, 자활 또는 구직활동 기준 확인이 필요합니다."
    ],
    "evidence": [
      {
        "field": "slct_crit_cn",
        "text": "근로소득 또는 사업소득 또는 종교인소득이 있는 거주자로서..."
      }
    ]
}

 

이렇게 명백히 매치되지 않는 서비스는 반환하지 않도록 개선했다.

 

정리 및 다음 방향

 

+ 추가로 지금 청킹 전략도 단순한 오버랩 청킹이라서 이 부분도 개선할 예정이다. 복지서비스의 필드 구조를 살리는 section-aware 청킹으로 바꿔볼 예정이다.


이번에는 로직 필터링을 통해 터무니 없는 서비스를 제외시켜보았습니다. 다음에는 검색 정확도 향상을 위해 청킹 전략을 개선해보겠습니다.