---
tags: [research, companyx, rag, answer-composition, scenario, troubleshooting]
---

# Company X RAG 답변합성 시나리오·트러블 동시종결 리서치

## 상위 원칙
- [Writing Principles](../../book/300_architecture/312_writing-principles.md)
- [Company X RAG 답변 합성 회귀](../troubleshooting/260312_companyx_rag_answer_composition_regression.md)
- [Company X 내부 문서 근거응답 사용자 시나리오](../scenarios/260312_companyx_내부문서_근거응답_사용자시나리오.md)

## 목적
- `Company X` 근거응답 이슈를 `트러블슈팅 + 시나리오` 한 문제로 보고, 둘을 함께 닫기 위한 원인 기준을 고정합니다.
- 현재 실패가 `검색 인프라 문제`인지, `답변 합성 문제`인지, `질문별 하드코딩 구조 문제`인지 단일 경로로 좁힙니다.
- 이 문서는 해결안 확장보다 `다음 plan 1건`을 만들 수 있을 정도로 직접 원인을 확정하는 데만 집중합니다.

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

## 조사 질문
1. 현재 실패의 직접 레이어는 검색인가, 답변 합성인가
2. `컴퍼니엑스의 투자사는 몇개야?`, `내부 규정 상 휴가는 얼마나 쓸 수 있어?`가 왜 깨지는가
3. 현재 Company X 경로는 일반 규칙 기반인가, 질문별 하드코딩 기반인가
4. 트러블 문서와 시나리오 문서를 함께 닫으려면 무엇을 plan에서 고정해야 하는가

## 범위 / 비범위
- 범위:
  - `rb8001`의 Company X grounding 진입/검색/응답 합성 경로
  - 재오픈 기준 질문 20개에 대한 처리 구조
  - 프롬프트/if 분기/질문 적합도 선별 구조
- 비범위:
  - Company X 전체 문서셋 확대 작업
  - `skill-rag-file` 인덱싱 성능 일반론
  - 다른 프로젝트나 다른 RAG 경로 전면 리팩토링
  - 최종 수정안 구현 자체

## 사실(Facts)

### 1. Company X 질문은 의도 분류 전에 전용 grounding 경로로 우선 진입한다
- `route_message()`는 사용자 컨텍스트 생성 직후 `try_companyx_grounding()`을 먼저 호출합니다.
- 위치:
  - `rb8001/app/services/message_service.py:81-93`
- 의미:
  - Company X 질문은 일반 intent/skill 경로 전에 별도 처리됩니다.
  - 따라서 이번 실패는 `일반 의도 분류가 Company X 질문을 놓쳤다`만으로 설명되지 않습니다.

### 2. Company X 경로 진입 조건은 팀 ID + 문자열 marker 기반 휴리스틱이다
- `should_handle_companyx_grounding()`은 `team_id == COMPANYX_TEAM_ID`와 `_looks_like_companyx_grounding_question(message)`로만 판단합니다.
- `_looks_like_companyx_grounding_question()`은 `근거`, `내부 문서`, `공동 운영`, `컴퍼니엑스`, `companyx` 같은 marker 포함 여부를 봅니다.
- 위치:
  - `rb8001/app/services/companyx_grounding_service.py:28-60`
- 의미:
  - 질문 유형이나 필요한 답변 방식보다 문자열 포함 여부가 우선입니다.
  - `컴퍼니엑스`만 들어가도 grounding 경로로 진입할 수 있습니다.

### 3. 검색 질의 확장은 사실상 질문 2종만 별도 하드코딩돼 있다
- `_build_query_candidates()`는 기본적으로 원문 질문 1개를 그대로 사용합니다.
- 추가 질의 확장은 아래 경우에만 있습니다.
  - `오늘전통 + 옐로펀치`
  - `X-COURSE`
- 위치:
  - `rb8001/app/services/companyx_grounding_service.py:63-91`
- 의미:
  - `투자사 몇 개`, `휴가 규정` 같은 재오픈 질문은 전용 query expansion이 없습니다.
  - 대표 질문만 상대적으로 유리한 구조입니다.

