Skill: 56xiaoli123/agent-usage-report (version 1.0.0)

--- name: agent-usage-report description: 生成 OpenClaw Agent 使用周报。当用户说"周报"、"生成周报"、"工作周报"、"agent usage report"、"usage report"、"帮我写周报"、"生成使用报告"时使用此技能。自动读取 session 日志和 memory 文件，统计任务量、工具调用、成本、高频功能，并输出结构化周报。 --- # Agent Usage Report 生成 OpenClaw Agent 的使用周报，包括运行数据、高频工具调用、memory 日志摘要。 ## 快速使用 ```bash python3 <skill-path>/scripts/generate_report.py ``` 默认输出上周（周一~周日）的统计数据。 ## 参数选项 | 参数 | 说明 | |------|------| | `--start YYYY-MM-DD` | 指定开始日期 | | `--end YYYY-MM-DD` | 指定结束日期 | | `--workspace /path` | 工作区路径（默认 `~/.openclaw/workspaces/bot6`） | | `--output file.txt` | 保存到文件而不是打印 | ### 示例 ```bash # 默认上周周报 python3 <skill-path>/scripts/generate_report.py # 指定日期范围 python3 <skill-path>/scripts/generate_report.py --start 2026-04-01 --end 2026-04-07 # 保存到文件 python3 <skill-path>/scripts/generate_report.py --output ~/weekly-report.txt ``` ## 输出内容 - **运行数据概览** — session 数、消息数、工具调用数、成本 - **高频工具调用 TOP10** — 统计所有工具使用频次 - **Memory 日志摘要** — 从 `memory/` 目录提取本周记录片段 - **异常记录** — 待扩展（可从 session 错误日志中提取） ## 数据来源 - Session 日志：`~/.openclaw/agents/<agentId>/sessions/*.jsonl` - Memory 文件：`~/.openclaw/workspaces/bot6/memory/*.md` ## 扩展周报内容 如果需要更完整的周报（风险记录、优化、下周计划），需要人工补充以下内容： 1. **风险记录** — 本周遇到的具体问题 2. **优化对策** — 解决方案和改进行动 3. **下周计划** — 下一周的演进方向 这些内容建议由使用者根据实际使用场景补充到报告末尾。 ## 配合其他技能 - 结合 `session-logs` skill 可以查询更详细的历史记录 - 配合 `clawhub` 可以将报告分享或存档

path: scripts/generate_report.py
content: #!/usr/bin/env python3
"""
OpenClaw Agent 周报生成器
读取 session 日志和 memory 文件，输出结构化周报
"""

import json
import os
import sys
import re
from datetime import datetime, timedelta
from pathlib import Path

AGENT_ID = "xiaotianmao"  # 可通过 Runtime 动态获取
SESSIONS_DIR = Path.home() / ".openclaw" / "agents" / AGENT_ID / "sessions"
WORKSPACE = Path.home() / ".openclaw" / "workspaces" / "bot6"


def get_week_range(date_str=None):
    """返回上周的起止日期（周一到周日）"""
    if date_str:
        today = datetime.strptime(date_str, "%Y-%m-%d")
    else:
        today = datetime.now()
    monday = today - timedelta(days=today.weekday() + 7)
    sunday = monday + timedelta(days=6)
    return monday.strftime("%Y-%m-%d"), sunday.strftime("%Y-%m-%d")


def list_sessions_in_week(start, end):
    """列出指定日期范围内的 session 文件"""
    sessions_dir = SESSIONS_DIR
    if not sessions_dir.exists():
        return []
    sessions = []
    start_dt = datetime.strptime(start, "%Y-%m-%d")
    end_dt = datetime.strptime(end, "%Y-%m-%d")
    for f in sessions_dir.glob("*.jsonl"):
        try:
            with open(f) as fh:
                first = fh.readline()
                if not first:
                    continue
                meta = json.loads(first)
                ts = meta.get("timestamp", "")
                if not ts:
                    continue
                session_date = datetime.strptime(ts[:10], "%Y-%m-%d")
                if start_dt <= session_date <= end_dt:
                    sessions.append(f)
        except Exception:
            continue
    return sessions


