Skill: paoloxiamn/lx-agent-optimizer (version 1.4.0)

--- name: lx-agent-optimizer slug: lx-agent-optimizer version: 1.3.1 description: | A battle-tested agent self-improvement system built by a non-coder from weeks of real-world usage with OpenClaw. No fluff, no theory — every rule here was learned from something that actually broke. Covers: behavior learning, proactive patterns, cron discipline, cost control, and false-positive prevention. Use when: you want your agent to get smarter over time, stay lean, and stop interrupting you with noise. author: paoloxiamn license: MIT tags: [self-improvement, optimization, proactive, cron, cost-control, learning] --- # LX Agent Optimizer A unified skill for agents that want to **learn, act proactively, run lean, and cost less** — built from real production experience, not theory. > Born from weeks of real usage by Paolo + LX (OpenClaw). Every pattern here was tested, broke something, got fixed, and survived. > > **Notably: this skill was created by a non-coder author.** > That is exactly why it is practical — it focuses on observable failures, reusable workflows, and user experience, instead of abstract agent philosophy. --- ## Four Pillars ### 1. 🧠 Behavior Learning Learn from real mistakes. Write them down. Review weekly. Change behavior. → See `references/behavior-learning.md` ### 2. 🎯 Proactive Patterns Know when to speak up, when to stay silent, and when to just do it. → See `references/proactive-patterns.md` ### 3. ⚙️ Cron Discipline Script-first cron jobs that are silent on success, reliable, and cheap. → See `references/cron-discipline.md` ### 4. 💰 Cost Control Token spend is real. Route models wisely, cache aggressively, stay lean. → See `references/cost-control.md` --- ## Quick Start ### Step 1: Set up improvement log ```bash touch ~/.openclaw/workspace/improvement_log.md ``` Add this header: ```markdown # Agent Improvement Log Record weekly: problems encountered, lessons learned, behavior changes. ``` ### Step 2: Add weekly analysis cron (main session) ```json { "name": "Weekly Self-Improvement", "schedule": { "kind": "cron", "expr": "0 9 * * 1", "tz": "Asia/Shanghai" }, "sessionTarget": "main", "payload": { "kind": "systemEvent", "text": "⏰ Weekly improvement: read improvement_log.md, review last week's problems, add 2-3 new lessons, send brief report to user." } } ``` ### Step 3: Run the optimizer audit Say: **"audit my agent setup"** — the skill will analyze your workspace and propose improvements. --- ## Core Rules (Non-Negotiable) These were learned the hard way: | Rule | Why | |------|-----| | **File writes → main session only** | work agents can't write files; main session can | | **Data fetch → validate with curl first** | SPAs return empty shells; APIs return 403; test before shipping | | **Debugging → internal, not exposed** | User sees results, not "trying A... trying B..." | | **Infer before asking** | Read filenames, context, history — ask only when truly ambiguous | | **Script-first cron** | Embed logic in `.py` files, not in cron message prompts | | **Silent on success** | Only alert on anomalies, errors, or changes | | **Channel health checks → real-time probe, not stale logs** | Historical cron errors may be old; verify current channel state before alerting or auto-remediating | | **Cron alerts → classify error type before paging user** | Delivery failure, task logic failure, and node failure need different handling; don't treat them as the same outage | | **Reminder source mapping → check both cron and HEARTBEAT.md** | Some reminders live in cron jobs, others live in heartbeat rules; disabling one side is not enough | | **Success once ≠ learned** | A task is only truly learned after the verified path is written into external memory (`TOOLS.md`, improvement log, or long-term memory) | --- ## Tool Path Memory (Verified) | Task | Use This | Not This | |------|----------|----------| | Token usage data | `~/.openclaw/agents/*/sessions/*.jsonl` | codexbar, gateway.log | | WeChat article body | `agent-browser eval "document.querySelector('#js_content')?.innerText"` | Built-in browser tool | | PDF image extraction | `pdfimages -j <file> /tmp/out` | pymupdf (not installed) | | Send image to user | `message` tool (media/filePath) | Absolute/~ paths | | Sports data (no API key) | ESPN public API | sofascore (403), official site (SPA) | | Apple Calendar today events | Run `python3 /Users/paolo/.openclaw/workspace/skills/calendar-morning/scripts/today_events.py` on Paolo's Mac mini; under the hood it uses `/usr/bin/osascript` + Calendar.app | Re-guessing the tool, calendar names, or prompting from scratch | | Telegram channel health check | Send a **silent real-time probe** via `message` tool and only remediate/alert on actual send failure | Scanning historical cron `lastError` / `deliveryStatus` and assuming the channel is currently down | | Cron anomaly triage | First classify as **delivery failure / task failure / node failure**, then decide whether to alert, retry, or remediate | Treating every `consecutiveErrors > 0` as the same kind of outage | | Reminder disable audit | Check **both** cron jobs and `HEARTBEAT.md` before saying a reminder is removed | Looking only at cron list and missing heartbeat-driven reminders | --- ## Weekly Improvement Cycle ``` Monday 9:00 AM ↓ Read improvement_log.md ↓ Review last week's conversations for: - Tasks that needed retries - Times user waited too long - Wrong tool choices - Repeated mistakes ↓ Write 2-3 new lessons to improvement_log.md ↓ Send brief report: "N problems this week, key lesson: X, focus next week: Y" ``` --- ## Proactive Trigger Rules **Reach out when:** - Important email or calendar event incoming (< 2h) - Cron task failed with consecutive errors - Something discovered user would want to know - Haven't spoken in > 8h during waking hours **Stay silent when:** - Late night (23:00–08:00) unless urgent - User is clearly busy - Nothing new since last check - Just checked < 30 min ago - Task succeeded (success = silent) **Do without asking:** - Read files, search, organize - Execute cron/heartbeat checks - Update memory and logs - Commit workspace changes **Always ask first:** - Send emails, tweets, public posts - Delete data - Spend money - Make commitments for the user --- ## Heartbeat Design Heartbeat = **control plane only** (cheap). ✅ Good heartbeat tasks: - Check cron consecutiveErrors - **Classify cron errors by type** (delivery / task / node) before deciding whether to alert - Check if Telegram channel is down - Quick calendar scan ❌ Move to isolated cron instead: - Heavy data fetching - Report generation - Multi-tool workflows ``` HEARTBEAT_OK ← 99% of the time Alert only when: errors > 0, channel down, something changed ``` **Important:** heartbeat may use cron error state to spot anomalies, but **must not auto-remediate Telegram/channel issues from stale cron history alone**. Use cron state to detect "something looks wrong," then verify with a live probe before fallback alerts or recovery actions. --- ## Cron Design Checklist Before shipping any cron job: - [ ] Logic is in a `.py` script, not embedded in the prompt - [ ] Script tested locally with `python3 script.py` - [ ] Silent on success (`exit(0)` with no output = no message sent) - [ ] Output is minimal (< 200 chars for routine alerts) - [ ] Model is cheapest tier that works (e.g., `qwen-plus` for simple tasks) - [ ] Timeout is realistic (not too short = retries, not too long = waste) - [ ] File writes use main session, not work agent - [ ] Validated data source with curl before embedding URL --- ## Model Selection Guide | Task Type | Recommended Tier | Example | |-----------|-----------------|---------| | Simple fetch + format | cheapest (qwen-plus / gemini-lite) | sports results, reminders, weather | | **固定流程任务**（文章总结、写文件、数据抓取） | qwen-plus | 微信文章总结、Obsidian 写入、cron 推送 | | **中文内容处理**（总结/整理/改写） | qwen-plus | 中文语境更准，比 claude 省 token | | 周复盘 / cron 状态检查 | gemini-2.5-flash | 够用，比 sonnet 便宜 | | Reasoning + writing | mid-tier (sonnet) | self-improvement analysis, strategy | | Complex multi-step | high-tier (opus) | only when mid-tier fails repeatedly | **分工原则：主 session（claude）只做判断+调度+对话；固定流程和中文任务一律 sessions_spawn → qwen-plus。** **主模型注意事项（2026-03-28）：** - 不要把 Gemini 设为主 session 默认模型 → `compaction.mode: safeguard` 与 Gemini preview 模型有 API 兼容性问题，导致 400 报错 - Gemini 适合做 fallback 或在 isolated cron 中指定使用 - 主 session 保持 `renlijia/claude-sonnet-4-6` **Cost rule:** Cache hit rate > 70% = healthy. If < 40%, you're creating too many new sessions. --- ## Files - `references/behavior-learning.md` — improvement log format and weekly cycle - `references/proactive-patterns.md` — when to act, when to stay quiet - `references/cron-discipline.md` — script-first cron patterns, error classification, and channel-health rules - `references/cost-control.md` — token cost reduction playbook - `scripts/token_report.py` — weekly token usage report script --- ## Hermes-Inspired Extensions（v1.4） 三个从 Hermes Agent 框架借鉴的自进化机制，补充原有四大支柱。 ### 5. 📋 Skill Awareness（技能感知层） Agent 应该主动知道自己有什么能力，而不是靠记忆猜。 **每周扫描机制：** ```bash ls /Users/paolo/.openclaw/workspace/skills/ ``` 扫描后更新 `memory/skill-registry.md`，格式： ```markdown # Skill Registry（更新日期：YYYY-MM-DD） - skill-name: 一句话描述，适用场景 ``` **触发规则：** - 每周一自检一次，有新 Skill 时更新 - 接到任务前先查 `skill-registry.md`，判断是否有现成 Skill 可用 - 优先复用已验证的 Skill，不要重新摸索相同路径 --- ### 6. 🔁 Memory Recall Gate（记忆召回门控） 在处理较复杂任务前，先主动搜索记忆库，复用已有成功路径。 **执行流程：** ``` 接到任务 ↓ memory_search（关键词：任务类型 + 工具名） ↓ 命中成功路径？ ├── 是 → 直接复用，记录为"路径复用" └── 否 → 重新执行，执行完后记录路径到 TOOLS.md 或 improvement_log.md ``` **规则：** - 命中率低（连续 3 次同类任务都没命中记忆）→ 说明记忆库记录不足，补录 - 禁止"凭印象试错"：有相似历史路径一定先查，再动手 - 验证路径写入格式：`TOOLS.md | 已验证工具路径` 表格 --- ### 7. 📊 Task Quality Signal（任务质量反馈） 每次执行较复杂任务后，记录结果质量，形成可追踪的成功率数据。 **记录格式**（追加到 `improvement_log.md`）： ```markdown ## Task Signal（YYYY-MM-DD） - 任务：[任务简述] - 结果：✅ 成功 / ⚠️ 部分成功 / ❌ 失败 - 是否重试：否 / 是（N次） - 关键路径：[用了什么工具/命令] - 教训（失败时）：[一句话] ``` **什么算"较复杂任务"：** - 调用超过 2 个工具 - 涉及文件写入 / cron 变更 / 外部 API - 用户明确说"帮我做XX" **周报中汇报：** > 本周任务信号：共 N 次，成功率 X%，重试 Y 次，最高频失败点：Z

