Skip to content

Latest commit

 

History

History
261 lines (195 loc) · 23.1 KB

File metadata and controls

261 lines (195 loc) · 23.1 KB

B-BOT AX 고도화 계획서

BCSD Lab Slack 봇(삐봇)을 "조직 지식 인터페이스 + 온보딩 도우미 + 운영 자동화"로 고도화하기 위한 계획서. 작성일: 2026-06-10


1. 개요

1.1 배경

  • 삐봇은 현재 재미 명령(주사위·룰렛·점메추 등), CI/CD 알림 웹훅(배포·PR·에러), Google Meet 생성, 그룹 멘션 등을 제공하는 Slack 봇이다.
  • 다음과 같은 조직 문제가 누적되어 있다:
    1. 신입 온보딩 난항 — 레포에 README·.env.example이 없어 환경 변수 위치·로컬 셋업을 신입이 스스로 알 수 없다.
    2. 지식 산재 — 개발/도메인 지식이 GitHub 레포, Notion(회의록·인수인계), Google Drive(문서)에 흩어져 있다.
    3. 멤버 DB 최신화 중단 — 봇이 읽는 멤버 데이터는 인터널 프로젝트(프론트/백엔드)의 DB인데, 새 DB로 분리하려다 멈춰 최신화가 되지 않는 상태다.
    4. 스택 불안정 — 소켓 모드 ↔ HTTP 웹훅 사이를 오가며 연결 미응답 등 churn이 반복됐다.

1.2 목적

봇에 **AI(RAG·자연어 처리)**를 결합하여,

  • 신입이 자연어로 셋업·도메인 질문을 해결하고,
  • 흩어진 조직 지식을 한 곳에서 검색하며,
  • 운영(회의록·주간 요약)을 자동화하고, (에러 트리아지·PR 요약은 보류 — §6, 기능명세서 D-2·D-3)
  • 멤버 관리 등 일부 작업을 자연어로 수행할 수 있게 하며,
  • 멤버 데이터를 봇 Postgres로 일원화하고 Nuxt 인터널 어드민(반납처) 으로 관리한다.

1.3 목표(Goals)

  • G1. 신입 온보딩 자력화 (셋업 문서 + 온보딩 Q&A 봇)
  • G2. 조직 지식 통합 검색 (GitHub/Notion/Drive → 단일 RAG)
  • G3. 운영 자동화 (회의록/주간 요약 ※ 에러 트리아지·PR 요약은 보류)
  • G4. 멤버 데이터 소유권 정리 및 자연어 관리 + Nuxt 어드민(반납처)
  • G5. 스택·인프라 안정화 (HTTP 웹훅 확정, DB 완성)

1.4 비목표(Non-goals)

  • 비밀(API key·AWS/DB 자격증명)을 봇/RAG가 다루지 않는다 → 비밀 관리자 영역.
  • 스프레드시트의 구조화 데이터를 RAG에 임베딩하지 않는다 → 조회 도구 영역.
  • 멤버 외 PII를 자유 텍스트로 임베딩하지 않는다.
  • 권한 없는 문서를 인덱싱하지 않는다(초기: "전원 공개" 문서만).

2. 기술 스택 결정 (확정)

