# InsuWiki 유튜브 3-Layer 요약 파이프라인 계획서

> **작성일**: 2026-03-03
> **작성자**: 불칸 (백엔드 엔지니어)
> **상태**: 초안 (Draft)
> **관련 문서**:
> - `docs/meetings/260225-17.07-youtube-pipeline-agent-meeting.md`
> - `docs/decisions/260225-rag-architecture-option-f.md`
> - `scripts/prompts/summary-prompts.ts`
> - `functions/src/crawlYoutubeChannels.ts`

---

## 1. 개요

### 1.1 목적

현재 `crawlYoutubeChannels` Cloud Function은 영상 1개당 Gemini 6섹션 요약을 생성하고 `youtube_knowledge` 컬렉션에 단일 청크로 저장한다. 이 구조는 **영상 단위의 검색과 참조는 가능하지만, 여러 영상에 걸친 주제별 통합 인사이트나 채널 수준의 트렌드 파악은 불가능**하다.

유튜브 콘텐츠에 약관 PDF 3-Layer 요약 체계와 동일한 계층적 구조를 적용함으로써 다음을 달성한다.

- **설계사 활용성 향상**: 단일 영상 요약을 넘어 주제별·채널별로 정제된 인사이트를 RAG 쿼리에 공급
- **정보 신뢰도 유지**: 4순위 참고자료라는 위상을 구조적으로 표현하고, 레벨 계층별로 환각 방지 가드레일을 강화
- **쿼리 성능 최적화**: 쿼리 목적에 따라 상세 자막(Level 3) / 영상 요약(Level 2) / 채널 통합(Level 1) 중 적합한 레벨을 선택적으로 검색
- **약관 3-Layer 시스템과의 일관성**: 동일한 설계 패턴으로 향후 RAG 쿼리 라우터가 두 도메인을 통합 처리할 수 있는 기반 마련

### 1.2 기대 효과

| 관점 | 현재 | 적용 후 |
|------|------|---------|
| 검색 단위 | 영상 1개 = 요약 1개 | 자막 청크 / 영상 요약 / 채널 통합 3계층 |
| 채널 비교 | 불가 | Level 1으로 채널별 전문성·포지션 파악 가능 |
| 설계사 활용 포인트 | 영상별 분산 | Level 2에 주제별로 구조화, 즉시 참조 가능 |
| RAG 정확도 | 긴 요약 청크로 노이즈 | 목적에 맞는 레벨의 청크 선택적 조회 |
| 운영 비용 | Level 2 생성비만 존재 | Level 1 추가 생성비 소량 증가 (주 1~2회 배치) |

---

## 2. 아키텍처 설계

### 2.1 유튜브 3-Layer 정의

```
[Level 1] 채널/기간별 핵심 인사이트 통합 요약
           ↑ (Level 2 요약들을 종합)
[Level 2] 영상 주제별 구조화 요약 (현재 6섹션 재구조화)
           ↑ (Level 3 자막 청크를 기반으로 생성)
[Level 3] 유튜브 자막 원문 청크 (현재 youtube_knowledge.chunkText)
```

#### Level 3: 자막 원문 청크

- **정의**: 자막 전체 텍스트를 일정 단위(약 1,000~2,000자)로 분할한 원문 조각
- **현황**: `crawlYoutubeChannels.ts`는 현재 요약 텍스트를 `insurance_chunks`에 `sourceType: 'youtube'`로 저장하며, 자막 원문 자체는 별도 저장하지 않음
- **개선 방향**: 자막 원문(raw transcript)을 청크 단위로 분할하여 저장 → 향후 원문 기반 재요약·재검증 가능
- **기본 단위**: 500~1,500 토큰 (시간 기반 분할 또는 문장 경계 기반)
- **메타데이터**: `videoId`, `channelId`, `chunkIndex`, `startTimeMs`, `endTimeMs`, `transcriptText`

#### Level 2: 영상 주제별 구조화 요약

- **정의**: 영상 1개를 분석하여 설계사 활용 관점의 구조화된 요약 생성
- **현황**: 현재 `YOUTUBE_SUMMARY_PROMPT`가 생성하는 6섹션 요약이 이에 해당
- **개선 방향**: 6섹션 구조를 그대로 유지하되, `youtube_summaries` 컬렉션에 `level: 2`로 저장하고 섹션별 메타데이터 보강
- **분량**: 영상 길이에 비례 (15분 이하 500자, 30~60분 1,000자, 1시간 이상 2,000자 기준)

