feat: enhance AIPlanner with loading spinner during Ollama requests and update print statements for clarity

This commit is contained in:
kswdev0
2026-03-27 09:26:38 +09:00
parent 82fa73342f
commit 7014b47231
2 changed files with 49 additions and 30 deletions

View File

@@ -15,7 +15,9 @@ LLM 백엔드: 로컬 Ollama (structured output으로 JSON 스키마 강제)
import json import json
import os import os
import re import re
import sys
import time import time
import threading
import traceback import traceback
import ollama import ollama
@@ -141,7 +143,7 @@ class AIPlanner:
"⚠️ 제작은 재료가 있어야 합니다. 인벤토리를 확인하세요." "⚠️ 제작은 재료가 있어야 합니다. 인벤토리를 확인하세요."
) )
print(f"\n[AI] 생각 중... (model={OLLAMA_MODEL}, host={OLLAMA_HOST})") print(f"\n[AI] 요청 시작 (model={OLLAMA_MODEL})")
try: try:
plan = self._call_ollama(user_message) plan = self._call_ollama(user_message)
@@ -177,36 +179,53 @@ class AIPlanner:
def _call_ollama(self, user_message: str) -> dict: def _call_ollama(self, user_message: str) -> dict:
t0 = time.perf_counter() t0 = time.perf_counter()
client = ollama.Client(host=OLLAMA_HOST) stop_event = threading.Event()
response = client.chat(
model=OLLAMA_MODEL, def _spinner():
messages=[ while not stop_event.is_set():
{"role": "system", "content": SYSTEM_PROMPT}, elapsed = time.perf_counter() - t0
{"role": "user", "content": user_message}, print(f"\r[AI] 생각 중... {elapsed:.0f}s", end="", flush=True)
], stop_event.wait(1.0)
format={ print("\r", end="")
"type": "object",
"properties": { spinner = threading.Thread(target=_spinner, daemon=True)
"thinking": {"type": "string"}, spinner.start()
"current_goal": {"type": "string"},
"actions": { try:
"type": "array", client = ollama.Client(host=OLLAMA_HOST)
"items": { response = client.chat(
"type": "object", model=OLLAMA_MODEL,
"properties": { messages=[
"action": {"type": "string"}, {"role": "system", "content": SYSTEM_PROMPT},
"params": {"type": "object"}, {"role": "user", "content": user_message},
"reason": {"type": "string"}, ],
format={
"type": "object",
"properties": {
"thinking": {"type": "string"},
"current_goal": {"type": "string"},
"actions": {
"type": "array",
"items": {
"type": "object",
"properties": {
"action": {"type": "string"},
"params": {"type": "object"},
"reason": {"type": "string"},
},
"required": ["action", "params"],
}, },
"required": ["action", "params"],
}, },
"after_this": {"type": "string"},
}, },
"after_this": {"type": "string"}, "required": ["actions"],
}, },
"required": ["actions"], options={"temperature": 0.3},
}, )
options={"temperature": 0.3}, finally:
) stop_event.set()
spinner.join()
dt = time.perf_counter() - t0 dt = time.perf_counter() - t0
content = response.message.content content = response.message.content
print(f"[AI] 응답 수신 ({dt:.2f}s, {len(content)}자)") print(f"[AI] 응답 수신 ({dt:.2f}s, {len(content)}자)")

View File

@@ -15,7 +15,7 @@ import json
from factorio_rcon import FactorioRCON from factorio_rcon import FactorioRCON
from state_reader import StateReader from state_reader import StateReader
from context_compressor import ContextCompressor from context_compressor import ContextCompressor
from ai_planner import AIPlanner from ai_planner import AIPlanner, OLLAMA_MODEL, OLLAMA_HOST
from action_executor import ActionExecutor from action_executor import ActionExecutor
from agent_last_action_memory import save_last_action_memory, load_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 from ore_patch_memory import load_ore_patch_memory, compute_distance_sq
@@ -137,8 +137,8 @@ def run():
print("=" * 60) print("=" * 60)
print(" 팩토리오 순수 AI 에이전트 (치트 없음)") print(" 팩토리오 순수 AI 에이전트 (치트 없음)")
print(" - 실제 걷기 / 실제 채굴 / 실제 제작 / 건설 거리 제한") print(" - 실제 걷기 / 실제 채굴 / 실제 제작 / 건설 거리 제한")
print(f" - LLM: Ollama {os.environ.get('OLLAMA_MODEL', 'qwen3:14b')}") print(f" - LLM: Ollama {OLLAMA_MODEL}")
print(f" - Ollama host: {os.environ.get('OLLAMA_HOST', 'http://192.168.50.67:11434')}") print(f" - Ollama host: {OLLAMA_HOST}")
print("=" * 60) print("=" * 60)
with FactorioRCON(RCON_HOST, RCON_PORT, RCON_PASSWORD) as rcon: with FactorioRCON(RCON_HOST, RCON_PORT, RCON_PASSWORD) as rcon: