# task-2936 보고서 — evaluate-consultation Gemini→아누 전환 (누락 PII 경로 보완)

## Situation
InsuRo 개인정보 P0-2에서 고객 PII AI경로를 무학습(아누 시스템=Claude 상업 API)으로 전환(task-2933: analyze-customer/transcribe-call). 그러나 Codex 교차검증에서 `evaluate-consultation`(상담 품질 평가) Edge Function이 **여전히 고객 상담 transcript 전문을 무료 Gemini(`gemini-2.0-flash-lite`)로 전송**하는 누락 PII 경로가 발견됨.

## Complication
Gemini 무료 등급은 데이터를 학습·사람 리뷰에 사용 → 고객 상담 전문(민감 PII)이 구글 학습/리뷰 대상이 되는 심각 유출. `evaluate-consultation/index.ts:72` 의 `callAI("gemini-2.0-flash-lite", ...)`가 하드코딩되어 아누 무학습 경로를 우회.

## Question
상담 transcript 평가 경로를 무학습 아누 경로로 전환하되, (1) 응답 파싱 정합 유지, (2) 실패 시 Gemini 폴백 절대 금지(폴백 시 PII 재유출)를 어떻게 보장하는가? — task-2933과 동일 패턴.

## Answer (수정 내역)

### worktree
- 경로: `/home/jay/projects/InsuRo/.worktrees/task-2936-dev1`
- branch: `task/task-2936-dev1`, base: `origin/main` = `6a38aab`
- 최종 diff: 3파일 +93/-3 (deno.lock 생성물은 커밋 제외, untracked 삭제)

### 수정 1: `supabase/functions/evaluate-consultation/index.ts` (line 71~77)
```diff
-    // AI 호출 (기본 provider 사용)
-    const result = await callAI(
-      "gemini-2.0-flash-lite",
-      systemPrompt,
-      [{ role: "user", content: `다음 상담 내용을 평가해주세요:\n\n${transcript}` }],
-      { stream: false, maxTokens: 2048 }
-    );
+    // AI 호출 (아누 시스템 — 무학습 경로, 고객 PII 보호). Gemini 폴백 없음: 실패 시 에러.
+    const result = await callAI(
+      "anu-system",
+      systemPrompt,
+      [{ role: "user", content: `다음 상담 내용을 평가해주세요:\n\n${transcript}` }],
+      { stream: false, maxTokens: 2048, modelTier: "sonnet", featureKey: "evaluate_consultation", userId: user.id }
+    );
```
- `user`는 line 22 `getUser()` 결과 + line 23 `if (authError || !user) throw`로 non-null narrowing → `user.id` 스코프 내 안전(deno check 확인).
- `!result.ok` 처리(429/402 매핑 후 `throw new Error("AI evaluation failed")`)는 **변경 없음** — 원래부터 Gemini 재호출 경로 없음, 그대로 유지.

### 수정 2: `supabase/functions/_shared/ai-provider.ts` (AICallOptions 인터페이스, line 35~)
```diff
 export interface AICallOptions {
   model?: string;
   maxTokens?: number;
   temperature?: number;
   stream?: boolean;
+  // anu-system 라우팅용 (callAI anu 분기에서 model_tier/feature_key/user_id로 매핑)
+  modelTier?: string;
+  featureKey?: string;
+  userId?: string;
 }
```
- 이유: origin/main의 `callAI` anu 분기는 `(options as Record<string,unknown>).modelTier` 캐스팅으로 값을 읽으나, 호출부 객체 리터럴의 excess-property 검사를 통과시키려면 인터페이스에 optional 필드가 필요. task-2933과 동일 필드(add/add identical).
- **범위 최소화**: PROVIDER_CONFIG / callAnuSystem / anu 분기 캐스팅(line ~333-335) 등은 일절 미변경. 인터페이스 3필드 추가만.

