import gradio as gr from google import genai import time import os # 外部ファイルの読み込み(news_collector.py と ai_analyst.py が同じ場所にある前提) from news_collector import fetch_rss_news from ai_analyst import analyze_news def run_insight_process(api_key): """ ニュース取得から解析までを順次実行し、ログをリアルタイムで画面に返す関数 """ if not api_key or len(api_key) < 10: yield "❌ エラー: 有効なGemini APIキーをサイドバーに入力してください。" return # 新しいSDKではClientを渡す形が望ましい # 新しいSDKではClientを渡す形が望ましいが、既存の構成を尊重して環境変数経由にする log_output = "🚀 投資インサイト解析を開始します...\n" yield log_output try: # 1. ニュース収集 log_output += "📡 ニュースサイトから最新記事を収集中...\n" yield log_output articles = fetch_rss_news() if not articles: yield log_output + "❌ ニュースの取得に失敗したか、記事が見つかりませんでした。" return log_output += f"✅ {len(articles)}件のニュースを取得しました。上位5件を解析します。\n\n" yield log_output # 2. 解析ループ(上位5件) top_articles = articles[:5] for i, article in enumerate(top_articles): log_output += f"🔍 解析中 ({i+1}/{len(top_articles)}): {article['title']}\n" yield log_output # AI解析の実行 try: res = analyze_news(article, api_key) # スコアの数値化とバリデーション try: score = int(res.get('market_impact_score', 0)) except: score = 0 # スコアに応じたアイコンと符号 if score > 0: score_display = f"+{score}" icon = "📈" elif score < 0: score_display = f"{score}" icon = "📉" else: score_display = "0" icon = "➖" # レポートの構築(文字バラバラ対策済み) report = f"===== 【分析結果】 {icon} スコア: {score_display} / 10 =====\n" report += f"■ 歴史的類似性:\n{res.get('historical_precedent', '情報なし')}\n\n" # リスト形式のデータ(弱気視点)が文章で返ってきた場合でも正しく表示する処理 report += f"■ 警戒すべきリスク/弱気視点:\n" bearish = res.get('bearish_view', []) if isinstance(bearish, list): for r in bearish: report += f" ・{str(r)}\n" else: report += f" ・{str(bearish)}\n" # リスト形式のデータ(影響セクター)の表示処理 report += f"\n■ 影響セクター:\n" sectors = res.get('sector_impact', []) if isinstance(sectors, list): report += f"{', '.join([str(s) for s in sectors])}\n" else: report += f"{str(sectors)}\n" report += "==========================================\n\n" log_output += report yield log_output except Exception as e: log_output += f"⚠️ この記事の解析中にエラーが発生しました: {str(e)}\n\n" yield log_output # 無料枠のAPI制限を考慮して少し待機 if i < len(top_articles) - 1: time.sleep(2) log_output += "🎉 すべての解析作業が完了しました。" yield log_output except Exception as e: yield log_output + f"\n❌ システムエラーが発生しました: {str(e)}" # --- Gradio UIのデザイン --- with gr.Blocks() as demo: gr.Markdown(""" # 📈 AI投資インサイト・アナライザー 最新の経済ニュースを取得し、Gemini 2.5 Flashが投資判断を21段階でスコアリングします。 """) with gr.Sidebar(): gr.Markdown("### 設定") api_input = gr.Textbox( label="1. Gemini API Key", type="password", placeholder="AI Studioのキーを貼り付け" ) start_btn = gr.Button("解析実行", variant="primary") gr.Markdown("---") gr.Markdown("[APIキーの取得はこちら(無料)](https://aistudio.google.com/app/apikey)") # 実行ログ兼レポート表示エリア status_log = gr.Textbox( label="実行ログおよび詳細レポート", lines=30, interactive=False ) # ボタンクリック時の動作 start_btn.click( fn=run_insight_process, inputs=[api_input], outputs=status_log ) # アプリの起動 if __name__ == "__main__": demo.launch(theme=gr.themes.Soft())