feat: Ollama qwen3:14b 기반 AI 뉴스 요약 + 텔레그램 통합 허브

- stock-lab: POST /api/stock/news/summarize 추가 (Ollama /api/generate 호출, 토큰/duration 추적)
- agent-office: telegram 패키지 분해 (client/formatter/messaging/webhook/router/agent_registry)
- send_agent_message 통합 API로 에이전트 중립 메시지 포맷 표준화
- 텔레그램 → 에이전트 명령 라우터 (/status, /stock news, /music credits 등)
- 토큰 사용량 집계 API 및 GET /agents/{id}/token-usage

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-13 01:44:27 +09:00
parent de91f424a3
commit 86e7f727eb
17 changed files with 696 additions and 116 deletions

View File

@@ -14,22 +14,35 @@ class StockAgent(BaseAgent):
return
task_id = create_task(self.agent_id, "news_summary", {"limit": 15})
await self.transition("working", "뉴스 수집 중...", task_id)
await self.transition("working", "AI 뉴스 요약 생성 중...", task_id)
try:
news = await service_proxy.fetch_stock_news(limit=15)
indices = await service_proxy.fetch_stock_indices()
summary = self._format_news_summary(news, indices)
# AI 요약 호출 (뉴스 수집 + LLM 처리는 stock-lab이 담당)
result = await service_proxy.summarize_stock_news(limit=15)
await self.transition("reporting", "뉴스 요약 전송 중...")
from ..telegram_bot import send_stock_summary
tg_result = await send_stock_summary(summary)
# 새 통합 텔레그램 API 사용
from ..telegram import send_agent_message
tg_result = await send_agent_message(
agent_id=self.agent_id,
kind="report",
title="아침 시장 브리핑",
body=result["summary"],
task_id=task_id,
metadata={
"tokens": result["tokens"]["total"],
"duration_ms": result["duration_ms"],
"model": result["model"],
},
)
update_task_status(task_id, "succeeded", {
"summary": summary,
"news_count": len(news) if isinstance(news, list) else 0,
"summary": result["summary"],
"article_count": result.get("article_count", 0),
"tokens": result["tokens"],
"model": result["model"],
"duration_ms": result["duration_ms"],
"telegram_sent": tg_result.get("ok", False),
"telegram_message_id": tg_result.get("message_id"),
})
@@ -50,8 +63,13 @@ class StockAgent(BaseAgent):
async def on_command(self, command: str, params: dict) -> dict:
if command == "test_telegram":
from ..telegram_bot import send_message
result = await send_message("🔔 [주식 에이전트] 텔레그램 테스트 메시지입니다.")
from ..telegram import send_agent_message
result = await send_agent_message(
agent_id=self.agent_id,
kind="info",
title="연결 테스트",
body="텔레그램 연동이 정상적으로 동작합니다.",
)
return {
"ok": result.get("ok", False),
"message": "텔레그램 전송 성공" if result.get("ok") else "텔레그램 전송 실패",
@@ -88,30 +106,3 @@ class StockAgent(BaseAgent):
async def on_approval(self, task_id: str, approved: bool, feedback: str = "") -> None:
pass
def _format_news_summary(self, news, indices) -> str:
lines = ["📈 [주식 에이전트] 아침 뉴스 요약", "" * 20]
if isinstance(news, list):
for item in news[:10]:
title = item.get("title", "")
if title:
lines.append(f"{title}")
elif isinstance(news, dict) and "articles" in news:
for item in news["articles"][:10]:
title = item.get("title", "")
if title:
lines.append(f"{title}")
if indices:
lines.append("")
lines.append("📊 주요 지수")
if isinstance(indices, dict):
for key, val in indices.items():
if isinstance(val, dict):
name = val.get("name", key)
price = val.get("price", "")
change = val.get("change", "")
lines.append(f"{name}: {price} ({change})")
return "\n".join(lines)