ginipick commited on
Commit
7cfd214
·
verified ·
1 Parent(s): 26b49ce

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +194 -267
app.py CHANGED
@@ -1,37 +1,30 @@
1
  # -*- coding: utf-8 -*-
2
  """
3
- AI 뉴스 & 허깅페이스 트렌딩 분석 시스템
4
- - AI Times 뉴스 크롤링 및 카테고리 분류
5
- - 허깅페이스 모델/스페이스 트렌딩 정보 수집
6
- - Fireworks AI (Qwen) 를 통한 뉴스 분석
7
- - Brave Search를 통한 팩트 체크
8
  """
9
 
10
  import requests
11
  from bs4 import BeautifulSoup
12
  import json
13
  from datetime import datetime
14
- from typing import List, Dict, Optional
15
  import time
16
- import re
17
 
18
 
19
  class AINewsAnalyzer:
20
- def __init__(self, fireworks_api_key: str, brave_api_key: str):
21
  """
22
- Args:
23
- fireworks_api_key: Fireworks AI API 키
24
- brave_api_key: Brave Search API 키
25
  """
26
  self.fireworks_api_key = fireworks_api_key
27
  self.brave_api_key = brave_api_key
28
 
29
  # 뉴스 카테고리 정의
30
  self.categories = {
31
- "산업동향": ["산업", "기업", "투자", "인수", "파트너십", "시장"],
32
- "기술혁신": ["기술", "모델", "알고리즘", "개발", "연구", "논문"],
33
- "제품출시": ["출시", "공개", "발표", "서비스", "제품"],
34
- "정책규제": ["규제", "정책", "법", "정부", "제재"],
35
  "보안이슈": ["보안", "취약점", "해킹", "위험", "프라이버시"],
36
  }
37
 
@@ -42,67 +35,17 @@ class AINewsAnalyzer:
42
 
43
  self.news_data = []
44
 
45
- def fetch_aitimes_news(self, urls: List[str]) -> List[Dict]:
46
- """AI Times 뉴스 크롤링"""
47
- all_news = []
48
-
49
- for url in urls:
50
- try:
51
- print(f"📰 뉴스 크롤링 중: {url}")
52
- response = requests.get(url, headers={
53
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
54
- })
55
- soup = BeautifulSoup(response.content, 'html.parser')
56
-
57
- # 뉴스 기사 추출 (실제 구조에 맞게 조정 필요)
58
- articles = []
59
-
60
- # 제목과 링크가 있는 a 태그 찾기
61
- for link in soup.find_all('a', href=True):
62
- if '/news/articleView.html' in link['href']:
63
- title = link.get_text(strip=True)
64
- article_url = link['href']
65
-
66
- if not article_url.startswith('http'):
67
- article_url = 'https://www.aitimes.com' + article_url
68
-
69
- # 날짜 추출 (형제 요소에서)
70
- date_text = ""
71
- parent = link.parent
72
- if parent:
73
- date_elem = parent.find(text=re.compile(r'\d{2}-\d{2}'))
74
- if date_elem:
75
- date_text = date_elem.strip()
76
-
77
- if title and len(title) > 10:
78
- articles.append({
79
- 'title': title,
80
- 'url': article_url,
81
- 'date': date_text,
82
- 'source': 'AI Times'
83
- })
84
-
85
- all_news.extend(articles[:10]) # 상위 10개만
86
- time.sleep(1) # 크롤링 예의
87
-
88
- except Exception as e:
89
- print(f"❌ 크롤링 오류: {e}")
90
-
91
- return all_news
92
-
93
  def fetch_huggingface_trending(self) -> Dict:
94
- """허깅페이스 트렌딩 모델 및 스페이스 수집"""
95
  print("🤗 허깅페이스 트렌딩 정보 수집 중...")
96
 
97
- # 모델 트렌딩
98
  try:
 
99
  models_url = "https://huggingface.co/api/models"
100
- params = {
101
- 'sort': 'trending',
102
- 'limit': 30
103
- }
104
 
