Fix unhashable type error in prompt templates\n\n- Fixed syntax error in prompts/investment_advice.py: {{}} -> {}\n- Cleaned up duplicate imports in llm_service.py\n- Removed unused lru_cache import\n- All prompt templates now work correctly\n- Demo script runs successfully

data-init-fixes
Eric0801 2 months ago
parent 45e9ea7ec2
commit 5a93aa5fb4
  1. 148
      LLM_DEMO.py
  2. 11
      llm_service.py
  3. 2
      prompts/investment_advice.py

@ -0,0 +1,148 @@
#!/usr/bin/env python3
"""
LLM 投資建議功能演示
展示如何使用LLM服務生成投資建議
"""
import os
import json
from unittest.mock import Mock
# 模擬環境變數
os.environ['OPENAI_API_KEY'] = 'sk-fake-key-for-demo'
from llm_service import LLMInvestmentAdvisor
from prompts.investment_advice import get_comprehensive_analysis_prompt
def demo_strategy_data():
"""模擬策略資料"""
return {
'id': 123,
'name': '科技成長策略',
'role': '最大化夏普比率',
'annual_ret': 0.28, # 年化28%報酬
'vol': 0.22, # 年化22%波動
'annual_sr': 1.95, # 夏普比率1.95
'mdd': -0.15, # 最大回落15%
'alpha': 0.12, # Alpha 12%
'beta': 1.05, # Beta 1.05
'var10': 0.08, # 10% VaR 8%
'r2': 0.89, # R-squared 89%
'gamma': 2.8, # Gamma 2.8
'assets': ['AAPL', 'MSFT', 'GOOGL', 'NVDA', 'TSLA'],
'tw': False, # 美國市場
'weight': {
'data': [[0.25, 0.20, 0.18, 0.22, 0.15]],
'columns': ['AAPL', 'MSFT', 'GOOGL', 'NVDA', 'TSLA'],
'index': ['2024-01-01']
}
}
def demo_llm_service():
"""演示LLM服務"""
print("🤖 LLM 投資建議功能演示")
print("=" * 50)
# 模擬策略資料
strategy_data = demo_strategy_data()
print("\n📊 策略概況:")
print(f" 策略名稱:{strategy_data['name']}")
print(f" 投資目標:{strategy_data['role']}")
print(f" 年化報酬:{strategy_data['annual_ret']:.1%}")
print(f" 年化波動:{strategy_data['vol']:.1%}")
print(f" 夏普比率:{strategy_data['annual_sr']:.2f}")
print(f" 最大回落:{strategy_data['mdd']:.1%}")
print(f" 持有資產:{', '.join(strategy_data['assets'])}")
print("\n🔧 測試 Prompt 生成...")
try:
# 直接調用函數,不使用快取
prompt = get_comprehensive_analysis_prompt(strategy_data)
print(f" ✅ Prompt 生成成功,長度:{len(prompt)} 字符")
print(" 📝 Prompt 預覽:")
print(f" {prompt[:200]}...")
except Exception as e:
print(f" ❌ Prompt 生成失敗:{e}")
print(f" 錯誤詳情:{str(e)}")
return
print("\n🤖 測試 LLM 服務...")
try:
# 由於是演示,我們使用模擬的OpenAI客戶端
advisor = LLMInvestmentAdvisor(api_key="fake-key")
# 模擬生成建議
demo_advice = advisor._get_fallback_advice(strategy_data)
print(" ✅ 建議生成成功:")
print(" " + "=" * 30)
print(f" {demo_advice}")
print(" " + "=" * 30)
except Exception as e:
print(f" ❌ LLM 服務測試失敗:{e}")
print("\n📝 真實使用場景:")
print(" 1. 用戶訪問策略詳情頁面")
print(" 2. 點擊「🤖 LLM 投資建議」區塊")
print(" 3. 前端調用 /api/llm_advice/123")
print(" 4. 後端查詢策略資料並生成建議")
print(" 5. 顯示專業的投資分析報告")
print("\n 設置說明:")
print(" 1. 設置 OPENAI_API_KEY 環境變數")
print(" 2. 選擇合適的模型 (GPT-4 或 GPT-3.5)")
print(" 3. 調整 prompt 模板以符合需求")
print(" 4. 監控 API 使用量和成本")
print("\n🚀 功能特色:")
print(" ✅ 智能投資分析")
print(" ✅ 風險評估報告")
print(" ✅ 市場適配建議")
print(" ✅ 快取機制")
print(" ✅ 錯誤處理")
print(" ✅ 專業UI介面")
def demo_prompt_templates():
"""演示不同的Prompt模板"""
print("\n🔧 Prompt 模板演示")
print("=" * 30)
strategy_data = demo_strategy_data()
templates = [
("基本分析", "basic"),
("全面分析", "comprehensive"),
("風險導向", "risk"),
]
for name, template_type in templates:
print(f"\n{name}模板:")
try:
if template_type == "risk":
from prompts.investment_advice import get_risk_focused_prompt
prompt = get_risk_focused_prompt(strategy_data)
elif template_type == "comprehensive":
prompt = get_comprehensive_analysis_prompt(strategy_data)
else:
prompt = get_comprehensive_analysis_prompt(strategy_data) # fallback
print(f" 長度:{len(prompt)} 字符")
print(f" 預覽:{prompt[:100]}...")
except Exception as e:
print(f" ❌ 錯誤:{e}")
print(f" 錯誤詳情:{str(e)}")
if __name__ == "__main__":
demo_llm_service()
demo_prompt_templates()
print("\n" + "=" * 50)
print("🎉 LLM 投資建議功能演示完成!")
print("📖 詳細設置說明請參閱 LLM_SETUP.md")
print("🧪 運行測試:python test_llm_service.py")

