feat(agent-office): InstaAgent 자율 발급 경로 + 커버 프리뷰

- on_schedule에 autonomous_issue 분기 추가 (eligible 픽만 선별·max_per_day 제한)
- _generate_and_preview 메서드: 슬레이트 생성 → 커버 PNG → 인라인 승인 버튼
- messaging.send_photo 신규 추가 (multipart/form-data, reply_markup 지원)
- insta_get_preferences 실패를 warning으로 격리해 자율 경로 중단 방지

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-11 02:36:26 +09:00
parent 9fc764a78c
commit 7c5ca15b64
3 changed files with 116 additions and 3 deletions

View File

@@ -71,14 +71,31 @@ class InstaAgent(BaseAgent):
config = get_agent_config(self.agent_id) or {}
custom = config.get("custom_config", {}) or {}
auto_select = bool(custom.get("auto_select", False))
autonomous = bool(custom.get("autonomous_issue", False))
threshold = float(custom.get("select_threshold", 0.6))
max_per_day = int(custom.get("max_per_day", 2))
task_id = create_task(self.agent_id, "insta_daily", {"auto_select": auto_select},
requires_approval=False)
await self.transition("working", "뉴스 수집·키워드 추출", task_id)
try:
prefs = await service_proxy.insta_get_preferences()
add_log(self.agent_id, f"insta preferences: {prefs}", "info", task_id)
try:
prefs = await service_proxy.insta_get_preferences()
add_log(self.agent_id, f"insta preferences: {prefs}", "info", task_id)
except Exception as _pref_err:
add_log(self.agent_id, f"insta preferences unavailable: {_pref_err}", "warning", task_id)
await self._run_collect_and_extract()
if autonomous:
ranked = await service_proxy.insta_ranked(threshold=threshold, limit=20)
eligible = [r for r in ranked if r.get("eligible")][:max_per_day]
if not eligible:
await messaging.send_raw("📰 [인스타 큐레이터] 오늘은 발행할 가치 있는 주제가 없습니다.")
else:
for pick in eligible:
await self._generate_and_preview(pick)
update_task_status(task_id, "succeeded", {"issued": len(eligible)})
await self.transition("idle", "자율 발급 후보 프리뷰 완료")
return
kws = await service_proxy.insta_list_keywords(used=False)
if auto_select:
await self._auto_render(kws)
@@ -161,6 +178,27 @@ class InstaAgent(BaseAgent):
full_caption = f"{caption}\n\n{hashtags}".strip()
await _send_media_group(media, caption=full_caption)
async def _generate_and_preview(self, pick: dict) -> None:
"""eligible 픽 → 슬레이트 생성·렌더 → 커버 프리뷰 + 승인 버튼."""
created = await service_proxy.insta_create_slate(
keyword=pick["keyword"], category=pick["category"], keyword_id=pick["id"],
)
st = await self._wait_task(created["task_id"], step="slate", timeout_sec=600)
slate_id = st["result_id"]
cover = await service_proxy.insta_get_asset_bytes(slate_id, 1)
bd = pick.get("breakdown", {})
caption = (f"🎴 <b>{pick['keyword']}</b> ({pick['category']})\n"
f"점수 {pick.get('final_score')} · fresh {bd.get('freshness')} "
f"fit {bd.get('account_fit')} claude {bd.get('claude')}\n승인하시겠어요?")
kb = {"inline_keyboard": [[
{"text": "✅ 승인", "callback_data": f"issue_approve_{slate_id}"},
{"text": "❌ 반려", "callback_data": f"issue_reject_{slate_id}"},
{"text": "🔄 재생성", "callback_data": f"issue_regen_{slate_id}"},
]]}
await messaging.send_photo(cover, caption=caption, reply_markup=kb)
create_task(self.agent_id, "insta_issue", {"slate_id": slate_id, "keyword_id": pick["id"]},
requires_approval=True)
async def on_command(self, command: str, params: dict) -> dict:
if command == "extract":
await self._run_collect_and_extract()