작성일: 2026-05-18 결정자: ryan@brain-crew.com 상태: v4 → v4.1. 두 가지 큰 정정 — (a) Discord 를 기둥 에서 Phase 1 인터페이스 로 격하, (b) 시멘틱 federation (git-like 분기) 을 4번째 ★ 패턴으로 추가. 전제: 기존 레포 wipe 후 백지에서 다시 작성.
Vanna AI(~20k), Wren AI(~12k), SQLCoder(~3.8k) 같은 Text-to-SQL 오픈소스들은 질문→SQL 파이프라인 자체 는 이미 완성도가 높음. 우리는 그 위에 무엇을 더 얹을 것인가가 정체성 결정.
기존 오픈소스의 공통 약점:
| 약점 | 누구의 문제 | 기존 처리 |
|---|---|---|
| DB 정보 완성도 낮으면 성능 급락 | 사용자가 description/sample 없이 등록 | Vanna: 학습 데이터 품질에 전적 의존 |
| 이전 대화·정의 못 기억 | 매번 새 세션처럼 동작 | 대부분 stateless |
| 비즈니스 맥락은 사람이 직접 정의 | metric·용어집을 일일이 입력 | Wren: MDL 수동 작성 |
| 단일 진실 (single truth) 모델의 조직 충돌 | 같은 회사라도 팀별로 "활성 사용자" 정의가 다름. 한 팀이 바꾸면 다른 팀 깨짐 | Wren: 단일 MDL. Vanna: 학습 데이터 혼선 |
이 네 가지가 실무에서 매우 중요 한데 비즈니스마다 다르기 때문에 정량 평가가 어려운 영역. 그래서 오픈소스가 잘 안 건드림. 우리는 이 네 가지를 한 번에 다루는 방향 으로 차별화.
"문서로 비즈니스 맥락을 학습하고, 팀별로 시멘틱이 분기되고, 불완전한 DB에서도 답하고, 모든 정의·대화를 기억하는 오픈소스 분석 에이전트."
기존 SQL 봇이 "질문하면 SQL 만들어 줄게" 였다면, 우리는 "우리 회사 문서 넣어줘 → 학습할게 → 팀별로 다른 정의도 같이 들고 있을게 → 너희가 묻고 답을 받자 → 다 기억하고 다음에도 적용할게".
Discord 는 Phase 1 인터페이스. Slack/Web/Teams 도 같은 코어 위에서 어댑터로 추가 가능. Discord 자체 는 정체성이 아니라 멀티 인터페이스 의 첫 구현.
| 기둥 | 정체성 | 차별점 |
|---|---|---|
| ① 비즈니스 맥락 학습 | 문서→시멘틱 자동 추출 + 사용자 confirm | Wren MDL 은 수동 작성. 우리는 문서가 곧 진실의 출처 |
| ② 강건성 (2축) | (2a) DB 강건성 — 불완전 메타데이터 자동 보강 (2b) 시멘틱 강건성 — 팀별 다른 정의 federation | 기존 오픈소스 없는 영역. 우리의 한 포인트 |
| ③ Hermes 기억 | conversation + facts + preferences 영속 | 대부분 stateless |
| ④ 멀티 인터페이스 | Phase 1: Discord, Phase 2: Slack, Phase 3: Web | Discord는 어댑터 하나, lock-in 없음 |
기둥 ②가 우리 제품·연구 정체성. 두 종류의 강건성 이 핵심:
- (2a) DB 강건성 — 사용자가 description/sample query 없이 등록해도 성능 하락 최소화
- (2b) 시멘틱 강건성 — 팀별 정의 차이를 충돌 없이 공존 (git-like 브랜칭)
V1엔 골격만, V1.5/V2에서 깊이 채움.
- 무결성 + 권한 — DB가 변하지 않고, 권한 있는 데이터만 본다
- 의미적 정확성 — 답이 진짜로 맞는다
- UX 단순성 — 한 번에 결과 (멘션→thread, 또는 다른 frontend 의 동치 패턴)
- 맥락 보존 — 끊겨도 이어진다 (Hermes 영속화)
USER (Phase 1: Discord. Phase 2+: Slack/Web/Teams)
│
▼
┌─────────────────────────────────────────────────────────┐
│ ★ Frontend 어댑터 (인터페이스 분리) │
│ │
│ discord/ ──┐ │
│ slack/ ──┤ 각 어댑터가 공통 인터페이스 구현 │
│ web/ ──┘ - 입력 받기 │
│ - 출력 보내기 │
│ - 세션 키 결정 │
└──────────────────┬──────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────┐
│ MEMORY + CONCIERGE │
│ │
│ 기억 (Memory) — 3축 분리 │
│ • 어디 저장하는가 │
│ • 무엇을 가져올지 │
│ • 어떻게 새로 만드는지 │
│ │
│ 문서 흡수 (Ingestion) — 출처 × 해석 │
│ • 어디서 문서를 받는가 (파일/URL/Notion ...) │
│ • 어떻게 metric/rule 을 뽑아낼지 │
│ │
│ 시멘틱 federation — scope 결정 │
│ • 이 대화가 어느 scope 인가 (DM/채널/스레드) │
│ • 같은 용어 다른 정의 시 어느 게 우선인가 │
│ │
│ ContextConcierge — 위 셋을 묶어 ctx 조립 │
└──────────────────┬──────────────────────────────────────┘
▼ ctx
┌─────────────────────────────────────────────────────────┐
│ ★ HARNESS (조립된 단위, 하나의 덩어리) │
│ │
│ ctx 안에 있는 것: │
│ • LLM 클라이언트 │
│ • 도구들 (run_sql, ingest_doc, ...) │
│ • semantic (scope-aware) │
│ • session (영속 conversation + facts) │
│ • safety pipeline │
│ • DB explorer │
│ • audit logger │
│ │
│ agent_loop: │
│ 1. system prompt 만들기 │
│ - 현재 scope 의 effective semantic 주입 │
│ - 관련 facts 주입 │
│ - DB schema 주입 │
│ 2. LLM 호출 │
│ 3. 도구 호출 결과 모으기 │
│ 4. 다음 턴 또는 종료 │
│ │
│ run_sql 도구 내부: │
│ ★ safety pipeline 통과 후 실행 │
│ layer 1 → 2 → 3 → ... │
└──────────────────┬──────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────┐
│ OUTBOUND ADAPTERS │
│ LLM · DB (asyncpg) · Storage (encrypted SQLite) │
└─────────────────────────────────────────────────────────┘
★ 4개가 확장 핵심 — Chap 3에서 자세히.
lang2sql/
├── README.md
├── pyproject.toml
│
├── src/lang2sql/
│ ├── core/ # 순수 타입 + 포트(추상)
│ │ ├── types.py
│ │ ├── identity.py
│ │ └── ports/
│ │ ├── llm.py
│ │ ├── explorer.py
│ │ ├── tool.py
│ │ ├── secrets.py
│ │ ├── audit.py
│ │ ├── session_store.py
│ │ ├── frontend.py ← NEW (Phase 분리)
│ │ ├── safety.py ← Safety layer
│ │ ├── memory.py ← Memory 3축
│ │ ├── ingestion.py ← Document source × extractor
│ │ └── semantic_scope.py ← NEW (Federation)
│ │
│ ├── harness/ # 조립된 단위
│ │ ├── context.py
│ │ ├── session.py
│ │ ├── loop.py
│ │ ├── system_prompt.py
│ │ └── tool_registry.py
│ │
│ ├── semantic/ # 도메인 모델 + scope 지원
│ │ ├── types.py # Metric/Dimension/Relationship/Rule
│ │ ├── layer.py
│ │ ├── scoped_layer.py ← NEW (git-like 분기)
│ │ ├── sql_composer.py
│ │ └── store.py
│ │
│ ├── safety/ # ★ Safety pipeline (체인)
│ │ ├── pipeline.py
│ │ └── layers/
│ │
│ ├── memory/ # ★ 3축 분리
│ │ ├── service.py
│ │ ├── stores/
│ │ ├── recall/
│ │ └── extractors/
│ │
│ ├── ingestion/ # ★ 출처 × 해석 매트릭스
│ │ ├── pipeline.py
│ │ ├── sources/
│ │ └── extractors/
│ │
│ ├── tools/ # ctx-aware
│ │ ├── run_sql.py
│ │ ├── explore_schema.py
│ │ ├── define_metric.py # scope 인지
│ │ ├── ingest_doc.py
│ │ ├── remember.py
│ │ └── ask_user.py
│ │
│ ├── tenancy/
│ │ ├── concierge.py
│ │ └── encrypted_secrets.py
│ │
│ ├── frontends/ # ★ 인터페이스 분리
│ │ ├── discord/ # Phase 1
│ │ ├── slack/ # Phase 2 (디렉토리만 비워둠)
│ │ ├── web/ # Phase 3
│ │ └── cli/ # 개발 도구
│ │
│ └── adapters/ # outbound
│ ├── llm/openai_.py
│ ├── db/postgres_explorer.py
│ └── storage/encrypted_sqlite.py
│
├── tests/
└── docs/
V1엔 가장 단순한 1개씩만 구현, V1.5/V2에서 어댑터를 추가 하는 방식으로 확장. 핵심 가치: V1엔 단순 구현으로 동작하지만 추상은 박혀 있어서 새 구현을 끼울 때 기존 코드 안 건드림.
비유로 풀면 — 콘센트와 가전제품. V1엔 LED 전구 하나만 꽂혀 있어도, 콘센트 규격이 표준이라 V1.5에 선풍기·스마트조명 그냥 꽂으면 됨. 콘센트 자체를 다시 만들 일 없음.
| ★ | 이름 | 역할 |
|---|---|---|
| ① | Safety pipeline | DB 강건성 — 검사 layer 줄 에 SQL 을 통과시킴, layer 추가식 확장 |
| ② | Memory service | Hermes 기억 — 어디 저장/무엇을 가져올지/어떻게 만들지 3축 독립 진화 |
| ③ | Ingestion pipeline | 문서 흡수 — 출처 × 해석 매트릭스로 입력 다양화 |
| ④ | Semantic federation | 시멘틱 강건성 — git-like 팀별 분기 |
SQL이 실행되기 전에 검사 layer 들의 줄 을 차례로 통과. 각 layer 는 통과/차단/사용자 확인 요청/SQL 수정 중 하나를 결정.
비유 — 공항 보안 검색대. 가방을 X-ray 통과 → 금속탐지 → 액체 검사 → 라벨 검사. 한 단계라도 막히면 통과 불가. 새 검사항목 (예: 화학물질 탐지) 추가는 기존 단계 그대로 둔 채 검색대 줄에 한 칸 끼우기.
| 버전 | 줄에 있는 layer |
|---|---|
| V1 (껍데기) | (1) Whitelist — SELECT/WITH 로 시작해야만 통과 (2) Timeout 설정 — 30초 |
| V1.5 | + AST 정밀 검증 (CTE 안 INSERT 도 잡음) + 위험 함수 차단 (pg_sleep 등) + LIMIT 자동 부착 + 5개 PG 설정 일괄 적용 + Rate limit + 메타데이터 자동 보강 (description 없는 컬럼 자동 생성) |
| V2 | + 비용 게이트 (EXPLAIN 으로 예상 비용 평가, 임계 초과 시 사용자 확인) + 엔진별 별도 pipeline (PG vs BigQuery) |
V1.5 의 메타데이터 자동 보강 layer 가 DB 강건성 차별점의 핵심. 사용자 DB에 description 빈 칸이면 LLM이 채워줌. 채워진 description 은 시멘틱 레이어로 흘러가 다음 질문에 활용. Vanna 가 학습 데이터 품질에 의존 하는 약점을 자동 보강 으로 메움.
새 layer 추가는 클래스 한 개 + 줄에 끼우기. run_sql 도구 코드 변경 0.
기억은 하나의 큰 상자 가 아니라 세 가지 독립 기능의 조합:
| 축 | 역할 |
|---|---|
| Store (저장소) | facts 를 어디에 보관하는가 (메모리/SQLite/PostgreSQL/Redis ...) |
| Recall (불러오기 전략) | 현재 질문에 어떤 facts 를 가져올지 (전부/키워드 매칭/벡터 유사도/하이브리드) |
| Extractor (새 fact 만들기) | 새 facts 를 어떻게 생성할지 (사용자 명시 명령만 / LLM이 대화에서 자동 추출) |
각 축이 독립적으로 진화. 예: Store 만 더 큰 DB로 교체 가능. Recall 만 더 똑똑하게 교체 가능.
| 버전 | Store | Recall | Extractor |
|---|---|---|---|
| V1 | 메모리 안 dict | 모든 facts 매번 주입 | /remember 명령만 |
| V1.5 | SQLite 영속 | 키워드 매칭으로 관련 facts 만 | LLM이 대화에서 반복 패턴 추출 |
| V2 | (같음) | 벡터 유사도 (embedding) | + 충돌 해결 (사용자가 X 했다가 not-X 시) |
| V2.5 | PostgreSQL (멀티 인스턴스) | 하이브리드 (키워드 + 벡터 + 최근성) | + 신뢰도 점수 |
V1엔 가장 단순한 조합 (메모리 + 전부 주입 + 수동) 으로 시작하되 3축 추상 은 박혀 있음. V1.5에 Store 만 SQLite 로 교체하는 게 어댑터 1개 추가 로 끝.
문서가 들어오면 두 단계:
| 단계 | 역할 |
|---|---|
| Source (출처) | 어디서 문서를 가져오는가 (파일 업로드/URL/Notion/Confluence/Google Drive) |
| Extractor (해석) | 문서 텍스트에서 metric/dimension/rule 후보를 어떻게 뽑아내는가 (LLM/DDL 파싱/하이브리드) |
Source 와 Extractor 가 매트릭스 라서 Source 1개 추가 = N 개 Extractor 와 자동 결합.
- 사용자가 Discord 에
/ingest와 함께 매출 정의 문서를 첨부 - 봇이 파일 내용을 가져와서 LLM 에 "이 문서에서 metric/dimension/rule 을 찾아줘" 라고 요청
- LLM 이 후보들을 추출 (예: "total_revenue = SUM(orders.amount) WHERE status != 'cancelled'")
- 봇이 Discord embed 로 사용자에게 보여줌:
- 📊 METRIC: total_revenue → SUM(orders.amount)
- 📋 RULE: exclude_cancelled (total_revenue 에 적용) → status != 'cancelled'
- [✅ 모두 등록] [개별 선택] [❌ 취소]
- 사용자 ✅
- 시멘틱 레이어에 등록 (출처 문서 ID 같이 보존)
- 이후 "이번 달 매출" 질문 시 자동으로 위 정의가 적용됨
| 버전 | Sources | Extractors |
|---|---|---|
| V1 | 파일 업로드 (MD/PDF/TXT) | LLM 추출 |
| V1.5 | + URL 가져오기 | + DDL 파일 직접 파싱 (schema 파일에서 자동 추출) |
| V2 | + Notion / Confluence MCP | + 하이브리드 (LLM + 규칙) |
| V2.5 | + GitHub Markdown / Google Drive MCP | + 청크 기반 RAG (긴 문서) |
V1.5엔 같은 문서를 다시 업로드하면 변경된 부분 diff 표시 → 사용자가 update/keep/remove 선택. 즉 문서가 곧 단일 진실 소스 패턴.
같은 회사라도 팀별로 같은 용어, 다른 의미 가 흔함:
| 용어 | 마케팅 | 프로덕트 | 파이낸스 |
|---|---|---|---|
| 활성 사용자 | 30일 내 로그인 | 7일 내 핵심 액션 | 유료 구독자 |
| 매출 | 광고 매출 | (관심 없음) | net (환불 차감) |
| 유저 | 가입자 | 활성자 | 결제자 |
기존 오픈소스는 단일 진실 (single truth) 모델:
- Wren MDL: 회사 단일 정의, 수동 유지 → 한 팀이 바꾸면 다른 팀 깨짐
- Vanna: 학습 데이터 혼선 → 의미 충돌이 RAG 결과에 섞임
결국 팀별로 자체 봇 운영 으로 갈라지거나, 모든 팀이 같은 정의에 합의 해야 함 (실현 불가).
builtin (시스템 기본 = 비어 있음)
│
▼
┌──────────────┐
│ main 브랜치 │ ← 회사 공통 시멘틱 (canonical)
│ (guild) │ admin 이 등록
└──────┬───────┘
│ 상속
┌───────────────┼───────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│#marketing│ │ #product │ │ #finance │
│ 채널 브랜치│ │ 채널 브랜치│ │ 채널 브랜치│
│ │ │ │ │ │
│active_ │ │active_ │ │revenue= │
│user=30d │ │user=7d │ │net │
└──────────┘ └──────────┘ └──────────┘
│
│ 상속
▼
┌──────────────────┐
│ thread 브랜치 │ ← 일시적 정의 (분석 1건 동안만)
│ │
│experiment_ │
│arm=treatment_b │
└──────────────────┘
사용자가 #marketing 채널에서 "활성 사용자 수" 라 물으면 봇은 시멘틱을 찾기 위해 아래에서 위로 순회:
- 현재 thread 안에 정의가 있는가? → 없음
#marketing채널에 정의가 있는가? → 있음 ("30일 내 로그인") → 여기서 멈춤
같은 사용자가 #product 채널에서 같은 질문:
- thread → 없음
#product채널 → 있음 ("7일 내 핵심 액션") → 여기서 멈춤
두 채널 모두 자기 정의를 사용. 충돌 없음 — 각자 자기 scope 에서 산다.
#marketing 채널에서 새 정의 등록:
- 사용자:
/define_metric active_user "30일 내 로그인" - 봇: ✅ 채널 scope 에 등록 (기본 동작)
- 효과:
#marketing에서만 적용. 다른 채널은 그대로.
#finance 채널에서 다른 정의 등록:
- 사용자:
/define_metric revenue "net (환불 제외)" - 봇: ✅ 채널 scope
admin 이 회사 공통 등록:
- admin:
/define_metric --guild revenue "gross + tax included" - 봇: ✅ main 브랜치 등록
- 효과: 다른 채널엔 영향, finance 채널엔 자체 override 가 우선
현재 scope 의 effective 시멘틱 조회:
- 사용자:
/semantic show - 봇: "이 채널에서 사용 중인 정의들:
- active_user (채널 override): 30일 내 로그인
- revenue (회사 main): gross + tax
- ..."
이 채널이 main 과 다른 점:
- 사용자:
/semantic diff main(V1.5 기능) - 봇: "이 채널이 main 과 다른 정의 1개: active_user 만 override"
채널 정의를 main 으로 승격 제안:
- 사용자:
/semantic promote active_user(V1.5 기능) - 봇: "admin 승인 요청 전송됨"
| 버전 | 기능 |
|---|---|
| V1 | 3-scope (guild / channel / thread) 자동 resolution. /define_metric 은 현재 scope 기본. /semantic show 로 effective layer 조회 |
| V1.5 | /semantic diff main, /semantic promote (admin 승인 flow), 충돌 알림 (admin 이 main 바꾸면 override 채널에 통지) |
| V2 | 외부 git 저장소 동기화 (semantic-as-code: .yaml 로 export/import), cross-guild template 공유 |
| V2.5 | branch fork & merge UI, 충돌 해결 flow, scope 별 audit (누가 언제 정의 바꿨나) |
이건 DB 강건성과는 다른 축의 강건성:
| 축 | 무엇에 견디는가 | 어떻게 |
|---|---|---|
| (2a) DB 강건성 | 불완전한 메타데이터 | Safety pipeline 의 메타데이터 보강 layer + auto description |
| (2b) 시멘틱 강건성 | 조직별 다른 비즈니스 정의 | Git-like scope chain (federation) |
둘 다 "현실의 messy함에 강건" 이라는 큰 우산. 둘 다 Vanna/Wren 이 못 하는 영역:
| Vanna | Wren | Lang2SQL | |
|---|---|---|---|
| DB 메타데이터 부족 시 동작 | ❌ 학습 데이터 의존 | ❌ MDL 사전 정의 필수 | ✅ 자동 보강 |
| 팀별 다른 정의 공존 | ❌ RAG 혼선 | ❌ 단일 MDL | ✅ Scope chain |
V1엔 추상만 박고 단순 구현. 코어 변경 없이 V1.5에 어댑터 추가:
- 추상 (port/interface) 들이 들어갈 위치:
core/ports/safety.py,memory.py,ingestion.py,semantic_scope.py,frontend.py - 각 추상의 V1 단순 구현 1~2개씩만 (Safety: layer 2개, Memory: 단순 3축 조합, Ingestion: 파일+LLM, Federation: SQLite 기반 3-scope)
추상 비용: 약 +400 LOC. V1.5/V2 절감: 약 1,500 LOC. 초기 투자가 빠르게 회수.
| 영역 | V1 |
|---|---|
| Frontend | Phase 1: Discord (DM + 채널 @bot 멘션→thread + thread reply) |
| 명령 | /connect, /ingest <파일>, /define_metric <name> "<def>", /remember "...", /semantic show, /audit me |
| LLM | OpenAI gpt-4.1-mini 단일 |
| DB | PostgreSQL only |
| 응답 | 텍스트 (≤50행) 또는 CSV 첨부 (>50행) |
| Safety | Safety pipeline + V1 layer 2개 (Whitelist + Timeout) |
| Memory | Memory service + V1 (메모리 + 전부 주입 + 수동) |
| Ingestion | 파일 업로드 + LLM 추출 |
| Semantic federation | 3-scope resolution (guild/channel/thread). /define_metric 현재 scope 기본. /semantic show 로 effective layer 조회. |
| 영속화 | secrets / audit / sessions / facts / semantic_entries → SQLite |
| 도구 | run_sql · explore_schema · ingest_doc · define_metric (scope 인지) · remember · ask_user |
| 호스팅 | Oracle Cloud Always Free 또는 fly.io free |
| 항목 | 미루는 곳 | 이유 |
|---|---|---|
run_code/write_code (Python 실행) |
영구 삭제 | read-only 약속을 무력화 |
| Discord-native 강조 패턴 (safety embed / metadata ask / reactions / #audit channel) | V1.5 | 사용자 워크플로우 다듬는 영역, 트라이얼 피드백 후 결정 |
| 비용 게이트 (EXPLAIN) | V1.5 | L1 + L0 timeout 1차 충분 |
| 위험 함수 차단 (pg_sleep 등) | V1.5 | Whitelist 가 1차 방어 |
| 메타데이터 자동 보강 (auto description) | V1.5 | DB 강건성 핵심 layer — V1엔 추상만, 구현은 V1.5 |
| Rate limit | V1.5 | 트라이얼 두 자릿수 |
| 자동 fact 추출 | V1.5 | 수동 /remember 로 시작 |
/semantic diff / /semantic promote |
V1.5 | 3-scope resolution 동작 검증 후 추가 |
| URL/Notion 문서 입력 | V1.5 / V2 | 파일 업로드 하나로 시작 |
| 벡터 유사도 recall | V2 | facts 적을 땐 효과 미미 |
| Persistent View / streaming / PNG 분할 | V1.7 | Discord SDK 깊은 영역 |
| Anthropic / NIM | V1.5 (검증 후) | OpenAI 하나로 시작 |
| Slack / Web 어댑터 | Phase 2/3 | 추상은 V1에 박힘 |
| Audit hash chain | V2 | append-only SQLite 1차 충분 |
visualize (PNG 차트) |
V1.7 | CSV 첨부로 충분 |
| Scope 별 audit (누가 언제 정의 바꿨나) | V2 | semantic_entries 에 created_by/created_at 만 V1 |
| 영역 | LOC |
|---|---|
| core | ~650 (ports + types + identity + semantic_scope/frontend port) |
| harness | ~700 |
| semantic | ~700 (types, layer, scoped_layer, sql_composer, store) |
| safety | ~250 (pipeline + 2 layers) |
| memory | ~350 (service + 3 simple impls) |
| ingestion | ~300 |
| tools | ~750 (6 tools — define_metric 가 scope 인지) |
| tenancy | ~400 |
| frontends/discord | ~800 (adapter, bot, commands 6개, session_router, render) |
| adapters | ~500 |
| tests | ~950 |
| 총 V1 | ~6,350 LOC 신규 |
| 주차 | 작업 |
|---|---|
| Week 1 | core 포트 정의 + HarnessContext, Session, agent_loop. cli 어댑터로 단위 검증 |
| Week 2 | semantic (scoped layer 포함) + safety pipeline + 첫 도구들 (run_sql, explore_schema, define_metric) + OpenAI/PG/SQLite 어댑터. safety 회귀 12개 |
| Week 3 | memory + ingestion + 나머지 도구 (ingest_doc, remember, ask_user) + tenancy (concierge, secrets, factstore, scope_resolver) |
| Week 4 | Discord 어댑터 (bot, 명령 6개, session_router, render). 동의 button view 등 |
| Week 5 | polish, e2e, README, DEPLOY 가이드, bench/ 데모 1개, CI YAML, 첫 길드 배포 |
V1 의 Whitelist + Timeout 만으로 충분히 막혀야 하는 12개 회귀 케이스:
| # | 입력 | 기대 |
|---|---|---|
| 1 | DROP TABLE users |
차단 |
| 2 | ; DELETE FROM t; -- |
차단 (multi-stmt) |
| 3 | INSERT INTO t VALUES (1) |
차단 |
| 4 | UPDATE t SET x=1 |
차단 |
| 5 | WITH x AS (INSERT INTO t ...) SELECT * FROM x |
차단 (INSERT 키워드 fail-closed) |
| 6 | SELECT * FROM nonexistent |
PG error (gate 통과, 실행 단계 실패) |
| 7 | SELECT pg_sleep(60) |
30초 timeout |
| 8 | SELECT * FROM huge_table (50k rows) |
row_limit 1000 으로 truncate |
| 9 | SELECT 1 |
통과 |
| 10 | WITH a AS (SELECT 1) SELECT * FROM a |
통과 |
| 11 | EXPLAIN SELECT 1 |
통과 |
| 12 | 빈 문자열 | 차단 (parse_error) |
V1.5 에 AST 정밀 검증 + 함수 차단 + 메타데이터 보강이 들어오면 추가 회귀:
- schema-qualified bypass (
public.pg_sleep) COPY ... TO PROGRAMEXPLAIN ANALYZE DELETE
(1) "오픈소스의 정체성" 이 차별점에 부합
Vanna/Wren/SQLCoder 가 질문→SQL 은 잘 풀고 있음. "GPT-4 보다 더 좋은 SQL을 만든다" 로 경쟁하면 모델 fine-tuning 싸움 — 우리 영역 아님.
대신 우리는 네 가지 조합 으로 차별:
- 문서로 비즈니스 맥락 학습
- 팀별 시멘틱 federation (git-like)
- 불완전 DB에서도 동작
- 모든 정의·대화 기억
이 조합은 기존 오픈소스에 없음.
(2) 강건성을 두 축 으로 분리한 게 깊이
기존엔 "DB 강건성" 만 봤는데, 라이브 사용해 보면 "팀별 정의 충돌" 이 더 자주 터지는 문제. 시멘틱 강건성 을 별도 축으로 명시한 것 자체가 새로움.
(3) 4개 패턴이 연구→제품 토대
DB 강건성 차별화는 "description 없으면 자동 생성" 같은 상황별 커스텀 전략 의 모음. Safety pipeline 의 layer 로 들어옴. 시멘틱 강건성은 "팀별 다른 정의" 의 federated 관리. Scoped semantic layer 로 들어옴. 연구자가 Hint Vector 같은 DB 구조 강건성 을 추가하고 싶으면 layer 1개로 됨. 연구→제품 흐름 자연스러움.
(4) Discord 는 Phase 1 인터페이스 일 뿐
정체성에 Discord 가 박혀 있으면 위험. v4.1 은 frontend 추상으로 분리. Phase 2 Slack 추가 시 코어 변경 0줄. 오픈소스 정체성과도 부합 — 플랫폼 lock-in 없음.
| 한계 | 영향 | 대응 |
|---|---|---|
| 의미적 정확성 정량 평가 부재 | "안전하게 틀린 답" 위험 | V1.5 에 골든 query set + 답 출처 표시 |
| LLM 비용 노출 | 토큰 폭주 시 사용자 비용 | V1.5 rate limit + per-user 토큰 cap |
| 첫 출하까지 4.5~5주 | 빠른 검증 어려움 | walking skeleton, wipe 라 4주 미만 무리 |
| 추상 비용 V1 LOC +400 | 일정 부담 | V1.5/V2 에서 ~1,500 LOC 절감 (흑자) |
| 시멘틱 충돌 해결 UI 부재 (V1) | admin 이 main 바꿔도 channel override 우선 — 사용자 혼선 가능 | V1.5 /semantic diff + 알림 |
| 의미 추출 LLM 환각 | 문서에서 잘못된 metric 추출 | 사용자 confirm 1차 방어, V1.5 검증 layer |
| 메모리 안 recall (V1) | facts 많아지면 토큰 폭증 | V1.5 키워드 recall, V2 벡터 recall |
| frontend 어댑터 추가 비용 | Slack/Web 각 ~1,000 LOC | Phase 2/3 단계 적용 |
| 영역 | Vanna AI | Wren AI | SQLCoder | Lang2SQL (우리) |
|---|---|---|---|---|
| 자연어 → SQL | ✅ RAG | ✅ MDL | ✅ fine-tuned | ✅ RAG + 시멘틱 + harness |
| DB 직접 연결 | ✅ | ✅ | ✅ | ✅ |
| Semantic layer | ❌ | ✅ (수동 MDL) | ❌ | ✅ (문서 자동 추출) |
| 시멘틱 federation (팀별 분기) | ❌ | ❌ | ❌ | ✅ (git-like 3-scope) |
| DB 메타데이터 자동 보강 | ❌ | ❌ | ❌ | ✅ (V1.5 보강 layer) |
| 대화 기억 | ❌ | ❌ | ❌ | ✅ (Hermes) |
| 멀티유저 협업 | 2.0 추가 | enterprise BI | ❌ | ✅ (Discord thread, frontend 추상) |
| Frontend 종류 | Web UI | Web UI | CLI | Discord/Slack/Web (어댑터) |
| 확장 모델 | 단일 아키텍처 | enterprise platform | 단일 LLM | 4가지 패턴 (chain/strategy/scope) |
위치 요약: Vanna 의 자연어→SQL + Wren 의 시멘틱 + 우리만의 (문서 자동 추출 + git-like 팀 federation + DB/시멘틱 두 축 강건성 + Hermes 기억 + 멀티 인터페이스).
"문서를 넣어 비즈니스 맥락을 학습시키고, 팀별로 시멘틱이 분기되고, DB가 불완전해도 견디고, 모든 정의·대화를 기억하는 오픈소스 분석 에이전트. Phase 1 은 Discord, 그 다음은 Slack·Web."
V1 착수 전 답이 있으면 좋은 항목:
- 첫 배포 Discord 길드 — 본인 테스트 / 가짜연구소 / 기타?
- OpenAI API 키 — 본인 / 팀?
- 레포 이름 —
lang2sql/유지 + 새 브랜치 (feature/v4-rebuild) 권고 - 첫 데모 시나리오 — bench/ 에 들어갈 완성된 use case 1개. 예: "이커머스 매출 분석 — 4 테이블, 3 metric, 1 문서, 5개 질문, 마케팅 채널 vs 파이낸스 채널에서 다른 정의로 같은 질문"
/define_metric디폴트 scope — channel (권고) vs DM 발생 시 personal vs admin 명시 강제?- NIM 도입 시점 — V1.5 contract test 후 / V2 보류?
답해 주시면 v4.1-final 마무리하고 Week 1 부터 시작.
— end —