kswdev0 a2648f9095 feat: GLM 응답에서 assistant 텍스트 추출 로직 개선 및 관련 경고 메시지 추가
- GLM 응답의 content가 비어있을 경우 reasoning_content를 대신 사용하도록 로직 개선
- 새로운 메서드 _extract_glm_assistant_text 추가하여 assistant 텍스트 추출을 명확히 함
- 관련 단위 테스트 추가 및 README.md에 변경 사항 반영
2026-03-26 11:11:24 +09:00

팩토리오 AI 에이전트 (순수 플레이)

AI가 팩토리오를 치트 없이 자율적으로 플레이하는 에이전트입니다. 실제 걷기, 실제 채굴, 실제 제작, 건설 거리 제한 등 모든 게임 메커닉을 준수합니다.


순수 플레이 모드란?

항목 치트 모드 순수 모드 (현재)
이동 텔레포트 실제 걷기 (walking_state)
채굴 인벤토리 직접 삽입 자원 패치에서 실제 채굴 (mining_state)
제작 무조건 지급 재료 소모 실제 제작 (begin_crafting)
건설 어디서든 create_entity 건설 거리 내에서 build_from_cursor
삽입 무한 아이템 플레이어 인벤토리에서 차감

파일 구조

factorio_ai/
├── main.py               ← 메인 루프 (여기서 실행)
├── factorio_rcon.py       ← RCON 연결 (게임과 통신)
├── state_reader.py        ← 게임 상태 읽기 (자원, 인벤토리, 건물)
├── context_compressor.py  ← 중반 이후 상태 압축
├── ai_planner.py          ← AI 행동 계획 (순수 플레이 제약 반영)
├── action_executor.py     ← 실제 게임 조작 (순수 메커닉)
└── agent_log.jsonl        ← 행동 로그 (자동 생성)

설치

# 외부 라이브러리 불필요! Python 표준 라이브러리만 사용

팩토리오 RCON 설정