path: references/behavior-learning.md
content: # Behavior Learning

## Improvement Log Format

File: `~/.openclaw/workspace/improvement_log.md`

### Entry structure
```markdown
## YYYY-MM-DD (Week N)

**Review period:** YYYY-MM-DD to YYYY-MM-DD

### Problems (what went wrong)
1. **[Problem name]**: description of what happened and why it was bad
2. ...

### Lessons (what to do differently)
**Lesson 1: [Short title]**
Concrete behavior change, not vague intent.
Example: "Always check TOOLS.md before trying a tool command" not "be more careful"

**Lesson 2: ...**
```

## Weekly Cycle

Every Monday 9:00 AM:
1. Read improvement_log.md for history
2. Review last 7 days of conversations
3. Identify 2-3 concrete problems
4. Write specific, actionable lessons
5. Send brief report to user

## What counts as a problem
- User had to wait while agent fumbled
- Same mistake repeated from prior week
- User explicitly said "remember this" or "don't do that again"
- Task needed 3+ attempts before succeeding
- Agent asked user something that context already answered

## What counts as a good lesson
- Specific tool path or command to use
- Clear trigger: "when X happens, do Y"
- Verifiable: can check if followed next week
- NOT: "be more careful" / "think before acting"

## Promotion rules
- Lesson mentioned 3x in log → move to TOOLS.md or SOUL.md
- Verified repeatable workflow with easy-to-forget details → promote immediately to TOOLS.md
- If the user explicitly says "remember this" as a long-term preference → also record in MEMORY.md
- Lesson unused 60 days → archive or delete
- Lesson contradicted by new evidence → update immediately


