문서 도구

말풍선과 로컬 모델

게임 속 어떤 유닛이든 원하는 신분으로 머리 위에 대화 말풍선을 띄웁니다. 로컬 LLM을 연결하면 한 마디가 들어가고 답변 한 마디가 유닛 머리 위에 나타납니다.

말풍선은 관전용 레이어입니다. 승패에 영향을 주지 않으며 방송, 해설, 디버깅에 적합합니다.

  • 어떤 유닛이든, 어떤 신분으로든 말할 수 있고, 여러 유닛이 동시에 말할 수도 있습니다.
  • 말풍선마다 글자 크기, 색상, 너비, 꼬리, 투명도, 타이핑 속도를 따로 설정할 수 있습니다.
  • 로컬 LLM(LM Studio)에 바로 연결할 수 있고 스트리밍 출력을 지원합니다. 생성되는 대로 말풍선이 갱신됩니다.

Bot에서 사용하기

가장 간단한 방법은 SDK에 내장된 say입니다.

g.say(hero, "나를 따르라!", seconds=4)

시작과 화면

가장 간단한 방법은 Farsight 첫 화면 “컨트롤 센터”입니다. 먼저 “로컬 LLM → 시작하고 모델 로드”를 누르고(LM Studio 로컬 서버 + 설정해 둔 모델을 VRAM에 로드), 그다음 “머리 위 채팅 말풍선 → 시작”을 누르세요. 카드에서 로그 보기, 중지, 재시작도 할 수 있습니다.

화면은 Farsight 왼쪽의 “머리 위 말풍선” 페이지에 있습니다. 유닛이 말하게 하기(유닛 선택, 글 입력, 스타일 조정, 모델과 대화), 일꾼 수다 모임, 카메라 대화, 전황 트리거, 모델 설정을 제공하며, 상단 바에서 고른 인스턴스를 대상으로 동작합니다.

명령줄로도 할 수 있습니다:

python speech/speak_launch.py              # 로컬 모델 서버 시작 + 모델 로드 및 워밍업 + 말풍선 API 시작
python speech/speak_launch.py --restart    # 코드를 고친 뒤 API 재시작
python speech/speak_launch.py --stop       # API를 멈추고 모델을 VRAM에서 내림

모든 단계가 “이미 있으면 건너뛰기”이므로 여러 번 실행해도 부작용이 없습니다.

HTTP API

기본 주소는 http://127.0.0.1:8872/입니다(포트는 openwar3.json의 ports.speech). 어떤 프로그램이든 호출할 수 있습니다.

유닛이 말하게 하기 POST /api/say

{
  "inst": 16,
  "bubbles": [
    { "unit": "0x14A12614", "name": "마운틴 킹", "text": "나를 따르라!" },
    { "unit": "0x14A12924", "name": "아크메이지", "text": "눈보라는 내가 맡지.",
      "style": { "font_px": 26, "text_color": "#FFE080", "bg_color": "#C0102040" } },
    { "screen": [960, 110], "key": 1, "name": "내레이션", "text": "첫 번째 오크 부대가 30초 후 도착합니다.",
      "style": { "tail": false, "type_ms": 0 } },
    { "world": [-4684, 2644], "key": 2, "text": "집결 지점", "style": { "font_px": 16 } }
  ]
}
필드설명
unit / world / screen셋 중 하나: 유닛을 따라다님(체력 바가 있으면 체력 바 바로 위에 붙음) / 맵 좌표 / 화면 픽셀(내레이션용)
name첫 줄에 표시되는 화자. 자유롭게 쓰면 되며 해당 유닛일 필요는 없음
text본문. 자동 줄바꿈
duration_ms표시 시간. 0 = 자동 3 ~ 5초
key월드 / 화면 말풍선의 번호. 같은 key의 새 메시지가 이전 메시지를 대체
update같은 말풍선이 이미 있으면 글자만 바꾸고 타이머는 초기화하지 않음(스트리밍 출력용)
stylefont_px, max_width_px, text_color, bg_color, border_color, tail, side_px, opacity, type_ms, font……

말풍선은 동시에 최대 32개까지 띄울 수 있으며, 프레임당 오버헤드는 평균 약 0.1 ~ 0.2 ms입니다.

로컬 모델과 대화하기 POST /api/chat

{
  "inst": 16, "unit": "0x14A12614", "name": "마운틴 킹",
  "persona": "너는 워크래프트의 마운틴 킹 무라딘을 연기한다. 호탕하고 술을 좋아한다. 구어체로 한두 문장, 40자 이내.",
  "message": "앞에 오우거 무리가 있는데, 돌격할까?",
  "stream": true
}

{"reply": "...", "first_token_ms": 283, "total_ms": 342}를 반환하며, 그와 동시에 답변이 이미 해당 유닛 머리 위에 나타나 있습니다. 같은 유닛은 최근 6턴의 대화를 기억합니다.

기타

API설명
GET /api/instances실행 중인 게임
GET /api/units?inst=16&mine=true&heroes=true유닛 목록(중국어 이름, 좌표, 체력 포함)
POST /api/clear말풍선 하나 또는 전부 지우기
GET /api/llm, POST /api/llm모델 설정 보기 / 변경(base_url, model, max_tokens, temperature)
POST /api/banter일꾼 수다 모임: 본진의 일꾼들이 캐릭터 설정에 따라 돌아가며 투덜거리고, 게임 시작 때 소개 멘트를 함(전황은 모두 실제 데이터)
POST /api/camtalk카메라 대화: 화면에 잡힌 영웅과 수행원이 신분에 맞게 대화
POST /api/events전황 트리거: 교전 시작, 교전 종료, 영웅 사망, 티어 업, 건물 파괴…… 일이 생겼을 때만 말함

로컬 모델 고르기

RTX 5090 한 장에서 실측한 결과입니다(게임 대사 5문장).

모델VRAM속도답변 1회결론
Qwen3.6-35B-A3B(MoE, 매번 3B만 활성화), Q4, 사고 끔20.6 GB약 142 token/s약 0.3초(첫 토큰 약 0.27초)추천: 빠르고, 중국어 롤플레이가 자연스러움
gpt-oss-20b(MXFP4), 추론 low11.3 GB약 280 token/s0.3 ~ 0.8초VRAM이 빠듯할 때 사용. 중국어가 다소 밋밋함
Qwen3.6-27B(dense), Q417.2 GB약 39 token/s5.5초가 지나도 아직 사고 중실시간 대화에 부적합
  • 속도는 총 파라미터가 아니라 “매번 활성화되는 파라미터 수”가 결정합니다: 35B MoE는 3B만 활성화하므로 27B dense 모델보다 3 ~ 4배 빠릅니다.
  • “사고(thinking)“는 반드시 꺼야 합니다: 끄지 않으면 토큰을 모두 사고에 써 버려 답변이 한 글자도 나오지 않습니다.
  • 말풍선은 초당 약 22자씩 한 글자씩 찍히므로 생성 속도는 더 이상 병목이 아닙니다. 체감에 실제로 영향을 주는 것은 첫 토큰 지연입니다.

대사를 “진짜처럼” 만들기

모델에 주는 전황은 모두 실제 데이터(경기 수, 승패, 병력, 보유 자원)로 채우고, “이 사실만 사용할 것”을 명시하세요. 실측해 보니 이 제한을 두지 않으면 모델이 일어나지도 않은 전투를 지어냈습니다.