방법 1: 싱글플레이

  1. 팩토리오 실행
  2. 게임 시작 후 콘솔 열기 (` 키 또는 ~)
  3. 아래 입력:
    /rcon-port 25575
    /rcon-password factorio_ai
    

방법 2: 서버 모드 (권장)

# Windows
factorio.exe --start-server saves/mysave.zip --rcon-port 25575 --rcon-password factorio_ai

# Linux/Mac
./factorio --start-server saves/mysave.zip --rcon-port 25575 --rcon-password factorio_ai

실행

# Z.ai API 키 설정
export ZAI_API_KEY="your-key-here"

# 기본 실행
python main.py

# 커스텀 서버
FACTORIO_HOST=192.168.1.10 FACTORIO_PORT=25575 FACTORIO_PASSWORD=mypass python main.py
# Z.ai API 키 설정
$env:ZAI_API_KEY="your-key-here"

# 기본 실행
python .\main.py

# 커스텀 서버 (한 줄 버전)
$env:FACTORIO_HOST="192.168.1.10"; $env:FACTORIO_PORT="25575"; $env:FACTORIO_PASSWORD="mypass"; python .\main.py

# (권장) 스크립트 실행
.\run_factorio_ai.ps1 "your-key-here" "mypass"

동작 원리

1. RCON → 팩토리오에서 현재 상태 읽기
   (플레이어 위치, 인벤토리, 자원 패치, 건물 목록)

2. 상태 → AI API로 전송
   "다음에 뭘 해야 하나요?" (순수 플레이 제약 포함)

3. AI → JSON 행동 시퀀스 반환
   [move → mine_resource → craft_item → place_entity ...]

4. RCON → 팩토리오에서 실제 게임 메커닉으로 실행
   - 캐릭터가 실제로 걸어감
   - 실제로 곡괭이질해서 채굴
   - 재료를 소모해서 제작
   - 건설 거리 내에서만 배치

AI 목표 변경

planner.set_goal(
    "전력 인프라 구축: offshore-pump → boiler → steam-engine → 전선 연결"
)

GLM API 연결 문제 디버깅

타임아웃·연결 오류가 나면 원인을 로그로 먼저 구분하는 것이 좋습니다.

  1. 전용 검사 스크립트 (에이전트와 동일한 urllib 경로):

    ZAI_API_KEY="your-key" python scripts/glm_connection_check.py
    
  2. 실행 시 상세 로그: GLM_DEBUG=1을 켜면 재시도마다 [GLM 원인] … 한 줄에 URLError.reason, SSL/소켓 errno, DNS 힌트 등이 붙고, 스택 트레이스도 출력됩니다.

  3. 자주 있는 원인: Docker/서버에서 외부 HTTPS(443) 차단, 프록시 필요( HTTPS_PROXY ), DNS 실패, API 키 만료·오타, 응답이 느려 타임아웃( GLM_HTTP_TIMEOUT_SECONDS 증가 ).


주의사항

  • 순수 플레이이므로 걷기, 채굴, 제작에 실제 시간이 소요됩니다
  • AI가 "move 먼저 → 작업" 패턴을 학습하도록 프롬프트가 설계되어 있습니다
  • agent_log.jsonl에 모든 행동과 타임스탬프가 기록됩니다
  • ai_planner.py는 GLM 응답이 잘리거나(중괄호/대괄호 불일치) 마크다운이 섞여도 JSON 파싱을 복구하도록 {} / [] 균형 추적과 보정 로직을 사용합니다. 또한 최상위에서 JSON 배열([...])로 답하는 경우도 actions로 래핑해 처리합니다. (추가) finish_reason=length 등으로 message.content가 비는 경우에는 message.reasoning_content를 대신 사용합니다.
  • ai_planner.pyAIPlanner.decide()TimeoutError/ConnectionError/urllib.error.URLError 같은 GLM HTTP 지연/연결 오류도 3회 재시도한 뒤, 상태 요약에 나온 광맥(앵커) 좌표가 있으면 mine_resource(먼 경우 move 후 채굴)로 폴백하고, 광맥 정보가 없을 때만 explore 방향을 순환하며 탐색합니다(동일 방향 탐색 루프 완화).
  • GLM HTTP 읽기 제한 시간은 기본 120초이며, GLM_HTTP_TIMEOUT_SECONDS로 조정할 수 있습니다. 광맥은 플레이어와 200타일 이상 떨어진 경우에만 폴백에서 move를 끼우며, 임계값은 GLM_FALLBACK_MOVE_THRESHOLD(기본 200)로 바꿀 수 있습니다.
  • ai_planner.py_call_glm()에서 GLM 지연 원인 분석을 위한 타이밍 로그가 출력됩니다.
    • total: 요청 시작~콘텐츠 반환까지 전체 소요
    • http_read: HTTP 응답 본문 수신까지 소요
    • json_parse: 응답 JSON 파싱 시간
  • 파이썬 에이전트 재실행 시 인벤토리 “기억”이 필요하면 inventory_memory.json 캐시를 사용합니다.
    • RCON에서 인벤토리를 읽지 못하거나(비정상 출력/파싱 실패 등) 직전에 성공적으로 읽은 인벤토리를 대신 사용합니다.
    • 성공적으로 인벤토리가 읽히면 해당 캐시를 갱신합니다(코드는 inv.get_contents()nil/비테이블을 반환해도 안전하게 처리).
    • 또한 inv.get_contents() 결과가 비면 Lua에서 인덱스 기반으로 한 번 더 읽어서 {}가 과도하게 나오는 걸 줄입니다.
    • 캐시 대체는 get_inventory()의 파싱이 실패한 경우에만 수행합니다.
    • 또한 일부 환경에서 p.get_main_inventory()가 없으면 defines.inventory.character_main으로 한 번 더 읽습니다.
    • 캐시 파일이 아직 없으면(초기 실행) fallback도 {}가 됩니다.
  • (중요) 일부 Factorio 버전에서는 game.table_to_json이 없을 수 있어서, Lua 내부에 간단 JSON 인코더를 넣어 인벤토리/상태를 안정적으로 가져옵니다.
  • explorescan_resources()로 발견한 광맥(자원 엔티티) 좌표는 ore_patch_memory.json에 ore 종류별로 여러 패치 좌표 목록으로 저장되고, 다음 상태 요약에서 AI가 좌표를 재사용(우선 이동)할 수 있게 합니다.
  • 디버깅용으로 INV_DEBUG=1을 켜면, main inventory 외에 cursor_stack/armor/trash 총합도 함께 출력합니다(어디에 아이템이 있는지 확인용).
  • mine_resource에서 실패한 채굴 타일 제외(exclude)는 Lua와 Python 양쪽에서 정수 타일 좌표(tx, ty) 키로 통일해, 제외한 좌표가 반복 선택되지 않도록 합니다.
  • 또한 채굴 시작(mining_state) 좌표는 정수 타일이 아니라, Lua가 찾은 실제 자원 엔티티의 e.position(정확 실수 좌표)을 사용해 “플레이어가 타일 위에 있는데도 즉시 채굴 감지 실패”를 줄입니다.
  • mine_resourceLuaControl.mining_state해당 타깃에 대해 한 번만 설정합니다. 이전에는 대기 루프마다 같은 값을반복 설정해 우클릭을 연타하는 것과 비슷한 동작이었으며, 이제는 인간이 우클릭을 누른 채로 두는 것에 가깝게 유지합니다.
  • mine_resource는 move 후 p.position 근처(반경 1.2)에서 실제로 해당 광물 엔티티가 있는지 재확인하고, 없으면 채굴을 시도하지 않고 다음 후보로 넘어갑니다.
  • scan_resources()는 패치 평균 중심(center_x/y)만이 아니라 플레이어 기준 가장 가까운 실제 엔티티 좌표(anchor_x/y)도 함께 반환하며, summarize_for_ai()에서는 앵커를 기준으로 거리/추천을 계산합니다.
  • mine_resource의 초기 “후보 엔티티 탐색” 반경은 패치 중심 오차를 흡수하기 위해 250으로 확장해, 이동 직후 바로 실패하는 케이스를 줄입니다.
  • 에이전트를 재시작하더라도 직전에 실행했던 action/result를 agent_last_action_memory.json에 저장해, 다음 상태 요약에서 AI가 참고할 수 있게 합니다.
  • 저장 기준: 성공/실패 상관없이 가장 마지막으로 실행을 시도한 action 1개만 저장합니다.
  • explorewanted_ores가 있으면 해당 자원이 발견될 때까지 멈추지 않고 계속 이동해, iron-ore처럼 주변에 흔한 자원만 계속 발견되어 진행이 막히는 문제를 줄입니다.
  • place_entity는 지정 좌표가 BLOCKED이거나 인벤토리에 해당 아이템이 없어도(예: 이미 설치됨) 주변 ±1 타일 및 요청 좌표 중심 반경 3 타일 내의 기존 엔티티를 우선 찾아 REUSED로 재사용합니다.
  • stone-furnaceplace_entity가 성공하면(신규 배치든 REUSED든) executor가 즉시 coal(연료)와 iron-ore/copper-ore(가능한 것 우선)를 투입해 제련이 시작되도록 보정합니다.
    • 또한 insert_to_entity는 burner 인벤토리 투입 전에 can_insert를 확인해, 미삽입/아이템 증발 위험을 줄입니다.
  • (Cursor) Windows에서 sessionStart 훅 실행 중 앱 선택창이 뜨는 경우:
    • 프로젝트 훅은 E:/develop/factorio-ai-agent/.cursor/hooks.jsonE:/develop/factorio-ai-agent/.cursor/session-start-hook.ps1를 PowerShell로 실행하도록 구성되어 있습니다.
    • Superpowers 플러그인의 ./hooks/session-start가 bash로 실행되도록 hooks-cursor.json을 수정했습니다(필요 시 Cursor 재시작 후 View -> Output -> Hooks에서 확인).
  • 인벤토리 판독은 inv.get_contents()를 우선 사용해, 일부 환경에서 #inv / 인덱스 접근이 비어있음으로 오인되는 문제를 줄입니다.
Description
팩토리오 완전 자율 AI 에이전트
Readme 1.2 MiB
Languages
Python 100%