Skip to content

About

No description, website, or topics provided.

Resources

Stars

3 stars

Watchers

0 watching

Forks

Repository files navigation

Hina Discord Bot

소라사키 히나를 연기하는 비공식 한국어 Discord 봇입니다. Python 3.11+, discord.py, SQLite와 OpenAI/Gemini/OpenRouter LLM provider를 사용합니다. 공식 서비스나 공식 대사 재현물은 아닙니다.

현재 저장소는 캐릭터 RP, 장기 기억, 최근 채널 문맥, lore/runtime knowledge, 웹 검색 routing, 멀티 provider와 현재 턴 이미지 입력을 함께 다룹니다. 비전 기능은 1차 구현 범위를 먼저 실제 Discord에서 검증하고 있으며, 검증 전에는 버그 수정과 문서 정리를 제외한 추가 확장을 하지 않습니다.

주요 기능

  • @멘션, 답장 핑, 메시지 시작의 히나야 같은 호출어로 일반 대화
  • 사용자별 장기 기억과 서버 내 공개 호출의 제한된 공유 기억
  • 장기 기억과 독립된 TTL 기반 최근 채널 문맥
  • 관리자용 서버 공통 메모, 동적 instruction, runtime knowledge
  • 검수된 lore를 질문과 관련된 범위만 로컬 검색해 사용
  • 현재 시각/runtime context와 최신 정보가 필요할 때 provider 웹 검색 사용
  • OpenAI, Gemini, OpenRouter provider 교체
  • 등록된 커스텀 이모지 출력
  • 현재 호출 메시지의 이미지 첨부, 커스텀 이모지, 래스터 스티커 해석
  • Discord slash command 기반 기억/설정/이모지 관리
  • usage/error logging, 테스트, Docker Compose
  • SQLite/telemetry를 읽기 전용으로 조회하는 dashboard foundation

빠른 실행

uv 기준:

uv sync --extra dev
cp .env.example .env.local
# .env.local에 DISCORD_TOKEN과 선택한 provider API key를 입력
# 기존 DB를 사용한다면 먼저 봇과 모든 DB writer를 중지
uv run hina-prepare-db --dry-run
uv run hina-prepare-db --yes --offline-confirmed
uv run hina-bot

DB 준비는 최초 설치/스키마 업그레이드 때 명시적으로 실행합니다. 조회·dry-run·유지보수는 봇 시작 설정 스냅샷을 덮어쓰지 않습니다. DB 초기화·이전·복구 절차를 참고하세요. 구형 비버전 DB는 일반 준비 명령이 자동 변환하지 않으며, 문서의 hina-upgrade-legacy-db 오프라인 절차를 사용합니다.

기본 provider 예시는 OpenAI입니다.

LLM_PROVIDER=openai
LLM_MODEL=gpt-4.1-mini
OPENAI_API_KEY=...
DISCORD_TOKEN=...

Gemini/OpenRouter 설정과 답변 모델·기억 모델 분리는 docs/model-providers.md를 참고하세요.

GitHub Actions 환경 변수 동기화

GitHub CLI를 설치하고 gh auth login을 마친 뒤 로컬 dotenv 값을 저장소의 Actions 설정으로 동기화할 수 있습니다. 먼저 값 없이 분류 결과만 확인하는 것을 권장합니다.

uv run python scripts/sync_github_actions_env.py .env.local \
  --repo sendoru/hina-discord-bot --dry-run
uv run python scripts/sync_github_actions_env.py .env.local \
  --repo sendoru/hina-discord-bot

*_API_KEY, *_TOKEN, *_SECRET, *_PASSWORD, *_PRIVATE_KEY는 Secret으로, 나머지는 Variable로 등록합니다. 빈 값은 기존 원격 값을 덮어쓰지 않도록 건너뜁니다. 이름만으로 분류하기 어려운 값은 --secret NAME 또는 --variable NAME으로 명시할 수 있습니다. GitHub Environment에 등록하려면 --environment production을 추가합니다. 값은 명령행 인자나 출력에 표시하지 않고 gh secret set/gh variable set의 표준입력으로 전달합니다.

테스트:

uv run ruff check .
uv run pytest

Docker Compose:

docker compose stop
docker compose build
docker compose run --rm hina hina-prepare-db --dry-run
docker compose run --rm hina hina-prepare-db --yes --offline-confirmed
docker compose up -d
docker compose logs -f

SQLite와 운영 중 생성되는 데이터는 기본적으로 data/ 아래에 두며 Git에서 제외합니다. Docker Compose에서는 named volume으로 보존합니다. 같은 SQLite를 여러 봇 프로세스가 동시에 사용하는 구성은 지원하지 않습니다.

