高橋慧 commited on
Commit
5e2744f
·
1 Parent(s): 7a3c9e9
Files changed (4) hide show
  1. OpenAITools/__init__.py +29 -0
  2. README.md +83 -17
  3. app.py +382 -193
  4. requirements.txt +28 -3
OpenAITools/__init__.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # OpenAITools package
2
+ # 臨床試験適格性評価システム用のAIツール群
3
+
4
+ __version__ = "1.0.0"
5
+ __author__ = "ClinicalTrialV2 Team"
6
+
7
+ # 主要クラスのインポート
8
+ try:
9
+ from .FetchTools import fetch_clinical_trials, fetch_clinical_trials_jp
10
+ from .CrinicalTrialTools import (
11
+ SimpleClinicalTrialAgent,
12
+ GraderAgent,
13
+ LLMTranslator,
14
+ generate_ex_question_English,
15
+ generate_ex_question
16
+ )
17
+ print("✅ OpenAITools モジュールが正常にロードされました")
18
+ except ImportError as e:
19
+ print(f"⚠️ OpenAITools モジュールの一部でインポートエラー: {e}")
20
+
21
+ __all__ = [
22
+ 'fetch_clinical_trials',
23
+ 'fetch_clinical_trials_jp',
24
+ 'SimpleClinicalTrialAgent',
25
+ 'GraderAgent',
26
+ 'LLMTranslator',
27
+ 'generate_ex_question_English',
28
+ 'generate_ex_question'
29
+ ]
README.md CHANGED
@@ -10,31 +10,97 @@ pinned: false
10
  license: mit
11
  ---
12
 
13
- # 臨床試験適格性評価システム
14
 
15
  このアプリケーションは患者情報に基づいて適切な臨床試験を見つけ、AIエージェントが適格性を自動評価するシステムです。
16
 
17
- ## 機能
18
 
19
- - 患者情報入力(年齢、性別、腫瘍タイプ等)
20
- - 臨床試験データの自動検索
21
- - AI による適格性評価
22
- - 結果のフィルタリング・エクスポート
23
 
24
- ## 必要な環境変数
 
 
 
25
 
26
- **Settings Variables and secrets** で以下を設定してください:
 
 
 
27
 
28
- - `GROQ_API_KEY`: GroqのAPIキー(必須)
29
- - `OPENAI_API_KEY`: OpenAIのAPIキー(オプション)
30
 
31
- ## 使用方法
 
32
 
33
- 1. 患者の基本情報(年齢、性別、腫瘍タイプ)を入力
34
- 2. 遺伝子変異情報、測定可能腫瘍の有無を選択
35
- 3. 「Generate Sample Data」をクリック
36
- 4. 結果をフィルタリング(Eligible/Ineligible/Unclear)
37
 
38
- ## 現在のバージョン
 
 
39
 
40
- これは軽量版です。完全版では実際の臨床試験データベースからリアルタイム検索を行います。
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  license: mit
11
  ---
12
 
13
+ # 🏥 臨床試験適格性評価システム(完全版)
14
 
15
  このアプリケーションは患者情報に基づいて適切な臨床試験を見つけ、AIエージェントが適格性を自動評価するシステムです。
16
 
17
+ ## ✨ 主要機能
18
 
19
+ ### 🔍 リアルタイム臨床試験検索
20
+ - **ClinicalTrials.gov API**: 日本で実施中の最新臨床試験を自動検索
21
+ - **多言語対応**: 日本語入力 → 英語検索 → 日本語結果
22
+ - **条件絞り込み**: 腫瘍タイプ、募集状況、実施地域での自動フィルタリング
23
 
24
+ ### 🤖 AI適格性評価システム
25
+ - **LangChain + Groq**: 高速で正確な自然言語処理
26
+ - **3段階評価**: Yes(適格)/ No(不適格)/ Unclear(要検討)
27
+ - **詳細判断理由**: AI が各試験への適格性を詳細に説明
28
 
29
+ ### 💾 データ管理機能
30
+ - **インタラクティブフィルタリング**: 適格性レベルでの結果絞り込み
31
+ - **CSV エクスポート**: フィルタ済み/全データのダウンロード
32
+ - **リアルタイム更新**: 最新の臨床試験情報を常時取得
33
 
34
+ ## 🚀 使用方法
 
35
 
36
+ ### 1. 環境設定
37
+ **Settings → Variables and secrets** で以下のAPIキーを設定:
38
 
39
+ ```
40
+ GROQ_API_KEY: あなたのGroq APIキー(必須)
41
+ OPENAI_API_KEY: あなたのOpenAI APIキー(オプション)
42
+ ```
43
 
44
+ ### 2. 患者情報入力
45
+ - **基本情報**: 年齢、性別、腫瘍タイプ
46
+ - **詳細情報**: 遺伝子変異、測定可能腫瘍、生検可能性
47
 
