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 Permalink 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 调用失败网络错误404500 记录错误到 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 条,删除最旧的记录