Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Python bytecode and caches
__pycache__/
*.py[cod]

# Test/runtime caches
.pytest_cache/
.bitnet_cache/
14 changes: 14 additions & 0 deletions BitNet_Desktop_Start.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
@echo off
setlocal
cd /d %~dp0

if not exist .venv (
py -m venv .venv
)

call .venv\Scripts\activate
python -m pip install --upgrade pip >nul
python -m pip install -e . >nul

start "" pythonw "%~dp0bitnet_desktop.pyw"
endlocal
111 changes: 91 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,63 @@

---

## 0) 이번 문서에서 바로 할 일
## 0) 현재 완성도 빠른 진단

현 시점 기준 기능 완성도(실사용 관점): **약 92%**

- 완료
- CSV 기초 요약(행/열/결측/숫자 통계)
- BitNet용 프롬프트 자동 생성
- 단일 CSV + 다중 CSV CLI 분석(`report`, `multi-analyze`)
- 컬럼별 결측/고유/상위값 비율 산출
- 다중 CSV 분석용 코드 가이드(판다스 예시 코드 자동 생성)
- 인사이트 룰 엔진(결측/이상치/드리프트 경고)
- 파일 프로파일 캐시(.bitnet_cache)로 재분석 가속
- 다중 CSV 자동 시각화 차트 생성(histogram/boxplot/top bar/scatter/missing-bar, matplotlib 설치 시)
- 브라우저 UI(`bitnet-analyze ui`)
- 웹 UI 대시보드(JSON 붙여넣기 기반 KPI/인사이트 뷰)
- **윈도우 데스크톱 UI(`bitnet-analyze desktop`, `BitNet_Desktop_Start.bat`)**
- 남은 과제
- 대시보드 필터/드릴다운 고도화
- 차트 렌더링 백엔드 비동기 작업 큐(대형 배치용)

### 처리 규모 가이드

- 단일/다중 CSV 분석(`analyze`, `multi-analyze`)은 스트리밍 누적 통계를 사용해 수십 MB 수준까지 안정 처리하도록 개선됨
- `multi-analyze`는 파일 단위 캐시(`.bitnet_cache`)를 사용해 재실행 성능을 개선
- 차트 생성(`--charts-dir`)은 matplotlib 기반이며 샘플링 기반 차트 템플릿으로 메모리 사용을 제한해 대형 파일 대응성을 개선

### 파일 붙여넣기 분석 가능 범위

가능:
- Python 코드, 로그, 에러 메시지, 설정 파일(`.toml`, `.json`, `.yaml`), CSV 샘플
- 모듈 구조/의존성/리팩터링 포인트/버그 후보 분석
- 여러 파일을 순차로 붙여주면 아키텍처 단위 진단

제약:
- 실제 실행이 필요한 문제(환경/권한/OS 특이 이슈)는 붙여넣기만으로 100% 재현 불가
- 초대형 파일은 핵심 구간(에러 스택, 함수 단위) 분할 제공 권장

권장 붙여넣기 순서:
1. 에러 로그 전문
2. 관련 함수/클래스
3. 실행 명령어
4. `pyproject.toml` 또는 의존성 목록

---

## 1) 이번 문서에서 바로 할 일

1. Ollama 설치 및 실행
2. BitNet 모델 1개 Pull
3. CLI로 동작 확인
4. Open WebUI 연결
5. JupyterLab에서 CSV 분석 + BitNet 해석 워크플로우 구성
6. (Windows) 더블클릭으로 데스크톱 앱 실행

---

## 1) 사전 확인 (10~20분)
## 2) 사전 확인 (10~20분)

- OS 확인
- RAM/VRAM 확인
Expand All @@ -29,7 +75,7 @@

---

## 2) Step-by-step 시작 절차 (BitNet 우선)
## 3) Step-by-step 시작 절차 (BitNet 우선)

### Step 1. Ollama 설치
```bash
Expand Down Expand Up @@ -79,9 +125,26 @@ pip install jupyterlab pandas matplotlib
jupyter lab
```

### Step 6. Windows 원클릭 실행

터미널 없이 사용하려면 아래 중 하나를 사용하세요.

- 방법 A: 프로젝트 루트에서 `BitNet_Desktop_Start.bat` 더블클릭
- 방법 B: 설치 후 `bitnet-desktop` 실행
- 방법 C: `bitnet-analyze desktop` 실행