@ -13,8 +13,6 @@ import json
import time
import logging
from typing import Dict, Any, Optional, Tuple
from functools import lru_cache
import time
import openai
from openai import OpenAI
@ -119,7 +117,6 @@ class LLMInvestmentAdvisor:
"""檢查快取是否有效"""
return time.time() - cache_time < self.cache_timeout
@lru_cache(maxsize=100)
def generate_advice(self, strategy_id: str, strategy_data: Dict[str, Any]) -> str:
"""生成投資建議
@ -130,7 +127,7 @@ class LLMInvestmentAdvisor:
Returns:
str: 投資建議文本
"""
cache_key = f"advice_{strategy_id}_{hash(str(strategy_data))}"
cache_key = f"advice_{strategy_id}_{hash(json.dumps(strategy_data, sort_keys=True))}"
# 檢查快取
if cache_key in self.cache:
@ -148,7 +145,6 @@ class LLMInvestmentAdvisor:
# 快取結果
self.cache[cache_key] = (response, time.time())
logger.info(f"Generated new advice for strategy {strategy_id}")
return response
@ -156,6 +152,11 @@ class LLMInvestmentAdvisor:
logger.error(f"Error generating advice for strategy {strategy_id}: {str(e)}")
return self._get_fallback_advice(strategy_data)
def clear_cache(self):
"""清除快取"""
self.cache.clear()
logger.info("LLM advice cache cleared")
def _call_openai_with_retry(self, prompt: str) -> str:
"""調用OpenAI API,包含重試機制

@ -53,7 +53,7 @@ def get_comprehensive_analysis_prompt(strategy_data: Dict[str, Any]) -> str:
投資組合配置
持有資產{', '.join(strategy_data.get('assets', []))}
權重配置{strategy_data.get('weight', {{}}).get('columns', [])}
權重配置{strategy_data.get('weight', {}).get('columns', [])}
分析要求
請從以下面向提供詳細分析和建議

Loading…
Cancel
Save