48
+ ### 3. 検索・評価実行
49
+ - 「🔍 Generate Clinical Trials Data (Real API)」をクリック
50
+ - AIが自動的に適格性を評価
51
+
52
+ ### 4. 結果の活用
53
+ - **フィルタリング**: ✅適格 / ❌不適格 / ❓要検討 別に表示
54
+ - **詳細確認**: 各試験の判断理由を確認
55
+ - **データ保存**: 必要に応じてCSVでダウンロード
56
+
57
+ ## 🛠️ 技術スタック
58
+
59
+ ### フロントエンド
60
+ - **Gradio 4.36.1**: 直感的なWebインターフェース
61
+ - **Pandas**: データ処理・表示
62
+
63
+ ### AI/機械学習
64
+ - **LangChain**: AIエージェント構築フレームワーク
65
+ - **Groq API**: 高速LLM推論(Llama3-70B)
66
+ - **OpenAI API**: 補完的なLLM機能(オプション)
67
+
68
+ ### データソース
69
+ - **ClinicalTrials.gov API**: 国際的な臨床試験データベース
70
+ - **リアルタイム検索**: 最新の募集状況を反映
71
+
72
+ ## 📊 システム動作モード
73
+
74
+ ### ✅ 完全版モード(API連携有効)
75
+ - 環境変数が正しく設定されている場合
76
+ - 実際のClinicalTrials.govからリアルタイムデータ取得
77
+ - AI による適格性自動評価
78
+
79
+ ### ⚠️ 制限モード(API制限あり)
80
+ - 一部の環境変数が未設定の場合
81
+ - 基本的な検索機能のみ利用可能
82
+
83
+ ### 🔧 軽量版モード(サンプルデータ)
84
+ - 依存関係エラーが発生した場合
85
+ - サンプルデータでの機能デモンストレーション
86
+
87
+ ## 🔐 プライバシー・セキュリティ
88
+
89
+ - **患者情報**: ローカル処理のみ、サーバーに保存されません
90
+ - **API通信**: HTTPS暗号化通信
91
+ - **データ匿名化**: 個人識別情報は使用されません
92
+
93
+ ## 📝 ライセンス
94
+
95
+ MIT License - 学術・商用利用可能
96
+
97
+ ## 🙋‍♂️ サポート
98
+
99
+ 問題が発生した場合:
100
+ 1. **ログ確認**: Spaces の Logs タブでエラー詳細を確認
101
+ 2. **環境変数**: API キーが正しく設定されているか確認
102
+ 3. **リフレッシュ**: ページを再読み込みしてリトライ
103
+
104
+ ---
105
+
106
+ *このシステムは研究・教育目的で開発されています。実際の臨床決定には専門医にご相談ください。*
app.py CHANGED
@@ -1,6 +1,22 @@
1
  import gradio as gr
 
 
 
2
  import os
3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  # 環境変数チェック
5
  def check_environment():
6
  """環境変数をチェックし、不足している場合は警告"""
@@ -12,220 +28,393 @@ def check_environment():
12
  if not os.getenv("OPENAI_API_KEY"):
13
  missing_vars.append("OPENAI_API_KEY")
14
 
15
- return missing_vars
 
 
 
 
16
 
