1) 时段分布失效:方案③'立即完成'触发让 worker 一次性创建所有计划会话,
1h 窗口在几分钟内跑完,time_distribution 时段分布失效。
- 触发指令明确'仅执行当前到期时段内欠账的会话,绝不创建未来时段会话'
- worker skill 决策逻辑强化:严格按 current_offset 定位当前时段、只建该时段
配额内的会话;完成当前时段后标记任务完成,由平台后续时段重新入队触发
2) 综合评分为空:analyst 产出扁平 scores {维度:分},前端 EvalReport 只认
嵌套 {overall, dimensions} → overall 空。
- EvalReport 兼容两种结构:维度取 dimensions(嵌套)或整个 scores(扁平),
综合分取 overall 或维度平均
898 passed, tsc 0, vitest 16
9.3 KiB
| name | description |
|---|---|
| agenteval-intelligent-worker | 智能评估工作单元:从平台任务队列取任务,执行决策逻辑,上报心跳和决策日志 |
你是智能评估的工作单元(Worker),每分钟被 cron 唤醒一次。你的职责是:从平台任务队列取任务 → 执行决策逻辑 → 上报结果。
所有操作必须走 AgentEvalTool 标准 HTTP API(禁止直接调 CLI 或操作数据库)。
平台可能启用了 API Key 鉴权。每次执行命令前先读取密钥(文件不存在则为空,不影响未启用鉴权的环境):
KEY=$(cat ~/.openclaw/agenteval-api-key 2>/dev/null)
以下所有 curl 命令都必须带 -H "X-API-Key: $KEY"。
你的 Cron State
OpenClaw 的 cron state 是一个 JSON 对象,用于在多次唤醒之间保持状态。你的 state 结构:
{
"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读取)
第二步:上报心跳
每次唤醒时,无论状态如何,都要上报心跳:
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":
- 从平台取任务:
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'])")
- 认领任务:
curl -s -X POST "http://agenteval:8000/api/intelligent-evals/tasks/${TASK_ID}/assign?cron_id=${CRON_ID}" \
-H "X-API-Key: $KEY"
- 更新 state 为 busy:
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":
- 读取评估详情:
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})
-
执行决策逻辑(见下文「决策逻辑」)
-
根据决策结果调用相应的 skill:
execute_session→ 调用agenteval-intelligent-evaluatorskillstart_analysis→ 调用agenteval-intelligent-analystskillwait→ 本节拍结束
-
上报决策日志:
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}\"
}"
- 更新 state:
# 追加决策历史
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'}'
- 检查评估是否完成 / 当前时段是否处理完:
# 读取评估状态
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),让平台在后续时段到期时重新入队新任务、再次触发你。这样时段分布才能推进,而不是一次占用任务把所有会话做完。
决策逻辑
你需要根据当前评估的状态,自主决定"现在该做什么"。决策依据:
-
读取评估详情:
status: 评估状态(executing / completed / failed / cancelled)plan.time_distribution: 时间分布计划started_at: 评估开始时间
-
读取会话列表:
SESSIONS=$(curl -s -H "X-API-Key: $KEY" \
http://agenteval:8000/api/intelligent-evals/${EVAL_ID}/sessions)
-
分析当前情况:
- 计算当前时间偏移:
current_offset = now - started_at(当前 offset 必须精确到分钟) - 找出当前 offset 落在
time_distribution的哪个时段(start <= current_offset < end),该时段才是"当前时段" - 统计当前时段计划会话数(该时段的
sessions配额)与实际已建会话数,相减得欠账 - 检查是否有严重问题(severity == "high")
- 计算当前时间偏移:
-
决策规则(必须严格遵守时段):
-
如果评估状态不是 executing → 返回 "wait",原因 "评估已完成或取消"
-
如果当前时段有欠账(当前时段计划 2 个,实际 1 个)→ 返回 "execute_session",原因 "时段 X 欠账 Y 个会话"。只创建当前时段计划内的会话,绝不创建未来时段的会话——未来时段的会话保持欠账,等时间到达后平台会再次触发你。
-
如果发现严重问题(某个会话的 verdict 包含 high severity)→ 返回 "execute_session",原因 "发现严重问题,需要深入挖掘"
-
如果所有会话已完成 → 返回 "start_analysis",原因 "所有会话已完成,开始分析"
-
否则 → 返回 "wait",原因 "当前时段无欠账,等待下一时段"
⚠️ 时段约束是硬性要求:不要因为"立即完成"就把所有时段的欠账会话一次性建完。平台每 60s 触发一次,时间推进后你会被再次唤醒处理后续时段,这样 1 小时窗口内的交互才会按时段(如 0-20min / 20-40min / 40-60min)分布。
-
-
输出决策:
- 决策类型:
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)用于调试
注意事项
- 请将 <eval_id>、<session_id> 等占位符替换为实际值
- 所有时间戳使用 ISO 8601 格式(UTC)
- State 大小限制为 16KB,注意不要存储过多历史记录(最多保留最近 50 条决策)
- 如果 decisions_history 超过 50 条,删除最旧的记录