484 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			484 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Python
		
	
	
	
#!/usr/bin/env python3
 | 
						||
# -*- coding: utf-8 -*-
 | 
						||
"""
 | 
						||
稷下学宫Swarm辩论系统 - 统一版本
 | 
						||
支持OpenRouter和Ollama两种模式的八仙论道
 | 
						||
"""
 | 
						||
 | 
						||
import asyncio
 | 
						||
import json
 | 
						||
from datetime import datetime
 | 
						||
from typing import Dict, List, Any, Optional, Union
 | 
						||
import random
 | 
						||
import os
 | 
						||
 | 
						||
try:
 | 
						||
    from swarm import Swarm, Agent
 | 
						||
    SWARM_AVAILABLE = True
 | 
						||
except ImportError:
 | 
						||
    print("⚠️ OpenAI Swarm未安装,请运行: pip install git+https://github.com/openai/swarm.git")
 | 
						||
    SWARM_AVAILABLE = False
 | 
						||
 | 
						||
class JixiaSwarmDebate:
 | 
						||
    """稷下学宫Swarm辩论系统 - 统一版本"""
 | 
						||
    
 | 
						||
    def __init__(self, mode: str = "openrouter", ollama_url: str = "http://100.99.183.38:11434", model: str = "qwen3:8b"):
 | 
						||
        """
 | 
						||
        初始化辩论系统
 | 
						||
        
 | 
						||
        Args:
 | 
						||
            mode: 运行模式 ("openrouter" 或 "ollama")
 | 
						||
            ollama_url: Ollama服务地址
 | 
						||
            model: 使用的模型名称
 | 
						||
        """
 | 
						||
        if not SWARM_AVAILABLE:
 | 
						||
            raise ImportError("OpenAI Swarm未安装")
 | 
						||
        
 | 
						||
        self.mode = mode
 | 
						||
        self.ollama_url = ollama_url
 | 
						||
        self.model = model
 | 
						||
        
 | 
						||
        # 初始化客户端
 | 
						||
        self.client = self._initialize_client()
 | 
						||
        
 | 
						||
        # 八仙配置
 | 
						||
        self.immortals = {
 | 
						||
            '吕洞宾': {
 | 
						||
                'role': '技术分析专家',
 | 
						||
                'stance': 'positive',
 | 
						||
                'specialty': '技术分析和图表解读',
 | 
						||
                'style': '犀利直接,一剑封喉',
 | 
						||
                'bagua': '乾卦 - 主动进取'
 | 
						||
            },
 | 
						||
            '何仙姑': {
 | 
						||
                'role': '风险控制专家', 
 | 
						||
                'stance': 'negative',
 | 
						||
                'specialty': '风险评估和资金管理',
 | 
						||
                'style': '温和坚定,关注风险',
 | 
						||
                'bagua': '坤卦 - 稳健保守'
 | 
						||
            },
 | 
						||
            '张果老': {
 | 
						||
                'role': '历史数据分析师',
 | 
						||
                'stance': 'positive', 
 | 
						||
                'specialty': '历史回测和趋势分析',
 | 
						||
                'style': '博古通今,从历史找规律',
 | 
						||
                'bagua': '兑卦 - 传统价值'
 | 
						||
            },
 | 
						||
            '铁拐李': {
 | 
						||
                'role': '逆向投资大师',
 | 
						||
                'stance': 'negative',
 | 
						||
                'specialty': '逆向思维和危机发现', 
 | 
						||
                'style': '不拘一格,挑战共识',
 | 
						||
                'bagua': '巽卦 - 逆向思维'
 | 
						||
            }
 | 
						||
        }
 | 
						||
        
 | 
						||
        # 创建智能体
 | 
						||
        self.agents = self._create_agents()
 | 
						||
        
 | 
						||
    def _initialize_client(self) -> Optional[Swarm]:
 | 
						||
        """初始化Swarm客户端"""
 | 
						||
        try:
 | 
						||
            from openai import OpenAI
 | 
						||
            
 | 
						||
            if self.mode == "ollama":
 | 
						||
                # Ollama模式
 | 
						||
                openai_client = OpenAI(
 | 
						||
                    api_key="ollama",  # Ollama不需要真实的API密钥
 | 
						||
                    base_url=f"{self.ollama_url}/v1"
 | 
						||
                )
 | 
						||
                print(f"🦙 使用本地Ollama服务: {self.ollama_url}")
 | 
						||
                print(f"🤖 使用模型: {self.model}")
 | 
						||
                
 | 
						||
            else:
 | 
						||
                # OpenRouter模式
 | 
						||
                api_key = self._get_openrouter_key()
 | 
						||
                if not api_key:
 | 
						||
                    print("❌ 未找到OpenRouter API密钥")
 | 
						||
                    return None
 | 
						||
                
 | 
						||
                openai_client = OpenAI(
 | 
						||
                    api_key=api_key,
 | 
						||
                    base_url="https://openrouter.ai/api/v1",
 | 
						||
                    default_headers={
 | 
						||
                        "HTTP-Referer": "https://github.com/ben/liurenchaxin",
 | 
						||
                        "X-Title": "Jixia Academy Swarm Debate"
 | 
						||
                    }
 | 
						||
                )
 | 
						||
                print(f"🌐 使用OpenRouter服务")
 | 
						||
                print(f"🔑 API密钥: {api_key[:20]}...")
 | 
						||
            
 | 
						||
            return Swarm(client=openai_client)
 | 
						||
            
 | 
						||
        except Exception as e:
 | 
						||
            print(f"❌ 客户端初始化失败: {e}")
 | 
						||
            return None
 | 
						||
    
 | 
						||
    def _get_openrouter_key(self) -> Optional[str]:
 | 
						||
        """获取OpenRouter API密钥"""
 | 
						||
        # 尝试从配置管理获取
 | 
						||
        try:
 | 
						||
            from config.settings import get_openrouter_key
 | 
						||
            return get_openrouter_key()
 | 
						||
        except ImportError:
 | 
						||
            pass
 | 
						||
        
 | 
						||
        # 尝试从环境变量获取
 | 
						||
        api_keys = [
 | 
						||
            os.getenv('OPENROUTER_API_KEY_1'),
 | 
						||
            os.getenv('OPENROUTER_API_KEY_2'), 
 | 
						||
            os.getenv('OPENROUTER_API_KEY_3'),
 | 
						||
            os.getenv('OPENROUTER_API_KEY_4')
 | 
						||
        ]
 | 
						||
        
 | 
						||
        for key in api_keys:
 | 
						||
            if key and key.startswith('sk-'):
 | 
						||
                return key
 | 
						||
        
 | 
						||
        return None
 | 
						||
    
 | 
						||
    def _create_agents(self) -> Dict[str, Agent]:
 | 
						||
        """创建八仙智能体"""
 | 
						||
        if not self.client:
 | 
						||
            return {}
 | 
						||
            
 | 
						||
        agents = {}
 | 
						||
        
 | 
						||
        # 吕洞宾 - 技术分析专家
 | 
						||
        agents['吕洞宾'] = Agent(
 | 
						||
            name="LuDongbin",
 | 
						||
            instructions="""
 | 
						||
            你是吕洞宾,八仙之首,技术分析专家。
 | 
						||
            
 | 
						||
            你的特点:
 | 
						||
            - 擅长技术分析和图表解读
 | 
						||
            - 立场:看涨派,善于发现投资机会
 | 
						||
            - 风格:犀利直接,一剑封喉
 | 
						||
            - 八卦:乾卦 - 主动进取
 | 
						||
            
 | 
						||
            在辩论中:
 | 
						||
            1. 从技术分析角度分析市场
 | 
						||
            2. 使用具体的技术指标支撑观点(如RSI、MACD、均线等)
 | 
						||
            3. 保持看涨的乐观态度
 | 
						||
            4. 发言以"吕洞宾曰:"开头
 | 
						||
            5. 发言控制在100字以内,简洁有力
 | 
						||
            6. 发言完毕后说"请何仙姑继续论道"
 | 
						||
            
 | 
						||
            请用古雅但现代的语言风格,结合专业的技术分析。
 | 
						||
            """,
 | 
						||
            functions=[self._to_hexiangu]
 | 
						||
        )
 | 
						||
        
 | 
						||
        # 何仙姑 - 风险控制专家
 | 
						||
        agents['何仙姑'] = Agent(
 | 
						||
            name="HeXiangu", 
 | 
						||
            instructions="""
 | 
						||
            你是何仙姑,八仙中唯一的女仙,风险控制专家。
 | 
						||
            
 | 
						||
            你的特点:
 | 
						||
            - 擅长风险评估和资金管理
 | 
						||
            - 立场:看跌派,关注投资风险
 | 
						||
            - 风格:温和坚定,关注风险控制
 | 
						||
            - 八卦:坤卦 - 稳健保守
 | 
						||
            
 | 
						||
            在辩论中:
 | 
						||
            1. 从风险控制角度分析市场
 | 
						||
            2. 指出潜在的投资风险和危险信号
 | 
						||
            3. 保持谨慎的态度,强调风险管理
 | 
						||
            4. 发言以"何仙姑曰:"开头
 | 
						||
            5. 发言控制在100字以内,温和但坚定
 | 
						||
            6. 发言完毕后说"请张果老继续论道"
 | 
						||
            
 | 
						||
            请用温和但专业的语调,体现女性的细致和关怀。
 | 
						||
            """,
 | 
						||
            functions=[self._to_zhangguolao]
 | 
						||
        )
 | 
						||
        
 | 
						||
        # 张果老 - 历史数据分析师
 | 
						||
        agents['张果老'] = Agent(
 | 
						||
            name="ZhangGuoLao",
 | 
						||
            instructions="""
 | 
						||
            你是张果老,历史数据分析师。
 | 
						||
            
 | 
						||
            你的特点:
 | 
						||
            - 擅长历史回测和趋势分析
 | 
						||
            - 立场:看涨派,从历史中寻找机会
 | 
						||
            - 风格:博古通今,从历史中找规律
 | 
						||
            - 八卦:兑卦 - 传统价值
 | 
						||
            
 | 
						||
            在辩论中:
 | 
						||
            1. 从历史数据角度分析市场
 | 
						||
            2. 引用具体的历史案例和数据
 | 
						||
            3. 保持乐观的投资态度
 | 
						||
            4. 发言以"张果老曰:"开头
 | 
						||
            5. 发言控制在100字以内,引经据典
 | 
						||
            6. 发言完毕后说"请铁拐李继续论道"
 | 
						||
            
 | 
						||
            请用博学的语调,多引用历史数据和案例。
 | 
						||
            """,
 | 
						||
            functions=[self._to_tieguaili]
 | 
						||
        )
 | 
						||
        
 | 
						||
        # 铁拐李 - 逆向投资大师
 | 
						||
        agents['铁拐李'] = Agent(
 | 
						||
            name="TieGuaiLi",
 | 
						||
            instructions="""
 | 
						||
            你是铁拐李,逆向投资大师。
 | 
						||
            
 | 
						||
            你的特点:
 | 
						||
            - 擅长逆向思维和危机发现
 | 
						||
            - 立场:看跌派,挑战主流观点
 | 
						||
            - 风格:不拘一格,敢于质疑
 | 
						||
            - 八卦:巽卦 - 逆向思维
 | 
						||
            
 | 
						||
            在辩论中:
 | 
						||
            1. 从逆向投资角度分析市场
 | 
						||
            2. 挑战前面三位仙人的观点
 | 
						||
            3. 寻找市场的潜在危机和泡沫
 | 
						||
            4. 发言以"铁拐李曰:"开头
 | 
						||
            5. 作为最后发言者,要总结四仙观点并给出结论
 | 
						||
            6. 发言控制在150字以内,包含总结
 | 
						||
            
 | 
						||
            请用直率犀利的语言,体现逆向思维的独特视角。
 | 
						||
            """,
 | 
						||
            functions=[]  # 最后一个,不需要转换
 | 
						||
        )
 | 
						||
        
 | 
						||
        return agents
 | 
						||
    
 | 
						||
    def _to_hexiangu(self):
 | 
						||
        """转到何仙姑"""
 | 
						||
        return self.agents['何仙姑']
 | 
						||
    
 | 
						||
    def _to_zhangguolao(self):
 | 
						||
        """转到张果老"""
 | 
						||
        return self.agents['张果老']
 | 
						||
    
 | 
						||
    def _to_tieguaili(self):
 | 
						||
        """转到铁拐李"""
 | 
						||
        return self.agents['铁拐李']
 | 
						||
    
 | 
						||
    async def conduct_debate(self, topic: str, context: Dict[str, Any] = None) -> Optional[Dict[str, Any]]:
 | 
						||
        """
 | 
						||
        进行八仙辩论
 | 
						||
        
 | 
						||
        Args:
 | 
						||
            topic: 辩论主题
 | 
						||
            context: 市场背景信息
 | 
						||
            
 | 
						||
        Returns:
 | 
						||
            辩论结果
 | 
						||
        """
 | 
						||
        if not self.client:
 | 
						||
            print("❌ 客户端未初始化,无法进行辩论")
 | 
						||
            return None
 | 
						||
        
 | 
						||
        print("🏛️ 稷下学宫八仙论道开始!")
 | 
						||
        print("=" * 60)
 | 
						||
        print(f"🎯 论道主题: {topic}")
 | 
						||
        print(f"⏰ 开始时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
 | 
						||
        print(f"🔧 运行模式: {self.mode.upper()}")
 | 
						||
        if self.mode == "ollama":
 | 
						||
            print(f"🦙 Ollama服务: {self.ollama_url}")
 | 
						||
        print()
 | 
						||
        
 | 
						||
        # 构建初始提示
 | 
						||
        prompt = self._build_prompt(topic, context)
 | 
						||
        
 | 
						||
        try:
 | 
						||
            print("⚔️ 吕洞宾仙长请先发言...")
 | 
						||
            print("-" * 40)
 | 
						||
            
 | 
						||
            # 开始辩论
 | 
						||
            model_override = self.model if self.mode == "ollama" else "openai/gpt-3.5-turbo"
 | 
						||
            
 | 
						||
            response = self.client.run(
 | 
						||
                agent=self.agents['吕洞宾'],
 | 
						||
                messages=[{"role": "user", "content": prompt}],
 | 
						||
                max_turns=10,
 | 
						||
                model_override=model_override
 | 
						||
            )
 | 
						||
            
 | 
						||
            print("\n" + "=" * 60)
 | 
						||
            print("🎊 八仙论道圆满结束!")
 | 
						||
            
 | 
						||
            # 处理结果
 | 
						||
            result = self._process_result(response, topic, context)
 | 
						||
            self._display_summary(result)
 | 
						||
            
 | 
						||
            return result
 | 
						||
            
 | 
						||
        except Exception as e:
 | 
						||
            print(f"❌ 论道过程中出错: {e}")
 | 
						||
            import traceback
 | 
						||
            traceback.print_exc()
 | 
						||
            return None
 | 
						||
    
 | 
						||
    def _build_prompt(self, topic: str, context: Dict[str, Any] = None) -> str:
 | 
						||
        """构建辩论提示"""
 | 
						||
        context_str = ""
 | 
						||
        if context:
 | 
						||
            context_str = f"\n📊 市场背景:\n{json.dumps(context, indent=2, ensure_ascii=False)}\n"
 | 
						||
        
 | 
						||
        prompt = f"""
 | 
						||
        🏛️ 稷下学宫八仙论道正式开始!
 | 
						||
        
 | 
						||
        📜 论道主题: {topic}
 | 
						||
        {context_str}
 | 
						||
        
 | 
						||
        🎭 论道规则:
 | 
						||
        1. 四仙按序发言:吕洞宾 → 何仙姑 → 张果老 → 铁拐李
 | 
						||
        2. 正反方交替:吕洞宾(看涨) → 何仙姑(看跌) → 张果老(看涨) → 铁拐李(看跌)
 | 
						||
        3. 每位仙人从专业角度分析,提供具体数据支撑
 | 
						||
        4. 可以质疑前面仙人的观点,但要有理有据
 | 
						||
        5. 保持仙风道骨的表达风格,但要专业
 | 
						||
        6. 每次发言简洁有力,控制在100字以内
 | 
						||
        7. 铁拐李作为最后发言者要总结观点
 | 
						||
        8. 体现各自的八卦属性和投资哲学
 | 
						||
        
 | 
						||
        🗡️ 请吕洞宾仙长首先发言!
 | 
						||
        记住:你是技术分析专家,要从技术面找到投资机会!
 | 
						||
        发言要简洁有力,一剑封喉!
 | 
						||
        """
 | 
						||
        return prompt
 | 
						||
    
 | 
						||
    def _process_result(self, response, topic: str, context: Dict[str, Any]) -> Dict[str, Any]:
 | 
						||
        """处理辩论结果"""
 | 
						||
        messages = response.messages if hasattr(response, 'messages') else []
 | 
						||
        
 | 
						||
        debate_messages = []
 | 
						||
        for msg in messages:
 | 
						||
            if msg.get('role') == 'assistant' and msg.get('content'):
 | 
						||
                content = msg['content']
 | 
						||
                speaker = self._extract_speaker(content)
 | 
						||
                
 | 
						||
                debate_messages.append({
 | 
						||
                    'speaker': speaker,
 | 
						||
                    'content': content,
 | 
						||
                    'timestamp': datetime.now().isoformat(),
 | 
						||
                    'stance': self.immortals.get(speaker, {}).get('stance', 'unknown'),
 | 
						||
                    'specialty': self.immortals.get(speaker, {}).get('specialty', ''),
 | 
						||
                    'bagua': self.immortals.get(speaker, {}).get('bagua', '')
 | 
						||
                })
 | 
						||
        
 | 
						||
        return {
 | 
						||
            "debate_id": f"jixia_swarm_{self.mode}_{datetime.now().strftime('%Y%m%d_%H%M%S')}",
 | 
						||
            "topic": topic,
 | 
						||
            "context": context,
 | 
						||
            "messages": debate_messages,
 | 
						||
            "final_output": debate_messages[-1]['content'] if debate_messages else "",
 | 
						||
            "timestamp": datetime.now().isoformat(),
 | 
						||
            "framework": f"OpenAI Swarm + {self.mode.upper()}",
 | 
						||
            "model": self.model,
 | 
						||
            "mode": self.mode,
 | 
						||
            "participants": list(self.immortals.keys())
 | 
						||
        }
 | 
						||
    
 | 
						||
    def _extract_speaker(self, content: str) -> str:
 | 
						||
        """从内容中提取发言者"""
 | 
						||
        for name in self.immortals.keys():
 | 
						||
            if f"{name}曰" in content:
 | 
						||
                return name
 | 
						||
        return "未知仙人"
 | 
						||
    
 | 
						||
    def _display_summary(self, result: Dict[str, Any]):
 | 
						||
        """显示辩论总结"""
 | 
						||
        print("\n🌟 八仙论道总结")
 | 
						||
        print("=" * 60)
 | 
						||
        print(f"📜 主题: {result['topic']}")
 | 
						||
        print(f"⏰ 时间: {result['timestamp']}")
 | 
						||
        print(f"🔧 框架: {result['framework']}")
 | 
						||
        print(f"🤖 模型: {result['model']}")
 | 
						||
        print(f"💬 发言数: {len(result['messages'])}条")
 | 
						||
        
 | 
						||
        # 统计正反方观点
 | 
						||
        positive_count = len([m for m in result['messages'] if m.get('stance') == 'positive'])
 | 
						||
        negative_count = len([m for m in result['messages'] if m.get('stance') == 'negative'])
 | 
						||
        
 | 
						||
        print(f"📊 观点分布: 看涨{positive_count}条, 看跌{negative_count}条")
 | 
						||
        
 | 
						||
        # 显示参与者
 | 
						||
        participants = ", ".join(result['participants'])
 | 
						||
        print(f"🎭 参与仙人: {participants}")
 | 
						||
        
 | 
						||
        print("\n🏆 最终总结:")
 | 
						||
        print("-" * 40)
 | 
						||
        if result['messages']:
 | 
						||
            print(result['final_output'])
 | 
						||
        
 | 
						||
        print("\n✨ Swarm辩论特色:")
 | 
						||
        if self.mode == "ollama":
 | 
						||
            print("🦙 使用本地Ollama,无需API密钥")
 | 
						||
            print("🔒 完全本地运行,数据安全")
 | 
						||
        else:
 | 
						||
            print("🌐 使用OpenRouter,模型选择丰富")
 | 
						||
            print("☁️ 云端运行,性能强劲")
 | 
						||
        print("🗡️ 八仙各展所长,观点多元")
 | 
						||
        print("⚖️ 正反方交替,辩论激烈")
 | 
						||
        print("🚀 基于Swarm,智能体协作")
 | 
						||
        print("🎯 八卦哲学,投资智慧")
 | 
						||
 | 
						||
# 便捷函数
 | 
						||
async def start_openrouter_debate(topic: str, context: Dict[str, Any] = None) -> Optional[Dict[str, Any]]:
 | 
						||
    """启动OpenRouter模式的辩论"""
 | 
						||
    debate = JixiaSwarmDebate(mode="openrouter")
 | 
						||
    return await debate.conduct_debate(topic, context)
 | 
						||
 | 
						||
async def start_ollama_debate(topic: str, context: Dict[str, Any] = None, 
 | 
						||
                             ollama_url: str = "http://100.99.183.38:11434", 
 | 
						||
                             model: str = "qwen3:8b") -> Optional[Dict[str, Any]]:
 | 
						||
    """启动Ollama模式的辩论"""
 | 
						||
    debate = JixiaSwarmDebate(mode="ollama", ollama_url=ollama_url, model=model)
 | 
						||
    return await debate.conduct_debate(topic, context)
 | 
						||
 | 
						||
# 主函数
 | 
						||
async def main():
 | 
						||
    """主函数 - 演示八仙论道"""
 | 
						||
    print("🏛️ 稷下学宫Swarm辩论系统")
 | 
						||
    print("🚀 支持OpenRouter和Ollama两种模式")
 | 
						||
    print()
 | 
						||
    
 | 
						||
    # 选择运行模式
 | 
						||
    mode = input("请选择运行模式 (openrouter/ollama) [默认: ollama]: ").strip().lower()
 | 
						||
    if not mode:
 | 
						||
        mode = "ollama"
 | 
						||
    
 | 
						||
    # 辩论主题
 | 
						||
    topics = [
 | 
						||
        "英伟达股价走势:AI泡沫还是技术革命?",
 | 
						||
        "美联储2024年货币政策:加息还是降息?", 
 | 
						||
        "比特币vs黄金:谁是更好的避险资产?",
 | 
						||
        "中国房地产市场:触底反弹还是继续下行?",
 | 
						||
        "特斯拉股价:马斯克效应还是基本面支撑?"
 | 
						||
    ]
 | 
						||
    
 | 
						||
    # 随机选择主题
 | 
						||
    topic = random.choice(topics)
 | 
						||
    
 | 
						||
    # 市场背景
 | 
						||
    context = {
 | 
						||
        "market_sentiment": "谨慎乐观",
 | 
						||
        "volatility": "中等",
 | 
						||
        "key_events": ["财报季", "央行会议", "地缘政治"],
 | 
						||
        "technical_indicators": {
 | 
						||
            "RSI": 65,
 | 
						||
            "MACD": "金叉",
 | 
						||
            "MA20": "上穿"
 | 
						||
        }
 | 
						||
    }
 | 
						||
    
 | 
						||
    # 开始辩论
 | 
						||
    if mode == "ollama":
 | 
						||
        result = await start_ollama_debate(topic, context)
 | 
						||
    else:
 | 
						||
        result = await start_openrouter_debate(topic, context)
 | 
						||
    
 | 
						||
    if result:
 | 
						||
        print(f"\n🎉 辩论成功!ID: {result['debate_id']}")
 | 
						||
        print(f"📁 使用模式: {result['mode']}")
 | 
						||
        print(f"🤖 使用模型: {result['model']}")
 | 
						||
    else:
 | 
						||
        print("❌ 辩论失败")
 | 
						||
 | 
						||
if __name__ == "__main__":
 | 
						||
    asyncio.run(main()) |