### 4. 검색 결과는 질문 적합도 재평가 없이 relevance score 순으로만 합쳐진다
- `_search_companyx_documents()`는 후보 질의별 검색 결과를 병합한 뒤 `relevance_score` 내림차순으로만 정렬합니다.
- 질문 유형, 문서 종류, 문서-질문 정합성 재평가 단계는 없습니다.
- 위치:
  - `rb8001/app/services/companyx_grounding_service.py:113-127`
- 의미:
  - 검색 hit가 있어도 그 hit가 질문에 맞는 근거인지 다시 검증하지 않습니다.

### 5. 직접 답 생성은 질문별 if 분기 하드코딩이고, 기본값은 generic 문장이다
- `_build_direct_answer()`는 아래 경우만 별도 문장을 가집니다.
  - `오늘전통 + 옐로펀치`
  - `X-COURSE`
- 그 외 모든 질문은 기본값 `Company X 내부 문서에서 관련 근거를 찾았습니다.`를 반환합니다.
- 위치:
  - `rb8001/app/services/companyx_grounding_service.py:136-153`
- 의미:
  - `투자사는 몇개야?` 같은 수치형 질문은 답을 계산하거나 `단정 불가`를 판단하지 않고 generic 문장으로 끝납니다.
  - 현재 구조는 질문 유형별 답변 계약이 아니라 일부 질문별 문장 하드코딩입니다.

### 6. 실패 처리는 `검색 결과가 0개일 때`만 작동하고, `검색은 됐지만 무관한 경우`를 다루지 않는다
- `_build_grounded_response()`의 실패 문장은 `if not results`일 때만 반환됩니다.
- 결과가 1개라도 있으면 질문 적합성 검증 없이 `근거 문서:` 목록을 붙입니다.
- 위치:
  - `rb8001/app/services/companyx_grounding_service.py:155-171`
- 의미:
  - 휴가 질문에 무관한 `companyx_todaytradition.pdf` 청크가 잡히면, 시스템은 이를 실패가 아니라 성공처럼 출력합니다.
  - 즉 현재 실패 경계는 `no result`만 있고 `irrelevant result`는 없습니다.

### 7. 현재 Company X 응답은 LLM 기반 합성이 아니라 규칙 기반 문자열 조합이다
- Company X 전용 경로는 검색 결과를 가져온 뒤 `_build_direct_answer()`와 청크 요약 문자열을 이어붙여 응답을 만듭니다.
- 위치:
  - `rb8001/app/services/companyx_grounding_service.py:130-171`
- 의미:
  - 이것은 RAG가 아닙니다. RAG의 정의는 검색된 청크를 컨텍스트로 LLM에 전달해 답변을 생성하는 구조입니다.
  - 현재 구조는 검색(Retrieval)만 있고 LLM 기반 생성(Generation)이 없습니다.
  - 따라서 현재 구현 형태는 질문별 문장/분기 하드코딩에 의존하고 있어 유지보수성이 낮고, 질문 유형을 감당하지 못합니다.
  - **진짜 RAG로 전환**: 검색된 청크를 컨텍스트로 LLM(현재 gpt-4o-mini)에 전달해 답변을 생성하는 구조로 바꿔야 합니다.

### 8. 스킬 문서가 요구하는 계약과 현재 구현 계약은 일치하지 않는다
- `SKILL.md`는 답변 순서를 `direct answer -> evidence documents -> short evidence summary`로 고정합니다.
- 또한 `documents are missing, say the evidence is insufficient`를 요구합니다.
- 위치:
  - `DOCS/skills/companyx-rag/SKILL.md`
- 현재 구현은 아래 차이가 있습니다.
  - `직접 답`이 generic 문장으로 대체될 수 있음
  - `문서 부족`과 `무관한 문서 반환`을 구분하지 않음
  - 증거 문서 적합성 검증 없이 score 상위 청크를 그대로 노출함
- 의미:
  - 문제의 직접 원인은 스킬 문서 부재가 아니라, 구현이 스킬 계약을 끝까지 지키지 못하는 데 있습니다.

