만들어 보기 · 글 11편만들어 보기
  1. 코딩 에이전트로 작은 로컬 AI 도구 만드는 법
  2. 로컬 채팅 웹앱 만들기
  3. 요약 창 프로그램 만들기
  4. 엑셀에서 로컬 모델을 함수로 부르기
  5. CSV를 한 줄씩 분류하는 도구 만들기
  6. 문서 한 개에 질문하는 도구 만들기
  7. 자연어로 DB를 조회하는 도구 만들기
  8. 로컬 OCR 문서 도구 만들기
  9. 영상·음성 더빙 파이프라인 만들기
  10. 개인정보 점검 도구 만들기
  11. 문서 폴더에 요약과 태그 붙이기

이 글에서 다루는 것CSV 파일을 올리면 로컬 모델이 행마다 분류와 한 줄 요약을 붙여 주고, 결과를 다시 CSV로 내려받는 HTML 도구를 만듭니다.

무엇을 만드나

CSV 파일을 끌어다 놓으면 표로 보여 주고, 분류 시작을 누르면 행을 하나씩 모델에 보내 담당 부서, 유형, 한 줄 요약 세 칸을 채워 주는 도구입니다. 끝나면 원래 열에 세 열을 붙인 CSV를 내려받습니다.

이럴 때 쓸모가 있습니다.

  • 접수된 문의나 의견 수십~수백 건을 미리 나누어 담당을 정할 때
  • 분류 기준(부서 목록, 유형 목록)이 자주 바뀔 때
  • 내용에 개인 사정이 적혀 있어 외부 서비스에 올리기 어려울 때

준비물

항목 내용
Ollama 실행 중, 모델 하나 이상
Python 화면 파일을 띄우는 데만 씁니다. 추가 패키지 없음
브라우저 크롬, 엣지 등

구조

브라우저(HTML 한 파일)
  ├─ CSV 읽기·표 그리기·결과 내려받기 : 브라우저 안에서 처리
  └─ 행마다 POST http://localhost:11434/api/generate
serve.py : 이 HTML을 http://localhost:8080 으로 띄워 줌

모델을 부르는 것은 HTML 쪽입니다. Python은 HTML 파일을 내 PC 주소로 열어 주는 일만 합니다. HTML을 더블클릭해 file://로 열면 브라우저 보안 정책 때문에 Ollama 호출이 막힐 수 있어서 이렇게 합니다.

부품 역할
CSV 읽기 쉼표와 탭 구분을 자동으로 가리고, 따옴표로 묶인 칸을 처리합니다
설정 분류할 열, 부서 목록, 유형 목록을 화면에서 고칩니다
분류 반복 행마다 프롬프트를 만들어 모델에 보내고 답을 해석합니다
내려받기 원래 열 + 결과 세 열을 CSV로 저장합니다

에이전트에게 줄 요청

CSV 일괄 분류 도구를 HTML 한 파일로 만들어 줘. 외부 라이브러리와 CDN은 쓰지 않는다.
- CSV를 끌어다 놓거나 선택하면 표로 보여 준다. 쉼표/탭 구분 자동 판별, 따옴표 처리.
- 분류할 내용이 든 열을 고르게 한다.
- 부서 목록과 유형 목록은 화면에서 한 줄에 하나씩 고칠 수 있다.
- 시작하면 행을 하나씩 http://localhost:11434/api/generate (stream=false)에 보낸다.
  프롬프트에 목록과 예시 3개를 넣고, 답에서 목록에 있는 낱말만 골라 채운다.
- 진행률 표시, 중단 버튼, 이미 채운 행은 건너뛰기.
- 결과는 AI_부서, AI_유형, AI_요약 열을 붙여 CSV로 내려받는다. 엑셀에서 한글이 깨지지 않게 BOM을 붙인다.
- serve.py: 표준 라이브러리만으로 이 HTML을 127.0.0.1:8080에 띄우고 브라우저를 연다.
  포트가 쓰이고 있으면 다음 번호를 시도한다.

핵심 코드

프롬프트는 “목록 안에서만 고르라”는 지시, 예시, 그리고 답을 이어 쓰게 만드는 끝맺음으로 이루어집니다. 마지막 줄을 부서:로 끝내면 모델이 그 형식을 이어서 씁니다.

