""" 交互式 CLI —— 四层架构自适应分析(增强版) 用法: python cli.py [数据库路径] """ import os import sys sys.path.insert(0, os.path.dirname(__file__)) from core.config import DB_PATH, LLM_CONFIG, MAX_EXPLORATION_ROUNDS, PLAYBOOK_DIR, PROJECT_ROOT from agent import DataAnalysisAgent def print_help(): print(""" 可用命令: <问题> 分析一个问题 rounds= <问题> 设置探索轮数 report [主题] 整合所有分析,生成综合报告 export 导出最近一次分析结果为 CSV reports 列出已保存的报告文件 schema 查看数据库 Schema playbooks 查看已加载的预设剧本 regen 重新生成预设剧本 history 查看分析历史 audit 查看 SQL 审计日志 clear 清空分析历史 help 显示帮助 quit / q 退出 """) def cmd_reports(agent): """列出已保存的报告""" reports_dir = agent.reports_dir if not os.path.isdir(reports_dir): print("(reports 目录不存在)") return files = sorted([f for f in os.listdir(reports_dir) if f.endswith(".md")]) if not files: print("(尚无保存的报告)") return print(f"\n📁 已保存 {len(files)} 份报告:") for f in files: fpath = os.path.join(reports_dir, f) size = os.path.getsize(fpath) print(f" 📄 {f} ({size/1024:.1f} KB)") def setup_readline(): """启用命令历史(Linux/macOS)""" try: import readline histfile = os.path.join(PROJECT_ROOT, ".cli_history") try: readline.read_history_file(histfile) except FileNotFoundError: pass import atexit atexit.register(readline.write_history_file, histfile) readline.set_history_length(100) except ImportError: pass def main(): db_path = sys.argv[1] if len(sys.argv) > 1 else DB_PATH if not os.path.exists(db_path): print(f"❌ 数据库不存在: {db_path}") sys.exit(1) if not LLM_CONFIG["api_key"]: print("❌ LLM_API_KEY 未配置!") print(" 请设置环境变量或创建 .env 文件:") print(" LLM_API_KEY=your-key") print(" LLM_BASE_URL=https://api.openai.com/v1") print(" LLM_MODEL=gpt-4o-mini") sys.exit(1) setup_readline() agent = DataAnalysisAgent(db_path) print("=" * 60) print(" 🤖 数据分析 Agent —— 四层架构") print("=" * 60) print(f"\n🔗 LLM: {LLM_CONFIG['model']} @ {LLM_CONFIG['base_url']}") print(f"🔄 最大探索轮数: {MAX_EXPLORATION_ROUNDS}") print(f"💾 数据库: {db_path}") print(f"📋 预设剧本: {len(agent.playbook_mgr.playbooks)} 个") print(f"\n💬 输入分析问题(help 查看命令)\n") last_steps = None # 记录最近一次分析的 steps,用于 export while True: try: user_input = input("📊 > ").strip() except (EOFError, KeyboardInterrupt): print("\n👋 再见!") break if not user_input: continue cmd = user_input.lower() if cmd in ("quit", "exit", "q"): print("👋 再见!") break elif cmd == "help": print_help() elif cmd == "schema": print(agent.get_schema()) elif cmd == "history": print(agent.get_history()) elif cmd == "audit": print(agent.get_audit()) elif cmd == "clear": agent.clear_history() last_steps = None print("✅ 历史已清空") elif cmd == "reports": cmd_reports(agent) elif cmd == "export": if last_steps: fpath = agent.export_data(last_steps) if fpath: print(f"✅ 导出成功: {fpath}") else: print("⚠️ 无数据可导出") else: print("⚠️ 请先执行一次分析") elif cmd.startswith("report"): topic = user_input[6:].strip() try: report = agent.full_report(question=topic) print("\n" + report) print("\n" + "~" * 60) except Exception as e: print(f"\n❌ 报告整合出错: {e}") import traceback traceback.print_exc() elif cmd == "playbooks": if not agent.playbook_mgr.playbooks: print("(无预设剧本,输入 regen 让 AI 自动生成)") else: for i, pb in enumerate(agent.playbook_mgr.playbooks, 1): print(f" {i}. 📋 {pb.name} — {pb.description} ({len(pb.preset_queries)} 条预设)") elif cmd == "regen": if os.path.isdir(PLAYBOOK_DIR): for f in os.listdir(PLAYBOOK_DIR): if f.startswith("auto_") and f.endswith(".json"): os.remove(os.path.join(PLAYBOOK_DIR, f)) agent.playbook_mgr.playbooks.clear() print("🤖 AI 正在重新生成预设剧本...") generated = agent.playbook_mgr.auto_generate(agent.schema_text, save_dir=PLAYBOOK_DIR) if generated: print(f"✅ 生成 {len(generated)} 个剧本:") for pb in generated: print(f" 📋 {pb.name} — {pb.description}") else: print("⚠️ 生成失败") else: # 解析 rounds=N max_rounds = MAX_EXPLORATION_ROUNDS question = user_input if "rounds=" in question.lower(): parts = question.split("rounds=") question = parts[0].strip() try: max_rounds = int(parts[1].strip().split()[0]) except (ValueError, IndexError): pass try: report = agent.analyze(question, max_rounds=max_rounds) # 保存 steps 用于 export if agent.context.sessions: last_steps = agent.context.sessions[-1].steps print("\n" + report) print("\n" + "~" * 60) except Exception as e: print(f"\n❌ 分析出错: {e}") import traceback traceback.print_exc() print("\n📋 本次会话审计:") print(agent.get_audit()) agent.close() if __name__ == "__main__": main()