### 9. 대표 성공 질문은 구조적으로 우대돼 있고, 재오픈 질문은 일반화 경로에 놓여 있다
- `오늘전통 + 옐로펀치` 질문은
  - 별도 query expansion이 있고
  - 별도 direct answer 하드코딩이 있으며
  - 대표 근거 문서셋도 이미 worklog에 남아 있습니다.
- 관련 문서:
  - [Company X 내부문서 RAG 근거응답 1차 구현 및 부분 검증](../worklog/260312_companyx_내부문서_rag_근거응답_구현및시나리오검증.md)
- 반면 재오픈 질문 다수는
  - 전용 query expansion이 없고
  - 질문 유형 분기 규칙도 없고
  - generic fallback 문장으로 마감됩니다.
- 의미:
  - 현재 성공은 일반화된 성공이 아니라 `대표 질문 최적화 성공`에 가깝습니다.

### 10. 따라서 지금 실패는 `검색 부재`보다 `답변 합성 + 실패 판정 부재 + 질문별 하드코딩 편향`의 결합이다
- 앞선 사실을 합치면 현재 경로는 아래 구조입니다.
  1. marker 기반으로 Company X 경로 진입
  2. 일부 질문만 유리한 query expansion
  3. relevance score만으로 검색 결과 채택
  4. 일부 질문만 direct answer 하드코딩
  5. 결과가 하나라도 있으면 성공처럼 문서 목록 노출
- 의미:
  - 이번 이슈의 직접 레이어는 `answer composition`입니다.
  - 그러나 더 깊게 보면 `질문 유형 계약이 없는 규칙 하드코딩 구조`가 상위 원인입니다.

### 11. 현재 Company X RAG는 `NAS 대량 문서셋 + 임베딩 전환 이슈` 위에 놓여 있다
- `51123` 기준 점검에서 `/mnt/nas/workspace/6.Company X` 아래 실파일은 `53,249`개였고, fullscan 최신 summary는 `2026-03-17 01:23:10+09:00`, hierarchical 최신 summary는 `2026-03-17 11:00:06+09:00`까지 누적돼 있습니다.
- 의미:
  - 지금 Company X RAG는 적은 대표 문서 몇 개만의 문제가 아니라, NAS에서 계속 동기화되는 대량 문서셋 위에서 동작해야 합니다.
- 따라서 대표 질문 일부만 통과하는 특례 구조로는 운영 상태를 닫을 수 없습니다.

### 12. Company X RAG의 임베딩 전제가 문서끼리 일치하지 않는다
- `DOCS/skills/companyx-rag/SKILL.md`는 `Default embedding path: local 384d`로 적고 있습니다.
- 반면 `260316_임베딩_전체프로젝트_현황_및_SSOT_리서치.md`와 `260316_임베딩_Gemini_Embedding_2_전환_문제오픈.md`는 로빙 전체 기준을 `Gemini Embedding 2`, `768d`, 전수 교체 대상으로 둡니다.
- 의미:
  - 지금 Company X 답변합성 문제는 순수한 응답 문구 문제만이 아닙니다.
  - 어떤 임베딩 모델/차원/컬렉션 상태를 SSOT로 볼지부터 다시 닫아야 합니다.

### 13. 그래서 2026-03-15 기준 리서치만으로 바로 구현에 들어가면 전제 충돌을 안고 시작한다
- 2026-03-15 리서치는 답변합성 구조 문제를 정확히 짚었지만, 그 이후 열린 `Gemini Embedding 2` 전환 문제와 NAS 대량 문서 운영 상태까지는 반영하지 못했습니다.
- 의미:
  - 지금 필요한 다음 단계는 `새 리서치 전체 재작성`이 아니라,
- `현재 NAS 문서량`, `현재 컬렉션 차원`, `현재 재임베딩 여부`, `현재 재오픈 질문셋(20개) 재현 상태`를 다시 확인하는 짧은 갱신 리서치입니다.

