DOCS/journey/plans/260315_companyx_rag_답변합성_시나리오동시종결_계획.md
happybell80 c272340fd3 close: CompanyX RAG 계열 닫힌 문서 20건 프론트메타 일괄 반영
- 260312/260315/260316 계열 트러블/리서치/계획/시나리오/워크로그
- 본문에만 닫힘 표기되어 있던 문서에 status: closed 프론트메타 추가
- 열린 문서는 260320 다형식문서 RAG 체인 10개만 남음

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 07:59:29 +09:00

30 KiB

status, closed_date, closed_reason, tags
status closed_date closed_reason tags
closed 2026-03-21 260320 다형식문서 RAG 계획으로 흡수 또는 구현 완료
plans
companyx
rag
answer-composition
scenario
troubleshooting

status: closed closed_date: 2026-03-21 closed_reason: 260320 다형식문서 RAG 계획으로 흡수 또는 구현 완료

Company X RAG 답변합성 시나리오·트러블 동시종결 계획

작성일: 2026-03-15 상태: 닫힘 — 미완료 항목은 260320 계획으로 흡수 검토일: 2026-03-17 갱신일: 2026-03-20 종결 사유: 260320 다형식문서 RAG 계획이 범위를 확장(벡터+키워드+Apache AGE 3중 검색)하여 본 계획의 미완료 항목(5A 배포, 5B 검증)을 흡수. 워크로그 참조. 후속 계획: 260320 로빙 다형식문서 RAG 적용1 계획 목표: Company X 내부문서 근거응답 경로를 대표 질문 특례 처리에서 공통 계약 기반 답변합성 구조로 바꾸되, 구현 전에 현재 NAS 문서 운영 상태 + 현재 임베딩 전제를 다시 닫고 대응 troubleshooting 문서와 scenario 문서를 함께 닫습니다.

2026-03-17 코드 검토 결과 (갱신):

  • Phase 1~3(구조 분리, 질문 유형 계약, 근거 채택 판정): 구현 완료
  • Phase 4(LLM 전환 + Pydantic 출력 검증): 구현 완료_call_llm_companyx_grounding() + CompanyXRAGOutput Pydantic 검증. settings.DEFAULT_LLM_MODEL(gpt-5-mini) SSOT 참조.
  • Phase 5A(인덱싱 파이프라인 전환): 코드 전환 완료, 배포 미완료 — PDF 6페이지 분할 → Base64 → embed_items(), task_type(RETRIEVAL_DOCUMENT/QUERY) 전달, metadata pass-through 구현. skill-embedding + skill-rag-file 양쪽 반영.
  • Phase 5B: 미완료 — A 배포 후 진행. 상세 내용: 260317_companyx_grounding_코드검토_및_문서현행화

관련 문서

연결 구조

  • 같은 레벨 입력: ideas/260312_companyx_내부문서_rag_응답_아이디어.md, scenarios/260312_companyx_내부문서_근거응답_사용자시나리오.md
  • 단일 리서치: research/260315_companyx_rag_답변합성_시나리오동시종결_리서치.md
  • 단일 플랜: 본 문서 1개
  • 닫힘 흐름: 본 플랜 완료 기준 충족 시 시나리오/리서치/트러블 동시 닫힘 (아이디어 문서는 종결 상태 유지)