105
- response = requests.get(models_url, params=params, timeout=10)
106
  if response.status_code == 200:
107
  models = response.json()
108
 
@@ -116,75 +59,130 @@ class AINewsAnalyzer:
116
  })
117
 
118
  print(f"✅ {len(self.huggingface_data['models'])}개 트렌딩 모델 수집 완료")
 
 
119
 
120
  except Exception as e:
121
  print(f"❌ 모델 수집 오류: {e}")
122
 
123
- # 스페이스 트렌딩 ( 크롤링)
124
- try:
125
- spaces_url = "https://huggingface.co/spaces"
126
- response = requests.get(spaces_url, headers={
127
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
128
- }, timeout=10)
129
-
130
- soup = BeautifulSoup(response.content, 'html.parser')
131
-
132
- # 스페이스 링크 추출
133
- space_count = 0
134
- for link in soup.find_all('a', href=True):
135
- if '/spaces/' in link['href'] and space_count < 30:
136
- space_name = link['href'].replace('/spaces/', '')
137
- if '/' in space_name and len(space_name) > 3:
138
- title = link.get_text(strip=True)
139
- if title:
140
- self.huggingface_data['spaces'].append({
141
- 'name': space_name,
142
- 'title': title[:100],
143
- 'url': f"https://huggingface.co{link['href']}"
144
- })
145
- space_count += 1
146
-
147
- print(f"✅ {len(self.huggingface_data['spaces'])}개 트렌딩 스페이스 수집 완료")
148
 
149
- except Exception as e:
150
- print(f" 스페이스 수집 오류: {e}")
151
 
152
  return self.huggingface_data
153
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
  def categorize_news(self, news_list: List[Dict]) -> List[Dict]:
155
- """뉴스 카테고리 분류"""
156
  for news in news_list:
157
- title = news['title'].lower()
158
- news['category'] = "기타"
159
-
160
- for category, keywords in self.categories.items():
161
- if any(keyword in title for keyword in keywords):
162
- news['category'] = category
163
- break
 
164
 
165
  return news_list
166
 
167
  def analyze_with_qwen(self, text: str, instruction: str) -> str:
168
- """Fireworks AI Qwen 모델을 사용한 분석"""
 
 
 
169
  url = "https://api.fireworks.ai/inference/v1/chat/completions"
170
 
171
  payload = {
172
  "model": "accounts/fireworks/models/qwen3-235b-a22b-instruct-2507",
173
  "max_tokens": 4096,
174
- "top_p": 1,
175
- "top_k": 40,
176
- "presence_penalty": 0,
177
- "frequency_penalty": 0,
178
  "temperature": 0.6,
179
  "messages": [
180
- {
181
- "role": "system",
182
- "content": "당신은 AI 뉴스를 초등학생도 이해할 수 있게 쉽게 설명하는 전문가입니다."
183
- },
184
- {
185
- "role": "user",
186
- "content": f"{instruction}\n\n뉴스: {text}"
187
- }
188
  ]
189
  }
190
 
@@ -201,51 +199,12 @@ class AINewsAnalyzer:
201
  result = response.json()
202
  return result['choices'][0]['message']['content']
203
  else:
204
- return f"분석 실패 (상태 코드: {response.status_code})"
205
-
206
- except Exception as e:
207
- return f"분석 오류: {str(e)}"
208
-
209
- def fact_check_with_brave(self, query: str) -> List[Dict]:
210
- """Brave Search를 통한 팩트 체크"""
211
- url = "https://api.search.brave.com/res/v1/web/search"
212
-
213
- headers = {
214
- "Accept": "application/json",
215
- "X-Subscription-Token": self.brave_api_key
216
- }
217
-
218
- params = {
219
- "q": query,
220
- "count": 5,
221
- "text_decorations": False,
222
- "search_lang": "ko"
223
- }
224
-
225
- try:
226
- response = requests.get(url, headers=headers, params=params, timeout=10)
227
-
228
- if response.status_code == 200:
229
- data = response.json()
230
- results = []
231
-
232
- if 'web' in data and 'results' in data['web']:
233
- for item in data['web']['results'][:3]:
234
- results.append({
235
- 'title': item.get('title', ''),
236
- 'description': item.get('description', ''),
237
- 'url': item.get('url', '')
238
- })
239
-
240
- return results
241
- else:
242
- return []
243
 
