File size: 10,074 Bytes
96e9df9 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 |
import requests
import json # 雖然 requests 會處理 json,但保留導入並無害
import time
from datetime import datetime
import difflib
# 設定使用的模型名稱
MODELS = ["gemmapro", "gemmapro-r", "gemmapro-20kctx"]
OLLAMA_URL = "http://localhost:11434/api/generate"
def send_request_to_ollama(prompt, model):
"""向指定模型發送請求並獲取回應"""
data = {
"model": model,
"prompt": prompt,
"stream": False
}
try:
response = requests.post(OLLAMA_URL, json=data)
response.raise_for_status() # 檢查 HTTP 請求是否成功 (狀態碼 2xx)
return response.json()["response"]
except requests.exceptions.RequestException as e:
print(f"[錯誤] 模型 {model} 請求失敗: {e}")
return f"[錯誤] 向 {model} 發送請求時發生錯誤: {str(e)}" # 提供更明確的錯誤訊息
except KeyError:
print(f"[錯誤] 模型 {model} 回應格式不符預期,找不到 'response' 鍵。")
return f"[錯誤] 模型 {model} 回應格式錯誤。"
except json.JSONDecodeError:
print(f"[錯誤] 模型 {model} 回應非有效的 JSON 格式: {response.text}")
return f"[錯誤] 無法解析來自 {model} 的回應。"
def initialize_markdown_file():
"""初始化 Markdown 報告檔案,包含 YAML metadata"""
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
metadata = {
"title": "多模型推理彙整報告",
"date": timestamp,
"models": MODELS,
"author": "自動化程式",
"description": "本報告整合多個模型對多個問題的回應,進行去蕪存菁後的彙整。"
}
try: # 增加檔案操作的錯誤處理
with open("output_moremodels.md", "w", encoding="utf-8") as file:
file.write("---\n")
for key, value in metadata.items():
if isinstance(value, list):
file.write(f"{key}:\n")
for item in value:
file.write(f" - {item}\n") # 標準 YAML 列表縮排
else:
file.write(f"{key}: {value}\n")
file.write("---\n\n")
file.write(f"# {metadata['title']}\n\n")
file.write(f"產出時間: {timestamp}\n\n")
file.write(f"使用模型: {', '.join(MODELS)}\n\n---\n\n")
print("[初始化] 已建立 output_moremodels.md")
except IOError as e:
print(f"[錯誤] 無法寫入檔案 output_moremodels.md: {e}")
# 如果無法建立檔案,後續的 append 會失敗,可以考慮在這裡中止程式
exit(1) # 中止程式執行
def append_to_markdown(index, prompt, responses):
"""將問題與各模型回應結果寫入 Markdown 檔案"""
try: # 增加檔案操作的錯誤處理
with open("output_moremodels.md", "a", encoding="utf-8") as file:
file.write(f"## 問題 {index}\n\n")
file.write(f"### 提問\n\n```\n{prompt}\n```\n\n")
for model, response in responses.items():
# 確保 response 是字串,避免後續處理錯誤
response_text = str(response) if response is not None else "[無回應]"
file.write(f"### 模型:{model}\n\n{response_text.strip()}\n\n")
# 自動生成摘要彙整
# 確保將有效的回應傳遞給摘要函數
valid_responses = {m: r for m, r in responses.items() if isinstance(r, str)}
summary = summarize_responses(prompt, valid_responses) # 傳遞有效的回應字典
file.write(f"### 彙整摘要\n\n{summary}\n\n---\n\n")
except IOError as e:
print(f"[錯誤] 無法附加內容至檔案 output_moremodels.md: {e}")
def summarize_responses(prompt, responses):
"""
將多個模型的回應進行比較,提取相似的句子,並整合成通順的摘要。
注意:目前的實作僅基於句法相似度,可能無法完全捕捉語意。
"""
# 如果沒有有效的回應,直接返回提示訊息
if not responses:
return "沒有從任何模型收到有效回應可供摘要。"
# 將每個回應分句
sentence_lists = []
for response in responses.values():
# 更穩健的分句方式,處理不同結尾符號和換行
# 替換換行符為空格,然後用常見的句尾符號分割
processed_response = response.replace('\n', ' ')
sentences = []
current_sentence = ""
for char in processed_response:
current_sentence += char
if char in ['。', '!', '?', '.', '!', '?']: # 包含中英文句尾符號
trimmed_sentence = current_sentence.strip()
if trimmed_sentence: # 確保不是空字串
sentences.append(trimmed_sentence)
current_sentence = ""
# 加入最後一句(如果有的話且沒有結尾符號)
trimmed_sentence = current_sentence.strip()
if trimmed_sentence:
sentences.append(trimmed_sentence)
sentence_lists.append(sentences)
# 建立一個集合來儲存已處理的句子索引,避免重複處理同一個句子
processed_indices = set()
summary_sentences = []
all_sentences = [(i, j, sent) for i, lst in enumerate(sentence_lists) for j, sent in enumerate(lst)]
# 比較所有句子對的相似度
for idx1 in range(len(all_sentences)):
model_idx1, sent_idx1, sent1 = all_sentences[idx1]
# 如果這個句子已經被處理過(作為相似對的一部分),則跳過
if (model_idx1, sent_idx1) in processed_indices:
continue
best_match = None
max_similarity = 0.7 # 設定一個基礎閾值 (可調整)
for idx2 in range(idx1 + 1, len(all_sentences)):
model_idx2, sent_idx2, sent2 = all_sentences[idx2]
# 確保比較的是不同模型的回應中的句子
if model_idx1 == model_idx2:
continue
# 如果第二個句子也處理過了,跳過
if (model_idx2, sent_idx2) in processed_indices:
continue
similarity = difflib.SequenceMatcher(None, sent1, sent2).ratio()
# 找尋最高相似度且高於閾值的句子
if similarity > max_similarity:
max_similarity = similarity
# 選擇較短的句子作為代表
chosen_sentence = sent1 if len(sent1) <= len(sent2) else sent2
best_match = ((model_idx1, sent_idx1), (model_idx2, sent_idx2), chosen_sentence)
# 如果找到了相似度高的句子對
if best_match:
idx_pair1, idx_pair2, chosen = best_match
# 將這對句子都標記為已處理
processed_indices.add(idx_pair1)
processed_indices.add(idx_pair2)
# 加入摘要列表
summary_sentences.append(chosen)
# 處理剩下的、沒有找到高相似度配對的句子(可以選擇性加入)
# 這裡可以加入邏輯來包含那些獨特但可能重要的句子
# unique_sentences = []
# for i, lst in enumerate(sentence_lists):
# for j, sent in enumerate(lst):
# if (i, j) not in processed_indices:
# unique_sentences.append(sent)
# # 可以選擇將 unique_sentences 加入摘要,或另外呈現
# 格式化輸出
if not summary_sentences:
summary = "各模型提供了不同的觀點,未偵測到足夠相似的核心內容可供直接彙整。重點預覽如下:\n\n"
for model, response in responses.items():
preview = response.strip().replace('\n', ' ')[:100] # 截取前 100 字元預覽
summary += f"- **{model}**: {preview}...\n"
else:
summary = "綜合各模型的相似觀點,摘要如下:\n\n"
# 可以稍微整理一下摘要句子的順序或進行潤飾
# 目前直接列出
unique_summary_sentences = []
for sentence in summary_sentences:
if sentence not in unique_summary_sentences: # 再次去重,以防萬一
unique_summary_sentences.append(sentence)
for sentence in unique_summary_sentences:
summary += f"- {sentence}\n" # 自動加上結尾句號(如果需要)或保留原樣
# 注意:這裡 prompt 參數沒有被使用,如果未來摘要邏輯需要參考原始問題,可以加入
return summary
def main():
# 可自訂多個問題
questions = [
"介紹台灣的夜市文化",
"台灣人工智慧發展的現況與挑戰為何?"
]
initialize_markdown_file() # 如果這裡失敗,程式會中止
print("[開始] 向模型發送請求...")
for i, prompt in enumerate(questions, 1):
print(f"[處理中] 問題 {i}/{len(questions)}: {prompt[:30]}...")
model_responses = {}
for model in MODELS:
print(f" └▶ 模型 {model} 推理中...")
start_time = time.time() # 記錄開始時間
response = send_request_to_ollama(prompt, model)
end_time = time.time() # 記錄結束時間
elapsed_time = end_time - start_time
print(f" 回應耗時: {elapsed_time:.2f} 秒") # 顯示每個模型的回應時間
model_responses[model] = response
# time.sleep(1) # 根據需要調整延遲,如果 Ollama 伺服器負載不高可以縮短或移除
append_to_markdown(i, prompt, model_responses)
print(f"[完成] 問題 {i} 已處理並寫入檔案。")
print("[完成] 所有問題已處理完畢,結果保存在 output_moremodels.md")
if __name__ == "__main__":
main() |