Discord 설정

Discord Developer Portal에서 Bot을 만들고 Message Content Intent를 켭니다. 히나야처럼 멘션 없는 호출어를 읽는 데 필요합니다.

서버의 다른 사용자를 이름·별명으로 식별하거나 정확히 mention할 수 있도록 봇은 Server Members Intent를 항상 요청합니다. Developer Portal에서도 이 intent를 반드시 켠 뒤 봇을 재시작해야 합니다. 일반 답변에는 사용자 명단을 미리 붙이지 않고, 대화 모델이 필요하다고 판단한 경우에만 로컬 get_current_channel_members 도구로 현재 채널을 볼 수 있는 사람들의 서버 닉네임·global name·username·user ID를 조회합니다. 대화·기억 조회 권한은 이 도구와 별도로 기존 privacy 경계를 따릅니다. 자세한 범위는 채널 멤버 도구를 참고하세요.

서버 초대에는 일반적으로 다음 권한이면 충분합니다.

  • View Channels
  • Send Messages
  • Read Message History
  • 스레드 사용 시 Send Messages in Threads
  • slash command를 위한 applications.commands scope

봇 전체 관리자 권한을 줄 필요는 없습니다.

호출 규칙

입력 기본 동작
@히나 오늘 어땠어? 응답
히나야 오늘 어땠어? 응답
히나 메시지에 답장 + 답장 핑 응답
히나 메시지에 답장, 답장 핑 없음 다른 호출 조건이 없으면 응답하지 않음
DM 일반 메시지 기본적으로 호출어/멘션 필요. DM_ALWAYS_REPLY=true로 변경 가능
ALWAYS_REPLY_CHANNEL_IDS에 지정된 서버 채널의 사람 메시지 호출어 없이도 응답
다른 봇의 서버 메시지 기본은 멘션/답장 핑 필요. always-reply 채널에서는 호출어도 허용. DM에서는 응답하지 않음
웹훅 메시지 응답하지 않음

기본 호출어는 히나야이며 CALL_PREFIXES에 쉼표로 구분해 여러 개를 지정할 수 있습니다. 특정 서버 채널을 DM처럼 항상 응답하게 하려면 ALWAYS_REPLY_CHANNEL_IDS에 채널 ID를 쉼표로 구분해 넣거나, 운영 중 /config set key:ALWAYS_REPLY_CHANNEL_IDS value:<ID,...>로 변경할 수 있습니다. 이 implicit trigger는 사람 메시지에만 적용되며 다른 봇은 명시적 호출이 계속 필요합니다.

이미지 입력

현재 호출 메시지, 사용자가 명시적으로 답장한 메시지, 그리고 제한된 최근 채널 메시지의 시각 입력을 모델에 전달할 수 있습니다.

지원:

  • PNG/JPEG/GIF/WebP 이미지 첨부
  • Discord 커스텀 이모지
  • 래스터 스티커
  • 호출어/멘션과 이미지만 보낸 image-only 호출
  • 이미지가 있는 메시지에 답장한 후속 질문
  • /chatlog가 켜진 채널에서 아까 그 사진, 저거 같은 후속 질문

기본 source별 count quota:

VISION_MAX_ATTACHMENTS=4
VISION_MAX_EMOJIS=12
VISION_MAX_STICKERS=8

각 값은 0~32이고 합계는 32 이하여야 합니다. 0이면 해당 source를 비전 입력에서 끕니다. 개별 이미지 5 MiB, 한 호출 전체 12 MiB의 byte limit도 별도로 적용됩니다. 파일 byte 크기와 실제 이미지 token 비용은 동일하지 않으며 provider/model과 이미지 크기에 따라 달라질 수 있습니다.

현재 지원하지 않는 범위:

  • Discord embed와 일반 URL을 따라가 이미지로 해석
  • PDF/문서 파일 해석
  • 이미지 생성·편집
  • 이미지 원본/caption을 장기 기억에 자동 저장
  • Lottie 스티커

최근 이미지는 현재 질문에 직접 첨부하지 않고, 원래 메시지의 본문·작성자·message ID와 묶인 과거 시각 문맥으로 시간순 배치합니다. 모델은 현재 질문의 답장 대상과 대화 흐름이 연결되는 경우에만 이를 사용하며, 무관하거나 관계가 불명확하면 답변에서 완전히 무시하도록 지시받습니다.

