diff --git a/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/logs/lzwcai_demp_tool_server_dify_to_mcp_test.log b/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/logs/lzwcai_demp_tool_server_dify_to_mcp_test.log deleted file mode 100644 index e69de29..0000000 diff --git a/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test.egg-info/PKG-INFO b/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test.egg-info/PKG-INFO index 00fbad7..bb8c4fe 100644 --- a/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test.egg-info/PKG-INFO +++ b/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test.egg-info/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: lzwcai-demp-tool-server-dify-to-mcp-test -Version: 0.1.1 +Version: 0.1.2 Summary: 这是一个Dify to MCP的集成工具;通过Dify的API接口,将Dify的模型部署到MCP平台,并进行推理。 Requires-Python: >=3.10 Requires-Dist: httpx>=0.28.1 diff --git a/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/main.py b/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/main.py index 92a59e3..959616b 100644 --- a/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/main.py +++ b/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/main.py @@ -19,7 +19,7 @@ def setup_mock_arguments(): # 默认配置 default_config = { "base_url": "http://192.168.2.236:3001/v1", - "app_sks": ["app-Oo0QRJismgQADRSt8Bj0RXWB"], + "app_sks": ["app-IfJayK9uu5oTo54Rpr2AS7wl"], "mode_type": "workflow", "transport": "stdio" } diff --git a/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/pyproject.toml b/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/pyproject.toml index bb90fa2..af294ed 100644 --- a/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/pyproject.toml +++ b/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "lzwcai-demp-tool-server-dify-to-mcp-test" -version = "0.1.1" +version = "0.1.2" description = "这是一个Dify to MCP的集成工具;通过Dify的API接口,将Dify的模型部署到MCP平台,并进行推理。" requires-python = ">=3.10" dependencies = [ diff --git a/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/src/__pycache__/create_mcp.cpython-312.pyc b/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/src/__pycache__/create_mcp.cpython-312.pyc index 099ca20..3eb52a1 100644 Binary files a/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/src/__pycache__/create_mcp.cpython-312.pyc and b/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/src/__pycache__/create_mcp.cpython-312.pyc differ diff --git a/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/src/__pycache__/create_mcp_utils.cpython-312.pyc b/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/src/__pycache__/create_mcp_utils.cpython-312.pyc index f2263ac..aec247e 100644 Binary files a/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/src/__pycache__/create_mcp_utils.cpython-312.pyc and b/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/src/__pycache__/create_mcp_utils.cpython-312.pyc differ diff --git a/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/src/create_mcp.py b/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/src/create_mcp.py index 5ca8b95..57f818a 100644 --- a/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/src/create_mcp.py +++ b/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/src/create_mcp.py @@ -17,6 +17,7 @@ from src.difyTaskCall.task_instance import TaskInstance from src.utils.dify_workflow_schema import process_user_input_form, extract_file_fields from src.create_mcp_utils import process_file_arguments from src.utils.logger_config import get_logger +from src.workflow.workflow_server import DifyAPIError # 使用统一的日志配置 logger = get_logger(__name__) @@ -147,29 +148,48 @@ async def handle_call_tool( processed_arguments = process_file_arguments(arguments, current_tool_file_fields, dify_api, name) logger.info(f"工具 {name} 处理后的 arguments: {processed_arguments}") - responses = dify_api.chat_message( - tool_sk, - inputs=processed_arguments, - ) + try: + responses = dify_api.chat_message( + tool_sk, + inputs=processed_arguments, + ) - # 初始化 outputs 变量,避免未定义错误 - outputs = {} - for res in responses: - if res["event"] == "workflow_finished": - outputs = res["data"]["outputs"] - break # 找到 workflow_finished 事件后退出循环 - - # 构建 MCP 输出 - mcp_out = [] - if outputs: - for _, v in outputs.items(): - mcp_out.append(types.TextContent(type="text", text=v)) - else: - # 如果没有获取到 outputs,返回错误信息 - logger.warning(f"工具 {name} 未获取到 workflow_finished 事件或 outputs 为空") - mcp_out.append(types.TextContent(type="text", text="工具执行完成,但未返回输出结果")) - - return mcp_out + # 初始化 outputs 变量,避免未定义错误 + outputs = {} + for res in responses: + if res["event"] == "workflow_finished": + outputs = res["data"]["outputs"] + break # 找到 workflow_finished 事件后退出循环 + + # 构建 MCP 输出 + mcp_out = [] + if outputs: + for _, v in outputs.items(): + mcp_out.append(types.TextContent(type="text", text=str(v))) + else: + # 如果没有获取到 outputs,返回错误信息 + logger.warning(f"工具 {name} 未获取到 workflow_finished 事件或 outputs 为空") + mcp_out.append(types.TextContent(type="text", text="工具执行完成,但未返回输出结果")) + + return mcp_out + + except DifyAPIError as e: + # 捕获 Dify API 错误,直接返回给用户 + logger.error(f"工具 {name} 调用 Dify API 失败: {e}") + error_message = f"API调用失败: {e.message}" + return [types.TextContent(type="text", text=error_message)] + + except requests.exceptions.RequestException as e: + # 捕获网络请求错误 + logger.error(f"工具 {name} 网络请求失败: {e}") + error_message = f"网络请求失败: {str(e)}" + return [types.TextContent(type="text", text=error_message)] + + except Exception as e: + # 捕获其他未知错误 + logger.error(f"工具 {name} 执行时发生未知错误: {e}", exc_info=True) + error_message = f"工具执行失败: {str(e)}" + return [types.TextContent(type="text", text=error_message)] else: raise ValueError(f"Unknown tool: {name}") diff --git a/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/src/create_mcp_utils.py b/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/src/create_mcp_utils.py index 7b3d34f..b3c3e5c 100644 --- a/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/src/create_mcp_utils.py +++ b/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/src/create_mcp_utils.py @@ -44,7 +44,7 @@ def process_file_arguments(arguments, current_tool_file_fields, dify_api, tool_n current_tool_file_fields (list): 当前工具的文件字段信息列表 数据结构: [{'variable': 'txt', 'label': '输入', 'required': True, 'max_length': 48, 'allowed_file_types': ['document'], 'allowed_file_upload_methods': ['local_file', 'remote_url'], - 'allowed_file_extensions': []}] + 'allowed_file_extensions': [], 'is_list': False}] dify_api: Dify API实例,包含file_parameter_pretreatment方法 tool_name (str): 工具名称,用于日志记录 @@ -58,8 +58,9 @@ def process_file_arguments(arguments, current_tool_file_fields, dify_api, tool_n logger.info(f"工具 {tool_name}: 无需处理文件字段 (arguments={bool(arguments)}, file_fields={len(current_tool_file_fields) if current_tool_file_fields else 0})") return arguments - # 创建文件字段变量名的集合,用于快速查找 - file_field_variables = {field['variable'] for field in current_tool_file_fields} + # 创建文件字段变量名到字段信息的映射,用于快速查找 + file_field_map = {field['variable']: field for field in current_tool_file_fields} + file_field_variables = set(file_field_map.keys()) logger.info(f"工具 {tool_name} 的文件字段变量名: {file_field_variables}") # 创建arguments的副本,避免修改原始数据 @@ -101,12 +102,20 @@ def process_file_arguments(arguments, current_tool_file_fields, dify_api, tool_n logger.error(f"工具 {tool_name}: 文件预处理失败,未返回有效结果") continue - # 取第一个处理后的文件对象 - processed_files_item = processed_files[0] - logger.info(f"工具 {tool_name}: 文件预处理完成,upload_file_id: {processed_files_item.get('upload_file_id', 'N/A')}") + # 过滤掉上传失败的文件 + valid_files = [f for f in processed_files if f.get('upload_file_id') and not f.get('upload_error')] + if not valid_files: + logger.error(f"工具 {tool_name}: 所有文件上传失败") + continue + logger.info(f"工具 {tool_name}: 文件预处理完成,成功上传 {len(valid_files)} 个文件") + + # 获取当前字段的配置信息,判断是单文件还是多文件 + field_config = file_field_map.get(arg_name, {}) + is_list = field_config.get('is_list', False) + # 更新processed_arguments中的值 - _update_processed_argument(processed_arguments, arg_name, arg_value, processed_files_item, tool_name) + _update_processed_argument(processed_arguments, arg_name, arg_value, valid_files, tool_name, is_list) except Exception as e: logger.error(f"工具 {tool_name}: 处理文件字段 {arg_name} 时发生错误: {str(e)}") @@ -180,27 +189,34 @@ def _is_file_object(obj): return any(key in obj for key in file_indicators) -def _update_processed_argument(processed_arguments, arg_name, original_value, processed_files, tool_name): +def _update_processed_argument(processed_arguments, arg_name, original_value, processed_files, tool_name, is_list=False): """ 更新处理后的参数值 - 重要:Dify API 的文件字段始终需要一个文件对象列表,即使只有一个文件 + 根据字段类型决定输出格式: + - file-list (is_list=True): 输出列表格式 [] + - file (is_list=False): 输出对象格式 {} Args: processed_arguments (dict): 处理后的参数字典 arg_name (str): 参数名称 original_value: 原始参数值(可以是字符串URL、文件对象或文件列表) - processed_files: 处理后的文件对象(单个对象,不是列表) + processed_files (list): 处理后的文件对象列表 tool_name (str): 工具名称 + is_list (bool): 是否为多文件类型 (file-list=True, file=False) """ - # 注意:processed_files 是单个文件对象,需要转换为列表 - # 因为 Dify API 要求文件字段必须是列表格式 - if processed_files: - # 始终将文件对象包装成列表 - processed_arguments[arg_name] = [processed_files] - logger.info(f"工具 {tool_name}: 已更新文件字段 {arg_name} 为列表格式: {processed_arguments[arg_name]}") - else: + if not processed_files: logger.warning(f"工具 {tool_name}: 文件处理后为空,保持原值") + return + + if is_list: + # file-list 类型:使用完整列表 + processed_arguments[arg_name] = processed_files + logger.info(f"工具 {tool_name}: 字段 {arg_name} 是 file-list 类型,输出 {len(processed_files)} 个文件") + else: + # file 类型:只取第一个文件对象 + processed_arguments[arg_name] = processed_files[0] + logger.info(f"工具 {tool_name}: 字段 {arg_name} 是 file 类型,输出单个对象") logger.info(f"工具 {tool_name}: 字段 {arg_name} 最终值: {processed_arguments[arg_name]}") diff --git a/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/src/utils/__pycache__/dify_workflow_schema.cpython-312.pyc b/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/src/utils/__pycache__/dify_workflow_schema.cpython-312.pyc index ebd42b3..ac7fbf9 100644 Binary files a/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/src/utils/__pycache__/dify_workflow_schema.cpython-312.pyc and b/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/src/utils/__pycache__/dify_workflow_schema.cpython-312.pyc differ diff --git a/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/src/utils/dify_workflow_schema.py b/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/src/utils/dify_workflow_schema.py index 83c7beb..c2b1f1c 100644 --- a/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/src/utils/dify_workflow_schema.py +++ b/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/src/utils/dify_workflow_schema.py @@ -247,7 +247,7 @@ def process_user_input_form(user_input_form): if default_value is not None: property_schema["default"] = str(default_value) - elif control_type == "file": + elif control_type in ["file", "file-list"]: # 文件上传控件处理 - 简化版本,仅支持remote_url # 获取允许的文件类型 allowed_file_types = control_config.get("allowed_file_types", []) @@ -259,27 +259,21 @@ def process_user_input_form(user_input_form): "type": "object", "description": label or f"文件上传字段: {variable}", "properties": { - # "type": { - # "type": "string", - # "description": file_type_desc - # }, - # "transfer_method": { - # "type": "string", - # "description": "文件传输方式", - # "enum": ["remote_url"], - # "default": "remote_url" - # }, "url": { "type": "string", "description": url_description } }, - "required": [ "url"] + "required": ["url"] } - # 处理文件数量限制 + # 判断是单文件还是多文件 + # file-list 类型或 max_length > 1 都视为多文件 + actual_type = control_config.get("type", control_type) max_length = control_config.get("max_length", 1) - if max_length > 1: + is_multi_file = actual_type == "file-list" or max_length > 1 + + if is_multi_file: # 多文件上传场景 property_schema = { "type": "array", @@ -330,6 +324,7 @@ def extract_file_fields(user_input_form): - allowed_file_types (list): 允许的文件类型 - allowed_file_upload_methods (list): 允许的上传方式 - allowed_file_extensions (list): 允许的文件扩展名 + - is_list (bool): 是否为多文件类型 (file-list=True, file=False) """ file_fields = [] @@ -353,6 +348,11 @@ def extract_file_fields(user_input_form): # 只处理type为file或file-list的字段 if control_type in ["file", "file-list"] or control_config.get("type") in ["file", "file-list"]: + # 判断是否为多文件类型 + # 优先使用 control_config 中的 type,其次使用 control_type + actual_type = control_config.get("type", control_type) + is_list = actual_type == "file-list" + # 提取文件字段的详细信息 file_field_info = { "variable": control_config.get("variable", ""), @@ -361,7 +361,8 @@ def extract_file_fields(user_input_form): "max_length": control_config.get("max_length", 1), "allowed_file_types": control_config.get("allowed_file_types", []), "allowed_file_upload_methods": control_config.get("allowed_file_upload_methods", []), - "allowed_file_extensions": control_config.get("allowed_file_extensions", []) + "allowed_file_extensions": control_config.get("allowed_file_extensions", []), + "is_list": is_list # 新增:标识是否为多文件类型 } # 只添加有效的字段(必须有variable) diff --git a/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/src/workflow/__pycache__/workflow_server.cpython-312.pyc b/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/src/workflow/__pycache__/workflow_server.cpython-312.pyc index 472ea9a..7c4f86e 100644 Binary files a/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/src/workflow/__pycache__/workflow_server.cpython-312.pyc and b/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/src/workflow/__pycache__/workflow_server.cpython-312.pyc differ diff --git a/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/src/workflow/workflow_server.py b/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/src/workflow/workflow_server.py index 1971f29..97b51bb 100644 --- a/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/src/workflow/workflow_server.py +++ b/lzwcai_demp_tool_server_dify_to_mcp_test/lzwcai_demp_tool_server_dify_to_mcp_test/src/workflow/workflow_server.py @@ -13,6 +13,27 @@ except ImportError: logger = get_logger(__name__) + +class DifyAPIError(Exception): + """Dify API 错误异常类""" + + def __init__(self, status_code: int, error_code: str, message: str, request_data: dict = None): + self.status_code = status_code + self.error_code = error_code + self.message = message + self.request_data = request_data + super().__init__(self.message) + + def __str__(self): + return f"[{self.status_code}] {self.error_code}: {self.message}" + + def to_dict(self): + return { + "status_code": self.status_code, + "error_code": self.error_code, + "message": self.message + } + def pinyin_to_camel(pinyin): """ 将中文名称转换为工具名称 @@ -131,8 +152,22 @@ class WorkflowDifyAPI(ABC): logger.error(f"API request failed with status {response.status_code}") logger.error(f"Response content: {response.text}") logger.error(f"Request data: {data}") - - response.raise_for_status() + + # 解析错误响应并抛出带有详细信息的异常 + try: + error_data = response.json() + error_message = error_data.get("message", response.text) + error_code = error_data.get("code", "unknown_error") + except json.JSONDecodeError: + error_message = response.text + error_code = "unknown_error" + + raise DifyAPIError( + status_code=response.status_code, + error_code=error_code, + message=error_message, + request_data=data + ) if response_mode == "streaming": def stream_generator(): for line in response.iter_lines(): @@ -148,12 +183,14 @@ class WorkflowDifyAPI(ABC): return response.json() def upload_file(self, api_key, file_path, user="pp666"): - url = f"{self.dify_base_url}/files/upload" headers = {"Authorization": f"Bearer {api_key}"} - files = {"file": open(file_path, "rb")} data = {"user": user} - response = requests.post(url, headers=headers, files=files, data=data) + + with open(file_path, "rb") as f: + files = {"file": f} + response = requests.post(url, headers=headers, files=files, data=data) + response.raise_for_status() return response.json() def upload_file_remote_url(self, file_url): diff --git a/lzwcai_mcpskills_mfg_data_agent/lzwcai_mcpskills_mfg_data_agent/logs/lzwcai_mcp_sqlexecutor.log b/lzwcai_mcpskills_mfg_data_agent/lzwcai_mcpskills_mfg_data_agent/logs/lzwcai_mcp_sqlexecutor.log index 677d99f..83e454e 100644 --- a/lzwcai_mcpskills_mfg_data_agent/lzwcai_mcpskills_mfg_data_agent/logs/lzwcai_mcp_sqlexecutor.log +++ b/lzwcai_mcpskills_mfg_data_agent/lzwcai_mcpskills_mfg_data_agent/logs/lzwcai_mcp_sqlexecutor.log @@ -329,3 +329,158 @@ 2026-01-08 00:56:21 - httpx - INFO - [_client.py:1025] - HTTP Request: POST http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema "HTTP/1.1 200 " 2026-01-08 00:56:21 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:154] - 测试SQL API调用成功 2026-01-08 00:56:21 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功 +2026-01-08 09:52:27 - mcp_services - INFO - [main.py:344] - MCP 服务器已关闭 +2026-01-08 10:00:42 - root - INFO - [logger_config.py:151] - 日志系统初始化完成 - 日志目录: E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcpskills_mfg_data_agent\lzwcai_mcpskills_mfg_data_agent\logs +2026-01-08 10:00:42 - root - INFO - [logger_config.py:152] - 日志配置 - 级别: INFO, 文件大小限制: 10MB, 备份数量: 5 +2026-01-08 10:00:42 - mcp_services - INFO - [main.py:362] - 开始运行 MCP SQL Executor 服务器 +2026-01-08 10:00:42 - mcp_services - INFO - [main.py:313] - ============================================================ +2026-01-08 10:00:42 - mcp_services - INFO - [main.py:314] - 正在启动 MCP 服务器: lzwcai-mcpskills-analyzeOrder +2026-01-08 10:00:42 - mcp_services - INFO - [main.py:315] - 版本: 0.1.0 +2026-01-08 10:00:42 - mcp_services - INFO - [main.py:316] - ============================================================ +2026-01-08 10:00:42 - mcp_services - INFO - [main.py:320] - 环境配置 - Database ID: 19 +2026-01-08 10:00:42 - mcp_services - INFO - [main.py:321] - 环境配置 - Datasource ID: 19 +2026-01-08 10:00:42 - mcp_services - INFO - [main.py:322] - 环境配置 - Skill ID: +2026-01-08 10:00:42 - mcp_services - INFO - [main.py:323] - 环境配置 - Backend Base URL: http://192.168.11.24:8088 +2026-01-08 10:00:42 - mcp_services - INFO - [main.py:324] - ============================================================ +2026-01-08 10:00:42 - mcp_services - INFO - [main.py:329] - MCP 服务器已启动,等待客户端连接... +2026-01-08 10:00:45 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type ListToolsRequest +2026-01-08 10:00:45 - mcp_services - INFO - [main.py:156] - 收到列出工具请求 +2026-01-08 10:00:45 - mcp_services - INFO - [main.py:119] - 初始化查询配置(数据源: local)... +2026-01-08 10:00:45 - mcp_services - INFO - [main.py:55] - 成功加载 6 个业务查询配置 +2026-01-08 10:00:45 - mcp_services - INFO - [main.py:123] - 本地配置: 6 条 +2026-01-08 10:00:45 - mcp_services - INFO - [main.py:165] - 成功生成 6 个 MCP 工具 +2026-01-08 10:02:28 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type CallToolRequest +2026-01-08 10:02:28 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: OrderDelayWarningAnalysis +2026-01-08 10:02:28 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API... +2026-01-08 10:02:28 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:138] - 正在调用测试SQL API: http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema +2026-01-08 10:02:28 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:139] - 请求参数: { + "datasourceId": "19", + "businessName": "OrderDelayWarningAnalysis", + "businessDescription": "订单延迟预警分析:依据历史订单的生产周期、物流延误、设备故障等特征,输出延迟概率与红/黄/绿预警等级", + "sqlTemplate": "WITH production_cycle_stats AS (SELECT COALESCE(AVG(GREATEST(0, EXTRACT(DAY FROM last_updated_utc - event_time_utc))), 0) AS avg_production_days FROM fact_work_order WHERE status = 'CLOSED' AND last_updated_utc >= event_time_utc), logistics_delay_stats AS (SELECT customer_id, AVG(GREATEST(0, EXTRACT(DAY FROM event_time_utc - doc_date_utc))) AS avg_logistics_delay_days, SUM(CASE WHEN EXTRACT(DAY FROM event_time_utc - doc_date_utc) > 3 THEN 1 ELSE 0 END) AS delay_count FROM fact_sales_shipment WHERE doc_date_utc IS NOT NULL AND event_time_utc IS NOT NULL GROUP BY customer_id), quality_issue_stats AS (SELECT COALESCE(ROUND(SUM(fail_qty) * 100.0 / NULLIF(SUM(pass_qty + fail_qty), 0), 2), 0) AS defect_rate_pct FROM fact_quality_inspection WHERE pass_qty IS NOT NULL AND fail_qty IS NOT NULL), scrap_stats AS (SELECT COALESCE((SELECT COUNT(*) FROM fact_scrap) * 100.0 / NULLIF((SELECT COUNT(*) FROM fact_work_order WHERE status = 'CLOSED'), 0), 0) AS scrap_rate_pct), active_work_order_risk AS (SELECT COUNT(*) AS active_wo_count, COALESCE(SUM(CASE WHEN planned_qty > 0 AND (completed_qty / planned_qty) < 0.3 AND EXTRACT(DAY FROM NOW() - event_time_utc) > 7 THEN 1 ELSE 0 END), 0) AS lagging_wo_count FROM fact_work_order WHERE status IN ('OPEN', 'STARTED')), global_metrics AS (SELECT pcs.avg_production_days, qis.defect_rate_pct, ss.scrap_rate_pct, awr.active_wo_count, awr.lagging_wo_count FROM production_cycle_stats pcs, quality_issue_stats qis, scrap_stats ss, active_work_order_risk awr) SELECT so.sales_order_number AS order_number, c.customer_name, so.order_date_utc::DATE AS order_date, so.deal_amount AS order_amount, so.payment_status, ROUND(gm.avg_production_days::NUMERIC, 1) AS avg_production_days, ROUND(COALESCE(lds.avg_logistics_delay_days, 0)::NUMERIC, 1) AS avg_logistics_delay_days, COALESCE(lds.delay_count, 0)::INT AS historical_delay_count, ROUND(gm.defect_rate_pct::NUMERIC, 2) AS defect_rate_pct, ROUND(gm.scrap_rate_pct::NUMERIC, 2) AS scrap_rate_pct, gm.active_wo_count::INT AS active_work_order_count, gm.lagging_wo_count::INT AS lagging_work_order_count, ROUND(LEAST(100, GREATEST(0, LEAST(25, GREATEST(0, gm.avg_production_days - 10) * 2.5) + LEAST(30, COALESCE(lds.avg_logistics_delay_days, 0) * 6) + LEAST(25, gm.defect_rate_pct * 2.5) + LEAST(20, gm.lagging_wo_count * 10)))::NUMERIC, 1) AS delay_probability_pct, CASE WHEN (LEAST(25, GREATEST(0, gm.avg_production_days - 10) * 2.5) + LEAST(30, COALESCE(lds.avg_logistics_delay_days, 0) * 6) + LEAST(25, gm.defect_rate_pct * 2.5) + LEAST(20, gm.lagging_wo_count * 10)) >= 60 THEN 'RED' WHEN (LEAST(25, GREATEST(0, gm.avg_production_days - 10) * 2.5) + LEAST(30, COALESCE(lds.avg_logistics_delay_days, 0) * 6) + LEAST(25, gm.defect_rate_pct * 2.5) + LEAST(20, gm.lagging_wo_count * 10)) >= 30 THEN 'YELLOW' ELSE 'GREEN' END AS warning_level, CASE WHEN gm.lagging_wo_count >= 2 THEN 'PRODUCTION_SEVERELY_DELAYED' WHEN COALESCE(lds.avg_logistics_delay_days, 0) > 5 THEN 'HIGH_LOGISTICS_DELAY_RISK' WHEN gm.avg_production_days > 15 THEN 'LONG_PRODUCTION_CYCLE' WHEN gm.defect_rate_pct > 10 THEN 'QUALITY_ISSUES' ELSE 'NORMAL' END AS primary_risk_factor FROM fact_sales_order so LEFT JOIN dim_customer c ON so.customer_id = c.customer_id AND c.is_current = 't' CROSS JOIN global_metrics gm LEFT JOIN logistics_delay_stats lds ON so.customer_id = lds.customer_id ORDER BY delay_probability_pct DESC, so.order_date_utc DESC", + "parameters": {}, + "testParams": {} +} +2026-01-08 10:02:28 - httpx - INFO - [_client.py:1025] - HTTP Request: POST http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema "HTTP/1.1 200 " +2026-01-08 10:02:28 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:154] - 测试SQL API调用成功 +2026-01-08 10:02:28 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功 +2026-01-08 10:02:30 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type CallToolRequest +2026-01-08 10:02:30 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: WorkOrderProgressAndAnomalyNodes +2026-01-08 10:02:30 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API... +2026-01-08 10:02:30 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:138] - 正在调用测试SQL API: http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema +2026-01-08 10:02:30 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:139] - 请求参数: { + "datasourceId": "19", + "businessName": "WorkOrderProgressAndAnomalyNodes", + "businessDescription": "工单执行进度与异常节点:实时拉取工单数据,动态映射订单各环节状态(OPEN→PENDING, STARTED→IN_PROGRESS, CLOSED→COMPLETED),呈现执行进度与异常节点", + "sqlTemplate": "WITH work_order_base AS (SELECT wo.work_order_id, wo.work_order_number, wo.product_id, wo.status, wo.planned_qty, wo.completed_qty, wo.event_time_utc::timestamp AS start_time, wo.last_updated_utc::timestamp AS last_update, wo.source_system FROM fact_work_order wo), product_info AS (SELECT product_id, product_name, product_category FROM dim_product WHERE is_current = 't'), labor_summary AS (SELECT work_order_number, COUNT(DISTINCT worker_name) AS worker_count, SUM(report_qty) AS total_report_qty, SUM(duration_minutes) AS total_minutes, MAX(event_time_utc::timestamp) AS last_report_time FROM fact_labor_report GROUP BY work_order_number), quality_summary AS (SELECT work_order_number, SUM(pass_qty) AS pass_qty, SUM(fail_qty) AS fail_qty FROM fact_quality_inspection GROUP BY work_order_number), work_order_progress AS (SELECT wb.work_order_id, wb.work_order_number, p.product_name, p.product_category, wb.status AS raw_status, CASE wb.status WHEN 'OPEN' THEN 'PENDING' WHEN 'STARTED' THEN 'IN_PROGRESS' WHEN 'CLOSED' THEN 'COMPLETED' ELSE 'UNKNOWN' END AS status_name, wb.planned_qty, wb.completed_qty, CASE WHEN wb.planned_qty > 0 THEN ROUND(wb.completed_qty * 100.0 / wb.planned_qty, 1) ELSE 0 END AS completion_rate, GREATEST(wb.planned_qty - wb.completed_qty, 0) AS remaining_qty, wb.start_time, wb.last_update, ROUND(EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - wb.start_time)) / 3600, 1) AS elapsed_hours, COALESCE(ls.worker_count, 0) AS worker_count, COALESCE(ls.total_report_qty, 0) AS total_report_qty, COALESCE(ls.total_minutes, 0) AS total_work_minutes, ls.last_report_time, COALESCE(qs.pass_qty, 0) AS qc_pass_qty, COALESCE(qs.fail_qty, 0) AS qc_fail_qty FROM work_order_base wb LEFT JOIN product_info p ON wb.product_id = p.product_id LEFT JOIN labor_summary ls ON wb.work_order_number = ls.work_order_number LEFT JOIN quality_summary qs ON wb.work_order_number = qs.work_order_number), anomaly_detection AS (SELECT *, CASE WHEN raw_status = 'STARTED' AND elapsed_hours > 48 AND completion_rate < 20 THEN 'SEVERELY_DELAYED' WHEN raw_status = 'STARTED' AND elapsed_hours > 24 AND completion_rate < 30 THEN 'DELAYED' ELSE NULL END AS progress_anomaly, CASE WHEN qc_pass_qty + qc_fail_qty > 0 AND qc_fail_qty * 100.0 / (qc_pass_qty + qc_fail_qty) > 10 THEN 'QUALITY_ISSUE' ELSE NULL END AS quality_anomaly, CASE WHEN raw_status = 'STARTED' AND last_report_time IS NOT NULL AND EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - last_report_time)) / 3600 > 24 THEN 'LABOR_STALLED' WHEN raw_status = 'STARTED' AND last_report_time IS NULL AND elapsed_hours > 24 THEN 'NO_LABOR_RECORD' ELSE NULL END AS labor_anomaly, CASE WHEN total_work_minutes > 0 AND total_report_qty / (total_work_minutes / 60.0) < 5 THEN 'LOW_EFFICIENCY' ELSE NULL END AS efficiency_anomaly FROM work_order_progress) SELECT work_order_number, product_name, product_category, status_name, planned_qty, completed_qty, remaining_qty, completion_rate, worker_count, ROUND(total_work_minutes / 60.0, 1) AS total_work_hours, elapsed_hours, COALESCE(progress_anomaly, '') || CASE WHEN progress_anomaly IS NOT NULL AND quality_anomaly IS NOT NULL THEN ',' ELSE '' END || COALESCE(quality_anomaly, '') || CASE WHEN (progress_anomaly IS NOT NULL OR quality_anomaly IS NOT NULL) AND labor_anomaly IS NOT NULL THEN ',' ELSE '' END || COALESCE(labor_anomaly, '') || CASE WHEN (progress_anomaly IS NOT NULL OR quality_anomaly IS NOT NULL OR labor_anomaly IS NOT NULL) AND efficiency_anomaly IS NOT NULL THEN ',' ELSE '' END || COALESCE(efficiency_anomaly, '') AS anomaly_flags, CASE WHEN progress_anomaly = 'SEVERELY_DELAYED' OR quality_anomaly IS NOT NULL THEN 'HIGH' WHEN progress_anomaly = 'DELAYED' OR labor_anomaly IS NOT NULL THEN 'MEDIUM' WHEN efficiency_anomaly IS NOT NULL THEN 'LOW' ELSE 'NONE' END AS risk_level FROM anomaly_detection ORDER BY CASE raw_status WHEN 'STARTED' THEN 1 WHEN 'OPEN' THEN 2 ELSE 3 END, CASE WHEN progress_anomaly IS NOT NULL THEN 0 ELSE 1 END, completion_rate ASC", + "parameters": {}, + "testParams": {} +} +2026-01-08 10:02:30 - httpx - INFO - [_client.py:1025] - HTTP Request: POST http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema "HTTP/1.1 200 " +2026-01-08 10:02:30 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:154] - 测试SQL API调用成功 +2026-01-08 10:02:30 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功 +2026-01-08 10:02:35 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type CallToolRequest +2026-01-08 10:02:35 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: MetricTrendAndTurningPointWarning +2026-01-08 10:02:35 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API... +2026-01-08 10:02:35 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:138] - 正在调用测试SQL API: http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema +2026-01-08 10:02:35 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:139] - 请求参数: { + "datasourceId": "19", + "businessName": "MetricTrendAndTurningPointWarning", + "businessDescription": "指标趋势分析与拐点预警:基于移动平均与线性回归分析人效、产量、废品率趋势,输出上升/下降/平稳判断与拐点预警", + "sqlTemplate": "WITH daily_metrics AS (SELECT DATE(lr.event_time_utc::timestamp) AS metric_date, COUNT(DISTINCT lr.worker_name) AS worker_count, SUM(lr.duration_minutes) / 60.0 AS total_hours, SUM(lr.report_qty) AS total_output, CASE WHEN SUM(lr.duration_minutes) > 0 THEN SUM(lr.report_qty) / (SUM(lr.duration_minutes) / 60.0) ELSE 0 END AS hourly_efficiency FROM fact_labor_report lr GROUP BY DATE(lr.event_time_utc::timestamp)), daily_quality AS (SELECT DATE(qi.event_time_utc::timestamp) AS metric_date, SUM(qi.pass_qty) AS pass_qty, SUM(qi.fail_qty) AS fail_qty, CASE WHEN SUM(qi.pass_qty) + SUM(qi.fail_qty) > 0 THEN SUM(qi.fail_qty) * 100.0 / (SUM(qi.pass_qty) + SUM(qi.fail_qty)) ELSE 0 END AS defect_rate FROM fact_quality_inspection qi GROUP BY DATE(qi.event_time_utc::timestamp)), daily_production AS (SELECT DATE(wo.event_time_utc::timestamp) AS metric_date, SUM(wo.planned_qty) AS planned_qty, SUM(wo.completed_qty) AS completed_qty, CASE WHEN SUM(wo.planned_qty) > 0 THEN SUM(wo.completed_qty) * 100.0 / SUM(wo.planned_qty) ELSE 0 END AS completion_rate FROM fact_work_order wo GROUP BY DATE(wo.event_time_utc::timestamp)), combined_daily AS (SELECT COALESCE(dm.metric_date, dq.metric_date, dp.metric_date) AS metric_date, COALESCE(dm.worker_count, 0) AS worker_count, COALESCE(dm.total_hours, 0) AS total_hours, COALESCE(dm.total_output, 0) AS total_output, COALESCE(dm.hourly_efficiency, 0) AS hourly_efficiency, COALESCE(dq.defect_rate, 0) AS defect_rate, COALESCE(dp.completion_rate, 0) AS completion_rate FROM daily_metrics dm FULL OUTER JOIN daily_quality dq ON dm.metric_date = dq.metric_date FULL OUTER JOIN daily_production dp ON dm.metric_date = dp.metric_date WHERE COALESCE(dm.metric_date, dq.metric_date, dp.metric_date) IS NOT NULL), numbered_data AS (SELECT *, ROW_NUMBER() OVER (ORDER BY metric_date) AS day_seq FROM combined_daily), moving_avg_step1 AS (SELECT metric_date, day_seq, worker_count, total_output, hourly_efficiency, defect_rate, completion_rate, AVG(hourly_efficiency) OVER (ORDER BY metric_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS ma7_efficiency, AVG(total_output) OVER (ORDER BY metric_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS ma7_output, AVG(defect_rate) OVER (ORDER BY metric_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS ma7_defect_rate, AVG(completion_rate) OVER (ORDER BY metric_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS ma7_completion_rate, AVG(day_seq) OVER (ORDER BY metric_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS avg_x FROM numbered_data), moving_avg AS (SELECT *, LAG(ma7_efficiency, 1) OVER (ORDER BY metric_date) AS prev_ma7_efficiency, LAG(ma7_output, 1) OVER (ORDER BY metric_date) AS prev_ma7_output, LAG(ma7_defect_rate, 1) OVER (ORDER BY metric_date) AS prev_ma7_defect_rate FROM moving_avg_step1), slope_calc AS (SELECT *, (ma7_efficiency - LAG(ma7_efficiency, 3) OVER (ORDER BY metric_date)) / 3.0 AS slope_efficiency, (ma7_output - LAG(ma7_output, 3) OVER (ORDER BY metric_date)) / 3.0 AS slope_output, (ma7_defect_rate - LAG(ma7_defect_rate, 3) OVER (ORDER BY metric_date)) / 3.0 AS slope_defect FROM moving_avg), trend_analysis AS (SELECT metric_date, hourly_efficiency, total_output, defect_rate, completion_rate, ROUND(ma7_efficiency, 2) AS ma7_efficiency, ROUND(ma7_output, 2) AS ma7_output, ROUND(ma7_defect_rate, 2) AS ma7_defect_rate, ROUND(COALESCE(slope_efficiency, 0), 4) AS slope_efficiency, ROUND(COALESCE(slope_output, 0), 4) AS slope_output, ROUND(COALESCE(slope_defect, 0), 4) AS slope_defect, prev_ma7_efficiency, prev_ma7_output, prev_ma7_defect_rate, CASE WHEN COALESCE(slope_efficiency, 0) > 0.3 THEN 'RISING' WHEN COALESCE(slope_efficiency, 0) < -0.3 THEN 'FALLING' ELSE 'STABLE' END AS efficiency_trend, CASE WHEN COALESCE(slope_output, 0) > 3 THEN 'RISING' WHEN COALESCE(slope_output, 0) < -3 THEN 'FALLING' ELSE 'STABLE' END AS output_trend, CASE WHEN COALESCE(slope_defect, 0) > 0.3 THEN 'RISING' WHEN COALESCE(slope_defect, 0) < -0.3 THEN 'FALLING' ELSE 'STABLE' END AS defect_trend, CASE WHEN ma7_efficiency > 0 AND ABS(hourly_efficiency - ma7_efficiency) > ma7_efficiency * 0.3 THEN 'ANOMALY' ELSE 'NORMAL' END AS efficiency_status, CASE WHEN ma7_output > 0 AND ABS(total_output - ma7_output) > ma7_output * 0.3 THEN 'ANOMALY' ELSE 'NORMAL' END AS output_status, CASE WHEN defect_rate > ma7_defect_rate * 1.5 AND defect_rate > 5 THEN 'ANOMALY' ELSE 'NORMAL' END AS defect_status FROM slope_calc) SELECT metric_date, ROUND(hourly_efficiency, 2) AS hourly_output, ma7_efficiency AS efficiency_ma7, slope_efficiency, efficiency_trend, efficiency_status, CASE WHEN prev_ma7_efficiency IS NOT NULL AND ma7_efficiency > prev_ma7_efficiency AND slope_efficiency < 0 THEN 'TURNING_POINT' WHEN prev_ma7_efficiency IS NOT NULL AND ma7_efficiency < prev_ma7_efficiency AND slope_efficiency > 0 THEN 'TURNING_POINT' ELSE 'NONE' END AS efficiency_turning_point, ROUND(total_output, 0) AS daily_output, ma7_output AS output_ma7, slope_output, output_trend, output_status, CASE WHEN prev_ma7_output IS NOT NULL AND ma7_output > prev_ma7_output AND slope_output < 0 THEN 'TURNING_POINT' WHEN prev_ma7_output IS NOT NULL AND ma7_output < prev_ma7_output AND slope_output > 0 THEN 'TURNING_POINT' ELSE 'NONE' END AS output_turning_point, ROUND(defect_rate, 2) AS defect_rate, ma7_defect_rate AS defect_rate_ma7, slope_defect, defect_trend, defect_status, CASE WHEN prev_ma7_defect_rate IS NOT NULL AND ma7_defect_rate > prev_ma7_defect_rate AND slope_defect < 0 THEN 'TURNING_POINT' WHEN prev_ma7_defect_rate IS NOT NULL AND ma7_defect_rate < prev_ma7_defect_rate AND slope_defect > 0 THEN 'TURNING_POINT' ELSE 'NONE' END AS defect_turning_point FROM trend_analysis ORDER BY metric_date DESC", + "parameters": {}, + "testParams": {} +} +2026-01-08 10:02:35 - httpx - INFO - [_client.py:1025] - HTTP Request: POST http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema "HTTP/1.1 200 " +2026-01-08 10:02:35 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:154] - 测试SQL API调用成功 +2026-01-08 10:02:35 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功 +2026-01-08 10:22:23 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type CallToolRequest +2026-01-08 10:22:23 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: OrderDelayWarningAnalysis +2026-01-08 10:22:23 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API... +2026-01-08 10:22:23 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:138] - 正在调用测试SQL API: http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema +2026-01-08 10:22:23 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:139] - 请求参数: { + "datasourceId": "19", + "businessName": "OrderDelayWarningAnalysis", + "businessDescription": "订单延迟预警分析:依据历史订单的生产周期、物流延误、设备故障等特征,输出延迟概率与红/黄/绿预警等级", + "sqlTemplate": "WITH production_cycle_stats AS (SELECT COALESCE(AVG(GREATEST(0, EXTRACT(DAY FROM last_updated_utc - event_time_utc))), 0) AS avg_production_days FROM fact_work_order WHERE status = 'CLOSED' AND last_updated_utc >= event_time_utc), logistics_delay_stats AS (SELECT customer_id, AVG(GREATEST(0, EXTRACT(DAY FROM event_time_utc - doc_date_utc))) AS avg_logistics_delay_days, SUM(CASE WHEN EXTRACT(DAY FROM event_time_utc - doc_date_utc) > 3 THEN 1 ELSE 0 END) AS delay_count FROM fact_sales_shipment WHERE doc_date_utc IS NOT NULL AND event_time_utc IS NOT NULL GROUP BY customer_id), quality_issue_stats AS (SELECT COALESCE(ROUND(SUM(fail_qty) * 100.0 / NULLIF(SUM(pass_qty + fail_qty), 0), 2), 0) AS defect_rate_pct FROM fact_quality_inspection WHERE pass_qty IS NOT NULL AND fail_qty IS NOT NULL), scrap_stats AS (SELECT COALESCE((SELECT COUNT(*) FROM fact_scrap) * 100.0 / NULLIF((SELECT COUNT(*) FROM fact_work_order WHERE status = 'CLOSED'), 0), 0) AS scrap_rate_pct), active_work_order_risk AS (SELECT COUNT(*) AS active_wo_count, COALESCE(SUM(CASE WHEN planned_qty > 0 AND (completed_qty / planned_qty) < 0.3 AND EXTRACT(DAY FROM NOW() - event_time_utc) > 7 THEN 1 ELSE 0 END), 0) AS lagging_wo_count FROM fact_work_order WHERE status IN ('OPEN', 'STARTED')), global_metrics AS (SELECT pcs.avg_production_days, qis.defect_rate_pct, ss.scrap_rate_pct, awr.active_wo_count, awr.lagging_wo_count FROM production_cycle_stats pcs, quality_issue_stats qis, scrap_stats ss, active_work_order_risk awr) SELECT so.sales_order_number AS order_number, c.customer_name, so.order_date_utc::DATE AS order_date, so.deal_amount AS order_amount, so.payment_status, ROUND(gm.avg_production_days::NUMERIC, 1) AS avg_production_days, ROUND(COALESCE(lds.avg_logistics_delay_days, 0)::NUMERIC, 1) AS avg_logistics_delay_days, COALESCE(lds.delay_count, 0)::INT AS historical_delay_count, ROUND(gm.defect_rate_pct::NUMERIC, 2) AS defect_rate_pct, ROUND(gm.scrap_rate_pct::NUMERIC, 2) AS scrap_rate_pct, gm.active_wo_count::INT AS active_work_order_count, gm.lagging_wo_count::INT AS lagging_work_order_count, ROUND(LEAST(100, GREATEST(0, LEAST(25, GREATEST(0, gm.avg_production_days - 10) * 2.5) + LEAST(30, COALESCE(lds.avg_logistics_delay_days, 0) * 6) + LEAST(25, gm.defect_rate_pct * 2.5) + LEAST(20, gm.lagging_wo_count * 10)))::NUMERIC, 1) AS delay_probability_pct, CASE WHEN (LEAST(25, GREATEST(0, gm.avg_production_days - 10) * 2.5) + LEAST(30, COALESCE(lds.avg_logistics_delay_days, 0) * 6) + LEAST(25, gm.defect_rate_pct * 2.5) + LEAST(20, gm.lagging_wo_count * 10)) >= 60 THEN 'RED' WHEN (LEAST(25, GREATEST(0, gm.avg_production_days - 10) * 2.5) + LEAST(30, COALESCE(lds.avg_logistics_delay_days, 0) * 6) + LEAST(25, gm.defect_rate_pct * 2.5) + LEAST(20, gm.lagging_wo_count * 10)) >= 30 THEN 'YELLOW' ELSE 'GREEN' END AS warning_level, CASE WHEN gm.lagging_wo_count >= 2 THEN 'PRODUCTION_SEVERELY_DELAYED' WHEN COALESCE(lds.avg_logistics_delay_days, 0) > 5 THEN 'HIGH_LOGISTICS_DELAY_RISK' WHEN gm.avg_production_days > 15 THEN 'LONG_PRODUCTION_CYCLE' WHEN gm.defect_rate_pct > 10 THEN 'QUALITY_ISSUES' ELSE 'NORMAL' END AS primary_risk_factor FROM fact_sales_order so LEFT JOIN dim_customer c ON so.customer_id = c.customer_id AND c.is_current = 't' CROSS JOIN global_metrics gm LEFT JOIN logistics_delay_stats lds ON so.customer_id = lds.customer_id ORDER BY delay_probability_pct DESC, so.order_date_utc DESC", + "parameters": {}, + "testParams": {} +} +2026-01-08 10:22:24 - httpx - INFO - [_client.py:1025] - HTTP Request: POST http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema "HTTP/1.1 200 " +2026-01-08 10:22:24 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:154] - 测试SQL API调用成功 +2026-01-08 10:22:24 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功 +2026-01-08 10:57:20 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type CallToolRequest +2026-01-08 10:57:20 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: WorkOrderProgressAndAnomalyNodes +2026-01-08 10:57:20 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API... +2026-01-08 10:57:20 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:138] - 正在调用测试SQL API: http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema +2026-01-08 10:57:20 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:139] - 请求参数: { + "datasourceId": "19", + "businessName": "WorkOrderProgressAndAnomalyNodes", + "businessDescription": "工单执行进度与异常节点:实时拉取工单数据,动态映射订单各环节状态(OPEN→PENDING, STARTED→IN_PROGRESS, CLOSED→COMPLETED),呈现执行进度与异常节点", + "sqlTemplate": "WITH work_order_base AS (SELECT wo.work_order_id, wo.work_order_number, wo.product_id, wo.status, wo.planned_qty, wo.completed_qty, wo.event_time_utc::timestamp AS start_time, wo.last_updated_utc::timestamp AS last_update, wo.source_system FROM fact_work_order wo), product_info AS (SELECT product_id, product_name, product_category FROM dim_product WHERE is_current = 't'), labor_summary AS (SELECT work_order_number, COUNT(DISTINCT worker_name) AS worker_count, SUM(report_qty) AS total_report_qty, SUM(duration_minutes) AS total_minutes, MAX(event_time_utc::timestamp) AS last_report_time FROM fact_labor_report GROUP BY work_order_number), quality_summary AS (SELECT work_order_number, SUM(pass_qty) AS pass_qty, SUM(fail_qty) AS fail_qty FROM fact_quality_inspection GROUP BY work_order_number), work_order_progress AS (SELECT wb.work_order_id, wb.work_order_number, p.product_name, p.product_category, wb.status AS raw_status, CASE wb.status WHEN 'OPEN' THEN 'PENDING' WHEN 'STARTED' THEN 'IN_PROGRESS' WHEN 'CLOSED' THEN 'COMPLETED' ELSE 'UNKNOWN' END AS status_name, wb.planned_qty, wb.completed_qty, CASE WHEN wb.planned_qty > 0 THEN ROUND(wb.completed_qty * 100.0 / wb.planned_qty, 1) ELSE 0 END AS completion_rate, GREATEST(wb.planned_qty - wb.completed_qty, 0) AS remaining_qty, wb.start_time, wb.last_update, ROUND(EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - wb.start_time)) / 3600, 1) AS elapsed_hours, COALESCE(ls.worker_count, 0) AS worker_count, COALESCE(ls.total_report_qty, 0) AS total_report_qty, COALESCE(ls.total_minutes, 0) AS total_work_minutes, ls.last_report_time, COALESCE(qs.pass_qty, 0) AS qc_pass_qty, COALESCE(qs.fail_qty, 0) AS qc_fail_qty FROM work_order_base wb LEFT JOIN product_info p ON wb.product_id = p.product_id LEFT JOIN labor_summary ls ON wb.work_order_number = ls.work_order_number LEFT JOIN quality_summary qs ON wb.work_order_number = qs.work_order_number), anomaly_detection AS (SELECT *, CASE WHEN raw_status = 'STARTED' AND elapsed_hours > 48 AND completion_rate < 20 THEN 'SEVERELY_DELAYED' WHEN raw_status = 'STARTED' AND elapsed_hours > 24 AND completion_rate < 30 THEN 'DELAYED' ELSE NULL END AS progress_anomaly, CASE WHEN qc_pass_qty + qc_fail_qty > 0 AND qc_fail_qty * 100.0 / (qc_pass_qty + qc_fail_qty) > 10 THEN 'QUALITY_ISSUE' ELSE NULL END AS quality_anomaly, CASE WHEN raw_status = 'STARTED' AND last_report_time IS NOT NULL AND EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - last_report_time)) / 3600 > 24 THEN 'LABOR_STALLED' WHEN raw_status = 'STARTED' AND last_report_time IS NULL AND elapsed_hours > 24 THEN 'NO_LABOR_RECORD' ELSE NULL END AS labor_anomaly, CASE WHEN total_work_minutes > 0 AND total_report_qty / (total_work_minutes / 60.0) < 5 THEN 'LOW_EFFICIENCY' ELSE NULL END AS efficiency_anomaly FROM work_order_progress) SELECT work_order_number, product_name, product_category, status_name, planned_qty, completed_qty, remaining_qty, completion_rate, worker_count, ROUND(total_work_minutes / 60.0, 1) AS total_work_hours, elapsed_hours, COALESCE(progress_anomaly, '') || CASE WHEN progress_anomaly IS NOT NULL AND quality_anomaly IS NOT NULL THEN ',' ELSE '' END || COALESCE(quality_anomaly, '') || CASE WHEN (progress_anomaly IS NOT NULL OR quality_anomaly IS NOT NULL) AND labor_anomaly IS NOT NULL THEN ',' ELSE '' END || COALESCE(labor_anomaly, '') || CASE WHEN (progress_anomaly IS NOT NULL OR quality_anomaly IS NOT NULL OR labor_anomaly IS NOT NULL) AND efficiency_anomaly IS NOT NULL THEN ',' ELSE '' END || COALESCE(efficiency_anomaly, '') AS anomaly_flags, CASE WHEN progress_anomaly = 'SEVERELY_DELAYED' OR quality_anomaly IS NOT NULL THEN 'HIGH' WHEN progress_anomaly = 'DELAYED' OR labor_anomaly IS NOT NULL THEN 'MEDIUM' WHEN efficiency_anomaly IS NOT NULL THEN 'LOW' ELSE 'NONE' END AS risk_level FROM anomaly_detection ORDER BY CASE raw_status WHEN 'STARTED' THEN 1 WHEN 'OPEN' THEN 2 ELSE 3 END, CASE WHEN progress_anomaly IS NOT NULL THEN 0 ELSE 1 END, completion_rate ASC", + "parameters": {}, + "testParams": {} +} +2026-01-08 10:57:20 - httpx - INFO - [_client.py:1025] - HTTP Request: POST http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema "HTTP/1.1 200 " +2026-01-08 10:57:20 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:154] - 测试SQL API调用成功 +2026-01-08 10:57:20 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功 +2026-01-08 11:05:47 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type CallToolRequest +2026-01-08 11:05:47 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: SupplyChainRiskWarning +2026-01-08 11:05:47 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API... +2026-01-08 11:05:47 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:138] - 正在调用测试SQL API: http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema +2026-01-08 11:05:47 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:139] - 请求参数: { + "datasourceId": "19", + "businessName": "SupplyChainRiskWarning", + "businessDescription": "供应链风险预警:整合采购系统中的供应商历史交期与质检合格率,结合外采物流数据,识别交期异常+物流停滞组合风险模式,自动推送高风险订单提示", + "sqlTemplate": "WITH supplier_delivery AS (SELECT po.supplier_id, COUNT(*) AS order_count, COUNT(pr.purchase_receipt_id) AS receipt_count, AVG(CASE WHEN pr.doc_date_utc IS NOT NULL AND po.doc_date_utc IS NOT NULL THEN EXTRACT(DAY FROM pr.doc_date_utc::timestamp - po.doc_date_utc::timestamp) ELSE NULL END) AS avg_delivery_days, MAX(CASE WHEN pr.doc_date_utc IS NOT NULL AND po.doc_date_utc IS NOT NULL THEN EXTRACT(DAY FROM pr.doc_date_utc::timestamp - po.doc_date_utc::timestamp) ELSE NULL END) AS max_delivery_days, STDDEV(CASE WHEN pr.doc_date_utc IS NOT NULL AND po.doc_date_utc IS NOT NULL THEN EXTRACT(DAY FROM pr.doc_date_utc::timestamp - po.doc_date_utc::timestamp) ELSE NULL END) AS stddev_delivery_days FROM fact_purchase_order po LEFT JOIN fact_purchase_receipt pr ON po.supplier_id = pr.supplier_id GROUP BY po.supplier_id), supplier_quality AS (SELECT pr.supplier_id, COUNT(pr.purchase_receipt_id) AS total_receipts, SUM(pr.receipt_qty_total) AS total_qty, SUM(pr.amount) AS total_amount FROM fact_purchase_receipt pr GROUP BY pr.supplier_id), supplier_returns AS (SELECT pret.supplier_id, COUNT(*) AS return_count, SUM(CASE WHEN pret.return_reason = '损坏' THEN 1 ELSE 0 END) AS damage_count FROM fact_purchase_return pret GROUP BY pret.supplier_id), supplier_quality_rate AS (SELECT sq.supplier_id, sq.total_receipts, sq.total_qty, sq.total_amount, COALESCE(sr.return_count, 0) AS return_count, COALESCE(sr.damage_count, 0) AS damage_count, CASE WHEN sq.total_receipts > 0 THEN (sq.total_receipts - COALESCE(sr.return_count, 0)) * 100.0 / sq.total_receipts ELSE 100 END AS quality_rate FROM supplier_quality sq LEFT JOIN supplier_returns sr ON sq.supplier_id = sr.supplier_id), supplier_risk AS (SELECT s.supplier_id, s.supplier_name, s.supplier_category, COALESCE(sd.order_count, 0) AS order_count, COALESCE(sd.receipt_count, 0) AS receipt_count, ROUND(COALESCE(sd.avg_delivery_days, 0), 1) AS avg_delivery_days, ROUND(COALESCE(sd.max_delivery_days, 0), 1) AS max_delivery_days, ROUND(COALESCE(sd.stddev_delivery_days, 0), 1) AS delivery_volatility, COALESCE(sqr.total_receipts, 0) AS total_receipts, COALESCE(sqr.return_count, 0) AS return_count, ROUND(COALESCE(sqr.quality_rate, 100), 1) AS quality_rate, CASE WHEN COALESCE(sd.avg_delivery_days, 0) > 60 THEN 40 WHEN COALESCE(sd.avg_delivery_days, 0) > 45 THEN 30 WHEN COALESCE(sd.avg_delivery_days, 0) > 30 THEN 20 ELSE 10 END + CASE WHEN COALESCE(sd.stddev_delivery_days, 0) > 20 THEN 30 WHEN COALESCE(sd.stddev_delivery_days, 0) > 10 THEN 20 ELSE 10 END AS delivery_risk_score, CASE WHEN COALESCE(sqr.quality_rate, 100) < 80 THEN 50 WHEN COALESCE(sqr.quality_rate, 100) < 90 THEN 30 WHEN COALESCE(sqr.quality_rate, 100) < 95 THEN 15 ELSE 5 END AS quality_risk_score FROM dim_supplier s LEFT JOIN supplier_delivery sd ON s.supplier_id = sd.supplier_id LEFT JOIN supplier_quality_rate sqr ON s.supplier_id = sqr.supplier_id WHERE s.is_current = 't'), supplier_risk_level AS (SELECT *, delivery_risk_score + quality_risk_score AS total_risk_score, CASE WHEN delivery_risk_score + quality_risk_score >= 80 THEN 'HIGH' WHEN delivery_risk_score + quality_risk_score >= 50 THEN 'MEDIUM' ELSE 'LOW' END AS risk_level, CASE WHEN delivery_risk_score >= 50 AND quality_risk_score >= 30 THEN 'DELIVERY_AND_QUALITY' WHEN delivery_risk_score >= 50 THEN 'DELIVERY_ISSUE' WHEN quality_risk_score >= 30 THEN 'QUALITY_ISSUE' ELSE 'NORMAL' END AS risk_pattern FROM supplier_risk) SELECT supplier_name, supplier_category, order_count, receipt_count, avg_delivery_days, max_delivery_days, delivery_volatility, total_receipts, return_count, quality_rate, delivery_risk_score, quality_risk_score, total_risk_score, risk_level, risk_pattern FROM supplier_risk_level ORDER BY total_risk_score DESC, supplier_name", + "parameters": {}, + "testParams": {} +} +2026-01-08 11:05:48 - httpx - INFO - [_client.py:1025] - HTTP Request: POST http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema "HTTP/1.1 200 " +2026-01-08 11:05:48 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:154] - 测试SQL API调用成功 +2026-01-08 11:05:48 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功 +2026-01-08 11:13:21 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type CallToolRequest +2026-01-08 11:13:21 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: EfficiencyOutputLossDashboard +2026-01-08 11:13:21 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API... +2026-01-08 11:13:21 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:138] - 正在调用测试SQL API: http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema +2026-01-08 11:13:21 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:139] - 请求参数: { + "datasourceId": "19", + "businessName": "EfficiencyOutputLossDashboard", + "businessDescription": "人效-产值-损耗三维模型仪表盘:关联订单量×工时×人员数×成本,构建人效—产值—损耗三维模型,按部门(产品类别)汇总展示", + "sqlTemplate": "WITH labor_stats AS (SELECT p.product_category AS department, COUNT(DISTINCT lr.worker_name) AS worker_count, SUM(lr.duration_minutes) AS total_work_minutes, SUM(lr.report_qty) AS total_output_qty, COUNT(DISTINCT lr.work_order_number) AS work_order_count FROM fact_labor_report lr INNER JOIN dim_product p ON lr.product_id = p.product_id AND p.is_current = 't' GROUP BY p.product_category), work_order_stats AS (SELECT p.product_category AS department, COUNT(*) AS total_work_orders, SUM(wo.planned_qty) AS total_planned_qty, SUM(wo.completed_qty) AS total_completed_qty, SUM(CASE WHEN wo.status = 'CLOSED' THEN 1 ELSE 0 END) AS closed_orders, SUM(CASE WHEN wo.status = 'STARTED' THEN 1 ELSE 0 END) AS started_orders, SUM(CASE WHEN wo.status = 'OPEN' THEN 1 ELSE 0 END) AS open_orders FROM fact_work_order wo INNER JOIN dim_product p ON wo.product_id = p.product_id AND p.is_current = 't' GROUP BY p.product_category), quality_stats AS (SELECT p.product_category AS department, SUM(qi.pass_qty) AS total_pass_qty, SUM(qi.fail_qty) AS total_fail_qty, COUNT(*) AS inspection_count FROM fact_quality_inspection qi INNER JOIN dim_product p ON qi.product_id = p.product_id AND p.is_current = 't' GROUP BY p.product_category), sales_stats AS (SELECT AVG(deal_amount) AS avg_order_amount, SUM(deal_amount) AS total_sales_amount, COUNT(*) AS order_count FROM fact_sales_order), scrap_stats AS (SELECT COUNT(*) AS total_scrap_count FROM fact_scrap) SELECT ls.department, ls.worker_count, ROUND(ls.total_work_minutes / 60.0, 2) AS total_work_hours, ls.total_output_qty, ROUND(ls.total_output_qty / NULLIF(ls.worker_count, 0), 2) AS output_per_worker, ROUND(ls.total_output_qty / NULLIF(ls.total_work_minutes / 60.0, 0), 2) AS output_per_hour, ROUND(ls.total_output_qty / NULLIF(ls.worker_count, 0) / NULLIF(ls.total_work_minutes / 60.0 / ls.worker_count, 0), 2) AS efficiency_index, ws.total_planned_qty AS planned_qty, ws.total_completed_qty AS completed_qty, ROUND(ws.total_completed_qty / NULLIF(ws.total_planned_qty, 0) * 100, 2) AS plan_completion_rate, ROUND(ws.total_completed_qty * ss.avg_order_amount / 100, 2) AS estimated_output_value, ROUND(ws.total_completed_qty * ss.avg_order_amount / 100 / NULLIF(ls.worker_count, 0), 2) AS output_value_per_worker, qs.total_pass_qty AS qc_pass_qty, qs.total_fail_qty AS qc_fail_qty, ROUND(qs.total_fail_qty / NULLIF(qs.total_pass_qty + qs.total_fail_qty, 0) * 100, 2) AS defect_rate, ROUND((ws.total_planned_qty - ws.total_completed_qty) / NULLIF(ws.total_planned_qty, 0) * 100, 2) AS production_loss_rate, ROUND((ls.total_output_qty / NULLIF(ls.worker_count, 0) / NULLIF(ls.total_work_minutes / 60.0 / ls.worker_count, 0)) * 0.4 + (ws.total_completed_qty / NULLIF(ws.total_planned_qty, 0) * 100) * 0.35 + (qs.total_pass_qty / NULLIF(qs.total_pass_qty + qs.total_fail_qty, 0) * 100) * 0.25, 2) AS performance_score, CASE WHEN (qs.total_fail_qty / NULLIF(qs.total_pass_qty + qs.total_fail_qty, 0) * 100) >= 10 OR (ws.total_completed_qty / NULLIF(ws.total_planned_qty, 0) * 100) < 50 THEN 'RED' WHEN (qs.total_fail_qty / NULLIF(qs.total_pass_qty + qs.total_fail_qty, 0) * 100) >= 5 OR (ws.total_completed_qty / NULLIF(ws.total_planned_qty, 0) * 100) < 70 THEN 'YELLOW' ELSE 'GREEN' END AS warning_level FROM labor_stats ls LEFT JOIN work_order_stats ws ON ls.department = ws.department LEFT JOIN quality_stats qs ON ls.department = qs.department CROSS JOIN sales_stats ss CROSS JOIN scrap_stats scr ORDER BY performance_score DESC", + "parameters": {}, + "testParams": {} +} +2026-01-08 11:13:22 - httpx - INFO - [_client.py:1025] - HTTP Request: POST http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema "HTTP/1.1 200 " +2026-01-08 11:13:22 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:154] - 测试SQL API调用成功 +2026-01-08 11:13:22 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功 +2026-01-08 11:34:55 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type CallToolRequest +2026-01-08 11:34:55 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: OnePageDecisionBrief +2026-01-08 11:34:55 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API... +2026-01-08 11:34:55 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:138] - 正在调用测试SQL API: http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema +2026-01-08 11:34:55 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:139] - 请求参数: { + "datasourceId": "19", + "businessName": "OnePageDecisionBrief", + "businessDescription": "一页式决策简报:自动聚合订单、生产、财务、售后等关键数据,生成经营决策简报", + "sqlTemplate": "WITH sales_summary AS (SELECT COUNT(*) AS total_orders, SUM(deal_amount) AS total_sales_amount, AVG(deal_amount) AS avg_order_amount, SUM(CASE WHEN payment_status = 'PAID' THEN 1 ELSE 0 END) AS paid_orders, SUM(CASE WHEN payment_status = 'PARTIAL' THEN 1 ELSE 0 END) AS partial_orders, SUM(CASE WHEN payment_status = 'UNPAID' THEN 1 ELSE 0 END) AS unpaid_orders, SUM(CASE WHEN payment_status = 'PAID' THEN deal_amount ELSE 0 END) AS paid_amount, SUM(CASE WHEN payment_status = 'UNPAID' THEN deal_amount ELSE 0 END) AS unpaid_amount FROM fact_sales_order), production_summary AS (SELECT COUNT(*) AS total_work_orders, SUM(planned_qty) AS total_planned_qty, SUM(completed_qty) AS total_completed_qty, SUM(CASE WHEN status = 'CLOSED' THEN 1 ELSE 0 END) AS closed_orders, SUM(CASE WHEN status = 'STARTED' THEN 1 ELSE 0 END) AS started_orders, SUM(CASE WHEN status = 'OPEN' THEN 1 ELSE 0 END) AS open_orders FROM fact_work_order), ar_summary AS (SELECT COUNT(*) AS receipt_count, SUM(amount) AS total_receipt_amount, AVG(amount) AS avg_receipt_amount FROM fact_ar_receipt), ap_summary AS (SELECT COUNT(*) AS payment_count, SUM(amount) AS total_payment_amount, AVG(amount) AS avg_payment_amount FROM fact_ap_payment), invoice_summary AS (SELECT COUNT(*) AS invoice_count, SUM(invoice_amount) AS total_invoice_amount, AVG(invoice_amount) AS avg_invoice_amount FROM fact_invoice), return_summary AS (SELECT COUNT(*) AS return_count, SUM(amount) AS total_return_amount, AVG(amount) AS avg_return_amount FROM fact_sales_return), shipment_summary AS (SELECT COUNT(*) AS shipment_count, SUM(amount) AS total_shipment_amount, AVG(amount) AS avg_shipment_amount FROM fact_sales_shipment), purchase_summary AS (SELECT COUNT(*) AS purchase_order_count FROM fact_purchase_order), quality_summary AS (SELECT COUNT(*) AS inspection_count, SUM(pass_qty) AS total_pass_qty, SUM(fail_qty) AS total_fail_qty FROM fact_quality_inspection), labor_summary AS (SELECT COUNT(DISTINCT worker_name) AS worker_count, SUM(duration_minutes) AS total_work_minutes, SUM(report_qty) AS total_output_qty FROM fact_labor_report), scrap_summary AS (SELECT COUNT(*) AS scrap_count FROM fact_scrap) SELECT 'Decision Brief' AS report_title, CURRENT_DATE AS report_date, ss.total_orders AS sales_order_count, ROUND(ss.total_sales_amount, 2) AS total_sales_amount, ROUND(ss.avg_order_amount, 2) AS avg_order_amount, ss.paid_orders AS paid_order_count, ss.partial_orders AS partial_paid_count, ss.unpaid_orders AS unpaid_order_count, ROUND(ss.paid_orders * 100.0 / NULLIF(ss.total_orders, 0), 1) AS payment_completion_rate, ROUND(ss.unpaid_amount, 2) AS receivable_amount, ps.total_work_orders AS work_order_count, ps.closed_orders AS completed_work_orders, ps.started_orders AS in_progress_work_orders, ps.open_orders AS pending_work_orders, ROUND(ps.total_planned_qty, 0) AS planned_qty, ROUND(ps.total_completed_qty, 0) AS completed_qty, ROUND(ps.total_completed_qty * 100.0 / NULLIF(ps.total_planned_qty, 0), 1) AS production_completion_rate, ls.worker_count AS active_worker_count, ROUND(ls.total_work_minutes / 60.0, 1) AS total_work_hours, ROUND(ls.total_output_qty, 0) AS total_output, ROUND(ls.total_output_qty / NULLIF(ls.worker_count, 0), 1) AS output_per_worker, ROUND(ls.total_output_qty / NULLIF(ls.total_work_minutes / 60.0, 0), 2) AS output_per_hour, qs.inspection_count AS qc_batch_count, ROUND(qs.total_pass_qty, 0) AS pass_qty, ROUND(qs.total_fail_qty, 0) AS fail_qty, ROUND(qs.total_pass_qty * 100.0 / NULLIF(qs.total_pass_qty + qs.total_fail_qty, 0), 2) AS pass_rate, ROUND(qs.total_fail_qty * 100.0 / NULLIF(qs.total_pass_qty + qs.total_fail_qty, 0), 2) AS defect_rate, scr.scrap_count AS scrap_record_count, ar.receipt_count AS ar_receipt_count, ROUND(ar.total_receipt_amount, 2) AS total_ar_amount, ap.payment_count AS ap_payment_count, ROUND(ap.total_payment_amount, 2) AS total_ap_amount, ROUND(ar.total_receipt_amount - ap.total_payment_amount, 2) AS net_cash_flow, inv.invoice_count, ROUND(inv.total_invoice_amount, 2) AS total_invoice_amount, sh.shipment_count, ROUND(sh.total_shipment_amount, 2) AS total_shipment_amount, pur.purchase_order_count, ret.return_count, ROUND(ret.total_return_amount, 2) AS total_return_amount, ROUND(ret.total_return_amount * 100.0 / NULLIF(ss.total_sales_amount, 0), 2) AS return_rate, CASE WHEN (qs.total_pass_qty * 100.0 / NULLIF(qs.total_pass_qty + qs.total_fail_qty, 0)) >= 95 AND (ps.total_completed_qty * 100.0 / NULLIF(ps.total_planned_qty, 0)) >= 80 AND (ret.total_return_amount * 100.0 / NULLIF(ss.total_sales_amount, 0)) < 5 THEN 'EXCELLENT' WHEN (qs.total_pass_qty * 100.0 / NULLIF(qs.total_pass_qty + qs.total_fail_qty, 0)) >= 90 AND (ps.total_completed_qty * 100.0 / NULLIF(ps.total_planned_qty, 0)) >= 60 AND (ret.total_return_amount * 100.0 / NULLIF(ss.total_sales_amount, 0)) < 10 THEN 'GOOD' ELSE 'WARNING' END AS health_status FROM sales_summary ss CROSS JOIN production_summary ps CROSS JOIN ar_summary ar CROSS JOIN ap_summary ap CROSS JOIN invoice_summary inv CROSS JOIN return_summary ret CROSS JOIN shipment_summary sh CROSS JOIN purchase_summary pur CROSS JOIN quality_summary qs CROSS JOIN labor_summary ls CROSS JOIN scrap_summary scr", + "parameters": {}, + "testParams": {} +} +2026-01-08 11:34:56 - httpx - INFO - [_client.py:1025] - HTTP Request: POST http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema "HTTP/1.1 200 " +2026-01-08 11:34:56 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:154] - 测试SQL API调用成功 +2026-01-08 11:34:56 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功 +2026-01-08 11:59:37 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type CallToolRequest +2026-01-08 11:59:37 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: MetricTrendAndTurningPointWarning +2026-01-08 11:59:37 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API... +2026-01-08 11:59:37 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:138] - 正在调用测试SQL API: http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema +2026-01-08 11:59:37 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:139] - 请求参数: { + "datasourceId": "19", + "businessName": "MetricTrendAndTurningPointWarning", + "businessDescription": "指标趋势分析与拐点预警:基于移动平均与线性回归分析人效、产量、废品率趋势,输出上升/下降/平稳判断与拐点预警", + "sqlTemplate": "WITH daily_metrics AS (SELECT DATE(lr.event_time_utc::timestamp) AS metric_date, COUNT(DISTINCT lr.worker_name) AS worker_count, SUM(lr.duration_minutes) / 60.0 AS total_hours, SUM(lr.report_qty) AS total_output, CASE WHEN SUM(lr.duration_minutes) > 0 THEN SUM(lr.report_qty) / (SUM(lr.duration_minutes) / 60.0) ELSE 0 END AS hourly_efficiency FROM fact_labor_report lr GROUP BY DATE(lr.event_time_utc::timestamp)), daily_quality AS (SELECT DATE(qi.event_time_utc::timestamp) AS metric_date, SUM(qi.pass_qty) AS pass_qty, SUM(qi.fail_qty) AS fail_qty, CASE WHEN SUM(qi.pass_qty) + SUM(qi.fail_qty) > 0 THEN SUM(qi.fail_qty) * 100.0 / (SUM(qi.pass_qty) + SUM(qi.fail_qty)) ELSE 0 END AS defect_rate FROM fact_quality_inspection qi GROUP BY DATE(qi.event_time_utc::timestamp)), daily_production AS (SELECT DATE(wo.event_time_utc::timestamp) AS metric_date, SUM(wo.planned_qty) AS planned_qty, SUM(wo.completed_qty) AS completed_qty, CASE WHEN SUM(wo.planned_qty) > 0 THEN SUM(wo.completed_qty) * 100.0 / SUM(wo.planned_qty) ELSE 0 END AS completion_rate FROM fact_work_order wo GROUP BY DATE(wo.event_time_utc::timestamp)), combined_daily AS (SELECT COALESCE(dm.metric_date, dq.metric_date, dp.metric_date) AS metric_date, COALESCE(dm.worker_count, 0) AS worker_count, COALESCE(dm.total_hours, 0) AS total_hours, COALESCE(dm.total_output, 0) AS total_output, COALESCE(dm.hourly_efficiency, 0) AS hourly_efficiency, COALESCE(dq.defect_rate, 0) AS defect_rate, COALESCE(dp.completion_rate, 0) AS completion_rate FROM daily_metrics dm FULL OUTER JOIN daily_quality dq ON dm.metric_date = dq.metric_date FULL OUTER JOIN daily_production dp ON dm.metric_date = dp.metric_date WHERE COALESCE(dm.metric_date, dq.metric_date, dp.metric_date) IS NOT NULL), numbered_data AS (SELECT *, ROW_NUMBER() OVER (ORDER BY metric_date) AS day_seq FROM combined_daily), moving_avg_step1 AS (SELECT metric_date, day_seq, worker_count, total_output, hourly_efficiency, defect_rate, completion_rate, AVG(hourly_efficiency) OVER (ORDER BY metric_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS ma7_efficiency, AVG(total_output) OVER (ORDER BY metric_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS ma7_output, AVG(defect_rate) OVER (ORDER BY metric_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS ma7_defect_rate, AVG(completion_rate) OVER (ORDER BY metric_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS ma7_completion_rate, AVG(day_seq) OVER (ORDER BY metric_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS avg_x FROM numbered_data), moving_avg AS (SELECT *, LAG(ma7_efficiency, 1) OVER (ORDER BY metric_date) AS prev_ma7_efficiency, LAG(ma7_output, 1) OVER (ORDER BY metric_date) AS prev_ma7_output, LAG(ma7_defect_rate, 1) OVER (ORDER BY metric_date) AS prev_ma7_defect_rate FROM moving_avg_step1), slope_calc AS (SELECT *, (ma7_efficiency - LAG(ma7_efficiency, 3) OVER (ORDER BY metric_date)) / 3.0 AS slope_efficiency, (ma7_output - LAG(ma7_output, 3) OVER (ORDER BY metric_date)) / 3.0 AS slope_output, (ma7_defect_rate - LAG(ma7_defect_rate, 3) OVER (ORDER BY metric_date)) / 3.0 AS slope_defect FROM moving_avg), trend_analysis AS (SELECT metric_date, hourly_efficiency, total_output, defect_rate, completion_rate, ROUND(ma7_efficiency, 2) AS ma7_efficiency, ROUND(ma7_output, 2) AS ma7_output, ROUND(ma7_defect_rate, 2) AS ma7_defect_rate, ROUND(COALESCE(slope_efficiency, 0), 4) AS slope_efficiency, ROUND(COALESCE(slope_output, 0), 4) AS slope_output, ROUND(COALESCE(slope_defect, 0), 4) AS slope_defect, prev_ma7_efficiency, prev_ma7_output, prev_ma7_defect_rate, CASE WHEN COALESCE(slope_efficiency, 0) > 0.3 THEN 'RISING' WHEN COALESCE(slope_efficiency, 0) < -0.3 THEN 'FALLING' ELSE 'STABLE' END AS efficiency_trend, CASE WHEN COALESCE(slope_output, 0) > 3 THEN 'RISING' WHEN COALESCE(slope_output, 0) < -3 THEN 'FALLING' ELSE 'STABLE' END AS output_trend, CASE WHEN COALESCE(slope_defect, 0) > 0.3 THEN 'RISING' WHEN COALESCE(slope_defect, 0) < -0.3 THEN 'FALLING' ELSE 'STABLE' END AS defect_trend, CASE WHEN ma7_efficiency > 0 AND ABS(hourly_efficiency - ma7_efficiency) > ma7_efficiency * 0.3 THEN 'ANOMALY' ELSE 'NORMAL' END AS efficiency_status, CASE WHEN ma7_output > 0 AND ABS(total_output - ma7_output) > ma7_output * 0.3 THEN 'ANOMALY' ELSE 'NORMAL' END AS output_status, CASE WHEN defect_rate > ma7_defect_rate * 1.5 AND defect_rate > 5 THEN 'ANOMALY' ELSE 'NORMAL' END AS defect_status FROM slope_calc) SELECT metric_date, ROUND(hourly_efficiency, 2) AS hourly_output, ma7_efficiency AS efficiency_ma7, slope_efficiency, efficiency_trend, efficiency_status, CASE WHEN prev_ma7_efficiency IS NOT NULL AND ma7_efficiency > prev_ma7_efficiency AND slope_efficiency < 0 THEN 'TURNING_POINT' WHEN prev_ma7_efficiency IS NOT NULL AND ma7_efficiency < prev_ma7_efficiency AND slope_efficiency > 0 THEN 'TURNING_POINT' ELSE 'NONE' END AS efficiency_turning_point, ROUND(total_output, 0) AS daily_output, ma7_output AS output_ma7, slope_output, output_trend, output_status, CASE WHEN prev_ma7_output IS NOT NULL AND ma7_output > prev_ma7_output AND slope_output < 0 THEN 'TURNING_POINT' WHEN prev_ma7_output IS NOT NULL AND ma7_output < prev_ma7_output AND slope_output > 0 THEN 'TURNING_POINT' ELSE 'NONE' END AS output_turning_point, ROUND(defect_rate, 2) AS defect_rate, ma7_defect_rate AS defect_rate_ma7, slope_defect, defect_trend, defect_status, CASE WHEN prev_ma7_defect_rate IS NOT NULL AND ma7_defect_rate > prev_ma7_defect_rate AND slope_defect < 0 THEN 'TURNING_POINT' WHEN prev_ma7_defect_rate IS NOT NULL AND ma7_defect_rate < prev_ma7_defect_rate AND slope_defect > 0 THEN 'TURNING_POINT' ELSE 'NONE' END AS defect_turning_point FROM trend_analysis ORDER BY metric_date DESC", + "parameters": {}, + "testParams": {} +} +2026-01-08 11:59:37 - httpx - INFO - [_client.py:1025] - HTTP Request: POST http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema "HTTP/1.1 200 " +2026-01-08 11:59:37 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:154] - 测试SQL API调用成功 +2026-01-08 11:59:37 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功 diff --git a/lzwcai_mcpskills_mfg_data_agent/lzwcai_mcpskills_mfg_data_agent/logs/lzwcai_mcp_sqlexecutor_daily.log b/lzwcai_mcpskills_mfg_data_agent/lzwcai_mcpskills_mfg_data_agent/logs/lzwcai_mcp_sqlexecutor_daily.log index ba82ae7..b3fb093 100644 --- a/lzwcai_mcpskills_mfg_data_agent/lzwcai_mcpskills_mfg_data_agent/logs/lzwcai_mcp_sqlexecutor_daily.log +++ b/lzwcai_mcpskills_mfg_data_agent/lzwcai_mcpskills_mfg_data_agent/logs/lzwcai_mcp_sqlexecutor_daily.log @@ -205,3 +205,158 @@ 2026-01-08 00:56:21 - httpx - INFO - [_client.py:1025] - HTTP Request: POST http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema "HTTP/1.1 200 " 2026-01-08 00:56:21 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:154] - 测试SQL API调用成功 2026-01-08 00:56:21 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功 +2026-01-08 09:52:27 - mcp_services - INFO - [main.py:344] - MCP 服务器已关闭 +2026-01-08 10:00:42 - root - INFO - [logger_config.py:151] - 日志系统初始化完成 - 日志目录: E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcpskills_mfg_data_agent\lzwcai_mcpskills_mfg_data_agent\logs +2026-01-08 10:00:42 - root - INFO - [logger_config.py:152] - 日志配置 - 级别: INFO, 文件大小限制: 10MB, 备份数量: 5 +2026-01-08 10:00:42 - mcp_services - INFO - [main.py:362] - 开始运行 MCP SQL Executor 服务器 +2026-01-08 10:00:42 - mcp_services - INFO - [main.py:313] - ============================================================ +2026-01-08 10:00:42 - mcp_services - INFO - [main.py:314] - 正在启动 MCP 服务器: lzwcai-mcpskills-analyzeOrder +2026-01-08 10:00:42 - mcp_services - INFO - [main.py:315] - 版本: 0.1.0 +2026-01-08 10:00:42 - mcp_services - INFO - [main.py:316] - ============================================================ +2026-01-08 10:00:42 - mcp_services - INFO - [main.py:320] - 环境配置 - Database ID: 19 +2026-01-08 10:00:42 - mcp_services - INFO - [main.py:321] - 环境配置 - Datasource ID: 19 +2026-01-08 10:00:42 - mcp_services - INFO - [main.py:322] - 环境配置 - Skill ID: +2026-01-08 10:00:42 - mcp_services - INFO - [main.py:323] - 环境配置 - Backend Base URL: http://192.168.11.24:8088 +2026-01-08 10:00:42 - mcp_services - INFO - [main.py:324] - ============================================================ +2026-01-08 10:00:42 - mcp_services - INFO - [main.py:329] - MCP 服务器已启动,等待客户端连接... +2026-01-08 10:00:45 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type ListToolsRequest +2026-01-08 10:00:45 - mcp_services - INFO - [main.py:156] - 收到列出工具请求 +2026-01-08 10:00:45 - mcp_services - INFO - [main.py:119] - 初始化查询配置(数据源: local)... +2026-01-08 10:00:45 - mcp_services - INFO - [main.py:55] - 成功加载 6 个业务查询配置 +2026-01-08 10:00:45 - mcp_services - INFO - [main.py:123] - 本地配置: 6 条 +2026-01-08 10:00:45 - mcp_services - INFO - [main.py:165] - 成功生成 6 个 MCP 工具 +2026-01-08 10:02:28 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type CallToolRequest +2026-01-08 10:02:28 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: OrderDelayWarningAnalysis +2026-01-08 10:02:28 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API... +2026-01-08 10:02:28 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:138] - 正在调用测试SQL API: http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema +2026-01-08 10:02:28 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:139] - 请求参数: { + "datasourceId": "19", + "businessName": "OrderDelayWarningAnalysis", + "businessDescription": "订单延迟预警分析:依据历史订单的生产周期、物流延误、设备故障等特征,输出延迟概率与红/黄/绿预警等级", + "sqlTemplate": "WITH production_cycle_stats AS (SELECT COALESCE(AVG(GREATEST(0, EXTRACT(DAY FROM last_updated_utc - event_time_utc))), 0) AS avg_production_days FROM fact_work_order WHERE status = 'CLOSED' AND last_updated_utc >= event_time_utc), logistics_delay_stats AS (SELECT customer_id, AVG(GREATEST(0, EXTRACT(DAY FROM event_time_utc - doc_date_utc))) AS avg_logistics_delay_days, SUM(CASE WHEN EXTRACT(DAY FROM event_time_utc - doc_date_utc) > 3 THEN 1 ELSE 0 END) AS delay_count FROM fact_sales_shipment WHERE doc_date_utc IS NOT NULL AND event_time_utc IS NOT NULL GROUP BY customer_id), quality_issue_stats AS (SELECT COALESCE(ROUND(SUM(fail_qty) * 100.0 / NULLIF(SUM(pass_qty + fail_qty), 0), 2), 0) AS defect_rate_pct FROM fact_quality_inspection WHERE pass_qty IS NOT NULL AND fail_qty IS NOT NULL), scrap_stats AS (SELECT COALESCE((SELECT COUNT(*) FROM fact_scrap) * 100.0 / NULLIF((SELECT COUNT(*) FROM fact_work_order WHERE status = 'CLOSED'), 0), 0) AS scrap_rate_pct), active_work_order_risk AS (SELECT COUNT(*) AS active_wo_count, COALESCE(SUM(CASE WHEN planned_qty > 0 AND (completed_qty / planned_qty) < 0.3 AND EXTRACT(DAY FROM NOW() - event_time_utc) > 7 THEN 1 ELSE 0 END), 0) AS lagging_wo_count FROM fact_work_order WHERE status IN ('OPEN', 'STARTED')), global_metrics AS (SELECT pcs.avg_production_days, qis.defect_rate_pct, ss.scrap_rate_pct, awr.active_wo_count, awr.lagging_wo_count FROM production_cycle_stats pcs, quality_issue_stats qis, scrap_stats ss, active_work_order_risk awr) SELECT so.sales_order_number AS order_number, c.customer_name, so.order_date_utc::DATE AS order_date, so.deal_amount AS order_amount, so.payment_status, ROUND(gm.avg_production_days::NUMERIC, 1) AS avg_production_days, ROUND(COALESCE(lds.avg_logistics_delay_days, 0)::NUMERIC, 1) AS avg_logistics_delay_days, COALESCE(lds.delay_count, 0)::INT AS historical_delay_count, ROUND(gm.defect_rate_pct::NUMERIC, 2) AS defect_rate_pct, ROUND(gm.scrap_rate_pct::NUMERIC, 2) AS scrap_rate_pct, gm.active_wo_count::INT AS active_work_order_count, gm.lagging_wo_count::INT AS lagging_work_order_count, ROUND(LEAST(100, GREATEST(0, LEAST(25, GREATEST(0, gm.avg_production_days - 10) * 2.5) + LEAST(30, COALESCE(lds.avg_logistics_delay_days, 0) * 6) + LEAST(25, gm.defect_rate_pct * 2.5) + LEAST(20, gm.lagging_wo_count * 10)))::NUMERIC, 1) AS delay_probability_pct, CASE WHEN (LEAST(25, GREATEST(0, gm.avg_production_days - 10) * 2.5) + LEAST(30, COALESCE(lds.avg_logistics_delay_days, 0) * 6) + LEAST(25, gm.defect_rate_pct * 2.5) + LEAST(20, gm.lagging_wo_count * 10)) >= 60 THEN 'RED' WHEN (LEAST(25, GREATEST(0, gm.avg_production_days - 10) * 2.5) + LEAST(30, COALESCE(lds.avg_logistics_delay_days, 0) * 6) + LEAST(25, gm.defect_rate_pct * 2.5) + LEAST(20, gm.lagging_wo_count * 10)) >= 30 THEN 'YELLOW' ELSE 'GREEN' END AS warning_level, CASE WHEN gm.lagging_wo_count >= 2 THEN 'PRODUCTION_SEVERELY_DELAYED' WHEN COALESCE(lds.avg_logistics_delay_days, 0) > 5 THEN 'HIGH_LOGISTICS_DELAY_RISK' WHEN gm.avg_production_days > 15 THEN 'LONG_PRODUCTION_CYCLE' WHEN gm.defect_rate_pct > 10 THEN 'QUALITY_ISSUES' ELSE 'NORMAL' END AS primary_risk_factor FROM fact_sales_order so LEFT JOIN dim_customer c ON so.customer_id = c.customer_id AND c.is_current = 't' CROSS JOIN global_metrics gm LEFT JOIN logistics_delay_stats lds ON so.customer_id = lds.customer_id ORDER BY delay_probability_pct DESC, so.order_date_utc DESC", + "parameters": {}, + "testParams": {} +} +2026-01-08 10:02:28 - httpx - INFO - [_client.py:1025] - HTTP Request: POST http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema "HTTP/1.1 200 " +2026-01-08 10:02:28 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:154] - 测试SQL API调用成功 +2026-01-08 10:02:28 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功 +2026-01-08 10:02:30 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type CallToolRequest +2026-01-08 10:02:30 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: WorkOrderProgressAndAnomalyNodes +2026-01-08 10:02:30 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API... +2026-01-08 10:02:30 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:138] - 正在调用测试SQL API: http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema +2026-01-08 10:02:30 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:139] - 请求参数: { + "datasourceId": "19", + "businessName": "WorkOrderProgressAndAnomalyNodes", + "businessDescription": "工单执行进度与异常节点:实时拉取工单数据,动态映射订单各环节状态(OPEN→PENDING, STARTED→IN_PROGRESS, CLOSED→COMPLETED),呈现执行进度与异常节点", + "sqlTemplate": "WITH work_order_base AS (SELECT wo.work_order_id, wo.work_order_number, wo.product_id, wo.status, wo.planned_qty, wo.completed_qty, wo.event_time_utc::timestamp AS start_time, wo.last_updated_utc::timestamp AS last_update, wo.source_system FROM fact_work_order wo), product_info AS (SELECT product_id, product_name, product_category FROM dim_product WHERE is_current = 't'), labor_summary AS (SELECT work_order_number, COUNT(DISTINCT worker_name) AS worker_count, SUM(report_qty) AS total_report_qty, SUM(duration_minutes) AS total_minutes, MAX(event_time_utc::timestamp) AS last_report_time FROM fact_labor_report GROUP BY work_order_number), quality_summary AS (SELECT work_order_number, SUM(pass_qty) AS pass_qty, SUM(fail_qty) AS fail_qty FROM fact_quality_inspection GROUP BY work_order_number), work_order_progress AS (SELECT wb.work_order_id, wb.work_order_number, p.product_name, p.product_category, wb.status AS raw_status, CASE wb.status WHEN 'OPEN' THEN 'PENDING' WHEN 'STARTED' THEN 'IN_PROGRESS' WHEN 'CLOSED' THEN 'COMPLETED' ELSE 'UNKNOWN' END AS status_name, wb.planned_qty, wb.completed_qty, CASE WHEN wb.planned_qty > 0 THEN ROUND(wb.completed_qty * 100.0 / wb.planned_qty, 1) ELSE 0 END AS completion_rate, GREATEST(wb.planned_qty - wb.completed_qty, 0) AS remaining_qty, wb.start_time, wb.last_update, ROUND(EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - wb.start_time)) / 3600, 1) AS elapsed_hours, COALESCE(ls.worker_count, 0) AS worker_count, COALESCE(ls.total_report_qty, 0) AS total_report_qty, COALESCE(ls.total_minutes, 0) AS total_work_minutes, ls.last_report_time, COALESCE(qs.pass_qty, 0) AS qc_pass_qty, COALESCE(qs.fail_qty, 0) AS qc_fail_qty FROM work_order_base wb LEFT JOIN product_info p ON wb.product_id = p.product_id LEFT JOIN labor_summary ls ON wb.work_order_number = ls.work_order_number LEFT JOIN quality_summary qs ON wb.work_order_number = qs.work_order_number), anomaly_detection AS (SELECT *, CASE WHEN raw_status = 'STARTED' AND elapsed_hours > 48 AND completion_rate < 20 THEN 'SEVERELY_DELAYED' WHEN raw_status = 'STARTED' AND elapsed_hours > 24 AND completion_rate < 30 THEN 'DELAYED' ELSE NULL END AS progress_anomaly, CASE WHEN qc_pass_qty + qc_fail_qty > 0 AND qc_fail_qty * 100.0 / (qc_pass_qty + qc_fail_qty) > 10 THEN 'QUALITY_ISSUE' ELSE NULL END AS quality_anomaly, CASE WHEN raw_status = 'STARTED' AND last_report_time IS NOT NULL AND EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - last_report_time)) / 3600 > 24 THEN 'LABOR_STALLED' WHEN raw_status = 'STARTED' AND last_report_time IS NULL AND elapsed_hours > 24 THEN 'NO_LABOR_RECORD' ELSE NULL END AS labor_anomaly, CASE WHEN total_work_minutes > 0 AND total_report_qty / (total_work_minutes / 60.0) < 5 THEN 'LOW_EFFICIENCY' ELSE NULL END AS efficiency_anomaly FROM work_order_progress) SELECT work_order_number, product_name, product_category, status_name, planned_qty, completed_qty, remaining_qty, completion_rate, worker_count, ROUND(total_work_minutes / 60.0, 1) AS total_work_hours, elapsed_hours, COALESCE(progress_anomaly, '') || CASE WHEN progress_anomaly IS NOT NULL AND quality_anomaly IS NOT NULL THEN ',' ELSE '' END || COALESCE(quality_anomaly, '') || CASE WHEN (progress_anomaly IS NOT NULL OR quality_anomaly IS NOT NULL) AND labor_anomaly IS NOT NULL THEN ',' ELSE '' END || COALESCE(labor_anomaly, '') || CASE WHEN (progress_anomaly IS NOT NULL OR quality_anomaly IS NOT NULL OR labor_anomaly IS NOT NULL) AND efficiency_anomaly IS NOT NULL THEN ',' ELSE '' END || COALESCE(efficiency_anomaly, '') AS anomaly_flags, CASE WHEN progress_anomaly = 'SEVERELY_DELAYED' OR quality_anomaly IS NOT NULL THEN 'HIGH' WHEN progress_anomaly = 'DELAYED' OR labor_anomaly IS NOT NULL THEN 'MEDIUM' WHEN efficiency_anomaly IS NOT NULL THEN 'LOW' ELSE 'NONE' END AS risk_level FROM anomaly_detection ORDER BY CASE raw_status WHEN 'STARTED' THEN 1 WHEN 'OPEN' THEN 2 ELSE 3 END, CASE WHEN progress_anomaly IS NOT NULL THEN 0 ELSE 1 END, completion_rate ASC", + "parameters": {}, + "testParams": {} +} +2026-01-08 10:02:30 - httpx - INFO - [_client.py:1025] - HTTP Request: POST http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema "HTTP/1.1 200 " +2026-01-08 10:02:30 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:154] - 测试SQL API调用成功 +2026-01-08 10:02:30 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功 +2026-01-08 10:02:35 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type CallToolRequest +2026-01-08 10:02:35 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: MetricTrendAndTurningPointWarning +2026-01-08 10:02:35 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API... +2026-01-08 10:02:35 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:138] - 正在调用测试SQL API: http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema +2026-01-08 10:02:35 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:139] - 请求参数: { + "datasourceId": "19", + "businessName": "MetricTrendAndTurningPointWarning", + "businessDescription": "指标趋势分析与拐点预警:基于移动平均与线性回归分析人效、产量、废品率趋势,输出上升/下降/平稳判断与拐点预警", + "sqlTemplate": "WITH daily_metrics AS (SELECT DATE(lr.event_time_utc::timestamp) AS metric_date, COUNT(DISTINCT lr.worker_name) AS worker_count, SUM(lr.duration_minutes) / 60.0 AS total_hours, SUM(lr.report_qty) AS total_output, CASE WHEN SUM(lr.duration_minutes) > 0 THEN SUM(lr.report_qty) / (SUM(lr.duration_minutes) / 60.0) ELSE 0 END AS hourly_efficiency FROM fact_labor_report lr GROUP BY DATE(lr.event_time_utc::timestamp)), daily_quality AS (SELECT DATE(qi.event_time_utc::timestamp) AS metric_date, SUM(qi.pass_qty) AS pass_qty, SUM(qi.fail_qty) AS fail_qty, CASE WHEN SUM(qi.pass_qty) + SUM(qi.fail_qty) > 0 THEN SUM(qi.fail_qty) * 100.0 / (SUM(qi.pass_qty) + SUM(qi.fail_qty)) ELSE 0 END AS defect_rate FROM fact_quality_inspection qi GROUP BY DATE(qi.event_time_utc::timestamp)), daily_production AS (SELECT DATE(wo.event_time_utc::timestamp) AS metric_date, SUM(wo.planned_qty) AS planned_qty, SUM(wo.completed_qty) AS completed_qty, CASE WHEN SUM(wo.planned_qty) > 0 THEN SUM(wo.completed_qty) * 100.0 / SUM(wo.planned_qty) ELSE 0 END AS completion_rate FROM fact_work_order wo GROUP BY DATE(wo.event_time_utc::timestamp)), combined_daily AS (SELECT COALESCE(dm.metric_date, dq.metric_date, dp.metric_date) AS metric_date, COALESCE(dm.worker_count, 0) AS worker_count, COALESCE(dm.total_hours, 0) AS total_hours, COALESCE(dm.total_output, 0) AS total_output, COALESCE(dm.hourly_efficiency, 0) AS hourly_efficiency, COALESCE(dq.defect_rate, 0) AS defect_rate, COALESCE(dp.completion_rate, 0) AS completion_rate FROM daily_metrics dm FULL OUTER JOIN daily_quality dq ON dm.metric_date = dq.metric_date FULL OUTER JOIN daily_production dp ON dm.metric_date = dp.metric_date WHERE COALESCE(dm.metric_date, dq.metric_date, dp.metric_date) IS NOT NULL), numbered_data AS (SELECT *, ROW_NUMBER() OVER (ORDER BY metric_date) AS day_seq FROM combined_daily), moving_avg_step1 AS (SELECT metric_date, day_seq, worker_count, total_output, hourly_efficiency, defect_rate, completion_rate, AVG(hourly_efficiency) OVER (ORDER BY metric_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS ma7_efficiency, AVG(total_output) OVER (ORDER BY metric_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS ma7_output, AVG(defect_rate) OVER (ORDER BY metric_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS ma7_defect_rate, AVG(completion_rate) OVER (ORDER BY metric_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS ma7_completion_rate, AVG(day_seq) OVER (ORDER BY metric_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS avg_x FROM numbered_data), moving_avg AS (SELECT *, LAG(ma7_efficiency, 1) OVER (ORDER BY metric_date) AS prev_ma7_efficiency, LAG(ma7_output, 1) OVER (ORDER BY metric_date) AS prev_ma7_output, LAG(ma7_defect_rate, 1) OVER (ORDER BY metric_date) AS prev_ma7_defect_rate FROM moving_avg_step1), slope_calc AS (SELECT *, (ma7_efficiency - LAG(ma7_efficiency, 3) OVER (ORDER BY metric_date)) / 3.0 AS slope_efficiency, (ma7_output - LAG(ma7_output, 3) OVER (ORDER BY metric_date)) / 3.0 AS slope_output, (ma7_defect_rate - LAG(ma7_defect_rate, 3) OVER (ORDER BY metric_date)) / 3.0 AS slope_defect FROM moving_avg), trend_analysis AS (SELECT metric_date, hourly_efficiency, total_output, defect_rate, completion_rate, ROUND(ma7_efficiency, 2) AS ma7_efficiency, ROUND(ma7_output, 2) AS ma7_output, ROUND(ma7_defect_rate, 2) AS ma7_defect_rate, ROUND(COALESCE(slope_efficiency, 0), 4) AS slope_efficiency, ROUND(COALESCE(slope_output, 0), 4) AS slope_output, ROUND(COALESCE(slope_defect, 0), 4) AS slope_defect, prev_ma7_efficiency, prev_ma7_output, prev_ma7_defect_rate, CASE WHEN COALESCE(slope_efficiency, 0) > 0.3 THEN 'RISING' WHEN COALESCE(slope_efficiency, 0) < -0.3 THEN 'FALLING' ELSE 'STABLE' END AS efficiency_trend, CASE WHEN COALESCE(slope_output, 0) > 3 THEN 'RISING' WHEN COALESCE(slope_output, 0) < -3 THEN 'FALLING' ELSE 'STABLE' END AS output_trend, CASE WHEN COALESCE(slope_defect, 0) > 0.3 THEN 'RISING' WHEN COALESCE(slope_defect, 0) < -0.3 THEN 'FALLING' ELSE 'STABLE' END AS defect_trend, CASE WHEN ma7_efficiency > 0 AND ABS(hourly_efficiency - ma7_efficiency) > ma7_efficiency * 0.3 THEN 'ANOMALY' ELSE 'NORMAL' END AS efficiency_status, CASE WHEN ma7_output > 0 AND ABS(total_output - ma7_output) > ma7_output * 0.3 THEN 'ANOMALY' ELSE 'NORMAL' END AS output_status, CASE WHEN defect_rate > ma7_defect_rate * 1.5 AND defect_rate > 5 THEN 'ANOMALY' ELSE 'NORMAL' END AS defect_status FROM slope_calc) SELECT metric_date, ROUND(hourly_efficiency, 2) AS hourly_output, ma7_efficiency AS efficiency_ma7, slope_efficiency, efficiency_trend, efficiency_status, CASE WHEN prev_ma7_efficiency IS NOT NULL AND ma7_efficiency > prev_ma7_efficiency AND slope_efficiency < 0 THEN 'TURNING_POINT' WHEN prev_ma7_efficiency IS NOT NULL AND ma7_efficiency < prev_ma7_efficiency AND slope_efficiency > 0 THEN 'TURNING_POINT' ELSE 'NONE' END AS efficiency_turning_point, ROUND(total_output, 0) AS daily_output, ma7_output AS output_ma7, slope_output, output_trend, output_status, CASE WHEN prev_ma7_output IS NOT NULL AND ma7_output > prev_ma7_output AND slope_output < 0 THEN 'TURNING_POINT' WHEN prev_ma7_output IS NOT NULL AND ma7_output < prev_ma7_output AND slope_output > 0 THEN 'TURNING_POINT' ELSE 'NONE' END AS output_turning_point, ROUND(defect_rate, 2) AS defect_rate, ma7_defect_rate AS defect_rate_ma7, slope_defect, defect_trend, defect_status, CASE WHEN prev_ma7_defect_rate IS NOT NULL AND ma7_defect_rate > prev_ma7_defect_rate AND slope_defect < 0 THEN 'TURNING_POINT' WHEN prev_ma7_defect_rate IS NOT NULL AND ma7_defect_rate < prev_ma7_defect_rate AND slope_defect > 0 THEN 'TURNING_POINT' ELSE 'NONE' END AS defect_turning_point FROM trend_analysis ORDER BY metric_date DESC", + "parameters": {}, + "testParams": {} +} +2026-01-08 10:02:35 - httpx - INFO - [_client.py:1025] - HTTP Request: POST http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema "HTTP/1.1 200 " +2026-01-08 10:02:35 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:154] - 测试SQL API调用成功 +2026-01-08 10:02:35 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功 +2026-01-08 10:22:23 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type CallToolRequest +2026-01-08 10:22:23 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: OrderDelayWarningAnalysis +2026-01-08 10:22:23 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API... +2026-01-08 10:22:23 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:138] - 正在调用测试SQL API: http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema +2026-01-08 10:22:23 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:139] - 请求参数: { + "datasourceId": "19", + "businessName": "OrderDelayWarningAnalysis", + "businessDescription": "订单延迟预警分析:依据历史订单的生产周期、物流延误、设备故障等特征,输出延迟概率与红/黄/绿预警等级", + "sqlTemplate": "WITH production_cycle_stats AS (SELECT COALESCE(AVG(GREATEST(0, EXTRACT(DAY FROM last_updated_utc - event_time_utc))), 0) AS avg_production_days FROM fact_work_order WHERE status = 'CLOSED' AND last_updated_utc >= event_time_utc), logistics_delay_stats AS (SELECT customer_id, AVG(GREATEST(0, EXTRACT(DAY FROM event_time_utc - doc_date_utc))) AS avg_logistics_delay_days, SUM(CASE WHEN EXTRACT(DAY FROM event_time_utc - doc_date_utc) > 3 THEN 1 ELSE 0 END) AS delay_count FROM fact_sales_shipment WHERE doc_date_utc IS NOT NULL AND event_time_utc IS NOT NULL GROUP BY customer_id), quality_issue_stats AS (SELECT COALESCE(ROUND(SUM(fail_qty) * 100.0 / NULLIF(SUM(pass_qty + fail_qty), 0), 2), 0) AS defect_rate_pct FROM fact_quality_inspection WHERE pass_qty IS NOT NULL AND fail_qty IS NOT NULL), scrap_stats AS (SELECT COALESCE((SELECT COUNT(*) FROM fact_scrap) * 100.0 / NULLIF((SELECT COUNT(*) FROM fact_work_order WHERE status = 'CLOSED'), 0), 0) AS scrap_rate_pct), active_work_order_risk AS (SELECT COUNT(*) AS active_wo_count, COALESCE(SUM(CASE WHEN planned_qty > 0 AND (completed_qty / planned_qty) < 0.3 AND EXTRACT(DAY FROM NOW() - event_time_utc) > 7 THEN 1 ELSE 0 END), 0) AS lagging_wo_count FROM fact_work_order WHERE status IN ('OPEN', 'STARTED')), global_metrics AS (SELECT pcs.avg_production_days, qis.defect_rate_pct, ss.scrap_rate_pct, awr.active_wo_count, awr.lagging_wo_count FROM production_cycle_stats pcs, quality_issue_stats qis, scrap_stats ss, active_work_order_risk awr) SELECT so.sales_order_number AS order_number, c.customer_name, so.order_date_utc::DATE AS order_date, so.deal_amount AS order_amount, so.payment_status, ROUND(gm.avg_production_days::NUMERIC, 1) AS avg_production_days, ROUND(COALESCE(lds.avg_logistics_delay_days, 0)::NUMERIC, 1) AS avg_logistics_delay_days, COALESCE(lds.delay_count, 0)::INT AS historical_delay_count, ROUND(gm.defect_rate_pct::NUMERIC, 2) AS defect_rate_pct, ROUND(gm.scrap_rate_pct::NUMERIC, 2) AS scrap_rate_pct, gm.active_wo_count::INT AS active_work_order_count, gm.lagging_wo_count::INT AS lagging_work_order_count, ROUND(LEAST(100, GREATEST(0, LEAST(25, GREATEST(0, gm.avg_production_days - 10) * 2.5) + LEAST(30, COALESCE(lds.avg_logistics_delay_days, 0) * 6) + LEAST(25, gm.defect_rate_pct * 2.5) + LEAST(20, gm.lagging_wo_count * 10)))::NUMERIC, 1) AS delay_probability_pct, CASE WHEN (LEAST(25, GREATEST(0, gm.avg_production_days - 10) * 2.5) + LEAST(30, COALESCE(lds.avg_logistics_delay_days, 0) * 6) + LEAST(25, gm.defect_rate_pct * 2.5) + LEAST(20, gm.lagging_wo_count * 10)) >= 60 THEN 'RED' WHEN (LEAST(25, GREATEST(0, gm.avg_production_days - 10) * 2.5) + LEAST(30, COALESCE(lds.avg_logistics_delay_days, 0) * 6) + LEAST(25, gm.defect_rate_pct * 2.5) + LEAST(20, gm.lagging_wo_count * 10)) >= 30 THEN 'YELLOW' ELSE 'GREEN' END AS warning_level, CASE WHEN gm.lagging_wo_count >= 2 THEN 'PRODUCTION_SEVERELY_DELAYED' WHEN COALESCE(lds.avg_logistics_delay_days, 0) > 5 THEN 'HIGH_LOGISTICS_DELAY_RISK' WHEN gm.avg_production_days > 15 THEN 'LONG_PRODUCTION_CYCLE' WHEN gm.defect_rate_pct > 10 THEN 'QUALITY_ISSUES' ELSE 'NORMAL' END AS primary_risk_factor FROM fact_sales_order so LEFT JOIN dim_customer c ON so.customer_id = c.customer_id AND c.is_current = 't' CROSS JOIN global_metrics gm LEFT JOIN logistics_delay_stats lds ON so.customer_id = lds.customer_id ORDER BY delay_probability_pct DESC, so.order_date_utc DESC", + "parameters": {}, + "testParams": {} +} +2026-01-08 10:22:24 - httpx - INFO - [_client.py:1025] - HTTP Request: POST http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema "HTTP/1.1 200 " +2026-01-08 10:22:24 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:154] - 测试SQL API调用成功 +2026-01-08 10:22:24 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功 +2026-01-08 10:57:20 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type CallToolRequest +2026-01-08 10:57:20 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: WorkOrderProgressAndAnomalyNodes +2026-01-08 10:57:20 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API... +2026-01-08 10:57:20 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:138] - 正在调用测试SQL API: http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema +2026-01-08 10:57:20 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:139] - 请求参数: { + "datasourceId": "19", + "businessName": "WorkOrderProgressAndAnomalyNodes", + "businessDescription": "工单执行进度与异常节点:实时拉取工单数据,动态映射订单各环节状态(OPEN→PENDING, STARTED→IN_PROGRESS, CLOSED→COMPLETED),呈现执行进度与异常节点", + "sqlTemplate": "WITH work_order_base AS (SELECT wo.work_order_id, wo.work_order_number, wo.product_id, wo.status, wo.planned_qty, wo.completed_qty, wo.event_time_utc::timestamp AS start_time, wo.last_updated_utc::timestamp AS last_update, wo.source_system FROM fact_work_order wo), product_info AS (SELECT product_id, product_name, product_category FROM dim_product WHERE is_current = 't'), labor_summary AS (SELECT work_order_number, COUNT(DISTINCT worker_name) AS worker_count, SUM(report_qty) AS total_report_qty, SUM(duration_minutes) AS total_minutes, MAX(event_time_utc::timestamp) AS last_report_time FROM fact_labor_report GROUP BY work_order_number), quality_summary AS (SELECT work_order_number, SUM(pass_qty) AS pass_qty, SUM(fail_qty) AS fail_qty FROM fact_quality_inspection GROUP BY work_order_number), work_order_progress AS (SELECT wb.work_order_id, wb.work_order_number, p.product_name, p.product_category, wb.status AS raw_status, CASE wb.status WHEN 'OPEN' THEN 'PENDING' WHEN 'STARTED' THEN 'IN_PROGRESS' WHEN 'CLOSED' THEN 'COMPLETED' ELSE 'UNKNOWN' END AS status_name, wb.planned_qty, wb.completed_qty, CASE WHEN wb.planned_qty > 0 THEN ROUND(wb.completed_qty * 100.0 / wb.planned_qty, 1) ELSE 0 END AS completion_rate, GREATEST(wb.planned_qty - wb.completed_qty, 0) AS remaining_qty, wb.start_time, wb.last_update, ROUND(EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - wb.start_time)) / 3600, 1) AS elapsed_hours, COALESCE(ls.worker_count, 0) AS worker_count, COALESCE(ls.total_report_qty, 0) AS total_report_qty, COALESCE(ls.total_minutes, 0) AS total_work_minutes, ls.last_report_time, COALESCE(qs.pass_qty, 0) AS qc_pass_qty, COALESCE(qs.fail_qty, 0) AS qc_fail_qty FROM work_order_base wb LEFT JOIN product_info p ON wb.product_id = p.product_id LEFT JOIN labor_summary ls ON wb.work_order_number = ls.work_order_number LEFT JOIN quality_summary qs ON wb.work_order_number = qs.work_order_number), anomaly_detection AS (SELECT *, CASE WHEN raw_status = 'STARTED' AND elapsed_hours > 48 AND completion_rate < 20 THEN 'SEVERELY_DELAYED' WHEN raw_status = 'STARTED' AND elapsed_hours > 24 AND completion_rate < 30 THEN 'DELAYED' ELSE NULL END AS progress_anomaly, CASE WHEN qc_pass_qty + qc_fail_qty > 0 AND qc_fail_qty * 100.0 / (qc_pass_qty + qc_fail_qty) > 10 THEN 'QUALITY_ISSUE' ELSE NULL END AS quality_anomaly, CASE WHEN raw_status = 'STARTED' AND last_report_time IS NOT NULL AND EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - last_report_time)) / 3600 > 24 THEN 'LABOR_STALLED' WHEN raw_status = 'STARTED' AND last_report_time IS NULL AND elapsed_hours > 24 THEN 'NO_LABOR_RECORD' ELSE NULL END AS labor_anomaly, CASE WHEN total_work_minutes > 0 AND total_report_qty / (total_work_minutes / 60.0) < 5 THEN 'LOW_EFFICIENCY' ELSE NULL END AS efficiency_anomaly FROM work_order_progress) SELECT work_order_number, product_name, product_category, status_name, planned_qty, completed_qty, remaining_qty, completion_rate, worker_count, ROUND(total_work_minutes / 60.0, 1) AS total_work_hours, elapsed_hours, COALESCE(progress_anomaly, '') || CASE WHEN progress_anomaly IS NOT NULL AND quality_anomaly IS NOT NULL THEN ',' ELSE '' END || COALESCE(quality_anomaly, '') || CASE WHEN (progress_anomaly IS NOT NULL OR quality_anomaly IS NOT NULL) AND labor_anomaly IS NOT NULL THEN ',' ELSE '' END || COALESCE(labor_anomaly, '') || CASE WHEN (progress_anomaly IS NOT NULL OR quality_anomaly IS NOT NULL OR labor_anomaly IS NOT NULL) AND efficiency_anomaly IS NOT NULL THEN ',' ELSE '' END || COALESCE(efficiency_anomaly, '') AS anomaly_flags, CASE WHEN progress_anomaly = 'SEVERELY_DELAYED' OR quality_anomaly IS NOT NULL THEN 'HIGH' WHEN progress_anomaly = 'DELAYED' OR labor_anomaly IS NOT NULL THEN 'MEDIUM' WHEN efficiency_anomaly IS NOT NULL THEN 'LOW' ELSE 'NONE' END AS risk_level FROM anomaly_detection ORDER BY CASE raw_status WHEN 'STARTED' THEN 1 WHEN 'OPEN' THEN 2 ELSE 3 END, CASE WHEN progress_anomaly IS NOT NULL THEN 0 ELSE 1 END, completion_rate ASC", + "parameters": {}, + "testParams": {} +} +2026-01-08 10:57:20 - httpx - INFO - [_client.py:1025] - HTTP Request: POST http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema "HTTP/1.1 200 " +2026-01-08 10:57:20 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:154] - 测试SQL API调用成功 +2026-01-08 10:57:20 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功 +2026-01-08 11:05:47 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type CallToolRequest +2026-01-08 11:05:47 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: SupplyChainRiskWarning +2026-01-08 11:05:47 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API... +2026-01-08 11:05:47 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:138] - 正在调用测试SQL API: http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema +2026-01-08 11:05:47 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:139] - 请求参数: { + "datasourceId": "19", + "businessName": "SupplyChainRiskWarning", + "businessDescription": "供应链风险预警:整合采购系统中的供应商历史交期与质检合格率,结合外采物流数据,识别交期异常+物流停滞组合风险模式,自动推送高风险订单提示", + "sqlTemplate": "WITH supplier_delivery AS (SELECT po.supplier_id, COUNT(*) AS order_count, COUNT(pr.purchase_receipt_id) AS receipt_count, AVG(CASE WHEN pr.doc_date_utc IS NOT NULL AND po.doc_date_utc IS NOT NULL THEN EXTRACT(DAY FROM pr.doc_date_utc::timestamp - po.doc_date_utc::timestamp) ELSE NULL END) AS avg_delivery_days, MAX(CASE WHEN pr.doc_date_utc IS NOT NULL AND po.doc_date_utc IS NOT NULL THEN EXTRACT(DAY FROM pr.doc_date_utc::timestamp - po.doc_date_utc::timestamp) ELSE NULL END) AS max_delivery_days, STDDEV(CASE WHEN pr.doc_date_utc IS NOT NULL AND po.doc_date_utc IS NOT NULL THEN EXTRACT(DAY FROM pr.doc_date_utc::timestamp - po.doc_date_utc::timestamp) ELSE NULL END) AS stddev_delivery_days FROM fact_purchase_order po LEFT JOIN fact_purchase_receipt pr ON po.supplier_id = pr.supplier_id GROUP BY po.supplier_id), supplier_quality AS (SELECT pr.supplier_id, COUNT(pr.purchase_receipt_id) AS total_receipts, SUM(pr.receipt_qty_total) AS total_qty, SUM(pr.amount) AS total_amount FROM fact_purchase_receipt pr GROUP BY pr.supplier_id), supplier_returns AS (SELECT pret.supplier_id, COUNT(*) AS return_count, SUM(CASE WHEN pret.return_reason = '损坏' THEN 1 ELSE 0 END) AS damage_count FROM fact_purchase_return pret GROUP BY pret.supplier_id), supplier_quality_rate AS (SELECT sq.supplier_id, sq.total_receipts, sq.total_qty, sq.total_amount, COALESCE(sr.return_count, 0) AS return_count, COALESCE(sr.damage_count, 0) AS damage_count, CASE WHEN sq.total_receipts > 0 THEN (sq.total_receipts - COALESCE(sr.return_count, 0)) * 100.0 / sq.total_receipts ELSE 100 END AS quality_rate FROM supplier_quality sq LEFT JOIN supplier_returns sr ON sq.supplier_id = sr.supplier_id), supplier_risk AS (SELECT s.supplier_id, s.supplier_name, s.supplier_category, COALESCE(sd.order_count, 0) AS order_count, COALESCE(sd.receipt_count, 0) AS receipt_count, ROUND(COALESCE(sd.avg_delivery_days, 0), 1) AS avg_delivery_days, ROUND(COALESCE(sd.max_delivery_days, 0), 1) AS max_delivery_days, ROUND(COALESCE(sd.stddev_delivery_days, 0), 1) AS delivery_volatility, COALESCE(sqr.total_receipts, 0) AS total_receipts, COALESCE(sqr.return_count, 0) AS return_count, ROUND(COALESCE(sqr.quality_rate, 100), 1) AS quality_rate, CASE WHEN COALESCE(sd.avg_delivery_days, 0) > 60 THEN 40 WHEN COALESCE(sd.avg_delivery_days, 0) > 45 THEN 30 WHEN COALESCE(sd.avg_delivery_days, 0) > 30 THEN 20 ELSE 10 END + CASE WHEN COALESCE(sd.stddev_delivery_days, 0) > 20 THEN 30 WHEN COALESCE(sd.stddev_delivery_days, 0) > 10 THEN 20 ELSE 10 END AS delivery_risk_score, CASE WHEN COALESCE(sqr.quality_rate, 100) < 80 THEN 50 WHEN COALESCE(sqr.quality_rate, 100) < 90 THEN 30 WHEN COALESCE(sqr.quality_rate, 100) < 95 THEN 15 ELSE 5 END AS quality_risk_score FROM dim_supplier s LEFT JOIN supplier_delivery sd ON s.supplier_id = sd.supplier_id LEFT JOIN supplier_quality_rate sqr ON s.supplier_id = sqr.supplier_id WHERE s.is_current = 't'), supplier_risk_level AS (SELECT *, delivery_risk_score + quality_risk_score AS total_risk_score, CASE WHEN delivery_risk_score + quality_risk_score >= 80 THEN 'HIGH' WHEN delivery_risk_score + quality_risk_score >= 50 THEN 'MEDIUM' ELSE 'LOW' END AS risk_level, CASE WHEN delivery_risk_score >= 50 AND quality_risk_score >= 30 THEN 'DELIVERY_AND_QUALITY' WHEN delivery_risk_score >= 50 THEN 'DELIVERY_ISSUE' WHEN quality_risk_score >= 30 THEN 'QUALITY_ISSUE' ELSE 'NORMAL' END AS risk_pattern FROM supplier_risk) SELECT supplier_name, supplier_category, order_count, receipt_count, avg_delivery_days, max_delivery_days, delivery_volatility, total_receipts, return_count, quality_rate, delivery_risk_score, quality_risk_score, total_risk_score, risk_level, risk_pattern FROM supplier_risk_level ORDER BY total_risk_score DESC, supplier_name", + "parameters": {}, + "testParams": {} +} +2026-01-08 11:05:48 - httpx - INFO - [_client.py:1025] - HTTP Request: POST http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema "HTTP/1.1 200 " +2026-01-08 11:05:48 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:154] - 测试SQL API调用成功 +2026-01-08 11:05:48 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功 +2026-01-08 11:13:21 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type CallToolRequest +2026-01-08 11:13:21 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: EfficiencyOutputLossDashboard +2026-01-08 11:13:21 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API... +2026-01-08 11:13:21 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:138] - 正在调用测试SQL API: http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema +2026-01-08 11:13:21 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:139] - 请求参数: { + "datasourceId": "19", + "businessName": "EfficiencyOutputLossDashboard", + "businessDescription": "人效-产值-损耗三维模型仪表盘:关联订单量×工时×人员数×成本,构建人效—产值—损耗三维模型,按部门(产品类别)汇总展示", + "sqlTemplate": "WITH labor_stats AS (SELECT p.product_category AS department, COUNT(DISTINCT lr.worker_name) AS worker_count, SUM(lr.duration_minutes) AS total_work_minutes, SUM(lr.report_qty) AS total_output_qty, COUNT(DISTINCT lr.work_order_number) AS work_order_count FROM fact_labor_report lr INNER JOIN dim_product p ON lr.product_id = p.product_id AND p.is_current = 't' GROUP BY p.product_category), work_order_stats AS (SELECT p.product_category AS department, COUNT(*) AS total_work_orders, SUM(wo.planned_qty) AS total_planned_qty, SUM(wo.completed_qty) AS total_completed_qty, SUM(CASE WHEN wo.status = 'CLOSED' THEN 1 ELSE 0 END) AS closed_orders, SUM(CASE WHEN wo.status = 'STARTED' THEN 1 ELSE 0 END) AS started_orders, SUM(CASE WHEN wo.status = 'OPEN' THEN 1 ELSE 0 END) AS open_orders FROM fact_work_order wo INNER JOIN dim_product p ON wo.product_id = p.product_id AND p.is_current = 't' GROUP BY p.product_category), quality_stats AS (SELECT p.product_category AS department, SUM(qi.pass_qty) AS total_pass_qty, SUM(qi.fail_qty) AS total_fail_qty, COUNT(*) AS inspection_count FROM fact_quality_inspection qi INNER JOIN dim_product p ON qi.product_id = p.product_id AND p.is_current = 't' GROUP BY p.product_category), sales_stats AS (SELECT AVG(deal_amount) AS avg_order_amount, SUM(deal_amount) AS total_sales_amount, COUNT(*) AS order_count FROM fact_sales_order), scrap_stats AS (SELECT COUNT(*) AS total_scrap_count FROM fact_scrap) SELECT ls.department, ls.worker_count, ROUND(ls.total_work_minutes / 60.0, 2) AS total_work_hours, ls.total_output_qty, ROUND(ls.total_output_qty / NULLIF(ls.worker_count, 0), 2) AS output_per_worker, ROUND(ls.total_output_qty / NULLIF(ls.total_work_minutes / 60.0, 0), 2) AS output_per_hour, ROUND(ls.total_output_qty / NULLIF(ls.worker_count, 0) / NULLIF(ls.total_work_minutes / 60.0 / ls.worker_count, 0), 2) AS efficiency_index, ws.total_planned_qty AS planned_qty, ws.total_completed_qty AS completed_qty, ROUND(ws.total_completed_qty / NULLIF(ws.total_planned_qty, 0) * 100, 2) AS plan_completion_rate, ROUND(ws.total_completed_qty * ss.avg_order_amount / 100, 2) AS estimated_output_value, ROUND(ws.total_completed_qty * ss.avg_order_amount / 100 / NULLIF(ls.worker_count, 0), 2) AS output_value_per_worker, qs.total_pass_qty AS qc_pass_qty, qs.total_fail_qty AS qc_fail_qty, ROUND(qs.total_fail_qty / NULLIF(qs.total_pass_qty + qs.total_fail_qty, 0) * 100, 2) AS defect_rate, ROUND((ws.total_planned_qty - ws.total_completed_qty) / NULLIF(ws.total_planned_qty, 0) * 100, 2) AS production_loss_rate, ROUND((ls.total_output_qty / NULLIF(ls.worker_count, 0) / NULLIF(ls.total_work_minutes / 60.0 / ls.worker_count, 0)) * 0.4 + (ws.total_completed_qty / NULLIF(ws.total_planned_qty, 0) * 100) * 0.35 + (qs.total_pass_qty / NULLIF(qs.total_pass_qty + qs.total_fail_qty, 0) * 100) * 0.25, 2) AS performance_score, CASE WHEN (qs.total_fail_qty / NULLIF(qs.total_pass_qty + qs.total_fail_qty, 0) * 100) >= 10 OR (ws.total_completed_qty / NULLIF(ws.total_planned_qty, 0) * 100) < 50 THEN 'RED' WHEN (qs.total_fail_qty / NULLIF(qs.total_pass_qty + qs.total_fail_qty, 0) * 100) >= 5 OR (ws.total_completed_qty / NULLIF(ws.total_planned_qty, 0) * 100) < 70 THEN 'YELLOW' ELSE 'GREEN' END AS warning_level FROM labor_stats ls LEFT JOIN work_order_stats ws ON ls.department = ws.department LEFT JOIN quality_stats qs ON ls.department = qs.department CROSS JOIN sales_stats ss CROSS JOIN scrap_stats scr ORDER BY performance_score DESC", + "parameters": {}, + "testParams": {} +} +2026-01-08 11:13:22 - httpx - INFO - [_client.py:1025] - HTTP Request: POST http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema "HTTP/1.1 200 " +2026-01-08 11:13:22 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:154] - 测试SQL API调用成功 +2026-01-08 11:13:22 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功 +2026-01-08 11:34:55 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type CallToolRequest +2026-01-08 11:34:55 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: OnePageDecisionBrief +2026-01-08 11:34:55 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API... +2026-01-08 11:34:55 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:138] - 正在调用测试SQL API: http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema +2026-01-08 11:34:55 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:139] - 请求参数: { + "datasourceId": "19", + "businessName": "OnePageDecisionBrief", + "businessDescription": "一页式决策简报:自动聚合订单、生产、财务、售后等关键数据,生成经营决策简报", + "sqlTemplate": "WITH sales_summary AS (SELECT COUNT(*) AS total_orders, SUM(deal_amount) AS total_sales_amount, AVG(deal_amount) AS avg_order_amount, SUM(CASE WHEN payment_status = 'PAID' THEN 1 ELSE 0 END) AS paid_orders, SUM(CASE WHEN payment_status = 'PARTIAL' THEN 1 ELSE 0 END) AS partial_orders, SUM(CASE WHEN payment_status = 'UNPAID' THEN 1 ELSE 0 END) AS unpaid_orders, SUM(CASE WHEN payment_status = 'PAID' THEN deal_amount ELSE 0 END) AS paid_amount, SUM(CASE WHEN payment_status = 'UNPAID' THEN deal_amount ELSE 0 END) AS unpaid_amount FROM fact_sales_order), production_summary AS (SELECT COUNT(*) AS total_work_orders, SUM(planned_qty) AS total_planned_qty, SUM(completed_qty) AS total_completed_qty, SUM(CASE WHEN status = 'CLOSED' THEN 1 ELSE 0 END) AS closed_orders, SUM(CASE WHEN status = 'STARTED' THEN 1 ELSE 0 END) AS started_orders, SUM(CASE WHEN status = 'OPEN' THEN 1 ELSE 0 END) AS open_orders FROM fact_work_order), ar_summary AS (SELECT COUNT(*) AS receipt_count, SUM(amount) AS total_receipt_amount, AVG(amount) AS avg_receipt_amount FROM fact_ar_receipt), ap_summary AS (SELECT COUNT(*) AS payment_count, SUM(amount) AS total_payment_amount, AVG(amount) AS avg_payment_amount FROM fact_ap_payment), invoice_summary AS (SELECT COUNT(*) AS invoice_count, SUM(invoice_amount) AS total_invoice_amount, AVG(invoice_amount) AS avg_invoice_amount FROM fact_invoice), return_summary AS (SELECT COUNT(*) AS return_count, SUM(amount) AS total_return_amount, AVG(amount) AS avg_return_amount FROM fact_sales_return), shipment_summary AS (SELECT COUNT(*) AS shipment_count, SUM(amount) AS total_shipment_amount, AVG(amount) AS avg_shipment_amount FROM fact_sales_shipment), purchase_summary AS (SELECT COUNT(*) AS purchase_order_count FROM fact_purchase_order), quality_summary AS (SELECT COUNT(*) AS inspection_count, SUM(pass_qty) AS total_pass_qty, SUM(fail_qty) AS total_fail_qty FROM fact_quality_inspection), labor_summary AS (SELECT COUNT(DISTINCT worker_name) AS worker_count, SUM(duration_minutes) AS total_work_minutes, SUM(report_qty) AS total_output_qty FROM fact_labor_report), scrap_summary AS (SELECT COUNT(*) AS scrap_count FROM fact_scrap) SELECT 'Decision Brief' AS report_title, CURRENT_DATE AS report_date, ss.total_orders AS sales_order_count, ROUND(ss.total_sales_amount, 2) AS total_sales_amount, ROUND(ss.avg_order_amount, 2) AS avg_order_amount, ss.paid_orders AS paid_order_count, ss.partial_orders AS partial_paid_count, ss.unpaid_orders AS unpaid_order_count, ROUND(ss.paid_orders * 100.0 / NULLIF(ss.total_orders, 0), 1) AS payment_completion_rate, ROUND(ss.unpaid_amount, 2) AS receivable_amount, ps.total_work_orders AS work_order_count, ps.closed_orders AS completed_work_orders, ps.started_orders AS in_progress_work_orders, ps.open_orders AS pending_work_orders, ROUND(ps.total_planned_qty, 0) AS planned_qty, ROUND(ps.total_completed_qty, 0) AS completed_qty, ROUND(ps.total_completed_qty * 100.0 / NULLIF(ps.total_planned_qty, 0), 1) AS production_completion_rate, ls.worker_count AS active_worker_count, ROUND(ls.total_work_minutes / 60.0, 1) AS total_work_hours, ROUND(ls.total_output_qty, 0) AS total_output, ROUND(ls.total_output_qty / NULLIF(ls.worker_count, 0), 1) AS output_per_worker, ROUND(ls.total_output_qty / NULLIF(ls.total_work_minutes / 60.0, 0), 2) AS output_per_hour, qs.inspection_count AS qc_batch_count, ROUND(qs.total_pass_qty, 0) AS pass_qty, ROUND(qs.total_fail_qty, 0) AS fail_qty, ROUND(qs.total_pass_qty * 100.0 / NULLIF(qs.total_pass_qty + qs.total_fail_qty, 0), 2) AS pass_rate, ROUND(qs.total_fail_qty * 100.0 / NULLIF(qs.total_pass_qty + qs.total_fail_qty, 0), 2) AS defect_rate, scr.scrap_count AS scrap_record_count, ar.receipt_count AS ar_receipt_count, ROUND(ar.total_receipt_amount, 2) AS total_ar_amount, ap.payment_count AS ap_payment_count, ROUND(ap.total_payment_amount, 2) AS total_ap_amount, ROUND(ar.total_receipt_amount - ap.total_payment_amount, 2) AS net_cash_flow, inv.invoice_count, ROUND(inv.total_invoice_amount, 2) AS total_invoice_amount, sh.shipment_count, ROUND(sh.total_shipment_amount, 2) AS total_shipment_amount, pur.purchase_order_count, ret.return_count, ROUND(ret.total_return_amount, 2) AS total_return_amount, ROUND(ret.total_return_amount * 100.0 / NULLIF(ss.total_sales_amount, 0), 2) AS return_rate, CASE WHEN (qs.total_pass_qty * 100.0 / NULLIF(qs.total_pass_qty + qs.total_fail_qty, 0)) >= 95 AND (ps.total_completed_qty * 100.0 / NULLIF(ps.total_planned_qty, 0)) >= 80 AND (ret.total_return_amount * 100.0 / NULLIF(ss.total_sales_amount, 0)) < 5 THEN 'EXCELLENT' WHEN (qs.total_pass_qty * 100.0 / NULLIF(qs.total_pass_qty + qs.total_fail_qty, 0)) >= 90 AND (ps.total_completed_qty * 100.0 / NULLIF(ps.total_planned_qty, 0)) >= 60 AND (ret.total_return_amount * 100.0 / NULLIF(ss.total_sales_amount, 0)) < 10 THEN 'GOOD' ELSE 'WARNING' END AS health_status FROM sales_summary ss CROSS JOIN production_summary ps CROSS JOIN ar_summary ar CROSS JOIN ap_summary ap CROSS JOIN invoice_summary inv CROSS JOIN return_summary ret CROSS JOIN shipment_summary sh CROSS JOIN purchase_summary pur CROSS JOIN quality_summary qs CROSS JOIN labor_summary ls CROSS JOIN scrap_summary scr", + "parameters": {}, + "testParams": {} +} +2026-01-08 11:34:56 - httpx - INFO - [_client.py:1025] - HTTP Request: POST http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema "HTTP/1.1 200 " +2026-01-08 11:34:56 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:154] - 测试SQL API调用成功 +2026-01-08 11:34:56 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功 +2026-01-08 11:59:37 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type CallToolRequest +2026-01-08 11:59:37 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: MetricTrendAndTurningPointWarning +2026-01-08 11:59:37 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API... +2026-01-08 11:59:37 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:138] - 正在调用测试SQL API: http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema +2026-01-08 11:59:37 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:139] - 请求参数: { + "datasourceId": "19", + "businessName": "MetricTrendAndTurningPointWarning", + "businessDescription": "指标趋势分析与拐点预警:基于移动平均与线性回归分析人效、产量、废品率趋势,输出上升/下降/平稳判断与拐点预警", + "sqlTemplate": "WITH daily_metrics AS (SELECT DATE(lr.event_time_utc::timestamp) AS metric_date, COUNT(DISTINCT lr.worker_name) AS worker_count, SUM(lr.duration_minutes) / 60.0 AS total_hours, SUM(lr.report_qty) AS total_output, CASE WHEN SUM(lr.duration_minutes) > 0 THEN SUM(lr.report_qty) / (SUM(lr.duration_minutes) / 60.0) ELSE 0 END AS hourly_efficiency FROM fact_labor_report lr GROUP BY DATE(lr.event_time_utc::timestamp)), daily_quality AS (SELECT DATE(qi.event_time_utc::timestamp) AS metric_date, SUM(qi.pass_qty) AS pass_qty, SUM(qi.fail_qty) AS fail_qty, CASE WHEN SUM(qi.pass_qty) + SUM(qi.fail_qty) > 0 THEN SUM(qi.fail_qty) * 100.0 / (SUM(qi.pass_qty) + SUM(qi.fail_qty)) ELSE 0 END AS defect_rate FROM fact_quality_inspection qi GROUP BY DATE(qi.event_time_utc::timestamp)), daily_production AS (SELECT DATE(wo.event_time_utc::timestamp) AS metric_date, SUM(wo.planned_qty) AS planned_qty, SUM(wo.completed_qty) AS completed_qty, CASE WHEN SUM(wo.planned_qty) > 0 THEN SUM(wo.completed_qty) * 100.0 / SUM(wo.planned_qty) ELSE 0 END AS completion_rate FROM fact_work_order wo GROUP BY DATE(wo.event_time_utc::timestamp)), combined_daily AS (SELECT COALESCE(dm.metric_date, dq.metric_date, dp.metric_date) AS metric_date, COALESCE(dm.worker_count, 0) AS worker_count, COALESCE(dm.total_hours, 0) AS total_hours, COALESCE(dm.total_output, 0) AS total_output, COALESCE(dm.hourly_efficiency, 0) AS hourly_efficiency, COALESCE(dq.defect_rate, 0) AS defect_rate, COALESCE(dp.completion_rate, 0) AS completion_rate FROM daily_metrics dm FULL OUTER JOIN daily_quality dq ON dm.metric_date = dq.metric_date FULL OUTER JOIN daily_production dp ON dm.metric_date = dp.metric_date WHERE COALESCE(dm.metric_date, dq.metric_date, dp.metric_date) IS NOT NULL), numbered_data AS (SELECT *, ROW_NUMBER() OVER (ORDER BY metric_date) AS day_seq FROM combined_daily), moving_avg_step1 AS (SELECT metric_date, day_seq, worker_count, total_output, hourly_efficiency, defect_rate, completion_rate, AVG(hourly_efficiency) OVER (ORDER BY metric_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS ma7_efficiency, AVG(total_output) OVER (ORDER BY metric_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS ma7_output, AVG(defect_rate) OVER (ORDER BY metric_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS ma7_defect_rate, AVG(completion_rate) OVER (ORDER BY metric_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS ma7_completion_rate, AVG(day_seq) OVER (ORDER BY metric_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS avg_x FROM numbered_data), moving_avg AS (SELECT *, LAG(ma7_efficiency, 1) OVER (ORDER BY metric_date) AS prev_ma7_efficiency, LAG(ma7_output, 1) OVER (ORDER BY metric_date) AS prev_ma7_output, LAG(ma7_defect_rate, 1) OVER (ORDER BY metric_date) AS prev_ma7_defect_rate FROM moving_avg_step1), slope_calc AS (SELECT *, (ma7_efficiency - LAG(ma7_efficiency, 3) OVER (ORDER BY metric_date)) / 3.0 AS slope_efficiency, (ma7_output - LAG(ma7_output, 3) OVER (ORDER BY metric_date)) / 3.0 AS slope_output, (ma7_defect_rate - LAG(ma7_defect_rate, 3) OVER (ORDER BY metric_date)) / 3.0 AS slope_defect FROM moving_avg), trend_analysis AS (SELECT metric_date, hourly_efficiency, total_output, defect_rate, completion_rate, ROUND(ma7_efficiency, 2) AS ma7_efficiency, ROUND(ma7_output, 2) AS ma7_output, ROUND(ma7_defect_rate, 2) AS ma7_defect_rate, ROUND(COALESCE(slope_efficiency, 0), 4) AS slope_efficiency, ROUND(COALESCE(slope_output, 0), 4) AS slope_output, ROUND(COALESCE(slope_defect, 0), 4) AS slope_defect, prev_ma7_efficiency, prev_ma7_output, prev_ma7_defect_rate, CASE WHEN COALESCE(slope_efficiency, 0) > 0.3 THEN 'RISING' WHEN COALESCE(slope_efficiency, 0) < -0.3 THEN 'FALLING' ELSE 'STABLE' END AS efficiency_trend, CASE WHEN COALESCE(slope_output, 0) > 3 THEN 'RISING' WHEN COALESCE(slope_output, 0) < -3 THEN 'FALLING' ELSE 'STABLE' END AS output_trend, CASE WHEN COALESCE(slope_defect, 0) > 0.3 THEN 'RISING' WHEN COALESCE(slope_defect, 0) < -0.3 THEN 'FALLING' ELSE 'STABLE' END AS defect_trend, CASE WHEN ma7_efficiency > 0 AND ABS(hourly_efficiency - ma7_efficiency) > ma7_efficiency * 0.3 THEN 'ANOMALY' ELSE 'NORMAL' END AS efficiency_status, CASE WHEN ma7_output > 0 AND ABS(total_output - ma7_output) > ma7_output * 0.3 THEN 'ANOMALY' ELSE 'NORMAL' END AS output_status, CASE WHEN defect_rate > ma7_defect_rate * 1.5 AND defect_rate > 5 THEN 'ANOMALY' ELSE 'NORMAL' END AS defect_status FROM slope_calc) SELECT metric_date, ROUND(hourly_efficiency, 2) AS hourly_output, ma7_efficiency AS efficiency_ma7, slope_efficiency, efficiency_trend, efficiency_status, CASE WHEN prev_ma7_efficiency IS NOT NULL AND ma7_efficiency > prev_ma7_efficiency AND slope_efficiency < 0 THEN 'TURNING_POINT' WHEN prev_ma7_efficiency IS NOT NULL AND ma7_efficiency < prev_ma7_efficiency AND slope_efficiency > 0 THEN 'TURNING_POINT' ELSE 'NONE' END AS efficiency_turning_point, ROUND(total_output, 0) AS daily_output, ma7_output AS output_ma7, slope_output, output_trend, output_status, CASE WHEN prev_ma7_output IS NOT NULL AND ma7_output > prev_ma7_output AND slope_output < 0 THEN 'TURNING_POINT' WHEN prev_ma7_output IS NOT NULL AND ma7_output < prev_ma7_output AND slope_output > 0 THEN 'TURNING_POINT' ELSE 'NONE' END AS output_turning_point, ROUND(defect_rate, 2) AS defect_rate, ma7_defect_rate AS defect_rate_ma7, slope_defect, defect_trend, defect_status, CASE WHEN prev_ma7_defect_rate IS NOT NULL AND ma7_defect_rate > prev_ma7_defect_rate AND slope_defect < 0 THEN 'TURNING_POINT' WHEN prev_ma7_defect_rate IS NOT NULL AND ma7_defect_rate < prev_ma7_defect_rate AND slope_defect > 0 THEN 'TURNING_POINT' ELSE 'NONE' END AS defect_turning_point FROM trend_analysis ORDER BY metric_date DESC", + "parameters": {}, + "testParams": {} +} +2026-01-08 11:59:37 - httpx - INFO - [_client.py:1025] - HTTP Request: POST http://192.168.11.24:8088/datasource/sqlExecutionLog/testSqlWithSchema "HTTP/1.1 200 " +2026-01-08 11:59:37 - lzwcai_mcpskills_mfg_data_agent.utils.api_client - INFO - [api_client.py:154] - 测试SQL API调用成功 +2026-01-08 11:59:37 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功 diff --git a/lzwcai_mcpskills_mfg_data_agent/lzwcai_mcpskills_mfg_data_agent/logs/mcp_services.log b/lzwcai_mcpskills_mfg_data_agent/lzwcai_mcpskills_mfg_data_agent/logs/mcp_services.log index 5beacee..c00ee3f 100644 --- a/lzwcai_mcpskills_mfg_data_agent/lzwcai_mcpskills_mfg_data_agent/logs/mcp_services.log +++ b/lzwcai_mcpskills_mfg_data_agent/lzwcai_mcpskills_mfg_data_agent/logs/mcp_services.log @@ -113,3 +113,47 @@ 2026-01-08 00:56:21 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: OnePageDecisionBrief 2026-01-08 00:56:21 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API... 2026-01-08 00:56:21 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功 +2026-01-08 09:52:27 - mcp_services - INFO - [main.py:344] - MCP 服务器已关闭 +2026-01-08 10:00:42 - mcp_services - INFO - [main.py:362] - 开始运行 MCP SQL Executor 服务器 +2026-01-08 10:00:42 - mcp_services - INFO - [main.py:313] - ============================================================ +2026-01-08 10:00:42 - mcp_services - INFO - [main.py:314] - 正在启动 MCP 服务器: lzwcai-mcpskills-analyzeOrder +2026-01-08 10:00:42 - mcp_services - INFO - [main.py:315] - 版本: 0.1.0 +2026-01-08 10:00:42 - mcp_services - INFO - [main.py:316] - ============================================================ +2026-01-08 10:00:42 - mcp_services - INFO - [main.py:320] - 环境配置 - Database ID: 19 +2026-01-08 10:00:42 - mcp_services - INFO - [main.py:321] - 环境配置 - Datasource ID: 19 +2026-01-08 10:00:42 - mcp_services - INFO - [main.py:322] - 环境配置 - Skill ID: +2026-01-08 10:00:42 - mcp_services - INFO - [main.py:323] - 环境配置 - Backend Base URL: http://192.168.11.24:8088 +2026-01-08 10:00:42 - mcp_services - INFO - [main.py:324] - ============================================================ +2026-01-08 10:00:42 - mcp_services - INFO - [main.py:329] - MCP 服务器已启动,等待客户端连接... +2026-01-08 10:00:45 - mcp_services - INFO - [main.py:156] - 收到列出工具请求 +2026-01-08 10:00:45 - mcp_services - INFO - [main.py:119] - 初始化查询配置(数据源: local)... +2026-01-08 10:00:45 - mcp_services - INFO - [main.py:55] - 成功加载 6 个业务查询配置 +2026-01-08 10:00:45 - mcp_services - INFO - [main.py:123] - 本地配置: 6 条 +2026-01-08 10:00:45 - mcp_services - INFO - [main.py:165] - 成功生成 6 个 MCP 工具 +2026-01-08 10:02:28 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: OrderDelayWarningAnalysis +2026-01-08 10:02:28 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API... +2026-01-08 10:02:28 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功 +2026-01-08 10:02:30 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: WorkOrderProgressAndAnomalyNodes +2026-01-08 10:02:30 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API... +2026-01-08 10:02:30 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功 +2026-01-08 10:02:35 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: MetricTrendAndTurningPointWarning +2026-01-08 10:02:35 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API... +2026-01-08 10:02:35 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功 +2026-01-08 10:22:23 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: OrderDelayWarningAnalysis +2026-01-08 10:22:23 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API... +2026-01-08 10:22:24 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功 +2026-01-08 10:57:20 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: WorkOrderProgressAndAnomalyNodes +2026-01-08 10:57:20 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API... +2026-01-08 10:57:20 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功 +2026-01-08 11:05:47 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: SupplyChainRiskWarning +2026-01-08 11:05:47 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API... +2026-01-08 11:05:48 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功 +2026-01-08 11:13:21 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: EfficiencyOutputLossDashboard +2026-01-08 11:13:21 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API... +2026-01-08 11:13:22 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功 +2026-01-08 11:34:55 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: OnePageDecisionBrief +2026-01-08 11:34:55 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API... +2026-01-08 11:34:56 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功 +2026-01-08 11:59:37 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: MetricTrendAndTurningPointWarning +2026-01-08 11:59:37 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API... +2026-01-08 11:59:37 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功