### 14. 현재 런타임 저장 구조는 PostgreSQL 중심으로 전환돼 있다
- `330_백엔드_PostgreSQL_ChromaDB_Vector_Memory.md` 상단 주석은 **2026-03-17 기준 운영 런타임이 `skill-rag-file`, `rb8001`의 핵심 벡터/검색/그래프 경로를 PostgreSQL 중심으로 전환**했다고 명시합니다.
- `rb8001` 기억 회상 핵심 경로는 이미 PostgreSQL `memory_vectors`, `memory_graph_nodes`, `memory_graph_edges` 기준으로 전환됐습니다.
- **`skill-rag-file`의 문서 청크 벡터는 코드(`upload.py`, `reindex.py`, `search.py`) 기준으로 `PostgresDocumentVectorStore`를 사용하며 `team_document_chunk` 테이블에 저장됩니다.** ChromaDB는 `rebuild_chroma_collection.py` 스크립트에서만 사용되며 운영 API 경로와 분리돼 있습니다.
- 확인된 파일별 저장소:

| 파일 | 사용 저장소 |
|------|------------|
| `upload.py` | `PostgresDocumentVectorStore` (`team_document_chunk`) |
| `reindex.py` | `PostgresDocumentVectorStore` (`team_document_chunk`) |
| `search.py` | `PostgresDocumentVectorStore` (`team_document_chunk`) |
| `rebuild_chroma_collection.py` | ChromaDB (레거시 스크립트, 운영 API 비사용) |

- 의미:
  - `skill-rag-file` 운영 API 경로는 이미 PostgreSQL 중심입니다. "ChromaDB에 저장 중" 표현은 부정확합니다.
  - Company X 문서 임베딩 저장 경로는 `team_document_chunk`(pgvector)가 SSOT입니다.
  - 레거시 `rebuild_chroma_collection.py`는 운영과 무관하며 정리 대상입니다.

### 15. NAS 전체 PDF 기준 임베딩 시간은 약 5.2시간(추정)이다
- `/mnt/nas/workspace` 전체 PDF 파일 수는 `27,849`개, 200개 샘플 평균 페이지는 `8.05`로 측정됐습니다.
- 100페이지 임베딩 벤치마크(768d, Gemini 2)에서 총 요청 시간은 `8.2768s`였고, 이를 페이지 단위로 환산하면 `0.0828s/페이지`입니다.
- 위 수치를 곱산하면 전체 PDF 예상 페이지는 약 `224,184`페이지, 임베딩 요청 시간은 약 `5.15시간`입니다.
- 의미:
  - 이 값은 **PDF 기준 단순 추정치**이며, 비PDF 파일/추가 전처리/네트워크 부하/실패 재시도는 포함하지 않습니다.

### 16. Go 전환이 “시간 단축”을 보장하지는 않으며, 확인된 이점은 제한적이다
- 내부 NAS Go 동기화 리서치의 1분 벤치마크(23 서버, DSM 목록 조회 루프)에서 Go는 Python 대비 **총 사이클 +5~6%**, 목록 조회는 더 빠르지만 로그인은 차이가 작았습니다.
- 해석:
  - Go 전환의 효과는 **작업 유형에 따라 제한적**이며, “무조건 큰 폭의 시간 단축”이라고 단정할 수 없습니다.
  - 따라서 임베딩/인덱싱 파이프라인에도 **A/B 실측 없이 Go 전환 효과를 가정하지 않습니다**.
- 근거 문서:
  - https://git.ro-being.com/ivada-infra/DOCS/src/branch/main/journey/research/260313_internal_nas_direct_go_sync_feasibility_research.md

### 17. 현재 텍스트 변환 기반 청킹은 Gemini Embedding 2의 멀티모달 능력을 전혀 활용하지 못하고 있다
- `skill-rag-file`은 모든 파일(PDF, 이미지, docx 등)을 텍스트로 변환한 뒤 문자 단위로 청킹합니다.
- Gemini Embedding 2는 텍스트뿐 아니라 **이미지, PDF, 오디오, 비디오를 직접 임베딩**할 수 있는 멀티모달 모델입니다. 텍스트 변환 없이 원본 파일을 그대로 넣을 수 있습니다.
- 현재 방식의 문제:
  - PDF: pdftotext/PyPDF2로 텍스트 추출 시 레이아웃, 표, 이미지 정보 손실
  - 이미지 문서: 텍스트 변환 불가하거나 OCR로 품질 저하
  - `char_per_token = 4` 한국어 과소 청킹으로 정보 단절