#### Level 1: 채널/기간별 핵심 인사이트 통합 요약

- **정의**: 동일 채널의 최근 N개 영상(또는 특정 기간) Level 2 요약을 종합하여 채널의 전문성·관점·주요 인사이트를 압축
- **현황**: 존재하지 않음 (신규)
- **생성 주기**: 채널당 주 1회 또는 신규 영상 5개 누적 시
- **분량**: 400~700자 (채널 소개 + 최근 핵심 메시지 + 설계사 활용 포인트)

### 2.2 약관 3-Layer vs 유튜브 3-Layer 비교

| 항목 | 약관 3-Layer | 유튜브 3-Layer |
|------|------------|--------------|
| Level 3 원천 | PDF 텍스트 청크 (pageNumber 기반) | 자막 원문 청크 (시간/문장 기반) |
| Level 3 분할 기준 | 페이지 단위 + 섹션 경계 감지 | 시간 단위(30초~1분) 또는 문장 경계 |
| Level 2 단위 | 관/장/절 (법적 구조) | 영상 1개 (주제별 6섹션) |
| Level 2 구조 | 핵심보장/면책/지급조건/특약주의사항 | 핵심요약/설계사활용/언급상품/수치정보/약관불일치/흐름요약 |
| Level 1 단위 | 상품 전체 | 채널 전체 또는 기간별 |
| Level 1 집약 기준 | 전체 약관 핵심 (법적 효력 중심) | 채널 전문성·인사이트 (참고 자료 성격 명시) |
| 환각 방지 방향 | "약관 원문에 없는 내용 추가 금지" | "자막에 없는 내용 추가 금지 + 약관 우선 명시" |
| 저장 컬렉션 | `insurance_chunks` (L3), `insurance_summaries` (L1/L2) | `youtube_transcripts` (L3 신규), `youtube_summaries` (L1/L2 신규) |
| 생성 방식 | 수동 CLI (`summary-pipeline.ts`) | 자동 Cloud Function (크롤링 시 연계) |
| 정보 권위 | 1순위 (법적 효력) | 4순위 (참고 자료, 단독 인용 금지) |

---

## 3. 파이프라인 흐름도

### 3.1 전체 흐름 (Level 3 ~ Level 1 포함)

```
[Cloud Scheduler: 6시간마다]
        |
        v
[crawlYoutubeChannels Cloud Function]
        |
        v
[1. youtube_channels → 활성 채널 목록 로드]
        |
        v
[2. YouTube Data API → 신규 영상 필터 (lastCrawledAt 이후)]
        |
        v
[영상별 루프 시작 ─────────────────────────────────────────]
        |
        v
[3. 중복 확인: youtube_knowledge.videoId 존재 시 스킵]
        |
        v
[4. 자막 추출: fetchYouTubeTranscript()]
    ├── 성공: 자막 원문 (12,000~18,000 토큰)
    └── 실패: 제목+설명 대체 텍스트
        |
        v
[5. Level 3 저장: 자막 원문 청크 분할 → youtube_transcripts]
    ├── chunkIndex 순서대로 분할 (1,000자 단위)
    └── startTimeMs / endTimeMs 메타데이터 포함
        |
        v
[6. Level 2 생성: Gemini 2.5 Flash → 6섹션 구조화 요약]
    ├── YOUTUBE_L2_PROMPT 적용 (설계사 활용 관점)
    └── youtube_summaries에 level: 2로 저장
        |
        v
[7. Google Drive 업로드: 04_유튜브요약/{채널명}/{날짜}_{제목}.md]
        |
        v
[8. Gemini 임베딩 → insurance_chunks 저장 (sourceType: 'youtube')]
        |
        v
[9. 약관 청크 유사도 비교 → conflictsWithPolicy 감지]
        |
        v
[10. youtube_knowledge 저장 (기존 구조 유지)]
[영상별 루프 종료 ─────────────────────────────────────────]
        |
        v
[11. youtube_channels.lastCrawledAt 갱신]
        |
        v
[Level 1 생성 트리거 판단]
    ├── 신규 영상 누적 >= 5개?  →  YES
    └── 마지막 Level 1 생성 >= 7일 경과? →  YES
            |
            v
    [12. Level 1 생성: 채널별 최근 Level 2 요약 N개 집약]
        ├── YOUTUBE_L1_PROMPT 적용 (채널 통합 인사이트)
        └── youtube_summaries에 level: 1로 저장
```