1. 이번 계획의 결정

  • 현재 코드 기준으로 Phase 1~3(구조 분리, 질문 유형 계약, 근거 채택 판정)은 구현 완료 상태입니다.
  • Phase 4(LLM 전환 + Pydantic 출력 검증)는 구현 완료입니다. _call_llm_companyx_grounding() + CompanyXRAGOutput Pydantic 검증 도입.
  • 남은 핵심은 Phase 5A 배포 + Phase 5B 테스트/검증입니다.
  • 저장 전제 (SSOT): PostgreSQL team_document_chunkskill-rag-file 문서 청크의 Single Source of Truth입니다. upload.py, reindex.py, search.py 모두 PostgresDocumentVectorStore를 사용하며, ChromaDB는 레거시 실험 스크립트(rebuild_chroma_collection.py)에서만 쓰이고 운영 API와 분리됩니다. 이 계획에서 "DB 전환" 작업은 없으며, 대신 스키마 최적화 및 인덱스 재생성 관점에서 관리합니다.
  • 임베딩 전제 갱신: 컬렉션 차원 384는 더 이상 사용하지 않으며, 768도 Gemini 2 기준으로 전량 재임베딩해야 합니다.
  • Go 전환 근거: 내부 NAS Go 동기화 리서치의 1분 벤치마크는 Go가 Python 대비 총 사이클 +5~6% 수준이며, “무조건 큰 폭의 시간 단축”을 보장하지 않습니다.
  • 인덱싱 파이프라인 전환: skill-rag-file의 텍스트 변환 후 청킹 방식(pdftotext/PyPDF2/OCR/문자단위 분할)을 Gemini Embedding 2 원본 파일 직접 임베딩으로 전환합니다. 이 계획에서 닫습니다. 텍스트 변환 방식으로 임베딩한 뒤 전환하면 전량 재임베딩이 두 번 발생하므로, 파이프라인 전환이 임베딩보다 반드시 선행합니다.
  • 임베딩 전담 원칙: 임베딩 관련 로직은 skill-embedding이 전담합니다. skill-rag-file에서 Gemini API를 직접 호출하면 모델 관리와 비용 트래킹이 파편화되므로, PDF/이미지 바이트 입력 엔드포인트를 skill-embedding에 추가하는 방향으로 확장합니다.
  • 이 문제는 검색 인프라 확장보다 답변 합성 계약 부재 문제로 다룹니다.
  • 질문별 if 분기, 질문별 direct answer, 질문별 프롬프트 추가로 닫지 않습니다.
  • Company X grounding 경로에 공통 계약 3개를 먼저 고정합니다.
    1. 질문 유형 계약
    2. 근거 채택 계약
    3. 근거 부족 시 실패 계약
  • 성공 기준은 검색 hit 존재가 아니라 직접 답 + 질문 적합 근거 또는 명시적 부족 안내로 바꿉니다.

2. 범위

  • 포함:
    • skill-rag-file 인덱싱 파이프라인 → Gemini Embedding 2 원본 파일 직접 임베딩 전환
    • Company X RAG의 현재 임베딩 경로/차원 재검증
    • NAS 최신 문서 동기화본과 검색 컬렉션 반영 상태 확인
    • rb8001/app/services/companyx_grounding_service.py
    • Company X 재오픈 기준 질문 20개의 응답 계약
    • SKILL.md와 코드 계약 정합화
    • 테스트 보강
  • 제외:
    • Company X 전체 문서셋 대규모 확대
    • 다른 RAG 경로 공통화
    • Prompt DB 전면화
    • skill-rag-file 자체 리팩토링 (인덱싱 파이프라인 전환 외)

3. 공통 계약 고정안

A. 질문 유형 계약

  • 최소 유형은 아래 4개로 고정합니다.
    • 설명형
    • 사실 확인형
    • 수치 확인형
    • 재정리형
  • 판단 결과는 코드 내부 enum 또는 동등한 상수로 다룹니다.
  • 질문별 문자열 특례가 아니라 유형별 처리로 묶습니다.

B. 근거 채택 계약

  • 검색 결과가 있다고 바로 근거로 채택하지 않습니다.
  • 채택 기준:
    • 질문의 핵심 엔티티와 직접 관련이 있어야 합니다.
    • 질문 유형에 맞는 문서여야 합니다.
    • 상위 결과라도 질문과 무관하면 버립니다.
  • relevance_score는 후보 신호일 뿐, 최종 성공 판정 기준이 아닙니다.

C. 실패 계약

  • 아래 경우는 성공처럼 반환하지 않습니다.
    • 검색 결과 0개
    • 검색 결과는 있으나 질문과 무관
    • 수치/규정 질문인데 단정 가능한 근거가 없음
  • 실패 응답은 아래 셋 중 하나로 고정합니다.
    • 문서 없음
    • 질문과 맞는 문서 미확인
    • 내부 문서만으로는 단정 불가