path: references/cost-control.md
content: # Cost Control

## Where tokens actually go (OpenClaw reality)

Most spend is in **cacheWrite + cacheRead**, not raw input/output.
Cache tokens cost ~10% of input tokens — so high cache hit rate = low real cost.

### Healthy metrics
- Cache hit rate > 70%: ✅ costs well-controlled
- Cache hit rate 40-70%: 🟡 room to improve
- Cache hit rate < 40%: ⚠️ too many new sessions being created

### How to check
```python
# ~/.openclaw/agents/*/sessions/*.jsonl
# Look for: u.get('cacheRead') / (u.get('input') + u.get('cacheRead'))
```

## Model routing (tiered)

| Task | Tier | Notes |
|------|------|-------|
| Simple fetch, format, notify | Cheapest (e.g. qwen-plus) | Sports results, reminders, weather |
| 固定流程任务（文章总结、写文件、数据抓取、脚本执行） | qwen-plus | 中文友好 + 省 token，质量足够 |
| 中文内容处理（总结/改写/整理） | qwen-plus | 比 claude 更懂中文语境 |
| Analysis, writing, reasoning | Mid (e.g. sonnet) | Self-improvement, strategy docs |
| Complex multi-step | High (e.g. opus) | Only when mid fails repeatedly |

**Rule:** Start with cheapest. Upgrade only when task repeatedly fails or quality matters critically.