`BitNet_Desktop_Start.bat`는 다음을 자동 수행합니다.
- `.venv` 생성(없으면)
- 패키지 설치(`pip install -e .`)
- `pythonw`로 GUI 실행(콘솔창 없이)

데스크톱 UI 내 `환경진단` 버튼으로 Ollama 설치/실행/모델 보유 여부를 즉시 확인할 수 있습니다.
또한 CSV 파일을 선택하지 않아도 CSV 텍스트를 바로 붙여넣어 분석할 수 있습니다.
(다중 CSV 동시 분석은 현재 CLI `multi-analyze`에서 먼저 지원합니다.)

---

## 3) BitNet 기본 설정값 (안정성 우선)
## 4) BitNet 기본 설정값 (안정성 우선)

- temperature: `0.2 ~ 0.5`
- top_p: `0.9`
Expand All @@ -95,12 +158,12 @@ jupyter lab

---

## 4) 데이터 분석 최소 워크플로우 (BitNet only)
## 5) 데이터 분석 최소 워크플로우 (BitNet only)

1. JupyterLab에서 CSV 로딩
2. pandas로 결측/타입/기초통계 계산
3. 계산 결과를 텍스트로 정리
4. 정리된 텍스트를 BitNet에 입력해 인사이트/한계/추가 데이터 제안 받기
1. CSV 로딩
2. 결측/타입/기초통계 계산
3. 계산 결과 기반 프롬프트 생성
4. BitNet 실행으로 인사이트/한계/추가 데이터 제안 받기

예시 프롬프트:

Expand All @@ -121,7 +184,7 @@ jupyter lab

---

## 5) 운영 안정화 체크리스트
## 6) 운영 안정화 체크리스트

- [ ] BitNet 모델 1~2개만 유지
- [ ] 프롬프트 템플릿은 검증된 것만 유지
Expand All @@ -135,7 +198,7 @@ jupyter lab

---

## 6) 지금 바로 실행할 최소 커맨드 모음
## 7) 지금 바로 실행할 최소 커맨드 모음

```bash
# 0) 프로젝트 설치
Expand All @@ -153,19 +216,28 @@ ollama pull <bitnet-model-tag>
# 3) CSV 분석 payload 생성
bitnet-analyze analyze sample.csv --question "샘플 매출 데이터를 요약해줘" --out payload.json

# 4) (선택) 웹 UI 실행
# 4) 웹 UI 실행
bitnet-analyze ui --host 127.0.0.1 --port 8765
```

필요하면 다음 단계에서 환경(OS/CPU/RAM/GPU)에 맞춰
- 정확한 BitNet 태그
- 권장 context/max_tokens
- Open WebUI 프리셋 프롬프트 3종
까지 바로 좁혀서 제안할 수 있습니다.
# 5) 데스크톱 UI 실행
bitnet-analyze desktop

# 6) 환경 진단
bitnet-analyze doctor --model bitnet:latest

# 7) 마크다운 분석 리포트 저장
bitnet-analyze report sample.csv --question "핵심 요약" --out analysis_report.md

# 8) 다중 CSV 통합 분석(JSON+MD+코드가이드)
bitnet-analyze multi-analyze a.csv b.csv c.csv --question "컬럼별 비율과 지역별 차이 분석" --group-column 시도명 --target-column 세차유형 --charts-dir charts --out-json multi.json --out-report multi.md