항목 결정 비고
프레임워크 Nitro 유지 마이그레이션 0, scheduledTasks가 RAG 동기화에 적합
Slack 연동 HTTP 웹훅으로 확정 소켓 모드 폐기 (연결 churn 종결). CI/CD·커넥터 웹훅 수신에도 HTTP 서버 필수
임베딩 Ollama bge-m3 (HTTP 사이드카) 한국어 검색 강함, 로컬·외부 유출 없음
음성인식 whisper.cpp 서버 (HTTP 사이드카) 회의록용, 배치
생성(LLM) 소형 로컬 LLM (Qwen2.5 3B/1.5B / EXAONE, Ollama) — 현재 B안 서버 축소(2코어/12GB)로 7B 동기 생성이 비현실적 → 소형 모델로 $0 유지. 품질·속도 부족분은 추후 A(생성만 Haiku) / C(하이브리드) 로 전환 (§5.1·§7)
DB PostgreSQL + pgvector 관계형 + 벡터 단일 DB. 멈춘 "새 DB"를 이걸로 완성
비밀 관리 Infisical 등 비밀 관리자 API key·자격증명을 RAG/DB에서 분리
인프라 Oracle Cloud Ampere A1, 2 OCPU / 12GB (ARM) 봇 + Postgres + 사이드카 상주. 7B 생성엔 부족 → 소형 모델 + 추후 API 전환
인터널 어드민 Nuxt (TS 풀스택) — 추후(Phase 1 이후) 봇 Postgres 공유 GUI = 멤버 데이터 반납처. Nuxt 서버=Nitro라 봇 자산 재활용, 솔로 풀스택 적합 (§6)

모델은 코드베이스에 Python을 들이지 않고 HTTP 사이드카(Ollama·whisper.cpp)로 두고 Nitro에서 $fetch로 호출한다.


3. 아키텍처

                        ┌──────────────────────────────────────────┐
                        │            Nitro (TS) — HTTP             │