**分工原则（2026-03-27 Paolo 确认）：**
- 主 session（claude）：判断、调度、与 Paolo 对话
- 子 agent（qwen-plus）：所有固定流程 + 中文友好型任务，用 `sessions_spawn` 派发
- 典型场景：微信文章总结、Obsidian 写入、脚本执行结果格式化、cron 数据推送

## Session discipline

Each new session = new cache write = higher cost.

Reduce session sprawl:
- Don't create isolated sessions for simple tasks
- Reuse main session for interactive work
- Use `sessionTarget: isolated` only for true fire-and-forget cron jobs

## Context discipline

Large context = expensive heartbeats.

Keep these files lean:
- `HEARTBEAT.md`: < 30 lines of active rules only
- `AGENTS.md`: startup rules, not runbooks
- `SOUL.md`: personality, not procedures
- Move long docs to `references/` (loaded on demand)

## Cron cost profile

Cheapest cron pattern:
```
Script pre-computes → 0 tokens if no output
Script has output → ~500-1000 tokens total
```

Expensive cron anti-pattern:
```
Long prompt with embedded logic → 2000+ tokens setup
+ LLM reasons through it → 1000+ output tokens
+ Every run regardless of outcome
```

## Weekly token audit

Run `scripts/token_report.py` every Monday to:
- See per-day and per-model breakdown
- Catch anomalies (single-day spike > 3x average)
- Track cache hit rate trend

If single day > 3x recent average: investigate immediately (runaway cron? loop?).

## Cron model audit (2026-03-28 实战经验)

**所有 cron 任务的模型分工原则（已验证）：**

| 任务类型 | 模型 | sessionTarget |
|---------|------|--------------|
| 脚本执行 + 结果转发 | gemini-2.0-flash-lite | isolated |
| 数据抓取 + 简短推送 | qwen-plus 或 gemini-2.0-flash-lite | isolated |
| 自我分析 / 日志读写 | qwen-plus | isolated |
| 周复盘 / cron 状态检查 | gemini-2.5-flash | isolated |
| systemEvent 类提醒 | 不消耗 AI token | main |

