feat: 四层架构全面增强
安全与稳定性: - 移除硬编码 API Key,改用 .env + 环境变量 - LLM 调用统一重试机制(指数退避,3 次重试,处理 429/5xx/超时) - 中文字体检测增强(CJK 关键词兜底 + 无字体时英文 fallback) - 缺失 API Key 给出友好提示而非崩溃 分析能力提升: - 异常检测新增 z-score 检测(标准差>2 标记异常) - 新增变异系数 CV 检测(数据波动性) - 新增零值/缺失检测 - 上下文管理器升级为关键词语义匹配(替代简单取最近 2 条) 用户体验: - 报告自动保存为 Markdown(reports/ 目录) - 新增 export 命令导出查询结果为 CSV - 新增 reports 命令查看已保存报告 - CLI 支持 readline 命令历史(方向键翻阅) - CSV 导入工具重写:自动列名映射、容错处理、dry-run 模式 - 新增 .env.example 配置模板
This commit is contained in:
64
cli.py
64
cli.py
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
交互式 CLI —— 四层架构自适应分析
|
||||
交互式 CLI —— 四层架构自适应分析(增强版)
|
||||
用法: python cli.py [数据库路径]
|
||||
"""
|
||||
import os
|
||||
@@ -7,7 +7,7 @@ import sys
|
||||
|
||||
sys.path.insert(0, os.path.dirname(__file__))
|
||||
|
||||
from core.config import DB_PATH, LLM_CONFIG, MAX_EXPLORATION_ROUNDS, PLAYBOOK_DIR
|
||||
from core.config import DB_PATH, LLM_CONFIG, MAX_EXPLORATION_ROUNDS, PLAYBOOK_DIR, PROJECT_ROOT
|
||||
from agent import DataAnalysisAgent
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ def print_help():
|
||||
<问题> 分析一个问题
|
||||
rounds=<N> <问题> 设置探索轮数
|
||||
report [主题] 整合所有分析,生成综合报告
|
||||
export 导出最近一次分析结果为 CSV
|
||||
reports 列出已保存的报告文件
|
||||
schema 查看数据库 Schema
|
||||
playbooks 查看已加载的预设剧本
|
||||
regen 重新生成预设剧本
|
||||
@@ -28,6 +30,39 @@ def print_help():
|
||||
""")
|
||||
|
||||
|
||||
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
|
||||
|
||||
@@ -36,9 +71,15 @@ def main():
|
||||
sys.exit(1)
|
||||
|
||||
if not LLM_CONFIG["api_key"]:
|
||||
print("⚠️ 未配置 LLM_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)
|
||||
@@ -50,6 +91,8 @@ def main():
|
||||
print(f"📋 预设剧本: {len(agent.playbook_mgr.playbooks)} 个")
|
||||
print(f"\n💬 输入分析问题(help 查看命令)\n")
|
||||
|
||||
last_steps = None # 记录最近一次分析的 steps,用于 export
|
||||
|
||||
while True:
|
||||
try:
|
||||
user_input = input("📊 > ").strip()
|
||||
@@ -75,7 +118,19 @@ def main():
|
||||
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:
|
||||
@@ -120,6 +175,9 @@ def main():
|
||||
|
||||
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:
|
||||
|
||||
Reference in New Issue
Block a user