프로젝트 목록

Enterprise Policy RAG

사내 규정 문서의 권한 기반 검색·답변 흐름을 구현한 RAG 개인 프로젝트입니다. 검색 후보에 권한 조건을 적용하고, 허용된 근거가 없으면 답변 생성을 중단합니다.

개인 프로젝트 · FastAPI 백엔드·React 관리 화면 구현 · 2026.05–2026.06 · AI

역할
개인 프로젝트 · FastAPI 백엔드·React 관리 화면 구현
기간
2026.05–2026.06
분야
AI
코드·테스트
주요 코드 3개 · 관련 테스트 3개

해결한 문제와 담당 역할

사내 문서 RAG는 검색이 잘 되는 것만으로는 부족합니다. 인사팀 문서가 다른 부서 사람 질문에 섞여 나오면 안 되고, 근거가 없는데 그럴듯하게 답해도 안 됩니다.

문서 등록과 분할, 권한별 검색, 출처를 붙인 답변과 거절 응답 API를 만들었습니다.

저장소와 모델을 인터페이스로 분리해 메모리·테스트 모델로 회귀 테스트를 구성하고, PostgreSQL·pgvector와 OpenAI 어댑터를 별도 설정으로 연결했습니다.

검색 결과, 문서 관리, 질문 이력을 볼 수 있는 관리 화면과 정적 데모를 만들었습니다.

핵심 요구사항

  • 다른 워크스페이스나 권한 밖 문서는 검색 결과와 출처에 나오지 않습니다.
  • 근거가 하나도 없으면 답변을 만들지 않습니다.
  • 기본 실행은 메모리 저장소와 가짜 모델을 쓰고, OpenAI 연동은 켜야 동작합니다.
  1. 문서 등록·분할
  2. 권한으로 먼저 거르기
  3. 관련 문단 검색
  4. 출처를 붙여 답변
  5. 질의 로그 기록
권한별 검색 결과, 답변 출처, 질문 이력을 보여 주는 관리 화면. 주요 흐름은 문서 등록·분할 → 권한으로 먼저 거르기 → 관련 문단 검색 → 출처를 붙여 답변 → 질의 로그 기록 순서로 이어집니다.

기술 선택과 이유

권한 확인은 검색 전에

적용 방식
볼 수 있는 문서만 검색 후보에 올립니다.
검토한 대안
전부 검색한 뒤 답변 단계에서 거르기
선택 이유
나중에 거르면 이미 모델 입력에 들어간 뒤입니다. 모델이 한 번이라도 본 내용은 답변에 새어 나올 수 있습니다.

근거 없으면 답하지 않기

적용 방식
인용할 문서가 없으면 답변과 출처가 빈 거절 응답을 돌려줍니다.
검토한 대안
모델이 일반 상식으로 답하게 두기
선택 이유
사내 규정 질문에 출처 없는 답이 나오면 맞는지 확인할 방법이 없습니다.

외부 의존성 분리

적용 방식
모델과 저장소를 인터페이스로 분리했습니다.
검토한 대안
OpenAI API와 pgvector를 서비스 코드에서 바로 호출
선택 이유
테스트는 가짜 모델로 빠르게 돌리고, 실제 모델 연동은 따로 확인하고 싶었습니다.

주요 구현과 관련 테스트

핵심 로직과 동작을 확인한 테스트를 함께 정리했습니다.

주요 구현

RetrievalService.retrieve

app/retrieval.py

access_reason = _access_reason(chunk, query)
if access_reason is None:
    continue

구현 내용

저장소에서 한 번 거른 뒤에도 워크스페이스, 작성자, 부서 권한을 다시 확인합니다.

관련 테스트

test_retrieval_allows_public_owner_and_department_documents_onlytests/test_retrieval_permissions.py

테스트 결과볼 수 있는 3건만 나옵니다.

추가 구현

AnswerService.answer

app/answer.py

if not citations:
    return AnswerResponse(
        query=query.query,
        answer=None,
        citations=[],
        refusal_reason="insufficient_evidence",
    )

구현 내용

인용할 문서가 없으면 답변과 출처를 비우고 insufficient_evidence를 돌려줍니다.

관련 테스트

test_answer_api_refuses_when_no_evidence_is_availabletests/test_answer_api.py

테스트 결과insufficient_evidence로 거절합니다.

추가 구현

PostgresPolicyRepository.search_candidate_chunks

app/repository.py

WHERE c.workspace_id = %s
  AND d.indexing_status = 'ready'
  AND (
    d.owner_user_id = %s
    OR d.visibility = 'public'
    OR (d.visibility = 'department' AND d.department_ids && %s::text[])
  )
ORDER BY c.embedding <=> %s::vector

구현 내용

벡터 유사도로 정렬하기 전에 SQL WHERE 절에서 권한 조건부터 적용합니다.

관련 테스트

test_postgres_repository_retrieval_filters_mixed_access_control_rowstests/test_postgres_repository_integration.py

테스트 결과SQL 후보 조회와 검색 결과에서 허용된 문서만 반환하도록 검증합니다.

테스트 결과

테스트 방법 보기
Enterprise Policy RAG 테스트 결과
구분테스트 방법결과
단위 테스트권한이 서로 다른 문서 5건으로 검색을 실행합니다.볼 수 있는 3건만 나옵니다.
통합 테스트관련 문서가 없는 질문을 답변 API에 보냅니다.insufficient_evidence로 거절합니다.
통합 테스트선택 실행 PostgreSQL 테스트에서 권한이 다른 문서와 색인 대기 문서를 등록합니다.SQL 후보 조회와 검색 결과에서 허용된 문서만 반환하도록 검증합니다.
정적 데모관리 화면을 정적 빌드로 띄워 봅니다.고정 데이터로 검색 결과·출처·질문 이력을 보여 줍니다. 실제 백엔드 검색 결과는 아닙니다.

프로젝트 범위

검증 한계와 미구현 범위

  • 기본 실행은 메모리 저장소와 가짜 모델입니다. 실제 사내 인증 연동이나 운영 환경 pgvector는 해 보지 않았습니다.
  • 화면의 토큰 수와 비용은 글자 수로 어림잡은 값이라 실제 청구액과 다릅니다.
  • 고정 질문 평가는 권한·검색·출처 회귀 확인용입니다. 실제 모델의 답변 품질이나 RAG 검색 품질을 일반화할 수 없습니다.
  • 출처는 권한 확인을 거친 검색 결과에서 구성합니다. 생성 답변의 모든 문장이 출처에 근거하는지 자동 검증하는 기능은 없습니다.

향후 개선

  • 사내 인증과 감사 로그를 붙이고, 실제 모델로 평가를 따로 쌓아 보려고 합니다.

관련 자료

ADR 테스트 소스