feat: AIPlanner JSON 파싱 실패 시 재시도 로직 개선
- JSON 파싱 실패 감지 시, 다음 재시도에 `JSON-only repair` 프롬프트를 추가하여 모델이 스키마를 준수하도록 유도하는 로직을 구현 - README.md에 JSON 파싱 실패 처리 및 재시도 로직에 대한 설명 추가
This commit is contained in:
@@ -143,7 +143,7 @@ planner.set_goal(
|
|||||||
- 순수 플레이이므로 **걷기, 채굴, 제작에 실제 시간이 소요**됩니다
|
- 순수 플레이이므로 **걷기, 채굴, 제작에 실제 시간이 소요**됩니다
|
||||||
- AI가 "move 먼저 → 작업" 패턴을 학습하도록 프롬프트가 설계되어 있습니다
|
- AI가 "move 먼저 → 작업" 패턴을 학습하도록 프롬프트가 설계되어 있습니다
|
||||||
- `agent_log.jsonl`에 모든 행동과 타임스탬프가 기록됩니다
|
- `agent_log.jsonl`에 모든 행동과 타임스탬프가 기록됩니다
|
||||||
- `ai_planner.py`는 GLM 응답이 잘리거나(중괄호/대괄호 불일치) 마크다운이 섞여도 JSON 파싱을 복구하도록 `{} / []` 균형 추적과 보정 로직을 사용합니다. 또한 최상위에서 JSON 배열(`[...]`)로 답하는 경우도 `actions`로 래핑해 처리합니다. (추가) `finish_reason=length` 등으로 `message.content`가 비거나(또는 JSON 형태가 아니면) `message.reasoning_content`를 우선 사용합니다. 그리고 응답 안에 여러 개의 `{...}`가 섞여 있어도 그중 `actions`를 포함한 계획 객체를 우선 선택합니다.
|
- `ai_planner.py`는 GLM 응답이 잘리거나(중괄호/대괄호 불일치) 마크다운이 섞여도 JSON 파싱을 복구하도록 `{} / []` 균형 추적과 보정 로직을 사용합니다. 또한 최상위에서 JSON 배열(`[...]`)로 답하는 경우도 `actions`로 래핑해 처리합니다. (추가) `finish_reason=length` 등으로 `message.content`가 비거나(또는 JSON 형태가 아니면) `message.reasoning_content`를 우선 사용합니다. 그리고 응답 안에 여러 개의 `{...}`가 섞여 있어도 그중 `actions`를 포함한 계획 객체를 우선 선택합니다. 또한 JSON 파싱 실패가 감지되면 다음 재시도에는 `JSON-only repair` 프롬프트를 덧붙여 모델이 스키마를 다시 따르도록 유도합니다.
|
||||||
- `ai_planner.py`의 `AIPlanner.decide()`는 `TimeoutError`/`ConnectionError`/`urllib.error.URLError` 같은 GLM HTTP 지연/연결 오류도 3회 재시도한 뒤, **상태 요약에 나온 광맥(앵커) 좌표가 있으면 `mine_resource`(먼 경우 `move` 후 채굴)로 폴백**하고, 광맥 정보가 없을 때만 `explore` 방향을 순환하며 탐색합니다(동일 방향 탐색 루프 완화).
|
- `ai_planner.py`의 `AIPlanner.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)로 바꿀 수 있습니다.
|
- GLM HTTP 읽기 제한 시간은 기본 120초이며, `GLM_HTTP_TIMEOUT_SECONDS`로 조정할 수 있습니다. 광맥은 플레이어와 200타일 이상 떨어진 경우에만 폴백에서 `move`를 끼우며, 임계값은 `GLM_FALLBACK_MOVE_THRESHOLD`(기본 200)로 바꿀 수 있습니다.
|
||||||
- `ai_planner.py`의 `_call_glm()`에서 GLM 지연 원인 분석을 위한 타이밍 로그가 출력됩니다.
|
- `ai_planner.py`의 `_call_glm()`에서 GLM 지연 원인 분석을 위한 타이밍 로그가 출력됩니다.
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ class AIPlanner:
|
|||||||
self.step += 1
|
self.step += 1
|
||||||
feedback_text = self._format_feedback()
|
feedback_text = self._format_feedback()
|
||||||
|
|
||||||
user_message = (
|
user_message_base = (
|
||||||
f"## 스텝 {self.step}\n\n"
|
f"## 스텝 {self.step}\n\n"
|
||||||
f"### 현재 게임 상태\n{state_summary}\n\n"
|
f"### 현재 게임 상태\n{state_summary}\n\n"
|
||||||
f"{feedback_text}"
|
f"{feedback_text}"
|
||||||
@@ -202,9 +202,12 @@ class AIPlanner:
|
|||||||
|
|
||||||
print(f"\n[GLM] 생각 중...")
|
print(f"\n[GLM] 생각 중...")
|
||||||
|
|
||||||
|
# JSON 스키마를 위반한 경우(예: 분석 텍스트만 반환)에는 재시도 프롬프트를 강화한다.
|
||||||
|
repair_suffix = ""
|
||||||
|
|
||||||
for attempt in range(3):
|
for attempt in range(3):
|
||||||
try:
|
try:
|
||||||
raw = self._call_glm(user_message, attempt=attempt)
|
raw = self._call_glm(user_message_base + repair_suffix, attempt=attempt)
|
||||||
plan = self._parse_json(raw)
|
plan = self._parse_json(raw)
|
||||||
break
|
break
|
||||||
except (
|
except (
|
||||||
@@ -217,6 +220,25 @@ class AIPlanner:
|
|||||||
) as e:
|
) as e:
|
||||||
detail = describe_glm_exception(e)
|
detail = describe_glm_exception(e)
|
||||||
if attempt < 2:
|
if attempt < 2:
|
||||||
|
if isinstance(e, ValueError) and (
|
||||||
|
str(e).startswith("JSON 파싱 실패")
|
||||||
|
or "JSON 파싱 실패" in str(e)
|
||||||
|
or "actions 스키마" in str(e)
|
||||||
|
):
|
||||||
|
repair_suffix = (
|
||||||
|
"\n\n[중요] 이전 응답은 JSON 요구사항을 위반했습니다.\n"
|
||||||
|
"지금은 아래 스키마의 JSON 객체만 '그대로' 반환하세요.\n"
|
||||||
|
"마크다운/설명 금지.\n"
|
||||||
|
"키 이름과 구문은 동일해야 합니다.\n"
|
||||||
|
"{"
|
||||||
|
"\"thinking\":\"\","
|
||||||
|
"\"current_goal\":\"\","
|
||||||
|
"\"actions\":[{\"action\":\"explore\",\"params\":{},\"reason\":\"\"}],"
|
||||||
|
"\"after_this\":\"\""
|
||||||
|
"}"
|
||||||
|
)
|
||||||
|
if _glm_debug_enabled():
|
||||||
|
print("[GLM][디버그] JSON-only repair 프롬프트 적용")
|
||||||
print(
|
print(
|
||||||
f"[경고] GLM 처리 실패 (시도 {attempt+1}/3): "
|
f"[경고] GLM 처리 실패 (시도 {attempt+1}/3): "
|
||||||
f"{type(e).__name__} 재시도..."
|
f"{type(e).__name__} 재시도..."
|
||||||
|
|||||||
Reference in New Issue
Block a user