Major architectural transformation from synchronous to asynchronous processing: ## Pipeline Services (8 microservices) - pipeline-scheduler: APScheduler for 30-minute periodic job triggers - pipeline-rss-collector: RSS feed collection with deduplication (7-day TTL) - pipeline-google-search: Content enrichment via Google Search API - pipeline-ai-summarizer: AI summarization using Claude API (claude-sonnet-4-20250514) - pipeline-translator: Translation using DeepL Pro API - pipeline-image-generator: Image generation with Replicate API (Stable Diffusion) - pipeline-article-assembly: Final article assembly and MongoDB storage - pipeline-monitor: Real-time monitoring dashboard (port 8100) ## Key Features - Redis-based job queue with deduplication - Asynchronous processing with Python asyncio - Shared models and queue manager for inter-service communication - Docker containerization for all services - Container names standardized with site11_ prefix ## Removed Services - Moved to backup: google-search, rss-feed, news-aggregator, ai-writer ## Configuration - DeepL Pro API: 3abbc796-2515-44a8-972d-22dcf27ab54a - Claude Model: claude-sonnet-4-20250514 - Redis Queue TTL: 7 days for deduplication 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
168 lines
6.6 KiB
Python
Executable File
168 lines
6.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
AI Writer Service Test
|
|
Claude API를 사용한 전문적인 뉴스 기사 생성 테스트
|
|
"""
|
|
import asyncio
|
|
import httpx
|
|
import json
|
|
from datetime import datetime
|
|
|
|
# Service URL
|
|
SERVICE_URL = "http://localhost:8019"
|
|
|
|
async def test_article_generation():
|
|
"""인공지능 키워드로 기사 생성 테스트"""
|
|
async with httpx.AsyncClient(timeout=120.0) as client:
|
|
print("\n" + "="*70)
|
|
print(" AI Writer Service - 전문 기사 생성 테스트 ")
|
|
print("="*70)
|
|
|
|
print("\n📰 '인공지능' 키워드로 전문 기사 생성 중...")
|
|
print("-" * 50)
|
|
|
|
# Generate article
|
|
response = await client.post(
|
|
f"{SERVICE_URL}/api/generate",
|
|
json={
|
|
"keyword": "인공지능",
|
|
"limit": 5,
|
|
"google_results_per_title": 3,
|
|
"lang": "ko",
|
|
"country": "KR",
|
|
"style": "professional"
|
|
}
|
|
)
|
|
|
|
if response.status_code == 200:
|
|
article = response.json()
|
|
|
|
print(f"\n✅ 기사 생성 완료!")
|
|
print(f"\n📌 기사 ID: {article['news_id']}")
|
|
print(f"📅 생성 시간: {article['created_at']}")
|
|
print(f"\n📰 제목: {article['title']}")
|
|
print(f"📝 요약: {article['summary']}")
|
|
|
|
print(f"\n🔍 카테고리: {', '.join(article['categories'])}")
|
|
|
|
# Print subtopics
|
|
print(f"\n📚 소주제 ({len(article['subtopics'])}개):")
|
|
for i, subtopic in enumerate(article['subtopics'], 1):
|
|
print(f"\n [{i}] {subtopic['title']}")
|
|
print(f" 문단 수: {len(subtopic['content'])}개")
|
|
for j, paragraph in enumerate(subtopic['content'][:1], 1): # Show first paragraph only
|
|
print(f" 미리보기: {paragraph[:150]}...")
|
|
|
|
# Print entities
|
|
entities = article['entities']
|
|
print(f"\n🏷️ 추출된 개체:")
|
|
if entities['people']:
|
|
print(f" 👤 인물: {', '.join(entities['people'])}")
|
|
if entities['organizations']:
|
|
print(f" 🏢 기관: {', '.join(entities['organizations'])}")
|
|
if entities['groups']:
|
|
print(f" 👥 단체: {', '.join(entities['groups'])}")
|
|
if entities['countries']:
|
|
print(f" 🌍 국가: {', '.join(entities['countries'])}")
|
|
if entities.get('events'):
|
|
events = entities['events']
|
|
if events:
|
|
print(f" 📅 이벤트 ({len(events)}개):")
|
|
for evt in events[:3]: # 처음 3개만 표시
|
|
if isinstance(evt, dict):
|
|
evt_str = f" - {evt.get('name', '')}"
|
|
if evt.get('date'):
|
|
evt_str += f" [{evt['date']}]"
|
|
if evt.get('location'):
|
|
evt_str += f" @{evt['location']}"
|
|
print(evt_str)
|
|
else:
|
|
# 이전 형식 (문자열) 지원
|
|
print(f" - {evt}")
|
|
if entities.get('keywords'):
|
|
keywords = entities['keywords']
|
|
if keywords:
|
|
print(f" 🔑 키워드: {', '.join(keywords[:5])}" +
|
|
("..." if len(keywords) > 5 else ""))
|
|
|
|
print(f"\n📊 참조 소스: {article.get('source_count', 0)}개")
|
|
|
|
# Save full article to file
|
|
with open('generated_article.json', 'w', encoding='utf-8') as f:
|
|
json.dump(article, f, ensure_ascii=False, indent=2)
|
|
print(f"\n💾 전체 기사가 'generated_article.json'에 저장되었습니다.")
|
|
|
|
else:
|
|
print(f"❌ 오류: {response.status_code}")
|
|
print(f" 상세: {response.text}")
|
|
|
|
async def test_health_check():
|
|
"""서비스 상태 확인"""
|
|
async with httpx.AsyncClient() as client:
|
|
print("\n" + "="*60)
|
|
print("서비스 Health Check")
|
|
print("="*60)
|
|
|
|
response = await client.get(f"{SERVICE_URL}/health")
|
|
if response.status_code == 200:
|
|
data = response.json()
|
|
print(f"✓ AI Writer 서비스 상태: {data.get('status', 'unknown')}")
|
|
if 'services' in data:
|
|
print(f" - News Aggregator: {data['services'].get('news_aggregator', 'unknown')}")
|
|
print(f" - MongoDB: {data['services'].get('mongodb', 'unknown')}")
|
|
print(f" - Claude API: {data['services'].get('claude_api', 'unknown')}")
|
|
if 'error' in data:
|
|
print(f" - Error: {data['error']}")
|
|
else:
|
|
print(f"✗ Health check 실패: {response.status_code}")
|
|
|
|
async def test_batch_generation():
|
|
"""여러 키워드 일괄 처리 테스트"""
|
|
async with httpx.AsyncClient(timeout=180.0) as client:
|
|
print("\n" + "="*60)
|
|
print("일괄 기사 생성 테스트")
|
|
print("="*60)
|
|
|
|
keywords = ["AI 혁신", "디지털 전환", "스마트시티"]
|
|
print(f"\n키워드: {', '.join(keywords)}")
|
|
|
|
response = await client.post(
|
|
f"{SERVICE_URL}/api/generate/batch",
|
|
json=keywords,
|
|
params={"style": "analytical"}
|
|
)
|
|
|
|
if response.status_code == 200:
|
|
data = response.json()
|
|
print(f"\n✅ 처리 완료: {data['total_processed']}개")
|
|
|
|
if data['success']:
|
|
print("\n성공한 기사:")
|
|
for item in data['success']:
|
|
print(f" - {item['keyword']}: {item['title'][:50]}...")
|
|
|
|
if data['errors']:
|
|
print("\n실패한 항목:")
|
|
for item in data['errors']:
|
|
print(f" - {item['keyword']}: {item['error']}")
|
|
else:
|
|
print(f"❌ 오류: {response.status_code}")
|
|
|
|
async def main():
|
|
"""메인 테스트 실행"""
|
|
print("\n" + "="*70)
|
|
print(" AI Writer Service Test Suite ")
|
|
print(" RSS → Google Search → Claude AI 기사 생성 ")
|
|
print("="*70)
|
|
|
|
# Run tests
|
|
await test_health_check()
|
|
await test_article_generation()
|
|
# await test_batch_generation() # Optional: batch test
|
|
|
|
print("\n" + "="*70)
|
|
print(" 테스트 완료 ")
|
|
print("="*70)
|
|
|
|
if __name__ == "__main__":
|
|
asyncio.run(main()) |