4. 구현 원칙

  • 인덱싱 파이프라인 전환: 텍스트 변환 후 청킹하는 현재 방식을 Gemini Embedding 2의 멀티모달 직접 임베딩으로 전환합니다.
    • 대상: PDF, 이미지, docx 등 모든 파일 형식 — 텍스트 추출 없이 원본 파일 직접 임베딩
    • 제한: 텍스트 8,192 토큰, PDF 6페이지 단위, 영상 120초, 오디오 80초
    • 기존 텍스트 추출 파이프라인(pdftotext, PyPDF2, OCR)은 제거 대상
  • RAG 구조 전환: 현재 규칙 기반 문자열 조합은 RAG가 아닙니다. 검색된 청크를 컨텍스트로 LLM에 전달해 답변을 생성하는 구조로 전환합니다.
    • 플로우: 질문 임베딩 → pgvector 유사도 검색 → 적합 청크 선별 → LLM 컨텍스트 전달 → 답변 생성
    • LLM: 현재 rb8001 기준 모델 사용 (gpt-4o-mini 계열)
    • 근거 부족 시 LLM에게 "문서 없음"을 명시적으로 지시하는 시스템 프롬프트 포함
  • LLM 출력 형식 강제: LLM 응답을 프롬프트 지시만으로 믿지 않습니다. Pydantic typed validation으로 출력 형식을 검증합니다.
    • 근거: Pydantic AI 도입 기반 LLM 출력 안정화 방향확정 리서치 — 프롬프트만으로 형식 일탈을 막지 못함이 실측 확인됨
    • 방식: Pydantic-only 우선 (Pydantic AI는 의존성 부담으로 2차)
    • 핵심: 텍스트를 생성하는 것이 아니라 evidence_docsfailure_reason을 구조화된 JSON으로 받는 것이 목적입니다. Slack UI나 다른 채널로 전달 시 파싱 에러를 원천 차단합니다.
    • 출력 스키마: direct_answer(str), evidence_docs(List[str]), failure_reason(Optional[str]) 필드 고정
    • 검증 실패 시 성공처럼 반환하지 않습니다. 명시적 실패 응답으로 처리합니다.
  • SKILL.md의 답변 순서(direct answer -> evidence documents -> short evidence summary)를 LLM 프롬프트 계약으로 내립니다.
  • 질문별 direct answer 하드코딩을 더 늘리지 않습니다.
  • 질문 유형 판정과 근거 채택 판정은 별도 함수로 분리합니다.
  • 검색됨답할 수 있음을 같은 상태로 취급하지 않습니다.
  • 메타 대화(어떤 부분이 더 필요하신지...)는 실패 기본 경로로 사용하지 않습니다.

5. 구현 단계

병렬 실행 구조 — 트랙 A / B / C

간섭 없이 3개 트랙으로 분리됩니다.

트랙 담당 Phase 건드리는 서비스 상태
A — 인덱싱 파이프라인 Phase 5A skill-embedding + skill-rag-file 인덱싱 경로 (upload.py, reindex.py) 코드 완료, 배포 대기
B — LLM 답변 합성 Phase 4 (Phase 1~3 구현 완료) rb8001/companyx_grounding_service 전용 구현 완료
C — 테스트·검증 Phase 5B A + B 결과물 A 배포 후 진행

A와 B가 간섭 없는 이유: A는 skill-embedding API 확장 + skill-rag-file 인덱싱 경로(upload/reindex) 수정. B는 rb8001/companyx_grounding_service에서 LLM 호출 + Pydantic 검증 추가. 공유 파일 없음.

A·B 시작 전 1회 합의 필요: 아래 멀티모달 임베딩 API 규격. 이것만 고정하면 A·B 독립 진행 가능합니다.

C는 A·B 완료 후: 선행 의존이 명확하므로 병렬 불가.

트랙 A·B 공유 인터페이스 계약: skill-embedding 멀티모달 엔드포인트

이 규격은 트랙 A(skill-embedding 구현측)와 트랙 B(rb8001 소비측)가 독립 개발을 착수하기 위한 합의 기준입니다.

현재 상태 (2026-03-17 코드 확인)

