|
|
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
"""
|
|
|
|
|
|
LLM 投資建議服務測試腳本
|
|
|
|
|
|
|
|
|
|
|
|
用於測試 LLM 服務是否正常工作
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
|
import sys
|
|
|
|
|
|
import json
|
|
|
|
|
|
from unittest.mock import Mock, patch
|
|
|
|
|
|
|
|
|
|
|
|
# 添加專案根目錄到 Python 路徑
|
|
|
|
|
|
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
|
|
|
|
|
|
|
|
|
|
|
from llm_service import LLMInvestmentAdvisor, get_llm_advisor
|
|
|
|
|
|
from prompts.investment_advice import get_comprehensive_analysis_prompt
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_llm_service_initialization():
|
|
|
|
|
|
"""測試 LLM 服務初始化"""
|
|
|
|
|
|
print("🔍 測試 LLM 服務初始化...")
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 測試沒有 API 金鑰的情況
|
|
|
|
|
|
try:
|
|
|
|
|
|
advisor = LLMInvestmentAdvisor(api_key=None)
|
|
|
|
|
|
print("❌ 應該拋出錯誤但沒有拋出")
|
|
|
|
|
|
return False
|
|
|
|
|
|
except ValueError as e:
|
|
|
|
|
|
print(f"✅ 正確拋出錯誤:{e}")
|
|
|
|
|
|
|
|
|
|
|
|
# 測試有假 API 金鑰的情況
|
|
|
|
|
|
fake_api_key = "sk-fake-key-for-testing"
|
|
|
|
|
|
try:
|
|
|
|
|
|
advisor = LLMInvestmentAdvisor(api_key=fake_api_key)
|
|
|
|
|
|
print("✅ 成功初始化 LLM 服務")
|
|
|
|
|
|
return True
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print(f"❌ 初始化失敗:{e}")
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print(f"❌ 測試失敗:{e}")
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_prompt_generation():
|
|
|
|
|
|
"""測試 Prompt 生成"""
|
|
|
|
|
|
print("\n🔍 測試 Prompt 生成...")
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 模擬策略資料
|
|
|
|
|
|
mock_strategy_data = {
|
|
|
|
|
|
'id': 123,
|
|
|
|
|
|
'name': '測試策略',
|
|
|
|
|
|
'role': '最大化夏普比率',
|
|
|
|
|
|
'annual_ret': 0.15,
|
|
|
|
|
|
'vol': 0.20,
|
|
|
|
|
|
'annual_sr': 1.8,
|
|
|
|
|
|
'mdd': -0.12,
|
|
|
|
|
|
'alpha': 0.08,
|
|
|
|
|
|
'beta': 0.95,
|
|
|
|
|
|
'var10': 0.05,
|
|
|
|
|
|
'r2': 0.85,
|
|
|
|
|
|
'gamma': 2.5,
|
|
|
|
|
|
'assets': ['AAPL', 'GOOGL', 'MSFT'],
|
|
|
|
|
|
'tw': True
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
prompt = get_comprehensive_analysis_prompt(mock_strategy_data)
|
|
|
|
|
|
|
|
|
|
|
|
if len(prompt) > 100 and '策略名稱:測試策略' in prompt:
|
|
|
|
|
|
print("✅ Prompt 生成成功")
|
|
|
|
|
|
print(f"📝 Prompt 長度:{len(prompt)} 字符")
|
|
|
|
|
|
return True
|
|
|
|
|
|
else:
|
|
|
|
|
|
print("❌ Prompt 生成格式錯誤")
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print(f"❌ Prompt 生成失敗:{e}")
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_fallback_advice():
|
|
|
|
|
|
"""測試 Fallback 建議"""
|
|
|
|
|
|
print("\n🔍 測試 Fallback 建議...")
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
advisor = LLMInvestmentAdvisor(api_key="fake-key")
|
|
|
|
|
|
|
|
|
|
|
|
# 模擬會失敗的策略資料
|
|
|
|
|
|
strategy_data = {
|
|
|
|
|
|
'annual_ret': 0.25,
|
|
|
|
|
|
'vol': 0.15,
|
|
|
|
|
|
'annual_sr': 2.1
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fallback_advice = advisor._get_fallback_advice(strategy_data)
|
|
|
|
|
|
|
|
|
|
|
|
if len(fallback_advice) > 100 and '年化報酬率:25.00%' in fallback_advice:
|
|
|
|
|
|
print("✅ Fallback 建議生成成功")
|
|
|
|
|
|
return True
|
|
|
|
|
|
else:
|
|
|
|
|
|
print("❌ Fallback 建議格式錯誤")
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print(f"❌ Fallback 測試失敗:{e}")
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_api_format():
|
|
|
|
|
|
"""測試 API 回應格式"""
|
|
|
|
|
|
print("\n🔍 測試 API 回應格式...")
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 模擬成功的 API 回應
|
|
|
|
|
|
mock_response = {
|
|
|
|
|
|
'success': True,
|
|
|
|
|
|
'advice': '這是一個測試建議。年化報酬率表現良好,建議持續監控市場變化。',
|
|
|
|
|
|
'strategy_id': 123
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if all(key in mock_response for key in ['success', 'advice', 'strategy_id']):
|
|
|
|
|
|
print("✅ API 回應格式正確")
|
|
|
|
|
|
return True
|
|
|
|
|
|
else:
|
|
|
|
|
|
print("❌ API 回應格式錯誤")
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print(f"❌ API 格式測試失敗:{e}")
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_error_format():
|
|
|
|
|
|
"""測試錯誤回應格式"""
|
|
|
|
|
|
print("\n🔍 測試錯誤回應格式...")
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 模擬錯誤回應
|
|
|
|
|
|
mock_error_response = {
|
|
|
|
|
|
'success': False,
|
|
|
|
|
|
'error': 'API 金鑰無效',
|
|
|
|
|
|
'details': '請檢查 OpenAI API 金鑰設定'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if all(key in mock_error_response for key in ['success', 'error']):
|
|
|
|
|
|
print("✅ 錯誤回應格式正確")
|
|
|
|
|
|
return True
|
|
|
|
|
|
else:
|
|
|
|
|
|
print("❌ 錯誤回應格式錯誤")
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print(f"❌ 錯誤格式測試失敗:{e}")
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
|
|
"""主測試函數"""
|
|
|
|
|
|
print("🚀 開始測試 LLM 投資建議服務\n")
|
|
|
|
|
|
|
|
|
|
|
|
tests = [
|
|
|
|
|
|
("LLM 服務初始化", test_llm_service_initialization),
|
|
|
|
|
|
("Prompt 生成", test_prompt_generation),
|
|
|
|
|
|
("Fallback 建議", test_fallback_advice),
|
|
|
|
|
|
("API 回應格式", test_api_format),
|
|
|
|
|
|
("錯誤回應格式", test_error_format),
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
passed = 0
|
|
|
|
|
|
total = len(tests)
|
|
|
|
|
|
|
|
|
|
|
|
for test_name, test_func in tests:
|
|
|
|
|
|
if test_func():
|
|
|
|
|
|
passed += 1
|
|
|
|
|
|
print()
|
|
|
|
|
|
|
|
|
|
|
|
print("📊 測試結果摘要")
|
|
|
|
|
|
print(f"✅ 通過:{passed}/{total}")
|
|
|
|
|
|
print(f"❌ 失敗:{total - passed}/{total}")
|
|
|
|
|
|
|
|
|
|
|
|
if passed == total:
|
|
|
|
|
|
print("🎉 所有測試通過!LLM 服務準備就緒。")
|
|
|
|
|
|
return 0
|
|
|
|
|
|
else:
|
|
|
|
|
|
print("⚠️ 部分測試失敗,請檢查配置。")
|
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
|
exit(main())
|