TsungChihTsai commited on
Commit
d8a89db
·
verified ·
1 Parent(s): 7827041

Upload moremodels2.py

Browse files
Files changed (1) hide show
  1. moremodels2.py +261 -0
moremodels2.py ADDED
@@ -0,0 +1,261 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ import json # 雖然 requests 會處理 json,但保留導入並無害
3
+ import time
4
+ from datetime import datetime
5
+ import difflib
6
+
7
+ # 設定 Ollama API 的基礎 URL
8
+ OLLAMA_BASE_URL = "http://localhost:11434/api"
9
+ OLLAMA_GENERATE_URL = f"{OLLAMA_BASE_URL}/generate"
10
+ OLLAMA_TAGS_URL = f"{OLLAMA_BASE_URL}/tags" # 用於獲取模型列表的 API 端點
11
+
12
+ # 不再需要硬性指定模型列表
13
+ # MODELS = ["TaiwanPro-r", "gemmapro", "gemmapro-r", "gemmapro-20kctx"]
14
+
15
+ def get_available_models():
16
+ """向 Ollama API 查詢本機可用的模型列表"""
17
+ print("[查詢] 正在獲取本機可用的 Ollama 模型...")
18
+ try:
19
+ response = requests.get(OLLAMA_TAGS_URL)
20
+ response.raise_for_status() # 檢查 HTTP 請求是否成功
21
+ data = response.json()
22
+ # 從回應中提取模型名稱列表
23
+ # API 回應格式通常是 {"models": [{"name": "model1:tag", ...}, ...]}
24
+ if "models" in data and isinstance(data["models"], list):
25
+ model_names = [model.get("name") for model in data["models"] if model.get("name")]
26
+ if not model_names:
27
+ print("[警告] 未在本機找到任何 Ollama 模型。請確認已安裝並載入模型。")
28
+ return []
29
+ print(f"[成功] 找到可用的模型: {', '.join(model_names)}")
30
+ return model_names
31
+ else:
32
+ print("[錯誤] 無法從 Ollama API 回應中解析模型列表。回應格式可能已變更。")
33
+ print(f"原始回應: {data}")
34
+ return []
35
+ except requests.exceptions.ConnectionError:
36
+ print(f"[錯誤] 無法連接到 Ollama 服務於 {OLLAMA_BASE_URL}。請確認 Ollama 正在運行。")
37
+ return None # 返回 None 表示連接失敗
38
+ except requests.exceptions.RequestException as e:
39
+ print(f"[錯誤] 查詢可用模型時發生錯誤: {e}")
40
+ return []
41
+ except json.JSONDecodeError:
42
+ print(f"[錯誤] 無法解析來自 {OLLAMA_TAGS_URL} 的回應 (非 JSON 格式): {response.text}")
43
+ return []
44
+
45
+ def send_request_to_ollama(prompt, model):
46
+ """向指定模型發送請求並獲取回應"""
47
+ data = {
48
+ "model": model,
49
+ "prompt": prompt,
50
+ "stream": False
51
+ }
52
+
53
+ try:
54
+ # 使用定義好的 generate URL
55
+ response = requests.post(OLLAMA_GENERATE_URL, json=data)
56
+ response.raise_for_status() # 檢查 HTTP 請求是否成功 (狀態碼 2xx)
57
+ return response.json()["response"]
58
+ except requests.exceptions.RequestException as e:
59
+ print(f"[錯誤] 模型 {model} 請求失敗: {e}")
60
+ # 如果是因為模型不存在 (404 Not Found),提供更具體的提示
61
+ if response is not None and response.status_code == 404:
62
+ print(f" 提示:模型 '{model}' 可能未完全下載或不存在於 Ollama 中。")
63
+ return f"[錯誤] 模型 '{model}' 未找到或不可用。"
64
+ return f"[錯誤] 向 {model} 發送請求時發生錯誤: {str(e)}" # 提供更明確的錯誤訊息
65
+ except KeyError:
66
+ print(f"[錯誤] 模型 {model} 回應格式不符預期,找不到 'response' 鍵。")
67
+ return f"[錯誤] 模型 {model} 回應格式錯誤。"
68
+ except json.JSONDecodeError:
69
+ print(f"[錯誤] 模型 {model} 回應非有效的 JSON 格式: {response.text}")
70
+ return f"[錯誤] 無法解析來自 {model} 的回應。"
71
+
72
+ def initialize_markdown_file(models_to_run):
73
+ """初始化 Markdown 報告檔案,包含 YAML metadata"""
74
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
75
+ metadata = {
76
+ "title": "多模型推理彙整報告 (自動偵測可用模型)",
77
+ "date": timestamp,
78
+ "models_queried": models_to_run, # 使用實際查詢的模型列表
79
+ "author": "自動化程式",
80
+ "description": "本報告整合多個本機可用的模型對多個問題的回應,進行去蕪存菁後的彙整。"
81
+ }
82
+
83
+ try: # 增加檔案操作的錯誤處理
84
+ with open("output_moremodels_auto.md", "w", encoding="utf-8") as file: # 更改檔案名以區分
85
+ file.write("---\n")
86
+ for key, value in metadata.items():
87
+ if isinstance(value, list):
88
+ file.write(f"{key}:\n")
89
+ for item in value:
90
+ file.write(f" - {item}\n") # 標準 YAML 列表縮排
91
+ else:
92
+ file.write(f"{key}: {value}\n")
93
+ file.write("---\n\n")
94
+ file.write(f"# {metadata['title']}\n\n")
95
+ file.write(f"產出時間: {timestamp}\n\n")
96
+ if models_to_run: # 只有在找到模型時才寫入
97
+ file.write(f"使用模型: {', '.join(models_to_run)}\n\n---\n\n")
98
+ else:
99
+ file.write("未找到可用的模型進行查詢。\n\n---\n\n")
100
+ print("[初始化] 已建立 output_moremodels_auto.md")
101
+ return True
102
+ except IOError as e:
103
+ print(f"[錯���] 無法寫入檔案 output_moremodels_auto.md: {e}")
104
+ return False # 返回 False 表示初始化失敗
105
+
106
+ def append_to_markdown(index, prompt, responses):
107
+ """將問題與各模型回應結果寫入 Markdown 檔案"""
108
+ try: # 增加檔案操作的錯誤處理
109
+ with open("output_moremodels_auto.md", "a", encoding="utf-8") as file:
110
+ file.write(f"## 問題 {index}\n\n")
111
+ file.write(f"### 提問\n\n```\n{prompt}\n```\n\n")
112
+
113
+ if not responses:
114
+ file.write("沒有任何模型提供回應。\n\n")
115
+ else:
116
+ for model, response in responses.items():
117
+ # 確保 response 是字串,避免後續處理錯誤
118
+ response_text = str(response) if response is not None else "[無回應]"
119
+ file.write(f"### 模型:{model}\n\n{response_text.strip()}\n\n")
120
+
121
+ # 自動生成摘要彙整
122
+ # 確保將有效的回應傳遞給摘要函數
123
+ valid_responses = {m: r for m, r in responses.items() if isinstance(r, str) and not r.startswith("[錯誤]")}
124
+ summary = summarize_responses(prompt, valid_responses) # 傳遞有效的回應字典
125
+ file.write(f"### 彙整摘要\n\n{summary}\n\n")
126
+
127
+ file.write("---\n\n")
128
+ except IOError as e:
129
+ print(f"[錯誤] 無法附加內容至檔案 output_moremodels_auto.md: {e}")
130
+
131
+
132
+ def summarize_responses(prompt, responses):
133
+ """
134
+ 將多個模型的回應進行比較,提取相似的句子,並整合成通順的摘要。
135
+ (此函數邏輯保持不變)
136
+ """
137
+ # 如果沒有有效的回應,直接返回提示訊息
138
+ if not responses:
139
+ return "沒有從任何模型收到有效回應可供摘要。"
140
+
141
+ # 將每個回應分句
142
+ sentence_lists = []
143
+ model_names = list(responses.keys()) # 記錄模型名稱順序
144
+ for response in responses.values():
145
+ # 更穩健的分句方式,處理不同結尾符號和換行
146
+ processed_response = response.replace('\n', ' ')
147
+ sentences = []
148
+ current_sentence = ""
149
+ for char in processed_response:
150
+ current_sentence += char
151
+ if char in ['。', '!', '?', '.', '!', '?']:
152
+ trimmed_sentence = current_sentence.strip()
153
+ if trimmed_sentence:
154
+ sentences.append(trimmed_sentence)
155
+ current_sentence = ""
156
+ trimmed_sentence = current_sentence.strip()
157
+ if trimmed_sentence:
158
+ sentences.append(trimmed_sentence)
159
+ sentence_lists.append(sentences)
160
+
161
+ processed_indices = set()
162
+ summary_sentences = []
163
+ # 將模型索引與句子索引和句子本身綁定
164
+ all_sentences = [(i, j, sent) for i, lst in enumerate(sentence_lists) for j, sent in enumerate(lst)]
165
+
166
+ for idx1 in range(len(all_sentences)):
167
+ model_idx1, sent_idx1, sent1 = all_sentences[idx1]
168
+ if (model_idx1, sent_idx1) in processed_indices:
169
+ continue
170
+
171
+ best_match = None
172
+ max_similarity = 0.7
173
+
174
+ for idx2 in range(idx1 + 1, len(all_sentences)):
175
+ model_idx2, sent_idx2, sent2 = all_sentences[idx2]
176
+ if model_idx1 == model_idx2: # 確保是不同模型的回應
177
+ continue
178
+ if (model_idx2, sent_idx2) in processed_indices:
179
+ continue
180
+
181
+ similarity = difflib.SequenceMatcher(None, sent1, sent2).ratio()
182
+ if similarity > max_similarity:
183
+ max_similarity = similarity
184
+ chosen_sentence = sent1 if len(sent1) <= len(sent2) else sent2
185
+ best_match = ((model_idx1, sent_idx1), (model_idx2, sent_idx2), chosen_sentence)
186
+
187
+ if best_match:
188
+ idx_pair1, idx_pair2, chosen = best_match
189
+ processed_indices.add(idx_pair1)
190
+ processed_indices.add(idx_pair2)
191
+ summary_sentences.append(chosen)
192
+
193
+ if not summary_sentences:
194
+ # 如果沒有找到足夠相似的句子,可以選擇提供各模型的簡短預覽
195
+ summary = "各模型提供了不同的觀點,未偵測到足夠相似的核心內容可供直接彙整。重點預覽如下:\n\n"
196
+ for model_index, model_name in enumerate(model_names):
197
+ response_text = responses[model_name]
198
+ preview = response_text.strip().replace('\n', ' ')[:100] # 截取前 100 字元預覽
199
+ summary += f"- **{model_name}**: {preview}...\n"
200
+ else:
201
+ summary = "綜合各模型的相似觀點,摘要如下:\n\n"
202
+ unique_summary_sentences = []
203
+ for sentence in summary_sentences:
204
+ if sentence not in unique_summary_sentences:
205
+ unique_summary_sentences.append(sentence)
206
+ for sentence in unique_summary_sentences:
207
+ summary += f"- {sentence}\n"
208
+
209
+ return summary
210
+
211
+ def main():
212
+ # 1. 獲取可用的模型
213
+ available_models = get_available_models()
214
+
215
+ # 如果無法連���到 Ollama 或找不到模型,則終止程式
216
+ if available_models is None: # 連接失敗
217
+ print("[終止] 無法執行,因無法連接到 Ollama 服務。")
218
+ return
219
+ if not available_models: # 找到服務但沒有模型
220
+ print("[終止] 無法執行,因未找到任何可用的模型。")
221
+ # 仍嘗試初始化文件以記錄情況
222
+ initialize_markdown_file([])
223
+ return
224
+
225
+ # 可自訂多個問題
226
+ questions = [
227
+ "介紹台灣的夜市文化",
228
+ "台灣人工智慧發展的現況與挑戰為何?",
229
+ "請用繁體中文解釋什麼是大型語言模型 (LLM)。"
230
+ ]
231
+
232
+ # 2. 初始化 Markdown 文件 (使用找到的模型列表)
233
+ if not initialize_markdown_file(available_models):
234
+ print("[終止] Markdown 檔案初始化失敗,程式結束。")
235
+ return # 如果檔案無法建立,後續寫入會失敗
236
+
237
+ print("[開始] 向偵測到的模型發送請求...")
238
+
239
+ # 3. 迭代問題和可用的模型
240
+ for i, prompt in enumerate(questions, 1):
241
+ print(f"[處理中] 問題 {i}/{len(questions)}: {prompt[:30]}...")
242
+ model_responses = {}
243
+
244
+ for model in available_models: # 使用動態獲取的模型列表
245
+ print(f" └▶ 模型 {model} 推理中...")
246
+ start_time = time.time() # 記錄開始時間
247
+ response = send_request_to_ollama(prompt, model)
248
+ end_time = time.time() # 記錄結束時間
249
+ elapsed_time = end_time - start_time
250
+ print(f" 回應耗時: {elapsed_time:.2f} 秒") # 顯示每個模型的回應時間
251
+ model_responses[model] = response
252
+ # 如果需要,可以在這裡加回 time.sleep(1)
253
+
254
+ # 4. 將結果寫入 Markdown
255
+ append_to_markdown(i, prompt, model_responses)
256
+ print(f"[完成] 問題 {i} 已處理並寫入檔案。")
257
+
258
+ print(f"[完成] 所有問題已處理完畢,結果保存在 output_moremodels_auto.md")
259
+
260
+ if __name__ == "__main__":
261
+ main()