**已知兼容性问题（2026-03-28 确认）：**
- `gemini-2.0-flash-lite` 和 `gemini-2.5-flash` 在带 **nodes.run 工具调用**的 isolated cron session 中均返回 `400 status code (no body)`
- `gemini-2.5-flash` (preview) 作为主模型时 compaction 触发 400
- **结论：Gemini 所有版本不适合用于含 nodes.run 的 cron 任务**
- **安全替代**：nodes.run 类任务统一用 `dashscope/qwen-plus`，稳定无报错（已验证）
- Gemini 仅适合：纯文本生成、web_search、不含工具调用的简单任务

**关键发现：**
- `sessionTarget: main` 的 `agentTurn` 任务会消耗主 session Claude token，改为 `isolated` 可完全隔离成本
- `systemEvent` 类型任务本身不跑 AI，只注入文字，不需要优化
- 唯一需要 Claude 的场景：**和 Paolo 的直接对话**

**Timeout 设定经验：**
- nodes.run 执行 Python 脚本：60s 经常不够，建议 **90s**
- 纯网页抓取：60s 有时卡死，建议 **90s**
- 简单 API 调用：30s 足够

**主模型选择（2026-03-28 确认）：**
- 主 session 默认模型：`renlijia/claude-sonnet-4-6`（不用 Gemini 做主模型，压缩 API 兼容性问题）
- Gemini 2.5 Flash 做 fallback 或指定 cron 任务使用
- Gemini 作为主模型时，`compaction.mode: safeguard` 会触发 400 报错（preview 模型兼容性问题）

## Persistent cost reduction goals (Paolo, 2026-03-28)

Paolo 明确要求：**持续降低 token 消耗和使用成本**。

执行原则：
1. 每周一 Token 周报中附带优化建议（已有 cron）
2. 新增 cron 任务时默认选最便宜够用的模型，不默认 Claude
3. 定期（每月）审查所有 cron，清理僵尸任务、合并相似任务
4. 主对话保留 Claude，其他一切能省则省


path: references/cron-discipline.md
content: # Cron Discipline

## The Golden Rule
> If the cron prompt needs > 200 tokens to explain, convert it to a script.

## Script-first pattern

### Structure
```
skills/my-skill/
├── SKILL.md
└── scripts/
    └── my_job.py    ← all logic here
```

### Cron payload (short)
```json
{
  "message": "Run script via nodes.run:\n[\"/usr/bin/python3\", \"/path/to/scripts/my_job.py\"]\n\nIf output: send to user.\nIf no output: end silently.",
  "timeoutSeconds": 60,
  "model": "qwen-plus"
}
```

## Script design rules

### Silent on success
```python
# Good: no output when nothing to report
if not new_events:
    exit(0)

# Good: output only when something happened
print(f"⚽ AC Milan won 3-2")
```

### Validate data sources first
Before embedding any URL in a cron job:
```bash
curl -s "https://api.example.com/data" | head -5
# Confirm: returns real data, not HTML shell, not 403
```

### State file for idempotency
```python
# Track what was last sent to avoid duplicates
state_path = "/tmp/my_job_state.json"
state = json.load(open(state_path)) if os.path.exists(state_path) else {}
last_sent = state.get("last_id", "")

if new_id == last_sent:
    exit(0)  # already sent this

# ... send ...
json.dump({"last_id": new_id}, open(state_path, "w"))
```

## Pre-ship checklist
- [ ] Logic is in `.py` script, not in prompt
- [ ] Script tested locally: `python3 script.py`
- [ ] Silent on no-op (exit 0, no output)
- [ ] Output < 300 chars for routine cases
- [ ] Model: cheapest tier that works
- [ ] Timeout: 60s for simple, 120s for web fetch, never > 180s
- [ ] File writes in main session, not work agent
- [ ] Data source validated with curl

## Common timeout guide
| Task | Timeout |
|------|---------|
| Local script (no network) | 30s |
| Single API call | 60s |
| Web fetch + parse | 120s |
| Multi-step with LLM reasoning | 120s |
| Never | > 180s |