- 의미:
  - 임베딩 모델을 Gemini Embedding 2로 바꿨으면, 인덱싱 파이프라인도 **원본 파일 직접 임베딩** 방식으로 전환해야 일관성이 있습니다.
  - 텍스트 변환 파이프라인을 유지하는 것은 Gemini Embedding 2로 전환한 의미를 절반 이상 버리는 것입니다.

## 해석(Interpretation)

### 1. 이 문제는 검색 인프라 장애가 아니라 응답 정책 부재 문제다
- Company X 전용 경로는 실제로 존재하고, 검색 호출도 수행합니다.
- 실패가 나는 지점은 `질문에 대한 직접 답을 어떻게 만들고, 어떤 검색 결과를 근거로 채택하며, 언제 모른다고 말할지`가 비어 있다는 점입니다.

### 2. 지금 구조는 시나리오를 닫기 어려운 형태다
- 시나리오는 `설명형`, `사실 확인형`, `수치 확인형`, `재정리형`을 모두 요구합니다.
- 하지만 현재 구현은 실질적으로 `대표 사실 확인형 2종`만 특례 처리합니다.
- 따라서 새 질문이 들어올수록 `if`와 문장 하드코딩이 늘어날 가능성이 높고, 시나리오를 일반 규칙으로 닫을 수 없습니다.

### 3. 트러블 문서가 지적한 회귀의 직접 원인은 질문 적합도 검증 부재다
- 재오픈 질문들은 검색 결과가 0개라서 실패한 것이 아닙니다.
- 결과가 있어도 질문과 무관한 청크를 success처럼 출력했습니다.
- 즉 이번 회귀를 닫는 핵심은 `retrieval result exists`가 아니라 `retrieval result is fit for this question`를 판정하는 단계입니다.

### 4. 스킬 문서는 방향이 맞지만, 구현 계약으로 내려오지 않았다
- `SKILL.md`는 이미 직접 답, 근거 문서, 부족 시 명시를 요구합니다.
- 문제는 그 계약이 코드에서 재현 가능한 판정 규칙으로 변환되지 않은 것입니다.
- 따라서 다음 plan은 새 문구를 더 쓰는 게 아니라, 스킬 계약을 코드가 판정 가능한 구조로 바꾸는 데 집중해야 합니다.

### 5. 다만 지금은 응답 계약 구현 전에 운영 전제를 다시 닫아야 한다
- Company X RAG는 이미 NAS 실문서 대량 적재 상태 위에서 동작하고 있습니다.
- 동시에 임베딩 SSOT는 `384d local`과 `Gemini 2 / 768d` 문서가 공존합니다.
- 따라서 다음 plan의 첫 단계는 `답변합성 코드 수정`이 아니라 `운영 전제 재검증`이어야 합니다.

### 6. 하드코딩 프롬프트/문장 증식은 이 문제의 해법이 아니다
- 현재도 대표 질문 성공은 이미 하드코딩 특례에 기대고 있습니다.
- 이 상태에서 `투자사`, `휴가`, `투자 건수`, `규정`, `복지` 식으로 질문별 문장을 더 추가하면,
  - 시나리오는 겉보기로만 닫히고
  - 트러블은 다른 질문에서 다시 열리며
  - 프롬프트와 if 분기 중복만 쌓이게 됩니다.

## 미확정 항목(Unresolved)

### 확정된 항목 (2026-03-17)
- ~~5. 컬렉션 차원~~: `384d` 폐기, `Gemini Embedding 2 / 768d` 전량 교체 확정 (0_VALUE 정책). SKILL.md 반영 완료.
- ~~1. 질문 적합도 판정~~: `_score_result_relevance()` + `_select_relevant_results()`로 구현됨. rerank는 현재 불필요로 판단 (코드 검토 기준).
- ~~2. 수치형 질문 판정~~: `_can_answer_quantitative()` + `_COUNT_MARKER_PATTERN`으로 구현됨. 정밀도 미검증이나 구조는 확정.

