codex를 활용한 업무 자동화(3)
[Codex
AX
]
이번에는 회의록 인덱싱 저장 기능을 한번 구현해 보겠다.
회의 녹취록 파일을 업로드 하면 이를 json파일로 Text화 하고, chromaDB에 인덱싱하여 저장한다. 이를 근거로 각 회의록 녹취를 RAG 검색을 하여 당시 회의에 대한 정보를 알 수 있다.
처음에는 chatGPT에 다음과 같이 요청을 했다.
회의 음성 녹음 파일을 텍스트화해 벡터DB에 저장하는 서비스가 사람들이 잘 쓸일이 있을 까?
GPT가 상당히 긴 답변을 주었지만 결론은 ‘잘 쓸 일이 있다’ 였다. 나는 이를 llama-index를 활용하고자 했다.
LlamaIndex기반으로 MVP구조를 만들어줘
🎯 MVP 목표 (중요)
이 정도만 되면 충분히 “쓸 만한 제품”입니다:
- 음성 업로드
- 자동 텍스트 변환
- 의미 기반 검색 (vector search)
- 질문하면 답변 (RAG)
- 클릭하면 해당 음성 구간 재생
🧱 전체 아키텍처
[Frontend] ↓ [API Server] ↓ ─────────────── 1. STT (Whisper) 2. Chunking 3. Embedding 4. Vector DB (pgvector) 5. LlamaIndex (Query Engine) ───────────────⚙️ 기술 스택 (현실적인 추천)
- STT: OpenAI Whisper
- Index/RAG: LlamaIndex
- DB: pgvector
- Backend: FastAPI
- Frontend: React or Next.js
👉 이 조합이 가장 빠르고 안정적
나만 쓸 건데 굳이 FASTAPI 및 React를 쓸 필요가 있을 까?
그냥 내 PC에서만 쓸 건데… 기술 스택을 FASTAPI까지 써야 할까?
그러자 GPT는 기술 스택을 좀 더 단순화하여 제안했다.
그냥 내 PC에서만 쓸 건데… 기술 스택을 FASTAPI까지 써야 할까?
⚙️ 기술 스택 (개인만 쓴다면…) STT: Whisper Index: LlamaIndex Vector DB: Chroma UI: Streamlit Backend: 없음
그래… 개인용이라면 이정도의 구성으로 충분할 것 같다.
이것을 이제 codex에 시켜보려 해. 이를 위한 프롬프트 초안을 만들어 줄 수 있어?
초안을 GPT가 만들었는데.. 상당히 길다
이것을 이제 codex에 시켜보려 해. 이를 위한 프롬프트 초안을 만들어 줄 수 있어?
다음은 프롬프트 초안이야…
내 PC에서만 사용할 “회의 음성 녹음 파일 → 텍스트화 → 벡터 인덱싱 → 질의응답” 로컬 앱을 만들어줘.
목표:
- 개인용 로컬 MVP
- FastAPI 같은 서버는 사용하지 않음
- Streamlit UI 또는 CLI 기반으로 실행 가능
- 음성 파일을 업로드/선택하면 STT로 텍스트화
- 텍스트를 chunk로 나누고 LlamaIndex로 인덱싱
- 로컬 Vector DB는 Chroma 사용
- 질문을 입력하면 관련 회의 내용을 기반으로 답변
- 답변과 함께 근거 transcript 조각, 파일명, 가능하면 시작/종료 시간 표시
기술 스택:
- Python 3.11+
- LlamaIndex
… <중략>...중략>
- Slack/Notion 연동 만들지 말 것
먼저 전체 설계를 간단히 설명한 뒤, 파일별로 코드를 작성해줘.
한 번에 모든 기능을 구현시키려 하면 Codex가 종종 맥락을 놓쳤다. 그래서 기능 단위를 잘게 나누고, 각 단계마다 실행 가능한 상태를 유지하는 방식으로 하려 한다.
이를 한꺼번에 던지면 제대로 일을 못할것 같어..
“맞아요. 한 번에 던지기보다 단계별 프롬프트로 쪼개는 게 훨씬 좋습니다.”
그러면서 GPT는 프롬프트를 단계별로 쪼개 주었다. 이를 이제 Codex에 하나 씩 실행시켜 보도록 한다.
1단계: 프로젝트 뼈대 프롬프트
로컬 개인용 “회의 음성 → 텍스트 → LlamaIndex 검색” 앱을 만들 것이다.
먼저 전체 프로젝트 뼈대만 만들어줘.
조건:
- Python 3.11+
- Streamlit UI
- CLI 스크립트도 나중에 추가 예정
- FastAPI/Django 사용 금지
- ChromaDB 사용
- LlamaIndex 사용
- OpenAI API는 .env에서 읽기
이번 단계에서는:
- 폴더 구조 제안
- requirements.txt 작성
- .env.example 작성
- README.md 초안 작성
- 빈 모듈 파일 생성용 코드 또는 파일 목록 제시
아직 STT, 인덱싱, 검색 로직은 구현하지 말고 TODO 주석만 넣어줘.
프로젝트 구조를 만들었다. codex에 python가상환경까지 만들게 하는 것은 시간이 좀 걸리므로(보안 문제 때문에 잘 안되었다) 내가 직접 해 버렸다. 터미널에서 다음과 같이 입력한다. 가상환경에 필수 라이브러리들을 설치하는 것이다.
python3 -m venv .venv
pip install -r requirements.txt
이제 다음 단계의 프롬프트를 실행시킨다
2단계: STT모듈 구현 프롬프트
이제 STT 모듈을 구현해줘.
대상 파일:
- src/stt.py
- ingest.py 일부
요구사항:
- audio 파일(mp3, wav, m4a)을 받아 텍스트로 변환
- OpenAI Whisper API 사용
- 결과를 data/transcripts/{원본파일명}.json 으로 저장
- JSON에는 source_file, text, segments 필드를 포함
- timestamp가 없으면 start/end는 null 처리
- 이미 transcript json이 있으면 재처리하지 않음
- 타입 힌트와 에러 처리 포함
이 단계에서 중간 검사를 하는 게 안전할 것 같다.
이 단계를 검사하려면 어떻게 할 까?
.venv/bin/python ingest.py data/audio/A301_중간.mp3
수행 결과 data/transcripts/A301_중간.json 파일이 생성되면 검사는 OK
이제 3번째 프롬프트 실행이다.
3단계: chunking
이제 transcript JSON을 chunk로 나누는 기능을 구현해줘.
대상 파일:
- src/chunking.py
요구사항:
- transcript JSON을 읽어서 chunk 목록 생성
- chunk 크기 기본값 700 tokens
- overlap 기본값 100 tokens
- 각 chunk에는 다음 metadata 포함:
- source_file
- chunk_id
- start_time
- end_time
- timestamp가 없으면 start_time/end_time은 null
- LlamaIndex Document로 변환하는 함수 포함
이것도 결과 검사를 하면 좋겠다.
이 기능을 어떻게 검증하면 좋을까?
chunk 생성 테스트
python -B -c "import sys; sys.path.insert(0, 'src'); import chunking; chunks = chunking.chunk_transcript_json('data/transcripts/A301_중간.json'); print(len(chunks)); print(chunks[0]['metadata']); print(chunks[0]['text'][:300])"예상 출력은 대략 이런 형태입니다:
{'source_file': 'data/audio/A301_중간.mp3', 'chunk_id': 'A301_중간-0000', 'start_time': 0.0, 'end_time': 427.0} 저희는 지능형 영업관리를 위한 ...LlamaIndex Document 변환 테스트
python -B -c "import sys; sys.path.insert(0, 'src'); import chunking; docs = chunking.transcript_json_to_documents('data/transcripts/A301_중간.json'); print(len(docs)); print(type(docs[0]).__name__); print(docs[0].metadata)"예상 출력:
Document {'source_file': 'data/audio/A301_중간.mp3', 'chunk_id': 'A301_중간-0000', 'start_time': 0.0, 'end_time': 427.0}chunk 크기를 바꿔 보고 싶으면:
python -B -c "import sys; sys.path.insert(0, 'src'); import chunking; chunks = chunking.chunk_transcript_json('data/transcripts/A301_중간.json', chunk_size=300, overlap=50); print(len(chunks)); [print(c['metadata']) for c in chunks]"
검사를 수행하고 결과를 눈으로 확인한 후 이제 네번째 프롬프트를 실행시킨다.
4단계: 벡터 인덱싱
이제 LlamaIndex와 ChromaDB를 연결해서 벡터 인덱싱을 구현해줘.
대상 파일:
- src/indexer.py
요구사항:
- data/chroma/에 Chroma persistent index 저장
- embedding model은 text-embedding-3-small 기본값
- transcript chunk를 LlamaIndex Document로 받아 인덱싱
- 이미 인덱싱된 source_file은 중복 인덱싱하지 않도록 처리
- index 로드 함수와 index 생성/추가 함수 분리
- 타입 힌트와 에러 처리 포함
지금 단계에서 따로 검사할 부분은 없어 보인다. 다음단계 프롬프트를 실행시킨다
5단계 : RAG검색 기능
이제 질문 검색 기능을 구현해줘.
대상 파일:
- src/query.py
- ask.py
요구사항:
- 저장된 Chroma index를 로드
- 사용자의 질문을 받아 LlamaIndex query engine으로 답변 생성
- LLM 기본값은 gpt-4o-mini
- 답변과 source nodes 3~5개를 출력
- source node마다 text, source_file, start_time, end_time 표시
- CLI 실행 예: python ask.py “지난 회의에서 결정된 내용은?”
테스트로 CLI 검색하고 눈으로 결과를 확인한다.
이제 여섯번째 프롬프트를 실행한다
6단계: UI구현
이제 Streamlit UI를 구현해줘.
대상 파일:
- app.py
요구사항:
- data/audio/ 폴더의 mp3, wav, m4a 파일 목록 표시
- 파일 선택 후 “텍스트화 및 인덱싱” 버튼
- 처리 상태 표시
- 질문 입력창
- 답변 출력
- 관련 transcript 조각 3~5개 표시
- 각 조각에는 source_file, start_time, end_time, text 표시
- FastAPI 같은 서버는 사용하지 말 것
여기까지 만들었다면, 다음과 비슷한 구조로 되어있을 것이다.
.
├── README.md
├── app.py
├── ask.py
├── data
│ ├── audio
│ │ └── A301_중간.mp3
│ ├── chroma
│ │ └── chroma.sqlite3
│ └── transcripts
│ └── A301_중간.json
├── ingest.py
├── requirements.txt
├── src
...<중략>
├── storage
│ └── chroma
└── tests
19 directories, 46 files
녹음 파일은 data디렉토리 및 audio 및에 넣어놓고 streamlit 을 실행시키면 로컬에서 이를 볼 수 있다.
> streamlit run app.py
화면은 잘 뜬다. 다른 녹음 파일을 audio폴더에 복사하고 페이지를 리로드하면 Audio파일 목록에 반영된다.