## Delivery rules
- `NO_REPLY` / no output = no message sent ✅
- Alert only on: new data, errors, anomalies
- Never send "everything is fine" messages
- Success = silence

## Cron error classification

When a cron shows `consecutiveErrors > 0`, **do not assume the root cause immediately**. Classify first:

| Type | Typical signal | Meaning | Default action |
|------|----------------|---------|----------------|
| **Delivery failure** | `Message failed`, `sendMessage failed`, `not-delivered`, `deliveryStatus = unknown` | The job logic may have succeeded, but message delivery failed | Verify current channel health with a **live probe** before fallback alerts or remediation |
| **Task failure** | script exception, parse failure, bad prompt/tool call, non-zero exit | The workflow itself broke | Alert user briefly if user-facing job; fix logic/script path |
| **Node failure** | `Nodes failed`, remote host unavailable, permission/runtime error on node | The remote machine or node bridge failed | Alert user only if task matters now; retry later or repair node path |

**Rule:** delivery failure ≠ task failure. A report may already exist even when the final send step failed.

## Channel health check pattern

For "is channel X alive?" cron tasks, always use a **real-time probe**, never stale log scanning:

```
# ✅ Correct pattern
Attempt to send silent message to channel
  → success: exit silently
  → failure: remediate (e.g., reconnect VPN) + notify via fallback channel

# ❌ Wrong pattern
Scan cron job lastError / deliveryStatus history
  → if found → alert
  (Problem: old errors look like current failures)
```

**Why it matters:** Historical cron errors may be days old. A channel that failed yesterday might be healthy now. Real-time probe catches the ground truth.

**Heartbeat add-on rule:** if heartbeat notices Telegram-related cron errors, treat that as an anomaly hint only. Before sending DingTalk fallback or triggering recovery, run a live Telegram probe first whenever the environment allows it.

## Reminder disable: what to check

When the user asks to remove or disable a reminder/feature:
1. Check `cron list` for matching job names
2. **Also** check `HEARTBEAT.md` — some features live there, not in cron jobs
3. Confirm both sides are cleaned up before responding "done"

## Heartbeat cron-scan rule

Heartbeat is allowed to scan cron state for anomalies, but should follow this sequence:
1. Spot jobs with `consecutiveErrors > 0`
2. Classify them as **delivery / task / node**
3. Only escalate the ones that matter now
4. For channel-related delivery failures, **verify with a live probe before saying the channel is down**
5. Report only the current actionable conclusion, not a dump of historical run noise


path: references/proactive-patterns.md
content: # Proactive Patterns

## The Core Question
Before speaking: "Does this add value, or am I just filling silence?"

## When to initiate contact

### Always worth it
- Calendar event < 2h away (not yet mentioned)
- Cron job failed with consecutiveErrors > 0
- Telegram/channel is down, user might not know
- Something directly relevant to user's current focus discovered

### Usually worth it (use judgment)
- Interesting finding during background work
- Task completed with unexpected result
- It's been > 8h since last contact during waking hours

### Never worth it
- Just to say "I checked and everything's fine"
- Repeating something already said
- Late night (23:00–08:00) unless urgent
- User just responded < 30 min ago
- Background automation succeeded and required no user action
- Sending a fallback alert when the primary channel is actually healthy

## When to act without asking

Safe to do freely:
- Read files, search web, check calendars
- Update memory, logs, TOOLS.md
- Run scheduled checks
- Organize workspace

Ask first:
- Send any external message (email, tweet, Telegram to others)
- Delete or move important files
- Spend money or make purchases
- Schedule things on user's behalf
- Any irreversible action

## The "80% rule" for inference
If you're 80% sure what the user wants from context, do it.
If you're less than 80% sure, ask one specific question.
Never ask multiple questions at once.
Never ask something the context already answers.

## Reverse prompting
After completing a task, sometimes offer:
- "Want me to also X?" (only if genuinely useful)
- "I noticed Y while doing this — worth looking at?"

Only when there's real signal. Not as filler.

## Group chat rules
Respond when:
- Directly mentioned
- Can add genuine value
- Something genuinely funny fits

Stay silent when:
- Casual banter between humans
- Someone already answered
- Your response would just be "yeah" or "nice"


