import gradio as gr import pandas as pd import time import traceback import os import requests # 完全版のimportエラー対策(段階的フォールバック) LANGCHAIN_AVAILABLE = False FULL_VERSION = False try: from langchain_groq import ChatGroq from langchain_openai import ChatOpenAI LANGCHAIN_AVAILABLE = True print("✅ LangChain基本ライブラリが利用可能です") except ImportError as e: print(f"⚠️ LangChain基本ライブラリが利用できません: {e}") try: from OpenAITools.FetchTools import fetch_clinical_trials from OpenAITools.CrinicalTrialTools import SimpleClinicalTrialAgent, GraderAgent, LLMTranslator, generate_ex_question_English if LANGCHAIN_AVAILABLE: FULL_VERSION = True print("✅ 完全版モジュールが正常にロードされました") except ImportError as e: print(f"⚠️ 完全版モジュールのインポートに失敗: {e}") print("軽量版モードで動作します") # 環境変数チェック def check_environment(): """環境変数をチェックし、不足している場合は警告""" missing_vars = [] if not os.getenv("GROQ_API_KEY"): missing_vars.append("GROQ_API_KEY") if not os.getenv("OPENAI_API_KEY"): missing_vars.append("OPENAI_API_KEY") if missing_vars: print(f"⚠️ 環境変数が設定されていません: {', '.join(missing_vars)}") print("一部の機能が制限される可能性があります。") return len(missing_vars) == 0 # 環境変数チェック実行 env_ok = check_environment() # モデルとエージェントの安全な初期化 def safe_init_agents(): """エージェントを安全に初期化""" if not FULL_VERSION: return None, None, None try: groq = ChatGroq(model_name="llama3-70b-8192", temperature=0) translator = LLMTranslator(groq) criteria_agent = SimpleClinicalTrialAgent(groq) grader_agent = GraderAgent(groq) print("✅ AIエージェントが正常に初期化されました") return translator, criteria_agent, grader_agent except Exception as e: print(f"❌ エージェント初期化エラー: {e}") return None, None, None # エージェント初期化 translator, CriteriaCheckAgent, grader_agent = safe_init_agents() # エラーハンドリング付きでエージェント評価を実行する関数 def evaluate_with_retry(agent, criteria, question, max_retries=3): """エラーハンドリング付きでエージェント評価を実行""" if agent is None: return "評価エラー: エージェントが初期化されていません。API keyを確認してください。" for attempt in range(max_retries): try: return agent.evaluate_eligibility(criteria, question) except Exception as e: if "missing variables" in str(e): print(f"プロンプトテンプレートエラー (試行 {attempt + 1}/{max_retries}): {e}") return "評価エラー: プロンプトテンプレートの設定に問題があります" elif "no healthy upstream" in str(e) or "InternalServerError" in str(e): print(f"Groqサーバーエラー (試行 {attempt + 1}/{max_retries}): {e}") if attempt < max_retries - 1: time.sleep(2) continue else: return "評価エラー: サーバーに接続できませんでした" elif "API key" in str(e) or "authentication" in str(e).lower(): return "評価エラー: API keyが無効または設定されていません" else: print(f"予期しないエラー (試行 {attempt + 1}/{max_retries}): {e}") if attempt < max_retries - 1: time.sleep(1) continue else: return f"評価エラー: {str(e)}" return "評価エラー: 最大リトライ回数に達しました" def evaluate_grade_with_retry(agent, judgment, max_retries=3): """エラーハンドリング付きでグレード評価を実行""" if agent is None: return "unclear" for attempt in range(max_retries): try: return agent.evaluate_eligibility(judgment) except Exception as e: if "no healthy upstream" in str(e) or "InternalServerError" in str(e): print(f"Groqサーバーエラー (グレード評価 - 試行 {attempt + 1}/{max_retries}): {e}") if attempt < max_retries - 1: time.sleep(2) continue else: return "unclear" elif "API key" in str(e) or "authentication" in str(e).lower(): return "unclear" else: print(f"予期しないエラー (グレード評価 - 試行 {attempt + 1}/{max_retries}): {e}") if attempt < max_retries - 1: time.sleep(1) continue else: return "unclear" return "unclear" # 基本的なClinicalTrials.gov API呼び出し(軽量版) def fetch_clinical_trials_basic(cancer_name): """基本的な臨床試験データ取得(requestsのみ使用)""" try: search_expr = f"{cancer_name} SEARCH[Location](AREA[LocationCountry]Japan AND AREA[LocationStatus]Recruiting)" base_url = "https://clinicaltrials.gov/api/v2/studies" params = { "query.titles": search_expr, "pageSize": 20 # 軽量版では20件に制限 } print(f"基本API呼び出し: {cancer_name}") response = requests.get(base_url, params=params) if response.status_code == 200: data = response.json() studies = data.get('studies', []) data_list = [] for study in studies: nctId = study['protocolSection']['identificationModule'].get('nctId', 'Unknown') title = study['protocolSection']['identificationModule'].get('briefTitle', 'no title') conditions = ', '.join(study['protocolSection']['conditionsModule'].get('conditions', ['No conditions listed'])) summary = study['protocolSection']['descriptionModule'].get('briefSummary', 'no summary') # 場所情報の抽出 locations_list = study['protocolSection'].get('contactsLocationsModule', {}).get('locations', []) japan_locations = [] for location in locations_list: if location.get('country') == 'Japan': city = location.get('city', 'Unknown City') japan_locations.append(city) primaryCompletionDate = study['protocolSection']['statusModule'].get('primaryCompletionDateStruct', {}).get('date', 'Unknown Date') eligibilityCriteria = study['protocolSection']['eligibilityModule'].get('eligibilityCriteria', 'Unknown') data_list.append({ "NCTID": nctId, "Title": title, "Primary Completion Date": primaryCompletionDate, "Cancer": conditions, "Summary": summary, "Japanes Locations": ', '.join(set(japan_locations)) if japan_locations else "No Japan locations", "Eligibility Criteria": eligibilityCriteria }) return data_list else: print(f"API呼び出し失敗: {response.status_code}") return [] except Exception as e: print(f"基本API呼び出しエラー: {e}") return [] # 軽量版データ生成関数 def generate_sample_data(age, sex, tumor_type, GeneMutation, Meseable, Biopsiable): """サンプルデータを生成(辞書のリスト形式)""" try: if not all([age, sex, tumor_type]): return [] sample_data = [ { "NCTID": "NCT12345678", "AgentGrade": "yes", "Title": f"Clinical Trial for {tumor_type} in {sex} patients", "AgentJudgment": f"{age}歳{sex}の{tumor_type}患者は参加可能です。詳細な検査結果により最終判断が必要です。", "Japanes Locations": "Tokyo, Osaka", "Primary Completion Date": "2025-12-31", "Cancer": tumor_type, "Summary": f"Phase II study evaluating new treatment for {tumor_type}", "Eligibility Criteria": f"Age 18-75, confirmed {tumor_type}, adequate organ function" }, { "NCTID": "NCT87654321", "AgentGrade": "no", "Title": f"Alternative treatment for {tumor_type}", "AgentJudgment": f"{age}歳{sex}の{tumor_type}患者は年齢制限により参加できません。", "Japanes Locations": "Kyoto, Fukuoka", "Primary Completion Date": "2026-06-30", "Cancer": tumor_type, "Summary": f"Comparative study of standard vs experimental therapy for {tumor_type}", "Eligibility Criteria": f"Age 20-65, {tumor_type} with specific mutations, ECOG 0-1" }, { "NCTID": "NCT11111111", "AgentGrade": "unclear", "Title": f"Experimental therapy for {tumor_type} with {GeneMutation}", "AgentJudgment": f"{age}歳{sex}の{tumor_type}患者の参加は追加情報が必要で不明確です。", "Japanes Locations": "Nagoya, Sendai", "Primary Completion Date": "2025-09-15", "Cancer": tumor_type, "Summary": f"Early phase trial testing combination therapy for {tumor_type}", "Eligibility Criteria": f"Age 18-80, advanced {tumor_type}, previous treatment failure" } ] return sample_data except Exception as e: print(f"サンプルデータ生成エラー: {e}") return [] # 基本版データ生成関数(ClinicalTrials.gov API使用、AI評価なし) def generate_basic_data(age, sex, tumor_type, GeneMutation, Meseable, Biopsiable): """基本版のデータ生成(API使用、AI評価なし)""" try: if not all([age, sex, tumor_type]): return [] # 実際のAPI呼び出し data_list = fetch_clinical_trials_basic(tumor_type) if not data_list: print("臨床試験データが見つかりませんでした") return [] # AI評価なしのプレースホルダーを追加 for item in data_list: item['AgentJudgment'] = f'基本版:{age}歳{sex}の{tumor_type}患者への詳細評価にはAI機能が必要です' item['AgentGrade'] = 'unclear' print(f"基本版評価完了。結果: {len(data_list)} 件") return data_list except Exception as e: print(f"基本版データ生成中に予期しないエラー: {e}") traceback.print_exc() return [] # 完全版データ生成関数(AI評価付き) def generate_full_data(age, sex, tumor_type, GeneMutation, Meseable, Biopsiable): """完全版のデータ生成(実際のAPI使用 + AI評価)""" try: if not all([age, sex, tumor_type]): return [] # 日本語の腫瘍タイプを英語に翻訳 try: if translator is not None: TumorName = translator.translate(tumor_type) print(f"腫瘍タイプ翻訳: {tumor_type} → {TumorName}") else: print("翻訳エージェントが利用できません。元の値を使用します。") TumorName = tumor_type except Exception as e: print(f"翻訳エラー: {e}") TumorName = tumor_type # 質問文を生成 try: ex_question = generate_ex_question_English(age, sex, TumorName, GeneMutation, Meseable, Biopsiable) print(f"生成された質問: {ex_question}") except Exception as e: print(f"質問生成エラー: {e}") return [] # 臨床試験データの取得 try: print(f"臨床試験データを検索中: {TumorName}") df = fetch_clinical_trials(TumorName) if df.empty: print("臨床試験データが見つかりませんでした") return [] print(f"取得した臨床試験数: {len(df)}") # DataFrameを辞書のリストに変換 data_list = df.to_dict('records') except Exception as e: print(f"臨床試験データ取得エラー: {e}") return [] # AI評価の実行(最大10件まで) evaluation_limit = min(len(data_list), 10) print(f"AI評価実行: {evaluation_limit} 件を処理します") for i, item in enumerate(data_list[:evaluation_limit]): try: print(f"評価中 ({i+1}/{evaluation_limit}): {item['NCTID']}") target_criteria = item['Eligibility Criteria'] # エラーハンドリング付きで評価実行 agent_judgment = evaluate_with_retry(CriteriaCheckAgent, target_criteria, ex_question) agent_grade = evaluate_grade_with_retry(grader_agent, agent_judgment) # データの更新 item['AgentJudgment'] = agent_judgment item['AgentGrade'] = agent_grade except Exception as e: print(f"NCTID {item['NCTID']} の評価中にエラー: {e}") item['AgentJudgment'] = f"エラー: {str(e)}" item['AgentGrade'] = "unclear" # 評価されなかった残りのアイテムにはプレースホルダーを設定 for item in data_list[evaluation_limit:]: item['AgentJudgment'] = f"完全版:{age}歳{sex}の{tumor_type}患者(評価制限により未処理)" item['AgentGrade'] = "unclear" print(f"完全版評価完了。結果: {len(data_list)} 件(うち{evaluation_limit}件をAI評価)") return data_list except Exception as e: print(f"完全版データ生成中に予期しないエラー: {e}") traceback.print_exc() return [] # HTMLテーブル生成関数 def create_html_table(data, show_grade=True): """データをHTMLテーブルに変換""" if not data: return "
NCTID | ' if show_grade: html += 'Grade | ' html += 'Title | ' if show_grade: html += 'AI Judgment | ' html += 'Japanese Locations | ' html += 'Completion Date | ' html += 'Cancer Type | ' html += '
---|---|---|---|---|---|---|
{nctid} | ' # Grade if show_grade: grade_emoji = {'yes': '✅', 'no': '❌', 'unclear': '❓'}.get(grade, '❓') html += f'{grade_emoji} {grade} | ' # Title title = item.get('Title', '').strip() html += f'{title} | ' # AI Judgment if show_grade: judgment = item.get('AgentJudgment', '').strip() html += f'{judgment} | ' # Japanese Locations locations = item.get('Japanes Locations', '').strip() html += f'{locations} | ' # Completion Date completion_date = item.get('Primary Completion Date', '').strip() html += f'{completion_date} | ' # Cancer Type cancer = item.get('Cancer', '').strip() html += f'{cancer} | ' html += '