This commit is contained in:
2025-08-24 21:15:38 +08:00
parent 2325eb2fc8
commit dbcd40f604
13 changed files with 6534 additions and 363 deletions

View File

@@ -55,7 +55,8 @@ KNOWLEDGE_BASE_PATHS = [
# 初始化OpenAI客户端用于Qwen模型
qwen_client = OpenAI(
api_key=os.getenv("DASHSCOPE_API_KEY"),
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
timeout=15.0 # 设置15秒超时避免MCP初始化超时
)
# 3. --- 向量缓存管理 ---
@@ -192,36 +193,47 @@ def find_most_similar_files(question_embedding, file_embeddings, top_n=3, min_si
return results
# 新增:重排序函数
def rerank_files(question, file_matches, top_n=5):
def rerank_files(question, file_matches, top_n=3): # 减少默认数量
"""使用DashScope重排序API对文件进行重新排序"""
try:
# 限制输入数量以减少超时风险
if len(file_matches) > 5: # 进一步限制最大输入数量以避免超时
file_matches = file_matches[:5]
# 准备重排序输入
documents = []
for match in file_matches:
# 读取文件内容
try:
with open(match['path'], 'r', encoding='utf-8') as f:
content = f.read()[:2000] # 限制内容长度以提高效率
content = f.read()[:1500] # 进一步限制内容长度以提高效率
documents.append(content)
except Exception as e:
logging.error(f"读取文件 {match['path']} 失败: {e}")
continue
if not documents:
logging.warning("重排序时未能读取任何文件内容")
return file_matches[:top_n]
# 调用重排序API
# 调用重排序API,添加超时处理
import time
start_time = time.time()
response = dashscope.TextReRank.call(
model='gte-rerank',
query=question,
documents=documents
)
elapsed_time = time.time() - start_time
logging.info(f"重排序API调用耗时: {elapsed_time:.2f}")
if response.status_code == 200:
# 根据重排序结果重新排序文件
reranked_results = []
for i, result in enumerate(response.output['results']):
if i < len(file_matches):
if i < len(file_matches) and i < len(documents): # 添加边界检查
reranked_results.append({
'path': file_matches[i]['path'],
'similarity': result['relevance_score']
@@ -417,6 +429,7 @@ def analyze_question_with_llm(question: str) -> dict:
messages=messages,
temperature=0.0, # 使用最低温度确保输出稳定
max_tokens=300,
timeout=12.0, # 12秒超时避免MCP初始化超时
stop=["\n\n"] # 防止模型生成过多内容
)
@@ -494,13 +507,20 @@ def get_context(question: str) -> str:
"""
logging.info(f"收到问题: {question}")
# 使用LLM分析问题
analysis = analyze_question_with_llm(question)
keywords = analysis["search_keywords"]
if not keywords:
logging.warning("无法从问题中提取关键词。")
return "无法从问题中提取关键词,请提供更具体的信息"
try:
# 使用LLM分析问题添加超时保护
analysis = analyze_question_with_llm(question)
keywords = analysis["search_keywords"]
if not keywords:
logging.warning("无法从问题中提取关键词。")
return "无法从问题中提取关键词,请提供更具体的信息。"
except Exception as e:
logging.error(f"LLM分析失败使用备用方案: {e}")
# 备用方案:使用简单的关键词提取
keywords = find_keywords_in_question(question)
if not keywords:
return "无法分析问题,请检查网络连接或稍后重试。"
logging.info(f"提取到关键词: {keywords}")
@@ -549,7 +569,7 @@ def get_context(question: str) -> str:
# 3. 向量化和相似度计算 (精准筛选)
# 增加超时保护:限制向量化的文件数量
MAX_FILES_TO_VECTORIZE = 50 # 增加处理文件数量
MAX_FILES_TO_VECTORIZE = 10 # 进一步减少处理文件数量以避免超时
if len(candidate_files) > MAX_FILES_TO_VECTORIZE:
logging.warning(f"候选文件过多 ({len(candidate_files)}),仅处理前 {MAX_FILES_TO_VECTORIZE} 个。")
candidate_files = candidate_files[:MAX_FILES_TO_VECTORIZE]
@@ -559,27 +579,36 @@ def get_context(question: str) -> str:
return "无法生成问题向量请检查API连接或问题内容。"
file_embeddings = []
for file_path in candidate_files:
for i, file_path in enumerate(candidate_files):
try:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
# 添加处理进度日志
if i % 5 == 0: # 每5个文件记录一次进度
logging.info(f"正在处理第 {i+1}/{len(candidate_files)} 个文件: {os.path.basename(file_path)}")
file_embedding = get_embedding(content[:8000]) # 限制内容长度以提高效率
if file_embedding:
file_embeddings.append({'path': file_path, 'embedding': file_embedding})
except Exception as e:
logging.error(f"处理文件 {file_path} 时出错: {e}")
continue # 继续处理下一个文件,而不是完全失败
if not file_embeddings:
return "无法为任何候选文件生成向量。"
logging.warning("未能为任何候选文件生成向量。可能是由于API超时或其他错误。")
return "未能为任何候选文件生成向量,请稍后重试或减少搜索范围。"
# 找到最相似的多个文件
best_matches = find_most_similar_files(question_embedding, file_embeddings, top_n=10) # 增加返回数量以供重排序
best_matches = find_most_similar_files(question_embedding, file_embeddings, top_n=5) # 进一步减少返回数量以避免超时
if not best_matches:
return "计算向量相似度失败或没有找到足够相似的文件。"
# 新增:重排序处理
reranked_matches = rerank_files(question, best_matches, top_n=5)
# 新增:重排序处理(仅在找到足够多匹配项时执行)
if len(best_matches) > 2:
reranked_matches = rerank_files(question, best_matches, top_n=3) # 减少重排序数量
else:
reranked_matches = best_matches # 如果匹配项太少,跳过重排序以节省时间
# 提取代码内容
results_with_code = []
@@ -617,5 +646,19 @@ def get_context(question: str) -> str:
if __name__ == "__main__":
logging.info(f"Python Executable: {sys.executable}")
logging.info("RimWorld 向量知识库 (FastMCP版, v2.1-v4-model) 正在启动...")
# 快速启动:延迟初始化重量级组件
try:
# 验证基本配置
if not dashscope.api_key:
logging.warning("警告DASHSCOPE_API_KEY 未配置,部分功能可能受限。")
# 创建必要目录
os.makedirs(CACHE_DIR, exist_ok=True)
logging.info("MCP服务器快速启动完成等待客户端连接...")
except Exception as e:
logging.error(f"服务器启动时出错: {e}")
# 使用 'stdio' 传输协议
mcp.run(transport="stdio")