def count_messages(session_file):
    """统计 session 中的消息数、用户/助手消息、工具调用"""
    user_count = 0
    assistant_count = 0
    tool_calls = []
    total_cost = 0.0
    try:
        with open(session_file) as f:
            for line in f:
                if not line.strip():
                    continue
                msg = json.loads(line)
                if msg.get("type") != "message":
                    continue
                role = msg.get("message", {}).get("role", "")
                content = msg.get("message", {}).get("content", [])
                usage = msg.get("message", {}).get("usage", {})
                cost = usage.get("cost", {}).get("total", 0) or 0
                total_cost += cost
                if role == "user":
                    user_count += 1
                elif role == "assistant":
                    assistant_count += 1
                    # 统计工具调用
                    for c in content:
                        if isinstance(c, dict) and c.get("type") == "toolCall":
                            tool_calls.append(c.get("name", "unknown"))
    except Exception as e:
        print(f"  ⚠️  读取 session 出错: {e}", file=sys.stderr)
    return user_count, assistant_count, tool_calls, total_cost


def get_memory_entries(start, end):
    """从 memory 目录读取指定日期范围的笔记"""
    memory_dir = WORKSPACE / "memory"
    if not memory_dir.exists():
        return []
    entries = []
    start_dt = datetime.strptime(start, "%Y-%m-%d")
    end_dt = datetime.strptime(end, "%Y-%m-%d")
    for f in memory_dir.glob("*.md"):
        try:
            date_str = f.stem  # YYYY-MM-DD 或 YYYY-MM-DD-HHmm
            # 匹配日期部分
            m = re.match(r"(\d{4}-\d{2}-\d{2})", date_str)
            if not m:
                continue
            d = datetime.strptime(m.group(1), "%Y-%m-%d")
            if start_dt <= d <= end_dt:
                entries.append(f.read_text())
        except Exception:
            continue
    return entries


def get_daily_summary(start, end):
    """从 memory 中提取每日汇总信息"""
    entries = get_memory_entries(start, end)
    if not entries:
        return None
    # 合并所有 entries
    combined = "\n".join(entries)
    return combined


def build_report():
    """生成完整周报"""
    import argparse
    parser = argparse.ArgumentParser(description="生成 OpenClaw Agent 周报")
    parser.add_argument("--start", help="开始日期 YYYY-MM-DD（默认上周一）")
    parser.add_argument("--end", help="结束日期 YYYY-MM-DD（默认上周日）")
    parser.add_argument("--workspace", default=str(WORKSPACE), help="工作区路径")
    parser.add_argument("--output", help="输出文件路径（默认打印到 stdout）")
    args = parser.parse_args()

    if args.start and args.end:
        start, end = args.start, args.end
    else:
        start, end = get_week_range()

    print(f"📅 统计周期：{start} ~ {end}", file=sys.stderr)

    sessions = list_sessions_in_week(start, end)
    print(f"📂 找到 {len(sessions)} 个 session 文件", file=sys.stderr)

    total_user = 0
    total_assistant = 0
    all_tool_calls = []
    total_cost = 0.0
    for sf in sessions:
        u, a, tc, c = count_messages(sf)
        total_user += u
        total_assistant += a
        all_tool_calls.extend(tc)
        total_cost += c

    # 工具调用统计
    from collections import Counter
    tool_counter = Counter(all_tool_calls)
    top_tools = tool_counter.most_common(10)

    # memory 内容摘要
    memory_content = get_daily_summary(start, end)

    # 生成报告
    lines = []
    lines.append("=" * 60)
    lines.append("OpenClaw Agent 使用周报")
    lines.append(f"统计周期：{start} ~ {end}")
    lines.append("=" * 60)
    lines.append("")
    lines.append("一、运行数据概览")
    lines.append(f"  • 活跃 session 数：{len(sessions)} 个")
    lines.append(f"  • 用户消息总数：{total_user} 条")
    lines.append(f"  • 助手响应总数：{total_assistant} 次")
    lines.append(f"  • 工具调用总数：{sum(tool_counter.values())} 次")
    if total_cost > 0:
        lines.append(f"  • 总成本：约 ${total_cost:.4f}")
    lines.append("")
    lines.append("二、高频工具调用 TOP10")
    for tool, count in top_tools:
        lines.append(f"  {count:>4} 次  {tool}")
    lines.append("")
    lines.append("三、Memory 日志摘要")
    if memory_content:
        # 截取前 2000 字
        snippet = memory_content[:2000]
        lines.append("---")
        lines.append(snippet)
        lines.append("---")
    else:
        lines.append("  （本周无 memory 记录）")
    lines.append("")
    lines.append("四、异常记录")
    lines.append("  （从 session 日志中提取错误信息，待补充）")
    lines.append("")
    lines.append("=" * 60)
    lines.append(f"生成时间：{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    lines.append("=" * 60)

    report = "\n".join(lines)

    if args.output:
        Path(args.output).write_text(report)
        print(f"✅ 报告已保存：{args.output}", file=sys.stderr)
    else:
        print(report)

    return report


if __name__ == "__main__":
    build_report()


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