const prompt = `민원을 분류하세요. 반드시 아래 부서와 유형 중에서만 고르세요.

부서: ${deptsStr}
유형: ${typesStr}

예시1)
민원: 쓰레기 불법 투기가 심합니다.
부서: 환경과
유형: 신고
요약: 쓰레기 불법 투기 단속 요청

(예시2, 예시3 생략)

민원: ${content}
부서:`;

답은 그대로 믿지 않습니다. 목록의 낱말이 답 안에 들어 있는지 찾아서 그 낱말만 칸에 넣습니다. 목록에 없는 부서를 지어내도 표에는 들어가지 않습니다.

const res = await fetch(OLLAMA+"/api/generate",{
  method:"POST",headers:{"Content-Type":"application/json"},
  body:JSON.stringify({model:currentModel,prompt,stream:false})
});
const data = await res.json();
const answer = (data.response||'').trim();

let dept='-',type='-',summary='-';
for(const d of depts){if(answer.includes(d)){dept=d;break}}
for(const t of types){if(answer.includes(t)){type=t;break}}

모델 목록을 가져올 때는 대화용이 아닌 임베딩 모델을 이름으로 걸러 냅니다.

const skip = ["embed","embedding","bge-","snowflake"];
const models = (data.models||[]).filter(m => !skip.some(k => m.name.toLowerCase().includes(k)));

화면을 띄우는 serve.py는 표준 라이브러리만 씁니다.

port = PORT
server = None
for attempt in range(10):
    try:
        server = http.server.HTTPServer(("127.0.0.1", port), QuietHandler)
        break
    except OSError:
        port += 1

실행하고 확인하기

  1. python serve.py를 실행합니다. 브라우저가 열립니다.
  2. 위쪽 표시가 Ollama 연결 상태와 모델 수를 보여 주는지 확인합니다.
  3. 직접 만든 시험용 CSV 10행쯤을 올립니다. 실제 자료를 쓰기 전에 지어낸 예문으로 먼저 봅니다.
  4. 분류할 열을 고르고, 부서와 유형 목록을 내 기준에 맞게 고칩니다.
  5. 분류 시작을 누르고 행이 하나씩 채워지는지 봅니다.
  6. 중간에 중단을 눌렀다가 다시 시작해, 채워진 행을 건너뛰는지 확인합니다.
  7. CSV를 내려받아 엑셀에서 열고 한글이 깨지지 않는지 봅니다.

정확도는 직접 재야 합니다. 20~30행을 사람이 먼저 분류해 두고 도구의 결과와 맞춰 봅니다. 자주 틀리는 유형이 보이면 그 사례를 예시에 넣습니다.

자주 생기는 문제

증상 원인과 해결
“Ollama 연결실패” HTML을 더블클릭해 열었거나 Ollama가 꺼져 있습니다. serve.py로 엽니다
칸이 -로 채워짐 답에 목록의 낱말이 없었습니다. 목록 표기와 예시 표기를 같게 맞춥니다
부서가 엉뚱함 목록에 이름이 서로 포함되는 항목이 있으면 앞의 것이 먼저 잡힙니다. 이름을 구분되게 고칩니다
한 행이 오래 걸림 내용이 아주 긴 행입니다. 앞부분 일정 길이만 보내도록 고칩니다
열이 밀려 보임 칸 안에 줄바꿈이나 쉼표가 있는데 따옴표로 묶이지 않은 CSV입니다. 엑셀에서 다시 CSV로 저장합니다

확장하기

  • 확인 열 추가: 답에 목록 낱말이 없어 -가 된 행만 모아 보여 주는 거름 단추를 답니다. 사람이 볼 행이 줄어듭니다.
  • 프롬프트 화면 편집: 예시를 화면에서 고칠 수 있게 해, 코드를 건드리지 않고 분류 기준을 다듬습니다.
  • 두 번 물어 비교: 같은 행을 두 번 분류해 결과가 다르면 표시합니다. 모델이 망설이는 행을 찾는 방법입니다.
  • 엑셀 파일 직접 읽기: .xlsx를 읽으려면 별도 라이브러리가 필요하므로, 외부 연결 없이 쓰려면 파일을 프로젝트 안에 넣어 두게 합니다.

엑셀 안에서 같은 분류를 수식으로 하려면 엑셀에서 로컬 모델을 함수로 부르기를 봅니다.