Files
aio-01d4-stu/Last3Mon/71/VIBE-A-TDOC.md
T

101 lines
7.0 KiB
Markdown
Raw Normal View History

# 7교시 바이브 코딩 · `tdoc` 만들기
> **정답본** · 수업이 끝난 뒤(8교시 뒤) 학생 저장소에 올립니다. 그 전에는 올리지 않습니다. 요청서는 [VIBE-REQ-TDOC.md](VIBE-REQ-TDOC.md), 문제는 [VIBE-Q-TDOC.md](VIBE-Q-TDOC.md).
> 68일차부터 바이브 코딩 프로그램은 **수업 준비 때 미리 만들어 보지 않습니다.** 아래 요청문과 확인 기준은 예시이고, 걸린 시간은 8교시 강사 바이브 코딩에서 재어 기록합니다.
## 요청서 → 요구 도출 예 (`SPEC.md` 예)
요청서의 말을 **확인할 수 있는 요구**로 바꾼 예입니다. 정답이 하나는 아닙니다. "됐다 / 안 됐다"를 말할 수 있는지가 기준입니다.
### 1. 요구
| 번호 | 요구 | 요청서의 말 | 확인 방법 |
| --- | --- | --- | --- |
| R1 | `tdoc index <폴더>`: `*.md`를 `## ` 섹션(기본) 또는 글자 수로 청크, SQLite 파일 하나에 저장. 파일 해시 · 청크 설정이 같으면 건너뜀 | ① | 가짜 임베딩: 파일 2개 → 청크 수 = `## ` 섹션 수. 바로 다시 → 임베딩 호출 0번. 한 파일만 고침 → 그 파일 청크 수만큼만 호출. 프로그램을 다시 켜도 `tdoc search`가 됨 |
| R2 | `tdoc search "..." --top-k N`: 점수(소수 셋째 자리) · 파일 · 섹션 · 글 앞부분을 N줄 | ② | `--top-k 5` → 5줄, 가짜 임베딩에서 질문 낱말이 가장 많이 겹치는 청크가 1등 |
| R3 | `tdoc ask "..."`: 1등 점수가 `--min-score`(기본 0.33) 아래면 LLM 없이 "문서에서 찾지 못했습니다". 아니면 번호 붙인 발췌만 주고 사실마다 `[번호]`, 번호 → 파일 · 섹션 | ③ | 가짜 LLM: 점수 낮은 질문 → LLM 호출 0번. `[7]`(범위 밖)을 돌려주는 가짜 LLM → 근거 목록에서 빠짐 |
| R4 | `tdoc <폴더>`: 질문 · `top_k` 입력, 답, 근거 목록. 근거를 고르면 그 청크 원문 전체 | ④ | 질문 하나 → 근거 `top_k`줄, 하나 고르면 아래 패널에 섹션 전체 |
| R5 | `tdoc eval questions.yaml --chunking <설정>`: 질문마다 1등 파일 · 3등 안 파일이 기대와 맞은 수 | ⑤ | 같은 `questions.yaml`(5개 이상)로 `section`, `200` → "1등 a/5 · 3등 안 b/5" 두 줄 |
### 2. 숨은 요구
- 저장할 것: `files(path, sha256, chunking)`, `chunks(path, section, text, vector)`. 청크 글 앞에 파일 제목 (오늘 Lab 01과 같음).
- 가짜 임베딩(낱말 해시 벡터) · 가짜 LLM. `Embedder` · `LLM`을 바꿔 끼울 수 있게. 테스트는 진짜를 부르지 않음.
- 근거 번호는 **코드가** 확인 (범위 밖은 버림). 오늘 Lab 02의 `cited`.
- `questions.yaml`(질문 · 기대 파일)을 프로그램보다 **먼저** 직접 씀. 답 없는 질문 하나는 R3 확인용으로.
- 설정마다 DB 파일 따로 (`tdoc-section.sqlite`, `tdoc-200.sqlite`).
- 키: `OPENAI_API_KEY` 환경 변수만 (`grep -c sk- *.sqlite` → 0).
- 화면: 색인 · 검색 · 답은 worker에서, 색인 진행 표시.
### 3. 범위 밖
PDF · 워드, 공유 서버, 벡터 DB 설치, n8n 연결. (강사 바이브 코딩에서 `tdoc serve`를 보여 줌)
### 4. 순서와 이유
1. 색인 + 검색 (R1 · R2), 가짜 임베딩 테스트. 답 · 화면 · 비교가 **모두 이 검색 결과를 씀**.
2. 답과 근거 (R3). 가장 위험한 규칙(점수 낮으면 LLM 안 부름, 없는 번호 버림)을 화면 없이 테스트로 먼저.
3. TUI와 비교 (R4 · R5). 검색 결과를 보여 주고 세기만 하면 됨.
### 자주 빠뜨리는 것 (수업 준비 때 예상)
| 빠뜨린 것 | 생기는 일 |
| --- | --- |
| "모른다고"의 조건 (점수 기준) | 문서에 없는 질문에도 LLM이 그럴듯한 답을 씀 (가장 위험) |
| 정답표(`questions.yaml`) | "좋아진 것 같다"만 남고 ⑤를 확인할 방법이 없음 |
| 파일 해시 | 한 줄 고칠 때마다 전부 다시 임베딩 (시간 · 비용) |
| 설정마다 DB 따로 | 200자 청크와 섹션 청크가 섞여 비교가 안 됨 |
| 가짜 임베딩 | 테스트할 때마다 진짜 임베딩 호출 |
## 확인 기준
| 요구 | 기준 |
| --- | --- |
| R1 | 가짜 임베딩 테스트: 파일 2개 색인 → 청크 수 = `## ` 섹션 수. 한 파일만 고치고 다시 색인 → 그 파일 청크만 다시 임베딩(호출 수로 확인) |
| R2 | 가짜 임베딩에서 질문 낱말이 가장 많이 겹치는 청크가 1등, `top_k`만큼 |
| R3 | 가짜 LLM으로: 근거 번호가 범위 밖이면 버림, 점수가 기준 아래면 LLM을 부르지 않음 |
| R5 | `questions.yaml` 5개, 두 설정의 맞은 수 표 |
| 비밀 | `OPENAI_API_KEY` 환경 변수만, SQLite·코드에 키 없음 |
## 단계별 요청문 예
### 1. 색인과 검색
```text
~/vibe/tdoc 에 Python 프로젝트를 만들어 줘. uv 사용, 명령 이름 tdoc, 코드와 화면 글자는 영어(답만 한국어).
이번 단계는 TUI 없이 두 명령만.
- tdoc index <folder> [--chunking section|<chars>] [--db tdoc.sqlite]: *.md 를 "## " 섹션(기본) 또는 글자 수로 청크.
청크 글 앞에 파일의 첫 줄 제목을 붙인다. SQLite 테이블 files(path, sha256, chunking), chunks(id, path, section, text, vector BLOB).
파일의 sha256 과 chunking 이 같으면 건너뛴다 (바뀐 파일만 다시).
- tdoc search "<question>" [--top-k 3]: 질문 임베딩과 코사인 유사도, 점수·파일·섹션·글 앞 80자.
- tdoc/embed.py: Embedder 인터페이스. OpenAI 구현(text-embedding-3-large, OPENAI_API_KEY, 한 번에 여러 개)과 테스트용 FakeEmbedder(낱말 해시 벡터).
- pytest: FakeEmbedder 로 청크 수, 바뀐 파일만 다시, 검색 순서.
```
### 2. 답과 근거
```text
- tdoc ask "<question>" [--top-k 3] [--min-score 0.33]: 1등 점수가 min-score 아래면 LLM을 부르지 않고 "문서에서 찾지 못했습니다".
아니면 gpt-4.1-mini 에 번호 붙인 발췌만 주고, 한국어 3문장 이내, 사실마다 [번호], 없으면 "문서에서 찾지 못했습니다".
번호를 파일·섹션으로 바꿔 근거 목록과 함께 출력. 범위 밖 번호는 버린다.
- tdoc/llm.py: LLM 인터페이스와 FakeLLM. pytest 로 min-score 경로, 번호 변환.
```
### 3. TUI와 비교
```text
- tdoc <folder>: Textual TUI. 위 질문 입력 + top_k 입력, 가운데 답, 아래 근거 목록(ListView). 근거를 고르면 원문 청크 전체를 아래 패널에.
검색·답은 worker 로. 색인이 없으면 먼저 색인 진행 표시.
- tdoc eval <questions.yaml> [--chunking ...]: questions: [{question, expect_path}] 로 1등 파일이 맞은 수, 3등 안에 맞은 수를 표로.
설정마다 DB 파일을 따로 (tdoc-section.sqlite, tdoc-200.sqlite).
```
## 7교시에 볼 것
| 볼 것 | 막혔을 때 한마디 |
| --- | --- |
| 색인할 때마다 전부 다시 임베딩 | "파일 해시로 바뀐 파일만" |
| 테스트가 진짜 임베딩을 부름 | "가짜 임베딩으로" |
| 근거 번호가 범위를 벗어남 | "번호를 코드에서 확인하고 버리게" |
| 비교할 때 두 설정이 한 DB에 섞임 | "설정마다 DB 파일 따로" |
| 키가 DB나 로그에 | "환경 변수만" |