From 8fe96a83cf1fb61830150a40489ee7b29bce8c13 Mon Sep 17 00:00:00 2001 From: lintsinghua Date: Fri, 19 Dec 2025 16:08:26 +0800 Subject: [PATCH] =?UTF-8?q?feat(agent):=20=E4=BD=BF=E7=94=A8=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E9=85=8D=E7=BD=AE=E7=9A=84LLM=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E6=9B=BF=E4=BB=A3=E7=A1=AC=E7=BC=96=E7=A0=81=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 重构所有Agent和LLM服务,移除硬编码的temperature和max_tokens参数 添加get_analysis_config函数统一处理分析配置 在LLM测试接口中显示用户保存的配置参数 前端调试面板默认显示LLM测试详细信息 --- backend/app/api/v1/endpoints/config.py | 64 ++++++++- backend/app/api/v1/endpoints/scan.py | 21 +-- backend/app/services/agent/agents/analysis.py | 8 +- backend/app/services/agent/agents/base.py | 39 +++--- .../app/services/agent/agents/orchestrator.py | 3 +- backend/app/services/agent/agents/recon.py | 6 +- .../app/services/agent/agents/verification.py | 3 +- .../app/services/agent/graph/audit_graph.py | 9 +- backend/app/services/llm/service.py | 122 ++++++++++++++---- backend/app/services/scanner.py | 36 +++++- .../src/components/system/SystemConfig.tsx | 35 ++++- 11 files changed, 257 insertions(+), 89 deletions(-) diff --git a/backend/app/api/v1/endpoints/config.py b/backend/app/api/v1/endpoints/config.py index 0b1b1c0..0423f51 100644 --- a/backend/app/api/v1/endpoints/config.py +++ b/backend/app/api/v1/endpoints/config.py @@ -299,6 +299,7 @@ class LLMTestResponse(BaseModel): @router.post("/test-llm", response_model=LLMTestResponse) async def test_llm_connection( request: LLMTestRequest, + db: AsyncSession = Depends(get_db), current_user: User = Depends(deps.get_current_user), ) -> Any: """测试LLM连接是否正常""" @@ -309,12 +310,53 @@ async def test_llm_connection( import time start_time = time.time() + + # 获取用户保存的配置 + result = await db.execute( + select(UserConfig).where(UserConfig.user_id == current_user.id) + ) + user_config_record = result.scalar_one_or_none() + + # 解析用户配置 + saved_llm_config = {} + saved_other_config = {} + if user_config_record: + if user_config_record.llm_config: + saved_llm_config = decrypt_config( + json.loads(user_config_record.llm_config), + SENSITIVE_LLM_FIELDS + ) + if user_config_record.other_config: + saved_other_config = decrypt_config( + json.loads(user_config_record.other_config), + SENSITIVE_OTHER_FIELDS + ) + + # 从保存的配置中获取参数(用于调试显示) + saved_timeout_ms = saved_llm_config.get('llmTimeout', settings.LLM_TIMEOUT * 1000) + saved_temperature = saved_llm_config.get('llmTemperature', settings.LLM_TEMPERATURE) + saved_max_tokens = saved_llm_config.get('llmMaxTokens', settings.LLM_MAX_TOKENS) + saved_concurrency = saved_other_config.get('llmConcurrency', settings.LLM_CONCURRENCY) + saved_gap_ms = saved_other_config.get('llmGapMs', settings.LLM_GAP_MS) + saved_max_files = saved_other_config.get('maxAnalyzeFiles', settings.MAX_ANALYZE_FILES) + saved_output_lang = saved_other_config.get('outputLanguage', settings.OUTPUT_LANGUAGE) + debug_info = { "provider": request.provider, "model_requested": request.model, "base_url_requested": request.baseUrl, "api_key_length": len(request.apiKey) if request.apiKey else 0, "api_key_prefix": request.apiKey[:8] + "..." if request.apiKey and len(request.apiKey) > 8 else "(empty)", + # 用户保存的配置参数 + "saved_config": { + "timeout_ms": saved_timeout_ms, + "temperature": saved_temperature, + "max_tokens": saved_max_tokens, + "concurrency": saved_concurrency, + "gap_ms": saved_gap_ms, + "max_analyze_files": saved_max_files, + "output_language": saved_output_lang, + }, } try: @@ -346,11 +388,21 @@ async def test_llm_connection( model = request.model or DEFAULT_MODELS.get(provider) base_url = request.baseUrl or DEFAULT_BASE_URLS.get(provider, "") + # 测试时使用用户保存的所有配置参数 + test_timeout = int(saved_timeout_ms / 1000) if saved_timeout_ms else settings.LLM_TIMEOUT + test_temperature = saved_temperature if saved_temperature is not None else settings.LLM_TEMPERATURE + test_max_tokens = saved_max_tokens if saved_max_tokens else settings.LLM_MAX_TOKENS + debug_info["model_used"] = model debug_info["base_url_used"] = base_url debug_info["is_native_adapter"] = provider in NATIVE_ONLY_PROVIDERS + debug_info["test_params"] = { + "timeout": test_timeout, + "temperature": test_temperature, + "max_tokens": test_max_tokens, + } - print(f"[LLM Test] 开始测试: provider={provider.value}, model={model}, base_url={base_url}") + print(f"[LLM Test] 开始测试: provider={provider.value}, model={model}, base_url={base_url}, temperature={test_temperature}, timeout={test_timeout}s, max_tokens={test_max_tokens}") # 创建配置 config = LLMConfig( @@ -358,8 +410,9 @@ async def test_llm_connection( api_key=request.apiKey, model=model, base_url=request.baseUrl, - timeout=30, # 测试使用较短的超时时间 - max_tokens=50, # 测试使用较少的token + timeout=test_timeout, + temperature=test_temperature, + max_tokens=test_max_tokens, ) # 直接创建新的适配器实例(不使用缓存),确保使用最新的配置 @@ -375,13 +428,14 @@ async def test_llm_connection( adapter = LiteLLMAdapter(config) debug_info["adapter_type"] = "LiteLLMAdapter" # 获取 LiteLLM 实际使用的模型名 - debug_info["litellm_model"] = getattr(adapter, '_get_model_name', lambda: model)() if hasattr(adapter, '_get_model_name') else model + debug_info["litellm_model"] = getattr(adapter, '_get_litellm_model', lambda: model)() if hasattr(adapter, '_get_litellm_model') else model test_request = LLMRequest( messages=[ LLMMessage(role="user", content="Say 'Hello' in one word.") ], - max_tokens=50, + temperature=test_temperature, + max_tokens=test_max_tokens, ) print(f"[LLM Test] 发送测试请求...") diff --git a/backend/app/api/v1/endpoints/scan.py b/backend/app/api/v1/endpoints/scan.py index b246ee7..0b1bc20 100644 --- a/backend/app/api/v1/endpoints/scan.py +++ b/backend/app/api/v1/endpoints/scan.py @@ -20,7 +20,7 @@ from app.models.project import Project from app.models.analysis import InstantAnalysis from app.models.user_config import UserConfig from app.services.llm.service import LLMService -from app.services.scanner import task_control, is_text_file, should_exclude, get_language_from_path +from app.services.scanner import task_control, is_text_file, should_exclude, get_language_from_path, get_analysis_config from app.services.zip_storage import load_project_zip, save_project_zip, has_project_zip from app.core.config import settings @@ -93,6 +93,11 @@ async def process_zip_task(task_id: str, file_path: str, db_session_factory, use except: pass + # 获取分析配置(优先使用用户配置) + analysis_config = get_analysis_config(user_config) + max_analyze_files = analysis_config['max_analyze_files'] + llm_gap_ms = analysis_config['llm_gap_ms'] + # 限制文件数量 # 如果指定了特定文件,则只分析这些文件 target_files = scan_config.get('file_paths', []) @@ -101,13 +106,13 @@ async def process_zip_task(task_id: str, file_path: str, db_session_factory, use normalized_targets = {normalize_path(p) for p in target_files} print(f"🎯 ZIP任务: 指定分析 {len(normalized_targets)} 个文件") files_to_scan = [f for f in files_to_scan if f['path'] in normalized_targets] - elif settings.MAX_ANALYZE_FILES > 0: - files_to_scan = files_to_scan[:settings.MAX_ANALYZE_FILES] - + elif max_analyze_files > 0: + files_to_scan = files_to_scan[:max_analyze_files] + task.total_files = len(files_to_scan) await db.commit() - print(f"📊 ZIP任务 {task_id}: 找到 {len(files_to_scan)} 个文件") + print(f"📊 ZIP任务 {task_id}: 找到 {len(files_to_scan)} 个文件 (最大文件数: {max_analyze_files}, 请求间隔: {llm_gap_ms}ms)") total_issues = 0 total_lines = 0 @@ -178,12 +183,12 @@ async def process_zip_task(task_id: str, file_path: str, db_session_factory, use print(f"📈 ZIP任务 {task_id}: 进度 {scanned_files}/{len(files_to_scan)}") # 请求间隔 - await asyncio.sleep(settings.LLM_GAP_MS / 1000) - + await asyncio.sleep(llm_gap_ms / 1000) + except Exception as file_error: failed_files += 1 print(f"❌ ZIP任务分析文件失败 ({file_info['path']}): {file_error}") - await asyncio.sleep(settings.LLM_GAP_MS / 1000) + await asyncio.sleep(llm_gap_ms / 1000) # 完成任务 avg_quality_score = sum(quality_scores) / len(quality_scores) if quality_scores else 100.0 diff --git a/backend/app/services/agent/agents/analysis.py b/backend/app/services/agent/agents/analysis.py index f1e5c8e..ff94c38 100644 --- a/backend/app/services/agent/agents/analysis.py +++ b/backend/app/services/agent/agents/analysis.py @@ -452,12 +452,11 @@ class AnalysisAgent(BaseAgent): break # 调用 LLM 进行思考和决策(流式输出) - # 🔥 增加 max_tokens 到 4096,避免长输出被截断 + # 🔥 使用用户配置的 temperature 和 max_tokens try: llm_output, tokens_this_round = await self.stream_llm_call( self._conversation_history, - temperature=0.1, - max_tokens=8192, + # 🔥 不传递 temperature 和 max_tokens,使用用户配置 ) except asyncio.CancelledError: logger.info(f"[{self.name}] LLM call cancelled") @@ -653,8 +652,7 @@ Final Answer:""", try: summary_output, _ = await self.stream_llm_call( self._conversation_history, - temperature=0.1, - max_tokens=4096, + # 🔥 不传递 temperature 和 max_tokens,使用用户配置 ) if summary_output and summary_output.strip(): diff --git a/backend/app/services/agent/agents/base.py b/backend/app/services/agent/agents/base.py index a294568..3f52919 100644 --- a/backend/app/services/agent/agents/base.py +++ b/backend/app/services/agent/agents/base.py @@ -838,25 +838,24 @@ class BaseAgent(ABC): Args: messages: 消息列表 tools: 可用工具描述 - + Returns: LLM 响应 """ self._iteration += 1 - + try: + # 🔥 不传递 temperature 和 max_tokens,让 LLMService 使用用户配置 response = await self.llm_service.chat_completion( messages=messages, - temperature=self.config.temperature, - max_tokens=self.config.max_tokens, tools=tools, ) - + if response.get("usage"): self._total_tokens += response["usage"].get("total_tokens", 0) - + return response - + except Exception as e: logger.error(f"LLM call failed: {e}") raise @@ -925,46 +924,46 @@ class BaseAgent(ABC): return messages # ============ 统一的流式 LLM 调用 ============ - + async def stream_llm_call( self, messages: List[Dict[str, str]], - temperature: float = 0.1, - max_tokens: int = 2048, + temperature: Optional[float] = None, + max_tokens: Optional[int] = None, auto_compress: bool = True, ) -> Tuple[str, int]: """ 统一的流式 LLM 调用方法 - + 所有 Agent 共用此方法,避免重复代码 - + Args: messages: 消息列表 - temperature: 温度 - max_tokens: 最大 token 数 + temperature: 温度(None 时使用用户配置) + max_tokens: 最大 token 数(None 时使用用户配置) auto_compress: 是否自动压缩过长的消息历史 - + Returns: (完整响应内容, token数量) """ # 🔥 自动压缩过长的消息历史 if auto_compress: messages = self.compress_messages_if_needed(messages) - + accumulated = "" total_tokens = 0 - + # 🔥 在开始 LLM 调用前检查取消 if self.is_cancelled: logger.info(f"[{self.name}] Cancelled before LLM call") return "", 0 - + logger.info(f"[{self.name}] 🚀 Starting stream_llm_call, emitting thinking_start...") await self.emit_thinking_start() logger.info(f"[{self.name}] ✅ thinking_start emitted, starting LLM stream...") - + try: - # 获取流式迭代器 + # 获取流式迭代器(传入 None 时使用用户配置) stream = self.llm_service.chat_completion_stream( messages=messages, temperature=temperature, diff --git a/backend/app/services/agent/agents/orchestrator.py b/backend/app/services/agent/agents/orchestrator.py index d527d6a..b12a407 100644 --- a/backend/app/services/agent/agents/orchestrator.py +++ b/backend/app/services/agent/agents/orchestrator.py @@ -241,8 +241,7 @@ class OrchestratorAgent(BaseAgent): try: llm_output, tokens_this_round = await self.stream_llm_call( self._conversation_history, - temperature=0.1, - max_tokens=8192, # 🔥 增加到 8192,避免截断 + # 🔥 不传递 temperature 和 max_tokens,使用用户配置 ) except asyncio.CancelledError: logger.info(f"[{self.name}] LLM call cancelled") diff --git a/backend/app/services/agent/agents/recon.py b/backend/app/services/agent/agents/recon.py index bd981f1..ad47707 100644 --- a/backend/app/services/agent/agents/recon.py +++ b/backend/app/services/agent/agents/recon.py @@ -358,8 +358,7 @@ class ReconAgent(BaseAgent): try: llm_output, tokens_this_round = await self.stream_llm_call( self._conversation_history, - temperature=0.1, - max_tokens=8192, # 🔥 增加到 8192,避免截断 + # 🔥 不传递 temperature 和 max_tokens,使用用户配置 ) except asyncio.CancelledError: logger.info(f"[{self.name}] LLM call cancelled") @@ -525,8 +524,7 @@ Final Answer:""", try: summary_output, _ = await self.stream_llm_call( self._conversation_history, - temperature=0.1, - max_tokens=2048, + # 🔥 不传递 temperature 和 max_tokens,使用用户配置 ) if summary_output and summary_output.strip(): diff --git a/backend/app/services/agent/agents/verification.py b/backend/app/services/agent/agents/verification.py index ee19053..9a0de82 100644 --- a/backend/app/services/agent/agents/verification.py +++ b/backend/app/services/agent/agents/verification.py @@ -587,8 +587,7 @@ class VerificationAgent(BaseAgent): try: llm_output, tokens_this_round = await self.stream_llm_call( self._conversation_history, - temperature=0.1, - max_tokens=8192, # 🔥 增加到 8192,避免截断 + # 🔥 不传递 temperature 和 max_tokens,使用用户配置 ) except asyncio.CancelledError: logger.info(f"[{self.name}] LLM call cancelled") diff --git a/backend/app/services/agent/graph/audit_graph.py b/backend/app/services/agent/graph/audit_graph.py index 60675db..4b0eada 100644 --- a/backend/app/services/agent/graph/audit_graph.py +++ b/backend/app/services/agent/graph/audit_graph.py @@ -125,8 +125,7 @@ class LLMRouter: {"role": "system", "content": "你是安全审计流程的决策者,负责决定下一步行动。"}, {"role": "user", "content": prompt}, ], - temperature=0.1, - max_tokens=200, + # 🔥 不传递 temperature 和 max_tokens,使用用户配置 ) content = response.get("content", "") @@ -180,8 +179,7 @@ class LLMRouter: {"role": "system", "content": "你是安全审计流程的决策者,负责决定下一步行动。"}, {"role": "user", "content": prompt}, ], - temperature=0.1, - max_tokens=200, + # 🔥 不传递 temperature 和 max_tokens,使用用户配置 ) content = response.get("content", "") @@ -227,8 +225,7 @@ class LLMRouter: {"role": "system", "content": "你是安全审计流程的决策者,负责决定下一步行动。"}, {"role": "user", "content": prompt}, ], - temperature=0.1, - max_tokens=200, + # 🔥 不传递 temperature 和 max_tokens,使用用户配置 ) content = response.get("content", "") diff --git a/backend/app/services/llm/service.py b/backend/app/services/llm/service.py index 8ef0c70..088413e 100644 --- a/backend/app/services/llm/service.py +++ b/backend/app/services/llm/service.py @@ -359,12 +359,14 @@ Please analyze the following code: try: adapter = LLMFactory.create_adapter(self.config) + # 使用用户配置的 temperature(如果未设置则使用 config 中的默认值) request = LLMRequest( messages=[ LLMMessage(role="system", content=system_prompt), LLMMessage(role="user", content=user_prompt) ], - temperature=0.1, + temperature=self.config.temperature, + max_tokens=self.config.max_tokens, ) response = await adapter.complete(request) @@ -401,39 +403,97 @@ Please analyze the following code: logger.error(f"Provider: {self.config.provider.value}, Model: {self.config.model}") # 重新抛出异常,让调用者处理 raise - - async def chat_completion_raw( + + async def chat_completion( self, messages: List[Dict[str, str]], - temperature: float = 0.1, - max_tokens: int = 4096, + temperature: Optional[float] = None, + max_tokens: Optional[int] = None, + tools: Optional[List[Dict[str, Any]]] = None, ) -> Dict[str, Any]: """ - 🔥 Agent 使用的原始聊天完成接口(兼容旧接口) - + 🔥 Agent 使用的聊天完成接口(支持工具调用) + Args: messages: 消息列表,格式为 [{"role": "user", "content": "..."}] - temperature: 温度参数 - max_tokens: 最大token数 - + temperature: 温度参数(None 时使用用户配置) + max_tokens: 最大token数(None 时使用用户配置) + tools: 工具描述列表(可选) + Returns: - 包含 content 和 usage 的字典 + 包含 content、usage 和 tool_calls 的字典 """ + # 使用用户配置作为默认值 + actual_temperature = temperature if temperature is not None else self.config.temperature + actual_max_tokens = max_tokens if max_tokens is not None else self.config.max_tokens + # 转换消息格式 llm_messages = [ LLMMessage(role=msg["role"], content=msg["content"]) for msg in messages ] - + request = LLMRequest( messages=llm_messages, - temperature=temperature, - max_tokens=max_tokens, + temperature=actual_temperature, + max_tokens=actual_max_tokens, + tools=tools, ) - + adapter = LLMFactory.create_adapter(self.config) response = await adapter.complete(request) - + + result = { + "content": response.content, + "usage": { + "prompt_tokens": response.usage.prompt_tokens if response.usage else 0, + "completion_tokens": response.usage.completion_tokens if response.usage else 0, + "total_tokens": response.usage.total_tokens if response.usage else 0, + }, + } + + # 添加工具调用信息 + if response.tool_calls: + result["tool_calls"] = response.tool_calls + + return result + + async def chat_completion_raw( + self, + messages: List[Dict[str, str]], + temperature: Optional[float] = None, + max_tokens: Optional[int] = None, + ) -> Dict[str, Any]: + """ + 🔥 Agent 使用的原始聊天完成接口(兼容旧接口) + + Args: + messages: 消息列表,格式为 [{"role": "user", "content": "..."}] + temperature: 温度参数(None 时使用用户配置) + max_tokens: 最大token数(None 时使用用户配置) + + Returns: + 包含 content 和 usage 的字典 + """ + # 使用用户配置作为默认值 + actual_temperature = temperature if temperature is not None else self.config.temperature + actual_max_tokens = max_tokens if max_tokens is not None else self.config.max_tokens + + # 转换消息格式 + llm_messages = [ + LLMMessage(role=msg["role"], content=msg["content"]) + for msg in messages + ] + + request = LLMRequest( + messages=llm_messages, + temperature=actual_temperature, + max_tokens=actual_max_tokens, + ) + + adapter = LLMFactory.create_adapter(self.config) + response = await adapter.complete(request) + return { "content": response.content, "usage": { @@ -446,29 +506,33 @@ Please analyze the following code: async def chat_completion_stream( self, messages: List[Dict[str, str]], - temperature: float = 0.1, - max_tokens: int = 4096, + temperature: Optional[float] = None, + max_tokens: Optional[int] = None, ): """ 流式聊天完成接口,逐 token 返回 - + Args: messages: 消息列表 - temperature: 温度参数 - max_tokens: 最大token数 - + temperature: 温度参数(None 时使用用户配置) + max_tokens: 最大token数(None 时使用用户配置) + Yields: dict: {"type": "token", "content": str} 或 {"type": "done", ...} """ + # 使用用户配置作为默认值 + actual_temperature = temperature if temperature is not None else self.config.temperature + actual_max_tokens = max_tokens if max_tokens is not None else self.config.max_tokens + llm_messages = [ LLMMessage(role=msg["role"], content=msg["content"]) for msg in messages ] - + request = LLMRequest( messages=llm_messages, - temperature=temperature, - max_tokens=max_tokens, + temperature=actual_temperature, + max_tokens=actual_max_tokens, ) if self.config.provider in NATIVE_ONLY_PROVIDERS: @@ -869,15 +933,17 @@ Please analyze the following code: try: adapter = LLMFactory.create_adapter(self.config) - + + # 使用用户配置的 temperature 和 max_tokens request = LLMRequest( messages=[ LLMMessage(role="system", content=full_system_prompt), LLMMessage(role="user", content=user_prompt) ], - temperature=0.1, + temperature=self.config.temperature, + max_tokens=self.config.max_tokens, ) - + response = await adapter.complete(request) content = response.content diff --git a/backend/app/services/scanner.py b/backend/app/services/scanner.py index 9937e28..fa0b799 100644 --- a/backend/app/services/scanner.py +++ b/backend/app/services/scanner.py @@ -15,6 +15,25 @@ from app.services.llm.service import LLMService from app.core.config import settings +def get_analysis_config(user_config: Optional[Dict[str, Any]] = None) -> Dict[str, Any]: + """ + 获取分析配置参数(优先使用用户配置,然后使用系统配置) + + Returns: + 包含以下字段的字典: + - max_analyze_files: 最大分析文件数 + - llm_concurrency: LLM 并发数 + - llm_gap_ms: LLM 请求间隔(毫秒) + """ + other_config = (user_config or {}).get('otherConfig', {}) + + return { + 'max_analyze_files': other_config.get('maxAnalyzeFiles') or settings.MAX_ANALYZE_FILES, + 'llm_concurrency': other_config.get('llmConcurrency') or settings.LLM_CONCURRENCY, + 'llm_gap_ms': other_config.get('llmGapMs') or settings.LLM_GAP_MS, + } + + # 支持的文本文件扩展名 TEXT_EXTENSIONS = [ ".js", ".ts", ".tsx", ".jsx", ".py", ".java", ".go", ".rs", @@ -344,19 +363,24 @@ async def scan_repo_task(task_id: str, db_session_factory, user_config: dict = N print(f"✅ 成功获取分支 {actual_branch} 的文件列表") + # 获取分析配置(优先使用用户配置) + analysis_config = get_analysis_config(user_config) + max_analyze_files = analysis_config['max_analyze_files'] + llm_gap_ms = analysis_config['llm_gap_ms'] + # 限制文件数量 # 如果指定了特定文件,则只分析这些文件 target_files = (user_config or {}).get('scan_config', {}).get('file_paths', []) if target_files: print(f"🎯 指定分析 {len(target_files)} 个文件") files = [f for f in files if f['path'] in target_files] - elif settings.MAX_ANALYZE_FILES > 0: - files = files[:settings.MAX_ANALYZE_FILES] - + elif max_analyze_files > 0: + files = files[:max_analyze_files] + task.total_files = len(files) await db.commit() - print(f"📊 获取到 {len(files)} 个文件,开始分析") + print(f"📊 获取到 {len(files)} 个文件,开始分析 (最大文件数: {max_analyze_files}, 请求间隔: {llm_gap_ms}ms)") # 4. 分析文件 total_issues = 0 @@ -484,7 +508,7 @@ async def scan_repo_task(task_id: str, db_session_factory, user_config: dict = N print(f"📈 任务 {task_id}: 进度 {scanned_files}/{len(files)} ({int(scanned_files/len(files)*100)}%)") # 请求间隔 - await asyncio.sleep(settings.LLM_GAP_MS / 1000) + await asyncio.sleep(llm_gap_ms / 1000) except Exception as file_error: failed_files += 1 @@ -494,7 +518,7 @@ async def scan_repo_task(task_id: str, db_session_factory, user_config: dict = N print(f"❌ 分析文件失败 ({file_info['path']}): {file_error}") print(f" 错误类型: {type(file_error).__name__}") print(f" 详细信息: {traceback.format_exc()}") - await asyncio.sleep(settings.LLM_GAP_MS / 1000) + await asyncio.sleep(llm_gap_ms / 1000) # 5. 完成任务 avg_quality_score = sum(quality_scores) / len(quality_scores) if quality_scores else 100.0 diff --git a/frontend/src/components/system/SystemConfig.tsx b/frontend/src/components/system/SystemConfig.tsx index 0903bef..13808d6 100644 --- a/frontend/src/components/system/SystemConfig.tsx +++ b/frontend/src/components/system/SystemConfig.tsx @@ -52,7 +52,7 @@ export function SystemConfig() { const [hasChanges, setHasChanges] = useState(false); const [testingLLM, setTestingLLM] = useState(false); const [llmTestResult, setLlmTestResult] = useState<{ success: boolean; message: string; debug?: Record } | null>(null); - const [showDebugInfo, setShowDebugInfo] = useState(false); + const [showDebugInfo, setShowDebugInfo] = useState(true); useEffect(() => { loadConfig(); }, []); @@ -396,15 +396,44 @@ export function SystemConfig() { {showDebugInfo && llmTestResult.debug && (
-
调试信息:
+
连接信息:
Provider: {String(llmTestResult.debug.provider)}
Model: {String(llmTestResult.debug.model_used || llmTestResult.debug.model_requested || 'N/A')}
Base URL: {String(llmTestResult.debug.base_url_used || llmTestResult.debug.base_url_requested || '(default)')}
Adapter: {String(llmTestResult.debug.adapter_type || 'N/A')}
API Key: {String(llmTestResult.debug.api_key_prefix)} (长度: {String(llmTestResult.debug.api_key_length)})
耗时: {String(llmTestResult.debug.elapsed_time_ms || 'N/A')} ms
+ + {/* 用户保存的配置参数 */} + {llmTestResult.debug.saved_config && ( +
+
已保存的配置参数:
+
+
温度: {String((llmTestResult.debug.saved_config as Record).temperature ?? 'N/A')}
+
最大Tokens: {String((llmTestResult.debug.saved_config as Record).max_tokens ?? 'N/A')}
+
超时: {String((llmTestResult.debug.saved_config as Record).timeout_ms ?? 'N/A')} ms
+
请求间隔: {String((llmTestResult.debug.saved_config as Record).gap_ms ?? 'N/A')} ms
+
并发数: {String((llmTestResult.debug.saved_config as Record).concurrency ?? 'N/A')}
+
最大文件数: {String((llmTestResult.debug.saved_config as Record).max_analyze_files ?? 'N/A')}
+
输出语言: {String((llmTestResult.debug.saved_config as Record).output_language ?? 'N/A')}
+
+
+ )} + + {/* 测试时实际使用的参数 */} + {llmTestResult.debug.test_params && ( +
+
测试时使用的参数:
+
+
温度: {String((llmTestResult.debug.test_params as Record).temperature ?? 'N/A')}
+
超时: {String((llmTestResult.debug.test_params as Record).timeout ?? 'N/A')}s
+
MaxTokens: {String((llmTestResult.debug.test_params as Record).max_tokens ?? 'N/A')}
+
+
+ )} + {llmTestResult.debug.error_category && ( -
错误类型: {String(llmTestResult.debug.error_category)}
+
错误类型: {String(llmTestResult.debug.error_category)}
)} {llmTestResult.debug.error_type && (
异常类型: {String(llmTestResult.debug.error_type)}