### 3.2 Phase 0 PoC 흐름 (단일 영상 수동 테스트)

```
[단일 videoId 지정]
        |
        v
[자막 추출 → 파일 저장 (/tmp/transcript.txt)]
        |
        v
[Level 3 청크 분할 확인 (콘솔 출력)]
        |
        v
[Level 2 프롬프트 수동 실행 → 출력 검토]
        |
        v
[Level 1 프롬프트 수동 실행 (Level 2 출력 입력으로) → 출력 검토]
        |
        v
[품질 기준 체크리스트 수동 확인]
```

---

## 4. Firestore 컬렉션 설계

### 4.1 방안 비교

#### 방안 A: 기존 컬렉션 확장

`youtube_knowledge`에 `summaryLevel` 필드를 추가하고, `insurance_summaries`에 `sourceType: 'youtube'`를 추가하여 약관 요약과 동일 컬렉션에서 관리.

| 장점 | 단점 |
|------|------|
| 신규 컬렉션 생성 불필요 | `insurance_summaries`의 의미 오염 (보험 약관 전용 설계) |
| RAG 쿼리 시 단일 컬렉션 조회 | Level 3 자막 원문 저장 구조가 없음 (현재 insurance_chunks는 요약 저장) |
| 인덱스 재사용 가능 | 약관과 유튜브 Firestore Rules 분리 불가 |

#### 방안 B: 신규 컬렉션 생성 (추천)

유튜브 전용 컬렉션을 신규 생성하여 약관 시스템과 명확히 분리.

| 장점 | 단점 |
|------|------|
| 도메인 분리 명확 | 신규 인덱스 설정 필요 |
| 유튜브 특화 필드 자유롭게 설계 가능 | 컬렉션 수 증가 |
| Firestore Rules를 도메인별로 독립 설정 | RAG 쿼리 시 복수 컬렉션 조회 |
| 정보 권위 계층(4순위)을 컬렉션 이름으로 명시 | - |

### 4.2 추천 방안: 방안 B (신규 컬렉션)

**근거**: `insurance_summaries`는 법적 효력이 있는 약관 원문 기반이며, 유튜브 콘텐츠(4순위 참고 자료)와 동일 컬렉션에 혼재하면 RAG 쿼리 시 정보 권위 계층 구분이 코드 레벨에서 어려워진다. 별도 컬렉션으로 분리하면 `sourceType` 필터 없이도 신뢰도 계층이 컬렉션 이름으로 표현된다.

### 4.3 신규 컬렉션 스키마

#### youtube_transcripts (Level 3 — 자막 원문 청크)

```typescript
// /youtube_transcripts/{chunkId}
{
  videoId: string,           // YouTube 영상 ID
  channelId: string,         // YouTube 채널 ID
  channelName: string,       // 채널명 (비정규화)
  chunkIndex: number,        // 청크 순서 (0-based)
  transcriptText: string,    // 자막 원문 청크 (1,000자 내외)
  startTimeMs?: number,      // 자막 시작 시간 (ms), 없을 경우 null
  endTimeMs?: number,        // 자막 종료 시간 (ms), 없을 경우 null
  videoPublishedAt: Timestamp,
  createdAt: Timestamp,
}
```

**인덱스**: `videoId` + `chunkIndex` (복합, 오름차순)

#### youtube_summaries (Level 1, Level 2 통합)

```typescript
// /youtube_summaries/{summaryId}
{
  level: 1 | 2,              // 요약 레벨

  // Level 2 전용 필드
  videoId?: string,          // Level 2: 영상 ID
  videoTitle?: string,       // Level 2: 영상 제목 (비정규화)
  videoPublishedAt?: Timestamp, // Level 2: 영상 업로드일
  hasTranscript?: boolean,   // Level 2: 자막 기반 여부
  driveUrl?: string,         // Level 2: Google Drive 업로드 URL

  // Level 1 전용 필드
  periodStart?: Timestamp,   // Level 1: 집약 기간 시작
  periodEnd?: Timestamp,     // Level 1: 집약 기간 종료
  sourceVideoIds?: string[], // Level 1: 집약에 사용된 videoId 목록
  videoCount?: number,       // Level 1: 집약된 영상 수

  // 공통 필드
  channelId: string,
  channelName: string,       // 비정규화
  content: string,           // 생성된 요약 텍스트
  embedding?: number[],      // Level 2: 임베딩 벡터 (RAG 검색용)
  conflictsWithPolicy?: boolean, // Level 2: 약관 상충 여부
  createdAt: Timestamp,
  updatedAt: Timestamp,
}
```