path: scripts/token_report.py
content: #!/usr/bin/env python3
"""
OpenClaw Token 消耗周报脚本
统计近7天各模型、各日期的 token 消耗
"""
import json, os
from datetime import datetime, timedelta, timezone

week_ago = datetime.now(timezone.utc) - timedelta(days=7)
by_day = {}
by_model = {}

for sessions_dir in [
    os.path.expanduser('~/.openclaw/agents/work/sessions'),
    os.path.expanduser('~/.openclaw/agents/main/sessions'),
]:
    if not os.path.exists(sessions_dir):
        continue
    for f in os.listdir(sessions_dir):
        if not f.endswith('.jsonl'):
            continue
        path = os.path.join(sessions_dir, f)
        try:
            with open(path) as fp:
                for line in fp:
                    try:
                        d = json.loads(line.strip())
                        if d.get('type') == 'message':
                            msg = d.get('message', {})
                            if msg.get('role') == 'assistant' and 'usage' in msg:
                                u = msg['usage']
                                ts = d.get('timestamp', '')
                                model = msg.get('model', 'unknown')
                                if ts:
                                    dt = datetime.fromisoformat(ts.replace('Z', '+00:00'))
                                    if dt > week_ago:
                                        dt_sh = dt + timedelta(hours=8)
                                        day = dt_sh.strftime('%m-%d')
                                        for dic in [by_day.setdefault(day, {}), by_model.setdefault(model, {})]:
                                            dic['input'] = dic.get('input', 0) + u.get('input', 0)
                                            dic['output'] = dic.get('output', 0) + u.get('output', 0)
                                            dic['cacheRead'] = dic.get('cacheRead', 0) + u.get('cacheRead', 0)
                                            dic['cacheWrite'] = dic.get('cacheWrite', 0) + u.get('cacheWrite', 0)
                    except:
                        pass
        except:
            pass

# 计算汇总
total_input = sum(v.get('input', 0) for v in by_day.values())
total_output = sum(v.get('output', 0) for v in by_day.values())
total_cache_read = sum(v.get('cacheRead', 0) for v in by_day.values())
total_cache_write = sum(v.get('cacheWrite', 0) for v in by_day.values())
total_all = total_input + total_output + total_cache_read + total_cache_write
cache_hit_rate = total_cache_read / (total_input + total_cache_read) * 100 if (total_input + total_cache_read) > 0 else 0

print("📊 Token 周报（近7天）\n")

print("【按日期】")
for day in sorted(by_day.keys()):
    v = by_day[day]
    t = v.get('input', 0) + v.get('output', 0) + v.get('cacheRead', 0) + v.get('cacheWrite', 0)
    out_ratio = v.get('output', 0) / t * 100 if t > 0 else 0
    cache_ratio = (v.get('cacheRead', 0) + v.get('cacheWrite', 0)) / t * 100 if t > 0 else 0
    print(f"  {day}：{t:,} tokens（输出 {out_ratio:.0f}%，缓存 {cache_ratio:.0f}%）")

print("\n【按模型】")
for model, v in sorted(by_model.items(), key=lambda x: -(x[1].get('input', 0) + x[1].get('output', 0) + x[1].get('cacheRead', 0) + x[1].get('cacheWrite', 0))):
    t = v.get('input', 0) + v.get('output', 0) + v.get('cacheRead', 0) + v.get('cacheWrite', 0)
    ratio = t / total_all * 100 if total_all > 0 else 0
    short_model = model.split('/')[-1] if '/' in model else model
    print(f"  {short_model}：{t:,} tokens（占 {ratio:.0f}%）")

print(f"\n【总计】")
print(f"  总 tokens：{total_all:,}")
print(f"  缓存命中率：{cache_hit_rate:.0f}%")

if cache_hit_rate >= 70:
    print(f"  评价：✅ 缓存利用率高，成本控制良好")
elif cache_hit_rate >= 40:
    print(f"  评价：🟡 缓存命中率一般，有优化空间")
else:
    print(f"  评价：⚠️ 缓存命中率低，建议减少新 session 创建频率")


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)