fix(agent-office): 일요 회고 견고화 (dead import 제거·send 가드·부분 payload 방어)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-31 18:02:01 +09:00
parent 1b8548a73f
commit 11212c4afd
4 changed files with 32 additions and 18 deletions

View File

@@ -159,7 +159,7 @@ class LottoAgent(BaseAgent):
async def run_sunday_review(self) -> dict: async def run_sunday_review(self) -> dict:
"""일 09:00 — 최신 회차 forward+calibration 보장 후 회고 텔레그램.""" """일 09:00 — 최신 회차 forward+calibration 보장 후 회고 텔레그램."""
from ..service_proxy import lotto_latest_draw, lotto_backtest_review, lotto_backtest_run_forward from ..service_proxy import lotto_latest_draw, lotto_backtest_review
from ..notifiers.telegram_lotto import send_sunday_review from ..notifiers.telegram_lotto import send_sunday_review
from ..db import create_task, update_task_status, add_log from ..db import create_task, update_task_status, add_log

View File

@@ -232,7 +232,7 @@ async def send_evolution_report(eval_result: Dict[str, Any], current_base: List[
def format_sunday_review(payload: Dict[str, Any]) -> str: def format_sunday_review(payload: Dict[str, Any]) -> str:
"""일요 회고 브리핑 텍스트 (HTML parse_mode).""" """일요 회고 브리핑 텍스트 (HTML parse_mode)."""
wa = payload.get("winner_analysis") or {} wa = payload.get("winner_analysis") or {}
draw_no = payload.get("draw_no") draw_no = payload.get("draw_no") or "?"
pct = wa.get("percentile") pct = wa.get("percentile")
pct_txt = f"{pct*100:.0f}%" if pct is not None else "" pct_txt = f"{pct*100:.0f}%" if pct is not None else ""
lines = [f"🔍 <b>로또 #{draw_no} 일요 회고</b>", ""] lines = [f"🔍 <b>로또 #{draw_no} 일요 회고</b>", ""]
@@ -243,18 +243,24 @@ def format_sunday_review(payload: Dict[str, Any]) -> str:
f"· 갭 {wa.get('score_gap',0):.2f} · 공동출현 {wa.get('score_cooccur',0):.2f} " f"· 갭 {wa.get('score_gap',0):.2f} · 공동출현 {wa.get('score_cooccur',0):.2f} "
f"· 다양성 {wa.get('score_diversity',0):.2f}") f"· 다양성 {wa.get('score_diversity',0):.2f}")
lines.append("") lines.append("")
if payload.get("forward"):
lines.append("📊 <b>이번 회차 가상구매 성적</b>") lines.append("📊 <b>이번 회차 가상구매 성적</b>")
for f in payload.get("forward", []): for f in payload.get("forward", []):
p = f["prizes"] p = f.get("prizes") or {}
name = {"engine_w": f"엔진({f['label']})", "random_null": "무작위", "coverage": "커버리지"}.get( name = {"engine_w": f"엔진({f.get('label','')})", "random_null": "무작위", "coverage": "커버리지"}.get(
f["strategy"], f["strategy"]) f.get("strategy", ""), f.get("strategy", "?"))
lines.append(f" {name}: 최고 {f['best_match']}일치 / " lines.append(f" {name}: 최고 {f.get('best_match','?')}일치 / "
f"4등 {p['4th']} · 5등 {p['5th']}") f"4등 {p.get('4th', 0)} · 5등 {p.get('5th', 0)}")
else:
lines.append("📊 <b>이번 회차 가상구매 성적</b>: 데이터 없음 (아직 집계 전)")
lines.append("") lines.append("")
lines.append(" 무작위 대비 우위가 통계적으로 의미있을 때만 가중치가 진화합니다.") lines.append(" 무작위 대비 우위가 통계적으로 의미있을 때만 가중치가 진화합니다.")
return "\n".join(lines) return "\n".join(lines)
async def send_sunday_review(payload: Dict[str, Any]) -> None: async def send_sunday_review(payload: Dict[str, Any]) -> None:
from ..telegram.messaging import send_raw text = format_sunday_review(payload)
await send_raw(format_sunday_review(payload)) try:
await send_raw(text)
except Exception as e:
logger.warning(f"[telegram_lotto] sunday review send failed: {e}")

View File

@@ -406,13 +406,6 @@ async def lotto_backtest_review(draw_no: int) -> Dict[str, Any]:
return resp.json() return resp.json()
async def lotto_backtest_run_forward(draw_no: int) -> Dict[str, Any]:
from .config import LOTTO_BACKEND_URL
resp = await _client.post(f"{LOTTO_BACKEND_URL}/api/lotto/backtest/run-forward",
params={"draw_no": draw_no})
resp.raise_for_status()
return resp.json()
from .config import AGENT_CONTAINER_MAP from .config import AGENT_CONTAINER_MAP

View File

@@ -21,3 +21,18 @@ def test_format_sunday_review_text():
assert "1170" in txt assert "1170" in txt
assert "%" in txt # percentile 표기 assert "%" in txt # percentile 표기
assert "engine" in txt.lower() or "엔진" in txt assert "engine" in txt.lower() or "엔진" in txt
def test_format_sunday_review_no_calibration():
payload = {"draw_no": 1171, "winner_analysis": None, "forward": []}
txt = tl.format_sunday_review(payload)
assert "1171" in txt
assert "%" not in txt # no percentile section when calibration absent
assert "데이터 없음" in txt
def test_format_sunday_review_missing_prizes_no_crash():
payload = {"draw_no": 1171, "winner_analysis": None,
"forward": [{"strategy": "engine_w", "label": "w1", "best_match": 3}]} # no 'prizes'
txt = tl.format_sunday_review(payload) # must NOT raise
assert "1171" in txt