diff --git a/backend/scripts/run_parallel_simulation.py b/backend/scripts/run_parallel_simulation.py index 2a627ff..35cc5e0 100644 --- a/backend/scripts/run_parallel_simulation.py +++ b/backend/scripts/run_parallel_simulation.py @@ -168,6 +168,23 @@ try: generate_twitter_agent_graph, generate_reddit_agent_graph ) + + # Patch camel tool schema for Groq compatibility. + # Groq rejects tool schemas where 'required' is present but 'properties' + # is missing (e.g. zero-parameter tools like do_nothing). + import camel.toolkits.function_tool as _ft + _original_get_openai_tool_schema = _ft.get_openai_tool_schema + + def _patched_get_openai_tool_schema(func): + schema = _original_get_openai_tool_schema(func) + params = schema.get("function", {}).get("parameters", {}) + if "required" in params and "properties" not in params: + params["properties"] = {} + if not params.get("properties") and "required" in params: + del params["required"] + return schema + + _ft.get_openai_tool_schema = _patched_get_openai_tool_schema except ImportError as e: print(f"错误: 缺少依赖 {e}") print("请先安装: pip install oasis-ai camel-ai") @@ -601,10 +618,51 @@ class ParallelIPCHandler: return True +def _fix_hour_array(val): + """Fix LLM-generated concatenated hour arrays. + + Some models produce [19202122] instead of [19,20,21,22] or + ["012345"] instead of [0,1,2,3,4,5]. Parse these back into + individual hour integers (0-23). + """ + if not isinstance(val, list) or len(val) != 1: + return val + item = val[0] + if isinstance(item, str): + return [int(ch) for ch in item if ch.isdigit()] + if isinstance(item, int) and item > 23: + s = str(item) + hours = [] + i = 0 + while i < len(s): + if i + 1 < len(s): + two_digit = int(s[i:i + 2]) + if 10 <= two_digit <= 23: + hours.append(two_digit) + i += 2 + continue + hours.append(int(s[i])) + i += 1 + return hours + return val + + def load_config(config_path: str) -> Dict[str, Any]: """加载配置文件""" with open(config_path, 'r', encoding='utf-8') as f: - return json.load(f) + config = json.load(f) + + # Fix malformed hour arrays from LLM config generation + time_config = config.get("time_config", {}) + for key in ("peak_hours", "off_peak_hours"): + if key in time_config: + time_config[key] = _fix_hour_array(time_config[key]) + + for agent_cfg in config.get("agent_configs", []): + if "active_hours" in agent_cfg: + agent_cfg["active_hours"] = _fix_hour_array(agent_cfg["active_hours"]) + + return config # 需要过滤掉的非核心动作类型(这些动作对分析价值较低)