1203 lines
47 KiB
Python
1203 lines
47 KiB
Python
|
||
import os
|
||
import sys
|
||
import json
|
||
import logging
|
||
import shutil
|
||
from pathlib import Path
|
||
import faiss
|
||
import numpy as np
|
||
# 导入自定义模块
|
||
from graph_builder import build_rag_database_interactive
|
||
from feature_retriever import FeatureRetriever
|
||
from static_analyzer import run_static_analysis
|
||
from issue_filter import run_issue_filtering
|
||
|
||
|
||
# ======================
|
||
# 导入原有 config.py 配置
|
||
# ======================
|
||
from config import (
|
||
QWEN_API_KEY, QWEN_API_URL,
|
||
PROJECT_ROOT, VECTOR_DB_PATH, METADATA_PATH, GRAPH_DATA_PATH, KNOWLEDGE_GRAPH_PATH,
|
||
Default_PROJECT_ROOT, Default_VECTOR_DB_PATH, Default_METADATA_PATH,
|
||
Default_GRAPH_DATA_PATH, Default_KNOWLEDGE_GRAPH_PATH,
|
||
MAX_CODE_LENGTH, CPP_EXTENSIONS, IGNORE_DIRS,
|
||
TOP_K, MAX_HOPS, MIN_SIMILARITY_THRESHOLD
|
||
)
|
||
|
||
USER_PROJECT_ROOT = PROJECT_ROOT
|
||
USER_VECTOR_DB_PATH = VECTOR_DB_PATH
|
||
USER_METADATA_PATH = METADATA_PATH
|
||
USER_GRAPH_DATA_PATH = GRAPH_DATA_PATH
|
||
USER_KNOWLEDGE_GRAPH_PATH = KNOWLEDGE_GRAPH_PATH
|
||
|
||
INTERNAL_DEFAULT_PROJECT_ROOT = Default_PROJECT_ROOT
|
||
INTERNAL_DEFAULT_VECTOR_DB_PATH = Default_VECTOR_DB_PATH
|
||
INTERNAL_DEFAULT_METADATA_PATH = Default_METADATA_PATH
|
||
INTERNAL_DEFAULT_GRAPH_DATA_PATH = Default_GRAPH_DATA_PATH
|
||
INTERNAL_DEFAULT_KNOWLEDGE_GRAPH_PATH = Default_KNOWLEDGE_GRAPH_PATH
|
||
|
||
# 配置日志
|
||
logging.basicConfig(
|
||
level=logging.INFO,
|
||
format='%(asctime)s - %(levelname)s - %(message)s',
|
||
handlers=[
|
||
logging.FileHandler("satellite_rag_system.log"),
|
||
logging.StreamHandler()
|
||
]
|
||
)
|
||
logger = logging.getLogger(__name__)
|
||
|
||
# 保存用户运行时配置的文件名
|
||
RUNTIME_CONFIG_FILE = "user_runtime_config.json"
|
||
|
||
def load_runtime_config():
|
||
"""加载用户运行时保存的配置"""
|
||
if os.path.exists(RUNTIME_CONFIG_FILE):
|
||
try:
|
||
with open(RUNTIME_CONFIG_FILE, 'r', encoding='utf-8') as f:
|
||
return json.load(f)
|
||
except Exception as e:
|
||
logger.warning(f"加载运行时配置文件失败: {e}")
|
||
return {}
|
||
|
||
def save_runtime_config(config):
|
||
"""保存用户运行时配置"""
|
||
try:
|
||
with open(RUNTIME_CONFIG_FILE, 'w', encoding='utf-8') as f:
|
||
json.dump(config, f, indent=2, ensure_ascii=False)
|
||
logger.info(f"运行时配置已保存至: {RUNTIME_CONFIG_FILE}")
|
||
return True
|
||
except Exception as e:
|
||
logger.error(f"保存运行时配置失败: {e}")
|
||
return False
|
||
|
||
|
||
def get_final_path(config_key, user_input, config_user_path, internal_default_path, runtime_config):
|
||
"""
|
||
统一的路径解析函数
|
||
优先级: 1.用户本次输入 > 2.已保存的运行时配置 > 3.config.py中的主路径变量 > 4.config.py中的Default_*后备路径
|
||
"""
|
||
# 优先级1: 用户本次运行的输入
|
||
if user_input and str(user_input).strip():
|
||
final_path_str = str(user_input).strip()
|
||
# 立即更新运行时配置
|
||
runtime_config[config_key] = final_path_str
|
||
final_path = Path(final_path_str)
|
||
# 优先级2: 之前保存的运行时配置
|
||
elif config_key in runtime_config and runtime_config[config_key]:
|
||
final_path_str = runtime_config[config_key]
|
||
final_path = Path(final_path_str)
|
||
# 优先级3: config.py中预设的主路径变量 (映射为USER_*)
|
||
elif config_user_path and str(config_user_path).strip():
|
||
final_path = Path(config_user_path)
|
||
# 优先级4: config.py中预设的Default_*后备路径 (映射为INTERNAL_DEFAULT_*)
|
||
else:
|
||
final_path = Path(internal_default_path)
|
||
# 如果内部默认路径是相对路径,尝试基于项目根目录解析
|
||
if not final_path.is_absolute() and config_key != 'PROJECT_ROOT':
|
||
project_root = get_final_path(
|
||
'PROJECT_ROOT', '', USER_PROJECT_ROOT,
|
||
INTERNAL_DEFAULT_PROJECT_ROOT, runtime_config
|
||
)
|
||
# 注意:避免创建不存在的路径,仅在 project_root 存在时拼接
|
||
if project_root.exists():
|
||
final_path = project_root / final_path
|
||
else:
|
||
# 如果 project_root 也不存在,回退到当前工作目录
|
||
final_path = Path.cwd() / final_path
|
||
|
||
# 关键修复:确保不返回空路径
|
||
if not str(final_path).strip():
|
||
# 如果最终路径仍然是空的,使用当前目录的默认文件名
|
||
default_filenames = {
|
||
'VECTOR_DB_PATH': 'satellite_rag.faiss',
|
||
'METADATA_PATH': 'satellite_rag_metadata.json',
|
||
'GRAPH_DATA_PATH': 'code_knowledge_graph.json',
|
||
'PROJECT_ROOT': '.'
|
||
}
|
||
if config_key in default_filenames:
|
||
final_path = Path(default_filenames[config_key])
|
||
else:
|
||
final_path = Path('.') # 回退到当前目录
|
||
|
||
# 尝试解析为绝对路径,并规范化
|
||
try:
|
||
return final_path.resolve()
|
||
except:
|
||
# 如果路径无效(如包含不存在的父目录),返回原始Path对象
|
||
return final_path
|
||
|
||
def ensure_directory_exists(file_path):
|
||
"""确保文件所在的目录存在"""
|
||
directory = os.path.dirname(file_path)
|
||
if directory and not os.path.exists(directory):
|
||
os.makedirs(directory, exist_ok=True)
|
||
logger.info(f"创建目录: {directory}")
|
||
return file_path
|
||
|
||
def check_api_key():
|
||
"""检查API Key配置"""
|
||
print("=" * 60)
|
||
print("检查API Key配置")
|
||
print("=" * 60)
|
||
|
||
if not QWEN_API_KEY or QWEN_API_KEY == "sk-your-api-key-here":
|
||
print("⚠ 警告: API Key未配置或为默认值")
|
||
print(f"当前API Key: {QWEN_API_KEY[:20]}..." if QWEN_API_KEY else "空")
|
||
print(f"API URL: {QWEN_API_URL}")
|
||
|
||
response = input("是否继续? (y/n): ").strip().lower()
|
||
if response != 'y':
|
||
print("请先配置 config.py 中的 QWEN_API_KEY 字段")
|
||
sys.exit(1)
|
||
else:
|
||
print(f"✓ API Key已配置: {QWEN_API_KEY[:20]}...")
|
||
print(f"✓ API URL: {QWEN_API_URL}")
|
||
|
||
return True
|
||
|
||
def check_knowledge_base_exists(runtime_config):
|
||
"""检查知识库是否已建设"""
|
||
print("\n检查知识库文件...")
|
||
|
||
# 使用统一的路径解析函数获取最终路径
|
||
vector_db_path = get_final_path(
|
||
'VECTOR_DB_PATH', '', USER_VECTOR_DB_PATH,
|
||
INTERNAL_DEFAULT_VECTOR_DB_PATH, runtime_config
|
||
)
|
||
metadata_path = get_final_path(
|
||
'METADATA_PATH', '', USER_METADATA_PATH,
|
||
INTERNAL_DEFAULT_METADATA_PATH, runtime_config
|
||
)
|
||
|
||
required_files = [vector_db_path, metadata_path]
|
||
existing_files = []
|
||
missing_files = []
|
||
|
||
for file_path in required_files:
|
||
if os.path.exists(file_path):
|
||
existing_files.append(file_path)
|
||
else:
|
||
missing_files.append(file_path)
|
||
|
||
if existing_files:
|
||
print(f"✓ 找到 {len(existing_files)} 个知识库文件:")
|
||
for file in existing_files:
|
||
print(f" - {file}")
|
||
|
||
if missing_files:
|
||
print(f"⚠ 缺失 {len(missing_files)} 个知识库文件:")
|
||
for file in missing_files:
|
||
print(f" - {file}")
|
||
|
||
return len(missing_files) == 0, existing_files, missing_files, vector_db_path, metadata_path
|
||
|
||
def manual_configure_knowledge_base(runtime_config):
|
||
"""手动配置知识库文件地址"""
|
||
print("\n" + "=" * 60)
|
||
print("手动配置知识库文件")
|
||
print("=" * 60)
|
||
|
||
config_changes = {}
|
||
|
||
# 获取当前默认值用于显示
|
||
current_vector_db = get_final_path(
|
||
'VECTOR_DB_PATH', '', USER_VECTOR_DB_PATH,
|
||
INTERNAL_DEFAULT_VECTOR_DB_PATH, runtime_config
|
||
)
|
||
current_metadata = get_final_path(
|
||
'METADATA_PATH', '', USER_METADATA_PATH,
|
||
INTERNAL_DEFAULT_METADATA_PATH, runtime_config
|
||
)
|
||
|
||
# 询问向量数据库路径
|
||
vector_input = input(f"请输入FAISS向量数据库文件路径 (当前: {current_vector_db}): ").strip()
|
||
if vector_input:
|
||
vector_path = Path(vector_input)
|
||
config_changes['VECTOR_DB_PATH'] = str(vector_path)
|
||
else:
|
||
vector_path = current_vector_db
|
||
|
||
# 询问元数据文件路径
|
||
metadata_input = input(f"请输入元数据文件路径 (当前: {current_metadata}): ").strip()
|
||
if metadata_input:
|
||
metadata_path = Path(metadata_input)
|
||
config_changes['METADATA_PATH'] = str(metadata_path)
|
||
else:
|
||
metadata_path = current_metadata
|
||
|
||
# 验证文件
|
||
print(f"\n验证配置的文件...")
|
||
all_valid = True
|
||
|
||
try:
|
||
if os.path.exists(vector_path):
|
||
index = faiss.read_index(str(vector_path))
|
||
dimension = index.d
|
||
print(f"✓ FAISS索引: {vector_path} (维度: {dimension})")
|
||
else:
|
||
print(f"⚠ 文件不存在: {vector_path}")
|
||
all_valid = False
|
||
except Exception as e:
|
||
print(f"⚠ 加载FAISS索引失败: {e}")
|
||
all_valid = False
|
||
|
||
try:
|
||
if os.path.exists(metadata_path):
|
||
with open(metadata_path, 'r', encoding='utf-8') as f:
|
||
metadata = json.load(f)
|
||
print(f"✓ 元数据文件: {metadata_path} (包含 {len(metadata)} 个函数)")
|
||
else:
|
||
print(f"⚠ 文件不存在: {metadata_path}")
|
||
all_valid = False
|
||
except Exception as e:
|
||
print(f"⚠ 加载元数据失败: {e}")
|
||
all_valid = False
|
||
|
||
if config_changes and all_valid:
|
||
# 更新运行时配置
|
||
runtime_config.update(config_changes)
|
||
save_config = input("\n是否保存此配置供下次使用? (y/n): ").strip().lower()
|
||
if save_config == 'y':
|
||
if save_runtime_config(runtime_config):
|
||
print(f"✓ 配置已保存到运行时文件: {RUNTIME_CONFIG_FILE}")
|
||
return True
|
||
elif not all_valid:
|
||
print("\n配置验证失败,请检查文件路径。")
|
||
return False
|
||
else:
|
||
print("\n使用现有配置,无更改。")
|
||
return True
|
||
|
||
|
||
def load_knowledge_base(runtime_config):
|
||
"""加载知识库文件"""
|
||
print("\n" + "=" * 60)
|
||
print("加载知识库")
|
||
print("=" * 60)
|
||
|
||
# 检查知识库
|
||
kb_exists, existing_files, missing_files, vector_db_path, metadata_path = check_knowledge_base_exists(
|
||
runtime_config)
|
||
|
||
if kb_exists:
|
||
print("\n✓ 使用现有知识库配置")
|
||
|
||
# 验证路径是否为空
|
||
if not str(vector_db_path).strip():
|
||
print("⚠ 错误: 向量数据库路径为空!")
|
||
print("正在尝试使用默认值...")
|
||
vector_db_path = Path("satellite_rag.faiss")
|
||
|
||
if not str(metadata_path).strip():
|
||
print("⚠ 错误: 元数据文件路径为空!")
|
||
print("正在尝试使用默认值...")
|
||
metadata_path = Path("satellite_rag_metadata.json")
|
||
|
||
return {
|
||
'vector_db_path': str(vector_db_path),
|
||
'metadata_path': str(metadata_path),
|
||
'graph_data_path': str(get_final_path(
|
||
'GRAPH_DATA_PATH', '', USER_GRAPH_DATA_PATH,
|
||
INTERNAL_DEFAULT_GRAPH_DATA_PATH, runtime_config
|
||
))
|
||
}
|
||
else:
|
||
print("\n知识库文件不完整,需要配置。")
|
||
if manual_configure_knowledge_base(runtime_config):
|
||
# 重新检查
|
||
kb_exists, existing_files, missing_files, vector_db_path, metadata_path = check_knowledge_base_exists(
|
||
runtime_config)
|
||
if kb_exists:
|
||
return {
|
||
'vector_db_path': str(vector_db_path),
|
||
'metadata_path': str(metadata_path),
|
||
'graph_data_path': str(get_final_path(
|
||
'GRAPH_DATA_PATH', '', USER_GRAPH_DATA_PATH,
|
||
INTERNAL_DEFAULT_GRAPH_DATA_PATH, runtime_config
|
||
))
|
||
}
|
||
return None
|
||
|
||
def feature_retrieval_mode(runtime_config):
|
||
"""功能需求检索模式(主动引导路径输入版)"""
|
||
print("\n" + "=" * 60)
|
||
print("功能需求检索分析")
|
||
print("=" * 60)
|
||
|
||
# 第一步:主动引导用户配置或确认知识库路径
|
||
print("请配置知识库路径(直接按Enter键将使用括号内显示的默认路径):")
|
||
|
||
# 获取当前根据优先级计算出的默认路径(用于提示用户)
|
||
default_vector_path = get_final_path(
|
||
'VECTOR_DB_PATH', '', USER_VECTOR_DB_PATH,
|
||
INTERNAL_DEFAULT_VECTOR_DB_PATH, runtime_config
|
||
)
|
||
default_metadata_path = get_final_path(
|
||
'METADATA_PATH', '', USER_METADATA_PATH,
|
||
INTERNAL_DEFAULT_METADATA_PATH, runtime_config
|
||
)
|
||
|
||
# 引导输入向量数据库路径
|
||
vector_prompt = f"请输入FAISS向量数据库文件路径 (默认: {default_vector_path}): "
|
||
vector_input = input(vector_prompt).strip()
|
||
|
||
# 引导输入元数据文件路径
|
||
metadata_prompt = f"请输入元数据文件路径 (默认: {default_metadata_path}): "
|
||
metadata_input = input(metadata_prompt).strip()
|
||
|
||
# 使用用户输入(或空输入)重新计算最终路径
|
||
# 注意:这里将用户输入传入 get_final_path,它会自动更新 runtime_config
|
||
final_vector_path = get_final_path(
|
||
'VECTOR_DB_PATH', vector_input, USER_VECTOR_DB_PATH,
|
||
INTERNAL_DEFAULT_VECTOR_DB_PATH, runtime_config
|
||
)
|
||
final_metadata_path = get_final_path(
|
||
'METADATA_PATH', metadata_input, USER_METADATA_PATH,
|
||
INTERNAL_DEFAULT_METADATA_PATH, runtime_config
|
||
)
|
||
|
||
# 检查文件是否存在
|
||
print(f"\n检查知识库文件...")
|
||
files_exist = True
|
||
if not os.path.exists(final_vector_path):
|
||
print(f"⚠ 向量数据库文件不存在: {final_vector_path}")
|
||
files_exist = False
|
||
else:
|
||
print(f"✓ 向量数据库: {final_vector_path}")
|
||
|
||
if not os.path.exists(final_metadata_path):
|
||
print(f"⚠ 元数据文件不存在: {final_metadata_path}")
|
||
files_exist = False
|
||
else:
|
||
print(f"✓ 元数据文件: {final_metadata_path}")
|
||
|
||
if not files_exist:
|
||
print("\n知识库文件缺失。")
|
||
choice = input("请选择: 1. 手动指定其他路径 2. 返回主菜单\n选择 (1-2): ").strip()
|
||
if choice == "1":
|
||
# 递归调用自身,重新引导配置
|
||
return feature_retrieval_mode(runtime_config)
|
||
else:
|
||
print("功能检索取消,返回主菜单。")
|
||
return
|
||
|
||
# 构建传递给检索器的配置
|
||
config = {
|
||
'vector_db_path': str(final_vector_path),
|
||
'metadata_path': str(final_metadata_path),
|
||
'graph_data_path': str(get_final_path(
|
||
'GRAPH_DATA_PATH', '', USER_GRAPH_DATA_PATH,
|
||
INTERNAL_DEFAULT_GRAPH_DATA_PATH, runtime_config
|
||
))
|
||
}
|
||
|
||
# 询问用户是否保存此次配置供下次使用
|
||
if vector_input or metadata_input: # 只有当用户本次有输入时才询问保存
|
||
save_choice = input(f"\n是否将此次的路径配置保存,供下次程序启动时默认使用? (y/n): ").strip().lower()
|
||
if save_choice == 'y':
|
||
if save_runtime_config(runtime_config):
|
||
print(f"✓ 配置已保存至: {RUNTIME_CONFIG_FILE}")
|
||
else:
|
||
print("⚠ 配置保存失败。")
|
||
|
||
# 第二步:加载知识库并进入检索循环
|
||
print("\n" + "=" * 60)
|
||
print("加载知识库并初始化检索器...")
|
||
print("=" * 60)
|
||
|
||
try:
|
||
retriever = FeatureRetriever(config)
|
||
if not retriever.load_knowledge_base():
|
||
print("加载知识库失败!可能文件格式不正确或已损坏。")
|
||
retry = input("是否重新配置路径? (y/n): ").strip().lower()
|
||
if retry == 'y':
|
||
return feature_retrieval_mode(runtime_config) # 重新开始
|
||
else:
|
||
print("功能检索取消,返回主菜单。")
|
||
return
|
||
|
||
print(f"知识库加载成功: {len(retriever.metadatas)} 个函数")
|
||
print("\n请输入自然语言描述功能需求进行分析")
|
||
print("输入 'quit' 或 'exit' 返回主菜单")
|
||
|
||
while True:
|
||
query = input("\n功能需求: ").strip()
|
||
if query.lower() in ["quit", "exit"]:
|
||
break
|
||
|
||
if not query:
|
||
continue
|
||
|
||
# 执行分析
|
||
result = retriever.analyze_with_multiple_constraints(query)
|
||
|
||
# 显示结果
|
||
print(f"\n分析结果:")
|
||
print(f"实现状态: {'✅ 已实现' if result['implemented'] else '❌ 未实现'}")
|
||
print(f"综合评分: {result.get('total_score', 0):.3f}")
|
||
print(f"判断理由: {result.get('reason', '无理由')}")
|
||
|
||
if result.get("most_relevant_function"):
|
||
rel_func = result["most_relevant_function"]
|
||
print(f"最相关函数: {rel_func.get('name')}")
|
||
|
||
except Exception as e:
|
||
print(f"分析失败: {e}")
|
||
logger.error(f"功能检索失败: {e}", exc_info=True)
|
||
# 发生异常时,也提供重新配置的选项
|
||
retry = input("\n发生错误,是否重新配置知识库路径? (y/n): ").strip().lower()
|
||
if retry == 'y':
|
||
feature_retrieval_mode(runtime_config)
|
||
|
||
|
||
def issue_filter_mode(runtime_config):
|
||
"""问题单过滤模式(修正知识库传递问题)"""
|
||
print("\n" + "=" * 60)
|
||
print("问题单过滤模式")
|
||
print("=" * 60)
|
||
|
||
# 第一步:首先引导用户配置知识库路径(先输入,不输入则默认)
|
||
print("第一步:配置知识库路径")
|
||
|
||
# 获取当前默认的知识库路径
|
||
default_vector_path = get_final_path(
|
||
'VECTOR_DB_PATH', '', USER_VECTOR_DB_PATH,
|
||
INTERNAL_DEFAULT_VECTOR_DB_PATH, runtime_config
|
||
)
|
||
default_metadata_path = get_final_path(
|
||
'METADATA_PATH', '', USER_METADATA_PATH,
|
||
INTERNAL_DEFAULT_METADATA_PATH, runtime_config
|
||
)
|
||
|
||
# 引导用户输入向量数据库路径
|
||
vector_prompt = f"请输入向量数据库文件路径 (FAISS索引文件)\n(直接按Enter使用当前默认值: {default_vector_path}): "
|
||
vector_input = input(vector_prompt).strip()
|
||
|
||
# 使用用户输入重新计算最终路径
|
||
final_vector_path = get_final_path(
|
||
'VECTOR_DB_PATH', vector_input, USER_VECTOR_DB_PATH,
|
||
INTERNAL_DEFAULT_VECTOR_DB_PATH, runtime_config
|
||
)
|
||
|
||
if not vector_input:
|
||
print(f"使用默认向量数据库: {final_vector_path}")
|
||
|
||
# 引导用户输入元数据文件路径
|
||
metadata_prompt = f"请输入元数据文件路径\n(直接按Enter使用当前默认值: {default_metadata_path}): "
|
||
metadata_input = input(metadata_prompt).strip()
|
||
|
||
# 使用用户输入重新计算最终路径
|
||
final_metadata_path = get_final_path(
|
||
'METADATA_PATH', metadata_input, USER_METADATA_PATH,
|
||
INTERNAL_DEFAULT_METADATA_PATH, runtime_config
|
||
)
|
||
|
||
if not metadata_input:
|
||
print(f"使用默认元数据文件: {final_metadata_path}")
|
||
|
||
# 检查知识库文件是否存在
|
||
print(f"\n检查知识库文件...")
|
||
files_exist = True
|
||
|
||
if not os.path.exists(final_vector_path):
|
||
print(f"⚠ 向量数据库文件不存在: {final_vector_path}")
|
||
files_exist = False
|
||
else:
|
||
print(f"✓ 向量数据库: {final_vector_path}")
|
||
|
||
if not os.path.exists(final_metadata_path):
|
||
print(f"⚠ 元数据文件不存在: {final_metadata_path}")
|
||
files_exist = False
|
||
else:
|
||
print(f"✓ 元数据文件: {final_metadata_path}")
|
||
|
||
if not files_exist:
|
||
print("\n知识库文件缺失,无法进行问题单过滤。")
|
||
print("请先使用主菜单选项1创建知识库,或确保文件路径正确。")
|
||
return
|
||
|
||
# 构建知识库配置字典
|
||
kb_config = {
|
||
'vector_db_path': str(final_vector_path),
|
||
'metadata_path': str(final_metadata_path),
|
||
'graph_data_path': str(get_final_path(
|
||
'GRAPH_DATA_PATH', '', USER_GRAPH_DATA_PATH,
|
||
INTERNAL_DEFAULT_GRAPH_DATA_PATH, runtime_config
|
||
))
|
||
}
|
||
|
||
# 询问是否保存此次配置
|
||
if vector_input or metadata_input:
|
||
save_choice = input(f"\n是否将此知识库配置保存供下次使用? (y/n): ").strip().lower()
|
||
if save_choice == 'y':
|
||
if save_runtime_config(runtime_config):
|
||
print(f"✓ 配置已保存至运行时配置文件")
|
||
|
||
print("\n✓ 知识库配置完成")
|
||
print("\n第二步:配置问题单过滤参数")
|
||
|
||
# 第二步:配置问题单过滤的其他参数
|
||
default_input_excel = "科代问题单样例.xlsx"
|
||
|
||
# 首先检查默认文件是否存在
|
||
if not os.path.exists(default_input_excel):
|
||
print(f"⚠ 注意: 默认输入文件 '{default_input_excel}' 不存在")
|
||
|
||
input_prompt = f"请输入待过滤的问题单Excel文件路径\n(直接按Enter使用默认值: {default_input_excel}): "
|
||
input_excel = input(input_prompt).strip()
|
||
|
||
# 用户不输入时使用默认值
|
||
if not input_excel:
|
||
input_excel = default_input_excel
|
||
print(f"使用默认输入文件: {input_excel}")
|
||
|
||
# 路径检查和修正逻辑...
|
||
# [保持您原有的路径检查逻辑,但这里简化展示]
|
||
if not os.path.exists(input_excel):
|
||
print(f"文件不存在: {input_excel}")
|
||
print("问题单过滤取消。")
|
||
return
|
||
|
||
# 输出文件路径
|
||
default_output_json = "filtered_defects.json"
|
||
|
||
output_prompt = f"请输入输出JSON文件路径\n(直接按Enter使用默认值: {default_output_json}): "
|
||
output_json = input(output_prompt).strip()
|
||
|
||
if not output_json:
|
||
output_json = default_output_json
|
||
print(f"使用默认输出文件: {output_json}")
|
||
|
||
# 项目根目录路径
|
||
default_project_root = get_final_path(
|
||
'PROJECT_ROOT', '', USER_PROJECT_ROOT,
|
||
INTERNAL_DEFAULT_PROJECT_ROOT, runtime_config
|
||
)
|
||
|
||
project_prompt = f"请输入问题单中代码文件所对应的项目根目录路径\n(直接按Enter使用当前默认值: {default_project_root}): "
|
||
project_input = input(project_prompt).strip()
|
||
|
||
final_project_root = get_final_path(
|
||
'PROJECT_ROOT', project_input, USER_PROJECT_ROOT,
|
||
INTERNAL_DEFAULT_PROJECT_ROOT, runtime_config
|
||
)
|
||
|
||
if not project_input:
|
||
print(f"使用默认项目路径: {final_project_root}")
|
||
|
||
if not os.path.isdir(final_project_root):
|
||
print(f"项目路径无效: {final_project_root}")
|
||
print("问题单过滤取消。")
|
||
return
|
||
|
||
# 保存项目路径配置(如果用户有输入)
|
||
if project_input:
|
||
runtime_config['PROJECT_ROOT'] = str(final_project_root)
|
||
save_runtime_config(runtime_config)
|
||
|
||
print("\n" + "=" * 60)
|
||
print("开始问题单过滤分析...")
|
||
print("=" * 60)
|
||
print(f"输入文件: {input_excel}")
|
||
print(f"输出文件: {output_json}")
|
||
print(f"项目路径: {final_project_root}")
|
||
print("=" * 60)
|
||
|
||
try:
|
||
# 关键:确保传递知识库路径参数
|
||
success = run_issue_filtering(
|
||
input_xlsx_path=input_excel,
|
||
output_json_path=output_json,
|
||
project_path_override=str(final_project_root),
|
||
# 明确传递知识库路径
|
||
kb_vector_path=str(final_vector_path), # 确保这个变量存在且正确
|
||
kb_metadata_path=str(final_metadata_path) # 确保这个变量存在且正确
|
||
)
|
||
|
||
if success:
|
||
print(f"\n" + "=" * 60)
|
||
print("✓ 问题单过滤完成!")
|
||
print(f"结果已保存至: {output_json}")
|
||
print("=" + "=" * 60)
|
||
|
||
# 结果查看逻辑
|
||
while True:
|
||
view_option = input(
|
||
"\n请选择操作:\n1. 查看统计摘要\n2. 打开结果文件\n3. 返回主菜单\n选择 (1-3): ").strip()
|
||
|
||
if view_option == "1":
|
||
try:
|
||
with open(output_json, 'r', encoding='utf-8') as f:
|
||
data = json.load(f)
|
||
if not data:
|
||
print("结果文件为空。")
|
||
continue
|
||
|
||
true_positives = sum(1 for r in data if r["analysis_result"].get("is_real_defect") is True)
|
||
false_positives = sum(1 for r in data if r["analysis_result"].get("is_real_defect") is False)
|
||
unknown = len(data) - true_positives - false_positives
|
||
|
||
high_urgency = sum(1 for r in data if r["analysis_result"].get("urgency_score", 0) >= 70)
|
||
medium_urgency = sum(1 for r in data if 40 <= r["analysis_result"].get("urgency_score", 0) < 70)
|
||
low_urgency = sum(1 for r in data if 0 < r["analysis_result"].get("urgency_score", 0) < 40)
|
||
|
||
total_affected = sum(len(r["analysis_result"].get("affected_functions", [])) for r in data)
|
||
defects_with_affected = sum(1 for r in data if r["analysis_result"].get("affected_functions"))
|
||
|
||
print(f"\n统计摘要:")
|
||
print(f" 总缺陷数: {len(data)}")
|
||
print(f" 真实缺陷: {true_positives} 条 ({true_positives / len(data) * 100:.1f}%)")
|
||
print(f" 误报缺陷: {false_positives} 条 ({false_positives / len(data) * 100:.1f}%)")
|
||
print(f" 未知/错误: {unknown} 条 ({unknown / len(data) * 100:.1f}%)")
|
||
print(f"\n紧急程度分布:")
|
||
print(f" 高紧急(≥70): {high_urgency} 条")
|
||
print(f" 中紧急(40-69): {medium_urgency} 条")
|
||
print(f" 低紧急(1-39): {low_urgency} 条")
|
||
print(f" 零分(非缺陷): {false_positives} 条")
|
||
print(f"\n影响域分析:")
|
||
print(f" {defects_with_affected} 个缺陷影响了 {total_affected} 个函数")
|
||
|
||
sheet_counts = {}
|
||
for item in data:
|
||
sheet_name = item.get("sheet_name", "unknown")
|
||
sheet_counts[sheet_name] = sheet_counts.get(sheet_name, 0) + 1
|
||
|
||
if sheet_counts:
|
||
print(f"\n工作表分布:")
|
||
for sheet, count in sheet_counts.items():
|
||
print(f" {sheet}: {count} 条")
|
||
|
||
except Exception as e:
|
||
print(f"读取结果文件失败: {e}")
|
||
|
||
elif view_option == "2":
|
||
try:
|
||
import subprocess
|
||
import platform
|
||
with open(output_json, 'r', encoding='utf-8') as f:
|
||
data = json.load(f)
|
||
if not data:
|
||
print("结果文件为空。")
|
||
continue
|
||
|
||
print(f"\n结果文件包含 {len(data)} 条记录。")
|
||
view_method = input(
|
||
"查看方式:\n1. 完整JSON文件\n2. 仅真实缺陷\n3. 高紧急缺陷(≥70分)\n选择 (1-3): ").strip()
|
||
|
||
if view_method == "1":
|
||
if platform.system() == "Windows":
|
||
os.startfile(output_json)
|
||
elif platform.system() == "Darwin":
|
||
subprocess.run(["open", output_json])
|
||
else:
|
||
subprocess.run(["xdg-open", output_json])
|
||
print(f"已使用默认程序打开: {output_json}")
|
||
|
||
elif view_method in ["2", "3"]:
|
||
if view_method == "2":
|
||
filtered_data = [item for item in data if
|
||
item["analysis_result"].get("is_real_defect") is True]
|
||
print(f"\n真实缺陷 ({len(filtered_data)} 条):")
|
||
else:
|
||
filtered_data = [item for item in data if
|
||
item["analysis_result"].get("urgency_score", 0) >= 70]
|
||
print(f"\n高紧急缺陷 ({len(filtered_data)} 条):")
|
||
|
||
for i, item in enumerate(filtered_data[:10], 1):
|
||
print(f"\n{i}. 工作表: {item.get('sheet_name')}, 行: {item.get('row_index')}")
|
||
print(
|
||
f" 文件: {os.path.basename(item.get('file_path', ''))}:{item.get('line_number')}")
|
||
print(f" 描述: {item.get('defect_description', '')[:80]}...")
|
||
print(f" 真实缺陷: {item['analysis_result'].get('is_real_defect')}")
|
||
print(f" 紧急分数: {item['analysis_result'].get('urgency_score', 0)}")
|
||
if item['analysis_result'].get('affected_functions'):
|
||
print(
|
||
f" 影响函数: {', '.join(item['analysis_result'].get('affected_functions', []))}")
|
||
|
||
if len(filtered_data) > 10:
|
||
print(f"\n... 还有 {len(filtered_data) - 10} 条未显示")
|
||
|
||
save_filtered = input(f"\n是否将筛选结果保存为新文件? (y/n): ").strip().lower()
|
||
if save_filtered == 'y':
|
||
filtered_file = output_json.replace('.json', '_filtered.json')
|
||
with open(filtered_file, 'w', encoding='utf-8') as f:
|
||
json.dump(filtered_data, f, indent=2, ensure_ascii=False)
|
||
print(f"筛选结果已保存至: {filtered_file}")
|
||
|
||
except Exception as e:
|
||
print(f"打开或处理结果文件失败: {e}")
|
||
|
||
elif view_option == "3":
|
||
break
|
||
else:
|
||
print("无效选择,请重新输入")
|
||
|
||
else:
|
||
print("\n" + "=" * 60)
|
||
print("✗ 问题单过滤过程失败。")
|
||
print("=" + "=" * 60)
|
||
|
||
except Exception as e:
|
||
print(f"\n问题单过滤执行失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
|
||
def static_analysis_mode(runtime_config):
|
||
"""静态分析模式(遵循"先输入,不输入则默认"原则)"""
|
||
print("\n" + "=" * 60)
|
||
print("静态分析模式")
|
||
print("=" * 60)
|
||
|
||
# 1. 首先检查并配置知识库(静态分析前必须指定知识库)
|
||
print("第一步:配置知识库")
|
||
|
||
# --- 修改开始:使用与项目路径相同的交互逻辑 ---
|
||
# 使用get_final_path计算当前默认的知识库路径
|
||
default_vector_path = get_final_path(
|
||
'VECTOR_DB_PATH', '', USER_VECTOR_DB_PATH,
|
||
INTERNAL_DEFAULT_VECTOR_DB_PATH, runtime_config
|
||
)
|
||
default_metadata_path = get_final_path(
|
||
'METADATA_PATH', '', USER_METADATA_PATH,
|
||
INTERNAL_DEFAULT_METADATA_PATH, runtime_config
|
||
)
|
||
|
||
# 引导输入向量数据库路径
|
||
vector_prompt = f"请输入FAISS向量数据库文件路径\n(直接按Enter使用当前默认值: {default_vector_path}): "
|
||
vector_input = input(vector_prompt).strip()
|
||
|
||
# 引导输入元数据文件路径
|
||
metadata_prompt = f"请输入元数据文件路径\n(直接按Enter使用当前默认值: {default_metadata_path}): "
|
||
metadata_input = input(metadata_prompt).strip()
|
||
|
||
# 使用用户输入(或空输入)重新计算最终路径
|
||
final_vector_path = get_final_path(
|
||
'VECTOR_DB_PATH', vector_input, USER_VECTOR_DB_PATH,
|
||
INTERNAL_DEFAULT_VECTOR_DB_PATH, runtime_config
|
||
)
|
||
final_metadata_path = get_final_path(
|
||
'METADATA_PATH', metadata_input, USER_METADATA_PATH,
|
||
INTERNAL_DEFAULT_METADATA_PATH, runtime_config
|
||
)
|
||
|
||
# 检查文件是否存在
|
||
print(f"\n检查知识库文件...")
|
||
files_exist = True
|
||
if not os.path.exists(final_vector_path):
|
||
print(f"⚠ 向量数据库文件不存在: {final_vector_path}")
|
||
files_exist = False
|
||
else:
|
||
print(f"✓ 向量数据库: {final_vector_path}")
|
||
|
||
if not os.path.exists(final_metadata_path):
|
||
print(f"⚠ 元数据文件不存在: {final_metadata_path}")
|
||
files_exist = False
|
||
else:
|
||
print(f"✓ 元数据文件: {final_metadata_path}")
|
||
|
||
if not files_exist:
|
||
print("\n知识库文件缺失。")
|
||
choice = input("请选择: 1. 手动指定其他路径 2. 返回主菜单\n选择 (1-2): ").strip()
|
||
if choice == "1":
|
||
# 递归调用自身,重新引导配置
|
||
return static_analysis_mode(runtime_config)
|
||
else:
|
||
print("静态分析取消,返回主菜单。")
|
||
return
|
||
|
||
# 构建知识库配置字典
|
||
config = {
|
||
'vector_db_path': str(final_vector_path),
|
||
'metadata_path': str(final_metadata_path),
|
||
'graph_data_path': str(get_final_path(
|
||
'GRAPH_DATA_PATH', '', USER_GRAPH_DATA_PATH,
|
||
INTERNAL_DEFAULT_GRAPH_DATA_PATH, runtime_config
|
||
))
|
||
}
|
||
|
||
# 询问是否保存此次配置(如果用户本次有输入)
|
||
if vector_input or metadata_input:
|
||
save_choice = input(f"\n是否将此次的知识库配置保存,供下次程序启动时默认使用? (y/n): ").strip().lower()
|
||
if save_choice == 'y':
|
||
if save_runtime_config(runtime_config):
|
||
print(f"✓ 配置已保存至: {RUNTIME_CONFIG_FILE}")
|
||
else:
|
||
print("⚠ 配置保存失败。")
|
||
# --- 修改结束 ---
|
||
|
||
print("✓ 知识库配置成功")
|
||
print("\n请配置静态分析参数:")
|
||
|
||
# 2. 获取要分析的项目路径 - 先显示默认值,再提示输入
|
||
# 使用get_final_path计算当前默认项目路径
|
||
default_project_root = get_final_path(
|
||
'PROJECT_ROOT', '', USER_PROJECT_ROOT,
|
||
INTERNAL_DEFAULT_PROJECT_ROOT, runtime_config
|
||
)
|
||
|
||
project_prompt = f"请输入要分析的C/C++项目根目录路径\n(直接按Enter使用当前默认值: {default_project_root}): "
|
||
project_input = input(project_prompt).strip()
|
||
# ...(后续代码保持不变)...
|
||
|
||
# 使用用户输入重新计算最终路径
|
||
project_path = get_final_path(
|
||
'PROJECT_ROOT', project_input, USER_PROJECT_ROOT,
|
||
INTERNAL_DEFAULT_PROJECT_ROOT, runtime_config
|
||
)
|
||
|
||
# 如果用户没有输入,使用默认值
|
||
if not project_input:
|
||
print(f"使用默认项目路径: {project_path}")
|
||
|
||
# 路径验证
|
||
if not project_path.exists():
|
||
print(f"⚠ 路径不存在: {project_path}")
|
||
retry = input("是否重新输入? (y/n): ").strip().lower()
|
||
if retry != 'y':
|
||
print("静态分析取消。")
|
||
return
|
||
|
||
# 重新获取输入
|
||
while True:
|
||
new_input = input("请输入正确的项目根目录路径: ").strip()
|
||
if not new_input:
|
||
print("路径不能为空")
|
||
continue
|
||
|
||
new_path = get_final_path(
|
||
'PROJECT_ROOT', new_input, USER_PROJECT_ROOT,
|
||
INTERNAL_DEFAULT_PROJECT_ROOT, runtime_config
|
||
)
|
||
|
||
if not new_path.exists():
|
||
print(f"路径不存在: {new_path}")
|
||
continue
|
||
|
||
if not new_path.is_dir():
|
||
print(f"路径不是目录: {new_path}")
|
||
continue
|
||
|
||
project_path = new_path
|
||
project_input = new_input
|
||
break
|
||
|
||
if not project_path.is_dir():
|
||
print(f"⚠ 路径不是目录: {project_path}")
|
||
retry = input("是否重新输入? (y/n): ").strip().lower()
|
||
if retry != 'y':
|
||
print("静态分析取消。")
|
||
return
|
||
|
||
# 重新获取输入
|
||
while True:
|
||
new_input = input("请输入正确的项目根目录路径: ").strip()
|
||
if not new_input:
|
||
print("路径不能为空")
|
||
continue
|
||
|
||
new_path = get_final_path(
|
||
'PROJECT_ROOT', new_input, USER_PROJECT_ROOT,
|
||
INTERNAL_DEFAULT_PROJECT_ROOT, runtime_config
|
||
)
|
||
|
||
if not new_path.is_dir():
|
||
print(f"路径不是目录: {new_path}")
|
||
continue
|
||
|
||
project_path = new_path
|
||
project_input = new_input
|
||
break
|
||
|
||
# 3. 获取规则文件路径 - 先显示默认值,再提示输入
|
||
default_rules = "审查规则.xlsx"
|
||
|
||
# 首先检查默认文件是否存在
|
||
if not os.path.exists(default_rules):
|
||
print(f"⚠ 注意: 默认规则文件 '{default_rules}' 不存在")
|
||
|
||
rules_prompt = f"请输入审查规则Excel文件路径\n(直接按Enter使用默认值: {default_rules}): "
|
||
rules_input = input(rules_prompt).strip()
|
||
|
||
# 用户不输入时使用默认值
|
||
if not rules_input:
|
||
rules_excel = default_rules
|
||
print(f"使用默认规则文件: {rules_excel}")
|
||
else:
|
||
rules_excel = rules_input
|
||
|
||
# 路径验证
|
||
rules_path = Path(rules_excel)
|
||
if not rules_path.exists():
|
||
print(f"⚠ 规则文件不存在: {rules_path}")
|
||
|
||
# 如果用户输入了但文件不存在,询问是否重新输入
|
||
if rules_excel != default_rules: # 用户输入了自定义路径
|
||
retry = input("文件不存在,是否重新输入路径? (y/n): ").strip().lower()
|
||
if retry == 'y':
|
||
# 重新获取输入
|
||
while True:
|
||
new_input = input("请输入正确的规则文件路径: ").strip()
|
||
if not new_input:
|
||
print("路径不能为空")
|
||
continue
|
||
|
||
new_path = Path(new_input)
|
||
if not new_path.exists():
|
||
print(f"文件不存在: {new_path}")
|
||
continue
|
||
|
||
rules_excel = new_input
|
||
rules_path = new_path
|
||
break
|
||
else:
|
||
print("静态分析取消。")
|
||
return
|
||
else: # 用户使用的是默认路径
|
||
print(f"默认规则文件 {default_rules} 不存在。")
|
||
manual_input = input("是否手动输入文件路径? (y/n): ").strip().lower()
|
||
if manual_input != 'y':
|
||
print("静态分析取消。")
|
||
return
|
||
while True:
|
||
new_input = input("请输入正确的规则文件路径: ").strip()
|
||
if not new_input:
|
||
print("路径不能为空")
|
||
continue
|
||
|
||
new_path = Path(new_input)
|
||
if not new_path.exists():
|
||
print(f"文件不存在: {new_path}")
|
||
continue
|
||
|
||
rules_excel = new_input
|
||
rules_path = new_path
|
||
break
|
||
|
||
# 4. 获取输出报告名称 - 先显示默认值,再提示输入
|
||
default_output = "audit_report"
|
||
|
||
output_prompt = f"请输入输出报告文件的基础名称 (不包含扩展名)\n(直接按Enter使用默认值: {default_output}): "
|
||
output_input = input(output_prompt).strip()
|
||
|
||
# 用户不输入时使用默认值
|
||
if not output_input:
|
||
output_base = default_output
|
||
print(f"使用默认输出报告名称: {output_base}")
|
||
else:
|
||
output_base = output_input
|
||
|
||
print("\n" + "=" * 60)
|
||
print("开始静态分析...")
|
||
print("=" * 60)
|
||
print(f"项目路径: {project_path}")
|
||
print(f"规则文件: {rules_path}")
|
||
print(f"输出报告: {output_base}")
|
||
print("=" * 60)
|
||
|
||
try:
|
||
success = run_static_analysis(
|
||
project_path=str(project_path),
|
||
rules_excel=str(rules_path),
|
||
output_report=output_base
|
||
)
|
||
|
||
if success:
|
||
print("\n" + "=" * 60)
|
||
print("✓ 静态分析执行成功!")
|
||
print("=" + "=" * 60)
|
||
|
||
# 保存更新后的运行时配置(如果用户有输入项目路径)
|
||
if project_input:
|
||
runtime_config['PROJECT_ROOT'] = str(project_path)
|
||
save_runtime_config(runtime_config)
|
||
print(f"✓ 项目路径已保存到运行时配置")
|
||
|
||
# 提供查看结果的选项
|
||
result_files = [
|
||
f"{output_base}.html",
|
||
f"{output_base}.pdf",
|
||
f"{output_base}.json"
|
||
]
|
||
|
||
existing_files = []
|
||
for file in result_files:
|
||
if os.path.exists(file):
|
||
existing_files.append(file)
|
||
|
||
if existing_files:
|
||
print(f"\n生成的报告文件:")
|
||
for file in existing_files:
|
||
print(f" - {file}")
|
||
|
||
view_option = input(f"\n是否打开HTML报告文件? (y/n): ").strip().lower()
|
||
if view_option == 'y' and os.path.exists(f"{output_base}.html"):
|
||
try:
|
||
import subprocess
|
||
import platform
|
||
html_file = f"{output_base}.html"
|
||
|
||
if platform.system() == "Windows":
|
||
os.startfile(html_file)
|
||
elif platform.system() == "Darwin":
|
||
subprocess.run(["open", html_file])
|
||
else:
|
||
subprocess.run(["xdg-open", html_file])
|
||
print(f"已使用默认程序打开: {html_file}")
|
||
except Exception as e:
|
||
print(f"打开报告文件失败: {e}")
|
||
else:
|
||
print("\n" + "=" * 60)
|
||
print("✗ 静态分析执行过程中遇到问题。")
|
||
print("=" + "=" * 60)
|
||
|
||
except Exception as e:
|
||
print(f"\n静态分析执行失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def main_menu():
|
||
"""主菜单界面"""
|
||
print("\n" + "=" * 60)
|
||
print("卫星星务软件代码RAG知识库系统")
|
||
print("=" * 60)
|
||
print("版本: 3.0")
|
||
print("功能: 支持代码知识库构建、功能检索、问题单过滤、静态分析")
|
||
print("=" * 60)
|
||
|
||
# 加载运行时配置
|
||
runtime_config = load_runtime_config()
|
||
if runtime_config:
|
||
print(f"已加载运行时配置 ({len(runtime_config)} 项)")
|
||
|
||
# 检查API Key
|
||
if not check_api_key():
|
||
return
|
||
|
||
while True:
|
||
print("\n" + "=" * 60)
|
||
print("主菜单")
|
||
print("=" * 60)
|
||
print("1. 创建项目知识库")
|
||
print("2. 功能需求检索")
|
||
print("3. 问题单过滤")
|
||
print("4. 静态分析")
|
||
print("5. 管理配置文件")
|
||
print("6. 退出系统")
|
||
print("=" * 60)
|
||
|
||
choice = input("\n请选择功能 (1-6): ").strip()
|
||
|
||
if choice == "1":
|
||
print("\n" + "=" * 60)
|
||
print("创建项目知识库")
|
||
print("=" * 60)
|
||
print("此功能将分析代码项目,构建知识图谱和向量数据库")
|
||
|
||
confirm = input("\n确认开始知识库构建? (y/n): ").strip().lower()
|
||
if confirm == 'y':
|
||
try:
|
||
result = build_rag_database_interactive()
|
||
if result:
|
||
print("\n知识库构建完成!")
|
||
except Exception as e:
|
||
print(f"知识库构建失败: {e}")
|
||
logger.error(f"知识库构建失败: {e}", exc_info=True)
|
||
|
||
elif choice == "2":
|
||
print("\n进入功能需求检索模式...")
|
||
kb_exists, _, _, _, _ = check_knowledge_base_exists(runtime_config)
|
||
if not kb_exists:
|
||
print("\n⚠ 尚未建设项目知识库!")
|
||
response = input("是否先创建或配置知识库? (y/n): ").strip().lower()
|
||
if response == 'y':
|
||
if not manual_configure_knowledge_base(runtime_config):
|
||
continue
|
||
else:
|
||
continue
|
||
feature_retrieval_mode(runtime_config)
|
||
|
||
elif choice == "3":
|
||
print("\n进入问题单过滤模式...")
|
||
kb_exists, _, _, _, _ = check_knowledge_base_exists(runtime_config)
|
||
if not kb_exists:
|
||
print("\n⚠ 尚未建设项目知识库!")
|
||
response = input("是否配置已有知识库文件? (y/n): ").strip().lower()
|
||
if response == 'y':
|
||
if not manual_configure_knowledge_base(runtime_config):
|
||
continue
|
||
else:
|
||
print("请先使用选项1创建知识库")
|
||
continue
|
||
issue_filter_mode(runtime_config)
|
||
|
||
elif choice == "4":
|
||
print("\n进入静态分析模式...")
|
||
static_analysis_mode(runtime_config)
|
||
|
||
elif choice == "5":
|
||
print("\n" + "=" * 60)
|
||
print("配置文件管理")
|
||
print("=" * 60)
|
||
print("1. 查看当前运行时配置")
|
||
print("2. 清除运行时配置")
|
||
print("3. 返回主菜单")
|
||
config_choice = input("\n请选择操作 (1-3): ").strip()
|
||
|
||
if config_choice == "1":
|
||
print(f"\n当前运行时配置 ({RUNTIME_CONFIG_FILE}):")
|
||
if runtime_config:
|
||
for key, value in runtime_config.items():
|
||
print(f" {key}: {value}")
|
||
else:
|
||
print(" 无运行时配置")
|
||
print(f"\n配置文件 (config.py) 中的主路径变量:")
|
||
print(f" PROJECT_ROOT: {PROJECT_ROOT}")
|
||
print(f" VECTOR_DB_PATH: {VECTOR_DB_PATH}")
|
||
print(f" METADATA_PATH: {METADATA_PATH}")
|
||
print(f" GRAPH_DATA_PATH: {GRAPH_DATA_PATH}")
|
||
print(f" KNOWLEDGE_GRAPH_PATH: {KNOWLEDGE_GRAPH_PATH}")
|
||
print(f"\n配置文件 (config.py) 中的默认后备路径 (Default_*):")
|
||
print(f" Default_PROJECT_ROOT: {Default_PROJECT_ROOT}")
|
||
print(f" Default_VECTOR_DB_PATH: {Default_VECTOR_DB_PATH}")
|
||
print(f" Default_METADATA_PATH: {Default_METADATA_PATH}")
|
||
input("\n按Enter键继续...")
|
||
|
||
elif config_choice == "2":
|
||
if os.path.exists(RUNTIME_CONFIG_FILE):
|
||
confirm = input(f"确定要删除运行时配置文件 {RUNTIME_CONFIG_FILE} 吗? (y/n): ").strip().lower()
|
||
if confirm == 'y':
|
||
os.remove(RUNTIME_CONFIG_FILE)
|
||
runtime_config.clear()
|
||
print("运行时配置文件已删除。")
|
||
else:
|
||
print("运行时配置文件不存在。")
|
||
|
||
elif config_choice == "3":
|
||
continue
|
||
else:
|
||
print("无效选择")
|
||
|
||
elif choice == "6":
|
||
print("\n感谢使用,再见!")
|
||
# 退出前保存配置
|
||
if runtime_config:
|
||
save_runtime_config(runtime_config)
|
||
break
|
||
|
||
else:
|
||
print("无效的选择,请重新输入")
|
||
|
||
if __name__ == "__main__":
|
||
try:
|
||
main_menu()
|
||
except KeyboardInterrupt:
|
||
print("\n\n程序被用户中断。")
|
||
except Exception as e:
|
||
print(f"\n程序执行出错: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
logger.error(f"主程序异常: {e}", exc_info=True) |