[인공지능 개발자] 게임 개발용 AI 하네스를 설계하는 방법
- 게임에서 LLM을 효과적으로 쓰려면 코드뿐 아니라 게임 상태, 데이터, 실행 결과를 에이전트가 읽고 조작할 수 있게 만들어야 함
- 결정론적 상태 재현과 저장/불러오기를 개발 초기부터 설계하면, 에이전트가 같은 조건에서 변경을 반복 검증할 수 있음
- 단일 객체 타입과 명령 패턴으로 상태 변경을 구조화하고 데이터를 외부로 분리하면, 재컴파일 없이 게임을 조작하고 수치를 시험하는 반복 작업이 가능함
- 게임 시뮬레이션을 엔진과 별도 라이브러리로 분리하고, 이미지 차이와 텍스트 프로파일링 결과를 제공하면 에이전트가 변경 효과를 더 쉽게 평가할 수 있음
- 이 구조에서는 게임 자체가 하네스가 되어 전체 반복 주기를 수초 안에 돌릴 수 있음. 다만 주된 적용 대상은 큰 프로젝트이며, 작은 게임에는 이런 설계가 필요하지 않을 수 있음
웹 개발과 달리 게임이 LLM에 불투명한 이유
- mood.site는 ChatGPT 웹 인터페이스에 코드를 복사해 넣는 방식으로 약 2~3주간 밤에 작업해, 총 20시간 정도에 구현한 사례임
- 직접 검증할 부분이 많고 작업 방식도 불편했지만, 실제로 작동하는 사이트를 완성했으며 이후 LLM 활용의 시험대가 됨
- Claude Code와 후속 Claude 모델을 사용한 개인 경험에서는 웹 기술 스택으로 구현하지 못할 일이 거의 없다고 느낄 만큼 발전했음
- 게임 개발에서는 반대로 LLM이 제대로 작업하지 못하는 경험이 반복됨
- 약 15년의 게임 프로그래밍 경험에 비해 Svelte 웹 앱 경험은 적다는 점이 코드 품질 판단에 편향을 줄 수 있음
- 공개된 고품질 웹 프로젝트가 많다는 점은 웹 코드 품질을 기대할 근거지만, 게임 학습 데이터가 훨씬 적다는 것은 추정임
- 게임에는 코드로 쉽게 표현하기 어려운 에셋과 여러 시스템이 있으며, 웹에서 다루는 텍스트와 레이아웃도 그중 일부에 불과함
- Three.js 기반 일회성 게임 생성은 에셋을 단순한 상자형 기본 도형으로 표현해 에셋 제작, 규격 맞춤, 처리 작업을 상당 부분 건너뜀
- 이런 결과물은 LLM이 만드는 평균적인 형태를 벗어난 창작자의 개성이 부족한 경우가 많지만, 모든 요소를 코드로 표현한다는 점에서는 LLM이 이해하고 수정하기 쉬움
- 기존 게임에서 중요한 비코드 요소가 불투명하게 남아 있으면, LLM은 그 요소를 조작하거나 변환하는 작업에서 막힘
- 여기서 다루는 문제는 사회적 영향이 아니라 개발 도구로서의 역량임
- 게임에서 LLM을 잘 쓰는 문제를 게임이나 LLM의 본질적 한계보다 컨텍스트 문제로 접근함
컨텍스트 관리와 하네스의 역할
- LLM 호출의 기본 구성은 시스템, 어시스턴트, 사용자 프롬프트로 구분할 수 있음
- 시스템 프롬프트는 서비스 사용자에게 공통으로 적용되는 모델의 기본 지침이며, 공개되거나 유출된 사례를 참고할 수 있음
- 어시스턴트 프롬프트는 입력에 대한 모델의 응답과 상호작용을 안내하는 부분임
- 사용자 프롬프트에는 채팅과 코드 등 대부분의 입력이 들어가며, 컨텍스트는 주로 사용자와 어시스턴트 영역에 쌓인 토큰을 가리킴
- 세션에서 새 메시지를 보낼 때 이전 메시지도 함께 다시 전달되므로, 과거의 잘못된 정보가 이후 작업에 계속 영향을 주는 컨텍스트 오염이 발생할 수 있음
- 게임마다 요구 사항이 달라 엔진이 있어도 모든 게임에 통하는 제작 템플릿이나 표준 도구를 기대하기 어려움
- 하네스(harness) 는 예상 입력에 대해 에이전트가 취할 행동 패턴을 구성한 것으로, Claude Code 같은 도구도 여기에 해당함
- 특정 작업에 맞게 확장하거나 추상화할 수 있으며 브라우저 반복 작업, 원격 측정, 프로파일링, 티켓과 문서 작성 같은 소프트웨어 개발 생명주기 작업까지 연결할 수 있음
- 하지만 코드만 보는 하네스로는 게임의 비코드 요소를 다루는 문제가 해결되지 않음
- 필요한 것은 게임이라는 블랙박스를 열어 더 많은 요소에 LLM이 직접 접근할 수 있게 만드는 것임
- 아래 기법 상당수는 AI 없이도 게임 개발에 도움이 되는 설계이며, AI와 결합하면 효과가 커짐
고정된 난수와 결정론적 상태 재현
- 결정론의 목표는 특정 게임 상태를 쉽고 정확하게 재현하는 것임
- 개발 막바지에 도입하면 근본적인 구조 변경이 필요할 수 있으므로, 첫 코드부터 설계 제약으로 삼는 편이 좋음
- 게임 시작 시 시드가 있는 단일 난수원을 만들고, 비결정적 동작에 필요한 난수 호출을 모두 이 경로로 통일함
- 같은 순서로 호출하면 30번째 난수 호출도 매번 같은 값을 얻는 식으로 상태 재현의 기반을 마련할 수 있음
- 같은 상태를 처음부터 반복 재현할 수 있어야 에이전트가 상태 복원 실패에 시간을 쓰지 않고 변경 자체를 검증할 수 있음
스크린샷보다 이미지 차이를 평가하기
- 엔진의 스크린샷 기능과 함께 UI와 게임플레이 레이어를 개별적으로 끄고 켜는 기능을 마련함
- UI 스타일을 수정할 때 뒤쪽 게임플레이를 숨기는 식으로, 평가 대상과 무관한 시각적 정보를 줄일 수 있음
- 변경 전후 스크린샷을 그대로 주는 것만으로는 멀티모달 LLM도 무엇이 바뀌었고 목표에 가까워졌는지 판단하기 어려울 수 있음
- 이미지에서 무관한 부분까지 처리하면서 생기는 잡음도 컨텍스트 오염이 될 수 있음
- 예를 들어 행사 홈페이지의 해골만 해적으로 바꾸려 해도 모델은 전체 이미지를 처리함
- Google Stitch처럼 이미지에 요청을 주석으로 붙일 수 있더라도, 나머지 화면 정보가 함께 전달되는 문제는 남음
- 이미지 차이 비교와 잘라내기를 결합하면 변경 영역과 목표 이미지와의 차이를 더 명확하게 평가할 수 있음
- 무료 명령줄 도구 odiff는 두 이미지의 차이를 이미지로 출력하고, ‘85% 유사’와 같은 정량적 비교 값도 제공함
- 에이전트는 이 값을 반복 작업 사이의 안정적인 평가 기준으로 삼아 차이를 줄이거나 유사도를 높일 수 있음
- 시각적 변경이라면 내부 수정과 검증을 여러 차례 수행한 뒤 스크린샷 비교로 돌아오는 반복 구조를 만들 수 있음
- 목표는 원하는 결과에 필요한 최소 변경을 빠르게 검증하는 것임
- 거의 독립적으로 평가할 수 있는 안정적이고 단순한 기준 상태를 유지해, 그 위에 쌓는 변경이 광범위한 기술 부채로 번지지 않도록 함
저장/불러오기와 Thing 중심 개발
- 결정론적 상태에 얼마나 가까운지는 정확히 같은 상태를 얼마나 쉽게 저장하고 불러올 수 있는가로 확인할 수 있음
- 저장/불러오기를 나중에 추가할 기능으로 보지 말고, 개발 시작부터 함께 설계해야 함
- 강한 객체지향 계층에서는 서로 다른 객체처럼 보이는 대상이 실제로는 비슷한 구성 요소의 조합인 경우가 있음
- 나무는 메시와 충돌체를, 개는 메시와 충돌체에 이동 능력을 더 가짐
- 이동은 개에만 고유한 성질이 아니므로, 코드 관점에서는 둘 다 구성 요소를 조합한 대상으로 볼 수 있음
- Thing 중심 개발(Thing-Driven Development) 은 모든 대상을 하나의
Thing 타입으로 표현하는 접근임
- 공통 수치 필드와
isAlive 같은 생존 상태를 두고, 구체적인 종류는 열거형 값으로 구분해 처리함
- 100개 클래스와 상속 관계를 이해한 뒤 확장 위치를 결정하는 대신, AI가 수정할 단일 기준점을 갖게 됨
- 단일 객체 타입은 직렬화와 역직렬화 경로도 단순하게 만듦
- 게임을
Thing의 거대한 목록으로 보고 하나의 경로로 저장하고, 역순으로 불러올 수 있음
- 여러 타입과 상속 트리에서는 역직렬화 순서, 객체와 자식 연결, 다형적 역직렬화 같은 복잡성이 생길 수 있음
- 같은 방식으로 턴 번호를 필드에 넣고
GameManager 종류를 두어 게임 전체 상태도 표현할 수 있음
명령 패턴으로 상태를 직접 조작하기
- 단일 대형 구조체 방식에서는 모든 변경 대상이 같은 타입이므로, 명령 패턴(Command Pattern)을 적용하기 쉬움
- 객체를 느슨한 함수 호출로 직접 변경하는 대신, 변경 요청을 가능하면 직렬화할 수 있는 객체로 만들고 처리기를 거쳐 적용함
- 시작 상태와 플레이어의 명령 기록이 있으면, 버그가 난 상황을 명령 재생으로 재현할 수 있음
- 플레이어에게 행동을 물어보고 사람이 다시 플레이할 필요 없이, AI 에이전트도 기록을 재생하며 변경 영향을 확인할 수 있음
- 명령은 게임 안에서만 생성할 필요가 없음
- 사람이나 AI가 텍스트 편집기에서 명령을 작성해 실행 중인 게임에 전달하면 재컴파일 없이 조작할 수 있음
- 타입이 있는 공통 인터페이스 덕분에 매번 별도 처리 로직이나 리플렉션을 추가할 필요가 줄어듦
- 컴파일을 기다리지 않고 시험할 수 있는 범위가 넓을수록 에이전트의 실행과 검증 주기가 짧아짐
정적 데이터를 코드 밖으로 분리하기
- 여기서 데이터는
Type, attackDamage, walkSpeed, boatSpeed처럼 객체의 기본 특성을 정의하는 정적 운영 데이터를 뜻함
- 게임 중 수정 효과가 적용되거나 플레이어에게 수치가 직접 보이지 않더라도, 객체의 기본 형태를 규정하는 값임
- 이런 값을 코드에 직접 넣으면 기본 이동 속도 하나를 바꾸는 데도 재컴파일하거나 외부 호출용 함수 바인딩을 작성해야 함
- 데이터를 게임 외부에 두고 역직렬화한 뒤 객체 생성과 연결하면, 데이터 원본을 바꾸는 방식으로 반복 작업이 가능함
- 개발자, 에이전트, 다른 팀원 모두 에디터 접근 없이 게임 빌드만으로 데이터를 시험할 수 있음
- Cantata 개발에서는 비프로그래머 팀원 약 7명에게 게임 빌드와 자체 데이터 편집기 Depot을 제공함
- 팀원이 Depot에서 값을 바꾸면 게임 빌드가 변경 데이터를 읽어 적용하므로 새 빌드를 받을 필요가 없었음
- 이 도구는 독립 제품으로 발전 중이며, 다른 팀은 Google Sheets나 자체 데이터 도구를 사용하기도 함
- AI에도 팀의 데이터 도구 사용법을 익히게 하면, 다른 팀원처럼 빌드를 반복 생성하지 않고 변경을 시험할 수 있음
시뮬레이션을 엔진 밖의 독립 라이브러리로 만들기
- 게임 로직과 UI, 시각 효과가 섞이면 간단한 테스트나 버그 재현에도 모델, 사운드, 장면을 포함한 게임 전체 실행이 필요해짐
- 문제가 시뮬레이션, 모델, 장면 설계 중 어디에서 발생했는지도 구분하기 어려워짐
- 블랙박스 게임 아키텍처(Blackbox Game Architecture) 에서는 시뮬레이션을 엔진 밖의 별도 DLL이나 라이브러리로 둠
- 엔진은 명령을 보내고, 시뮬레이션은 갱신된 상태를 반환함
- 엔진은 전체 상태 또는 이전 상태와의 차이를 해석해 화면과 소리를 갱신하며, 시뮬레이션이 상태의 기준이 됨
- 이동의 경우 애니메이션, 모델, 효과음, 이동 시작과 종료, 시야 갱신 등이 얽혀 있어도 핵심은 입력에 따라 A에서 B로 위치가 바뀌는 것임
- 엔진이
Thing의 이동 요청을 보내면 시뮬레이션이 목적지의 유효성을 확인하고 이동 결과를 반환함
- 엔진에서 먼저 걸러낼 수도 있으며, 반환 상태 비교나 승인된 명령 버퍼 등을 통해 이동을 감지할 수 있음
- 최종적으로 시뮬레이션이 이동했다고 판단하면 엔진은 그 결과에 따라 객체를 움직임
- 분리된 코어에는 어떤 주체든 명령을 보낼 수 있음
- 상태와 차이만 소비하는 구조에서는 결과가 로컬, 네트워크, LLM 중 어디에서 왔는지가 중요하지 않아 네트워크 구현도 쉬워짐
- 상태, 로직, 데이터의 강한 결합 때문에 필요했던 복잡한 테스트 준비도 줄일 수 있음
- 엔진 없이 순수 C# 프로젝트와 표준 도구로 시뮬레이션을 빠르게 테스트할 수 있음
- 플레이어 경험을 구현하는 층과 분리해 시뮬레이션, 가설 검증, 테스트를 직접 수행할 수 있음
- Brian Cronin의 Roguelike Celebration 발표는 Monster Train에 적용한 방식을 다루며, Solium Infernum 팀도 Game Data Podcast에서 유사한 기법을 소개했음
텍스트 프로파일링으로 변경 영향을 평가하기
- 변경이 실제 게임에 원하는 영향을 줬는지는 영향 평가로 확인해야 함
- 일반적인 테스트뿐 아니라 고정된 게임플레이 시나리오로 밸런스 영향을 확인하는 방법도 있지만, 구체적인 구성은 게임마다 달라짐
- 공통적으로 중요한 성능 평가는 보통 Unity/Unreal 내장 프로파일러나 dotMemory 같은 사람 중심 도구에 의존함
- 사람이 호출 스택과 그래프를 살펴보는 인터페이스는 에이전트가 읽기 어려움
- Unity도 프로파일러를 에이전트가 더 잘 읽을 수 있게 하는 작업을 진행 중임
- AI가 읽기 쉬운 텍스트 형태의 원시 캡처 결과를 제공하면 반복 평가를 돕기 쉬움
- 무료 도구 samply는 프로젝트에 연결해 실행하고 캡처 결과를 콘솔로 출력할 수 있음
- 에이전트가 소스와 문서를 읽어 평가 방식을 파악하면, 캡처와 수정의 반복을 사실상 자율적으로 수행할 수 있음
게임 자체를 하네스로 만들되 규모에 맞게 적용하기
- 결정론적 입력, 구조화된 상태 변경, 외부 데이터, 독립 시뮬레이션, 이미지 비교, 성능 평가를 연결하면 게임 중심 하네스가 됨
- 데이터 중심이며 바이너리로 분리되고 재컴파일이 필요 없는 작업 흐름에서는, 에이전트가 전체 반복 주기를 수초 안에 수행할 수 있음
- 각 단계가 게임과 작업 목표를 더 잘 이해하도록 돕고, 충분히 빠른 검증은 중요한 시점에만이 아니라 매 변경마다 실행할 수 있음
- 다만 이 설계가 모든 프로젝트에 필수인 것은 아님
- 실제로 출시 작업 중인 아주 작은 프로젝트에는 이 기법들을 전혀 사용하지 않았으며, 별도의 하네스 작업이 많이 필요하지 않았음
- 주된 적용 대상은 더 큰 게임 프로젝트이며, 규모가 커질 때 이런 도구와 구조의 필요성이 커짐
[출처] https://news.hada.io/topic?id=35355
본 웹사이트는 광고를 포함하고 있습니다.
광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.