From ae4f0d42703add201551f316092b454a822038be Mon Sep 17 00:00:00 2001 From: gahusb Date: Thu, 16 Apr 2026 00:12:24 +0900 Subject: [PATCH] =?UTF-8?q?fix(agent-office/blog):=20generate/market/revie?= =?UTF-8?q?w=20=EB=B9=84=EB=8F=99=EA=B8=B0=20task=20=ED=8F=B4=EB=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit blog-lab의 generate/market/review 엔드포인트는 task_id만 즉시 반환하고 BackgroundTask로 실제 작업을 수행한다. 기존 코드는 응답에서 바로 post_id를 꺼내려 해 항상 'generate did not return post_id' 실패. 공통 폴링 헬퍼 _await_task로 research처럼 status=succeeded 대기하도록 수정. 점수는 review 완료 후 post를 다시 읽어 review_score로 판정. Co-Authored-By: Claude Opus 4.6 --- agent-office/app/agents/blog.py | 59 +++++++++++++++++---------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/agent-office/app/agents/blog.py b/agent-office/app/agents/blog.py index e8645bf..b93c875 100644 --- a/agent-office/app/agents/blog.py +++ b/agent-office/app/agents/blog.py @@ -49,49 +49,50 @@ class BlogAgent(BaseAgent): await self.transition("working", f"리서치: {keyword}", task_id) asyncio.create_task(self._run_pipeline(task_id, keyword)) + async def _await_task(self, step: str, task_id: str, timeout_sec: int = 240) -> Optional[int]: + """blog-lab BackgroundTask 완료 폴링. 완료 시 result_id 반환.""" + attempts = max(1, timeout_sec // 5) + for _ in range(attempts): + await asyncio.sleep(5) + status = await service_proxy.blog_task_status(task_id) + s = status.get("status") + if s == "succeeded": + return status.get("result_id") + if s == "failed": + raise Exception(f"{step} failed: {status.get('error')}") + raise Exception(f"{step} timeout ({timeout_sec}s 내 완료되지 않음)") + async def _run_pipeline(self, task_id: str, keyword: str) -> None: try: - # 1) 리서치 시작 (백그라운드 task) + # 1) 리서치 research = await service_proxy.blog_research(keyword) - research_task_id = research.get("task_id") - keyword_id = None - - # 2) 리서치 완료까지 폴링 (최대 3분) - timed_out = True - for _ in range(36): - await asyncio.sleep(5) - status = await service_proxy.blog_task_status(research_task_id) - if status.get("status") == "succeeded": - keyword_id = status.get("result_id") - timed_out = False - break - if status.get("status") == "failed": - raise Exception(f"research failed: {status.get('error')}") - if timed_out: - raise Exception("research timeout (3분 내 완료되지 않음)") + keyword_id = await self._await_task("research", research.get("task_id"), 180) if not keyword_id: raise Exception("research succeeded but result_id missing") - # 3) 작가 단계 + # 2) 작가 단계 (비동기) await self.transition("working", f"글 생성: {keyword}", task_id) gen = await service_proxy.blog_generate(keyword_id) - post_id = gen.get("post_id") or gen.get("id") + post_id = await self._await_task("generate", gen.get("task_id"), 300) if not post_id: - raise Exception("generate did not return post_id") + raise Exception("generate succeeded but post_id missing") - # 4) 마케터 단계 + # 3) 마케터 단계 (비동기) await self.transition("working", "링크 삽입 중", task_id) - await service_proxy.blog_market(post_id) + mkt = await service_proxy.blog_market(post_id) + await self._await_task("market", mkt.get("task_id"), 180) - # 5) 평가자 단계 + # 4) 평가자 단계 (비동기) await self.transition("working", "품질 리뷰 중", task_id) - review = await service_proxy.blog_review(post_id) - score = review.get("score") - passed = review.get("passed", False) + rev = await service_proxy.blog_review(post_id) + await self._await_task("review", rev.get("task_id"), 180) - post = await service_proxy.blog_get_post(post_id) - title = post.get("title", "(제목 없음)") - excerpt = (post.get("body") or "")[:300] + post_after = await service_proxy.blog_get_post(post_id) + score = post_after.get("review_score") + passed = (score or 0) >= 42 + + title = post_after.get("title", "(제목 없음)") + excerpt = (post_after.get("body") or "")[:300] update_task_status(task_id, "pending", { "keyword": keyword,