**인덱스**:
- `channelId` + `level` + `createdAt` (복합, 내림차순)
- `videoId` + `level` (Level 2 조회용, 복합)

### 4.4 기존 컬렉션과의 관계

현재 `crawlYoutubeChannels.ts`가 생성하는 다음 두 컬렉션은 **그대로 유지**한다.

- `insurance_chunks` (`sourceType: 'youtube'`): RAG 임베딩 검색용 (변경 없음)
- `youtube_knowledge`: 영상별 메타데이터 + 임베딩 (변경 없음)

신규 컬렉션(`youtube_transcripts`, `youtube_summaries`)은 기존 컬렉션을 대체하지 않고 **추가**한다.

---

## 5. 프롬프트 설계 방향

### 5.1 공통 환각 방지 가드레일

기존 `HALLUCINATION_GUARD`를 유튜브 도메인에 맞게 확장한다.

```typescript
/** 유튜브 요약 프롬프트 공통 환각 방지 가드레일 */
const YOUTUBE_HALLUCINATION_GUARD = `
【절대 준수 사항 — 환각(Hallucination) 방지】
- 자막 원문에 명시된 내용만 요약합니다. 자막에 없는 내용은 절대 추가하지 않습니다.
- 수치(보험료, 보험금, 비율, 기간)는 자막 원문 그대로만 인용합니다. 추정하거나 계산하지 않습니다.
- "아마도", "일반적으로", "보통은", "대개", "추정", "~인 것 같" 표현을 절대 사용하지 않습니다.
- 자막에서 확인할 수 없는 내용은 해당 항목을 생략하거나 "(자막에 해당 내용 없음)"으로 명시합니다.
- 다른 영상·채널 내용과 혼동하거나 병합하지 않습니다.
- 이 내용은 약관 원문(1순위)보다 낮은 4순위 참고 자료입니다. 요약 마지막에 반드시 포함:
  "※ 이 요약은 자동 생성이며 약관 원문을 우선합니다. 단독 인용 금지."
`.trim();
```

### 5.2 Level 2 프롬프트: 영상 주제별 구조화 요약

현재 `YOUTUBE_SUMMARY_PROMPT`의 6섹션 구조를 **설계사 활용 관점**으로 재구조화한다. 현재 구조에서 달라지는 핵심은 (1) 각 섹션에 자막 근거 인용을 명시적으로 요구하고, (2) 설계사 활용 포인트를 상담 시나리오 형태로 구체화하는 것이다.

```
【시스템 역할】
당신은 보험 콘텐츠 전문 분석가이자 설계사 코치입니다.
보험 유튜브 영상 자막을 분석하여 설계사가 고객 상담에 즉시 활용할 수 있는
구조화된 요약을 제공합니다.

【출력 형식 — 6섹션】

## 1. 핵심 요약
- 영상의 주제와 결론을 3~7개 불릿으로 압축
- 각 불릿은 "(자막 인용)" 형태로 근거 명시

## 2. 설계사 상담 활용 포인트
- 고객 상담 시나리오별로 구체적 활용법 제시
- 형식: "고객이 [상황]일 때 → [대응 포인트]"

## 3. 언급된 보험 상품 / 보험사 / 특약
- 영상에서 언급된 상품명, 보험사명, 특약명 목록
- 없으면 "없음"

## 4. 수치 / 금액 정보
- 자막 원문 그대로 인용 (예: "월 보험료 15만원" 언급)
- 없으면 "없음"

## 5. 약관과 다를 수 있는 내용 (요주의)
- 약관 규정과 상충 가능성이 있는 발언만 명시
- 억지로 찾지 말 것 — 없으면 "없음"

## 6. 주제별 섹션 흐름
- 영상 진행 순서를 3~10개 섹션으로 분류
- 각 섹션: "[시작 시간대] 제목 — 핵심 내용 1줄"
```