### 응답 파싱 정합
- `callAnuSystem` 성공 반환 = `{ ok: true, content }` → Gemini 비스트림 반환과 **동일 shape**. 호출부의 ` ```json ``` 블록 추출 + `JSON.parse` 로직 **수정 불필요**(그대로 호환).

### 폴백 제거 확인
- `result.ok===false` 시 429/402 상태 매핑 후 `throw` 만. `callGemini`/gemini provider 재호출 흔적 **0**. 가드 테스트(#3)로 회귀 차단.

### 추가 산출물 (회귀 방지 가드 테스트)
- `supabase/functions/tests/evaluate-consultation-pii.test.ts` (신규, 86줄, deno test 4종):
  1. evaluate-consultation `callAI` 첫 인자 = `"anu-system"` (정규식)
  2. `gemini-2.0-flash-lite` 및 `gemini-` 계열 provider 문자열 미포함
  3. Gemini 폴백 없음: `callGemini` 미포함 + `callAI(` 호출 정확히 1회 + 에러 블록이 `"AI evaluation failed"` 귀결
  4. anu 라우팅 옵션 `modelTier`/`featureKey`/`userId` 호출부 전달

## 검증 결과

### 코드 검증 (worktree 실제 실행)
- `deno check` (3파일): **EXIT 0, 타입 에러 0**
- provider 문자열 `gemini-2.0-flash-lite` 잔존(index.ts): **0건**
- `deno test --allow-read evaluate-consultation-pii.test.ts`: **4 passed / 0 failed**
- 기존 deno 테스트 스위트: 이 브랜치(origin/main 기반)에 tests/ 부재 → 회귀 대상 0, 신규 가드만 추가
- IDE TS LSP 진단(`Cannot find name 'Deno'` / 원격 import 미해석): 표준 TS LSP가 Deno 런타임 전역·원격 모듈을 인식 못 하는 **기존 환경 특성**(변경과 무관). 실제 `deno check` EXIT 0 → 무시.

### L1 스모크테스트 결과
- **서버 재시작**: 해당없음 (Supabase Edge Function은 ANU가 배포 — finalize-only)
- **API 응답 확인**: `POST https://api.insuro.biz/api/insuro/ai/anu-generate` → **HTTP 401** (아누 시스템 업스트림 = 인증 게이트 존재 = 전환 대상 엔드포인트 라이브)
- **deno check / deno test**: 위 코드 검증 참조 (실제 실행 통과)
- **스크린샷**: 해당없음 (백엔드/API 작업)
- **판정**: L1 통과 (deno check/test 실제 실행 통과 + 아누 업스트림 엔드포인트 도달성·인증 게이트 확인)

## ★ 배포 필요 사항 (ANU 조율 — 봇 직접 설정 불가, task-2933과 함께 finalize-only)
Supabase Edge Function secret(task-2933과 동일):
- `ANU_SYSTEM_URL=https://api.insuro.biz`
- `ANU_API_KEY=<아누 서버 anu-generate 인증키>`

배포 명령(ANU):
```
supabase functions deploy evaluate-consultation
```
※ secret 미설정 시 `callAI`가 `ANU_API_KEY is not configured` 에러 반환(안전 fail — PII가 Gemini로 가지 않음).
※ task-2933(PR #205)와 **함께 배포**. ai-provider.ts의 AICallOptions 3필드 추가는 두 브랜치 동일(add/add identical) — 머지 시 자동 해소 또는 사소 충돌(ANU 조율).

## 발견 이슈 및 해결
- **deno.lock 생성물**: `deno check/test`가 worktree 루트에 `deno.lock`(119줄)을 생성 → main에 없던 산출물이라 커밋 제외(untracked 삭제). scope 밖 `.gitignore` 변경도 롤백하여 diff를 대상 3파일로 한정.
- **task-2933 미머지 상태**: origin/main에 AICallOptions 3필드가 아직 없음(PR #205 OPEN) → 본 브랜치에서 인터페이스 3필드를 자체 추가하여 deno check 독립 통과 확보. 두 PR 동일 필드라 머지 충돌 위험 최소.

## 모델 사용 기록
- 불칸(백엔드): sonnet — provider 교체 + AICallOptions 필드 추가(일반 로직 구현)
- 아르고스(테스터): sonnet — 회귀 방지 가드 테스트(테스트 코드)
- 팀장(헤르메스, Opus): 설계/검토/통합/커밋/L1 검증만 (직접 코딩 없음)

## 머지 판단
- **머지 필요**: Yes
- **브랜치**: task/task-2936-dev1
- **워크트리 경로**: /home/jay/projects/InsuRo/.worktrees/task-2936-dev1
- **머지 의견**: 변경 최소(3파일 +93/-3, 코어 로직 +7/-3 + 테스트 신규), deno check/test 통과, PII 무학습 전환 목적 정확 달성, Gemini 폴백 없음 확인. Lv.2 tiered → PR 생성 후 ANU/Codex 리뷰. 배포는 secret 포함 ANU finalize-only(task-2933과 함께).

## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회