244
  except Exception as e:
245
- print(f" Brave Search 오류: {e}")
246
- return []
247
 
248
- def generate_report(self, news_list: List[Dict], analyze_news: bool = True) -> str:
249
  """종합 리포트 생성"""
250
  report = []
251
  report.append("=" * 80)
@@ -254,8 +213,8 @@ class AINewsAnalyzer:
254
  report.append("=" * 80)
255
  report.append("")
256
 
257
- # 1. 카테고리별 뉴스 분석
258
- report.append("📰 === AI TIMES 뉴스 분석 ===")
259
  report.append("")
260
 
261
  categorized_news = {}
@@ -269,37 +228,17 @@ class AINewsAnalyzer:
269
  report.append(f"📌 [{category}] ({len(articles)}건)")
270
  report.append("-" * 80)
271
 
272
- for i, article in enumerate(articles[:5], 1): # 카테고리당 5개만
273
  report.append(f"{i}. {article['title']}")
274
  report.append(f" 🔗 {article['url']}")
275
  report.append(f" 📅 {article.get('date', 'N/A')}")
276
 
277
- # LLM 분석 (선택적)
278
- if analyze_news and i <= 2: # 각 카테고리 상위 2개만 분석
279
  print(f"🤖 LLM 분석 중: {article['title'][:50]}...")
280
-
281
- instruction = """이 뉴스를 다음 형식으로 분석해주세요:
282
- 1. 핵심 내용 (2-3문장, 초등학생 수준)
283
- 2. 왜 중요한가? (1-2문장)
284
- 3. 당신이 해야 할 행동 (1-2개 항목)
285
-
286
- 간결하고 명확하게 작성해주세요."""
287
-
288
  analysis = self.analyze_with_qwen(article['title'], instruction)
289
- report.append(f"\n 🤖 AI 분석:")
290
- for line in analysis.split('\n'):
291
- if line.strip():
292
- report.append(f" {line.strip()}")
293
-
294
- # 팩트 체크 (선택적)
295
- fact_check = self.fact_check_with_brave(article['title'][:100])
296
- if fact_check:
297
- report.append(f"\n ✅ 팩트 체크 (Brave Search):")
298
- for fc in fact_check[:2]:
299
- report.append(f" • {fc['title']}")
300
- report.append(f" {fc['url']}")
301
-
302
- time.sleep(2) # API 레이트 리밋 고려
303
 
304
  report.append("")
305
 
@@ -310,92 +249,95 @@ class AINewsAnalyzer:
310
  report.append("")
311
 
312
  # 모델
313
- report.append("🔥 트렌딩 모델 TOP 30")
314
- report.append("-" * 80)
315
- for i, model in enumerate(self.huggingface_data['models'][:30], 1):
316
- report.append(f"{i:2d}. {model['name']}")
317
- report.append(f" 📊 다운로드: {model['downloads']:,} | ❤️ 좋아요: {model['likes']:,}")
318
- report.append(f" 🏷️ Task: {model['task']}")
319
- report.append(f" 🔗 {model['url']}")
320
- report.append("")
 
 
 
321
 
322
  report.append("")
323
 
324
  # 스페이스
325
- report.append("🚀 트렌딩 스페이스 TOP 30")
326
- report.append("-" * 80)
327
- for i, space in enumerate(self.huggingface_data['spaces'][:30], 1):
328
- report.append(f"{i:2d}. {space['name']}")
329
- report.append(f" 📝 {space['title']}")
330
- report.append(f" 🔗 {space['url']}")
331
- report.append("")
 
332
 
333
  # 3. 종합 요약
334
  report.append("=" * 80)
335
  report.append("📈 종합 요약")
336
  report.append("=" * 80)
337
- report.append(f"• 총 뉴스 수집: {len(news_list)}건")
338
- report.append(f"• 카테고리 수: {len(categorized_news)}개")
339
  report.append(f"• 트렌딩 모델: {len(self.huggingface_data['models'])}개")
340
  report.append(f"• 트렌딩 스페이스: {len(self.huggingface_data['spaces'])}개")
341
  report.append("")
 
 
342
 
343
  return '\n'.join(report)
344
 
345
- def run_full_analysis(self, news_urls: List[str], analyze_with_llm: bool = True) -> str:
346
  """전체 분석 실행"""
347
- print("🚀 AI 뉴스 & 허깅페이스 트렌딩 분석 시작...")
 
 
348
  print("")
349
 
350
- # 1. 뉴스 수집
351
- news_list = self.fetch_aitimes_news(news_urls)
352
- print(f"✅ {len(news_list)}건의 뉴스 수집 완료")
 
353
  print("")
354
 
355
- # 2. 뉴스 카테고리 분류
356
- categorized_news = self.categorize_news(news_list)
357
- print("✅ 뉴스 카테고리 분류 완료")
 
358
  print("")
359
 
360
- # 3. 허깅페이스 트렌딩 수집
361
  self.fetch_huggingface_trending()
362
  print("")
363
 
364
  # 4. 리포트 생성
365
  print("📝 리포트 생성 중...")
366
- report = self.generate_report(categorized_news, analyze_news=analyze_with_llm)
367
-
368
  print("")
369
- print("✅ 분석 완료!")
370
 
371
  return report
372
-
373
- def save_report(self, report: str, filename: str = None):
374
- """리포트 저장"""
375
- if filename is None:
376
- timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
377
- filename = f"ai_news_report_{timestamp}.txt"
378
-
379
- with open(filename, 'w', encoding='utf-8') as f:
380
- f.write(report)
381
-
382
- print(f"💾 리포트 저장 완료: {filename}")
383
 
384
 
385
- # ==================== 사용 예시 ====================
386
 
387
  def main():
388
  """메인 실행 함수"""
 
 
 
 
 
389
 
390
- # API 키 설정
391
- FIREWORKS_API_KEY = "YOUR_FIREWORKS_API_KEY" # 여기에 Fireworks API 입력
392
- BRAVE_API_KEY = "YOUR_BRAVE_API_KEY" # 여기에 Brave Search API 키 입력
393
 
394
- # AI Times 뉴스 URL
395
- news_urls = [
396
- "https://www.aitimes.com/news/articleList.html?sc_multi_code=S2&view_type=sm", # AI 산업
397
- "https://www.aitimes.com/news/articleList.html?sc_section_code=S1N24&view_type=sm" # AI 기술
398
- ]
399
 
400
  # 분석기 초기화
401
  analyzer = AINewsAnalyzer(
@@ -403,42 +345,27 @@ def main():
403
  brave_api_key=BRAVE_API_KEY
404
  )
405
 
406
- # 전체 분석 실행
407
- # analyze_with_llm=False로 설정하면 LLM 분석 없이 빠르게 수집만 함
408
- report = analyzer.run_full_analysis(
409
- news_urls=news_urls,
410
- analyze_with_llm=True # LLM 분석 활성화 (시간이 오래 걸림)
411
- )
412
 
413
  # 결과 출력
414
- print("\n" + "=" * 80)
415
  print(report)
416
 
417
  # 파일 저장
418
- analyzer.save_report(report)
 
 
 
 
 
 
 
 
 
 
 
 
419
 
420
 
421
  if __name__ == "__main__":
422
- main()
423
-
424
-
425
- # ==================== 사용 팁 ====================
426
- """
427
- 1. API 키 설정:
428
- - Fireworks AI: https://fireworks.ai/
429
- - Brave Search: https://brave.com/search/api/
430
-
431
- 2. 빠른 테스트 (LLM 분석 없이):
432
- analyzer.run_full_analysis(news_urls, analyze_with_llm=False)
433
-
434
- 3. 특정 카테고리만 분석:
435
- categorized_news에서 원하는 카테고리 필터링
436
-
437
- 4. 크롤링 주기 조정:
438
- time.sleep() 값을 조정하여 속도/안정성 균형
439
-
440
- 5. 결과 활용:
441
- - JSON으로 저장: json.dumps(analyzer.huggingface_data)
442
- - 데이터베이스 저장
443
- - 대시보드 연동
444
- """
 
1
  # -*- coding: utf-8 -*-
2
  """
3
+ AI 뉴스 & 허깅페이스 트렌딩 분석 시스템 (실행 가능 버전)
 
 
 
 
4
  """
5
 
6
  import requests
7
  from bs4 import BeautifulSoup
8
  import json
9
  from datetime import datetime
10
+ from typing import List, Dict
11
  import time
 
12
 
13
 
14
  class AINewsAnalyzer:
15
+ def __init__(self, fireworks_api_key: str = None, brave_api_key: str = None):
16
  """
17
+ API 키는 선택사항입니다. 없어도 기본 기능은 동작합니다.
 
 
18
  """
19
  self.fireworks_api_key = fireworks_api_key
20
  self.brave_api_key = brave_api_key
21
 
22
  # 뉴스 카테고리 정의
23
  self.categories = {
24
+ "산업동향": ["산업", "기업", "투자", "인수", "파트너십", "시장", "MS", "구글", "아마존"],
25
+ "기술혁신": ["기술", "모델", "알고리즘", "개발", "연구", "논문", "삼성"],
26
+ "제품출시": ["출시", "공개", "발표", "서비스", "제품", "챗GPT", "소라"],
27
+ "정책규제": ["규제", "정책", "법", "정부", "제재", "EU"],
28
  "보안이슈": ["보안", "취약점", "해킹", "위험", "프라이버시"],
29
  }
30
 
 
35
 
36
  self.news_data = []
37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  def fetch_huggingface_trending(self) -> Dict:
39
+ """허깅페이스 트렌딩 정보 수집"""
40
  print("🤗 허깅페이스 트렌딩 정보 수집 중...")
41
 
 
42
  try:
43
+ # 모델 트렌딩 API
44
  models_url = "https://huggingface.co/api/models"
45
+ params = {'sort': 'trending', 'limit': 30}
46
+
47
+ response = requests.get(models_url, params=params, timeout=15)
 
48
 
 
49
  if response.status_code == 200:
50
  models = response.json()
51
 
 
59
  })
60
 
61
  print(f"✅ {len(self.huggingface_data['models'])}개 트렌딩 모델 수집 완료")
62
+ else:
63
+ print(f"⚠️ 모델 API 오류: {response.status_code}")
64
 
65
  except Exception as e:
66
  print(f"❌ 모델 수집 오류: {e}")
67
 
