feat: 메모리 섹션 추가 및 상태 요약 개선

- `_append_memory_sections` 함수를 추가하여 압축 모드에서 기억된 광맥과 마지막 행동 정보를 상태 요약에 포함하도록 개선
- `main.py`에서 상태 요약 생성 시 메모리 섹션을 자동으로 추가하여 AI의 의사결정에 필요한 정보를 제공
- `README.md`에 새로운 메모리 기능 및 사용 방법에 대한 설명 추가
- `state_reader.py`에서 인벤토리 판독 로직을 개선하여 캐시 사용 조건을 명확히 하여 안정성 향상
This commit is contained in:
kswdev0
2026-03-26 10:45:30 +09:00
parent 3d118fe649
commit 153f02f5e9
4 changed files with 103 additions and 13 deletions

79
main.py
View File

@@ -17,7 +17,8 @@ from state_reader import StateReader
from context_compressor import ContextCompressor
from ai_planner import AIPlanner
from action_executor import ActionExecutor
from agent_last_action_memory import save_last_action_memory
from agent_last_action_memory import save_last_action_memory, load_last_action_memory
from ore_patch_memory import load_ore_patch_memory, compute_distance_sq
RCON_HOST = os.getenv("FACTORIO_HOST", "127.0.0.1")
RCON_PORT = int(os.getenv("FACTORIO_PORT", "25575"))
@@ -58,6 +59,80 @@ end
return False
def _append_memory_sections(summary: str) -> str:
"""
압축 모드(중반/후반)에서는 context_compressor 출력만 들어가서
state_reader의 "기억된 광맥/마지막 행동" 섹션이 사라질 수 있다.
이 정보를 프롬프트에 다시 덧붙여 폴백/복구를 가능하게 한다.
"""
ore_mem = load_ore_patch_memory()
last_action = load_last_action_memory()
# compressed summary에도 "위치: (x, y)"가 포함되므로, 여기서 플레이어 좌표만 뽑는다.
m = None
try:
import re
m = re.search(r"위치:\s*\(\s*(-?\d+)\s*,\s*(-?\d+)\s*\)", summary)
except Exception:
m = None
px = py = None
if m:
try:
px = int(m.group(1))
py = int(m.group(2))
except Exception:
px = py = None
mem_lines: list[str] = []
mem_lines.append("### 기억된 광맥 (좌표)")
if ore_mem:
known: list[tuple[int, str, int, int, object]] = []
for ore, patches in ore_mem.items():
if not isinstance(patches, list):
continue
for patch in patches:
if not isinstance(patch, dict):
continue
tx = patch.get("tile_x")
ty = patch.get("tile_y")
if isinstance(tx, int) and isinstance(ty, int):
if px is not None and py is not None:
dist = int(compute_distance_sq(px, py, tx, ty) ** 0.5)
else:
dist = 10**18
known.append((dist, ore, tx, ty, patch.get("count", "?")))
known.sort(key=lambda x: x[0])
for dist, ore, tx, ty, cnt in known[:10]:
if dist >= 10**18:
mem_lines.append(f"- {ore}: ({tx},{ty}) [거리: ~?타일] count={cnt}")
else:
mem_lines.append(f"- {ore}: ({tx},{ty}) [거리: ~{dist}타일] count={cnt}")
else:
mem_lines.append("- 없음")
mem_lines.append("")
mem_lines.append("### 마지막 행동(기억)")
if last_action and isinstance(last_action, dict) and last_action.get("action"):
act = last_action.get("action", "")
success = last_action.get("success", None)
msg = last_action.get("message", "")
mem_lines.append(f"- action={act} success={success} message={msg}")
params = last_action.get("params", {})
if params:
try:
mem_lines.append(f"- params={json.dumps(params, ensure_ascii=False)}")
except Exception:
pass
else:
mem_lines.append("- 없음")
return summary + "\n\n" + "\n".join(mem_lines)
def run():
print("=" * 60)
print(" 팩토리오 순수 AI 에이전트 (치트 없음)")
@@ -97,9 +172,11 @@ def run():
elif entity_count < 200:
print(f"[상태] 중반 모드 - 구역 압축 (건물 {entity_count}개)")
summary = compressor.get_compressed_state(detail_level=1)
summary = _append_memory_sections(summary)
else:
print(f"[상태] 후반 모드 - 글로벌 압축 (건물 {entity_count}개)")
summary = compressor.get_compressed_state(detail_level=0)
summary = _append_memory_sections(summary)
global_info = compressor._get_global_summary()
entity_count = global_info.get("total_entities", entity_count)