[Slack] ──events/cmd───▶│  routes: event / slash / interaction     │
[GitHub Actions]──webhook▶│  routes/api/*: deploy, PR, ...           │
                        │  scheduledTasks: RAG 동기화 cron         │
                        │  plugins: WebClient, DB pool 주입         │
                        └───┬───────────────┬──────────────┬───────┘
                            │               │              │
                   ┌────────▼──────┐ ┌──────▼──────┐ ┌─────▼────────────┐
                   │ Ollama        │ │ whisper.cpp │ │ 생성(LLM)        │
                   │ (bge-m3 임베딩 │ │ (음성인식)   │ │ 現: 소형 로컬(B) │
                   │  + 소형 생성)  │ │             │ │ 後: Haiku API(A/C)│
                   └────────┬──────┘ └─────────────┘ └──────────────────┘
                            │
                   ┌────────▼─────────────────────────────┐
                   │ PostgreSQL + pgvector                 │
                   │  - 벡터(임베딩), 메모리, 감사로그       │  ← 봇 고유
                   │  - 멤버(임시 주인, 인터널에서 씨앗)     │  ← 동기화/유지
                   │  - RAG 메타데이터, 동기화 커서          │
                   └───────────────▲───────────────────────┘
                                   │ 같은 DB 공유(별도 DB 아님)
                   ┌───────────────┴───────────────────────┐
                   │ Nuxt 인터널 어드민 (추후, Phase 1 이후) │
                   │  멤버 일괄관리 · 감사로그 · 동기화 현황   │  ← 반납처
                   └───────────────────────────────────────┘
            (Oracle Ampere A1 단일 박스에 전부 상주)

[지식 소스]
  GitHub 레포(코인 생태계 + BCSD 사이트들) ──push 웹훅──┐
  Notion(회의록·인수인계)               ──cron 폴링──┤──▶ 청킹 → bge-m3 → pgvector
  Google Drive(Docs)                    ──cron 폴링──┘     (project 메타데이터로 스코프)

[제외]
  Drive: API key·AWS/DB 자격증명 → 비밀 관리자 (RAG 금지)
  Drive: 인명부(PII) → 멤버 DB (구조화, 임베딩 금지)
  Drive: 스프레드시트 → 조회 도구 (임베딩 금지)

4. 데이터 전략

4.1 데이터 3분류

종류 처리 예시
🟢 인덱싱(RAG) 청킹 → 임베딩 → pgvector Notion 회의록·인수인계, GitHub 코드/README, 프로젝트 회의록
🟡 특별 취급 RAG 밖에서 인명부(→멤버DB), 스프레드시트(→조회도구)
🔴 절대 금지 비밀 관리자 프론트 API key, Google/DB/AWS 자격증명

4.2 멤버 데이터: "임시 주인" 전략

  • 멤버 데이터의 미래 주인은 (재구축 예정인) 인터널 프로젝트지만, 그 완성을 기다릴 수 없다.

  • 따라서 봇이 임시 source of truth가 된다:

    1. 현재 인터널 DB의 멤버 데이터를 봇 Postgres로 1회 씨앗 복사 (이때 stale 데이터를 검수·정리 → 사실상 최신화의 출발점). → 기존 라즈베리파이(bcsd-mysql) 덤프를 로컬 backup/bcsdlab_*.sql로 확보 완료 (멤버 등 13개 테이블, PII 포함 → .gitignore).
    2. 봇의 멤버 조회 소스를 봇 Postgres로 전환 (인터널 DB 의존성 절단).
    3. 이후 봇에서 멤버를 유지 → 자연어 등급변경 기능이 안전해짐.
    4. 반납 대비: 깔끔한 스키마 + 감사 로그 + export 경로 유지. 인터널 부활 시 import/동기화로 반납.
  • 인터널 API 의존도 절단: 봇의 PR 스레드 기능이 INTERAL_BASE_URL(인터널 백엔드 API)에 위임 중이었음 → thread 테이블을 봇 Postgres로 흡수하고 해당 엔드포인트를 봇 라우트로 재구현(기능명세 B-1.1). DB뿐 아니라 API 의존까지 끊는다.

  • 이관 범위(확정, 기능명세 B-1.1): 디렉터리(member·track·team·team_map·member_withdraw) + pr_thread 이관 / dues·jobs·slack 아카이브는 보류(아카이브 보존) / 예약은 폐기.

반납처 = Nuxt 인터널 어드민 (별도 DB 아님, 봇 Postgres 공유). 과거 인터널이 멈춘 원인이 "새 DB로 갈라치기"였으므로, 어드민은 새 DB를 만들지 않고 봇이 임시 주인인 그 Postgres 위에 GUI만 얹는다. 봇=빠른 1건(자연어), 웹=일괄·검수·대시보드로 역할 분담하며 같은 audit_log에 기록 → drift 없음. 그러면 "반납"은 사실상 주인 이름표만 바꾸는 일이 된다.

4.3 비밀 분리 (선행 보안 작업)

  • Drive에 평문으로 있는 자격증명을 비밀 관리자(Infisical 등)로 이전하고 RAG/DB에서 영구 제외.
  • 봇은 위치만 안내(예: "AWS 키는 koin/prod에 있음"), 값은 절대 노출하지 않는다.

4.4 지식 소스 권한 정책

  • 초기에는 "BCSD 전원이 볼 수 있는 문서만" 수집. 제한 문서는 인덱싱 제외.
  • 사용자별 접근 필터링은 초기 범위에서 제외(작업량 과다).
  • 답변에는 항상 출처(원본 URL)를 함께 제공 → 검증성 + 최신성 보완.

4.5 지식 품질 — 메타데이터·큐레이션 (OKF 차용, Phase 2 마무리 후)

  • 실사용에서 드러난 문제: 커넥터가 회의록·작업기록을 대량 수집하면 온보딩 how-to 질문에 노이즈가 섞인다(Notion 트리 크롤 시 확인).
  • Google의 OKF(Open Knowledge Format) = "YAML 프론트매터 마크다운 디렉토리" 표준에서 형식 채택이 아니라 아이디어만 차용:
    • A. 청크 메타데이터 doc_type/tags/updated_at — 문서 종류(readme/onboarding/회의록/runbook/api-spec)·수정일을 document_chunk에 부여 → 타입 인식 검색(회의록 down-rank, onboarding·runbook 우선)으로 노이즈 억제 + 최신성 신호. 무저작, 우선.
    • B. 큐레이션 지식 번들 — 봇 레포 knowledge/(프론트매터 마크다운)에 핵심 정답(온보딩·컨벤션·FAQ)을 PR로 관리하는 1급 소스. 흩어진 수천 페이지보다 소수 고신호 문서가 검색에서 이기게 가중. 사람 저작(LLM 초안+검수), A 이후 빈칸만 점진적.
  • 비채택: 파이프라인을 OKF 규격으로 재작성(신생 v0.1 + 우리는 ingest형이라 ROI 낮음).

5. AI 적용 원칙

AI는 다음 두 종류의 작업에만 쓴다. 그 외(DB·웹훅·검색·권한·비밀)는 전부 코드.

  1. 읽고 답하기(읽기) — RAG Q&A, 요약, 에러 분석
  2. 알아듣기(번역) — 자연어 명령을 구조화된 도구 호출로 변환

쓰기/행동(예: 등급 변경)은 AI가 의도만 추출하고 검증·권한·confirm·실행·감사는 코드가 한다. AI에 raw DB 권한을 주지 않으며, 정해진 소수의 도구만 노출한다.

5.1 성능 정책 (로컬 속도) — "일단 명시, 실사용 후 수정"

  • 서버가 2코어/12GB로 축소되어 7B 동기 생성(~2 tok/s + prefill)은 비현실적 → 현재 생성은 소형 로컬(3B/1.5B, B안) 으로 $0 유지.
  • 지연 민감(동기, 불편 가능): D-1 온보딩 RAG Q&A(최우선), E-1 명령 라우팅, E-2 멤버 의도 추출, E-3 메모리 회수. → 소형 모델로 응답 속도는 확보되나 한국어 합성 품질은 하락 가능.
  • 지연 무관(비동기/배치, 무난): D-4 회의록 요약, D-5 주간 요약, E-3 메모리 저장.
  • 임베딩(bge-m3)·STT(whisper)는 로컬 유지 — 오프라인 배치라 2코어에서도 무난, 민감정보 외부 유출 없음.
  • 전환 경로: 모델 추상화 레이어('local' | 'sonnet')를 통해 백엔드 교체는 baseURL+modelId 한 줄.
    • B(현재): 소형 로컬, $0.
    • A: 사용자 대면 생성만 Haiku 4.5 API(프롬프트 캐싱 시 ~0.1×). 단, 생성 컨텍스트가 외부로 나가므로 "🟢 전원 공개 문서만 인덱싱" 경계 엄수.
    • C: 비동기(D-4·D-5)=로컬, 대면(D-1·E-*)=Haiku 하이브리드.
  • 정책: 지금은 B로 완성 → 실제 사용 → 체감/측정 → 품질·속도 부족 시 A/C로 전환. 선최적화 금지.

6. 단계별 로드맵

Phase 내용 AI 산출물
0. 기반 정비 .env.example, README, setup 스크립트, env 검증, CLAUDE.md / Slack HTTP 웹훅 확정 온보딩 문서, 안정화
1. DB·보안 Postgres+pgvector 셋업, 멤버 1회 마이그레이션+검수, 읽기 소스 전환, 비밀 분리(Infisical) 봇 고유 DB 완성
2. 지식+첫 AI 커넥터(GitHub/Notion/Drive 동기화, cron), 온보딩 RAG Q&A PoC 첫 AI 기능
3. 운영 자동화 회의록 요약(Whisper), 주간 요약 (※ PR 리뷰 요약·에러 트리아지는 보류 — 기능명세서 D-2·D-3) 운영 봇
4. 자연어 행동 NL 명령 라우팅, NL 멤버 관리(쓰기, authz/confirm/audit) 액션 봇

우선 PoC: 온보딩 RAG (가치 크고 위험 적음, 벡터 인프라 위에 자연 연결).

현황 (2026-06-21)

  • Phase 0·1 완료 — Slack HTTP 웹훅 확정, Oracle Postgres+pgvector, 멤버 마이그레이션, mysql2→pg, 비밀은 .env 단일 소스.
  • Phase 2 + 첫 AI 완료 🎉 — 커넥터 3종 + 온보딩 RAG Q&A. 코퍼스 ~33K 청크:
    • GitHub(17 레포 README + /languages, 89청크), ✅ Notion(트리 크롤 + 접근가능 DB 자동발견(B) + 증분 색인, 30,954청크/4,183문서 — 에러리포트·명세 등 DB 포함), ✅ Google Drive(폴더 스코프 16개, 2,134청크), ✅ 온보딩 RAG Q&A(!질문 스트리밍·출처)
    • Notion 접근 모델: 페이지 연결이 하위 DB로 상속 → 영역만 연결하면 그 안 DB 자동 색인(PII 영역 회원관리는 미연결로 보호)
  • 검색 품질: 프로젝트 라우팅+사후보정, 헤딩 인식 청킹, 타입 재랭킹(C-5) + 최신성 가중치(반감기 180일·최대+0.05, 같은 타입이면 최신 우선), 출처 4개 제한, 링크 환각 금지, TOP_K 5(prefill↓). (기능명세 D-1·C-5)
  • 운영 안정화: Notion 증분 색인(매주 last_edited Search로 변경분만 → 3~5h→수 초, 매월 첫 월요일만 full) + 삭제 안전화(불완전 크롤 시 오삭제 방지, 증분은 삭제 안 함) + 일시에러 재시도. 1회 코퍼스 손실 사고 → 복구 완료.
  • SDK·신모델: Notion 커넥터를 공식 @notionhq/client v5 + 신모델(data_sources)로 전환 — search(object=data_source)dataSources.query. full 재색인 완료(scanned 9,729 / errors 0 / 신규청크 0 = 692 data_source는 새 페이지 아님). Client timeoutMs 필수. 풀 크롤 end-to-end 검증.
  • 다음: Phase 3(회의록·주간 요약) / Phase 4(자연어 행동) / Nuxt 어드민. (선택: C-5 튜닝, C-6 큐레이션 번들)

6.1 봇 vs 인터널 어드민 — 착수 순서

  • 봇이 먼저, 페이지는 그 위에. 페이지는 봇 Postgres에 얹는 GUI라 DB 토대가 의존성이다. 토대가 흔들리는데 UI를 짜면 스키마 변경마다 프론트 재작업 → 솔로에겐 이중고.
  • 순서: 봇 Phase 0(안정화)Phase 1(Postgres·멤버 시드, 스키마 확정)여기서 Nuxt 어드민 착수. RAG(Phase 2~4)는 페이지를 막지 않으므로 이후 병렬 진행.
  • ⚠️ 페이지를 DB 이전보다 먼저 시작하지 않는다 (과거 인터널 중단 패턴의 재현 방지). 단 멤버 스키마가 굳는 Phase 1 직후면 어드민은 RAG를 기다리지 않고 독립적으로 가치 있음.

6.2 인터널 어드민(Nuxt) — 재활용·구조

  • Nuxt 선택 이유: Nuxt의 서버 엔진이 봇과 같은 Nitro. 서버 라우팅·플러그인 주입·useStorage KV·scheduledTasks 패턴을 그대로 재활용. 새로 짜는 건 Vue 화면뿐.
  • 재활용 대상: DB 어댑터(드라이버만 mysql2→pg), member 도메인 타입·쿼리, context 주입 플러그인 패턴, Slack WebClient(알림·OAuth 로그인).
  • 공유 구조(단계적 모노레포 승격): 지금은 봇 단독 repo 유지 → 어드민 착수 시점packages/core(db·member 타입·도메인 쿼리)를 뽑아 pnpm workspace 모노레포(apps/bot + apps/admin)로 승격. Turborepo/Nx는 솔로+2앱엔 오버킬. 모노레포여도 배포는 앱별 별도 프로세스.
  • 참고(기존 인터널): BCSD_INTERNAL_WEB는 React+MUI 프론트엔드 SPA로, 멈춘 건 백엔드였다(프론트는 멀쩡). 화면(Member/Role/Team/Track/회비/예약/엑셀) 구성을 새 Nuxt 어드민의 참고로 활용. 원래 인증은 학번+비번(SHA256)+JWT였으나 아래로 교체한다.

6.2.1 인증·인가 (확정)

  • 인증(authn) = Slack OAuth ("Slack으로 로그인"). Slack OIDC가 반환한 slack user id를 member.slack_id로 조회 → 매칭 & is_active면 입장, 없으면 거부. 비밀번호 없음(회원가입·비번찾기·해시저장 불필요) → password 컬럼 미이관과 일관. 봇의 기존 Slack 앱 재활용.
  • 인가(authz) = DB authority 컬럼(ADMIN/MANAGER/NORMAL). 예: ADMIN·MANAGER만 편집, NORMAL은 조회. 코드 레벨 미들웨어 체크로 시작, SpiceDB(Pi 상주)는 규칙이 복잡해질 때만 도입.
  • 세션 = 서버 세션 + httpOnly 쿠키(기존 JWT-localStorage 대신, XSS 방어). nuxt-auth-utils 등.
  • 부트스트랩: 현재 활동멤버에 ADMIN이 없음(MANAGER 9·NORMAL 36) → 시드 시 본인을 authority=ADMIN 지정.
  • 데이터 선결: 활동멤버 4명 slack_id 결측 → Slack 로그인 위해 보완 필요(검수 정리 항목).
  • 대안 비교: Google OAuth는 깔끔한 매핑 키(slack_id) 부재로 열위, 학번+비번은 보안 부담으로 폐기.

7. 비용 추정

현재 목표: 정기 비용 $0 (B안 — 소형 로컬). 동아리 예산 제약상 돈 대신 일부 품질을 양보한다. 2코어/12GB 축소로 7B는 포기하고 3B/1.5B 소형 모델로 $0를 유지한다.

  • 임베딩·검색·음성인식·소형 생성 모두 Oracle 박스(무료)에서 로컬 처리 → 정기 API 비용 $0.
  • 대가(정직): 소형 모델이라 한국어 RAG 합성 품질↓. 박스 CPU 경합 → whisper 배치와 생성 시간대 분리.
  • 단일 추상화 레이어('local' | 'sonnet')로 B → A/C 전환은 한 줄.
방식 월 비용 트레이드오프 상태
B. 소형 로컬 $0 한국어 합성 품질↓ ✅ 현재
A. 대면 생성만 Haiku $13 품질↑·속도↑, 공개문서만 인덱싱 경계 엄수 추후 후보
C. 하이브리드(비동기 로컬) $02 A·B 절충 추후 후보

※ Haiku 4.5 = $1/$5 per MTok(입력/출력), 동아리 트래픽 소량 + 프롬프트 캐싱(~0.1×) 가정 시 A/C도 월 몇 달러 수준. ※ 유료 의존이 컸던 PR 리뷰 요약은 보류(기능명세서 D-3)라 B안 $0가 깔끔히 성립한다.


8. 리스크 및 대응

리스크 대응
지식베이스 stale → 잘못된 답 push 웹훅 + cron 동기화, 출처(라이브 링크) 병기
비밀 유출(RAG에 자격증명) 비밀 분리를 Phase 1 선행, RAG 인덱싱 제외
멤버 데이터 이중 소스/drift 봇을 단일 임시 주인으로, 감사 로그, 반납 설계
로컬 모델 속도/품질(2코어/12GB) 7B 불가 → 소형 모델(B) 로 속도 확보, 품질은 §5.1 정책대로 실사용 후 A/C 전환. 비동기는 로컬 유지
Slack 연결 불안정 HTTP 웹훅 단일 모드 확정
자연어 쓰기 오작동 authz + confirm 버튼 + 검증 + 감사 로그, 좁은 도구셋
스프레드시트 RAG 부적합 임베딩 대신 조회 도구로 분리

9. 용어

  • RAG: 검색 증강 생성. 문서를 검색해 그 내용 기반으로 답변 생성.
  • 임베딩/벡터: 문서를 의미 벡터로 변환, 유사도로 검색.
  • 사이드카: 메인 앱과 별개로 같은 박스에서 도는 HTTP 서비스(Ollama·whisper).
  • source of truth: 어떤 데이터의 유일한 권위 출처.
  • authz: 권한 인가(누가 그 작업을 할 수 있는가).