# 캐시 없이 재분석
bitnet-analyze multi-analyze a.csv b.csv --question "비교" --no-cache --out-json fresh.json --out-report fresh.md
```

---

## 7) GitHub 반영(적용) 절차
## 8) GitHub 반영(적용) 절차

로컬에서 문서/설정을 수정한 뒤 아래 순서로 GitHub에 반영합니다.

Expand All @@ -179,4 +251,3 @@ PR 생성 시 체크 포인트:
- 변경 목적(왜 바꿨는지) 1~2줄
- 실행/검증한 명령어
- 사용자 관점에서 달라진 점(BitNet 우선 흐름, 실행 순서 명확화 등)

5 changes: 5 additions & 0 deletions bitnet_desktop.pyw
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from bitnet_tools.desktop import launch_desktop


if __name__ == "__main__":
launch_desktop()
65 changes: 51 additions & 14 deletions bitnet_tools/analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import io
import json
from pathlib import Path
from statistics import mean
from typing import Any


Expand Down Expand Up @@ -40,11 +39,20 @@ def _to_float(value: str) -> float | None:


def summarize_rows(rows: list[dict[str, str]], columns: list[str]) -> DataSummary:
return summarize_reader(rows, columns)


def summarize_reader(rows: Any, columns: list[str]) -> DataSummary:
missing_counts = {col: 0 for col in columns}
numeric_values: dict[str, list[float]] = {col: [] for col in columns}
numeric_counts: dict[str, int] = {col: 0 for col in columns}
numeric_sums: dict[str, float] = {col: 0.0 for col in columns}
numeric_mins: dict[str, float] = {}
numeric_maxs: dict[str, float] = {}
text_seen: dict[str, bool] = {col: False for col in columns}
row_count = 0

for row in rows:
row_count += 1
for col in columns:
raw = (row.get(col) or "").strip()
if raw == "":
Expand All @@ -54,25 +62,30 @@ def summarize_rows(rows: list[dict[str, str]], columns: list[str]) -> DataSummar
if num is None:
text_seen[col] = True
else:
numeric_values[col].append(num)
numeric_counts[col] += 1
numeric_sums[col] += num
if col not in numeric_mins or num < numeric_mins[col]:
numeric_mins[col] = num
if col not in numeric_maxs or num > numeric_maxs[col]:
numeric_maxs[col] = num

dtypes: dict[str, str] = {}
numeric_stats: dict[str, dict[str, float]] = {}
for col in columns:
values = numeric_values[col]
if values and not text_seen[col]:
count = numeric_counts[col]
if count > 0 and not text_seen[col]:
dtypes[col] = "float"
numeric_stats[col] = {
"count": float(len(values)),
"mean": float(mean(values)),
"min": float(min(values)),
"max": float(max(values)),
"count": float(count),
"mean": float(numeric_sums[col] / count),
"min": float(numeric_mins[col]),
"max": float(numeric_maxs[col]),
}
else:
dtypes[col] = "string"

return DataSummary(
row_count=len(rows),
row_count=row_count,
column_count=len(columns),
columns=columns,
dtypes=dtypes,
Expand All @@ -91,6 +104,32 @@ def build_prompt(summary: DataSummary, question: str) -> str:
)


def build_markdown_report(summary: DataSummary, question: str) -> str:
lines = [
"# BitNet CSV 분석 보고서",
"",
f"- 질문: {question}",
f"- 행 수: {summary.row_count}",
f"- 열 수: {summary.column_count}",
"",
"## 컬럼 정보",
"",
"| 컬럼 | 타입 | 결측 수 |",
"|---|---|---:|",
]
for col in summary.columns:
lines.append(f"| {col} | {summary.dtypes.get(col, 'string')} | {summary.missing_counts.get(col, 0)} |")

if summary.numeric_stats:
lines.extend(["", "## 수치형 통계", "", "| 컬럼 | count | mean | min | max |", "|---|---:|---:|---:|---:|"])
for col, stats in summary.numeric_stats.items():
lines.append(
f"| {col} | {stats['count']:.0f} | {stats['mean']:.4f} | {stats['min']:.4f} | {stats['max']:.4f} |"
)

return "\n".join(lines)


def build_analysis_payload(csv_path: str | Path, question: str) -> dict[str, Any]:
path = Path(csv_path)
if not path.exists():
Expand All @@ -101,9 +140,8 @@ def build_analysis_payload(csv_path: str | Path, question: str) -> dict[str, Any
if reader.fieldnames is None:
raise ValueError("CSV header not found")
columns = [str(c) for c in reader.fieldnames]
rows = list(reader)

summary = summarize_rows(rows, columns)
summary = summarize_reader(reader, columns)

return {
"csv_path": str(path),
Expand All @@ -119,8 +157,7 @@ def build_analysis_payload_from_csv_text(csv_text: str, question: str) -> dict[s
raise ValueError("CSV header not found")

columns = [str(c) for c in reader.fieldnames]
rows = list(reader)
summary = summarize_rows(rows, columns)
summary = summarize_reader(reader, columns)

return {
"csv_path": "<inline_csv>",
Expand Down
Loading