### 여전히 미확정
3. `재정리형 질문`은 이전 응답 재사용 없이 재검색함 (세션 연결 없음). 현재 스코프에서는 재검색 허용으로 운영.
4. Company X 전용 규칙은 현재 독립 모듈(`companyx_grounding_service.py`)로 유지. 범용화는 이 문제 세트 밖.
~~6. NAS 최신 문서 동기화본이 Company X RAG 검색 컬렉션에 언제, 어떤 방식으로 반영되는지는 아직 미확정.~~ → **확인 완료 (2026-03-17)**: NAS 마운트 `/mnt/nas` (CIFS). Company X 원본: `/mnt/nas/workspace/6.Company X/` (53,249개). 외부→내부 NAS는 cron(30분 계층형 + 하루 1회 전수조사)으로 동기화. 단, NAS → skill-rag-file 인덱싱은 자동화 없음 — 수동 upload API 호출만 가능. 현재 114개만 인덱싱. 대량 투입 배치 스크립트 필요.
~~7. 한국어 과소 청킹 리스크 및 `Gemini 2 PDF 직접 임베딩` 미구현은 **본 문제 세트에서 즉시 전환** 대상으로 결정할지 미확정.~~ → **확정 (2026-03-17)**: 계획 Phase 5A에서 문자 단위 청킹 제거 + PDF 직접 임베딩 전환으로 결정됨. 이 계획에서 닫음.

## 결론
- 이 이슈의 직접 원인은 `Company X 검색 실패`가 아니라 `질문 적합 근거 선별 없는 답변 합성`입니다.
- 더 근본 원인은 `질문 유형 계약 없이 일부 질문만 하드코딩 특례 처리하는 구조`입니다.
- **가장 근본 원인**: 현재 구조는 RAG가 아닙니다. 검색된 청크를 LLM 컨텍스트로 전달해 답변을 생성하는 구조가 없고, 규칙 기반 문자열 조합으로 대체하고 있습니다. 이를 진짜 RAG 구조로 전환해야 합니다.
- 다만 현재는 `NAS 대량 문서 운영 상태`와 `Gemini Embedding 2 전환 상태`가 겹쳐 있으므로, 다음 plan은 구현 전에 운영 전제 재검증부터 고정해야 합니다.
- PostgreSQL 저장 경로 전제는 확정됐으므로, plan에서는 이를 기준으로 재검증/테스트 항목을 정렬해야 합니다.
- 그 전제가 닫힌 뒤 구현 단계에서 아래 4가지를 고정해야 합니다.
  1. 검색된 청크를 컨텍스트로 LLM에 전달해 답변을 생성하는 구조 도입
  2. 질문 유형별 판정 계약
  3. 검색 결과의 질문 적합도 판정 계약
  4. 근거 부족 시 명시적 실패 계약

## 다음 plan이 반드시 고정해야 할 항목
1. 현재 Company X RAG의 실제 임베딩 경로와 컬렉션 차원(`384d`/`768d`)을 먼저 재검증할 것
2. NAS 최신 문서 동기화본이 현재 검색 컬렉션에 언제 반영되는지 확인할 것
3. 재오픈 기준 질문 20개를 현재 운영 상태에서 다시 재현해 `검색 실패 / 무관한 hit / 답변 합성 실패`를 분리할 것
4. `success`의 정의를 `검색 hit 존재`가 아니라 `직접 답 + 질문 적합 근거 또는 명시적 부족 안내`로 바꿀 것
5. 질문별 direct answer 하드코딩 추가를 금지하고, 질문 유형별 공통 계약으로 대체할 것
6. `무관한 청크 반환`을 `실패`로 판정하는 경계를 추가할 것
7. 스킬 문서 계약과 코드 계약을 1:1로 맞출 것
8. PostgreSQL 저장 경로 전제를 plan과 테스트 기준에 반영할 것