**분량 기준**:
| 영상 길이 | Level 2 목표 분량 |
|-----------|-----------------|
| 15분 미만 | 400~600자 |
| 15~30분 | 600~1,000자 |
| 30~60분 | 1,000~1,500자 |
| 60분 이상 | 1,500~2,500자 |

### 5.3 Level 1 프롬프트: 채널 통합 인사이트 요약

```
【시스템 역할】
당신은 보험 유튜브 채널 분석 전문가입니다.
동일 채널의 최근 영상 요약들을 종합하여 이 채널의 전문성·관점·핵심 메시지를
설계사가 채널 활용 여부를 판단할 수 있도록 요약합니다.

【출력 형식 — 3섹션】

## 채널 핵심 인사이트 요약
- 이 채널이 주로 다루는 보험 주제와 타깃 고객층
- 최근 N개 영상에서 반복 강조된 핵심 메시지

## 설계사 활용 포인트 (채널 수준)
- 어떤 상담 상황에서 이 채널 내용을 참조하면 유용한지
- 주의가 필요한 편향·과장 표현 패턴 (있는 경우)

## 최근 주요 영상 목록
- 날짜 | 영상 제목 | 핵심 한 줄 요약
(최대 10개)

※ 이 요약은 {N}개 영상의 자동 생성 요약을 기반으로 합니다.
   약관 원문(1순위)보다 낮은 4순위 참고 자료이며 단독 인용을 금지합니다.
```

**분량**: 400~700자

### 5.4 프롬프트 파일 위치

기존 `scripts/prompts/summary-prompts.ts`에 유튜브 전용 프롬프트를 **별도 파일**로 분리한다.

```
scripts/prompts/
  summary-prompts.ts          ← 기존 (약관 3-Layer, 변경 없음)
  youtube-summary-prompts.ts  ← 신규 (유튜브 3-Layer)
```

---

## 6. 기존 파이프라인과의 통합 전략

### 6.1 현재 6섹션 요약과의 관계

현재 `crawlYoutubeChannels.ts`의 `YOUTUBE_SUMMARY_PROMPT`가 생성하는 6섹션 요약은 **Level 2 요약의 초안**으로 정의한다. 이 요약 결과를:

1. 기존대로 `youtube_knowledge.chunkText`에 저장 (하위 호환성 유지)
2. 추가로 `youtube_summaries` 컬렉션에 `level: 2`로 저장 (신규)

즉 기존 저장 로직을 삭제하지 않고, **동일한 요약 결과를 두 곳에 저장**하는 방식으로 시작한다.

### 6.2 운영 방식: 확장(Extension) 선택

세 가지 방안을 검토했다.

| 방안 | 설명 | 판정 |
|------|------|------|
| 병렬 운영 | 기존 파이프라인과 별도 파이프라인을 각각 유지 | 비추천: 코드 중복, 운영 복잡도 증가 |
| 대체 | 기존 파이프라인을 신규로 완전 교체 | 비추천: 기존 `youtube_knowledge` 의존 코드 영향 범위 넓음 |
| 확장 | 기존 파이프라인에 Level 3/Level 1 저장 단계를 추가 | **추천**: 최소 변경, 하위 호환 유지 |

**확장 방안 세부 전략**:
- `crawlYoutubeChannels.ts`에 Level 3(자막 청크 저장)와 Level 2(`youtube_summaries` 저장) 단계를 **추가**한다. 기존 `youtube_knowledge` 저장 로직은 삭제하지 않는다.
- Level 1 생성은 별도 Cloud Function `generateYoutubeChannelInsight`로 분리한다. 이 함수는 채널별 Level 2 요약이 누적될 때마다 주기적으로 실행된다 (Cloud Scheduler: 주 1회 또는 신규 영상 5개 누적 시).

```
[기존 파이프라인 흐름] — 변경 없음
크롤링 → 자막추출 → Gemini 6섹션 요약 → Drive 업로드
→ insurance_chunks 저장 (sourceType: 'youtube')
→ youtube_knowledge 저장

[추가되는 단계]
자막추출 완료 후 → youtube_transcripts 청크 저장 (Level 3 신규)
Gemini 6섹션 요약 완료 후 → youtube_summaries 저장 (level: 2 신규)

[별도 배치 함수]
generateYoutubeChannelInsight → youtube_summaries 저장 (level: 1 신규)
```

---

## 7. 위험 요소 및 완화 방안