이미지 bytes는 현재 answer() 요청에만 사용하고 recent chatlog나 SQLite memory에 저장하지 않습니다. 이미지 안의 텍스트, QR, prompt처럼 보이는 내용도 모두 신뢰할 수 없는 사용자 데이터로 취급합니다.

선택한 LLM_MODEL 자체가 vision 입력을 지원해야 합니다. provider adapter 지원과 모델 capability는 별개입니다. 구현 구조, 저장 경계, 검증 체크리스트는 docs/vision-input.md를 참고하세요.

정보 routing과 웹 검색

현재 시각/날짜는 runtime context로 제공합니다. 질문이 외부 현실의 최신 상태에 의존하면 기존 information routing이 provider의 웹 검색 기능을 사용합니다.

예:

  • 지금 몇 시야? → runtime clock
  • 서울 날씨 어때? → web
  • 이번 추석 연휴 시작까지 며칠 남았어? → 현재 날짜 + 공개 일정 확인
  • 히나 생일 언제야? → local lore

비전은 이 routing과 별개의 입력 modality입니다. 예를 들어 이미지 속 장소를 보고 지금 열었어?라고 묻는 요청은 이미지 해석과 web route를 동시에 사용할 수 있습니다.

자세한 내용은 docs/runtime-web-search.md를 참고하세요.

기억과 최근 채널 문맥

장기 기억과 recent chatlog는 서로 다른 시스템입니다.

  • 장기 기억: SQLite에 저장되는 직접 호출 기록, 자동 요약, 개인 메모, 제한된 공개 공유 기억
  • recent chatlog: 같은 채널의 최근 텍스트를 TTL 동안 메모리에만 유지하는 임시 문맥

recent chatlog는 장기 요약 입력에 포함되지 않습니다. 현재 턴 이미지 원본도 두 시스템 어느 쪽에도 저장하지 않습니다.

일반 서버에서 다른 사용자의 최근 발언을 참고할 때는 user_id와 name을 함께 전달해 화자를 구분합니다. 멘션 대상과 최근 발언의 연결은 모델이 이 구조화된 문맥을 바탕으로 추론하며, 과거 이미지 까지 결정론적으로 연결하는 기능은 현재 없습니다.

관리 명령

Discord에서는 현재 서버·채널을 대상으로 하는 즉시 조작을 제공하고, 여러 서버에 걸친 관리·검색·일반 runtime 설정 편집은 Dashboard에서 수행합니다.

자동 기억 및 수동 메모

/memory show
/memory clear
/memory mode
/memory purge
/note show
/note set
/note clear
/state show

/memory show/clear는 본인의 자동 기억을 관리하며, /memory mode/purge와 /state show는 봇 관리자 전용입니다. /memory purge는 채널·현재 서버 범위만 허용하며 전역 purge는 Discord에서 제공하지 않습니다. /note의 사용자 메모는 자신이 관리하고, 서버 공통 메모의 변경에는 Manage Server 권한이 필요합니다.

최근 채널 문맥과 자동 응답

/chatlog mode
/chatlog clear
/config always-reply enable
/config always-reply disable
/config always-reply status
/config privacy

/chatlog mode는 관리자용이며, /chatlog clear는 해당 채널의 임시 문맥을 초기화합니다. /config always-reply는 현재/선택 서버 채널의 자동 응답 opt-in만 바꾸며 /config privacy는 외부 LLM 전송 경계를 관리합니다. 범용 런타임 설정 편집은 Dashboard /admin/runtime에서 수행합니다.

이모지 및 knowledge ingest

/emoji add
/emoji import
/emoji list
/emoji edit
/emoji remove
/knowledge ingest
/help

동적 instruction 및 runtime knowledge CRUD는 Dashboard /admin/prompts에서 관리합니다. /knowledge ingest는 Dashboard 이전 작업 #333이 완료될 때까지 Discord에서 계속 제공합니다.

전체 명령·권한·scope 설정 예시는 docs/slash-commands.md를 참고하세요.

Lore와 runtime knowledge

정적·검수 완료 lore는 src/hina_bot/data/lore.jsonl 또는 LORE_PATH에서 읽습니다. 운영 중 관리자가 추가하는 dynamic knowledge는 SQLite에 저장합니다.

한국 서버에 정식 출시된 범위를 기준으로 canon/community 자료를 분리해 관리하며, 원본 조사 자료가 자동으로 전부 runtime prompt에 들어가지는 않습니다.

관련 문서:

Provider

지원 provider:

  • openai
  • gemini
  • openrouter