17
- # サンプルデータ生成(辞書のリスト形式)
18
- def generate_sample_data(age, sex, tumor_type, gene_mutation, measurable, biopsiable):
19
- """サンプルデータを生成(pandas無し版)"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  try:
21
  # 入力検証
22
  if not all([age, sex, tumor_type]):
23
- return []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
 
25
- # サンプルデータの作成
26
- sample_data = [
27
- {
28
- 'NCTID': 'NCT12345678',
29
- 'AgentGrade': 'yes',
30
- 'Title': f'Clinical Trial for {tumor_type} in {sex} patients',
31
- 'AgentJudgment': f'{age}歳{sex}の{tumor_type}患者は参加可能です',
32
- 'Japanese_Locations': 'Tokyo',
33
- 'Cancer': tumor_type,
34
- 'Gene_Mutation': gene_mutation,
35
- 'Measurable': measurable,
36
- 'Biopsiable': biopsiable
37
- },
38
- {
39
- 'NCTID': 'NCT87654321',
40
- 'AgentGrade': 'no',
41
- 'Title': f'Alternative treatment for {tumor_type}',
42
- 'AgentJudgment': f'{age}歳{sex}の{tumor_type}患者は参加できません',
43
- 'Japanese_Locations': 'Osaka',
44
- 'Cancer': tumor_type,
45
- 'Gene_Mutation': gene_mutation,
46
- 'Measurable': measurable,
47
- 'Biopsiable': biopsiable
48
- },
49
- {
50
- 'NCTID': 'NCT11111111',
51
- 'AgentGrade': 'unclear',
52
- 'Title': f'Experimental therapy for {tumor_type} with {gene_mutation}',
53
- 'AgentJudgment': f'{age}歳{sex}の{tumor_type}患者の参加は不明確です',
54
- 'Japanese_Locations': 'Kyoto',
55
- 'Cancer': tumor_type,
56
- 'Gene_Mutation': gene_mutation,
57
- 'Measurable': measurable,
58
- 'Biopsiable': biopsiable
59
- }
60
- ]
61
 
62
- return sample_data
 
63
 
64
  except Exception as e:
65
- print(f"データ生成エラー: {e}")
66
- return []
 
67
 
68
- # フィルタリング関数
69
- def filter_data(data, grade):
70
- """データをフィルタリング"""
71
- if not data:
72
- return data
73
-
 
 
 
 
 
 
 
74
  try:
75
- if grade == "all":
76
- return data
77
- return [item for item in data if item.get('AgentGrade') == grade]
 
 
78
  except Exception as e:
79
- print(f"フィルタリングエラー: {e}")
80
- return data
81
-
82
- # データを表形式に変換
83
- def data_to_table(data):
84
- """データを表形式に変換"""
85
- if not data:
86
- return []
87
-
88
- # ヘッダー行
89
- headers = ['NCTID', 'Grade', 'Title', 'Judgment', 'Location', 'Cancer']
90
-
91
- # データ行
92
- rows = []
93
- for item in data:
94
- row = [
95
- item.get('NCTID', ''),
96
- item.get('AgentGrade', ''),
97
- item.get('Title', ''),
98
- item.get('AgentJudgment', ''),
99
- item.get('Japanese_Locations', ''),
100
- item.get('Cancer', '')
101
- ]
102
- rows.append(row)
103
-
104
- return [headers] + rows
105
 
106
  # Gradioインターフェースの作成
107
- def create_interface():
108
- missing_vars = check_environment()
109
 
110
- with gr.Blocks(title="臨床試験適格性評価", theme=gr.themes.Soft()) as demo:
111
- gr.Markdown("## 🏥 臨床試験適格性評価インターフェース(軽量版)")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
 
113
- # 環境変数状態の表示
114
- if not missing_vars:
115
- gr.Markdown(" **ステータス**: 全ての環境変数が設定されています")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
  else:
117
- gr.Markdown(f"⚠️ **注意**: 環境変数が不足しています: {', '.join(missing_vars)}")
118
- gr.Markdown("**Settings → Variables and secrets** で API キーを設定してください")
119
-
120
- gr.Markdown("💡 **使用方法**: 患者情報を入力して「Generate Sample Data」をクリックしてください。")
121
-
122
- # 入力フィールド
123
- with gr.Row():
124
- with gr.Column():
125
- age_input = gr.Textbox(label="Age", placeholder="例: 65", value="65")
126
- sex_input = gr.Dropdown(choices=["男性", "女性"], label="Sex", value="男性")
127
- tumor_type_input = gr.Textbox(label="Tumor Type", placeholder="例: gastric cancer", value="gastric cancer")
128
-
129
- with gr.Column():
130
- gene_mutation_input = gr.Textbox(label="Gene Mutation", placeholder="例: HER2", value="HER2")
131
- measurable_input = gr.Dropdown(choices=["有り", "無し", "不明"], label="Measurable Tumor", value="有り")
132
- biopsiable_input = gr.Dropdown(choices=["有り", "無し", "不明"], label="Biopsiable Tumor", value="有り")
133
 
134
- # 結果表示用のHTMLエリア
135
- results_html = gr.HTML(label="Clinical Trials Results")
136
-
137
- # フィルタボタン
138
- with gr.Row():
139
- generate_button = gr.Button("Generate Sample Data", variant="primary")
140
- all_button = gr.Button("Show All", variant="secondary")
141
- yes_button = gr.Button("Show Eligible", variant="secondary")
142
- no_button = gr.Button("Show Ineligible", variant="secondary")
143
- unclear_button = gr.Button("Show Unclear", variant="secondary")
144
-
145
- # 状態管理
146
- data_state = gr.State(value=[])
147
-
148
- # 結果をHTMLテーブルに変換
149
- def create_html_table(data):
150
- if not data:
151
- return "<p>データがありません</p>"
152
-
153
- html = "<table style='width:100%; border-collapse: collapse;'>"
154
- html += "<tr style='background-color: #f0f0f0;'>"
155
- html += "<th style='border: 1px solid #ddd; padding: 8px;'>NCTID</th>"
156
- html += "<th style='border: 1px solid #ddd; padding: 8px;'>Grade</th>"
157
- html += "<th style='border: 1px solid #ddd; padding: 8px;'>Title</th>"
158
- html += "<th style='border: 1px solid #ddd; padding: 8px;'>Judgment</th>"
159
- html += "<th style='border: 1px solid #ddd; padding: 8px;'>Location</th>"
160
- html += "</tr>"
161
 
162
- for item in data:
163
- grade_color = {
164
- 'yes': '#d4edda',
165
- 'no': '#f8d7da',
166
- 'unclear': '#fff3cd'
167
- }.get(item.get('AgentGrade', ''), '#ffffff')
168
 
169
- html += f"<tr style='background-color: {grade_color};'>"
170
- html += f"<td style='border: 1px solid #ddd; padding: 8px;'>{item.get('NCTID', '')}</td>"
171
- html += f"<td style='border: 1px solid #ddd; padding: 8px;'>{item.get('AgentGrade', '')}</td>"
172
- html += f"<td style='border: 1px solid #ddd; padding: 8px;'>{item.get('Title', '')}</td>"
173
- html += f"<td style='border: 1px solid #ddd; padding: 8px;'>{item.get('AgentJudgment', '')}</td>"
174
- html += f"<td style='border: 1px solid #ddd; padding: 8px;'>{item.get('Japanese_Locations', '')}</td>"
175
- html += "</tr>"
176
-
177
- html += "</table>"
178
- return html
179
-
180
- # イベントハンドリング
181
- def update_data(age, sex, tumor_type, gene_mutation, measurable, biopsiable):
182
- data = generate_sample_data(age, sex, tumor_type, gene_mutation, measurable, biopsiable)
183
- html_table = create_html_table(data)
184
- return html_table, data
185
-
186
- def filter_and_show(data, grade):
187
- filtered_data = filter_data(data, grade)
188
- html_table = create_html_table(filtered_data)
189
- return html_table
190
-
191
- # ボタンイベント
192
- generate_button.click(
193
- fn=update_data,
194
- inputs=[age_input, sex_input, tumor_type_input, gene_mutation_input, measurable_input, biopsiable_input],
195
- outputs=[results_html, data_state]
196
- )
197
-
198
- all_button.click(
199
- fn=lambda data: filter_and_show(data, "all"),
200
- inputs=[data_state],
201
- outputs=[results_html]
202
- )
203
-
204
- yes_button.click(
205
- fn=lambda data: filter_and_show(data, "yes"),
206
- inputs=[data_state],
207
- outputs=[results_html]
208
- )
209
-
210
- no_button.click(
211
- fn=lambda data: filter_and_show(data, "no"),
212
- inputs=[data_state],
213
- outputs=[results_html]
214
- )
215
 
216
- unclear_button.click(
217
- fn=lambda data: filter_and_show(data, "unclear"),
218
- inputs=[data_state],
219
- outputs=[results_html]
220
- )
 
 
 
 
221
 
222
- # フッター情報
223
- gr.Markdown("---")
224
- gr.Markdown("🔬 **技術情報**: これは軽量版です。完全版では実際のClinicalTrials.gov APIから臨床試験データを取得し、AIエージェントが適格性を自動評価します。")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
225
 
226
- return demo
 
 
227
 
228
- # アプリケーション起動
229
  if __name__ == "__main__":
230
- demo = create_interface()
231
- demo.launch()
 
 
 
 
 
 
1
  import gradio as gr
2
+ import pandas as pd
3
+ import time
4
+ import traceback
5
  import os
6
 
7
+ # 完全版のimportエラー対策
8
+ try:
9
+ from OpenAITools.FetchTools import fetch_clinical_trials
10
+ from langchain_openai import ChatOpenAI
11
+ from langchain_groq import ChatGroq
12
+ from OpenAITools.CrinicalTrialTools import SimpleClinicalTrialAgent, GraderAgent, LLMTranslator, generate_ex_question_English
13
+ FULL_VERSION = True
14
+ print("✅ 完全版モジュールが正常にロードされました")
15
+ except ImportError as e:
16
+ print(f"⚠️ 完全版モジュールのインポートに失敗: {e}")
17
+ print("軽量版モードで動作します")
18
+ FULL_VERSION = False
19
+
20
  # 環境変数チェック
21
  def check_environment():
22
  """環境変数をチェックし、不足している場合は警告"""
 
28
  if not os.getenv("OPENAI_API_KEY"):
29
  missing_vars.append("OPENAI_API_KEY")
30
 
31
+ if missing_vars:
32
+ print(f"⚠️ 環境変数が設定されていません: {', '.join(missing_vars)}")
33
+ print("一部の機能が制限される可能性があります。")
34
+
35
+ return len(missing_vars) == 0
36
 
37
+ # 環境変数チェック実行
38
+ env_ok = check_environment()
39
+
40
+ # モデルとエージェントの安全な初期化
41
+ def safe_init_agents():
42
+ """エージェントを安全に初期化"""
43
+ if not FULL_VERSION:
44
+ return None, None, None
45
+
46
+ try:
47
+ groq = ChatGroq(model_name="llama3-70b-8192", temperature=0)
48
+ translator = LLMTranslator(groq)
49
+ criteria_agent = SimpleClinicalTrialAgent(groq)
50
+ grader_agent = GraderAgent(groq)
51
+ print("✅ AIエージェントが正常に初期化されました")
52
+ return translator, criteria_agent, grader_agent
53
+ except Exception as e:
54
+ print(f"❌ エージェント初期化エラー: {e}")
55
+ return None, None, None
56
+
57
+ # エージェント初期化
58
+ translator, CriteriaCheckAgent, grader_agent = safe_init_agents()
59
+
60
+ # エラーハンドリング付きでエージェント評価を実行する関数
61
+ def evaluate_with_retry(agent, criteria, question, max_retries=3):
62
+ """エラーハンドリング付きでエージェント評価を実行"""
63
+ if agent is None:
64
+ return "評価エラー: エージェントが初期化されていません。API keyを確認してください。"
65
+
66
+ for attempt in range(max_retries):
67
+ try:
68
+ return agent.evaluate_eligibility(criteria, question)
69
+ except Exception as e:
70
+ if "missing variables" in str(e):
71
+ print(f"プロンプトテンプレートエラー (試行 {attempt + 1}/{max_retries}): {e}")
72
+ return "評価エラー: プロンプトテンプレートの設定に問題があります"
73
+ elif "no healthy upstream" in str(e) or "InternalServerError" in str(e):
74
+ print(f"Groqサーバーエラー (試行 {attempt + 1}/{max_retries}): {e}")
75
+ if attempt < max_retries - 1:
76
+ time.sleep(2)
77
+ continue
78
+ else:
79
+ return "評価エラー: サーバーに接続できませんでした"
80
+ elif "API key" in str(e) or "authentication" in str(e).lower():
81
+ return "評価エラー: API keyが無効または設定されていません"
82
+ else:
83
+ print(f"予期しないエラー (試行 {attempt + 1}/{max_retries}): {e}")
84
+ if attempt < max_retries - 1:
85
+ time.sleep(1)
86
+ continue
87
+ else:
88
+ return f"評価エラー: {str(e)}"
89
+ return "評価エラー: 最大リトライ回数に達しました"
90
+
91
+ def evaluate_grade_with_retry(agent, judgment, max_retries=3):
92
+ """エラーハンドリング付きでグレード評価を実行"""
93
+ if agent is None:
94
+ return "unclear"
95
+
96
+ for attempt in range(max_retries):
97
+ try:
98
+ return agent.evaluate_eligibility(judgment)
99
+ except Exception as e:
100
+ if "no healthy upstream" in str(e) or "InternalServerError" in str(e):
101
+ print(f"Groqサーバーエラー (グレード評価 - 試行 {attempt + 1}/{max_retries}): {e}")
102
+ if attempt < max_retries - 1:
103
+ time.sleep(2)
104
+ continue
105
+ else:
106
+ return "unclear"
107
+ elif "API key" in str(e) or "authentication" in str(e).lower():
108
+ return "unclear"
109
+ else:
110
+ print(f"予期しないエラー (グレード評価 - 試行 {attempt + 1}/{max_retries}): {e}")
111
+ if attempt < max_retries - 1:
112
+ time.sleep(1)
113
+ continue
114
+ else:
115
+ return "unclear"
116
+ return "unclear"
117
+
118
+ # 軽量版データ生成関数
119
+ def generate_sample_dataframe(age, sex, tumor_type, GeneMutation, Meseable, Biopsiable):
120
+ """サンプルデータを生成(軽量版)"""
121
+ try:
122
+ if not all([age, sex, tumor_type]):
123
+ return pd.DataFrame()
124
+
125
+ sample_data = {
126
+ 'NCTID': ['NCT12345678', 'NCT87654321', 'NCT11111111'],
127
+ 'AgentGrade': ['yes', 'no', 'unclear'],
128
+ 'Title': [
129
+ f'Clinical Trial for {tumor_type} in {sex} patients',
130
+ f'Alternative treatment for {tumor_type}',
131
+ f'Experimental therapy for {tumor_type} with {GeneMutation}'
132
+ ],
133
+ 'AgentJudgment': [
134
+ f'{age}歳{sex}の{tumor_type}患者は参加可能です。詳細な検査結果により最終判断が必要です。',
135
+ f'{age}歳{sex}の{tumor_type}患者は年齢制限により参加できません。',
136
+ f'{age}歳{sex}の{tumor_type}患者の参加は追加情報が必要で不明確です。'
137
+ ],
138
+ 'Japanes Locations': ['Tokyo, Osaka', 'Kyoto, Fukuoka', 'Nagoya, Sendai'],
139
+ 'Primary Completion Date': ['2025-12-31', '2026-06-30', '2025-09-15'],
140
+ 'Cancer': [tumor_type, tumor_type, tumor_type],
141
+ 'Summary': [
142
+ f'Phase II study evaluating new treatment for {tumor_type}',
143
+ f'Comparative study of standard vs experimental therapy for {tumor_type}',
144
+ f'Early phase trial testing combination therapy for {tumor_type}'
145
+ ],
146
+ 'Eligibility Criteria': [
147
+ f'Age 18-75, confirmed {tumor_type}, adequate organ function',
148
+ f'Age 20-65, {tumor_type} with specific mutations, ECOG 0-1',
149
+ f'Age 18-80, advanced {tumor_type}, previous treatment failure'
150
+ ]
151
+ }
152
+
153
+ return pd.DataFrame(sample_data)
154
+
155
+ except Exception as e:
156
+ print(f"サンプルデータ生成エラー: {e}")
157
+ return pd.DataFrame()
158
+
159
+ # 完全版データ生成関数
160
+ def generate_full_dataframe(age, sex, tumor_type, GeneMutation, Meseable, Biopsiable):
161
+ """完全版のデータ生成(実際のAPI使用)"""
162
  try:
163
  # 入力検証
164
  if not all([age, sex, tumor_type]):
165
+ return pd.DataFrame()
166
+
167
+ # 日本語の腫瘍タイプを英語に翻訳
168
+ try:
169
+ if translator is not None:
170
+ TumorName = translator.translate(tumor_type)
171
+ print(f"腫瘍タイプ翻訳: {tumor_type} → {TumorName}")
172
+ else:
173
+ print("翻訳エージェントが利用できません。元の値を使用します。")
174
+ TumorName = tumor_type
175
+ except Exception as e:
176
+ print(f"翻訳エラー: {e}")
177
+ TumorName = tumor_type
178
+
179
+ # 質問文を生成
180
+ try:
181
+ ex_question = generate_ex_question_English(age, sex, TumorName, GeneMutation, Meseable, Biopsiable)
182
+ print(f"生成された質問: {ex_question}")
183
+ except Exception as e:
184
+ print(f"質問生成エラー: {e}")
185
+ return pd.DataFrame()
186
+
187
+ # 臨床試験データの取得
188
+ try:
189
+ print(f"臨床試験データを検索中: {TumorName}")
190
+ df = fetch_clinical_trials(TumorName)
191
+ if df.empty:
192
+ print("臨床試験データが見つかりませんでした")
193
+ return pd.DataFrame()
194
+ print(f"取得した臨床試験数: {len(df)}")
195
+ except Exception as e:
196
+ print(f"臨床試験データ取得エラー: {e}")
197
+ return pd.DataFrame()
198
+
199
+ df['AgentJudgment'] = None
200
+ df['AgentGrade'] = None
201
+
202
+ # 臨床試験の適格性の評価
203
+ NCTIDs = list(df['NCTID'])
204
+
205
+ for i, nct_id in enumerate(NCTIDs):
206
+ try:
207
+ print(f"評価中 ({i+1}/{len(NCTIDs)}): {nct_id}")
208
+ target_criteria = df.loc[df['NCTID'] == nct_id, 'Eligibility Criteria'].values[0]
209
+
210
+ # エラーハンドリング付きで評価実行
211
+ agent_judgment = evaluate_with_retry(CriteriaCheckAgent, target_criteria, ex_question)
212
+ agent_grade = evaluate_grade_with_retry(grader_agent, agent_judgment)
213
+
214
+ # データフレームの更新
215
+ df.loc[df['NCTID'] == nct_id, 'AgentJudgment'] = agent_judgment
216
+ df.loc[df['NCTID'] == nct_id, 'AgentGrade'] = agent_grade
217
+
218
+ except Exception as e:
219
+ print(f"NCTID {nct_id} の評価中にエラー: {e}")
220
+ df.loc[df['NCTID'] == nct_id, 'AgentJudgment'] = f"エラー: {str(e)}"
221
+ df.loc[df['NCTID'] == nct_id, 'AgentGrade'] = "unclear"
222
+
223
+ # 列を指定した順に並び替え
224
+ columns_order = ['NCTID', 'AgentGrade', 'Title', 'AgentJudgment', 'Japanes Locations',
225
+ 'Primary Completion Date', 'Cancer', 'Summary', 'Eligibility Criteria']
226
 
227
+ # 存在する列のみを選択
228
+ available_columns = [col for col in columns_order if col in df.columns]
229
+ df = df[available_columns]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
 
231
+ print(f"評価完了。結果: {len(df)} 件")
232
+ return df
233
 
234
  except Exception as e:
235
+ print(f"完全版データフレーム生成中に予期しないエラー: {e}")
236
+ traceback.print_exc()
237
+ return pd.DataFrame()
238
 
239
+ # CSVとして保存しダウンロードする関数
240
+ def download_filtered_csv(df):
241
+ try:
242
+ if df is None or len(df) == 0:
243
+ return None
244
+ file_path = "filtered_data.csv"
245
+ df.to_csv(file_path, index=False)
246
+ return file_path
247
+ except Exception as e:
248
+ print(f"CSV保存エラー: {e}")
249
+ return None
250
+
251
+ def download_full_csv(df):
252
  try:
253
+ if df is None or len(df) == 0:
254
+ return None
255
+ file_path = "full_data.csv"
256
+ df.to_csv(file_path, index=False)
257
+ return file_path
258
  except Exception as e:
259
+ print(f"CSV保存エラー: {e}")
260
+ return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
261
 
262
  # Gradioインターフェースの作成
263
+ with gr.Blocks(title="臨床試験適格性評価", theme=gr.themes.Soft()) as demo:
264
+ gr.Markdown("## 🏥 臨床試験適格性評価インターフェース")
265
 
266
+ # バージョン情報と状態表示
267
+ if FULL_VERSION and env_ok:
268
+ gr.Markdown("✅ **モード**: 完全版(API連携有効)")
269
+ elif FULL_VERSION and not env_ok:
270
+ gr.Markdown("⚠️ **モード**: 完全版(API制限あり)")
271
+ else:
272
+ gr.Markdown("🔧 **モード**: 軽量版(サンプルデータ)")
273
+
274
+ # 環境変数状態の表示
275
+ if env_ok:
276
+ gr.Markdown("✅ **API Status**: 全ての環境変数が設定されています")
277
+ else:
278
+ gr.Markdown("⚠️ **API Status**: 環境変数が不足しています。Settings → Variables and secrets で設定してください")
279
+
280
+ gr.Markdown("💡 **使用方法**: 患者情報を入力してボタンをクリックしてください。完全版では実際のClinicalTrials.govからリアルタイムでデータを取得し、AIが適格性を評価します。")
281
+
282
+ # 各種入力フィールド
283
+ with gr.Row():
284
+ with gr.Column():
285
+ age_input = gr.Textbox(label="Age", placeholder="例: 65", value="65")
286
+ sex_input = gr.Dropdown(choices=["男性", "女性"], label="Sex", value="男性")
287
+ tumor_type_input = gr.Textbox(label="Tumor Type", placeholder="例: gastric cancer", value="gastric cancer")
288
 
289
+ with gr.Column():
290
+ gene_mutation_input = gr.Textbox(label="Gene Mutation", placeholder="例: HER2", value="HER2")
291
+ measurable_input = gr.Dropdown(choices=["有り", "無し", "不明"], label="Measurable Tumor", value="有り")
292
+ biopsiable_input = gr.Dropdown(choices=["有り", "無し", "不明"], label="Biopsiable Tumor", value="有り")
293
+
294
+ # データフレーム表示エリア
295
+ dataframe_output = gr.DataFrame(
296
+ label="Clinical Trials Results",
297
+ interactive=False,
298
+ wrap=True
299
+ )
300
+
301
+ # 内部状態用の非表示コンポーネント
302
+ original_df_state = gr.State(value=None)
303
+ filtered_df_state = gr.State(value=None)
304
+
305
+ # ボタン類
306
+ with gr.Row():
307
+ if FULL_VERSION and env_ok:
308
+ generate_button = gr.Button("🔍 Generate Clinical Trials Data (Real API)", variant="primary")
309
  else:
310
+ generate_button = gr.Button("📋 Generate Sample Data", variant="primary")
311
+
312
+ with gr.Row():
313
+ yes_button = gr.Button(" Show Eligible Trials", variant="secondary")
314
+ no_button = gr.Button("❌ Show Ineligible Trials", variant="secondary")
315
+ unclear_button = gr.Button("❓ Show Unclear Trials", variant="secondary")
316
+ all_button = gr.Button("📊 Show All Trials", variant="secondary")
317
+
318
+ with gr.Row():
319
+ download_filtered_button = gr.Button("💾 Download Filtered Data")
320
+ download_full_button = gr.Button("💾 Download Full Data")
321
+
322
+ # ダウンロードファイル
323
+ download_filtered_output = gr.File(label="Download Filtered Data", visible=False)
324
+ download_full_output = gr.File(label="Download Full Data", visible=False)
 
325
 
326
+ # プログレス表示
327
+ progress_text = gr.Textbox(label="Processing Status", value="Ready", interactive=False)
328
+
329
+ # イベントハンドリング
330
+ def update_dataframe_and_state(age, sex, tumor_type, gene_mutation, measurable, biopsiable):
331
+ """データフレーム生成と状態更新"""
332
+ try:
333
+ if FULL_VERSION and env_ok:
334
+ progress_text.value = "🔍 実際の臨床試験データを検索中..."
335
+ df = generate_full_dataframe(age, sex, tumor_type, gene_mutation, measurable, biopsiable)
336
+ else:
337
+ progress_text.value = "📋 サンプルデータを生成中..."
338
+ df = generate_sample_dataframe(age, sex, tumor_type, gene_mutation, measurable, biopsiable)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
339
 
340
+ if len(df) > 0:
341
+ progress_text.value = f"✅ 完了: {len(df)} 件の臨床試験が見つかりました"
342
+ else:
343
+ progress_text.value = "⚠️ 該当する臨床試験が見つかりませんでした"
 
 
344
 
345
+ return df, df, df, progress_text.value
346
+ except Exception as e:
347
+ error_msg = f" エラー: {str(e)}"
348
+ print(f"データフレーム更新エラー: {e}")
349
+ return pd.DataFrame(), pd.DataFrame(), pd.DataFrame(), error_msg
350
+
351
+ def filter_and_update(original_df, grade):
352
+ """フィルタリングと表示更新"""
353
+ if original_df is None or len(original_df) == 0:
354
+ return original_df, original_df
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
355
 
356
+ try:
357
+ if grade == "all":
358
+ df_filtered = original_df
359
+ else:
360
+ df_filtered = original_df[original_df['AgentGrade'] == grade]
361
+ return df_filtered, df_filtered
362
+ except Exception as e:
363
+ print(f"フィルタリングエラー: {e}")
364
+ return original_df, original_df
365
 
366
+ # ボタン動作の設定
367
+ generate_button.click(
368
+ fn=update_dataframe_and_state,
369
+ inputs=[age_input, sex_input, tumor_type_input, gene_mutation_input, measurable_input, biopsiable_input],
370
+ outputs=[dataframe_output, original_df_state, filtered_df_state, progress_text]
371
+ )
372
+
373
+ yes_button.click(
374
+ fn=lambda df: filter_and_update(df, "yes"),
375
+ inputs=[original_df_state],
376
+ outputs=[dataframe_output, filtered_df_state]
377
+ )
378
+
379
+ no_button.click(
380
+ fn=lambda df: filter_and_update(df, "no"),
381
+ inputs=[original_df_state],
382
+ outputs=[dataframe_output, filtered_df_state]
383
+ )
384
+
385
+ unclear_button.click(
386
+ fn=lambda df: filter_and_update(df, "unclear"),
387
+ inputs=[original_df_state],
388
+ outputs=[dataframe_output, filtered_df_state]
389
+ )
390
+
391
+ all_button.click(
392
+ fn=lambda df: filter_and_update(df, "all"),
393
+ inputs=[original_df_state],
394
+ outputs=[dataframe_output, filtered_df_state]
395
+ )
396
+
397
+ download_filtered_button.click(
398
+ fn=download_filtered_csv,
399
+ inputs=[filtered_df_state],
400
+ outputs=[download_filtered_output]
401
+ )
402
+
403
+ download_full_button.click(
404
+ fn=download_full_csv,
405
+ inputs=[original_df_state],
406
+ outputs=[download_full_output]
407
+ )
408
 
409
+ # フッター情報
410
+ gr.Markdown("---")
411
+ gr.Markdown("🔬 **技術情報**: このシステムはClinicalTrials.gov API、LangChain、およびGroq/OpenAI APIを使用しています。")
412
 
 
413
  if __name__ == "__main__":
414
+ demo.launch(
415
+ server_name="0.0.0.0",
416
+ server_port=7860,
417
+ share=False,
418
+ debug=False,
419
+ show_error=True
420
+ )
requirements.txt CHANGED
@@ -1,5 +1,30 @@
1
- gradio==4.36.1
2
- # requirements.txt を以下に変更
3
  gradio==4.36.1
4
  numpy==1.21.6
5
- pandas==1.3.5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Stage 3: 完全版 requirements.txt
 
2
  gradio==4.36.1
3
  numpy==1.21.6
4
+ pandas==1.3.5
5
+ requests==2.31.0
6
+
7
+ # LangChain ecosystem
8
+ langchain==0.1.20
9
+ langchain-community==0.0.38
10
+ langchain-core==0.1.52
11
+ langchain-openai==0.1.7
12
+ langchain-groq==0.1.5
13
+ langchain-text-splitters==0.0.1
14
+
15
+ # LLM providers
16
+ openai==1.12.0
17
+ groq==0.4.2
18
+
19
+ # Database utilities
20
+ SQLAlchemy==2.0.23
21
+
22
+ # Pydantic
23
+ pydantic==2.5.3
24
+
25
+ # Text processing
26
+ tiktoken==0.5.2
27
+
28
+ # Utilities
29
+ tenacity==8.2.3
30
+ packaging==23.0.0