| 위험 요소 | 발생 가능성 | 영향도 | 완화 방안 |
|-----------|------------|--------|----------|
| **자막 미제공 영상 증가** (유튜브 정책 변경) | 중간 | 높음 | Level 3 미저장 시 Level 2는 제목+설명 기반으로 생성하되 `hasTranscript: false` 명시. Level 1 집약 시 미자막 영상은 가중치 감소. |
| **Gemini 2.5 Flash Rate Limit** | 높음 | 중간 | 기존 지수 백오프(`withBackoff`) 유지. Level 1 생성은 별도 배치 시간 분산. |
| **youtube_transcripts 용량 폭발** | 중간 | 중간 | 자막 청크 TTL 정책 도입 (예: 6개월 이상 된 영상 청크는 삭제). Level 3 임베딩은 생성하지 않아 비용 절감. |
| **Level 1 요약 품질 저하** (영상 간 주제 편차 큼) | 중간 | 낮음 | Level 1 생성 시 최근 5~10개 영상 중 관련 주제 클러스터링 후 집약. 주제 다양도가 높으면 "이 채널은 다양한 주제를 다룹니다" 형태로 표현 유도. |
| **기존 youtube_knowledge 의존 코드 영향** | 낮음 | 높음 | 확장 방안 채택으로 기존 컬렉션 유지. 신규 컬렉션은 Optional Read로만 접근. |
| **환각 → 잘못된 보험 정보 유통** | 낮음 | 매우 높음 | YOUTUBE_HALLUCINATION_GUARD 강제 적용. 모든 Level 요약 말미에 "약관 원문 우선, 단독 인용 금지" 고정 문구 삽입. conflictsWithPolicy 감지 로직 고도화 (현재 Phase 2 예정). |
| **Cloud Function 타임아웃** (Level 3 저장 추가로 처리 시간 증가) | 중간 | 중간 | Level 3 청크 저장은 `Promise.allSettled`로 병렬 처리. 현재 540초 타임아웃 내 수용 가능하나 영상 수 증가 시 재검토. |

---

## 8. 구현 단계 (Phase)

### Phase 0: PoC — 단일 영상 수동 테스트 (1~2일)

**목표**: 유튜브 3-Layer 구조의 실현 가능성과 품질을 단일 영상으로 검증한다.

**작업 목록**:
- [ ] 테스트 영상 1개 선정 (보험명의정닥터 또는 ins-king, 30~60분짜리 권장)
- [ ] 기존 `fetchYouTubeTranscript()` 호출 → 자막 원문 파일로 저장
- [ ] Level 3: 자막을 1,000자 단위로 수동 분할 → 청크 구조 검토
- [ ] Level 2: `YOUTUBE_SUMMARY_PROMPT` 기반으로 Gemini 요약 생성 → 품질 검토
- [ ] Level 1: Level 2 출력 3~5개를 입력으로 Level 1 프롬프트 초안 실행 → 품질 검토
- [ ] 환각 여부, 분량, 설계사 활용도 관점에서 체크리스트 작성

**완료 기준**: Level 2와 Level 1 요약이 설계사 업무에 실제 활용 가능한 수준임을 팀이 동의할 것.

---

### Phase 1: Level 3 저장 구조 확정 (2~3일)

**목표**: `youtube_transcripts` 컬렉션 스키마와 청크 분할 로직을 코드로 구현하고 Firestore에 테스트 데이터를 저장한다.

**작업 목록**:
- [ ] `functions/src/crawlYoutubeChannels.ts`에 `splitTranscriptToChunks()` 유틸 함수 추가
  - 분할 기준: 문장 경계 우선, 최대 1,000자
  - `chunkIndex`, `startTimeMs`, `endTimeMs` 메타데이터 추출
- [ ] `youtube_transcripts` Firestore 컬렉션에 Level 3 청크 저장 로직 추가
- [ ] `firestore.indexes.json`에 `videoId + chunkIndex` 복합 인덱스 추가
- [ ] 테스트 영상 5개로 저장 검증
- [ ] Phase 1 완료 후 `youtube_transcripts` 데이터 구조 확정

**변경 파일**:
- `functions/src/crawlYoutubeChannels.ts`
- `firestore.indexes.json`

---

### Phase 2: Level 2 프롬프트 교체 + youtube_summaries 저장 (3~4일)