## 관련 문서
- [Company X RAG 답변 합성 회귀](../troubleshooting/260312_companyx_rag_answer_composition_regression.md)
- [Company X 내부 문서 RAG 응답 아이디어](../ideas/260312_companyx_내부문서_rag_응답_아이디어.md)
- [Company X 내부 문서 근거응답 사용자 시나리오](../scenarios/260312_companyx_내부문서_근거응답_사용자시나리오.md)
- [Company X 내부문서 RAG 근거응답 현황 리서치](./260312_companyx_내부문서_rag_근거응답_현황_리서치.md)
- [Company X RAG 답변합성 시나리오·트러블 동시종결 계획](../plans/260315_companyx_rag_답변합성_시나리오동시종결_계획.md)
- [Company X 내부문서 RAG 근거응답 1차 구현 및 부분 검증](../worklog/260312_companyx_내부문서_rag_근거응답_구현및시나리오검증.md)
- [Company X RAG 스킬 문서](../../skills/companyx-rag/SKILL.md)

---

## 2026-03-17 코드 검토: 사실 섹션 현행화

> 이 리서치는 2026-03-15 코드 기준으로 작성됐습니다.
> 2026-03-17 실제 코드(`rb8001/app/services/companyx_grounding_service.py`)를 직접 검토한 결과,
> 아래 사실들이 이미 코드에서 변경되거나 구현된 것으로 확인됐습니다.

### 이미 구현되어 outdated된 사실

| 리서치 사실 | 현재 코드 상태 |
|---|---|
| Fact 3: query expansion이 2종 하드코딩 | `_build_query_candidates()`가 `_extract_keywords()` + `_DOCUMENT_HINTS_BY_TYPE` 기반 동적 확장으로 변경됨 |
| Fact 4: relevance score만으로 결과 채택 | `_score_result_relevance()`, `_select_relevant_results()`로 질문 적합도 재평가 구현됨 |
| Fact 5: 직접 답이 2종 하드코딩 + generic 기본값 | `_classify_question_type()` + type-based `_build_direct_answer()`로 4개 유형 처리 구현됨 |
| Fact 6: 실패 처리가 results=0일 때만 작동 | `_select_relevant_results()` 필터링 후 relevant=0이면 `_build_failure_answer()` 호출됨. `QUESTION_QUANTITATIVE`는 `_can_answer_quantitative()`로 수치 존재 여부도 판정함 |
| Fact 9: 대표 질문만 특례 처리 | 이제 `_classify_question_type()` → `_build_query_candidates()` → `_score_result_relevance()` 공통 경로. 하드코딩 특례 제거됨 |

### 여전히 사실인 항목

- **Fact 1**: Company X 경로는 의도 분류 전에 우선 진입 (`message_service.py:81`)
- **Fact 2**: 진입 조건은 여전히 marker 기반 (`_looks_like_companyx_grounding_question`)
- **Fact 7**: 응답 합성은 여전히 규칙 기반 문자열 조합 (LLM 합성 아님)

### 현재 미해결 항목 (2026-03-17 갱신)

- ~~relevance score만으로 질문 적합도 판정 가능한지~~: `_score_result_relevance()` 구현됨. 현재 구조로 운영.
- 수치형 질문의 `단정 가능/불가`를 `_COUNT_MARKER_PATTERN` 패턴만으로 충분한지 미검증 (정밀도 테스트 필요)
- 재정리형 질문은 이전 응답 재사용 없이 재검색함 (세션 연결 없음). 현재 스코프에서는 재검색 허용으로 운영.
- 임베딩 전제: `384d` → `Gemini Embedding 2 / 768d` 전환 확정 (SKILL.md 반영 완료)

### 현재 코드에서 추가 확인된 구조

- `_DOCUMENT_HINTS_BY_TYPE`: 유형별 문서 힌트 (`quantitative`는 투자/포트폴리오/실적 계열)
- `_supports_quantitative_focus()`: 수치형 질문에서 count marker 패턴(`\d+개사`, `총\d+` 등) 존재 여부 판정
- `_select_evidence_results()`: 관련 결과 중 상위 2개만 근거로 선별 (중복 문서 제거)
- `_build_failure_answer()`: 유형별 다른 실패 메시지 반환