답변 모델과 기억 요약 모델을 분리할 수 있습니다. Gemini는 별도 thinking level/total output token 설정을 사용할 수 있고, 각 provider의 검색 도구 형식은 adapter에서 변환합니다.

자세한 설정과 주의점은 docs/model-providers.md를 참고하세요.

Prompt와 보안 경계

기본 캐릭터 지침은 src/hina_bot/prompts/hina.md입니다. 관계 지침, 고정 POLICY, 동적 instruction은 역할을 분리해 적용합니다.

사용자 메시지뿐 아니라 다음 항목은 모두 신뢰할 수 없는 데이터로 취급합니다.

  • 사용자 이름
  • 저장된 기억과 요약
  • 최근 채널 발언
  • 서버 공통/개인 메모
  • lore/reference 데이터
  • 이모지 이름·설명
  • 이미지 안의 텍스트·QR·화면 속 prompt

관리 권한, memory 삭제, Discord 전송 경계는 모델의 주장에 맡기지 않고 Python 코드에서 검사합니다. 모델 출력의 일반 사용자 멘션은 허용하지만 @everyone, @here, 역할 멘션은 전송 전에 비활성화합니다.

로그와 데이터 보관

기본 API usage 로그는 data/logs/usage.jsonl, Discord 호출 단위 집계는 data/logs/discord-usage.jsonl에 기록합니다. 메시지 원문, 사용자 ID, API key, 이미지 bytes/base64, Discord CDN URL을 usage 로그에 남기는 용도로 사용하지 않습니다.

Discord 호출의 접수, 처리 제외 사유, 응답 생성·전송, 기억 후처리 결과는 기본적으로 data/logs/events.jsonl에 기록합니다. 각 호출에는 Discord 식별자와 무관한 임의의 turn_id가 부여되며 같은 값이 usage 로그에도 기록됩니다. 세 structured telemetry 파일의 각 row에는 app_version, build_revision, runtime_id도 함께 기록합니다. build_revision은 명시적으로 주입한 HINA_GIT_SHA 또는 checkout의 Git HEAD를 우선 사용하고, Git metadata가 없는 이미지에서는 설치된 hina_bot 패키지 소스 fingerprint로 fallback합니다. EVENT_LOG_PATH를 비우면 이벤트 파일 로깅을 끌 수 있습니다. 이벤트 로그에도 메시지·응답 원문, 사용자/서버/채널 ID, URL을 기록하지 않습니다.

일반 provider 호출은 기본적으로 store=False를 사용합니다. Gemini 일반 채팅 answer는 GEMINI_STORE_INTERACTIONS를 명시적으로 켜면 interaction 저장을 opt-in할 수 있습니다. semantic/web routing classifier는 GEMINI_STORE_CLASSIFIER_INTERACTIONS로 별도 opt-in하며, memory summary·identity resolution 같은 다른 내부 보조 호출은 계속 저장하지 않습니다. 디버깅 중 AI Studio Logs에서 필요한 raw request/response만 확인할 때 켜는 것을 권장합니다. 이 경우를 포함해 provider의 모든 데이터 보관 정책에서 제외된다는 의미는 아니므로, 운영자는 사용하는 provider의 데이터 정책을 별도로 확인해야 합니다.

새 dashboard foundation은 production Store를 공유하지 않고 SQLite를 read-only로 열며, rotated usage/event JSONL을 별도로 읽습니다. 현재는 /healthz만 제공하고 inspection UI는 후속 단계입니다. 설치·보안 경계와 turn_id correlation은 docs/dashboard.md를 참고하세요.

테스트 전략

일반 CI에서는 네트워크를 mock하고 다음 경계를 결정적으로 테스트합니다.

  • routing/memory/chatlog
  • provider adapter
  • prompt/output safety
  • Discord command surface
  • vision request wrapping과 Discord visual collection

실제 모델·Discord 조합은 smoke test가 필요합니다. 특히 비전 기능은 unit test 통과만으로 provider별 실제 이미지 이해 품질까지 보장하지 않습니다.

비전 기능의 현재 검증 체크리스트는 docs/vision-input.md에 있습니다. 이 체크리스트가 안정적으로 통과하기 전에는 과거 이미지 context, caption cache, Lottie 렌더링 같은 2차 기능을 추가하지 않습니다.

프로젝트 구조

패키지는 책임별로 ai/, core/, discord/, tooling/으로 분리되어 있습니다. docs/project-layout.md를 참고하세요.

주요 문서

저장소

gh repo clone sendoru/hina-discord-bot
cd hina-discord-bot

About

No description, website, or topics provided.

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages