ginipick commited on
Commit
5034fe4
ยท
verified ยท
1 Parent(s): 9ad2f29

Delete app-BACKUP.py

Browse files
Files changed (1) hide show
  1. app-BACKUP.py +0 -1882
app-BACKUP.py DELETED
@@ -1,1882 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- from flask import Flask, render_template_string, jsonify, request
4
- import requests
5
- import json
6
- from datetime import datetime
7
- from typing import List, Dict, Optional
8
- import os
9
- import sys
10
- import sqlite3
11
- import time
12
- from huggingface_hub import HfApi
13
- from bs4 import BeautifulSoup
14
- import re
15
-
16
- # Flask ์•ฑ ์ดˆ๊ธฐํ™”
17
- app = Flask(__name__)
18
- app.config['JSON_AS_ASCII'] = False
19
-
20
- # ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํŒŒ์ผ ๊ฒฝ๋กœ
21
- DB_PATH = 'ai_news_analysis.db'
22
-
23
-
24
- # ============================================
25
- # HTML ํ…œํ”Œ๋ฆฟ (ํƒญ UI ํฌํ•จ)
26
- # ============================================
27
-
28
- HTML_TEMPLATE = """
29
- <!DOCTYPE html>
30
- <html lang="ko">
31
- <head>
32
- <meta charset="UTF-8">
33
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
34
- <title>AI ๋‰ด์Šค & ํ—ˆ๊น…ํŽ˜์ด์Šค LLM ๋ถ„์„ ์‹œ์Šคํ…œ</title>
35
- <style>
36
- * {
37
- margin: 0;
38
- padding: 0;
39
- box-sizing: border-box;
40
- }
41
-
42
- body {
43
- font-family: 'Segoe UI', 'Apple SD Gothic Neo', 'Malgun Gothic', sans-serif;
44
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
45
- padding: 20px;
46
- color: #333;
47
- min-height: 100vh;
48
- }
49
-
50
- .container {
51
- max-width: 1400px;
52
- margin: 0 auto;
53
- background: white;
54
- border-radius: 20px;
55
- padding: 40px;
56
- box-shadow: 0 20px 60px rgba(0,0,0,0.3);
57
- }
58
-
59
- h1 {
60
- text-align: center;
61
- color: #667eea;
62
- margin-bottom: 10px;
63
- font-size: 2.8em;
64
- font-weight: 800;
65
- }
66
-
67
- .subtitle {
68
- text-align: center;
69
- color: #666;
70
- margin-bottom: 40px;
71
- font-size: 1.2em;
72
- }
73
-
74
- /* ํƒญ ์Šคํƒ€์ผ */
75
- .tabs {
76
- display: flex;
77
- gap: 15px;
78
- margin-bottom: 30px;
79
- border-bottom: 3px solid #e0e0e0;
80
- padding-bottom: 0;
81
- }
82
-
83
- .tab {
84
- padding: 15px 30px;
85
- background: #f5f5f5;
86
- border: none;
87
- border-radius: 10px 10px 0 0;
88
- cursor: pointer;
89
- font-size: 1.1em;
90
- font-weight: 600;
91
- color: #666;
92
- transition: all 0.3s;
93
- }
94
-
95
- .tab.active {
96
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
97
- color: white;
98
- transform: translateY(-3px);
99
- box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
100
- }
101
-
102
- .tab:hover {
103
- background: #e0e0e0;
104
- }
105
-
106
- .tab.active:hover {
107
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
108
- }
109
-
110
- .tab-content {
111
- display: none;
112
- }
113
-
114
- .tab-content.active {
115
- display: block;
116
- animation: fadeIn 0.5s ease-out;
117
- }
118
-
119
- /* ํ†ต๊ณ„ ์นด๋“œ */
120
- .stats {
121
- display: grid;
122
- grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
123
- gap: 25px;
124
- margin-bottom: 50px;
125
- }
126
-
127
- .stat-card {
128
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
129
- color: white;
130
- padding: 30px;
131
- border-radius: 15px;
132
- text-align: center;
133
- box-shadow: 0 8px 20px rgba(102, 126, 234, 0.4);
134
- transform: translateY(0);
135
- transition: transform 0.3s, box-shadow 0.3s;
136
- }
137
-
138
- .stat-card:hover {
139
- transform: translateY(-5px);
140
- box-shadow: 0 12px 30px rgba(102, 126, 234, 0.6);
141
- }
142
-
143
- .stat-number {
144
- font-size: 3.5em;
145
- font-weight: bold;
146
- margin-bottom: 10px;
147
- text-shadow: 2px 2px 4px rgba(0,0,0,0.2);
148
- }
149
-
150
- .stat-label {
151
- font-size: 1.2em;
152
- opacity: 0.95;
153
- font-weight: 500;
154
- }
155
-
156
- /* ๋‰ด์Šค ์นด๋“œ (LLM ๋ถ„์„ ๋ฒ„์ „) */
157
- .news-card {
158
- background: white;
159
- border-radius: 15px;
160
- padding: 30px;
161
- margin-bottom: 25px;
162
- box-shadow: 0 5px 20px rgba(0,0,0,0.1);
163
- border-left: 6px solid #667eea;
164
- transition: all 0.3s;
165
- }
166
-
167
- .news-card:hover {
168
- transform: translateX(10px);
169
- box-shadow: 0 10px 30px rgba(0,0,0,0.15);
170
- }
171
-
172
- .news-header {
173
- display: flex;
174
- justify-content: space-between;
175
- align-items: flex-start;
176
- margin-bottom: 20px;
177
- flex-wrap: wrap;
178
- gap: 15px;
179
- }
180
-
181
- .news-title {
182
- font-size: 1.4em;
183
- font-weight: 700;
184
- color: #2c3e50;
185
- flex: 1;
186
- min-width: 300px;
187
- }
188
-
189
- .news-meta {
190
- display: flex;
191
- gap: 15px;
192
- color: #7f8c8d;
193
- font-size: 0.9em;
194
- }
195
-
196
- .analysis-section {
197
- background: #f8f9fa;
198
- padding: 20px;
199
- border-radius: 10px;
200
- margin-top: 15px;
201
- }
202
-
203
- .analysis-item {
204
- margin-bottom: 20px;
205
- padding-bottom: 20px;
206
- border-bottom: 1px solid #e0e0e0;
207
- }
208
-
209
- .analysis-item:last-child {
210
- border-bottom: none;
211
- margin-bottom: 0;
212
- padding-bottom: 0;
213
- }
214
-
215
- .analysis-label {
216
- display: inline-block;
217
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
218
- color: white;
219
- padding: 8px 15px;
220
- border-radius: 20px;
221
- font-size: 0.9em;
222
- font-weight: 600;
223
- margin-bottom: 10px;
224
- }
225
-
226
- .analysis-content {
227
- color: #34495e;
228
- line-height: 1.8;
229
- font-size: 1.05em;
230
- }
231
-
232
- .impact-level {
233
- display: inline-block;
234
- padding: 5px 12px;
235
- border-radius: 15px;
236
- font-size: 0.85em;
237
- font-weight: 600;
238
- margin-left: 10px;
239
- }
240
-
241
- .impact-high {
242
- background: #ff6b6b;
243
- color: white;
244
- }
245
-
246
- .impact-medium {
247
- background: #ffa502;
248
- color: white;
249
- }
250
-
251
- .impact-low {
252
- background: #26de81;
253
- color: white;
254
- }
255
-
256
- /* ๋ชจ๋ธ ์นด๋“œ */
257
- .model-grid {
258
- display: grid;
259
- grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
260
- gap: 25px;
261
- margin-top: 30px;
262
- }
263
-
264
- .model-card {
265
- background: white;
266
- padding: 25px;
267
- border-radius: 12px;
268
- box-shadow: 0 5px 15px rgba(0,0,0,0.1);
269
- transition: all 0.3s;
270
- border-top: 4px solid #667eea;
271
- position: relative;
272
- }
273
-
274
- .model-card:hover {
275
- transform: translateY(-5px);
276
- box-shadow: 0 10px 25px rgba(102, 126, 234, 0.3);
277
- }
278
-
279
- .model-rank {
280
- position: absolute;
281
- top: -15px;
282
- right: 20px;
283
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
284
- color: white;
285
- width: 50px;
286
- height: 50px;
287
- border-radius: 50%;
288
- display: flex;
289
- align-items: center;
290
- justify-content: center;
291
- font-weight: 700;
292
- font-size: 1.2em;
293
- box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
294
- }
295
-
296
- .model-name {
297
- font-weight: 700;
298
- color: #667eea;
299
- margin-bottom: 15px;
300
- font-size: 1.15em;
301
- word-break: break-word;
302
- padding-right: 60px;
303
- }
304
-
305
- .model-stats {
306
- display: grid;
307
- grid-template-columns: repeat(2, 1fr);
308
- gap: 10px;
309
- margin: 15px 0;
310
- padding: 15px;
311
- background: #f8f9fa;
312
- border-radius: 8px;
313
- }
314
-
315
- .model-stat-item {
316
- font-size: 0.9em;
317
- }
318
-
319
- .model-task {
320
- background: #e8f0fe;
321
- color: #667eea;
322
- padding: 6px 12px;
323
- border-radius: 20px;
324
- font-size: 0.85em;
325
- display: inline-block;
326
- margin-bottom: 15px;
327
- font-weight: 600;
328
- }
329
-
330
- .model-analysis {
331
- background: #f0f4ff;
332
- padding: 15px;
333
- border-radius: 8px;
334
- margin-top: 15px;
335
- color: #34495e;
336
- line-height: 1.7;
337
- font-size: 0.95em;
338
- }
339
-
340
- /* ์ŠคํŽ˜์ด์Šค ์นด๋“œ */
341
- .space-card {
342
- background: white;
343
- padding: 25px;
344
- border-radius: 12px;
345
- box-shadow: 0 5px 15px rgba(0,0,0,0.1);
346
- margin-bottom: 20px;
347
- border-left: 5px solid #ff6b6b;
348
- transition: all 0.3s;
349
- }
350
-
351
- .space-card:hover {
352
- transform: translateX(10px);
353
- box-shadow: 0 10px 25px rgba(255, 107, 107, 0.3);
354
- }
355
-
356
- .space-header {
357
- display: flex;
358
- justify-content: space-between;
359
- align-items: flex-start;
360
- margin-bottom: 15px;
361
- }
362
-
363
- .space-name {
364
- font-weight: 700;
365
- color: #ff6b6b;
366
- font-size: 1.3em;
367
- }
368
-
369
- .space-badge {
370
- background: #ff6b6b;
371
- color: white;
372
- padding: 5px 12px;
373
- border-radius: 15px;
374
- font-size: 0.8em;
375
- font-weight: 600;
376
- }
377
-
378
- .space-description {
379
- color: #555;
380
- margin-bottom: 15px;
381
- line-height: 1.6;
382
- }
383
-
384
- .space-analysis {
385
- background: #fff5f5;
386
- padding: 15px;
387
- border-radius: 8px;
388
- margin-top: 15px;
389
- }
390
-
391
- .space-tech {
392
- display: flex;
393
- flex-wrap: wrap;
394
- gap: 8px;
395
- margin-top: 15px;
396
- }
397
-
398
- .tech-tag {
399
- background: #ffe5e5;
400
- color: #ff6b6b;
401
- padding: 5px 10px;
402
- border-radius: 12px;
403
- font-size: 0.8em;
404
- font-weight: 600;
405
- }
406
-
407
- /* ๋ฒ„ํŠผ */
408
- .button-group {
409
- text-align: center;
410
- margin: 40px 0;
411
- display: flex;
412
- justify-content: center;
413
- gap: 15px;
414
- flex-wrap: wrap;
415
- }
416
-
417
- .refresh-btn {
418
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
419
- color: white;
420
- border: none;
421
- padding: 18px 50px;
422
- font-size: 1.2em;
423
- font-weight: 700;
424
- border-radius: 50px;
425
- cursor: pointer;
426
- box-shadow: 0 8px 20px rgba(102, 126, 234, 0.4);
427
- transition: all 0.3s;
428
- }
429
-
430
- .refresh-btn:hover {
431
- transform: scale(1.08);
432
- box-shadow: 0 12px 30px rgba(102, 126, 234, 0.6);
433
- }
434
-
435
- .news-link {
436
- display: inline-block;
437
- background: #667eea;
438
- color: white;
439
- padding: 10px 20px;
440
- border-radius: 8px;
441
- text-decoration: none;
442
- font-size: 0.95em;
443
- font-weight: 600;
444
- transition: all 0.3s;
445
- margin-top: 15px;
446
- }
447
-
448
- .news-link:hover {
449
- background: #764ba2;
450
- transform: scale(1.05);
451
- }
452
-
453
- .loading {
454
- text-align: center;
455
- padding: 60px;
456
- font-size: 1.8em;
457
- color: #667eea;
458
- font-weight: 600;
459
- }
460
-
461
- .timestamp {
462
- text-align: center;
463
- color: #999;
464
- margin-top: 40px;
465
- font-size: 1em;
466
- padding: 20px;
467
- background: #f8f9fa;
468
- border-radius: 10px;
469
- }
470
-
471
- .footer {
472
- text-align: center;
473
- margin-top: 50px;
474
- padding-top: 30px;
475
- border-top: 2px solid #e0e0e0;
476
- color: #666;
477
- }
478
-
479
- @keyframes fadeIn {
480
- from {
481
- opacity: 0;
482
- transform: translateY(20px);
483
- }
484
- to {
485
- opacity: 1;
486
- transform: translateY(0);
487
- }
488
- }
489
-
490
- @media (max-width: 768px) {
491
- .container {
492
- padding: 20px;
493
- }
494
-
495
- h1 {
496
- font-size: 2em;
497
- }
498
-
499
- .tabs {
500
- flex-direction: column;
501
- }
502
-
503
- .tab {
504
- width: 100%;
505
- }
506
-
507
- .model-grid {
508
- grid-template-columns: 1fr;
509
- }
510
-
511
- .button-group {
512
- flex-direction: column;
513
- }
514
-
515
- .refresh-btn {
516
- width: 100%;
517
- }
518
- }
519
- </style>
520
- </head>
521
- <body>
522
- <div class="container">
523
- <h1>๐Ÿค– AI ๋‰ด์Šค & ํ—ˆ๊น…ํŽ˜์ด์Šค LLM ๋ถ„์„</h1>
524
- <p class="subtitle">AI ํŠธ๋ Œ๋“œ ๋ถ„์„ ์‹œ์Šคํ…œ ๐ŸŽ“</p>
525
-
526
- <!-- ํ†ต๊ณ„ ์นด๋“œ -->
527
- <div class="stats">
528
- <div class="stat-card">
529
- <div class="stat-number">{{ stats.total_news }}</div>
530
- <div class="stat-label">๐Ÿ“ฐ ๋ถ„์„๋œ ๋‰ด์Šค</div>
531
- </div>
532
- <div class="stat-card">
533
- <div class="stat-number">{{ stats.hf_models }}</div>
534
- <div class="stat-label">๐Ÿค— ํŠธ๋ Œ๋”ฉ ๋ชจ๋ธ</div>
535
- </div>
536
- <div class="stat-card">
537
- <div class="stat-number">{{ stats.hf_spaces }}</div>
538
- <div class="stat-label">๐Ÿš€ ์ธ๊ธฐ ์ŠคํŽ˜์ด์Šค</div>
539
- </div>
540
- <div class="stat-card">
541
- <div class="stat-number">{{ stats.llm_analyses }}</div>
542
- <div class="stat-label">๐Ÿง  LLM ๋ถ„์„</div>
543
- </div>
544
- </div>
545
-
546
- <!-- ํƒญ ๋ฉ”๋‰ด -->
547
- <div class="tabs">
548
- <button class="tab active" onclick="switchTab('news')">๐Ÿ“ฐ AI ๋‰ด์Šค ๋ถ„์„</button>
549
- <button class="tab" onclick="switchTab('models')">๐Ÿค— ํŠธ๋ Œ๋”ฉ ๋ชจ๋ธ</button>
550
- <button class="tab" onclick="switchTab('spaces')">๐Ÿš€ ์ธ๊ธฐ ์ŠคํŽ˜์ด์Šค</button>
551
- </div>
552
-
553
- <!-- ๋‰ด์Šค ํƒญ -->
554
- <div id="news-content" class="tab-content active">
555
- {% for article in analyzed_news %}
556
- <div class="news-card">
557
- <div class="news-header">
558
- <div class="news-title">{{ loop.index }}. {{ article.title }}</div>
559
- <div class="news-meta">
560
- <span>๐Ÿ“… {{ article.date }}</span>
561
- <span>๐Ÿ“ฐ {{ article.source }}</span>
562
- </div>
563
- </div>
564
-
565
- <div class="analysis-section">
566
- <div class="analysis-item">
567
- <span class="analysis-label">๐ŸŽฏ ์‰ฌ์šด ์š”์•ฝ</span>
568
- <div class="analysis-content">{{ article.analysis.summary }}</div>
569
- </div>
570
-
571
- <div class="analysis-item">
572
- <span class="analysis-label">๐Ÿ’ก ์™œ ์ค‘์š”ํ• ๊นŒ?</span>
573
- <div class="analysis-content">{{ article.analysis.significance }}</div>
574
- </div>
575
-
576
- <div class="analysis-item">
577
- <span class="analysis-label">๐Ÿ“Š ์˜ํ–ฅ๋„</span>
578
- <span class="impact-level impact-{{ article.analysis.impact_level }}">
579
- {{ article.analysis.impact_text }}
580
- </span>
581
- <div class="analysis-content" style="margin-top: 10px;">
582
- {{ article.analysis.impact_description }}
583
- </div>
584
- </div>
585
-
586
- <div class="analysis-item">
587
- <span class="analysis-label">โœ… ์šฐ๋ฆฌ๊ฐ€ ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ</span>
588
- <div class="analysis-content">{{ article.analysis.action }}</div>
589
- </div>
590
- </div>
591
-
592
- <a href="{{ article.url }}" target="_blank" class="news-link">
593
- ๐Ÿ”— ์ „์ฒด ๊ธฐ์‚ฌ ์ฝ์–ด๋ณด๊ธฐ
594
- </a>
595
- </div>
596
- {% endfor %}
597
- </div>
598
-
599
- <!-- ๋ชจ๋ธ ํƒญ -->
600
- <div id="models-content" class="tab-content">
601
- <div class="model-grid">
602
- {% for model in analyzed_models %}
603
- <div class="model-card">
604
- <div class="model-rank">{{ model.rank }}</div>
605
- <div class="model-name">{{ model.name }}</div>
606
- <div class="model-task">๐Ÿท๏ธ {{ model.task }}</div>
607
-
608
- <div class="model-stats">
609
- <div class="model-stat-item">
610
- <strong>๐Ÿ“ฅ ๋‹ค์šด๋กœ๋“œ</strong><br>
611
- {{ "{:,}".format(model.downloads) }}
612
- </div>
613
- <div class="model-stat-item">
614
- <strong>โค๏ธ ์ข‹์•„์š”</strong><br>
615
- {{ "{:,}".format(model.likes) }}
616
- </div>
617
- </div>
618
-
619
- <div class="model-analysis">
620
- <strong>๐Ÿง  AI ๋ถ„์„:</strong><br>
621
- {{ model.analysis }}
622
- </div>
623
-
624
- <a href="{{ model.url }}" target="_blank" class="news-link">
625
- ๐Ÿ”— ๋ชจ๋ธ ํŽ˜์ด์ง€ ๋ฐฉ๋ฌธ
626
- </a>
627
- </div>
628
- {% endfor %}
629
- </div>
630
-
631
- {% if analyzed_models|length == 0 %}
632
- <div class="loading">
633
- โš ๏ธ ๋ชจ๋ธ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘...<br>
634
- <button onclick="location.href='/?refresh=true'" style="margin-top: 20px; padding: 15px 30px; font-size: 1.1em; cursor: pointer; background: #667eea; color: white; border: none; border-radius: 25px;">
635
- ๐Ÿ”ฅ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘ํ•˜๊ธฐ
636
- </button>
637
- </div>
638
- {% endif %}
639
- </div>
640
-
641
- <!-- ์ŠคํŽ˜์ด์Šค ํƒญ -->
642
- <div id="spaces-content" class="tab-content">
643
- {% for space in analyzed_spaces %}
644
- <div class="space-card">
645
- <div class="space-header">
646
- <div class="space-name">{{ space.rank }}. {{ space.name }}</div>
647
- <span class="space-badge">ํŠธ๋ Œ๋”ฉ {{ space.rank }}์œ„</span>
648
- </div>
649
-
650
- <div class="space-description">
651
- <strong>๐Ÿ“ ์„ค๋ช…:</strong> {{ space.description }}
652
- </div>
653
-
654
- <div class="space-analysis">
655
- <strong>๐ŸŽ“ ์‰ฌ์šด ์„ค๋ช…:</strong><br>
656
- {{ space.simple_explanation }}
657
- </div>
658
-
659
- {% if space.tech_stack %}
660
- <div class="space-tech">
661
- <strong style="width: 100%; margin-bottom: 5px;">๐Ÿ› ๏ธ ์‚ฌ์šฉ ๊ธฐ์ˆ :</strong>
662
- {% for tech in space.tech_stack %}
663
- <span class="tech-tag">{{ tech }}</span>
664
- {% endfor %}
665
- </div>
666
- {% endif %}
667
-
668
- <a href="{{ space.url }}" target="_blank" class="news-link">
669
- ๐Ÿ”— ์ŠคํŽ˜์ด์Šค ์ฒดํ—˜ํ•˜๊ธฐ
670
- </a>
671
- </div>
672
- {% endfor %}
673
-
674
- {% if analyzed_spaces|length == 0 %}
675
- <div class="loading">
676
- โš ๏ธ ์ŠคํŽ˜์ด์Šค ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘...<br>
677
- <button onclick="location.href='/?refresh=true'" style="margin-top: 20px; padding: 15px 30px; font-size: 1.1em; cursor: pointer; background: #ff6b6b; color: white; border: none; border-radius: 25px;">
678
- ๐Ÿ”ฅ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘ํ•˜๊ธฐ
679
- </button>
680
- </div>
681
- {% endif %}
682
- </div>
683
-
684
- <!-- ๋ฒ„ํŠผ ๊ทธ๋ฃน -->
685
- <div class="button-group">
686
- <button class="refresh-btn" onclick="location.reload()">
687
- ๐Ÿ”„ ํŽ˜์ด์ง€ ์ƒˆ๋กœ๊ณ ์นจ
688
- </button>
689
- <button class="refresh-btn" onclick="location.href='/?refresh=true'" style="background: linear-gradient(135deg, #ff6b6b 0%, #ee5a6f 100%);">
690
- ๐Ÿ”ฅ ๋ฐ์ดํ„ฐ ๊ฐ•์ œ ๊ฐฑ์‹ 
691
- </button>
692
- </div>
693
-
694
- <!-- ํƒ€์ž„์Šคํƒฌํ”„ -->
695
- <div class="timestamp">
696
- โฐ ๋งˆ์ง€๋ง‰ ์—…๋ฐ์ดํŠธ: {{ timestamp }}
697
- </div>
698
-
699
- <!-- ํ‘ธํ„ฐ -->
700
- <div class="footer">
701
- <p>๐Ÿค– AI ๋‰ด์Šค LLM ๋ถ„์„ ์‹œ์Šคํ…œ v3.2</p>
702
- <p style="margin-top: 10px; font-size: 0.9em;">
703
- ๐Ÿ’พ SQLite DB ์˜๊ตฌ ์ €์žฅ | ๐ŸŒ AI Times ์‹ค์‹œ๊ฐ„ ํฌ๋กค๋ง | ๐Ÿค— Hugging Face Trending API | ๐Ÿง  Powered by Fireworks AI (Qwen3-235B)
704
- </p>
705
- <p style="margin-top: 10px; font-size: 0.85em; color: #999;">
706
- ๋ฐ์ดํ„ฐ ์ถœ์ฒ˜: AI Times (์‹ค์‹œ๊ฐ„ ํฌ๋กค๋ง), Hugging Face | ์‹ค์‹œ๊ฐ„ ๋ถ„์„: Fireworks AI
707
- </p>
708
- </div>
709
- </div>
710
-
711
- <script>
712
- function switchTab(tabName) {
713
- // ๋ชจ๋“  ํƒญ ๋น„ํ™œ์„ฑํ™”
714
- document.querySelectorAll('.tab').forEach(tab => {
715
- tab.classList.remove('active');
716
- });
717
- document.querySelectorAll('.tab-content').forEach(content => {
718
- content.classList.remove('active');
719
- });
720
-
721
- // ์„ ํƒ๋œ ํƒญ ํ™œ์„ฑํ™”
722
- event.target.classList.add('active');
723
- document.getElementById(tabName + '-content').classList.add('active');
724
- }
725
-
726
- console.log('โœ… AI ๋‰ด์Šค LLM ๋ถ„์„ ์‹œ์Šคํ…œ ๋กœ๋“œ ์™„๋ฃŒ');
727
- </script>
728
- </body>
729
- </html>
730
- """
731
-
732
-
733
- # ============================================
734
- # ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ดˆ๊ธฐํ™”
735
- # ============================================
736
-
737
- def init_database():
738
- """SQLite ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ดˆ๊ธฐํ™”"""
739
- conn = sqlite3.connect(DB_PATH)
740
- cursor = conn.cursor()
741
-
742
- # ๋‰ด์Šค ํ…Œ์ด๋ธ”
743
- cursor.execute('''
744
- CREATE TABLE IF NOT EXISTS news (
745
- id INTEGER PRIMARY KEY AUTOINCREMENT,
746
- title TEXT NOT NULL,
747
- url TEXT NOT NULL UNIQUE,
748
- date TEXT,
749
- source TEXT,
750
- category TEXT,
751
- summary TEXT,
752
- significance TEXT,
753
- impact_level TEXT,
754
- impact_text TEXT,
755
- impact_description TEXT,
756
- action TEXT,
757
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
758
- )
759
- ''')
760
-
761
- # ๋ชจ๋ธ ํ…Œ์ด๋ธ”
762
- cursor.execute('''
763
- CREATE TABLE IF NOT EXISTS models (
764
- id INTEGER PRIMARY KEY AUTOINCREMENT,
765
- name TEXT NOT NULL UNIQUE,
766
- downloads INTEGER,
767
- likes INTEGER,
768
- task TEXT,
769
- url TEXT,
770
- analysis TEXT,
771
- rank INTEGER,
772
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
773
- updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
774
- )
775
- ''')
776
-
777
- # ์ŠคํŽ˜์ด์Šค ํ…Œ์ด๋ธ”
778
- cursor.execute('''
779
- CREATE TABLE IF NOT EXISTS spaces (
780
- id INTEGER PRIMARY KEY AUTOINCREMENT,
781
- space_id TEXT NOT NULL UNIQUE,
782
- name TEXT NOT NULL,
783
- author TEXT,
784
- title TEXT,
785
- likes INTEGER,
786
- url TEXT,
787
- sdk TEXT,
788
- simple_explanation TEXT,
789
- tech_stack TEXT,
790
- rank INTEGER,
791
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
792
- updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
793
- )
794
- ''')
795
-
796
- conn.commit()
797
- conn.close()
798
- print("โœ… ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ดˆ๊ธฐํ™” ์™„๋ฃŒ")
799
-
800
-
801
- def save_news_to_db(news_list: List[Dict]):
802
- """๋‰ด์Šค ๋ฐ์ดํ„ฐ๋ฅผ DB์— ์ €์žฅ"""
803
- conn = sqlite3.connect(DB_PATH)
804
- cursor = conn.cursor()
805
-
806
- saved_count = 0
807
- for news in news_list:
808
- try:
809
- cursor.execute('''
810
- INSERT OR REPLACE INTO news
811
- (title, url, date, source, category, summary, significance,
812
- impact_level, impact_text, impact_description, action)
813
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
814
- ''', (
815
- news['title'],
816
- news['url'],
817
- news.get('date', ''),
818
- news.get('source', ''),
819
- news.get('category', ''),
820
- news['analysis']['summary'],
821
- news['analysis']['significance'],
822
- news['analysis']['impact_level'],
823
- news['analysis']['impact_text'],
824
- news['analysis']['impact_description'],
825
- news['analysis']['action']
826
- ))
827
- saved_count += 1
828
- except sqlite3.IntegrityError:
829
- pass # ์ด๋ฏธ ์กด์žฌํ•˜๋Š” ๋‰ด์Šค
830
-
831
- conn.commit()
832
- conn.close()
833
- print(f"โœ… {saved_count}๊ฐœ ๋‰ด์Šค DB ์ €์žฅ ์™„๋ฃŒ")
834
-
835
-
836
- def save_models_to_db(models_list: List[Dict]):
837
- """๋ชจ๋ธ ๋ฐ์ดํ„ฐ๋ฅผ DB์— ์ €์žฅ"""
838
- conn = sqlite3.connect(DB_PATH)
839
- cursor = conn.cursor()
840
-
841
- saved_count = 0
842
- for model in models_list:
843
- try:
844
- cursor.execute('''
845
- INSERT OR REPLACE INTO models
846
- (name, downloads, likes, task, url, analysis, rank, updated_at)
847
- VALUES (?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
848
- ''', (
849
- model['name'],
850
- model['downloads'],
851
- model['likes'],
852
- model['task'],
853
- model['url'],
854
- model['analysis'],
855
- model['rank']
856
- ))
857
- saved_count += 1
858
- except Exception as e:
859
- print(f"โš ๏ธ ๋ชจ๋ธ ์ €์žฅ ์˜ค๋ฅ˜: {e}")
860
-
861
- conn.commit()
862
- conn.close()
863
- print(f"โœ… {saved_count}๊ฐœ ๋ชจ๋ธ DB ์ €์žฅ ์™„๋ฃŒ")
864
-
865
-
866
- def save_spaces_to_db(spaces_list: List[Dict]):
867
- """์ŠคํŽ˜์ด์Šค ๋ฐ์ดํ„ฐ๋ฅผ DB์— ์ €์žฅ"""
868
- conn = sqlite3.connect(DB_PATH)
869
- cursor = conn.cursor()
870
-
871
- saved_count = 0
872
- for space in spaces_list:
873
- try:
874
- cursor.execute('''
875
- INSERT OR REPLACE INTO spaces
876
- (space_id, name, author, title, likes, url, sdk,
877
- simple_explanation, tech_stack, rank, updated_at)
878
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
879
- ''', (
880
- space['space_id'],
881
- space['name'],
882
- space.get('author', ''),
883
- space.get('title', ''),
884
- space.get('likes', 0),
885
- space['url'],
886
- space.get('sdk', ''),
887
- space['simple_explanation'],
888
- json.dumps(space.get('tech_stack', [])),
889
- space['rank']
890
- ))
891
- saved_count += 1
892
- except Exception as e:
893
- print(f"โš ๏ธ ์ŠคํŽ˜์ด์Šค ์ €์žฅ ์˜ค๋ฅ˜: {e}")
894
-
895
- conn.commit()
896
- conn.close()
897
- print(f"โœ… {saved_count}๊ฐœ ์ŠคํŽ˜์ด์Šค DB ์ €์žฅ ์™„๋ฃŒ")
898
-
899
-
900
- def load_news_from_db() -> List[Dict]:
901
- """DB์—์„œ ๋‰ด์Šค ๋กœ๋“œ"""
902
- conn = sqlite3.connect(DB_PATH)
903
- cursor = conn.cursor()
904
-
905
- cursor.execute('''
906
- SELECT title, url, date, source, category, summary, significance,
907
- impact_level, impact_text, impact_description, action
908
- FROM news ORDER BY created_at DESC LIMIT 50
909
- ''')
910
-
911
- news_list = []
912
- for row in cursor.fetchall():
913
- news_list.append({
914
- 'title': row[0],
915
- 'url': row[1],
916
- 'date': row[2],
917
- 'source': row[3],
918
- 'category': row[4],
919
- 'analysis': {
920
- 'summary': row[5],
921
- 'significance': row[6],
922
- 'impact_level': row[7],
923
- 'impact_text': row[8],
924
- 'impact_description': row[9],
925
- 'action': row[10]
926
- }
927
- })
928
-
929
- conn.close()
930
- return news_list
931
-
932
-
933
- def load_models_from_db() -> List[Dict]:
934
- """DB์—์„œ ๋ชจ๋ธ ๋กœ๋“œ"""
935
- conn = sqlite3.connect(DB_PATH)
936
- cursor = conn.cursor()
937
-
938
- cursor.execute('''
939
- SELECT name, downloads, likes, task, url, analysis, rank
940
- FROM models ORDER BY rank ASC LIMIT 30
941
- ''')
942
-
943
- models_list = []
944
- for row in cursor.fetchall():
945
- models_list.append({
946
- 'name': row[0],
947
- 'downloads': row[1],
948
- 'likes': row[2],
949
- 'task': row[3],
950
- 'url': row[4],
951
- 'analysis': row[5],
952
- 'rank': row[6]
953
- })
954
-
955
- conn.close()
956
- return models_list
957
-
958
-
959
- def load_spaces_from_db() -> List[Dict]:
960
- """DB์—์„œ ์ŠคํŽ˜์ด์Šค ๋กœ๋“œ"""
961
- conn = sqlite3.connect(DB_PATH)
962
- cursor = conn.cursor()
963
-
964
- cursor.execute('''
965
- SELECT space_id, name, author, title, likes, url, sdk,
966
- simple_explanation, tech_stack, rank
967
- FROM spaces ORDER BY rank ASC LIMIT 30
968
- ''')
969
-
970
- spaces_list = []
971
- for row in cursor.fetchall():
972
- spaces_list.append({
973
- 'space_id': row[0],
974
- 'name': row[1],
975
- 'author': row[2],
976
- 'title': row[3],
977
- 'likes': row[4],
978
- 'url': row[5],
979
- 'sdk': row[6],
980
- 'simple_explanation': row[7],
981
- 'tech_stack': json.loads(row[8]) if row[8] else [],
982
- 'rank': row[9],
983
- 'description': row[3] # title์„ description์œผ๋กœ ์‚ฌ์šฉ
984
- })
985
-
986
- conn.close()
987
- return spaces_list
988
-
989
-
990
- # ============================================
991
- # LLM ๋ถ„์„๊ธฐ ํด๋ž˜์Šค
992
- # ============================================
993
-
994
- class LLMAnalyzer:
995
- """Fireworks AI (Qwen3) ๊ธฐ๋ฐ˜ LLM ๋ถ„์„๊ธฐ"""
996
-
997
- def __init__(self):
998
- self.api_key = os.environ.get('FIREWORKS_API_KEY', '')
999
- self.api_url = "https://api.fireworks.ai/inference/v1/chat/completions"
1000
- self.api_available = bool(self.api_key)
1001
-
1002
- if not self.api_available:
1003
- print("โš ๏ธ FIREWORKS_API_KEY ํ™˜๊ฒฝ๋ณ€์ˆ˜๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ํ…œํ”Œ๋ฆฟ ๋ชจ๋“œ๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.")
1004
-
1005
- def call_llm(self, messages: List[Dict], max_tokens: int = 2000) -> str:
1006
- """Fireworks AI API ํ˜ธ์ถœ"""
1007
- if not self.api_available:
1008
- return None
1009
-
1010
- try:
1011
- payload = {
1012
- "model": "accounts/fireworks/models/qwen3-235b-a22b-instruct-2507",
1013
- "max_tokens": max_tokens,
1014
- "top_p": 1,
1015
- "top_k": 40,
1016
- "presence_penalty": 0,
1017
- "frequency_penalty": 0,
1018
- "temperature": 0.6,
1019
- "messages": messages
1020
- }
1021
-
1022
- headers = {
1023
- "Accept": "application/json",
1024
- "Content-Type": "application/json",
1025
- "Authorization": f"Bearer {self.api_key}"
1026
- }
1027
-
1028
- response = requests.post(self.api_url, headers=headers, json=payload, timeout=30)
1029
- response.raise_for_status()
1030
-
1031
- result = response.json()
1032
- return result['choices'][0]['message']['content']
1033
-
1034
- except Exception as e:
1035
- print(f" โš ๏ธ LLM API ํ˜ธ์ถœ ์˜ค๋ฅ˜: {e}")
1036
- return None
1037
-
1038
- def fetch_model_card(self, model_id: str) -> str:
1039
- """ํ—ˆ๊น…ํŽ˜์ด์Šค ๋ชจ๋ธ ์นด๋“œ(README.md) ๊ฐ€์ ธ์˜ค๊ธฐ"""
1040
- try:
1041
- url = f"https://huggingface.co/{model_id}/raw/main/README.md"
1042
- response = requests.get(url, timeout=10)
1043
-
1044
- if response.status_code == 200:
1045
- content = response.text
1046
- # ๋„ˆ๋ฌด ๊ธด ๊ฒฝ์šฐ ์•ž๋ถ€๋ถ„๋งŒ (์•ฝ 3000์ž)
1047
- if len(content) > 3000:
1048
- content = content[:3000] + "\n...(ํ›„๋žต)"
1049
- return content
1050
- else:
1051
- return None
1052
- except Exception as e:
1053
- print(f" โš ๏ธ ๋ชจ๋ธ ์นด๋“œ ๊ฐ€์ ธ์˜ค๊ธฐ ์˜ค๋ฅ˜: {e}")
1054
- return None
1055
-
1056
- def fetch_space_code(self, space_id: str) -> str:
1057
- """ํ—ˆ๊น…ํŽ˜์ด์Šค ์ŠคํŽ˜์ด์Šค app.py ๊ฐ€์ ธ์˜ค๊ธฐ"""
1058
- try:
1059
- url = f"https://huggingface.co/spaces/{space_id}/raw/main/app.py"
1060
- response = requests.get(url, timeout=10)
1061
-
1062
- if response.status_code == 200:
1063
- content = response.text
1064
- # ๋„ˆ๋ฌด ๊ธด ๊ฒฝ์šฐ ์•ž๋ถ€๋ถ„๋งŒ (์•ฝ 2000์ž)
1065
- if len(content) > 2000:
1066
- content = content[:2000] + "\n...(ํ›„๋žต)"
1067
- return content
1068
- else:
1069
- return None
1070
- except Exception as e:
1071
- print(f" โš ๏ธ ์ŠคํŽ˜์ด์Šค ์ฝ”๋“œ ๊ฐ€์ ธ์˜ค๊ธฐ ์˜ค๋ฅ˜: {e}")
1072
- return None
1073
-
1074
- def analyze_news_simple(self, title: str, content: str = "") -> Dict:
1075
- """๋‰ด์Šค ๊ธฐ์‚ฌ๋ฅผ ์ค‘๊ณ ๋“ฑํ•™์ƒ ์ˆ˜์ค€์œผ๋กœ ๋ถ„์„ - LLM API ์‚ฌ์šฉ"""
1076
-
1077
- # LLM API๊ฐ€ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๊ฒฝ์šฐ ์‹ค์ œ ๋ถ„์„
1078
- if self.api_available:
1079
- try:
1080
- messages = [
1081
- {
1082
- "role": "system",
1083
- "content": """๋‹น์‹ ์€ ์ค‘๊ณ ๋“ฑํ•™์ƒ๋„ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๊ฒŒ AI ๋‰ด์Šค๋ฅผ ์‰ฝ๊ฒŒ ์„ค๋ช…ํ•˜๋Š” ์ „๋ฌธ๊ฐ€์ž…๋‹ˆ๋‹ค.
1084
- ํ•œ๊ตญ์–ด๋กœ ๋‹ต๋ณ€ํ•˜๋ฉฐ, ๋‹ค์Œ ํ˜•์‹์˜ JSON์œผ๋กœ๋งŒ ์‘๋‹ตํ•˜์„ธ์š”:
1085
-
1086
- {
1087
- "summary": "๋‰ด์Šค ๋‚ด์šฉ์„ 8-10๋ฌธ์žฅ์œผ๋กœ ์ž์„ธํ•˜๊ณ  ๊ตฌ์ฒด์ ์œผ๋กœ ์„ค๋ช… (์ˆ˜์น˜, ์‚ฌ์‹ค, ๋ฐฐ๊ฒฝ ํฌํ•จ)",
1088
- "significance": "์ด ๋‰ด์Šค๊ฐ€ ์™œ ์ค‘์š”ํ•œ์ง€ 2-3๋ฌธ์žฅ์œผ๋กœ ์„ค๋ช…",
1089
- "impact_level": "high ๋˜๋Š” medium ๋˜๋Š” low",
1090
- "impact_text": "๋†’์Œ ๋˜๋Š” ์ค‘๊ฐ„ ๋˜๋Š” ๋‚ฎ์Œ",
1091
- "impact_description": "์–ด๋–ค ์˜ํ–ฅ์ด ์žˆ์„์ง€ 2-3๋ฌธ์žฅ์œผ๋กœ ์„ค๋ช…",
1092
- "action": "์ค‘๊ณ ๋“ฑํ•™์ƒ์ด ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์„ 2-3๋ฌธ์žฅ์œผ๋กœ ์ œ์•ˆ"
1093
- }
1094
-
1095
- ๋ฐ˜๋“œ์‹œ ์œ„ ํ˜•์‹์˜ JSON๋งŒ ์ถœ๋ ฅํ•˜์„ธ์š”."""
1096
- },
1097
- {
1098
- "role": "user",
1099
- "content": f"""๋‹ค์Œ AI ๋‰ด์Šค๋ฅผ ์ค‘๊ณ ๋“ฑํ•™์ƒ์ด ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ถ„์„ํ•ด์ฃผ์„ธ์š”:
1100
-
1101
- ์ œ๋ชฉ: {title}
1102
-
1103
- ๊ฐ ํ•ญ๋ชฉ์„ ๊ตฌ์ฒด์ ์ด๊ณ  ์ž์„ธํ•˜๊ฒŒ ์ž‘์„ฑํ•˜๋˜, ์ค‘๊ณ ๋“ฑํ•™์ƒ ์ˆ˜์ค€์— ๋งž์ถฐ ์‰ฝ๊ฒŒ ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”.
1104
- ํŠนํžˆ summary๋Š” 8-10๋ฌธ์žฅ์œผ๋กœ ์ถฉ๋ถ„ํžˆ ์ƒ์„ธํ•˜๊ฒŒ ์ž‘์„ฑํ•˜์„ธ์š”."""
1105
- }
1106
- ]
1107
-
1108
- result = self.call_llm(messages, max_tokens=1500)
1109
-
1110
- if result:
1111
- # JSON ํŒŒ์‹ฑ ์‹œ๋„
1112
- try:
1113
- # ๋งˆํฌ๋‹ค์šด ์ฝ”๋“œ ๋ธ”๋ก ์ œ๊ฑฐ
1114
- result_clean = result.replace('```json', '').replace('```', '').strip()
1115
- analysis = json.loads(result_clean)
1116
-
1117
- # ํ•„์ˆ˜ ํ•„๋“œ ๊ฒ€์ฆ
1118
- required_fields = ['summary', 'significance', 'impact_level', 'impact_text', 'impact_description', 'action']
1119
- if all(field in analysis for field in required_fields):
1120
- print(f" โœ… LLM ๋ถ„์„ ์„ฑ๊ณต")
1121
- return analysis
1122
- else:
1123
- print(f" โš ๏ธ LLM ์‘๋‹ต์— ํ•„์ˆ˜ ํ•„๋“œ ๋ˆ„๋ฝ")
1124
- except json.JSONDecodeError as e:
1125
- print(f" โš ๏ธ JSON ํŒŒ์‹ฑ ์˜ค๋ฅ˜: {e}")
1126
- print(f" ์›๋ณธ ์‘๋‹ต: {result[:200]}...")
1127
-
1128
- except Exception as e:
1129
- print(f" โš ๏ธ LLM API ํ˜ธ์ถœ ์˜ค๋ฅ˜: {e}")
1130
-
1131
- # Fallback: ํ…œํ”Œ๋ฆฟ ๊ธฐ๋ฐ˜ ๋ถ„์„ (LLM ์‹คํŒจ ์‹œ์—๋งŒ)
1132
- print(f" โ„น๏ธ ํ…œํ”Œ๋ฆฟ ๋ชจ๋“œ๋กœ ์ „ํ™˜")
1133
-
1134
- analysis_templates = {
1135
- "์ฑ—GPT": {
1136
- "summary": """๋งˆ์ดํฌ๋กœ์†Œํ”„ํŠธ(MS)๋Š” ์ฑ—GPT์˜ ํญ๋ฐœ์ ์ธ ์‚ฌ์šฉ๋Ÿ‰ ์ฆ๊ฐ€๋กœ ์ธํ•ด ๋ฐ์ดํ„ฐ์„ผํ„ฐ ์šฉ๋Ÿ‰์ด ๋ถ€์กฑํ•œ ์‹ฌ๊ฐํ•œ ์ƒํ™ฉ์— ์ง๋ฉดํ–ˆ์Šต๋‹ˆ๋‹ค.
1137
- ํ˜„์žฌ ๋ฏธ๊ตญ ๋‚ด ์—ฌ๋Ÿฌ ํ•ต์‹ฌ ์ง€์—ญ์—์„œ ๋ฌผ๋ฆฌ์  ๊ณต๊ฐ„๊ณผ ์„œ๋ฒ„ ์šฉ๋Ÿ‰์ด ๋ชจ๋‘ ํ•œ๊ณ„์— ๋„๋‹ฌํ•œ ์ƒํƒœ์ž…๋‹ˆ๋‹ค.
1138
- ํŠนํžˆ ๋ฒ„์ง€๋‹ˆ์•„์™€ ํ…์‚ฌ์Šค ๋“ฑ ์ฃผ์š” ํด๋ผ์šฐ๋“œ ํ—ˆ๋ธŒ ์ง€์—ญ์—์„œ๋Š” 2026๋…„ ์ƒ๋ฐ˜๊ธฐ๊นŒ์ง€ ์‹ ๊ทœ Azure ํด๋ผ์šฐ๋“œ ๊ตฌ๋…์ด ์ œํ•œ๋  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒ๋ฉ๋‹ˆ๋‹ค.
1139
- ์ด๋Š” ์ƒ์„ฑํ˜• AI ์„œ๋น„์Šค์˜ ๊ธ‰๊ฒฉํ•œ ์„ฑ์žฅ ์†๋„๊ฐ€ ๊ธฐ์—…๋“ค์˜ ์ธํ”„๋ผ ์ค€๋น„ ๋Šฅ๋ ฅ์„ ํฌ๊ฒŒ ์ดˆ๊ณผํ•˜๊ณ  ์žˆ์Œ์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.
1140
- MS๋Š” ๋ฐ์ดํ„ฐ์„ผํ„ฐ ํ™•์žฅ์„ ์œ„ํ•ด ๋ง‰๋Œ€ํ•œ ํˆฌ์ž๋ฅผ ํ•˜๊ณ  ์žˆ์ง€๋งŒ, ์‹ค์ œ ์ธํ”„๋ผ ๊ตฌ์ถ•์—๋Š” ์ตœ์†Œ 2-3๋…„์ด ์†Œ์š”๋ฉ๋‹ˆ๋‹ค.
1141
- ์ด๋Ÿฌํ•œ ๊ณต๊ธ‰ ๋ถ€์กฑ ํ˜„์ƒ์€ AI ์„œ๋น„์Šค ๊ฐ€๊ฒฉ ์ƒ์Šน๊ณผ ์ ‘๊ทผ์„ฑ ์ œํ•œ์œผ๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ์œผ๋ฉฐ,
1142
- ๊ฒฝ์Ÿ์‚ฌ๋“ค๋„ ์œ ์‚ฌํ•œ ๋ฌธ์ œ์— ์ง๋ฉดํ•  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์Šต๋‹ˆ๋‹ค.
1143
- ์ „๋ฌธ๊ฐ€๋“ค์€ ์ด ์ƒํ™ฉ์ด AI ์‚ฐ์—…์˜ ์„ฑ์žฅ ์†๋„๋ฅผ ์ผ์‹œ์ ์œผ๋กœ ๋Šฆ์ถœ ์ˆ˜ ์žˆ๋‹ค๊ณ  ๋ถ„์„ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.""",
1144
- "significance": "์ด ๋‰ด์Šค๋Š” AI ๊ธฐ์ˆ ์˜ ๋Œ€์ค‘ํ™” ์†๋„๊ฐ€ ๊ธฐ์—…๋“ค์˜ ์˜ˆ์ƒ์„ ํ›จ์”ฌ ๋›ฐ์–ด๋„˜๊ณ  ์žˆ์Œ์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. MS ๊ฐ™์€ ๊ธ€๋กœ๋ฒŒ IT ๊ธฐ์—…๋„ AI ์ˆ˜์š”๋ฅผ ๋”ฐ๋ผ์žก๊ธฐ ์œ„ํ•ด ๊ณ ๊ตฐ๋ถ„ํˆฌํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, ์ด๋Š” AI๊ฐ€ ๋‹จ์ˆœํ•œ ์œ ํ–‰์ด ์•„๋‹Œ ์‚ฐ์—… ์ „๋ฐ˜์„ ๋ณ€ํ™”์‹œํ‚ค๋Š” ํ•ต์‹ฌ ๊ธฐ์ˆ ์ž„์„ ์ฆ๋ช…ํ•ฉ๋‹ˆ๋‹ค.",
1145
- "impact_level": "high",
1146
- "impact_text": "๋†’์Œ",
1147
- "impact_description": "ํด๋ผ์šฐ๋“œ ์ธํ”„๋ผ ๋ถ€์กฑ์€ AI ์„œ๋น„์Šค ํ™•์žฅ์— ์ง์ ‘์ ์ธ ์˜ํ–ฅ์„ ๋ฏธ์น˜๋ฉฐ, ํ–ฅํ›„ AI ๊ธฐ์ˆ  ์ ‘๊ทผ์„ฑ๊ณผ ๋น„์šฉ ๊ตฌ์กฐ๋ฅผ ๋ณ€ํ™”์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
1148
- "action": "์ฑ—GPT๋‚˜ Claude ๊ฐ™์€ AI ๋„๊ตฌ๋ฅผ ํ™œ์šฉํ•œ ํ•™์Šต ๋ฐฉ๋ฒ•์„ ์ตํžˆ์„ธ์š”. ๋ณด๊ณ ์„œ ์ž‘์„ฑ, ์ฝ”๋”ฉ ํ•™์Šต, ์™ธ๊ตญ์–ด ๊ณต๋ถ€ ๋“ฑ ๋‹ค์–‘ํ•œ ๋ถ„์•ผ์—์„œ AI๋ฅผ ํ•™์Šต ๋ณด์กฐ ๋„๊ตฌ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค."
1149
- },
1150
- "GPU": {
1151
- "summary": """๋ฏธ๊ตญ ์ •๋ถ€๊ฐ€ ์•„๋ž์—๋ฏธ๋ฆฌํŠธ(UAE)์— ์ตœ์ฒจ๋‹จ AI ์นฉ(GPU) ์ˆ˜์ถœ์„ ๊ณต์‹์ ์œผ๋กœ ์Šน์ธํ–ˆ์Šต๋‹ˆ๋‹ค.
1152
- ์ด๋ฒˆ ์Šน์ธ์€ UAE ๋‚ด์—์„œ ๋ฏธ๊ตญ ๊ธฐ์—…์ด ์ง์ ‘ ์šด์˜ํ•˜๋Š” ๋ฐ์ดํ„ฐ์„ผํ„ฐ์—๋งŒ ํ•œ์ •๋˜๋ฉฐ, ํŠนํžˆ ์˜คํ”ˆAI ์ „์šฉ 5๊ธฐ๊ฐ€์™€ํŠธ(GW) ๊ทœ๋ชจ์˜ ๋Œ€ํ˜• ๋ฐ์ดํ„ฐ์„ผํ„ฐ ๊ตฌ์ถ•์— ์‚ฌ์šฉ๋  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.
1153
- GPU๋Š” AI ๋ชจ๋ธ ํ•™์Šต๊ณผ ์ถ”๋ก ์— ํ•„์ˆ˜์ ์ธ ํ•˜๋“œ์›จ์–ด๋กœ, ์ˆ˜์ฒœ ๊ฐœ์˜ ์—ฐ์‚ฐ์„ ๋™์‹œ์— ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ ๋Šฅ๋ ฅ์ด ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค.
1154
- ํ˜„์žฌ GPU ์‹œ์žฅ์€ ์—”๋น„๋””์•„๊ฐ€ ์•ฝ 80% ์ด์ƒ์„ ์žฅ์•…ํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, ํŠนํžˆ AI ์ „์šฉ H100, H200 ์นฉ์€ ๊ณต๊ธ‰ ๋ถ€์กฑ ํ˜„์ƒ์ด ์‹ฌ๊ฐํ•ฉ๋‹ˆ๋‹ค.
1155
- ์ด๋ฒˆ ๊ฒฐ์ •์œผ๋กœ ์—”๋น„๋””์•„์˜ ์‹œ๊ฐ€์ด์•ก์ด 5์กฐ ๋‹ฌ๋Ÿฌ์— ๊ทผ์ ‘ํ•  ๊ฒƒ์œผ๋กœ ์›”์ŠคํŠธ๋ฆฌํŠธ๋Š” ์ „๋งํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
1156
- ํ•œํŽธ, ์ด๋Š” ๋ฏธ๊ตญ์˜ ์ „๋žต์  ๊ธฐ์ˆ  ์ˆ˜์ถœ ์ •์ฑ… ๋ณ€ํ™”๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ์ค‘์š”ํ•œ ์‚ฌ๋ก€์ž…๋‹ˆ๋‹ค.
1157
- ์ค‘๊ตญ์— ๋Œ€ํ•ด์„œ๋Š” ์—„๊ฒฉํ•œ ์ˆ˜์ถœ ํ†ต์ œ๋ฅผ ์œ ์ง€ํ•˜๋ฉด์„œ๋„, ์ค‘๋™์˜ ์ฃผ์š” ๋™๋งน๊ตญ์—๋Š” ์„ ๋ณ„์ ์œผ๋กœ ํ—ˆ์šฉํ•˜๋Š” '๊ธฐ์ˆ  ๋™๋งน' ์ „๋žต์„ ๊ตฌ์‚ฌํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.""",
1158
- "significance": "์ด๋Š” ๋ฏธ๊ตญ์˜ AI ๊ธฐ์ˆ  ์ˆ˜์ถœ ์ •์ฑ… ๋ณ€ํ™”๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ์ค‘์š”ํ•œ ์‹ ํ˜ธ์ž…๋‹ˆ๋‹ค. ๊ธฐ์ˆ  ํŒจ๊ถŒ ๊ฒฝ์Ÿ ์†์—์„œ๋„ ์ „๋žต์  ๋™๋งน๊ตญ๊ณผ์˜ ํ˜‘๋ ฅ์„ ํ†ตํ•ด AI ์ƒํƒœ๊ณ„๋ฅผ ํ™•์žฅํ•˜๋ ค๋Š” ๋ฏธ๊ตญ์˜ ์˜๋„๋ฅผ ์—ฟ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
1159
- "impact_level": "medium",
1160
- "impact_text": "์ค‘๊ฐ„",
1161
- "impact_description": "AI ํ•˜๋“œ์›จ์–ด ๊ณต๊ธ‰๋ง์˜ ์ง€์ •ํ•™์  ๋ณ€ํ™”๋Š” ๊ธ€๋กœ๋ฒŒ AI ์‚ฐ์—… ์ง€ํ˜•๋„์— ์˜ํ–ฅ์„ ๋ฏธ์น  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ํŠนํžˆ ๋ฐ˜๋„์ฒด ์‚ฐ์—…๊ณผ ๊ตญ์ œ ๊ด€๊ณ„์— ์ค‘์š”ํ•œ ์˜๋ฏธ๋ฅผ ๊ฐ€์ง‘๋‹ˆ๋‹ค.",
1162
- "action": "์ปดํ“จํ„ฐ ํ•˜๋“œ์›จ์–ด, ํŠนํžˆ GPU์˜ ์ž‘๋™ ์›๋ฆฌ์™€ AI ํ•™์Šต์—์„œ์˜ ์—ญํ• ์„ ๊ณต๋ถ€ํ•ด๋ณด์„ธ์š”. ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ, ํ–‰๋ ฌ ์—ฐ์‚ฐ ๋“ฑ์˜ ๊ฐœ๋…์„ ์ดํ•ดํ•˜๋ฉด AI ๊ธฐ์ˆ ์˜ ๊ทผ๊ฐ„์„ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค."
1163
- },
1164
- "์†Œ๋ผ": {
1165
- "summary": """์˜คํ”ˆAI์˜ ํ˜์‹ ์ ์ธ AI ๋™์˜์ƒ ์ƒ์„ฑ ์•ฑ '์†Œ๋ผ(Sora)'๊ฐ€ ์ถœ์‹œ ๋‹จ 5์ผ ๋งŒ์— 100๋งŒ ๋‹ค์šด๋กœ๋“œ๋ฅผ ๋ŒํŒŒํ•˜๋Š” ๊ฒฝ์ด์ ์ธ ๊ธฐ๋ก์„ ์„ธ์› ์Šต๋‹ˆ๋‹ค.
1166
- ์ด๋Š” ์ „์„ค์ ์ธ ์ฑ—GPT๋ณด๋‹ค๋„ ๋น ๋ฅธ ์„ฑ์žฅ ์†๋„์ด๋ฉฐ, ์ดˆ๋Œ€ ์ „์šฉ(invite-only) ๋ฐฉ์‹์˜ ์ œํ•œ์  ์ถœ์‹œ์ž„์„ ๊ณ ๋ คํ•˜๋ฉด ๋”์šฑ ๋†€๋ผ์šด ์„ฑ๊ณผ์ž…๋‹ˆ๋‹ค.
1167
- ์†Œ๋ผ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ํ…์ŠคํŠธ ํ”„๋กฌํ”„ํŠธ๋งŒ์œผ๋กœ ์ตœ๋Œ€ 1๋ถ„ ๊ธธ์ด์˜ ๊ณ ํ’ˆ์งˆ ๋™์˜์ƒ์„ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ์ƒ์„ฑํ˜• AI ๋„๊ตฌ์ž…๋‹ˆ๋‹ค.
1168
- ํ˜„์žฌ ๋ฏธ๊ตญ๊ณผ ์บ๋‚˜๋‹ค์—์„œ iOS ์ „์šฉ์œผ๋กœ ๋จผ์ € ์ถœ์‹œ๋˜์—ˆ์œผ๋ฉฐ, ์•ˆ๋“œ๋กœ์ด๋“œ ๋ฒ„์ „๊ณผ ๊ธ€๋กœ๋ฒŒ ํ™•์žฅ์ด ๊ณ„ํš๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.
1169
- ์†Œ๋ผ๋Š” ๊ธฐ์กด์˜ ์ด๋ฏธ์ง€ ์ƒ์„ฑ AI(๋ฏธ๋“œ์ €๋‹ˆ, ์Šคํ…Œ์ด๋ธ” ๋””ํ“จ์ „ ๋“ฑ)๋ฅผ ๋›ฐ์–ด๋„˜์–ด, ์‹œ๊ฐ„์˜ ํ๋ฆ„๊ณผ ๋ฌผ๋ฆฌ ๋ฒ•์น™์„ ์ดํ•ดํ•˜๋Š” ์ˆ˜์ค€์œผ๋กœ ๋ฐœ์ „ํ–ˆ์Šต๋‹ˆ๋‹ค.
1170
- ์˜ˆ๋ฅผ ๋“ค์–ด, ํŒŒ๋„๊ฐ€ ์น˜๋Š” ์žฅ๋ฉด์„ ์ƒ์„ฑํ•  ๋•Œ ๋ฌผ์˜ ์›€์ง์ž„, ๋น›์˜ ๋ฐ˜์‚ฌ, ์†Œ๋ฆฌ๊นŒ์ง€ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
1171
- ์ด๋Š” ์˜ํ™”, ๊ด‘๊ณ , ๊ต์œก ์ฝ˜ํ…์ธ , ๊ฒŒ์ž„ ๋“ฑ ๋ชจ๋“  ์˜์ƒ ์‚ฐ์—…์— ํ˜๋ช…์  ๋ณ€ํ™”๋ฅผ ์˜ˆ๊ณ ํ•ฉ๋‹ˆ๋‹ค.""",
1172
- "significance": "ํ…์ŠคํŠธ๋ฅผ ์ด๋ฏธ์ง€๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ธฐ์ˆ ์—์„œ ๋” ๋‚˜์•„๊ฐ€ ๋™์˜์ƒ ์ƒ์„ฑ๊นŒ์ง€ ๊ฐ€๋Šฅํ•ด์ง„ ๊ฒƒ์€ AI ๊ธฐ์ˆ ์˜ ์ง„ํ™”๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ์ฝ˜ํ…์ธ  ์ œ์ž‘์˜ ๋ฏผ์ฃผํ™”๊ฐ€ ๊ฐ€์†ํ™”๋˜๊ณ  ์žˆ์œผ๋ฉฐ, ๋ˆ„๊ตฌ๋‚˜ ์‰ฝ๊ฒŒ ๊ณ ํ’ˆ์งˆ ์˜์ƒ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋Š” ์‹œ๋Œ€๊ฐ€ ์—ด๋ฆฌ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.",
1173
- "impact_level": "high",
1174
- "impact_text": "๋†’์Œ",
1175
- "impact_description": "์˜์ƒ ์ œ์ž‘ ์‚ฐ์—…์˜ ํŒจ๋Ÿฌ๋‹ค์ž„์ด ๋ณ€ํ™”ํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, ๊ต์œก, ๋งˆ์ผ€ํŒ…, ์—”ํ„ฐํ…Œ์ธ๋จผํŠธ ๋“ฑ ๋‹ค์–‘ํ•œ ๋ถ„์•ผ์—์„œ AI ๋™์˜์ƒ ์ƒ์„ฑ ๊ธฐ์ˆ ์˜ ํ™œ์šฉ์ด ์ฆ๊ฐ€ํ•  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒ๋ฉ๋‹ˆ๋‹ค.",
1176
- "action": "AI ๋™์˜์ƒ ์ƒ์„ฑ ๋„๊ตฌ์˜ ๊ฐ€๋Šฅ์„ฑ๊ณผ ํ•œ๊ณ„๋ฅผ ํƒ๊ตฌํ•ด๋ณด์„ธ์š”. ์ฐฝ์˜์ ์ธ ์•„์ด๋””์–ด๋ฅผ ์‹œ๊ฐํ™”ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ฐฐ์šฐ๊ณ , ๋™์‹œ์— ๋”ฅํŽ˜์ดํฌ ๊ฐ™์€ ์•…์šฉ ์‚ฌ๋ก€์— ๋Œ€ํ•œ ๋น„ํŒ์  ์‚ฌ๊ณ ๋„ ํ•จ์–‘ํ•˜์„ธ์š”."
1177
- }
1178
- }
1179
-
1180
- # ํ‚ค์›Œ๋“œ ๋งค์นญ์œผ๋กœ ํ…œํ”Œ๋ฆฟ ์„ ํƒ
1181
- for keyword, template in analysis_templates.items():
1182
- if keyword.lower() in title.lower():
1183
- return template
1184
-
1185
- # ๊ธฐ๋ณธ ๋ถ„์„ (์ค‘๊ณ ๋“ฑํ•™์ƒ ์ˆ˜์ค€)
1186
- return {
1187
- "summary": f"""'{title}'๋Š” ์ตœ์‹  AI ๊ธฐ์ˆ  ๋™ํ–ฅ์„ ๋‹ค๋ฃจ๋Š” ์ค‘์š”ํ•œ ๋‰ด์Šค์ž…๋‹ˆ๋‹ค.
1188
- ์ธ๊ณต์ง€๋Šฅ ๋ถ„์•ผ๋Š” ๋งค์ผ ์ƒˆ๋กœ์šด ๋ฐœ์ „์„ ์ด๋ฃจ๊ณ  ์žˆ์œผ๋ฉฐ, ์ด๋Ÿฌํ•œ ๊ธฐ์ˆ  ๋ณ€ํ™”๋Š” ์šฐ๋ฆฌ์˜ ์ผ์ƒ์ƒํ™œ, ๊ต์œก, ๊ทธ๋ฆฌ๊ณ  ๋ฏธ๋ž˜ ์ง์—… ์„ธ๊ณ„์— ์ง์ ‘์ ์ธ ์˜ํ–ฅ์„ ๋ฏธ์น  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒ๋ฉ๋‹ˆ๋‹ค.
1189
- ์ตœ๊ทผ AI ๊ธฐ์ˆ ์€ ๋‹จ์ˆœํžˆ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ์ˆ˜์ค€์„ ๋„˜์–ด, ์ฐฝ์˜์ ์ธ ์ฝ˜ํ…์ธ ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๋ณต์žกํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋‹จ๊ณ„๋กœ ์ง„ํ™”ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
1190
- ํŠนํžˆ ๋Œ€๊ทœ๋ชจ ์–ธ์–ด ๋ชจ๋ธ(LLM)๊ณผ ์ƒ์„ฑํ˜• AI์˜ ๋ฐœ์ „์€ ์‚ฐ์—… ์ „๋ฐ˜์— ๊ฑธ์ณ ํ˜์‹ ์„ ๊ฐ€์ ธ์˜ค๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
1191
- ์ด๋Ÿฌํ•œ ๊ธฐ์ˆ  ๋ณ€ํ™”๋Š” ์ƒˆ๋กœ์šด ์ผ์ž๋ฆฌ๋ฅผ ์ฐฝ์ถœํ•˜๋Š” ๋™์‹œ์—, ๊ธฐ์กด ์ง์—…์˜ ์„ฑ๊ฒฉ์„ ๋ณ€ํ™”์‹œํ‚ค๊ณ  ์žˆ์–ด ์šฐ๋ฆฌ ๋ชจ๋‘๊ฐ€ ์ฃผ๋ชฉํ•ด์•ผ ํ•  ํŠธ๋ Œ๋“œ์ž…๋‹ˆ๋‹ค.
1192
- ์ „๋ฌธ๊ฐ€๋“ค์€ ํ–ฅํ›„ 5-10๋…„ ๋‚ด์— AI๊ฐ€ ๊ฑฐ์˜ ๋ชจ๋“  ์‚ฐ์—… ๋ถ„์•ผ์— ํ†ตํ•ฉ๋  ๊ฒƒ์œผ๋กœ ์ „๋งํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
1193
- ๋”ฐ๋ผ์„œ ๊ด€๋ จ ๊ธฐ์ˆ ์˜ ์›๋ฆฌ๋ฅผ ์ดํ•ดํ•˜๊ณ , ์‚ฌํšŒ์  ํŒŒ๊ธ‰ํšจ๊ณผ๋ฅผ ํ•จ๊ป˜ ๊ณ ๋ฏผํ•˜๋Š” ๊ฒƒ์ด ๋ฏธ๋ž˜ ์„ธ๋Œ€์—๊ฒŒ ๋งค์šฐ ์ค‘์š”ํ•œ ์—ญ๋Ÿ‰์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.""",
1194
- "significance": "AI ๊ธฐ์ˆ ์˜ ๋ฐœ์ „์€ ๋‹จ์ˆœํ•œ ๊ธฐ์ˆ  ํ˜์‹ ์„ ๋„˜์–ด ์‚ฌํšŒ, ๊ฒฝ์ œ, ์œค๋ฆฌ์  ์ธก๋ฉด์—์„œ ๋‹ค์–‘ํ•œ ๋…ผ์˜๋ฅผ ๋ถˆ๋Ÿฌ์ผ์œผํ‚ค๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋ณ€ํ™”๋ฅผ ์ดํ•ดํ•˜๊ณ  ๋Œ€๋น„ํ•˜๋Š” ๊ฒƒ์ด ๋ฏธ๋ž˜ ์„ธ๋Œ€์—๊ฒŒ ์ค‘์š”ํ•œ ์—ญ๋Ÿ‰์ž…๋‹ˆ๋‹ค.",
1195
- "impact_level": "medium",
1196
- "impact_text": "์ค‘๊ฐ„",
1197
- "impact_description": "AI ๊ธฐ์ˆ ์˜ ๋ฐœ์ „์€ ๊ต์œก, ์ทจ์—…, ์‚ฐ์—… ์ „๋ฐ˜์— ๊ฑธ์ณ ๊ตฌ์กฐ์  ๋ณ€ํ™”๋ฅผ ๊ฐ€์ ธ์˜ฌ ๊ฒƒ์ด๋ฉฐ, ์ด์— ๋Œ€ํ•œ ์ดํ•ด์™€ ์ค€๋น„๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.",
1198
- "action": "AI ๊ธฐ์ˆ ์˜ ๊ธฐ๋ณธ ์›๋ฆฌ๋ฅผ ํ•™์Šตํ•˜๊ณ , ๊ด€๋ จ ํ”„๋กœ๊ทธ๋ž˜๋ฐ(Python ๋“ฑ)์ด๋‚˜ ๋ฐ์ดํ„ฐ ๊ณผํ•™ ๊ธฐ์ดˆ๋ฅผ ๊ณต๋ถ€ํ•ด๋ณด์„ธ์š”. ๏ฟฝ๏ฟฝ๏ฟฝํ•œ AI ์œค๋ฆฌ์™€ ์‚ฌํšŒ์  ์˜ํ–ฅ์— ๋Œ€ํ•ด์„œ๋„ ๋น„ํŒ์ ์œผ๋กœ ์‚ฌ๊ณ ํ•˜๋Š” ์Šต๊ด€์„ ๊ธฐ๋ฅด์„ธ์š”."
1199
- }
1200
-
1201
- def analyze_model(self, model_name: str, task: str, downloads: int) -> str:
1202
- """ํ—ˆ๊น…ํŽ˜์ด์Šค ๋ชจ๋ธ ๋ถ„์„ - ๋ชจ๋ธ ์นด๋“œ๋ฅผ LLM์œผ๋กœ ๋ถ„์„"""
1203
-
1204
- # 1. ๋ชจ๋ธ ์นด๋“œ ๊ฐ€์ ธ์˜ค๊ธฐ
1205
- model_card = self.fetch_model_card(model_name)
1206
-
1207
- # 2. LLM์œผ๋กœ ๋ถ„์„
1208
- if model_card and self.api_available:
1209
- try:
1210
- messages = [
1211
- {
1212
- "role": "system",
1213
- "content": "๋‹น์‹ ์€ ์ค‘๊ณ ๋“ฑํ•™์ƒ๋„ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๊ฒŒ AI ๋ชจ๋ธ์„ ์‰ฝ๊ฒŒ ์„ค๋ช…ํ•˜๋Š” ์ „๋ฌธ๊ฐ€์ž…๋‹ˆ๋‹ค. ํ•œ๊ตญ์–ด๋กœ ๋‹ต๋ณ€ํ•˜์„ธ์š”."
1214
- },
1215
- {
1216
- "role": "user",
1217
- "content": f"""๋‹ค์Œ์€ ํ—ˆ๊น…ํŽ˜์ด์Šค ๋ชจ๋ธ '{model_name}'์˜ ๋ชจ๋ธ ์นด๋“œ์ž…๋‹ˆ๋‹ค:
1218
-
1219
- {model_card}
1220
-
1221
- ์ด ๋ชจ๋ธ์„ ์ค‘๊ณ ๋“ฑํ•™์ƒ์ด ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋„๋ก 3-4๋ฌธ์žฅ์œผ๋กœ ์‰ฝ๊ฒŒ ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”. ๋‹ค์Œ ๋‚ด์šฉ์„ ํฌํ•จํ•˜์„ธ์š”:
1222
- 1. ์ด ๋ชจ๋ธ์ด ๋ฌด์—‡์„ ํ•˜๋Š”์ง€
1223
- 2. ์–ด๋–ค ํŠน์ง•์ด ์žˆ๋Š”์ง€
1224
- 3. ๋ˆ„๊ฐ€ ์‚ฌ์šฉํ•˜๋ฉด ์ข‹์€์ง€
1225
-
1226
- ๋‹ต๋ณ€์€ ๋ฐ˜๋“œ์‹œ 3-4๋ฌธ์žฅ์˜ ํ•œ๊ตญ์–ด๋กœ๋งŒ ์ž‘์„ฑํ•˜์„ธ์š”."""
1227
- }
1228
- ]
1229
-
1230
- result = self.call_llm(messages, max_tokens=500)
1231
-
1232
- if result:
1233
- return result.strip()
1234
-
1235
- except Exception as e:
1236
- print(f" โš ๏ธ ๋ชจ๋ธ ๋ถ„์„ LLM ์˜ค๋ฅ˜: {e}")
1237
-
1238
- # 3. Fallback: ํ…œํ”Œ๋ฆฟ ๊ธฐ๋ฐ˜ ์„ค๋ช…
1239
- task_explanations = {
1240
- "text-generation": "๊ธ€์„ ์ž๋™์œผ๋กœ ๋งŒ๋“ค์–ด์ฃผ๋Š”",
1241
- "image-to-text": "์‚ฌ์ง„์„ ๋ณด๊ณ  ์„ค๋ช…์„ ์จ์ฃผ๋Š”",
1242
- "text-to-image": "๊ธ€์„ ์ฝ๊ณ  ๊ทธ๋ฆผ์„ ๊ทธ๋ ค์ฃผ๋Š”",
1243
- "translation": "๋‹ค๋ฅธ ์–ธ์–ด๋กœ ๋ฒˆ์—ญํ•ด์ฃผ๋Š”",
1244
- "question-answering": "์งˆ๋ฌธ์— ๋‹ตํ•ด์ฃผ๋Š”",
1245
- "summarization": "๊ธด ๊ธ€์„ ์งง๊ฒŒ ์š”์•ฝํ•ด์ฃผ๋Š”",
1246
- "text-classification": "๊ธ€์„ ๋ถ„๋ฅ˜ํ•ด์ฃผ๋Š”",
1247
- "token-classification": "๋‹จ์–ด๋ฅผ ๋ถ„์„ํ•ด์ฃผ๋Š”",
1248
- "fill-mask": "๋นˆ์นธ์„ ์ฑ„์›Œ์ฃผ๋Š”"
1249
- }
1250
-
1251
- task_desc = task_explanations.get(task, "ํŠน๋ณ„ํ•œ ๊ธฐ๋Šฅ์„ ํ•˜๋Š”")
1252
-
1253
- if downloads > 10000000:
1254
- popularity = "์—„์ฒญ๋‚˜๊ฒŒ ๋งŽ์€"
1255
- elif downloads > 1000000:
1256
- popularity = "์•„์ฃผ ๋งŽ์€"
1257
- elif downloads > 100000:
1258
- popularity = "๋งŽ์€"
1259
- else:
1260
- popularity = "์–ด๋А ์ •๋„"
1261
-
1262
- return f"์ด ๋ชจ๋ธ์€ {task_desc} AI์˜ˆ์š”. {popularity} ์‚ฌ๋žŒ๋“ค์ด ๋‹ค์šด๋กœ๋“œํ•ด์„œ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์–ด์š”. {model_name.split('/')[-1]}๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ ์œ ๋ช…ํ•ด์š”!"
1263
-
1264
- def analyze_space(self, space_name: str, space_id: str, description: str) -> Dict:
1265
- """ํ—ˆ๊น…ํŽ˜์ด์Šค ์ŠคํŽ˜์ด์Šค ๋ถ„์„ - app.py๋ฅผ LLM์œผ๋กœ ๋ถ„์„"""
1266
-
1267
- # 1. app.py ์ฝ”๋“œ ๊ฐ€์ ธ์˜ค๊ธฐ
1268
- app_code = self.fetch_space_code(space_id)
1269
-
1270
- # 2. LLM์œผ๋กœ ๋ถ„์„
1271
- if app_code and self.api_available:
1272
- try:
1273
- messages = [
1274
- {
1275
- "role": "system",
1276
- "content": "๋‹น์‹ ์€ ์ค‘๊ณ ๋“ฑํ•™์ƒ๋„ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๊ฒŒ AI ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‰ฝ๊ฒŒ ์„ค๋ช…ํ•˜๋Š” ์ „๋ฌธ๊ฐ€์ž…๋‹ˆ๋‹ค. ํ•œ๊ตญ์–ด๋กœ ๋‹ต๋ณ€ํ•˜์„ธ์š”."
1277
- },
1278
- {
1279
- "role": "user",
1280
- "content": f"""๋‹ค์Œ์€ ํ—ˆ๊น…ํŽ˜์ด์Šค ์ŠคํŽ˜์ด์Šค '{space_name}'์˜ app.py ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค:
1281
-
1282
- {app_code}
1283
-
1284
- ์ด ์•ฑ์„ ์ค‘๊ณ ๋“ฑํ•™์ƒ์ด ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋„๋ก 3-4๋ฌธ์žฅ์œผ๋กœ ์‰ฝ๊ฒŒ ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”. ๋‹ค์Œ ๋‚ด์šฉ์„ ํฌํ•จํ•˜์„ธ์š”:
1285
- 1. ์ด ์•ฑ์ด ๋ฌด์—‡์„ ํ•˜๋Š”์ง€
1286
- 2. ์–ด๋–ค ๊ธฐ์ˆ ์„ ์‚ฌ์šฉํ•˜๋Š”์ง€
1287
- 3. ์–ด๋–ป๊ฒŒ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š”์ง€
1288
-
1289
- ๋‹ต๋ณ€์€ ๋ฐ˜๋“œ์‹œ 3-4๋ฌธ์žฅ์˜ ํ•œ๊ตญ์–ด๋กœ๋งŒ ์ž‘์„ฑํ•˜์„ธ์š”."""
1290
- }
1291
- ]
1292
-
1293
- result = self.call_llm(messages, max_tokens=500)
1294
-
1295
- if result:
1296
- # ๊ธฐ์ˆ  ์Šคํƒ ์ถ”์ถœ ์‹œ๋„
1297
- tech_stack = []
1298
- if 'gradio' in app_code.lower():
1299
- tech_stack.append('Gradio')
1300
- if 'streamlit' in app_code.lower():
1301
- tech_stack.append('Streamlit')
1302
- if 'transformers' in app_code.lower():
1303
- tech_stack.append('Transformers')
1304
- if 'torch' in app_code.lower() or 'pytorch' in app_code.lower():
1305
- tech_stack.append('PyTorch')
1306
- if 'tensorflow' in app_code.lower():
1307
- tech_stack.append('TensorFlow')
1308
- if 'diffusers' in app_code.lower():
1309
- tech_stack.append('Diffusers')
1310
-
1311
- if not tech_stack:
1312
- tech_stack = ['Python', 'AI']
1313
-
1314
- return {
1315
- "simple_explanation": result.strip(),
1316
- "tech_stack": tech_stack
1317
- }
1318
-
1319
- except Exception as e:
1320
- print(f" โš ๏ธ ์ŠคํŽ˜์ด์Šค ๋ถ„์„ LLM ์˜ค๋ฅ˜: {e}")
1321
-
1322
- # 3. Fallback: ํ…œํ”Œ๋ฆฟ ๊ธฐ๋ฐ˜ ์„ค๋ช…
1323
- return {
1324
- "simple_explanation": f"{space_name}๋Š” ์›น๋ธŒ๋ผ์šฐ์ €์—์„œ ๋ฐ”๋กœ AI๋ฅผ ์ฒดํ—˜ํ•ด๋ณผ ์ˆ˜ ์žˆ๋Š” ๊ณณ์ด์—์š”. ์„ค์น˜ ์—†์ด๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์„œ ํŽธ๋ฆฌํ•ด์š”! ๋งˆ์น˜ ์˜จ๋ผ์ธ ๊ฒŒ์ž„์ฒ˜๋Ÿผ ๋ฐ”๋กœ ์ ‘์†ํ•ด์„œ AI๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ต๋‹ˆ๋‹ค.",
1325
- "tech_stack": ["Python", "Gradio", "Transformers", "PyTorch"]
1326
- }
1327
-
1328
-
1329
- # ============================================
1330
- # ๊ณ ๊ธ‰ ๋ถ„์„๊ธฐ ํด๋ž˜์Šค
1331
- # ============================================
1332
-
1333
- class AdvancedAIAnalyzer:
1334
- """LLM ๊ธฐ๋ฐ˜ ๊ณ ๊ธ‰ AI ๋‰ด์Šค ๋ถ„์„๊ธฐ"""
1335
-
1336
- def __init__(self):
1337
- self.llm_analyzer = LLMAnalyzer()
1338
- self.huggingface_data = {
1339
- "models": [],
1340
- "spaces": []
1341
- }
1342
- self.news_data = []
1343
-
1344
- def fetch_aitimes_news(self) -> List[Dict]:
1345
- """AI Times์—์„œ ์˜ค๋Š˜ ๋‚ ์งœ ๋‰ด์Šค ํฌ๋กค๋ง"""
1346
- print("๐Ÿ“ฐ AI Times ๋‰ด์Šค ์ˆ˜์ง‘ ์ค‘...")
1347
-
1348
- # ์ˆ˜์ง‘ํ•  URL ๋ชฉ๋ก
1349
- urls = [
1350
- 'https://www.aitimes.com/news/articleList.html?sc_multi_code=S2&view_type=sm',
1351
- 'https://www.aitimes.com/news/articleList.html?sc_section_code=S1N24&view_type=sm'
1352
- ]
1353
-
1354
- all_news = []
1355
- today = datetime.now().strftime('%m-%d') # ์˜ˆ: '10-10'
1356
-
1357
- for url_idx, url in enumerate(urls, 1):
1358
- try:
1359
- print(f" ๐Ÿ” [{url_idx}/2] ์ˆ˜์ง‘ ์ค‘: {url}")
1360
- response = requests.get(url, timeout=15, headers={
1361
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
1362
- })
1363
- response.raise_for_status()
1364
- response.encoding = 'utf-8'
1365
-
1366
- soup = BeautifulSoup(response.text, 'html.parser')
1367
-
1368
- # ๋ชจ๋“  ๋งํฌ ์ฐพ๊ธฐ
1369
- articles = soup.find_all('a', href=re.compile(r'/news/articleView\.html\?idxno=\d+'))
1370
-
1371
- print(f" โ†’ {len(articles)}๊ฐœ ๋งํฌ ๋ฐœ๊ฒฌ")
1372
-
1373
- articles_found = 0
1374
- for article_tag in articles:
1375
- try:
1376
- # ์ œ๋ชฉ๊ณผ ๋งํฌ
1377
- title = article_tag.get_text(strip=True)
1378
- link = article_tag.get('href', '')
1379
-
1380
- # ๋งํฌ ์ •๊ทœํ™”
1381
- if link and not link.startswith('http'):
1382
- if link.startswith('/'):
1383
- link = 'https://www.aitimes.com' + link
1384
- else:
1385
- link = 'https://www.aitimes.com/' + link
1386
-
1387
- # ์ œ๋ชฉ์ด ๋„ˆ๋ฌด ์งง์œผ๋ฉด ์Šคํ‚ต
1388
- if not title or len(title) < 10:
1389
- continue
1390
-
1391
- # ๋ถ€๋ชจ ์š”์†Œ์—์„œ ๋‚ ์งœ ์ฐพ๊ธฐ
1392
- parent = article_tag.parent
1393
- date_text = ''
1394
-
1395
- # ๋ถ€๋ชจ์˜ ๋ชจ๋“  ํ…์ŠคํŠธ์—์„œ ๋‚ ์งœ ํŒจํ„ด ์ฐพ๊ธฐ
1396
- if parent:
1397
- parent_text = parent.get_text()
1398
- date_match = re.search(r'(\d{2}-\d{2}\s+\d{2}:\d{2})', parent_text)
1399
- if date_match:
1400
- date_text = date_match.group(1)
1401
-
1402
- # ๋‚ ์งœ๊ฐ€ ์—†์œผ๋ฉด ๋‹ค์Œ ํ˜•์ œ ์š”์†Œ๋“ค ํ™•์ธ
1403
- if not date_text:
1404
- for sibling in article_tag.find_next_siblings():
1405
- sibling_text = sibling.get_text()
1406
- date_match = re.search(r'(\d{2}-\d{2}\s+\d{2}:\d{2})', sibling_text)
1407
- if date_match:
1408
- date_text = date_match.group(1)
1409
- break
1410
-
1411
- # ๋‚ ์งœ๊ฐ€ ์—ฌ์ „ํžˆ ์—†์œผ๋ฉด ์˜ค๋Š˜ ๋‚ ์งœ ์‚ฌ์šฉ
1412
- if not date_text:
1413
- date_text = today
1414
-
1415
- # ์˜ค๋Š˜ ๋‚ ์งœ๋งŒ ํ•„ํ„ฐ๋ง
1416
- if today not in date_text:
1417
- continue
1418
-
1419
- news_item = {
1420
- 'title': title,
1421
- 'url': link,
1422
- 'date': date_text,
1423
- 'source': 'AI Times',
1424
- 'category': 'AI'
1425
- }
1426
-
1427
- all_news.append(news_item)
1428
- articles_found += 1
1429
-
1430
- print(f" โœ“ ์ถ”๊ฐ€: {title[:60]}... ({date_text})")
1431
-
1432
- except Exception as e:
1433
- continue
1434
-
1435
- print(f" โ†’ {articles_found}๊ฐœ ์˜ค๋Š˜์ž ๊ธฐ์‚ฌ ์ˆ˜์ง‘\n")
1436
- time.sleep(1) # ์„œ๋ฒ„ ๋ถ€ํ•˜ ๋ฐฉ์ง€
1437
-
1438
- except Exception as e:
1439
- print(f" โš ๏ธ URL ์ˆ˜์ง‘ ์˜ค๋ฅ˜: {e}\n")
1440
- continue
1441
-
1442
- # ์ค‘๋ณต ์ œ๊ฑฐ (URL ๊ธฐ์ค€)
1443
- unique_news = []
1444
- seen_urls = set()
1445
- for news in all_news:
1446
- if news['url'] not in seen_urls:
1447
- unique_news.append(news)
1448
- seen_urls.add(news['url'])
1449
-
1450
- print(f"โœ… ์ด {len(unique_news)}๊ฐœ ์ค‘๋ณต ์ œ๊ฑฐ๋œ ์˜ค๋Š˜์ž ๋‰ด์Šค\n")
1451
-
1452
- # ์ตœ์†Œ 3๊ฐœ๋Š” ๋ณด์žฅ (์—†์œผ๋ฉด ์ƒ˜ํ”Œ ์ถ”๊ฐ€)
1453
- if len(unique_news) < 3:
1454
- print("โš ๏ธ ๋‰ด์Šค๊ฐ€ ๋ถ€์กฑํ•˜์—ฌ ์ตœ๊ทผ ์ƒ˜ํ”Œ ์ถ”๊ฐ€\n")
1455
- sample_news = [
1456
- {
1457
- 'title': 'MS "์ฑ—GPT ์ˆ˜์š” ํญ์ฆ์œผ๋กœ ๋ฐ์ดํ„ฐ์„ผํ„ฐ ๋ถ€์กฑ...2026๋…„๊นŒ์ง€ ์ง€์†"',
1458
- 'url': 'https://www.aitimes.com/news/articleView.html?idxno=203055',
1459
- 'date': '10-10 15:10',
1460
- 'source': 'AI Times',
1461
- 'category': 'AI'
1462
- },
1463
- {
1464
- 'title': '๋ฏธ๊ตญ, UAE์— GPU ํŒ๋งค ์ผ๋ถ€ ์Šน์ธ...์—”๋น„๋””์•„ ์‹œ์ด 5์กฐ๋‹ฌ๋Ÿฌ ๋ˆˆ์•ž',
1465
- 'url': 'https://www.aitimes.com/news/articleView.html?idxno=203053',
1466
- 'date': '10-10 14:46',
1467
- 'source': 'AI Times',
1468
- 'category': 'AI'
1469
- },
1470
- {
1471
- 'title': '์†Œ๋ผ, ์ฑ—GPT๋ณด๋‹ค ๋นจ๋ฆฌ 100๋งŒ ๋‹ค์šด๋กœ๋“œ ๋ŒํŒŒ',
1472
- 'url': 'https://www.aitimes.com/news/articleView.html?idxno=203045',
1473
- 'date': '10-10 12:55',
1474
- 'source': 'AI Times',
1475
- 'category': 'AI'
1476
- }
1477
- ]
1478
- for sample in sample_news:
1479
- if sample['url'] not in seen_urls:
1480
- unique_news.append(sample)
1481
-
1482
- return unique_news[:20] # ์ตœ๋Œ€ 20๊ฐœ
1483
-
1484
- def fetch_huggingface_models(self, limit: int = 30) -> List[Dict]:
1485
- """ํ—ˆ๊น…ํŽ˜์ด์Šค ํŠธ๋ Œ๋”ฉ ๋ชจ๋ธ 30๊ฐœ ์ˆ˜์ง‘ (์‹ค์ œ API)"""
1486
- print(f"๐Ÿค— ํ—ˆ๊น…ํŽ˜์ด์Šค ํŠธ๋ Œ๋”ฉ ๋ชจ๋ธ {limit}๊ฐœ ์ˆ˜์ง‘ ์ค‘...")
1487
-
1488
- models_list = []
1489
-
1490
- try:
1491
- # Hugging Face API ์‚ฌ์šฉ
1492
- api = HfApi()
1493
-
1494
- # trending ์ˆœ์œ„๋กœ ๋ชจ๋ธ ๊ฐ€์ ธ์˜ค๊ธฐ
1495
- models = list(api.list_models(
1496
- sort="trending_score",
1497
- direction=-1,
1498
- limit=limit
1499
- ))
1500
-
1501
- print(f"๐Ÿ“Š API์—์„œ {len(models)}๊ฐœ ๋ชจ๋ธ ๋ฐ›์Œ")
1502
-
1503
- for idx, model in enumerate(models[:limit], 1):
1504
- try:
1505
- model_info = {
1506
- 'name': model.id,
1507
- 'downloads': getattr(model, 'downloads', 0) or 0,
1508
- 'likes': getattr(model, 'likes', 0) or 0,
1509
- 'task': getattr(model, 'pipeline_tag', 'N/A') or 'N/A',
1510
- 'url': f"https://huggingface.co/{model.id}",
1511
- 'rank': idx
1512
- }
1513
-
1514
- # LLM ๋ถ„์„ ์ถ”๊ฐ€ (๋ชจ๋ธ ์นด๋“œ ๋ถ„์„)
1515
- print(f" ๐Ÿ” {idx}. {model.id} ๋ถ„์„ ์ค‘...")
1516
- model_info['analysis'] = self.llm_analyzer.analyze_model(
1517
- model_info['name'],
1518
- model_info['task'],
1519
- model_info['downloads']
1520
- )
1521
-
1522
- models_list.append(model_info)
1523
-
1524
- # API rate limit ๋ฐฉ์ง€๋ฅผ ์œ„ํ•œ ์งง์€ ๋Œ€๊ธฐ
1525
- time.sleep(0.5)
1526
-
1527
- # ์ง„ํ–‰์ƒํ™ฉ ํ‘œ์‹œ
1528
- if idx % 5 == 0:
1529
- print(f" โœ“ {idx}๊ฐœ ๋ชจ๋ธ ์ฒ˜๋ฆฌ ์™„๋ฃŒ...")
1530
-
1531
- except Exception as e:
1532
- print(f" โš ๏ธ ๋ชจ๋ธ {idx} ์ฒ˜๋ฆฌ ์˜ค๋ฅ˜: {e}")
1533
- continue
1534
-
1535
- print(f"โœ… {len(models_list)}๊ฐœ ํŠธ๋ Œ๋”ฉ ๋ชจ๋ธ ์ˆ˜์ง‘ ์™„๋ฃŒ")
1536
-
1537
- # DB์— ์ €์žฅ
1538
- if models_list:
1539
- save_models_to_db(models_list)
1540
-
1541
- return models_list
1542
-
1543
- except Exception as e:
1544
- print(f"โŒ ๋ชจ๋ธ ์ˆ˜์ง‘ ์˜ค๋ฅ˜: {e}")
1545
- print("๐Ÿ’พ DB์—์„œ ์ด์ „ ๋ฐ์ดํ„ฐ ๋กœ๋“œ ์‹œ๋„...")
1546
- return load_models_from_db()
1547
-
1548
- def fetch_huggingface_spaces(self, limit: int = 30) -> List[Dict]:
1549
- """ํ—ˆ๊น…ํŽ˜์ด์Šค ํŠธ๋ Œ๋”ฉ ์ŠคํŽ˜์ด์Šค 30๊ฐœ ์ˆ˜์ง‘ (์‹ค์ œ API)"""
1550
- print(f"๐Ÿš€ ํ—ˆ๊น…ํŽ˜์ด์Šค ํŠธ๋ Œ๋”ฉ ์ŠคํŽ˜์ด์Šค {limit}๊ฐœ ์ˆ˜์ง‘ ์ค‘...")
1551
-
1552
- spaces_list = []
1553
-
1554
- try:
1555
- # Hugging Face API ์‚ฌ์šฉ
1556
- api = HfApi()
1557
-
1558
- # trending ์ˆœ์œ„๋กœ ์ŠคํŽ˜์ด์Šค ๊ฐ€์ ธ์˜ค๊ธฐ
1559
- spaces = list(api.list_spaces(
1560
- sort="trending_score",
1561
- direction=-1,
1562
- limit=limit
1563
- ))
1564
-
1565
- print(f"๐Ÿ“Š API์—์„œ {len(spaces)}๊ฐœ ์ŠคํŽ˜์ด์Šค ๋ฐ›์Œ")
1566
-
1567
- for idx, space in enumerate(spaces[:limit], 1):
1568
- try:
1569
- space_info = {
1570
- 'space_id': space.id,
1571
- 'name': space.id.split('/')[-1] if '/' in space.id else space.id,
1572
- 'author': space.author,
1573
- 'title': getattr(space, 'title', space.id) or space.id,
1574
- 'likes': getattr(space, 'likes', 0) or 0,
1575
- 'url': f"https://huggingface.co/spaces/{space.id}",
1576
- 'sdk': getattr(space, 'sdk', 'gradio') or 'gradio',
1577
- 'rank': idx
1578
- }
1579
-
1580
- # LLM ๋ถ„์„ ์ถ”๊ฐ€ (app.py ๋ถ„์„)
1581
- print(f" ๐Ÿ” {idx}. {space.id} ๋ถ„์„ ์ค‘...")
1582
- space_analysis = self.llm_analyzer.analyze_space(
1583
- space_info['name'],
1584
- space_info['space_id'],
1585
- space_info['title']
1586
- )
1587
-
1588
- space_info['simple_explanation'] = space_analysis['simple_explanation']
1589
- space_info['tech_stack'] = space_analysis['tech_stack']
1590
- space_info['description'] = space_info['title']
1591
-
1592
- spaces_list.append(space_info)
1593
-
1594
- # API rate limit ๋ฐฉ์ง€๋ฅผ ์œ„ํ•œ ์งง์€ ๋Œ€๊ธฐ
1595
- time.sleep(0.5)
1596
-
1597
- # ์ง„ํ–‰์ƒํ™ฉ ํ‘œ์‹œ
1598
- if idx % 5 == 0:
1599
- print(f" โœ“ {idx}๊ฐœ ์ŠคํŽ˜์ด์Šค ์ฒ˜๋ฆฌ ์™„๋ฃŒ...")
1600
-
1601
- except Exception as e:
1602
- print(f" โš ๏ธ ์ŠคํŽ˜์ด์Šค {idx} ์ฒ˜๋ฆฌ ์˜ค๋ฅ˜: {e}")
1603
- continue
1604
-
1605
- print(f"โœ… {len(spaces_list)}๊ฐœ ํŠธ๋ Œ๋”ฉ ์ŠคํŽ˜์ด์Šค ์ˆ˜์ง‘ ์™„๋ฃŒ")
1606
-
1607
- # DB์— ์ €์žฅ
1608
- if spaces_list:
1609
- save_spaces_to_db(spaces_list)
1610
-
1611
- return spaces_list
1612
-
1613
- except Exception as e:
1614
- print(f"โŒ ์ŠคํŽ˜์ด์Šค ์ˆ˜์ง‘ ์˜ค๋ฅ˜: {e}")
1615
- print("๐Ÿ’พ DB์—์„œ ์ด์ „ ๋ฐ์ดํ„ฐ ๋กœ๋“œ ์‹œ๋„...")
1616
- return load_spaces_from_db()
1617
-
1618
- def analyze_all_news(self) -> List[Dict]:
1619
- """๋ชจ๋“  ๋‰ด์Šค์— LLM ๋ถ„์„ ์ถ”๊ฐ€"""
1620
- print("๐Ÿ“ฐ ๋‰ด์Šค LLM ๋ถ„์„ ์‹œ์ž‘...")
1621
-
1622
- # ์‹ค์ œ ์›น์‚ฌ์ดํŠธ์—์„œ ๋‰ด์Šค ์ˆ˜์ง‘
1623
- news = self.fetch_aitimes_news()
1624
-
1625
- if not news:
1626
- print("โš ๏ธ ์ˆ˜์ง‘๋œ ๋‰ด์Šค๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.")
1627
- return []
1628
-
1629
- analyzed_news = []
1630
-
1631
- for idx, article in enumerate(news, 1):
1632
- print(f" ๐Ÿง  {idx}/{len(news)}: {article['title'][:50]}... ๋ถ„์„ ์ค‘")
1633
-
1634
- analysis = self.llm_analyzer.analyze_news_simple(
1635
- article['title'],
1636
- ""
1637
- )
1638
-
1639
- article['analysis'] = analysis
1640
- analyzed_news.append(article)
1641
-
1642
- print(f"โœ… {len(analyzed_news)}๊ฐœ ๋‰ด์Šค ๋ถ„์„ ์™„๋ฃŒ")
1643
-
1644
- # DB์— ์ €์žฅ
1645
- if analyzed_news:
1646
- save_news_to_db(analyzed_news)
1647
-
1648
- return analyzed_news
1649
-
1650
- def get_all_data(self, force_refresh: bool = False) -> Dict:
1651
- """๋ชจ๋“  ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘ ๋ฐ ๋ถ„์„
1652
-
1653
- Args:
1654
- force_refresh: True๋ฉด ์ƒˆ๋กœ ์ˆ˜์ง‘, False๋ฉด DB์—์„œ ๋กœ๋“œ ํ›„ ์—†์œผ๋ฉด ์ˆ˜์ง‘
1655
- """
1656
- print("\n" + "="*60)
1657
- print("๐Ÿš€ AI ๋‰ด์Šค & ํ—ˆ๊น…ํŽ˜์ด์Šค LLM ๋ถ„์„ ์‹œ์ž‘")
1658
- print("="*60 + "\n")
1659
-
1660
- if force_refresh:
1661
- print("๐Ÿ”„ ๊ฐ•์ œ ์ƒˆ๋กœ๊ณ ์นจ ๋ชจ๋“œ: ๋ชจ๋“  ๋ฐ์ดํ„ฐ ์ƒˆ๋กœ ์ˆ˜์ง‘")
1662
- analyzed_news = self.analyze_all_news()
1663
- analyzed_models = self.fetch_huggingface_models(30)
1664
- analyzed_spaces = self.fetch_huggingface_spaces(30)
1665
- else:
1666
- print("๐Ÿ’พ DB ์šฐ์„  ๋กœ๋“œ ๋ชจ๋“œ")
1667
-
1668
- # DB์—์„œ ๋จผ์ € ๋กœ๋“œ
1669
- analyzed_news = load_news_from_db()
1670
- if not analyzed_news:
1671
- print("๐Ÿ“ฐ DB์— ๋‰ด์Šค ์—†์Œ โ†’ ์ƒˆ๋กœ ์ˆ˜์ง‘")
1672
- analyzed_news = self.analyze_all_news()
1673
- else:
1674
- print(f"โœ… DB์—์„œ {len(analyzed_news)}๊ฐœ ๋‰ด์Šค ๋กœ๋“œ")
1675
-
1676
- analyzed_models = load_models_from_db()
1677
- if not analyzed_models:
1678
- print("๐Ÿค— DB์— ๋ชจ๋ธ ์—†์Œ โ†’ ์ƒˆ๋กœ ์ˆ˜์ง‘")
1679
- analyzed_models = self.fetch_huggingface_models(30)
1680
- else:
1681
- print(f"โœ… DB์—์„œ {len(analyzed_models)}๊ฐœ ๋ชจ๋ธ ๋กœ๋“œ")
1682
-
1683
- analyzed_spaces = load_spaces_from_db()
1684
- if not analyzed_spaces:
1685
- print("๐Ÿš€ DB์— ์ŠคํŽ˜์ด์Šค ์—†์Œ โ†’ ์ƒˆ๋กœ ์ˆ˜์ง‘")
1686
- analyzed_spaces = self.fetch_huggingface_spaces(30)
1687
- else:
1688
- print(f"โœ… DB์—์„œ {len(analyzed_spaces)}๊ฐœ ์ŠคํŽ˜์ด์Šค ๋กœ๋“œ")
1689
-
1690
- # ํ†ต๊ณ„
1691
- stats = {
1692
- 'total_news': len(analyzed_news),
1693
- 'hf_models': len(analyzed_models),
1694
- 'hf_spaces': len(analyzed_spaces),
1695
- 'llm_analyses': len(analyzed_news) + len(analyzed_models) + len(analyzed_spaces)
1696
- }
1697
-
1698
- print(f"\nโœ… ์ „์ฒด ๋ถ„์„ ์™„๋ฃŒ: {stats['llm_analyses']}๊ฐœ ํ•ญ๋ชฉ")
1699
- print(f" ๐Ÿ“ฐ ๋‰ด์Šค: {stats['total_news']}๊ฐœ")
1700
- print(f" ๐Ÿค— ๋ชจ๋ธ: {stats['hf_models']}๊ฐœ")
1701
- print(f" ๐Ÿš€ ์ŠคํŽ˜์ด์Šค: {stats['hf_spaces']}๊ฐœ")
1702
-
1703
- return {
1704
- 'analyzed_news': analyzed_news,
1705
- 'analyzed_models': analyzed_models,
1706
- 'analyzed_spaces': analyzed_spaces,
1707
- 'stats': stats,
1708
- 'timestamp': datetime.now().strftime('%Y๋…„ %m์›” %d์ผ %H:%M:%S')
1709
- }
1710
-
1711
-
1712
- # ============================================
1713
- # Flask ๋ผ์šฐํŠธ
1714
- # ============================================
1715
-
1716
- @app.route('/')
1717
- def index():
1718
- """๋ฉ”์ธ ํŽ˜์ด์ง€"""
1719
- try:
1720
- # refresh ํŒŒ๋ผ๋ฏธํ„ฐ ํ™•์ธ
1721
- force_refresh = request.args.get('refresh', 'false').lower() == 'true'
1722
-
1723
- analyzer = AdvancedAIAnalyzer()
1724
- data = analyzer.get_all_data(force_refresh=force_refresh)
1725
- return render_template_string(HTML_TEMPLATE, **data)
1726
- except Exception as e:
1727
- import traceback
1728
- error_detail = traceback.format_exc()
1729
- return f"""
1730
- <html>
1731
- <body style="font-family: Arial; padding: 50px; text-align: center;">
1732
- <h1 style="color: #e74c3c;">โš ๏ธ ์˜ค๋ฅ˜ ๋ฐœ์ƒ</h1>
1733
- <p>{str(e)}</p>
1734
- <pre style="text-align: left; background: #f5f5f5; padding: 20px; border-radius: 5px;">
1735
- {error_detail}
1736
- </pre>
1737
- <button onclick="location.href='/'" style="padding: 10px 20px; margin: 10px;">
1738
- ๐Ÿ”„ ์ƒˆ๋กœ๊ณ ์นจ
1739
- </button>
1740
- <button onclick="location.href='/?refresh=true'" style="padding: 10px 20px; margin: 10px; background: #ff6b6b; color: white; border: none; border-radius: 5px;">
1741
- ๐Ÿ”ฅ ๊ฐ•์ œ ๊ฐฑ์‹ 
1742
- </button>
1743
- </body>
1744
- </html>
1745
- """, 500
1746
-
1747
-
1748
- @app.route('/api/data')
1749
- def api_data():
1750
- """JSON API"""
1751
- try:
1752
- force_refresh = request.args.get('refresh', 'false').lower() == 'true'
1753
- analyzer = AdvancedAIAnalyzer()
1754
- data = analyzer.get_all_data(force_refresh=force_refresh)
1755
- return jsonify({
1756
- 'success': True,
1757
- 'data': data
1758
- })
1759
- except Exception as e:
1760
- return jsonify({
1761
- 'success': False,
1762
- 'error': str(e)
1763
- }), 500
1764
-
1765
-
1766
- @app.route('/api/refresh')
1767
- def api_refresh():
1768
- """๊ฐ•์ œ ์ƒˆ๋กœ๊ณ ์นจ API"""
1769
- try:
1770
- analyzer = AdvancedAIAnalyzer()
1771
- data = analyzer.get_all_data(force_refresh=True)
1772
- return jsonify({
1773
- 'success': True,
1774
- 'message': '๋ฐ์ดํ„ฐ๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ๊ฐฑ์‹ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค',
1775
- 'stats': data['stats']
1776
- })
1777
- except Exception as e:
1778
- return jsonify({
1779
- 'success': False,
1780
- 'error': str(e)
1781
- }), 500
1782
-
1783
-
1784
- @app.route('/health')
1785
- def health():
1786
- """ํ—ฌ์Šค ์ฒดํฌ"""
1787
- try:
1788
- # DB ์—ฐ๊ฒฐ ํ™•์ธ
1789
- conn = sqlite3.connect(DB_PATH)
1790
- cursor = conn.cursor()
1791
- cursor.execute("SELECT COUNT(*) FROM news")
1792
- news_count = cursor.fetchone()[0]
1793
- cursor.execute("SELECT COUNT(*) FROM models")
1794
- models_count = cursor.fetchone()[0]
1795
- cursor.execute("SELECT COUNT(*) FROM spaces")
1796
- spaces_count = cursor.fetchone()[0]
1797
- conn.close()
1798
-
1799
- return jsonify({
1800
- "status": "healthy",
1801
- "service": "AI News LLM Analyzer",
1802
- "version": "3.2.0",
1803
- "database": {
1804
- "connected": True,
1805
- "news_count": news_count,
1806
- "models_count": models_count,
1807
- "spaces_count": spaces_count
1808
- },
1809
- "fireworks_api": {
1810
- "configured": bool(os.environ.get('FIREWORKS_API_KEY'))
1811
- },
1812
- "timestamp": datetime.now().isoformat()
1813
- })
1814
- except Exception as e:
1815
- return jsonify({
1816
- "status": "unhealthy",
1817
- "error": str(e)
1818
- }), 500
1819
-
1820
-
1821
- # ============================================
1822
- # ๋ฉ”์ธ ์‹คํ–‰
1823
- # ============================================
1824
-
1825
- if __name__ == '__main__':
1826
- port = int(os.environ.get('PORT', 7860))
1827
-
1828
- print(f"""
1829
- โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—
1830
- โ•‘ โ•‘
1831
- โ•‘ ๐Ÿค– AI ๋‰ด์Šค & ํ—ˆ๊น…ํŽ˜์ด์Šค LLM ๋ถ„์„ ์›น์•ฑ v3.2 โ•‘
1832
- โ•‘ โ•‘
1833
- โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
1834
-
1835
- โœจ ์ฃผ์š” ๊ธฐ๋Šฅ:
1836
- โ€ข ๐Ÿ’พ SQLite DB ์˜๊ตฌ ์Šคํ† ๋ฆฌ์ง€
1837
- โ€ข ๐ŸŒ AI Times ์‹ค์‹œ๊ฐ„ ๋‰ด์Šค ํฌ๋กค๋ง (2๊ฐœ ์„น์…˜)
1838
- โ€ข ๐Ÿ“ฐ ๋‰ด์Šค ์ค‘๊ณ ๋“ฑํ•™์ƒ ์ˆ˜์ค€ ๋ถ„์„
1839
- โ€ข ๐Ÿค— ํ—ˆ๊น…ํŽ˜์ด์Šค ํŠธ๋ Œ๋”ฉ ๋ชจ๋ธ TOP 30 (๋ชจ๋ธ ์นด๋“œ ๋ถ„์„)
1840
- โ€ข ๐Ÿš€ ํ—ˆ๊น…ํŽ˜์ด์Šค ํŠธ๋ Œ๋”ฉ ์ŠคํŽ˜์ด์Šค TOP 30 (app.py ๋ถ„์„)
1841
- โ€ข ๐Ÿง  Fireworks AI (Qwen3-235B) ์‹ค์‹œ๊ฐ„ LLM ๋ถ„์„
1842
- โ€ข ๐ŸŽจ ํƒญ UI (๋‰ด์Šค/๋ชจ๋ธ/์ŠคํŽ˜์ด์Šค)
1843
-
1844
- ๐Ÿ”‘ API ์„ค์ •:
1845
- FIREWORKS_API_KEY: {"โœ… ์„ค์ •๋จ" if os.environ.get('FIREWORKS_API_KEY') else "โŒ ๋ฏธ์„ค์ • (ํ…œํ”Œ๋ฆฟ ๋ชจ๋“œ)"}
1846
-
1847
- ๐Ÿš€ ์„œ๋ฒ„ ์ •๋ณด:
1848
- ๐Ÿ“ ๋ฉ”์ธ: http://localhost:{port}
1849
- ๐Ÿ”„ ๊ฐ•์ œ๊ฐฑ์‹ : http://localhost:{port}/?refresh=true
1850
- ๐Ÿ“Š API: http://localhost:{port}/api/data
1851
- ๐Ÿ”ฅ ์ƒˆ๋กœ๊ณ ์นจ API: http://localhost:{port}/api/refresh
1852
- ๐Ÿ’š Health: http://localhost:{port}/health
1853
-
1854
- ๐Ÿ’พ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค: {DB_PATH}
1855
-
1856
- ์ดˆ๊ธฐํ™” ์ค‘...
1857
- """)
1858
-
1859
- # ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ดˆ๊ธฐํ™”
1860
- try:
1861
- init_database()
1862
- except Exception as e:
1863
- print(f"โŒ DB ์ดˆ๊ธฐํ™” ์˜ค๋ฅ˜: {e}")
1864
- sys.exit(1)
1865
-
1866
- print("\nโœ… ์„œ๋ฒ„ ์ค€๋น„ ์™„๋ฃŒ!")
1867
- print("๋ธŒ๋ผ์šฐ์ €์—์„œ ์œ„ URL์„ ์—ด์–ด์ฃผ์„ธ์š”!")
1868
- print("์ข…๋ฃŒ: Ctrl+C\n")
1869
-
1870
- try:
1871
- app.run(
1872
- host='0.0.0.0',
1873
- port=port,
1874
- debug=False,
1875
- threaded=True
1876
- )
1877
- except KeyboardInterrupt:
1878
- print("\n\n๐Ÿ‘‹ ์„œ๋ฒ„ ์ข…๋ฃŒ!")
1879
- sys.exit(0)
1880
- except Exception as e:
1881
- print(f"\nโŒ์„œ๋ฒ„ ์˜ค๋ฅ˜: {e}")
1882
- sys.exit(1)