fix(agent-office): 일요 회고 견고화 (dead import 제거·send 가드·부분 payload 방어)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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("")
|
||||||
lines.append("📊 <b>이번 회차 가상구매 성적</b>")
|
if payload.get("forward"):
|
||||||
for f in payload.get("forward", []):
|
lines.append("📊 <b>이번 회차 가상구매 성적</b>")
|
||||||
p = f["prizes"]
|
for f in payload.get("forward", []):
|
||||||
name = {"engine_w": f"엔진({f['label']})", "random_null": "무작위", "coverage": "커버리지"}.get(
|
p = f.get("prizes") or {}
|
||||||
f["strategy"], f["strategy"])
|
name = {"engine_w": f"엔진({f.get('label','')})", "random_null": "무작위", "coverage": "커버리지"}.get(
|
||||||
lines.append(f" {name}: 최고 {f['best_match']}일치 / "
|
f.get("strategy", ""), f.get("strategy", "?"))
|
||||||
f"4등 {p['4th']} · 5등 {p['5th']}")
|
lines.append(f" {name}: 최고 {f.get('best_match','?')}일치 / "
|
||||||
|
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}")
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user