#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 稷下学宫AI辩论系统主入口 提供命令行界面来运行不同的辩论模式 """ import argparse import asyncio import sys import os from typing import Dict, Any, List, Tuple # 将 src 目录添加到 Python 路径,以便能正确导入模块 project_root = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, os.path.join(project_root, 'src')) from config.settings import validate_config, get_database_config from google.adk import Agent, Runner from google.adk.sessions import InMemorySessionService, Session from google.genai import types import pymongo from datetime import datetime def check_environment(mode: str = "hybrid"): """检查并验证运行环境""" print("🔧 检查运行环境...") if not validate_config(mode=mode): print("❌ 环境配置验证失败") return False print("✅ 环境检查通过") return True async def _get_llm_reply(runner: Runner, prompt: str) -> str: """一个辅助函数,用于调用Runner并获取纯文本回复,同时流式输出到控制台""" # 每个调用创建一个新的会话 session = await runner.session_service.create_session(state={}, app_name=runner.app_name, user_id="debate_user") content = types.Content(role='user', parts=[types.Part(text=prompt)]) response = runner.run_async( user_id=session.user_id, session_id=session.id, new_message=content ) reply = "" async for event in response: chunk = "" if hasattr(event, 'content') and event.content and hasattr(event.content, 'parts'): for part in event.content.parts: if hasattr(part, 'text') and part.text: chunk = str(part.text) elif hasattr(event, 'text') and event.text: chunk = str(event.text) if chunk: print(chunk, end="", flush=True) reply += chunk return reply.strip() async def run_adk_turn_based_debate(topic: str, rounds: int = 2): """运行由太上老君主持的,基于八卦对立和顺序的辩论""" try: print(f"🚀 启动ADK八仙论道 (太上老君主持)...") print(f"📋 辩论主题: {topic}") print(f"🔄 辩论总轮数: {rounds}") # 1. 初始化记忆银行 print("🧠 初始化记忆银行...") from src.jixia.memory.factory import get_memory_backend memory_bank = get_memory_backend() print("✅ 记忆银行准备就绪。") character_configs = { "太上老君": {"name": "太上老君", "model": "gemini-2.5-flash", "instruction": "你是太上老君,天道化身,辩论的主持人。你的言辞沉稳、公正、充满智慧。你的任务是:1. 对辩论主题进行开场介绍。2. 在每轮或每场对决前进行引导。3. 在辩论结束后,对所有观点进行全面、客观的总结。保持中立,不偏袒任何一方。"}, "吕洞宾": {"name": "吕洞宾", "model": "gemini-2.5-flash", "instruction": "你是吕洞宾(乾卦),男性代表,善于理性分析,逻辑性强,推理严密。"}, "何仙姑": {"name": "何仙姑", "model": "gemini-2.5-flash", "instruction": "你是何仙姑(坤卦),女性代表,注重平衡与和谐,善于创新思维。"}, "张果老": {"name": "张果老", "model": "gemini-2.5-flash", "instruction": "你是张果老(兑卦),老者代表,具传统智慧,发言厚重沉稳,经验导向。"}, "韩湘子": {"name": "韩湘子", "model": "gemini-2.5-flash", "instruction": "你是韩湘子(艮卦),少年代表,具创新思维,发言活泼灵动,具前瞻性。"}, "汉钟离": {"name": "汉钟离", "model": "gemini-2.5-flash", "instruction": "你是汉钟离(离卦),富者代表,有权威意识,发言威严庄重,逻辑清晰。"}, "蓝采和": {"name": "蓝采和", "model": "gemini-2.5-flash", "instruction": "你是蓝采和(坎卦),贫者代表,关注公平,发言平易近人。"}, "曹国舅": {"name": "曹国舅", "model": "gemini-2.5-flash", "instruction": "你是曹国舅(震卦),贵者代表,具商业思维,发言精明务实,效率优先。"}, "铁拐李": {"name": "铁拐李", "model": "gemini-2.5-flash", "instruction": "你是铁拐李(巽卦),贱者代表,具草根智慧,发言朴实直接,实用至上。"} } # 为每个Runner创建独立的SessionService runners: Dict[str, Runner] = { name: Runner( app_name="稷下学宫八仙论道系统", agent=Agent(name=config["name"], model=config["model"], instruction=config["instruction"]), session_service=InMemorySessionService() ) for name, config in character_configs.items() } host_runner = runners["太上老君"] debate_history = [] print("\n" + "="*20 + " 辩论开始 " + "="*20) print(f"\n👑 太上老君: ", end="", flush=True) opening_prompt = f"请为本次关于“{topic}”的辩论,发表一段公正、深刻的开场白,并宣布辩论开始。" opening_statement = await _get_llm_reply(host_runner, opening_prompt) print() # Newline after streaming # --- 第一轮:核心对立辩论 --- if rounds >= 1: print(f"\n👑 太上老君: ", end="", flush=True) round1_intro = await _get_llm_reply(host_runner, "请为第一轮核心对立辩论进行引导。") print() # Newline after streaming duel_pairs: List[Tuple[str, str, str]] = [ ("乾坤对立 (男女)", "吕洞宾", "何仙姑"), ("兑艮对立 (老少)", "张果老", "韩湘子"), ("离坎对立 (富贫)", "汉钟离", "蓝采和"), ("震巽对立 (贵贱)", "曹国舅", "铁拐李") ] for title, p1, p2 in duel_pairs: print(f"\n--- {title} ---") print(f"👑 太上老君: ", end="", flush=True) duel_intro = await _get_llm_reply(host_runner, f"现在开始“{title}”的对决,请{p1}和{p2}准备。") print() # Newline after streaming print(f"🗣️ {p1}: ", end="", flush=True) s1 = await _get_llm_reply(runners[p1], f"主题:{topic}。作为开场,请从你的角度阐述观点。") print(); debate_history.append(f"{p1}: {s1}") await memory_bank.add_memory(agent_name=p1, content=s1, memory_type="statement", debate_topic=topic) print(f"🗣️ {p2}: ", end="", flush=True) s2 = await _get_llm_reply(runners[p2], f"主题:{topic}。对于刚才{p1}的观点“{s1[:50]}...”,请进行回应。") print(); debate_history.append(f"{p2}: {s2}") await memory_bank.add_memory(agent_name=p2, content=s2, memory_type="statement", debate_topic=topic) print(f"🗣️ {p1}: ", end="", flush=True) s3 = await _get_llm_reply(runners[p1], f"主题:{topic}。对于{p2}的回应“{s2[:50]}...”,请进行反驳。") print(); debate_history.append(f"{p1}: {s3}") await memory_bank.add_memory(agent_name=p1, content=s3, memory_type="statement", debate_topic=topic) print(f"🗣️ {p2}: ", end="", flush=True) s4 = await _get_llm_reply(runners[p2], f"主题:{topic}。针对{p1}的反驳“{s3[:50]}...”,请为本场对决做总结。") print(); debate_history.append(f"{p2}: {s4}") await memory_bank.add_memory(agent_name=p2, content=s4, memory_type="statement", debate_topic=topic) await asyncio.sleep(1) # --- 第二轮:先天八卦顺序发言 (集成记忆银行) --- if rounds >= 2: print(f"\n👑 太上老君: ", end="", flush=True) round2_intro = await _get_llm_reply(host_runner, "请为第二轮,也就是结合场上观点的综合发言,进行引导。") print() # Newline after streaming baxi_sequence = ["吕洞宾", "张果老", "汉钟离", "曹国舅", "铁拐李", "蓝采和", "韩湘子", "何仙姑"] for name in baxi_sequence: print(f"\n--- {name}的回合 ---") context = await memory_bank.get_agent_context(name, topic) prompt = f"这是你关于“{topic}”的记忆上下文,请参考并对其他人的观点进行回应:\n{context}\n\n现在请从你的角色特点出发,继续发表你的看法。" print(f"🗣️ {name}: ", end="", flush=True) reply = await _get_llm_reply(runners[name], prompt) print(); debate_history.append(f"{name}: {reply}") await memory_bank.add_memory(agent_name=name, content=reply, memory_type="statement", debate_topic=topic) await asyncio.sleep(1) print("\n" + "="*20 + " 辩论结束 " + "="*20) # 4. 保存辩论会话到记忆银行 print("\n💾 正在保存辩论会话记录到记忆银行...") await memory_bank.save_debate_session( debate_topic=topic, participants=[name for name in character_configs.keys() if name != "太上老君"], conversation_history=[{"agent": h.split(": ")[0], "content": ": ".join(h.split(": ")[1:])} for h in debate_history if ": " in h], outcomes={} ) print("✅ 辩论会话已保存到记忆银行。") # 5. 保存辩论过程资产到MongoDB db_config = get_database_config() if db_config.get("mongodb_url"): print("\n💾 正在保存辩论过程资产到 MongoDB...") try: client = pymongo.MongoClient(db_config["mongodb_url"]) db = client.get_database("jixia_academy") collection = db.get_collection("debates") summary_prompt = f"辩论已结束。以下是完整的辩论记录:\n\n{' '.join(debate_history)}\n\n请对本次辩论进行全面、公正、深刻的总结。" print(f"\n👑 太上老君: ", end="", flush=True) summary = await _get_llm_reply(host_runner, summary_prompt) print() # Newline after streaming debate_document = { "topic": topic, "rounds": rounds, "timestamp": datetime.utcnow(), "participants": [name for name in character_configs.keys() if name != "太上老君"], "conversation": [{"agent": h.split(": ")[0], "content": ": ".join(h.split(": ")[1:])} for h in debate_history if ": " in h], "summary": summary } collection.insert_one(debate_document) print("✅ 辩论过程资产已成功保存到 MongoDB。") client.close() except Exception as e: print(f"❌ 保存到 MongoDB 失败: {e}") else: print("⚠️ 未配置 MONGODB_URL,跳过保存到 MongoDB。") print(f"\n👑 太上老君: ", end="", flush=True) summary_prompt = f"辩论已结束。以下是完整的辩论记录:\n\n{' '.join(debate_history)}\n\n请对本次辩论进行全面、公正、深刻的总结。" summary = await _get_llm_reply(host_runner, summary_prompt) print() # Newline after streaming for runner in runners.values(): await runner.close() print(f"\n🎉 ADK八仙轮流辩论完成!") return True except Exception as e: print(f"❌ 运行ADK八仙轮流辩论失败: {e}") import traceback traceback.print_exc() return False async def main_async(args): if not check_environment(mode="google_adk"): return 1 await run_adk_turn_based_debate(args.topic, args.rounds) return 0 def main(): parser = argparse.ArgumentParser(description="稷下学宫AI辩论系统 (ADK版)") parser.add_argument("--topic", "-t", default="AI是否应该拥有创造力?", help="辩论主题") parser.add_argument("--rounds", "-r", type=int, default=2, choices=[1, 2], help="辩论轮数 (1: 核心对立, 2: 对立+顺序发言)") args = parser.parse_args() try: sys.exit(asyncio.run(main_async(args))) except KeyboardInterrupt: print("\n\n👋 用户中断,退出程序") sys.exit(0) except Exception as e: print(f"\n\n💥 程序运行出错: {e}") import traceback traceback.print_exc() sys.exit(1) if __name__ == "__main__": main()