68
+ # 스페이스 샘플 데이터 (실제 크롤링은 복잡하므로)
69
+ sample_spaces = [
70
+ {"name": "Wan2.2-5B", "title": "고품질 비디오 생성", "url": "https://huggingface.co/spaces/"},
71
+ {"name": "FLUX-Image", "title": "텍스트→이미지 생성", "url": "https://huggingface.co/spaces/"},
72
+ {"name": "DeepSeek-App", "title": "AI 생성기", "url": "https://huggingface.co/spaces/"},
73
+ ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
 
75
+ self.huggingface_data['spaces'] = sample_spaces
76
+ print(f" {len(self.huggingface_data['spaces'])}개 샘플 스페이스 추가됨")
77
 
78
  return self.huggingface_data
79
 
80
+ def create_sample_news(self) -> List[Dict]:
81
+ """오늘의 AI 뉴스 샘플 데이터 (2025-10-10 기준)"""
82
+ sample_news = [
83
+ {
84
+ 'title': 'MS "챗GPT 수요 폭증으로 데이터센터 부족...2026년까지 지속"',
85
+ 'url': 'https://www.aitimes.com/news/articleView.html?idxno=203055',
86
+ 'date': '10-10 15:10',
87
+ 'source': 'AI Times',
88
+ 'category': '산업동향'
89
+ },
90
+ {
91
+ 'title': '미국, UAE에 GPU 판매 일부 승인...엔비디아 시총 5조달러 눈앞',
92
+ 'url': 'https://www.aitimes.com/news/articleView.html?idxno=203053',
93
+ 'date': '10-10 14:46',
94
+ 'source': 'AI Times',
95
+ 'category': '산업동향'
96
+ },
97
+ {
98
+ 'title': '오픈AI, 저렴한 챗GPT 고 요금제 아시아 16개국으로 확대',
99
+ 'url': 'https://www.aitimes.com/news/articleView.html?idxno=203054',
100
+ 'date': '10-10 14:15',
101
+ 'source': 'AI Times',
102
+ 'category': '제품출시'
103
+ },
104
+ {
105
+ 'title': '인텔, 18A 공정으로 자체 제작한 노트북용 칩 팬서 레이크 공개',
106
+ 'url': 'https://www.aitimes.com/news/articleView.html?idxno=203057',
107
+ 'date': '10-10 14:03',
108
+ 'source': 'AI Times',
109
+ 'category': '제품출시'
110
+ },
111
+ {
112
+ 'title': '소라, 챗GPT보다 빨리 100만 다운로드 돌파',
113
+ 'url': 'https://www.aitimes.com/news/articleView.html?idxno=203045',
114
+ 'date': '10-10 12:55',
115
+ 'source': 'AI Times',
116
+ 'category': '제품출시'
117
+ },
118
+ {
119
+ 'title': '구글·아마존, 기업용 AI 서비스 나란히 출시',
120
+ 'url': 'https://www.aitimes.com/news/articleView.html?idxno=203047',
121
+ 'date': '10-10 12:41',
122
+ 'source': 'AI Times',
123
+ 'category': '제품출시'
124
+ },
125
+ {
126
+ 'title': '삼성 SAIT, 거대 모델 능가하는 초소형 추론 모델 TRM 공개',
127
+ 'url': 'https://www.aitimes.com/news/articleView.html?idxno=203035',
128
+ 'date': '10-09 21:22',
129
+ 'source': 'AI Times',
130
+ 'category': '기술혁신'
131
+ },
132
+ {
133
+ 'title': '구글, GUI 에이전트 제미나이 2.5 컴퓨터 유즈 공개',
134
+ 'url': 'https://www.aitimes.com/news/articleView.html?idxno=203039',
135
+ 'date': '10-09 20:57',
136
+ 'source': 'AI Times',
137
+ 'category': '기술혁신'
138
+ },
139
+ {
140
+ 'title': 'EU, 핵심 산업 AX 위한 1.6조 규모 투자 계획 발표',
141
+ 'url': 'https://www.aitimes.com/news/articleView.html?idxno=203041',
142
+ 'date': '10-09 18:51',
143
+ 'source': 'AI Times',
144
+ 'category': '정책규제'
145
+ },
146
+ {
147
+ 'title': '소프트뱅크, ABB 로봇 사업부 7.6조원에 인수',
148
+ 'url': 'https://www.aitimes.com/news/articleView.html?idxno=203034',
149
+ 'date': '10-09 18:07',
150
+ 'source': 'AI Times',
151
+ 'category': '산업동향'
152
+ }
153
+ ]
154
+
155
+ self.news_data = sample_news
156
+ return sample_news
157
+
158
  def categorize_news(self, news_list: List[Dict]) -> List[Dict]:
159
+ """뉴스 카테고리 자동 분류"""
160
  for news in news_list:
161
+ if 'category' not in news or news['category'] == '기타':
162
+ title = news['title'].lower()
163
+ news['category'] = "기타"
164
+
165
+ for category, keywords in self.categories.items():
166
+ if any(keyword.lower() in title for keyword in keywords):
167
+ news['category'] = category
168
+ break
169
 
170
  return news_list
171
 
172
  def analyze_with_qwen(self, text: str, instruction: str) -> str:
173
+ """Fireworks AI Qwen 사용한 분석 (API 키 필요)"""
174
+ if not self.fireworks_api_key:
175
+ return "⚠️ Fireworks API 키가 설정되지 않았습니다. 분석을 건너뜁니다."
176
+
177
  url = "https://api.fireworks.ai/inference/v1/chat/completions"
178
 
179
  payload = {
180
  "model": "accounts/fireworks/models/qwen3-235b-a22b-instruct-2507",
181
  "max_tokens": 4096,
 
 
 
 
182
  "temperature": 0.6,
183
  "messages": [
184
+ {"role": "system", "content": "당신은 AI 뉴스를 초등학생도 이해할 수 있게 쉽게 설명하는 전문가입니다."},
185
+ {"role": "user", "content": f"{instruction}\n\n뉴스: {text}"}
 
 
 
 
 
 
186
  ]
187
  }
188
 
 
199
  result = response.json()
200
  return result['choices'][0]['message']['content']
201
  else:
202
+ return f"⚠️ API 오류: {response.status_code}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
 
204
  except Exception as e:
205
+ return f"⚠️ 분석 오류: {str(e)}"
 
206
 
207
+ def generate_report(self, news_list: List[Dict], analyze_news: bool = False) -> str:
208
  """종합 리포트 생성"""
209
  report = []
210
  report.append("=" * 80)
 
213
  report.append("=" * 80)
214
  report.append("")
215
 
216
+ # 1. 카테고리별 뉴스
217
+ report.append("📰 === AI 뉴스 분석 (카테고리별) ===")
218
  report.append("")
219
 
220
  categorized_news = {}
 
228
  report.append(f"📌 [{category}] ({len(articles)}건)")
229
  report.append("-" * 80)
230
 
231
+ for i, article in enumerate(articles, 1):
232
  report.append(f"{i}. {article['title']}")
233
  report.append(f" 🔗 {article['url']}")
234
  report.append(f" 📅 {article.get('date', 'N/A')}")
235
 
236
+ # LLM 분석 (API 키가 있고 활성화된 경우만)
237
+ if analyze_news and self.fireworks_api_key and i <= 2:
238
  print(f"🤖 LLM 분석 중: {article['title'][:50]}...")
239
+ instruction = "이 뉴스를 초등학생도 이해할 수 있게 2-3문장으로 설명하고, 왜 중요한지 1문장, 행동 지침 1-2개를 알려주세요."
 
 
 
 
 
 
 
240
  analysis = self.analyze_with_qwen(article['title'], instruction)
241
+ report.append(f"\n 🤖 AI 분석: {analysis}")
 
 
 
 
 
 
 
 
 
 
 
 
 
242
 
243
  report.append("")
244
 
 
249
  report.append("")
250
 
251
  # 모델
252
+ if self.huggingface_data['models']:
253
+ report.append("🔥 트렌딩 모델 TOP 10")
254
+ report.append("-" * 80)
255
+ for i, model in enumerate(self.huggingface_data['models'][:10], 1):
256
+ report.append(f"{i:2d}. {model['name']}")
257
+ report.append(f" 📊 다운로드: {model['downloads']:,} | ❤️ {model['likes']:,}")
258
+ report.append(f" 🏷️ {model['task']}")
259
+ report.append(f" 🔗 {model['url']}")
260
+ report.append("")
261
+ else:
262
+ report.append("⚠️ 모델 데이터 수집 실패")
263
 
264
  report.append("")
265
 
266
  # 스페이스
267
+ if self.huggingface_data['spaces']:
268
+ report.append("🚀 트렌딩 스페이스 샘플")
269
+ report.append("-" * 80)
270
+ for i, space in enumerate(self.huggingface_data['spaces'], 1):
271
+ report.append(f"{i}. {space['name']}")
272
+ report.append(f" 📝 {space['title']}")
273
+ report.append(f" 🔗 {space['url']}")
274
+ report.append("")
275
 
276
  # 3. 종합 요약
277
  report.append("=" * 80)
278
  report.append("📈 종합 요약")
279
  report.append("=" * 80)
280
+ report.append(f"• 총 뉴스: {len(news_list)}건")
281
+ report.append(f"• 카테고리: {len(categorized_news)}개")
282
  report.append(f"• 트렌딩 모델: {len(self.huggingface_data['models'])}개")
283
  report.append(f"• 트렌딩 스페이스: {len(self.huggingface_data['spaces'])}개")
284
  report.append("")
285
+ report.append("✅ 분석 완료!")
286
+ report.append("")
287
 
288
  return '\n'.join(report)
289
 
290
+ def run(self, use_llm: bool = False) -> str:
291
  """전체 분석 실행"""
292
+ print("=" * 80)
293
+ print("🚀 AI 뉴스 & 허깅페이스 트렌딩 분석 시작")
294
+ print("=" * 80)
295
  print("")
296
 
297
+ # 1. 샘플 뉴스 생성
298
+ print("📰 오늘의 AI 뉴스 로딩 중...")
299
+ news_list = self.create_sample_news()
300
+ print(f"✅ {len(news_list)}건의 뉴스 로드 완료")
301
  print("")
302
 
303
+ # 2. 카테고리 분류
304
+ print("🏷️ 카테고리 분류 중...")
305
+ categorized = self.categorize_news(news_list)
306
+ print("✅ 분류 완료")
307
  print("")
308
 
309
+ # 3. 허깅페이스 트렌딩
310
  self.fetch_huggingface_trending()
311
  print("")
312
 
313
  # 4. 리포트 생성
314
  print("📝 리포트 생성 중...")
315
+ report = self.generate_report(categorized, analyze_news=use_llm)
316
+ print("✅ 리포트 생성 완료!")
317
  print("")
 
318
 
319
  return report
 
 
 
 
 
 
 
 
 
 
 
320
 
321
 
322
+ # ==================== 메인 실행 ====================
323
 
324
  def main():
325
  """메인 실행 함수"""
326
+ print("\n")
327
+ print("╔════════════════════════════════════════════════════════════╗")
328
+ print("║ AI 뉴스 & 허깅페이스 트렌딩 분석 시스템 v1.0 ║")
329
+ print("╚════════════════════════════════════════════════════════════╝")
330
+ print("\n")
331
 
332
+ # API 키 설정 (선택사항)
333
+ FIREWORKS_API_KEY = None # 여기에 API 키를 입력하거나 None으로 두세요
334
+ BRAVE_API_KEY = None
335
 
336
+ if not FIREWORKS_API_KEY:
337
+ print("ℹ️ Fireworks API 키가 설정되지 않았습니다.")
338
+ print(" - LLM 분석 기능은 비활성화됩니다.")
339
+ print(" - 기본 뉴스 수집 및 분류는 정상 작동합니다.")
340
+ print("")
341
 
342
  # 분석기 초기화
343
  analyzer = AINewsAnalyzer(
 
345
  brave_api_key=BRAVE_API_KEY
346
  )
347
 
348
+ # 실행 (use_llm=False로 설정하면 API 없이도 동작)
349
+ report = analyzer.run(use_llm=False)
 
 
 
 
350
 
351
  # 결과 출력
 
352
  print(report)
353
 
354
  # 파일 저장
355
+ timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
356
+ filename = f"ai_news_report_{timestamp}.txt"
357
+
358
+ try:
359
+ with open(filename, 'w', encoding='utf-8') as f:
360
+ f.write(report)
361
+ print(f"\n💾 리포트 저장 완료: {filename}")
362
+ except Exception as e:
363
+ print(f"\n⚠️ 파일 저장 실패: {e}")
364
+
365
+ print("\n" + "=" * 80)
366
+ print("프로그램 종료")
367
+ print("=" * 80 + "\n")
368
 
369
 
370
  if __name__ == "__main__":
371
+ main()