그런데 A304_중간.mp3 파일을 ‘텍스트화 및 인덱싱’을 한 후 Search를 하는데 Search대상이 계속 A301_중간.mp3의 내용이 검색되는 오류가 있었다. 이의 수정을 보강하자.
mp3 파일을 새로 data/audio파일에 추가하고 이를 인덱싱을 app.py를 통해 했어.. 그리고 질문을 던졌는데 계속 transcripts는 예전 것을 읽는 것 같어… 이를 선택된 오디오 파일에 맞게 transcripts를 변경하여 검색하는 걸로 바꿔줘
실행해 보니 이제 제대로 검색대상이 바뀌어 나온다.

여기서 이제 코드의 마무리 정리를 위한 마지막 프롬프트를 실행한다.
7단계: 코드 정리
전체 코드를 점검하고 실행 가능하게 수정해줘.
확인할 것:
- import 경로 오류
- requirements 누락
- .env 로딩
- data/audio, data/transcripts, data/chroma 폴더 자동 생성
- 중복 인덱싱 방지
- README 실행 방법 업데이트
가능하면 최소 테스트용 mock transcript도 하나 추가해줘.
이렇게 해서 나만의 회의 녹취록 관리 시스템 하나를 만들었다.
여기에서 눈여겨 볼 점은 하나의 프롬프트에 너무 많은 요구사항을 구겨넣기 보다는 단계별로 나누어 넣고 중간중간에 확인이 가능한 부분은 실행하면서 잘 진행되는지 여부를 체크해야 한다는 것이다.