기존 POST /embed 엔드포인트가 이미 멀티모달 입력 구조를 갖고 있습니다.

  • 활성 서비스: skill-embedding-repo/ (Gemini Embedding 2 기반, GeminiEmbedder)
  • 레거시 서비스: skill-embedding/ (ONNX 기반) — 배포 이미지 불일치 의심은 해소됨
  • 기존 EmbedItem 모델에 mime_type + data_base64 필드가 이미 존재:
    class EmbedItem(BaseModel):
        text: Optional[str] = None
        mime_type: Optional[str] = None       # application/pdf, image/jpeg 등
        data_base64: Optional[str] = None     # Base64 인코딩 바이너리
    
  • dimensions: 환경변수 EMBEDDING_DIM=768 고정. 요청별 지정 불가이나 768 고정이므로 문제 없음.
  • MAX_BATCH_SIZE: 100 (환경변수로 조정 가능)

따라서 신규 엔드포인트(/v1/embed/multimodal)를 만들지 않고, 기존 POST /embed를 확장합니다.

기존 엔드포인트 확장 — 구현 완료

항목 구현 내용
task_type EmbedRequest.task_type 필드 추가 → GeminiEmbedder.encode(task_type=...)EmbedContentConfig(task_type=...) 전달. skill-rag-file: 인덱싱 시 RETRIEVAL_DOCUMENT, 검색 시 RETRIEVAL_QUERY 전달.
metadata pass-through EmbedItem.metadata 필드(dict) 추가 → 임베딩 처리에서 제외 → EmbedResponse.item_metadata로 그대로 반환.

확장 후 Request Payload

{
  "items": [
    {
      "mime_type": "application/pdf",
      "data_base64": "BASE64_ENCODED_STRING",
      "metadata": {
        "file_path": "/mnt/nas/6.Company X/...",
        "page_range": [1, 6],
        "chunk_index": 0
      }
    },
    {
      "mime_type": "image/jpeg",
      "data_base64": "BASE64_ENCODED_STRING",
      "metadata": {
        "file_path": "/mnt/nas/6.Company X/...",
        "chunk_index": 0
      }
    }
  ],
  "task_type": "RETRIEVAL_DOCUMENT"
}
필드 설명
data_base64 파일 바이너리의 Base64 인코딩 문자열. PDF는 6페이지 단위로 분할된 바이너리.
mime_type application/pdf, image/png, image/jpeg 등 Gemini가 직접 처리할 수 있는 형식.
metadata skill-embedding은 처리하지 않고 결과에 그대로 반환(pass-through). 클라이언트가 결과 매핑에 사용.
task_type RETRIEVAL_DOCUMENT(인덱싱) 또는 RETRIEVAL_QUERY(검색). Gemini Embedding API 최적화 힌트.

확장 후 Response Payload

기존 EmbedResponsemetadata 리스트를 추가합니다:

{
  "embeddings": [[0.123, -0.456, ...]],
  "item_metadata": [
    {
      "file_path": "/mnt/nas/6.Company X/...",
      "page_range": [1, 6],
      "chunk_index": 0
    }
  ],
  "model": "gemini-embedding-2-preview",
  "backend": "gemini",
  "dimensions": 768,
  "processing_time": 1.23
}

에러 처리

기존 FastAPI 에러 응답 구조를 유지하되, batch 부분 실패 시:

{
  "error": {
    "code": "UNSUPPORTED_MIME_TYPE | PAYLOAD_TOO_LARGE | EMBEDDING_API_FAILURE",
    "message": "상세 사유",
    "failed_indices": [0, 2]
  }
}
  • failed_indices: batch 중 실패한 input의 인덱스. 성공한 항목은 정상 반환하고 실패 항목만 별도 표시.

역할 분리 원칙

  • PDF를 6페이지 단위로 나누는 로직은 skill-rag-file이 담당 (파일 시스템 접근 권한 보유)
  • skill-embedding은 순수하게 '바이너리 → 벡터' 변환만 수행
  • 검색 시 질문(텍스트) 임베딩은 기존 POST /embedtexts 필드 그대로 사용 (768d 고정, task_type=RETRIEVAL_QUERY 추가)

확인 완료 항목

  • 기존 /embed 엔드포인트가 dimensions=768을 지원하는지 → 환경변수 EMBEDDING_DIM=768 고정. 확인 완료.
  • skill-embedding 레포 코드(ONNX)와 배포 이미지(Gemini 2) 불일치skill-embedding-repo가 Gemini 2 기반 활성 서비스. 해소.
  • Base64 인코딩 시 6페이지 PDF 바이트 크기 → nginx/gunicorn HTTP payload 크기 제한과 맞춰야 함 (구현 시 확인)

