로컬 LLM으로 'Slay the Spire 2' 에이전트 구축하기: 교훈과 미해결 과제

개발자가 KoboldCPP/Ollama를 통해 로컬 LLM을 사용하여 Slay the Spire 2를 플레이하는 에이전트를 구축했습니다. 게임은 커뮤니티 모드를 통해 REST API로 노출되며, 에이전트는 중간에 위치합니다: 게임 상태 읽기 → 도구와 함께 LLM 호출 → 액션 실행 → 반복.
설정 및 성능
설정은 KoboldCPP를 통해 RTX 4090에서 Qwen3.5-27B(Q4_K_M)를 사용합니다. 성능 지표: 액션당 약 10초, 액션 성공률 약 88%. 최고 결과: Act 1 보스 격파. 프로젝트는 GitHub에서 확인할 수 있습니다: https://github.com/Alex5418/STS2-Agent.
작동하는 것들
- 상태 기반 도구 라우팅 — 한 번에 20개 이상의 도구를 노출하는 대신, 현재 게임 상태와 관련된 1-3개의 도구만 제공됩니다. 전투에는
play_card,end_turn,use_potion이 제공됩니다. 맵 화면에는choose_map_node가 제공됩니다. 이로 인해 환각 도구 호출이 극적으로 감소했습니다. - 단일 도구 모드 — 작은 모델은 액션 후 게임 상태가 어떻게 변하는지 예측할 수 없습니다(예: 카드를 플레이한 후 카드 인덱스가 이동함). 따라서 응답당 첫 번째 도구 호출만 실행된 후 게임 상태를 다시 가져오고 모델에게 다시 요청합니다. 느리지만 훨씬 더 신뢰할 수 있습니다.
- 텍스트 기반 도구 호출 파서(폴백) — KoboldCPP는 종종 구조화된 JSON 대신 텍스트로 도구 호출을 출력합니다. 다중 패턴 정규식 폴백은 다음과 같은 형식을 포착합니다:
json [{"name": "play_card", "arguments": {...}}],Made a function call ... to play_card with arguments = {...},play_card({"card_index": 1, "target": "NIBBIT_0"}), 그리고end_turn과 같은 인수가 없는 도구의 단순 언급. 이는 그렇지 않으면 손실될 액션의 약 15-20%를 복구합니다. - 에너지 가드 — 남은 에너지의 클라이언트 측 추적. 모델이 감당할 수 없는 카드를 플레이하려고 하면 API 호출이 차단되고 턴이 자동으로 종료됩니다. 이는 가장 일반적인 오류 루프(모델이 동일한 감당할 수 없는 카드를 3회 이상 재시도)를 방지합니다.
- 적 턴을 위한 스마트 대기 — 적의 턴 동안 게임 상태는 "Play Phase: False."라고 표시합니다. 이에 대해 LLM 호출을 낭비하는 대신, 에이전트는 플레이어의 턴이 다시 될 때까지 매 1초마다 폴링합니다.
개방형 문제
- 모델이 시스템 프롬프트 규칙을 일관되게 따르지 않음 — 시스템 프롬프트는 "적의 의도가 공격이면, 방어 카드를 먼저 플레이하세요."와 같은 내용을 말합니다. 모델은 이를 약 30%의 경우에 따릅니다. 나머지 70%는 관계없이 공격만 플레이합니다. 시도된 해결책: 더 강력한 어조("반드시 먼저 방어해야 합니다"), 프롬프트의 몇 가지 샘플 예시, 계산된 힌트 주입("경고: 15의 들어오는 피해"). 어느 것도 신뢰할 수 없습니다. 질문: 작은 모델이 조건부 규칙을 따르도록 하는 더 나은 프롬프트 전략이 있을까요? 아니면 이것이 27B에서의 근본적인 한계일까요?
- KoboldCPP와의 도구 호출 신뢰성 — 텍스트 폴백 파서가 있더라도, 약 12%의 응답은 사용 가능한 도구 호출을 생성하지 않습니다. 모델은 때때로 빈
<think></think>블록 뒤에 잘못된 JSON을 출력합니다. Ollama OpenAI 호환성 레이어도 가끔arguments를 딕셔너리 대신 문자열로 반환합니다. 질문: 14-30B 범위에서 도구 호출에 특히 신뢰할 수 있는 모델을 찾은 사람이 있을까요? 개발자는 Phi-4(14B)를 잠시 시도했지만 적절한 비교는 하지 않았습니다. Mistral-Small이나 Command-R을 고려 중입니다. - 컨텍스트 창 관리 — 각 게임 상태는 마크다운으로 약 800-1500 토큰입니다. 시스템 프롬프트(약 500 토큰)와 대화 기록을 포함하면 컨텍스트가 빠르게 채워집니다. 현재는 마지막 5개의 교환만 유지하고 상태 전환(전투 → 맵 등) 시 기록을 재설정합니다. 그러나 모델은 전투 간에 기억이 없습니다 — 실수로부터 배울 수 없습니다. 질문: 롤링 요약 접근 방식이 작동할까요? 예: 마지막 전투를 "당신은 Jaw Worm와 싸웠습니다. 턴 2에 방어하지 않아 15의 피해를 입었습니다. 4턴 만에 승리했습니다."로 압축하는 것.
- 로컬 모델의 더 나은 구조화된 출력 — 핵심 문제는 모델이 JSON 도구 호출을 출력해야 하지만, 실제로는 먼저 자연어로 생각하고 싶어 한다는 것입니다. Qwen3.5는 제거되는
<think>블록을 사용하지만, 때로는 생각과 도구 호출이 뒤섞입니다. 질문: 두 단계 접근 방식이 더 잘 작동할까요? 1단계: "게임 상태를 분석하고 무엇을 할지 결정하세요"(자유 텍스트). 2단계: "이제 정확히 하나의 도구 호출을 출력하세요"(제약됨). 이는 지연 시간을 두 배로 늘리지만 신뢰성을 향상시킬 수 있습니다. 이 패턴을 시도한 사람이 있을까요? - 모델 간 A/B 테스트 — 개발자는 비교를 위해 액션을 기록하는 JSONL 로깅 시스템을 가지고 있습니다.
📖 전체 소스 읽기: r/LocalLLaMA
👀 See Also

