AgentEvalTool/backend/plugins/openclaw/skills/agenteval-intelligent-worker/SKILL.md
sinohqb 8959e7ac42
All checks were successful
CI / test (push) Successful in 3m57s
fix(intelligent-eval): respect time-window slot distribution + flat scores
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
2026-08-18 13:43:14 +08:00

265 lines
9.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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`)用于调试
## 注意事项
- 请将 <eval_id>、<session_id> 等占位符替换为实际值
- 所有时间戳使用 ISO 8601 格式(UTC)
- State 大小限制为 16KB,注意不要存储过多历史记录(最多保留最近 50 条决策)
- 如果 decisions_history 超过 50 条,删除最旧的记录