diff --git a/README.md b/README.md index d57e24722f1b26970092e491311fe9efe180d817..9b4477d947354254bd70466cb44f67c1ff032861 100644 --- a/README.md +++ b/README.md @@ -138,7 +138,7 @@ EMQX Cloud 地址:`https://cloud.emqx.com/console/` - 操作系统 : Ubuntu 24.x 或更新版本 - 硬件 : 2 Core / 8 GB RAM (10+并发) - LLM :公网的大模型服务,如 GPT5.4、Qwen3.5-Plus 等 -- 网络 :需要确保该服务器可以连接到互联网(包括 MQTT Broker 和七牛云对象存储) +- 网络 :需要确保该服务器可以连接到互联网(包括 MQTT Broker 和对象存储) ### 4.1 准备执行器的配置文件 @@ -304,7 +304,7 @@ Agent 执行器的镜像保存在阿里云,您需要将其拉取到 `OC端`。 docker pull crpi-4auaoyyj6r36p6lb.cn-hangzhou.personal.cr.aliyuncs.com/huozige_lab/enterprise-agent-platform-oc-x64:{版本号} ``` -版本号请参考本方案的 release ,如 `20260424.02`。如需在 arm64 架构的服务器上部署,请发邮件联系:`will.ning@grapecity.com` +版本号请参考本方案的 release ,如 `20260429.02`。如需在 arm64 架构的服务器上部署,请发邮件联系:`will.ning@grapecity.com` 拉取完成后,您需要执行命令并启动名为 `enterprise-agent-platform-oc` 的容器。 @@ -437,8 +437,6 @@ Agent 执行器基于 `OpenClaw`,内置了以下常用组件(CLI 程序 / AP 在管理控制台上,设置 `claw` 应用的全局变量: -说明:这里的 `QINIU_*` 是门户应用现有字段命名;执行器容器内的文件传输配置改为 `env.json` 中的 `S3_*`。 - - MQTT_BROKER_HOST : 步骤2中记录的访问地址(去掉 schema 和端口,如 example.ala.cn-hangzhou.emqxsl.cn) - MQTT_BROKER_SCHEMA : 步骤2中记录的访问地址的 schema,如 `mqtts` 或 `mqtt` - MQTT_BROKER_PORT : 步骤2中记录的访问地址的端口 @@ -543,10 +541,10 @@ huozige-ontology-builder https://gitee.com/kadbbz_admin/hzg-ontology-builder-sam ### 6.4 技术限制 -截止活字格 `v11.0 Update 1` 版本,本方案存在以下技术限制: +截止活字格 `v12.0` 版本,本方案存在以下技术限制: - 无法读取活字格 App 存储在数据库的文件(含附件和图片),也无法上传文件到活字格 App。这将导致附件/图片类型的列,和附件/图片类型的参数在调用时被忽略 -- 无法直接启动或操作活字格构建的工作流 +- 无法调用活字格工程中引用的“外部服务端命令”。 ## 技术支持 diff --git a/platform_home/FILE-TRANSFER.md b/platform_home/FILE-TRANSFER.md index e5007805a32c527179e55cba4c1f1df00412bf55..f1525ad4bdf762fa6ad73aefe1303dd3e73a78de 100644 --- a/platform_home/FILE-TRANSFER.md +++ b/platform_home/FILE-TRANSFER.md @@ -29,7 +29,7 @@ 1. 先给我回复解释该文件的内容,包括文件中有什么、该如何使用等,然后再做上传操作。 2. 固定使用 bucket `agents-out`。 3. 先把文件复制到 Workspace 下的 `file_output` 目录。 -4. 复制后的文件名必须加时间戳前缀,例如 `123456_fname.bin`。如果原文件名已有时间戳前缀,只替换,不重复追加。 +4. 复制后的文件名必须加时间戳前缀,例如 `123456_fname.bin`。如果原文件名已有时间戳前缀,只替换,不重复追加。文件名中的空格,需要全部替换为 `_`。 5. 如果 bucket 不存在,先执行 `aws s3 ls "s3://agents-out" >/dev/null 2>&1 || aws s3 mb "s3://agents-out"`。 6. 用 `aws s3 cp "" "s3://agents-out/"` 上传。 7. 上传成功后,立即给我回复这个格式的内容:`file_output://agents-out/`,不要再添加任何其他内容。 diff --git a/platform_home/HZG-INVOKE.md b/platform_home/HZG-INVOKE.md index cc2a3072ce68d4014a4f3d21e2a965958a9c7d2c..8a36aaf2956a5cac46aaf469b01b32333074f952 100644 --- a/platform_home/HZG-INVOKE.md +++ b/platform_home/HZG-INVOKE.md @@ -132,6 +132,10 @@ JSON必须严格遵循这个结构,不允许增加或忽略任何一个节点 ## 必需约束 +### 黑名单 + +对于描述或备注中出现 `[HOB_EXCLUDE]` 字样的服务端命令,应忽略,不得调用。 + ### JSON 格式 - JSON 中的字符串值必须使用双引号 `"` diff --git a/test/docker_test_common.py b/test/docker_test_common.py index d3f09fb01d90c0d409d7736a2a16da1598019987..42ebc821ba705811f749693fe2e9f17c19959b60 100644 --- a/test/docker_test_common.py +++ b/test/docker_test_common.py @@ -300,6 +300,89 @@ def wait_for_gateway_ready(test_run: TestRun, name: str, container: str, timeout return timeout_result +def wait_for_managed_mqtt_ready( + test_run: TestRun, + name: str, + container: str, + agent_id: str, + timeout_seconds: int = 180, +) -> CommandResult: + started = time.time() + command_line = f"wait for managed mqtt channel ready in {container} for agent {agent_id}" + + agent_info_result = run_command(docker_exec_args(container, "agents", "info", agent_id), timeout_seconds=60) + if agent_info_result.exit_code != 0: + test_run.add_result(name, "FAIL", agent_info_result, notes="Failed to resolve managed agent topics before waiting for mqtt-channel readiness.") + return agent_info_result + + try: + agent_info = json.loads(agent_info_result.stdout) + except Exception as exc: + result = synthetic_result( + command_line=command_line, + stdout=agent_info_result.stdout, + stderr=f"Failed to parse agents info JSON: {exc}", + exit_code=1, + duration_ms=agent_info_result.duration_ms, + ) + test_run.add_result(name, "FAIL", result) + return result + + inbound_topic = str(agent_info.get("inboundTopic") or agent_info.get("inbound") or "").strip() + if not inbound_topic: + result = synthetic_result( + command_line=command_line, + stdout=json.dumps(agent_info, ensure_ascii=False, indent=2), + stderr="Managed agent info did not include inbound MQTT topic.", + exit_code=1, + duration_ms=agent_info_result.duration_ms, + ) + test_run.add_result(name, "FAIL", result) + return result + + readiness_marker = f"MQTT channel ready, subscribed to {inbound_topic}" + last_logs = "" + last_stderr = "" + while time.time() - started < timeout_seconds: + logs_result = run_command(docker_exec_args(container, "logs", "--limit", "200", "--plain"), timeout_seconds=30) + combined_logs = "\n".join(part for part in (logs_result.stdout, logs_result.stderr) if part).strip() + if logs_result.exit_code == 0: + last_logs = combined_logs + if readiness_marker in combined_logs: + result = synthetic_result( + command_line=command_line, + stdout=readiness_marker, + duration_ms=round((time.time() - started) * 1000), + ) + test_run.add_result( + name, + "PASS", + result, + notes="Waited for mqtt-channel to subscribe to the managed agent inbound topic after gateway healthz became ready.", + ) + return result + else: + last_logs = logs_result.stdout + last_stderr = logs_result.stderr + time.sleep(2) + + timeout_result = synthetic_result( + command_line=command_line, + stdout=last_logs, + stderr=last_stderr, + exit_code=124, + timed_out=True, + duration_ms=round((time.time() - started) * 1000), + ) + test_run.add_result( + name, + "TIMEOUT", + timeout_result, + notes=f"mqtt-channel did not report subscription readiness for {inbound_topic} within the wait window.", + ) + return timeout_result + + def remove_container_if_exists(test_run: TestRun, name: str, container: str) -> CommandResult: inspect_result = run_command(["docker", "inspect", container], timeout_seconds=20) if inspect_result.exit_code != 0: @@ -453,6 +536,44 @@ def validate_agent_list_step(test_run: TestRun, name: str, container: str, agent ) +def delete_managed_agent_step( + test_run: TestRun, + name: str, + container: str, + agent_id: str, + timeout_seconds: int = 120, +) -> CommandResult: + result = run_command(docker_exec_args(container, "agents", "delete", agent_id, "--no-restart"), timeout_seconds=timeout_seconds) + if result.timed_out: + status = "TIMEOUT" + notes = "" + elif result.exit_code == 0: + status = "PASS" + notes = "" + elif result.exit_code == 137: + verify_result = run_command(docker_exec_args(container, "agents", "list"), timeout_seconds=60) + if verify_result.exit_code == 0 and not test_agent_listed(verify_result.stdout, agent_id): + status = "PASS" + notes = ( + "agents delete was interrupted by gateway restart after applying config changes; " + f"verified agent removal with: {verify_result.command_line}" + ) + else: + status = "FAIL" + notes = ( + "agents delete exited 137 and agent removal could not be verified. " + f"Verification command: {verify_result.command_line}\n" + f"Verification stdout:\n{verify_result.stdout or '(empty)'}\n" + f"Verification stderr:\n{verify_result.stderr or '(empty)'}" + ) + else: + status = "FAIL" + notes = "" + + test_run.add_result(name, status, result, notes=notes) + return result + + def mqtt_roundtrip_step( test_run: TestRun, name: str, @@ -769,7 +890,7 @@ def quick_phase(test_run: TestRun, phase_name: str, container: str, agent_id: st timeout_seconds=60, ) invoke_test_step(test_run, f"{phase_name}_logs", docker_exec_args(container, "logs", "--limit", "20", "--plain"), timeout_seconds=60) - invoke_test_step(test_run, f"{phase_name}_agents_delete", docker_exec_args(container, "agents", "delete", agent_id, "--no-restart"), timeout_seconds=120) + delete_managed_agent_step(test_run, f"{phase_name}_agents_delete", container, agent_id, timeout_seconds=120) validate_agent_list_step(test_run, f"{phase_name}_agents_list_after_delete", container, agent_id, should_exist=False) @@ -855,6 +976,7 @@ def run_full_test(data_path: str, container_name: str = "", image: str = "", out invoke_test_step(test_run, "ai_agents_add", docker_exec_args(resolved_container, "agents", "add", ai_agent, "--no-restart"), timeout_seconds=120) invoke_test_step(test_run, "docker_restart_before_ai", ["docker", "restart", resolved_container], timeout_seconds=120) wait_for_gateway_ready(test_run, "ai_gateway_ready", resolved_container, timeout_seconds=240) + wait_for_managed_mqtt_ready(test_run, "ai_mqtt_ready", resolved_container, ai_agent, timeout_seconds=180) mqtt_expected_token = f"mqttpong_{time.strftime('%m%d%H%M%S')}" mqtt_roundtrip_step( test_run, @@ -903,7 +1025,7 @@ def run_full_test(data_path: str, container_name: str = "", image: str = "", out timeout_seconds=120, ) - invoke_test_step(test_run, "ai_agents_delete", docker_exec_args(resolved_container, "agents", "delete", ai_agent, "--no-restart"), timeout_seconds=120) + delete_managed_agent_step(test_run, "ai_agents_delete", resolved_container, ai_agent, timeout_seconds=120) validate_agent_list_step(test_run, "ai_agents_list_after_delete", resolved_container, ai_agent, should_exist=False) invoke_test_step( test_run,