AI 협업 도구 — 런북과 자동화된 워크플로우를 통해 혼자서도 40만 줄 규모의 코드를 관리함
지속적 업데이트 — 매일 3~5회의 프로덕션 릴리스를 통해 게임 메커니즘과 시스템을 빠르게 개선함
지난 1년 동안 LLM 기반 인생 시뮬레이션 게임인 Slow Vale을 혼자서 만들었다. 현재 중국 서버에는 800명이 넘는 AI 주민들이 하나의 끊임없이 돌아가는 도시에서 살고 있다. 이 글은 동시 결정, 동적 행동 공간, 컨텍스트 캐싱, 그리고 지속적인 멀티 에이전트 시스템을 운영하는 데 드는 비용에 관한 엔지니어링 기록이다.
현재 런타임은 로컬 추론 대신 호스팅된 DeepSeek Flash를 사용한다. 나는 이 게임의 개발자다. 원문은 중국어로 작성했고, AI를 사용해 영어로 번역하고 다듬었다. 아래 제품 지표는 2026년 10월 7일 기준이다.
끊임없이 돌아가는 세상에서의 비동기적 결정
각 캐릭터는 하루에 대략 300~400번의 LLM 호출을 수행하며, 호출당 평균 컨텍스트는 약 30,000 토큰 정도다. 호출에는 캐릭터의 상태, 관련 경험, 현재 환경, 그리고 가능한 행동들이 포함된다. 모델이 행동과 그 매개변수를 선택하면, 백엔드는 그 결정을 시간과 자원을 소모하는 활동으로 변환한다.
게임 시간과 현실 시간은 공존한다. 잠을 자는 건 게임 내 시간으로 8시간을 잡아먹을 수도 있고, 문장 하나를 말하는 건 게임 내 시간으로 1분이 걸릴 수도 있다. 추론 자체에는 현실 시간이 소요된다. 호출이 진행되는 동안 다른 캐릭터들이 환경을 바꿀 수도 있고, 세계의 시계는 계속 흘러간다.
상호작용에는 상호 배제도 적용된다. A가 B와 대화 중이라면, C가 동시에 B를 다른 대화로 끌어들일 수 없다. 시설, 생산 작업, 기타 활동들은 자원을 점유하고 해제하는 각자의 규칙을 가지고 있다.
동시 추론과 세계 상태 변경의 분리
LLM 호출은 동시에 실행될 수 있지만, 모델의 응답이 세계를 직접 바꾸지는 않는다. 결과는 세계의 실행 흐름으로 돌아와 유효성 검사를 거친 뒤, 세계 상태를 관리하는 실행 컴포넌트에 의해 적용된다.
예를 들어, 캐릭터가 추론을 시작할 때는 선반에 마지막 물고기가 남아있을 수 있다. 하지만 응답이 도착할 때쯤이면 다른 주민이 이미 그걸 사버렸을 수도 있다. 구매 의도는 현재 재고 상태에 맞춰 다시 확인해야 한다. 마찬가지로 모델이 대화하고 싶어 하는 상대가 이미 자리를 떴거나, 잠들었거나, 다른 활동을 시작했을 수도 있다.
따라서 결정을 내릴 때 사용한 컨텍스트와 실행 시점의 상태 사이에는 명확한 시간 차이가 존재한다. 시스템은 캐릭터가 무엇을 하려고 하는지, 그 행동이 여전히 유효한지, 그리고 실제로 어떤 결과가 발생했는지를 구분해야 한다. 완료, 실패, 중단, 복구 각각에 일관된 상태 전환이 필요하다.
시간을 점유하는 활동을 위한 공유 런타임
이동, 생산, 대화, 수면은 지속 시간, 참여자, 완료 조건이 모두 다르다. 공통 런타임을 사용하면 모든 메커니즘마다 별도의 스케줄러를 만들지 않고도 바쁜 캐릭터, 자원 충돌, 서비스 복구를 관리할 수 있다.
프론트엔드 또한 실제 진행 상황을 따라가야 한다. 활동이 언제 시작됐는지, 얼마나 진행됐는지, 완료됐는지, 무엇을 생산했는지 말이다. 로그와 장면 애니메이션은 백엔드가 확정한 사실과 일치해야 한다. 이건 지속적인 세계를 구현할 때 복잡성을 유발하는 주된 요인이다. 하나의 이벤트가 미래의 결정, 데이터 영속성, 다른 주민들, 그리고 플레이어 인터페이스에까지 영향을 미칠 수 있기 때문이다.
결정 컨텍스트는 백엔드 아키텍처의 일부
오랜 기간 행동하는 캐릭터에게 성격 묘사만으로는 부족하다. 각 결정에는 캐릭터의 현재 욕구, 위치, 자산, 지속적인 고민, 관련 관계, 그리고 그 순간 실제로 가능한 행동들이 필요하다.
이런 입력값들은 업데이트 주기와 수명이 제각각이다. 성격은 비교적 안정적이지만, 허기나 에너지는 계속 변하고, 인벤토리나 다른 캐릭터의 상태는 몇 초 만에도 바뀔 수 있다. 어떤 경험은 한참 지난 뒤에도 관계에 영향을 미칠 수 있다. 각 정보 유형마다 컨텍스트에 진입하고, 업데이트되고, 캐릭터의 현재 관심사에서 벗어나는 규칙이 필요하다.
액션 스페이스도 게임 상태를 제대로 반영해야 함. 모델한테 제시하는 옵션은 실행 조건이랑 관련 상태를 명확히 알려줘야 하고, 최종 검증은 백엔드에서 처리해야지. 안 그러면 캐릭터들이 맨날 안 되는 행동만 하려고 덤비거나, 규칙 파악하겠다고 API 호출만 주구장창 날리면서 시간 다 버림.
이 부분에 진짜 공 많이 들였음. 안정적인 정보랑 유동적인 정보 구분하고, 쓸데없는 히스토리 쌓이는 거 막고, 중복 알림 피하고, 컨텍스트 프리픽스 고정하는 거까지. 이게 행동 퀄리티, 추론 지연 시간, 캐시 적중률에 다 직결되니까 사실상 백엔드 아키텍처의 핵심임.
하루 토큰 50억 개, 모델 비용은 100달러 미만
현재 중국 서버는 하루에 토큰 50억 개 정도 처리하는데 모델 비용은 100달러도 안 나옴. DeepSeek Flash 같은 저렴한 모델 위주로 쓰면서 캐시 적중률은 90% 이상 유지 중임.
토큰 수에는 캐시된 입력값도 포함됨. 호출 빈도 높고 캐릭터 많고 컨텍스트 긴 시스템에서는 재사용 가능한 고정 프리픽스가 비용에 직빵으로 영향을 줌. 어떤 정보를 고정하고, 어떤 걸 매번 바꿀지, 순서는 어떻게 할지 하나하나 다 계산해서 설계해야 함.
2026년 10월 7일(GMT+8) 기준 실제 DeepSeek 사용량 및 비용: 전체 API 키 합산 약 44억 2,400만 토큰, 요청 163,742건, 비용은 472.33 위안. 해당 일자에 사용된 모델은 deepseek-flash임.
동적 액션 스페이스와 프리픽스 캐싱 사이의 트레이드오프
엔지니어링 하면서 겪은 진짜 고민은 툴 콜링이나 구조화된 출력을 쓸 때 동적 액션 스페이스를 어떻게 표현하느냐였음. 주변에 뭐가 있는지, 살 수 있는 물건은 뭔지, 누구랑 대화할 수 있는지 같은 건 매번 바뀌니까. 이걸 툴 정의나 출력 스키마에 직접 박아버리면 출력 제약은 확실해지는데, 스키마가 너무 자주 바뀜. 초기에 테스트했던 몇몇 API 구현체는 그 정의들이 요청 프리픽스에 포함되더라고. 스키마가 바뀌니까 그 뒤에 오는 안정적인 컨텍스트까지 캐시가 안 먹히는 문제가 생김.
그래서 나는 메인 경로에서는 그냥 JSON 텍스트 생성 방식을 택했음. 파싱이랑 검증은 백엔드에서 하고, 파싱 실패하면 엄격한 스키마를 쓰는 폴백(fallback)을 두는 식으로. 모델한테 상태 의존적인 액션 옵션을 주는 건 맞는데, 이걸 출력 스키마가 아니라 현재 의사결정 컨텍스트에 넣은 거지. 이렇게 하니까 안정적인 지침이랑 재사용 가능한 히스토리는 앞쪽에 두고, 현재 상태랑 액션 옵션은 뒤쪽으로 몰 수 있었음. 대신 메인 경로에서 디코딩 시점에 형식을 100% 보장받는 건 포기해야 했지. 앱 단에서 이상한 출력 처리하고, 실행 시점에 월드 상태랑 대조해서 액션이랑 파라미터 검증하는 과정을 다 짜야 했음.
결국 90% 넘는 캐시 적중률은 단순히 기능 하나 켠다고 나오는 게 아니라, 요청 구조 전체를 어떻게 짜느냐에 달린 거임. 이 수치는 캐시에서 처리된 입력 토큰 비율을 말하는 거고, 모델은 여전히 결정 내릴 때마다 새로운 출력을 만들어냄. 호출 방식을 비교할 때는 형식 신뢰도, 캐릭터 행동, 캐시 재사용, 지연 시간, 비용을 다 같이 고려해야 함.
이 수치들은 모델 비용만 따진 거임. 거주민 늘어나면 데이터베이스 부하, 상태 전달, 로그 저장, 장면 렌더링 같은 것도 다 문제거든. 추론 비용이 싸야 시뮬레이션이 계속 돌아가는 거지, 결국 시스템 전체의 자원 관리가 뒷받침돼야 운영이 가능함.
런북을 활용한 AI 협업 체계 구축
이 정도로 많은 모듈을 혼자 관리하려면 AI한테도 제대로 된 작업 환경을 줘야 함. 로그, Langfuse, 성장 분석, 데이터베이스 접근 권한, 그리고 프로덕션 서비스 유지보수 절차까지 포함된 개발 및 운영 툴을 AI한테 다 쥐여줬음.
내 Codex 사용량: 누적 토큰 약 432.5억 개, 최장 연속 사용 기록 85일. Codex는 내가 쓰는 AI 코딩 툴의 일부일 뿐임. 이건 개발용 수치고, 게임 속 주민들 돌리는 모델 호출량은 별개임.
운영 매뉴얼이 따로 있어서 그거대로 굴러감. 프로젝트 문서화도 엄청 빡세게 해놔서 작업별, 모듈별로 다 정리돼 있음. 작업마다 뭘 읽어야 하는지, 현재 계약(contract)을 정의하는 소스가 뭔지, 내가 직접 결정해야 할 건 뭔지, 구현이랑 실제 코드랑 다를 때 AI가 어떤 문서를 수정해야 하는지까지 다 정해져 있음.
작업 진입점과 행동 범위가 핵심임. 조사를 시작할 땐 데이터 소스랑 시간대부터 파악함. 쿼리 권한이 있다고 프로덕션 데이터를 수정할 수 있는 건 아니고, 코드를 고칠 수 있다고 배포까지 할 수 있는 건 아님. 툴을 쓸 땐 반드시 명확한 사용 조건이 붙음.
반복되는 유지보수 작업은 아예 자동화 워크플로우로 돌려버림. 프로덕션 문제 진단 및 수정, 캐릭터 행동과 게임 결과에 대한 매일 심층 리뷰, 다 쓴 유지보수 코드 매일 정리하기 같은 것들임. 워크플로우마다 필요한 증거, 허용된 행동, 검증 방식, 중단 조건이 다 정해져 있음.
분야마다 내 관여도는 다름. 프론트엔드/백엔드 계약, 백엔드 아키텍처, 상태 및 리소스 소유권 같은 건 내가 직접 결정하거나 깊게 관여함. 프론트엔드랑 Phaser 구현 쪽은 디자인 토큰, 페이지 구조, 재사용 가능한 컴포넌트, 표현 범위 같은 걸 정해주고 결과물 평가에 더 집중함.
이 방식은 프로젝트 지식이 계속 유지돼야 돌아감. 작업하다 발견한 제약 사항은 다시 공식 문서에 반영해야 하고, 구식 절차는 바로바로 고쳐야 함. 안 그러면 프로젝트 커질 때 AI가 옛날 가정만 믿고 그럴싸한 코드를 짜다가 다른 쪽 계약 다 깨먹는 사태가 벌어짐.
하루에 프로덕션 배포 3~5번
이 도시는 게임 시간으로 350일 넘게, 현실 시간으로는 거의 100일 가까이 돌아가고 있음. 초기 플레이어 상당수가 아직도 하고 있음. 백엔드, 프론트엔드, Phaser 씬, 콘텐츠 제작, 모니터링, 운영까지 전부 내가 혼자 다 만들었음. 지금 코드만 40만 줄이 넘고, 핵심 백엔드만 20만 줄 이상임. 커밋은 2,200개 정도 됨.
AI 코딩 툴을 엄청나게 써먹음. 제품 방향성, 핵심 메커니즘, 아키텍처 경계 같은 건 내가 결정하거나 깊게 관여하고, 구현이나 조사, 유지보수 같은 잡일은 AI한테 다 맡김. 프로토타입에서 계속 돌아가는 제품으로 넘어가면서 시스템 설계랑 개발 워크플로우가 업무의 큰 비중을 차지하게 됐음.
지금은 하루 평균 3~5번씩 배포함. 아키텍처 변경, 밸런스 및 게임플레이 조정, 새로운 시스템, UI/아트 변경, 성능 개선, 버그 수정까지 다 포함임. 커밋은 총 2,200개 정도인데, 개발 한창 할 때는 하루에 10개 넘게 찍어냄.
이런 반복 속도는 구현, 관찰, 조정으로 이어지는 짧은 피드백 루프에서 나옴. 플레이어들은 계속 같은 도시에서 살아가니까. 기능 하나 배포하면 실제 사용 현황이랑 캐릭터 행동을 관찰하고, 메커니즘을 바꿀지, 캐릭터가 받는 정보를 더 명확하게 할지, 아니면 구현 문제를 고칠지 결정함.
소프트웨어 운영이랑 게임플레이 결과는 따로 평가함. 에러율, 지연 시간, DB 부하, 모델 호출량은 시스템이 제대로 돌아가는지 확인하는 용도임. 캐릭터가 같은 말을 반복하는지, 새로운 메커니즘을 이해하는지, 생산 활동이나 사회적 활동을 잘 끝내는지 확인하려면 결국 걔네들이 겪은 실제 경험이랑 결정 과정을 다 읽어봐야 함.
그러니까 모니터링, 쿼리, 행동 평가, 복구 워크플로우 같은 것들이 매일 하는 개발 업무의 일부가 된 거지. 릴리즈를 자주 하려면 모듈 경계도 확실해야 하고, 검증 범위나 복구 절차도 깔끔해야 해. 임시로 짠 유지보수용 코드는 바로바로 치워야 하고. 안 그러면 개별 변경 사항들이 아무리 빨라도 시스템 유지보수가 갈수록 헬이 될 테니까.
가상 펫에서 몇 시간씩 보는 콘텐츠로
처음엔 그냥 가상 펫 같은 게임을 생각했어. 유저들이 하루에 한 번 들어와서 캐릭터 밥 먹었나 확인하고, 돈 좀 벌었나 보고, 메시지 하나 보내고 나가는 그런 거 말이야.
근데 실제로는 전혀 다른 패턴이 나오더라고. 어떤 유저들은 이걸 무슨 라이브 스트리밍 보듯이 하루에 몇 시간씩 캐릭터를 관찰해. 관계가 어떻게 발전하나 지켜보고, 가게에 손님은 왔나 확인하고, 방금 보낸 제안을 캐릭터가 따르나 안 따르나 기다리는 거지. 그래서 이 제품은 잠깐씩 확인하는 유저랑 계속 켜두고 보는 유저 둘 다 만족시켜야 하는 상황이 됐어.
지난 한 달 동안 중국 서버 일일 활성 유저(DAU)는 9월 10일 177명에서 10월 7일 865명으로, 대략 4.9배 정도 늘었어. 10월 1일부터 7일 사이에는 DAU가 431명에서 865명까지 뛰었지. 이 기간 성장은 주로 유저들이 자발적으로 게임을 공유하면서 일어난 거야.
중국 서버 DAU, 게임에 성공적으로 접속한 고유 유저 기준. 차트는 PostHog 쿼리 결과로 다시 그렸고, 날짜는 아시아/상하이 기준임.
8월에 처음 게임에 접속한 유저 225명을 기준으로 보면, 1일 차 리텐션은 68.9%, 7일 차는 56.0%, 30일 차는 40.9%였어(각각 155명, 126명, 92명이 복귀).
10월 7일 기준으로, 유효한 포그라운드 실행 기록이 있는 비관리자 유저 850명의 중앙값은 29.9분이었고, 상위 10%(P90)는 약 4시간 정도였어. 10월 1일부터 7일 사이에는 86명의 유저가 최소 4일 이상 접속했고, 활성 일당 평균 3시간 이상 게임을 켜뒀더라고.
같은 225명 코호트의 리텐션: 각각 155명, 126명, 92명이 복귀함.
포그라운드 사용 시간은 게임을 화면에 띄워둔 시간을 말하는 거지, 계속 집중해서 보고 있다는 뜻은 아니야. 하지만 유저 피드백이랑 종합해보면, 게임을 오랫동안 켜두는 고정 유저층이 확실히 있다는 걸 알 수 있어.
이게 엔지니어링 측면에서 요구사항을 까다롭게 만들어. 가끔 들어오는 유저는 자기가 없는 동안 무슨 일이 있었는지 알아야 하고, 계속 보는 유저는 캐릭터가 왜 이런 행동을 하는지, 뭘 기다리는지, 상호작용이 어떻게 끝나는지 다 이해해야 하거든. 그래서 활동 로그, 요약, 실시간 장면 같은 것들이 핵심 인터페이스가 되는 거야.
800명 넘는 주민이 공유하는 하나의 도시
도시 모습. 공유 환경 내의 상점과 작업장은 실제 게임 활동을 지원함.
유저들은 자기만의 성격을 가진 캐릭터를 만들고, 메시지나 선물을 보내서 영향을 주고, 그 캐릭터의 삶을 관찰해. LLM이 캐릭터의 이동, 식사, 수면, 업무, 사회적 상호작용을 다 결정하지. 다른 유저들이 만든 캐릭터들도 같은 세상에서 살아. 얘네들은 실시간으로 대화하고, 거래하고, 같이 밥 먹고, 사랑에 빠지고, 같이 살기도 해.
주민들은 먹고살아야 하니까 농장이나 목장을 운영하거나, 바다에서 낚시를 하거나, 사무실에서 일하거나, 가게를 차리거나, 시장 좌판에서 물건을 팔 수 있어. 이런 활동들은 공유 경제랑 연결돼. 주민들이 농산물을 생산하면 실제 재고가 쌓이고, 수요와 공급에 따라 가격이 변하고, 가게 주인들은 비용을 계산해서 물건을 사고 가격을 정해야 하거든.
모든 음식은 주민들의 노동으로 만들어짐. 식당 주인은 가게를 운영하고, 요리사는 음식을 만들고, 배달원은 주문을 배달하고, 손님은 돈을 내고 음식을 먹음. 모든 식사 뒤에는 재료, 생산, 서비스, 소비로 이어지는 체인이 있고, 도시 주민들은 이 모든 과정에 참여함.
농사와 목축. 이 네 장의 영어 쇼케이스 이미지는 게임의 기본 렌더러와 UI를 사용해 연출된 장면과 데모 데이터를 보여줌.
씨앗을 뿌리는 주민. 페이저(Phaser) 장면을 통해 일상적인 생산 활동을 시각화함.
농장 관리: 작물 성장, 가축, 사료, 생산 현황.
시장 인벤토리, 주민 상점, 가격 추이. 여기에 표시된 값은 데모 데이터임.
플레이어는 실시간 장면, 캐릭터 상태, 관계, 활동 로그를 볼 수 있고 캐릭터가 보낸 엽서도 받을 수 있음. 관계는 실제로 일어난 상호작용을 통해 쌓임. 캐릭터 삶에서 일어난 사건들은 나중에 내릴 결정의 맥락이 됨.
꿈은 최근에 추가된 기능임. 잠을 자는 동안 캐릭터는 자신의 경험을 바탕으로 꿈을 꾸고, 가끔 잠꼬대도 함. 예를 들어, 영어 서버의 '브래드 피트(Bread Pitt)'는 배달원이 이미 돈을 낸 햄버거를 들고 사무실 복도에서 자신을 쫓아오는 꿈을 꿨음. 문을 열 때마다 여전히 배고파하는 두 친구가 나타났고, 그는 잠결에 "그냥 문 앞에 두고 가..."라고 중얼거렸음.
영어 서버에서 실제로 발생한 꿈. 꿈과 잠꼬대는 기존의 결정 및 로깅 메커니즘을 사용해 수면 활동 로그에 나타남.
이런 디테일들이 플레이어에게 캐릭터 삶의 연속성을 느끼게 해줌. 하루의 일과, 친구, 혹은 거른 끼니 같은 것들이 나중에 다른 형태로 다시 나타날 수 있음.
지속 가능한 세계를 위한 엔지니어링
이 프로젝트는 캐릭터 프로토타입에서 시작해 계속 돌아가는 도시로 성장했음. 주민들은 인벤토리, 시설, 공간, 시간을 공유함. 한 캐릭터의 행동은 다른 캐릭터가 다음에 내릴 결정의 조건을 바꿈. 추론으로 생성된 행동은 현재 세계에서 유효해야 하고, 데이터 지속성, 인터페이스 전달, 서비스 복구 과정에서도 살아남아야 함.
플레이어들의 행동을 보면서 이 제품에 대한 내 생각도 바뀌고 있음. 하루에 한 번 확인하는 가상 반려동물이 될 수도 있고, 몇 시간씩 지켜보는 인생 시뮬레이션이 될 수도 있음. 장기 플레이어들은 캐릭터, 관계, 도시에 대한 지식을 쌓아가고, 이 연속성 자체가 경험의 중요한 부분이 됨.
앞으로도 이 지속 가능한 세계의 메커니즘, 표현 방식, 확장성을 계속 개선할 예정임. 영어 브라우저 버전은 slowvale.com에서 이용할 수 있음. 초대 코드는 필요 없고, 이메일 주소로 가입해서 바로 플레이하면 됨.
주요 댓글
r/localllama
사용자들은 800명 이상의 에이전트를 운영하는 기술적 구현 방식, 특히 캐시 활용 전략에 큰 관심을 보이며 프로젝트의 성과를 높게 평가하고 있습니다.
6
와, 이거 진짜 대박이네.
3
에이전트들이 컨텍스트 폭발 없이 어떻게 각자의 역사를 유지하는지 궁금하네요. 매일매일 정말로 지속성이 느껴지나요?
3
매일 캐릭터의 경험을 요약하고 있고, 장기 기억을 유지하기 위한 전용 작업들도 따로 있습니다.
2
미친 듯이 멋지네요. 근데 캐릭터한테 아주 부정적인 영향을 주는 것도 가능한가요? 살인, 강도, 마약 판매, 폭행, 마피아, 납치 같은 거요?
2
기술적으로는 구현하기 어렵지 않습니다. 하지만 현재 플레이어들은 대부분 평화로운 생활 시뮬레이션을 즐기고 있고, 플레이어 캐릭터들이 서로 범죄를 저지르게 하면 플레이어 간 갈등이 생길까 봐 걱정되네요. 절도, 사기, 강도 같은 건 이미 존재하지만, 현재로서는 NPC들이 수행하고 있습니다.
1
1
로컬 모델로 이걸 해보고 싶은 사람이 있다면: llama-server는 슬롯의 KV 캐시를 디스크에 저장했다가 다시 불러올 수 있음. 우리는 각 페르소나의 고정 프롬프트를 미리 한 번 채워두고 채팅할 때마다 복원함. CPU에서 27k 토큰 프리필(prefill)하는 데 98초 걸리던 게 복원할 때는 67ms밖에 안 걸렸음. DeepSeek가 캐시 유지 시간에 대해 어떤 제어권을 주는지, 아니면 90%라는 수치가 그냥 호출량에서 나오는 건지 궁금함.
1
각 캐릭터마다 호출 간격이 보통 30분 이내라서(잠자는 경우 제외), 호출은 대체로 DeepSeek의 캐시 유지 기간 내에 들어옴.