# 설계서: VECTOR_DISTANCE와 역할 기반 VPD 필터 분리 표시 (#572)

> **상태**: Approved
> **작성**: [AI] Architect · **최종수정**: 2026-06-30
> **추적성** — Redmine: #572 · 선행 기능: #558, #570, #571
> · 구현 파일: `probe-result.html`, `OrdsProbeService`, `GuidedFlowTemplateTest`
> · 테스트: `mvn test`

## 1. 목적

벡터 검색의 유사도 계산과 VPD 권한 필터를 서로 다른 개념으로 표시한다.

- **벡터 검색**: `VECTOR_DISTANCE(d.embedding, TO_VECTOR(:embedding), COSINE)`로 score를 계산하고 `ORDER BY score`로 가까운 순서의 Top-K를 반환한다.
- **권한 필터**: `set_vpd_context`가 설정한 사용자/역할을 `CB_AGENT_DOC_VPD_FILTER`가 읽고, 해당 역할의 permission rule에 따라 `TECH_TAG` 등 대상 컬럼 조건을 만든다. 이 조건은 검색어가 아니라 접근 통제다.

최종 effective SQL에는 두 조건이 함께 존재할 수 있지만, 화면에서는 각각을 분리해 오해를 막는다.

## 2. 인수조건

- [ ] 벡터 결과에 `VECTOR_DISTANCE`와 `ORDER BY score`가 “벡터 유사도 검색 기준”으로 별도 표시된다.
- [ ] `REGEXP_LIKE`가 있으면 “역할 기반 VPD 권한 필터”로 표시하고, 벡터 검색 조건이 아님을 설명한다.
- [ ] 권한 필터 설명에 선택 사용자에게 직접/그룹으로 연결된 role의 permission rule에서 계산되었다고 표시한다.
- [ ] `ALL` 권한은 `1 = 1`, 권한 없음/허용 rule 없음은 `1 = 0`, TAG 권한은 해당 role의 TAG 값만 표시한다.
- [ ] 최종 effective SQL은 실제 결합 형태를 유지하되, 화면 앞에서 검색 기준과 권한 필터를 분리해 보여준다.
- [ ] 일반 객체의 기존 VPD trace, 사용자 context, SQL 결과는 회귀하지 않는다.

## 3. 화면 흐름

```text
[set_vpd_context 사용자]
          │
          ▼
[역할 permission rule → VPD 권한 필터]
          │
          ├──────────────┐
          ▼              ▼
[TECH_TAG 조건]   [VECTOR_DISTANCE + ORDER BY score]
          └──────┬───────┘
                 ▼
        [effective SQL / Top-K]
```

## 4. 데이터/보안 계약

- 사용자 입력 평문은 임베딩으로 변환되어 `:embedding` 경계로만 전달된다. 평문·임베딩은 VPD predicate에 섞지 않는다.
- VPD predicate는 DB 함수가 현재 context에서 반환한 값만 표시한다. UI가 임의로 role 조건이나 SQL을 생성하지 않는다.
- `REGEXP_LIKE`는 TAG rule을 저장한 역할이 실제로 effective role에 포함될 때만 나타난다. role/permission이 없으면 fail-closed predicate `1 = 0`이 반환된다.
- `SELECT *`를 사용하지 않고 명시적 컬럼과 score만 반환해 embedding 원문을 노출하지 않는다.

## 5. 테스트/운영 검증

- 템플릿 테스트: 벡터 유사도 기준, 역할 기반 VPD 필터, `VECTOR_DISTANCE`, `ORDER BY score` 문구 확인.
- 전체 테스트: `mvn test`.
- 운영 smoke: `agent_hr`에서 `SPRING_BOOT/ORDS` 권한 필터와 vector score를 함께 확인하고, `agent_all`에서는 전체 TAG rule이 role에 연결된 경우에만 전체 조건이 표시되는지 확인한다.
