fix(intelligent-eval): trigger OpenClaw planner for planning evals
All checks were successful
CI / test (push) Successful in 3m59s

方案③只自动化了 executing(worker)→completed(analyst),缺少 planning 阶段的
planner 触发——新建/被打回的评估永远停在 planning(无任何机制唤醒
agenteval-intelligent-planner skill)。
- 新增 _has_planning_eval + _trigger_intelligent_planner:scan loop 每 60s
  对 planning 状态评估触发 planner skill(产出粗计划并 PUT /plan 提交),
  与 worker 触发同模式(docker exec openclaw agent -m 带'立即完成'语义)
- scan loop 在 worker 触发后追加 planner 触发
测试:+2(无 planning 不触发 / 有 planning 触发 planner skill),897 passed
This commit is contained in:
sinohqb 2026-08-18 11:30:02 +08:00
parent 4b66c94969
commit 9d87ecf736
2 changed files with 126 additions and 4 deletions

View File

@ -42,6 +42,25 @@ def _has_pending_task() -> bool:
session.close() session.close()
def _has_planning_eval() -> bool:
"""True if any intelligent eval is waiting in ``planning`` (needs the OpenClaw planner)."""
from sqlmodel import select
from agenteval.intelligent_eval.models import IntelligentEvalStatus
from agenteval.storage.db import IntelligentEvalDB
session = get_session()
try:
ev = session.exec(
select(IntelligentEvalDB).where(
IntelligentEvalDB.status == IntelligentEvalStatus.PLANNING.value
)
).first()
return ev is not None
finally:
session.close()
def _supplement_decision_logs(session) -> int: def _supplement_decision_logs(session) -> int:
"""Platform audit backfill for decision logs. """Platform audit backfill for decision logs.
@ -206,13 +225,65 @@ async def _trigger_intelligent_worker() -> bool:
return True return True
async def _trigger_intelligent_planner() -> bool:
"""Trigger OpenClaw's headless agent to run the planner skill.
方案③只自动化了 executingworker completedanalyst**缺少 planning
阶段的 planner 触发**新建或被打回的评估会永远停在 planning这里对
planning 状态评估触发 `agenteval-intelligent-planner` skillplanner 自会
拉取 planning 评估列表读取四件套产出粗计划并 PUT /plan 提交planner
skill 定义见 OpenClaw workspace skills处理完评估离开 planning 后不再触发
Returns:
True 若确实触发了 agent存在 planning 评估
"""
if not _has_planning_eval():
return False
import subprocess
_logger = logging.getLogger("agenteval")
# 同 worker`openclaw agent` 无 cron state须带"立即完成"语义,否则 planner
# 会"决策后等下一拍"而死锁。
planner_msg = (
"执行 agenteval-intelligent-planner skill立即完成当前任务不要等待下一节拍"
"为 planning 状态的智能评估读取输入、产出粗计划并提交平台审批。"
)
try:
proc = await asyncio.to_thread(
subprocess.run,
[
"docker",
"exec",
"openclaw-eval",
"openclaw",
"agent",
"--agent",
"main",
"-m",
planner_msg,
"--json",
],
capture_output=True,
text=True,
timeout=300,
)
_logger.info("Planner 触发完成 exit=%s", proc.returncode)
if proc.returncode != 0:
_logger.warning("Planner 触发 stderr: %s", proc.stderr[-300:])
except Exception as exc:
_logger.warning("Planner 触发失败(忽略): %s", exc)
return True
async def _intelligent_eval_scan_loop() -> None: async def _intelligent_eval_scan_loop() -> None:
"""Scan executing intelligent evals, enqueue tasks, and trigger the worker. """Scan intelligent evals, enqueue tasks, and trigger planner/worker.
v1.1.0 缺陷修复`scan_and_enqueue_tasks` 此前没有调度点OpenClaw Worker v1.1.0 缺陷修复`scan_and_enqueue_tasks` 此前没有调度点OpenClaw Worker
每分钟唤醒却永远取不到任务平台启动后每 60s 扫描 executing 的评估入队 每分钟唤醒却永远取不到任务平台启动后每 60s
若有 pending 任务则触发 OpenClaw agent 执行 worker skill方案③免外部 planning 评估则触发 OpenClaw planner skill 产出粗计划planning待审批
channel失败不阻断下次循环继续 扫描 executing 的评估入队 若有 pending 任务则触发 worker skill 执行会话/
分析方案③免外部 channel失败不阻断下次循环继续
""" """
_logger = logging.getLogger("agenteval") _logger = logging.getLogger("agenteval")
while True: while True:
@ -242,6 +313,10 @@ async def _intelligent_eval_scan_loop() -> None:
await _trigger_intelligent_worker() await _trigger_intelligent_worker()
except Exception as exc: except Exception as exc:
logging.getLogger("agenteval").warning("Worker 触发失败(忽略): %s", exc) logging.getLogger("agenteval").warning("Worker 触发失败(忽略): %s", exc)
try:
await _trigger_intelligent_planner()
except Exception as exc:
logging.getLogger("agenteval").warning("Planner 触发失败(忽略): %s", exc)
await asyncio.sleep(60) await asyncio.sleep(60)

View File

@ -111,6 +111,53 @@ def test_trigger_worker_msg_has_execute_semantics(monkeypatch):
assert "agenteval-intelligent-analyst" in joined assert "agenteval-intelligent-analyst" in joined
def test_trigger_planner_skips_when_no_planning(monkeypatch):
"""No planning eval → no docker exec invocation."""
import asyncio
import subprocess
from unittest.mock import MagicMock
import agenteval.web.app as app_mod
calls: list = []
monkeypatch.setattr(app_mod, "_has_planning_eval", lambda: False)
def fake_run(cmd, **kwargs):
calls.append(cmd)
return MagicMock(returncode=0, stderr="")
monkeypatch.setattr(subprocess, "run", fake_run)
assert asyncio.run(app_mod._trigger_intelligent_planner()) is False
assert not calls, "should not invoke docker exec when no planning eval"
def test_trigger_planner_calls_docker_exec(monkeypatch):
"""Planning eval present → invoke `docker exec openclaw-eval openclaw agent` planner skill."""
import asyncio
import subprocess
from unittest.mock import MagicMock
import agenteval.web.app as app_mod
calls: list = []
monkeypatch.setattr(app_mod, "_has_planning_eval", lambda: True)
def fake_run(cmd, **kwargs):
calls.append(cmd)
return MagicMock(returncode=0, stderr="")
monkeypatch.setattr(subprocess, "run", fake_run)
assert asyncio.run(app_mod._trigger_intelligent_planner()) is True
assert calls, "docker exec should be invoked"
joined = " ".join(calls[0])
assert "docker" in joined and "openclaw" in joined
assert "agenteval-intelligent-planner" in joined
assert "立即完成当前任务" in joined
assert "不要等待下一节拍" in joined
def test_supplement_execute_session_log(monkeypatch, db_session): def test_supplement_execute_session_log(monkeypatch, db_session):
"""Executing eval with deficit and no execute_session log → platform backfills.""" """Executing eval with deficit and no execute_session log → platform backfills."""
import agenteval.web.app as app_mod import agenteval.web.app as app_mod