**목표**: 기존 6섹션 요약 프롬프트를 개선된 Level 2 프롬프트로 교체하고, `youtube_summaries` 컬렉션에 `level: 2`로 저장하는 로직을 추가한다.

**작업 목록**:
- [ ] `scripts/prompts/youtube-summary-prompts.ts` 신규 파일 생성
  - `YOUTUBE_HALLUCINATION_GUARD` 상수 정의
  - `YOUTUBE_L2_SYSTEM_PROMPT` + `YOUTUBE_L2_USER_PROMPT_TEMPLATE` 정의
  - `buildYoutubeL2Prompt(transcript, videoTitle, channelName)` 빌더 함수
- [ ] `functions/src/crawlYoutubeChannels.ts` 업데이트
  - 기존 `YOUTUBE_SUMMARY_PROMPT` → `YOUTUBE_L2_SYSTEM_PROMPT` 교체
  - Gemini 요약 완료 후 `youtube_summaries` 컬렉션에 `level: 2` 저장 추가
  - 기존 `youtube_knowledge` 저장 로직은 유지 (하위 호환)
- [ ] `firestore.indexes.json`에 `channelId + level + createdAt` 복합 인덱스 추가
- [ ] 영상 길이별 분량 자동 조절 로직 (15분/30분/60분 임계값)
- [ ] 10개 영상으로 A/B 비교 (구 프롬프트 vs 신 프롬프트) → 팀 품질 검토

**변경 파일**:
- `scripts/prompts/youtube-summary-prompts.ts` (신규)
- `functions/src/crawlYoutubeChannels.ts`
- `firestore.indexes.json`

---

### Phase 3: Level 1 통합 요약 배치 함수 + 배포 (4~5일)

**목표**: 채널별 Level 1 통합 요약을 자동으로 생성하는 배치 Cloud Function을 구현하고 전체 3-Layer 파이프라인을 프로덕션에 배포한다.

**작업 목록**:
- [ ] `functions/src/generateYoutubeChannelInsight.ts` 신규 파일 생성
  - Cloud Scheduler 트리거: 주 1회 (월요일 오전 9시, Asia/Seoul)
  - 채널별 최근 Level 2 요약 5~10개 조회
  - `YOUTUBE_L1_SYSTEM_PROMPT` + `buildYoutubeL1Prompt()` 적용
  - `youtube_summaries`에 `level: 1` 저장 (기존 Level 1이 있으면 덮어쓰기)
- [ ] `scripts/prompts/youtube-summary-prompts.ts`에 Level 1 프롬프트 추가
  - `YOUTUBE_L1_SYSTEM_PROMPT` + `YOUTUBE_L1_USER_PROMPT_TEMPLATE`
  - `buildYoutubeL1Prompt(channelName, recentL2Summaries)` 빌더 함수
- [ ] `functions/src/index.ts`에 `generateYoutubeChannelInsight` export 추가
- [ ] `firebase.json` 또는 GCP Console에 Cloud Scheduler 등록
- [ ] Level 1 출력 품질 검토 (채널별 최소 2개 채널로 테스트)
- [ ] 전체 3-Layer 파이프라인 E2E 테스트
  - 신규 영상 크롤링 → L3 저장 → L2 저장 → L1 생성 확인
- [ ] 프로덕션 배포 (`npm run deploy` 또는 `scripts/deploy.sh`)

**변경 파일**:
- `functions/src/generateYoutubeChannelInsight.ts` (신규)
- `scripts/prompts/youtube-summary-prompts.ts` (Level 1 추가)
- `functions/src/index.ts`
- `firebase.json`

---

## 부록: 컬렉션 관계도

```
youtube_channels (기존)
    |
    | 1:N
    v
youtube_knowledge (기존, 영상별 메타+임베딩)   ←→   insurance_chunks (기존, sourceType:'youtube')
    |
    | 1:N (videoId 공유)
    v
youtube_transcripts (신규, Level 3)
    |
    | N:1 (videoId 집약)
    v
youtube_summaries level:2 (신규, 영상별 구조화 요약)
    |
    | N:1 (channelId 집약)
    v
youtube_summaries level:1 (신규, 채널 통합 인사이트)
```

---

*작성: 불칸 (백엔드 엔지니어) / 2026-03-03*
*검토 요청: 아누 (개발실장), 미미르 (UX/프롬프트 설계자)*