Phase 0. 운영 전제 재검증

  • 확인 항목과 결과:
항목 결과 상태
Company X RAG 임베딩 차원 384d 폐기, Gemini Embedding 2 / 768d 확정 (0_VALUE 정책) 확정
SKILL.md 전제 384dGemini Embedding 2 / 768d 갱신 완료 (2026-03-17) 확정
저장 경로 (rb8001 메모리) PostgreSQL memory_vectors 등 중심 전환 확인 확정
저장 경로 (skill-rag-file 문서 청크) SSOT PostgreSQL team_document_chunk. upload.py, reindex.py, search.py 모두 PostgresDocumentVectorStore 사용 확인. "DB 전환" 아님 — 스키마 최적화 및 인덱스 재생성 관점으로 관리. 확정
ChromaDB 레거시 격리 rebuild_chroma_collection.py — 실험용/레거시. 운영 API와 완전 분리됨. 긴급하지 않으나 정리 대상. 레거시 격리
인덱싱 파이프라인 PDF 6페이지 단위 바이너리 직접 임베딩으로 전환 완료 (IndexingPipelineService). 비PDF는 텍스트 추출 fallback 유지. task_type 구분(RETRIEVAL_DOCUMENT/QUERY) 적용. 코드 완료, 배포 대기
NAS 동기화 경로 확인 완료 (2026-03-17). NAS 마운트: /mnt/nas (CIFS //192.168.0.101/home). Company X 원본: /mnt/nas/workspace/6.Company X/ (53,249개). 외부→내부 NAS 동기화: cron 2건 (30분 주기 계층형 + 하루 1회 전수조사, Go 바이너리). 단, NAS → skill-rag-file 인덱싱은 자동화 없음 — 현재 수동 upload API 호출만 가능. 업로드 저장소(/mnt/hdd/data/documents/)에 114개만 인덱싱된 상태. 1순위 2,957개 투입에는 NAS 경로에서 직접 읽어 upload API를 일괄 호출하는 배치 스크립트 필요. 확인 완료 (배치 투입 수단 미구현)
재오픈 질문 20개 재현 Slack 실응답 재현 미실시 미완료
  • 현재 상태:
    • 임베딩/저장 전제는 확정됨.
    • 인덱싱 파이프라인 코드 전환 완료, 배포 대기.
    • NAS 동기화 경로 확인 완료. NAS 원본 53,249개 중 114개만 인덱싱. 대량 투입 배치 스크립트가 Phase 5A 남은 핵심 작업.
    • 재오픈 질문 20개 재현은 Phase 5B에서 검증.

Phase 1. 구조 분리 — 구현 완료

  • companyx_grounding_service에서 아래 책임을 분리합니다.
    • 질문 유형 판정
    • query candidate 생성
    • retrieval 결과 수집
    • 근거 채택 판정
    • direct answer 생성
    • failure answer 생성
  • 목표:
    • 현재 질문별 특례 분기와 generic fallback이 어디서 작동하는지 코드상 경계를 명확히 나눕니다.

Phase 2. 질문 유형 계약 도입 — 구현 완료

  • 최소 4개 유형 분류 함수를 추가합니다.
  • 재오픈 기준 질문 20개는 각 유형에 명시 매핑돼야 합니다.
  • 아래 3개는 대표 매핑 예시입니다.
    • 컴퍼니엑스의 투자사는 몇개야? -> 수치 확인형
    • 그럼 컴퍼니엑스 내부 규정 상 휴가는 얼마나 쓸 수 있어? -> 사실 확인형 또는 규정 확인형 성격
    • 오늘전통 프로그램을 Company X가 옐로펀치랑 같이 운영한다는 근거 있어? -> 사실 확인형

Phase 3. 근거 채택 판정 — LLM 위임으로 전환

  • 검색 결과를 그대로 상위 3개 노출하지 않습니다.
  • 키워드 기반 룰 필터링은 룰베이스 절제 원칙(global-principles.md §B.6)에 따라 제거했습니다.
  • 벡터 유사도 상위 결과를 문서 다양성 기준으로 선택한 뒤, LLM에 컨텍스트로 전달합니다.
  • LLM이 컨텍스트를 보고 질문 적합도를 재판단하며, 무관한 청크는 LLM이 failure_reason으로 처리합니다.
  • 휴가 질문에 todaytradition 청크가 잡히는 경우는 LLM이 질문과 맞는 문서 미확인으로 답해야 합니다.

Phase 4. LLM 기반 답변 생성 + Pydantic 출력 검증 — 구현 완료

  • 현재 _build_direct_answer() 규칙 문자열 조합을 LLM 호출로 대체합니다.
  • 전체 흐름 (이 Phase가 책임지는 경계):
    1. Phase 3에서 선별된 청크 원문(질문과 무관한 청크는 이미 탈락)을 컨텍스트로 조합
    2. 질문 유형별 시스템 프롬프트 + 컨텍스트 + 사용자 질문을 LLM에 전달
    3. LLM은 컨텍스트를 보고 질문에 답할 수 있는지 재판단 — 컨텍스트가 있어도 질문에 답하기 부족하면 failure_reason을 채움
    4. LLM 응답을 Pydantic 모델로 검증 (direct_answer, evidence_docs, failure_reason)
    5. 검증 통과 시 구조체를 파싱해 답변 조합 (직접 답 + 근거 문서 목록), Slack/프론트 출력
    6. 검증 실패 시 성공처럼 반환하지 않고 명시적 실패 처리
  • 역할 경계:
    • Phase 3(RAG): 벡터 유사도 기반 질문 적합도 판정 → 무관한 청크 탈락
    • Phase 4(LLM): 컨텍스트 기반 답변 가능 여부 재판단 → 형식 보장
    • 둘은 다른 판정이며 둘 다 필요합니다. Phase 3을 통과했다고 LLM이 무조건 답할 수 있는 것이 아닙니다.
  • Pydantic 출력 스키마:
    class CompanyXRAGOutput(BaseModel):
        direct_answer: str
        evidence_docs: List[str]
        failure_reason: Optional[str] = None
    
    • failure_reason이 None이 아니면 성공처럼 반환하지 않습니다.
    • 값은 문서 없음 / 문서 미확인 / 단정 불가 중 하나로 고정합니다.
    • 검증 실패 시에도 성공처럼 반환하지 않습니다.
  • 시스템 프롬프트 골격:
    • 질문 유형별로 다른 지시를 포함합니다.
    • 근거 부족 시: "문서에서 직접 답할 수 없으면 failure_reason 필드에 이유를 명시하고 direct_answer는 빈 문자열로 두세요."
    • 무관한 청크 주의: "제공된 컨텍스트가 질문과 직접 관련이 없다면 근거로 채택하지 마세요."
    • 하드코딩 문장 금지: 질문별 특례 문구 삽입 없이 공통 구조로 답변합니다.
  • LLM 호출 위치: companyx_grounding_service 내부에서 호출합니다. rb8001의 기존 gpt-4o-mini 호출 경로(LLM 클라이언트)를 재사용합니다. 별도 서비스로 분리하지 않습니다.
  • 프롬프트 지시만으로 형식을 믿지 않습니다. Pydantic typed validation이 출력 형식의 최종 보장입니다.
  • 질문별 하드코딩 문장을 추가하지 않습니다.

Phase 5A. 인덱싱 파이프라인 전환 및 Company X 문서 임베딩

  • 전환 후 파이프라인 흐름 (upload.py / reindex.py 기준):
    text_extractor.extract() → indexing_pipeline.build_index_payloads()
    → [PDF: PyPDF2 6페이지 분할 → Base64 | 비PDF: chunk_text fallback]
    → embedding_service.embed_items(task_type="RETRIEVAL_DOCUMENT")
    → vector_store.replace_document_chunks()
    
    • PDF: 6페이지 단위 바이너리 Base64 → embed_items() (멀티모달 직접 임베딩). chunk_text는 검색 호환용 미리보기 텍스트로 유지.
    • 비PDF: 기존 텍스트 추출 → 청킹 → embed_items(text=chunk) fallback.
    • 검색: embed_text(task_type="RETRIEVAL_QUERY") — RETRIEVAL_QUERY/DOCUMENT 구분 적용.
    • task_type + metadata pass-through: skill-embedding API에 구현 완료.
  • skill-rag-file 인덱싱 파이프라인에서 아래를 제거하고 Gemini Embedding 2 직접 임베딩으로 교체합니다.
    • pdftotext / PyPDF2 텍스트 추출 경로 제거
    • OCR 경로 제거
    • 문자 단위 청킹(chunk_text) 제거
    • 대체: PDF 6페이지 단위 직접 임베딩, 이미지 직접 임베딩
  • 구현 선택 (확정):
    • 임베딩 전담 원칙 유지: 임베딩 관련 로직은 skill-embedding이 전담합니다. skill-rag-file에서 Gemini API를 직접 호출하지 않습니다.
    • API: 기존 POST /embed를 확장 (task_type + metadata pass-through 추가). 신규 엔드포인트 불필요. 상세는 위 "트랙 A·B 공유 인터페이스 계약" 참조.
    • skill-rag-file측 구현: EmbeddingServiceembed_file(file_path, file_type) 메서드를 신설합니다. 내부적으로 파일을 읽어 Base64 인코딩 후 POST /embeditems 필드로 전달합니다.
    • 파일 형식별 처리 분기 (skill-rag-file이 담당):
      • PDF: 6페이지 단위로 분할 후 각 분할 바이너리를 Base64 인코딩하여 batch 요청
      • 이미지(PNG/JPEG): 바이트를 Base64 인코딩하여 요청
      • docx 등 Gemini 미지원 형식: 텍스트 추출 유지 → 기존 POST /embedtexts 필드 사용
  • chunk_by_tokens 정리: chunk_by_tokens()가 다른 경로에서 사용되는지 확인 후 미사용이면 제거합니다. 사용처가 있으면 문서에 반영합니다.
  • 임베딩 대상 (1·2순위): 전체 53,249개 중 시나리오 질문 커버리지가 높은 폴더만 우선 임베딩합니다.
순위 폴더 파일 수 대상 질문
1 6.Company X/10. MOU&인증 91개 옐로펀치 공동운영 근거
1 6.Company X/12. 컴퍼니엑스 소개자료 1개 X-COURSE 정의, 슬로건/미션
1 6.Company X/3. 배치프로그램(X-COURSE, X-HISSTORY 등) 2,865개 X-COURSE, 오늘전통, 프로그램 운영 흐름
2 6.Company X/6. 투자 13,018개 투자사 수, 투자 건수 수치형 질문
  • 1순위 합계: 2,957개 (전체의 5.5%). 2순위 포함 시 15,976개 (전체의 30%).
  • 파이프라인 전환 완료 후 1순위 → 2순위 순서로 임베딩을 실행합니다.
  • 임베딩 완료 후 Phase 0 표의 인덱싱 파이프라인 항목을 완료로 갱신합니다.
  • 현재 상태: 코드 전환 완료, 배포 미완료
  • 남은 작업:
    1. skill-embedding + skill-rag-file 배포/재기동
    2. NAS 동기화 경로 확인확인 완료. NAS 원본 경로: /mnt/nas/workspace/6.Company X/. 인덱싱 저장: /mnt/hdd/data/documents/. 자동 동기화 없음 — 수동 upload API만 가능.
    3. NAS 일괄 투입 배치 스크립트 작성/mnt/nas/workspace/6.Company X/ 하위 1순위 폴더 파일을 순회하며 upload API를 호출하는 스크립트. 현재 114/53,249개만 인덱싱된 상태.
    4. 1순위/2순위 폴더 실제 임베딩 실행
    • pdftotext/OCR 경로는 텍스트 미리보기 생성용으로 잔존 — PDF 직접 임베딩과 병행. 별도 제거 계획 불필요.

Phase 5B. 테스트 고정 및 검증

  • Phase 5A 완료 후 진행합니다.
  • 자동화 테스트 (코드):
    • 질문 유형 분류 정확성: 20개 질문 → 4유형 매핑 검증
    • 근거 채택 판정: 무관한 청크 반환 시 grounding_present=false 확인
    • 실패 응답 형식: generic 문장(관련 근거를 찾았습니다) 금지 확인
    • 성공 응답 형식: 직접 답 + 근거 문서명 + 요약 구조 확인
    • Pydantic 검증: CompanyXRAGOutput 파싱 성공 여부, failure_reason 유무에 따른 분기 확인
    • 저장 경로: 임베딩 결과가 team_document_chunk(PostgreSQL)에 저장됨을 확인
  • Slack 실응답 검증 (수동, 대표 5개):
    1. 오늘전통 프로그램을 Company X가 옐로펀치랑 같이 운영한다는 근거 있어? → 직접 답 + MOU 근거
    2. 컴퍼니엑스의 투자사는 몇개야? → 수치 답 또는 단정 불가
    3. 내부 규정 상 휴가는 얼마나 쓸 수 있어? → 규정 문서 확인 또는 문서 미확인
    4. X-COURSE가 뭐야? → 설명 + 근거 문서
    5. 근거 문서명만 다시 정리해줘 → 직전 근거 목록 재정리
  • Phase 0 갱신: NAS 동기화 경로/시점 검증을 끝내면 Phase 0 표에 반영
  • 종결 worklog: 테스트 통과 + Slack 5개 검증 완료 시 종결 worklog 작성
  • 테스트는 질문별 예외 성공이 아니라 공통 계약 준수 여부를 검증합니다.
  • 현재 상태: 미완료

6. 검증 기준

  • Phase 0에서 아래가 먼저 확인돼야 합니다.
    1. Company X RAG의 실제 임베딩 경로와 차원이 문서/런타임 기준으로 식별된다.
    2. NAS 최신 문서 동기화본과 검색 컬렉션 반영 경로가 설명 가능해진다.
    3. 재오픈 기준 질문 20개의 현재 실패 양상이 다시 재현된다.
  • 재오픈 기준 질문 20개에서 아래가 확인돼야 합니다.
    1. 직접 답이 먼저 나온다.
    2. 질문과 맞는 근거만 붙는다.
    3. 근거가 부족하면 명시적으로 부족하다고 답한다.
  • 검색 hit 있음 = success 경로가 제거돼야 합니다.
  • 질문별 특례 추가 없이도 재오픈 질문셋이 통과해야 합니다.
  • SKILL.md 요구사항과 실제 응답 형식이 일치해야 합니다.

7. 완료 판정 기준

  1. skill-rag-file 인덱싱 파이프라인이 Gemini Embedding 2 원본 파일 직접 임베딩으로 전환됩니다. (pdftotext/PyPDF2/OCR/문자단위 청킹 제거, EmbeddingService.embed_file() 구현, skill-embedding API 확장 확인)
  2. Company X NAS 문서가 전환된 파이프라인으로 임베딩됩니다.
  3. 260312_companyx_rag_answer_composition_regression.md에서 정의한 재현 질문셋이 더 이상 회귀하지 않습니다.
  4. 260312_companyx_내부문서_근거응답_사용자시나리오.md의 재오픈 기준 질문 20개가 기대 결과를 만족합니다.
  5. Company X RAG의 현재 임베딩 경로/차원과 NAS 문서 반영 경로가 research에 최신 기준으로 반영됩니다.
  6. companyx_grounding_service에 질문별 direct answer 특례를 추가하지 않고 공통 계약 구조로 바뀝니다.
  7. LLM 응답이 Pydantic 모델(direct_answer, evidence_docs, failure_reason)로 검증됩니다. 검증 실패 시 성공처럼 반환하는 경로가 없습니다.
  8. 테스트가 추가되고 통과합니다.
  9. 실행 결과는 worklog 1건으로 마감하고, 시나리오/트러블 문서와 양방향 링크를 연결합니다.

8. 후속 경계

  • 이 계획이 닫혀도 Company X 전체 문서군 확대나 범용 RAG 정책 공통화는 별도 계획으로 다룹니다.
  • Prompt DB나 전역 오케스트레이션 구조 변경은 이번 계획 범위 밖입니다.

한 줄 결론

  • 이번 계획은 대표 질문 특례 처리 제거 자체가 아니라, 현재 NAS 문서 운영 상태와 임베딩 전제를 다시 닫은 뒤 Company X 근거응답을 질문 유형 계약 + 근거 채택 계약 + 실패 계약으로 재구성해 시나리오와 트러블을 함께 닫는 작업입니다.