### 미완료 항목 (코드 기준)

- Phase 5 테스트 고정 미완료: 재오픈 기준 질문 20개에 대한 자동화 테스트 없음
- Slack 실응답 검증 미완료: 코드는 구현됐으나 실제 Slack에서 재오픈 질문 20개 통과 여부 미확인
- 종결 worklog 미존재: 트러블슈팅 문서가 참조하는 `260315_companyx_정확표기_slack_근거응답_경로종결.md` 파일 없음
- ~~**청킹-임베딩 전제 불일치 미해결**~~ → **해소 (2026-03-17)**: `IndexingPipelineService` 도입. PDF는 6페이지 단위 바이너리 Base64 → `embed_items(task_type="RETRIEVAL_DOCUMENT")` 직접 임베딩. 비PDF는 텍스트 추출 → 청킹 fallback 유지. `chunk_text`는 검색 호환용 미리보기 텍스트로만 사용.
- ~~**Phase 4 LLM 전환 미구현**~~ → **구현 완료 (2026-03-17)**: `_call_llm_companyx_grounding()` + `CompanyXRAGOutput` Pydantic 검증 도입. `settings.DEFAULT_LLM_MODEL`(gpt-5-mini) SSOT 참조. 규칙 기반 `_build_direct_answer()`는 LLM 실패 시 fallback으로 유지.
- ~~**skill-embedding 배포 이미지와 레포 코드 일치 여부 미확인**~~ → **확인 완료 (2026-03-17)**: 활성 서비스는 `skill-embedding-repo/`(`GeminiEmbedder`, Gemini Embedding 2 기반). 레거시 `skill-embedding/`(ONNX)와는 별도 레포. 해소.
- ~~**Phase 5A 파이프라인 전환 + 인터페이스 계약**~~ → **코드 구현 완료 (2026-03-17)**: `task_type`(RETRIEVAL_DOCUMENT/QUERY) 전달 + `metadata` pass-through 양쪽(skill-embedding, skill-rag-file) 구현. 배포/재기동은 미완료.

### 260317_companyx_grounding_코드검토_및_문서현행화](../worklog/260317_companyx_grounding_코드검토_및_문서현행화.md)

---

## 2026-03-19 최신 리서치: Gemini Embedding 2 멀티모달 검색 이슈 및 하이브리드 해결책

#### 1. 문제 발견 (관측 및 검증)
*   **현상:** PDF 바이너리 직접 임베딩(`pdf_original_bytes`) 시, 텍스트 쿼리에 대한 검색 결과가 빈 배열(`[]`)로 반환되거나 유사도가 극히 낮음.
*   **근본 원인:** 
    - **페이지 제한:** Gemini Embedding 2는 PDF 직접 임베딩 시 1회 최대 6페이지 제한이 있어 대형 문서의 정보 손실 발생.
    - **벡터 공간 편차:** 바이너리 기반 벡터(시각적 문맥 포함)와 텍스트 기반 쿼리 벡터 간의 공간적 거리가 멀어 기존 검색 임계값 미달.

#### 2. 해결 방안: 하이브리드 임베딩 (Hybrid Indexing)
*   **전략:** 동일 PDF 문서에 대해 '원본 바이너리 임베딩(시각적 맥락)'과 '텍스트 추출 임베딩(정밀 키워드)'을 병행 수행.
*   **실측 데이터:** 최신 50개 텍스트 중심 문서(MOU, 투자계획서 등)에 하이브리드 모드 적용 결과, 텍스트 쿼리에 대한 검색 적중률(Recall)이 정상 복구됨을 확인.

#### 3. 결론 및 향후 조치
*   **인덱싱 파이프라인 수정:** `IndexingPipelineService`를 하이브리드 모드로 상시 전환 (배포 완료).
*   **재인덱싱:** 기존 Company X의 텍스트 밀집 문서들을 하이브리드 방식으로 재인덱싱하여 RAG 답변 합성의 '근거 데이터' 신뢰성 회복.