클로드 AI를 사용하여 사용자 기록에서 성과 평가 문서 생성
한 개발자가 Claude AI에게 '나에 대한 정보를 사용하여 이 문서를 완성해줘'라고 요청하여 3-4페이지 분량의 성과 평가 문서를 완성했습니다. AI는 5-6분 만에 사용자가 거의 잊어버렸던 업무 기여까지 포함한 상세한 문서를 생성했습니다.

OpenClaw와 Retell AI를 활용한 지역 비즈니스를 위한 AI 리셉셔니스트 도입
한 개발자가 OpenClaw와 Retell AI를 사용하여 지역 서비스 업체를 위한 AI 안내원을 배포한 사례 연구를 공유했습니다. 첫 주에 23건의 통화를 처리하여 7건의 예약을 확보했으며, 비용은 4.12달러였습니다.

한 개발자가 AI 에이전트 시스템의 16가지 아키텍처 약점을 어떻게 해결했는가
한 개발자가 OpenClaw AI 에이전트 시스템에서 발견한 16가지 아키텍처 문제를 문서화하고, 명시적 레이어 정의, 게이트웨이 인증, 증거 기반 실행 등의 구체적인 해결책을 구현했습니다.

OpenClaw 패밀리 게이트웨이: 토큰 예산, 메모리 튜닝, 그리고 커스텀 플러그인
한 개발자가 Mac과 QNAP NAS에서 OpenClaw를 사용하여 가족용 AI 게이트웨이를 구축했습니다. 엄격한 토큰 예산을 설정하고, 재순위화와 상황별 임베딩으로 메모리 회상을 조정하며, 175개 이상의 명령어를 가진 12개의 맞춤 플러그인을 만들었습니다.