전체 작업

Bonsai 로컬 LLM 디스코드 봇

외부 AI 서비스 없이 내 컴퓨터에서 도는 언어 모델에 디스코드와 터미널로 말을 걸 수 있는 경량 어시스턴트를 만들었다.

작업 기록 3건

무엇을 만들었나

개인 컴퓨터에서 직접 실행하는 AI 모델에 디스코드 채널과 터미널 양쪽에서 질문할 수 있게 한 가벼운 챗봇이다. 채널에 말만 걸면 앞선 대화를 이어서 답하고, 문맥 없이 한 번만 묻는 질문과 기억 지우기도 지원한다. 모델 서버에 인증 키를 걸 때를 대비해, 키가 있을 때만 사용하고 어디에도 노출하지 않는 경로를 먼저 준비했다.

구현 방식과 보안 설계는 ‘기술 상세’에 있습니다.

구조와 설계

discord.py·httpx 기반 봇과, 표준 라이브러리만 쓰는 CLI(원샷·REPL·SSE 스트리밍)를 11개 파일로 새로 구현해 OpenAI 호환 Chat Completions API(llama-server)에 연결했다. 모델 서버 슬롯이 1개라고 가정해 호출을 전역 asyncio.Lock으로 직렬화했고, 설정 로딩·명령 파싱은 파일당 300줄 제한에 맞춰 bonsai_core로 분리해 봇·CLI·테스트가 공유한다. 이후 접두사 없는 일반 메시지를 모두 대화로 처리하도록 바꾸면서 원샷(히스토리를 읽지도 쌓지도 않음)과 기억 비우기는 유지했고, 세션 제거 시점을 맞춰 활성 세션 수가 히스토리를 가진 사용자 수와 같게 했다. 마지막으로 이력 보관량(기본 15)과 API 키를 환경변수로 노출하고, 키가 있을 때만 Authorization 헤더를 붙이는 조건부 경로로 서버에 키를 걸기 전후 어느 쪽을 먼저 바꿔도 동작하게 했다.

사용 기술

Python 3, discord.py, httpx, python-dotenv, pytest, asyncio, llama-server (OpenAI 호환 API), SSE 스트리밍, macOS launchd, Bash

보안·안정성 설계

  • 토큰·채널 ID·API 주소·모델명·허용 역할 등 설정을 전부 환경변수로 분리하고 예시 파일만 문서화했다. 필수값이 없으면 누락 항목을 밝히고 종료한다.
  • 모델 서버에 API 키를 걸 수 있도록, 키가 있을 때만 Authorization 헤더를 붙이는 봇 쪽 인증 경로를 먼저 준비했다. 서버에 키를 걸기 전후 어느 쪽을 먼저 바꿔도 봇이 동작한다.
  • API 키가 있을 때만 Authorization 헤더를 붙이고 없으면 아예 붙이지 않는다. 401 응답 처리에서 키 값이 로그·예외 메시지·디스코드 응답 어디에도 나오지 않게 정리했다.
  • 로그에는 시각·사용자 ID·모드·소요 시간·토큰 수만 남기고 메시지 본문은 기록하지 않는다.
  • 지정한 채널 외의 메시지, DM, 봇 메시지는 무시하고 허용 역할을 설정할 수 있다. 사용자당 분당 10회를 넘는 요청은 무시한다.
  • 모델 호출 타임아웃(120초)과 서버 미기동·연결 실패 시 사람이 읽을 수 있는 안내 응답을 두었다.

작업 기록

  1. 설정 2개와 키 비노출 인증 경로 추가

    대화 이력 보관량(기본 15)과 API 키를 설정으로 빼고, 키가 있을 때만 인증 헤더를 붙이도록 했다. 오류 경로에서 키가 새지 않게 정리했고 테스트 23개가 통과했다.

  2. 로컬 LLM 디스코드 봇과 CLI 신규 구현

    빈 저장소에서 디스코드 봇과 터미널 CLI를 11개 파일로 새로 만들었다. 단위 테스트 6개가 통과했고, 모델 서버와 디스코드 토큰이 없는 조건이라 통합 테스트는 하지 못했다.

  3. 접두사 없는 상시 응답 모드로 전환

    모드 진입 없이 채널에 말만 걸면 바로 문맥을 이어 답하도록 명령 해석을 바꿨다. 원샷 질문과 기억 비우기는 남겼고 변경한 동작을 테스트로 고정했다.

이 페이지는 작업 기록 문서 3건을 바탕으로 정리했습니다. 고객 정보, 접속 주소, 비밀값은 공개 전에 걸러 냅니다.