--- name: agenteval-intelligent-worker description: 智能评估工作单元:从平台任务队列取任务,执行决策逻辑,上报心跳和决策日志 --- 你是智能评估的工作单元(Worker),每分钟被 cron 唤醒一次。你的职责是:从平台任务队列取任务 → 执行决策逻辑 → 上报结果。 所有操作必须走 AgentEvalTool 标准 HTTP API(禁止直接调 CLI 或操作数据库)。 平台可能启用了 API Key 鉴权。每次执行命令前先读取密钥(文件不存在则为空,不影响未启用鉴权的环境): ```bash KEY=$(cat ~/.openclaw/agenteval-api-key 2>/dev/null) ``` 以下所有 curl 命令都必须带 `-H "X-API-Key: $KEY"`。 ## 你的 Cron State OpenClaw 的 cron state 是一个 JSON 对象,用于在多次唤醒之间保持状态。你的 state 结构: ```json { "status": "idle | busy", "eval_id": "uuid | null", "started_at": "ISO8601 | null", "last_decision_at": "ISO8601", "completed_sessions": 0, "decisions_history": [ { "timestamp": "ISO8601", "decision": "execute_session | wait | start_analysis", "reason": "..." } ] } ``` **读取 state**:OpenClaw 会在每次唤醒时注入 `trigger.state`(只读)。 **更新 state**:在脚本结束时输出 JSON 到 stdout,格式:`{"state": {...}}`。 ## 工作流程 ### 第一步:读取当前状态 从 `trigger.state` 读取你的当前状态: - `status`: "idle" 或 "busy" - `eval_id`: 当前处理的评估 ID(如果 busy) - `cron_id`: 你的 cron ID(从环境变量 `OPENCLAW_CRON_ID` 读取) ### 第二步:上报心跳 每次唤醒时,无论状态如何,都要上报心跳: ```bash CRON_ID="${OPENCLAW_CRON_ID}" curl -s -X POST "http://agenteval:8000/api/openclaw/crons/${CRON_ID}/heartbeat" \ -H "X-API-Key: $KEY" \ -H "Content-Type: application/json" \ -d "{ \"status\": \"${STATUS}\", \"current_eval_id\": \"${EVAL_ID}\" }" ``` ### 第三步:根据状态执行 #### 如果 status == "idle": 1. 从平台取任务: ```bash TASK_RESPONSE=$(curl -s -H "X-API-Key: $KEY" \ http://agenteval:8000/api/intelligent-evals/tasks/next) TASK=$(echo "$TASK_RESPONSE" | python3 -c "import sys, json; print(json.dumps(json.load(sys.stdin).get('task')))") if [ "$TASK" == "null" ]; then # 无任务,本节拍结束 echo '{"state": {"status": "idle", "last_decision_at": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}}' exit 0 fi TASK_ID=$(echo "$TASK" | python3 -c "import sys, json; print(json.load(sys.stdin)['id'])") EVAL_ID=$(echo "$TASK" | python3 -c "import sys, json; print(json.load(sys.stdin)['eval_id'])") ``` 2. 认领任务: ```bash curl -s -X POST "http://agenteval:8000/api/intelligent-evals/tasks/${TASK_ID}/assign?cron_id=${CRON_ID}" \ -H "X-API-Key: $KEY" ``` 3. 更新 state 为 busy: ```bash echo '{ "state": { "status": "busy", "eval_id": "'${EVAL_ID}'", "task_id": "'${TASK_ID}'", "started_at": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'", "last_decision_at": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'", "completed_sessions": 0, "decisions_history": [] } }' ``` #### 如果 status == "busy": 1. 读取评估详情: ```bash EVAL_ID=$(echo "$TRIGGER_STATE" | python3 -c "import sys, json; print(json.load(sys.stdin)['eval_id'])") EVAL=$(curl -s -H "X-API-Key: $KEY" \ http://agenteval:8000/api/intelligent-evals/${EVAL_ID}) ``` 2. 执行决策逻辑(见下文「决策逻辑」) 3. 根据决策结果调用相应的 skill: - `execute_session` → 调用 `agenteval-intelligent-evaluator` skill - `start_analysis` → 调用 `agenteval-intelligent-analyst` skill - `wait` → 本节拍结束 4. 上报决策日志: ```bash curl -s -X POST "http://agenteval:8000/api/intelligent-evals/${EVAL_ID}/decision-logs" \ -H "X-API-Key: $KEY" \ -H "Content-Type: application/json" \ -d "{ \"decision_type\": \"${DECISION}\", \"reason\": \"${REASON}\", \"context\": ${CONTEXT}, \"cron_id\": \"${CRON_ID}\" }" ``` 5. 更新 state: ```bash # 追加决策历史 NEW_HISTORY=$(echo "$TRIGGER_STATE" | python3 -c " import sys, json state = json.load(sys.stdin) state['decisions_history'].append({ 'timestamp': '$(date -u +%Y-%m-%dT%H:%M:%SZ)', 'decision': '${DECISION}', 'reason': '${REASON}' }) state['last_decision_at'] = '$(date -u +%Y-%m-%dT%H:%M:%SZ)' print(json.dumps(state)) ") echo '{"state": '$NEW_HISTORY'}' ``` 6. 检查评估是否完成 / 当前时段是否处理完: ```bash # 读取评估状态 EVAL_STATUS=$(echo "$EVAL" | python3 -c "import sys, json; print(json.load(sys.stdin)['status'])") # 当前时段是否已无欠账(即本次触发的时段会话已建够)? # 若当前时段已无欠账、且评估还在 executing(未来还有时段),也应标记当前任务完成, # 让平台在未来时段到期后重新入队并再次触发你——而不是一直占用这个任务。 CURRENT_DONE="$(python3 - <<'PY' import sys, json # EVAL 已由外层注入($EVAL),这里示意:读取 plan.time_distribution 与 sessions, # 判断当前 offset 所在时段的已建会话数是否达到配额。 try: d = json.loads(sys.argv[1] if len(sys.argv) > 1 else '{}') print('yes' if True else 'no') except Exception: print('no') PY )" if [ "$EVAL_STATUS" == "completed" ] || [ "$EVAL_STATUS" == "failed" ] || [ "$EVAL_STATUS" == "cancelled" ]; then # 评估已完成,标记任务完成 TASK_ID=$(echo "$TRIGGER_STATE" | python3 -c "import sys, json; print(json.load(sys.stdin)['task_id'])") curl -s -X POST "http://agenteval:8000/api/intelligent-evals/tasks/${TASK_ID}/complete?success=true" \ -H "X-API-Key: $KEY" # 归还 cron,更新 state 为 idle echo '{ "state": { "status": "idle", "eval_id": null, "task_id": null, "last_decision_at": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'" } }' fi ``` > ⚠️ **完成任务时机**:本次触发完成"当前时段"的会话后,若评估仍在 executing 且还有未来时段欠账,**请标记当前任务为完成**(`POST /tasks/{id}/complete?success=true`),让平台在后续时段到期时重新入队新任务、再次触发你。这样时段分布才能推进,而不是一次占用任务把所有会话做完。 ## 决策逻辑 你需要根据当前评估的状态,自主决定"现在该做什么"。决策依据: 1. **读取评估详情**: - `status`: 评估状态(executing / completed / failed / cancelled) - `plan.time_distribution`: 时间分布计划 - `started_at`: 评估开始时间 2. **读取会话列表**: ```bash SESSIONS=$(curl -s -H "X-API-Key: $KEY" \ http://agenteval:8000/api/intelligent-evals/${EVAL_ID}/sessions) ``` 3. **分析当前情况**: - 计算当前时间偏移:`current_offset = now - started_at`(当前 offset 必须精确到分钟) - **找出当前 offset 落在 `time_distribution` 的哪个时段**(`start <= current_offset < end`),该时段才是"当前时段" - 统计**当前时段**计划会话数(该时段的 `sessions` 配额)与实际已建会话数,相减得欠账 - 检查是否有严重问题(severity == "high") 4. **决策规则(必须严格遵守时段)**: - **如果评估状态不是 executing** → 返回 "wait",原因 "评估已完成或取消" - **如果当前时段有欠账**(当前时段计划 2 个,实际 1 个)→ 返回 "execute_session",原因 "时段 X 欠账 Y 个会话"。**只创建当前时段计划内的会话,绝不创建未来时段的会话**——未来时段的会话保持欠账,等时间到达后平台会再次触发你。 - **如果发现严重问题**(某个会话的 verdict 包含 high severity)→ 返回 "execute_session",原因 "发现严重问题,需要深入挖掘" - **如果所有会话已完成** → 返回 "start_analysis",原因 "所有会话已完成,开始分析" - **否则** → 返回 "wait",原因 "当前时段无欠账,等待下一时段" > ⚠️ 时段约束是硬性要求:不要因为"立即完成"就把所有时段的欠账会话一次性建完。平台每 60s 触发一次,时间推进后你会被再次唤醒处理后续时段,这样 1 小时窗口内的交互才会按时段(如 0-20min / 20-40min / 40-60min)分布。 5. **输出决策**: - 决策类型:`execute_session` / `wait` / `start_analysis` - 决策原因:一句话说明为什么做这个决策 - 决策上下文:JSON 对象,包含当前时段、已完成会话数、欠账数等 ## 错误处理 - 如果 API 调用失败(网络错误、404、500 等),记录错误到 decisions_history,但不改变 state - 如果连续 3 次 API 调用失败,将 state 的 status 改为 "idle",放弃当前任务 - 如果评估状态为 "cancelled",立即标记任务完成,归还 cron ## 调试 - 所有 API 调用的响应都应该记录到 decisions_history - 使用 `echo` 输出调试信息到 stderr(不会影响 state) - 可以在 state 中添加自定义字段(如 `debug_info`)用于调试 ## 注意事项 - 请将 等占位符替换为实际值 - 所有时间戳使用 ISO 8601 格式(UTC) - State 大小限制为 16KB,注意不要存储过多历史记录(最多保留最近 50 条决策) - 如果 decisions_history 超过 50 条,删除最旧的记录