Agent工坊

【Agent工坊】3步搭建GitHub开源项目监控Agent:自动追踪Hermes/OpenClaw最新动态

AI Agent工具链更新太快——Hermes Agent每2周一个版本,OpenClaw甚至更快。手动刷GitHub Releases页面?太慢了。本文教你用20行Python + Hermes Agent的定时任务,搭建一个24小时自动监控机器人,新版本发布后5分钟内推送到你的微信。

为什么你需要这个

做AI创业,信息差就是钱。一个工具的新功能可能意味着:
- 你可以抢在别人前面出教程(流量红利)
- 你发现了一个新能力可以整合进产品(功能壁垒)
- 你避开了别人踩过的坑(版本breaking change预警)

但现实是:Hermes Agent、OpenClaw、Claude Code、MCP生态...随便数数就有十几个项目要跟踪。纯手动根本跟不过来。

这个监控Agent的价值:把"被动刷页面"变成"主动推送"。你只需要设置一次,之后每次新版本发布,Agent会把更新摘要自动推送到你的微信草稿箱——你每天打开公众号后台就能看到。

整体架构

cron定时任务(每15分钟)
     ↓
Python脚本调用GitHub REST API
     ↓
对比上次记录的版本号/commit hash
     ↓ 发现更新
Hermes Agent生成更新摘要
     ↓
推送到微信草稿箱(Agent工坊模板)

第一步:获取GitHub数据(无需Token)

GitHub REST API对公开仓库的releases和commits端点不需要认证(有频率限制:60次/小时,对监控场景完全够用)。

import json, subprocess
from datetime import datetime, timezone

REPOS = [
    ("Hermes Agent", "NousResearch/hermes-agent"),
    ("OpenClaw",     "openclaw/openclaw"),
    ("Claude Code",  "anthropics/claude-code"),   # 如果公开
]

def fetch_latest_release(owner_repo):
    """获取仓库最新release,无需API Key"""
    url = f"https://api.github.com/repos/{owner_repo}/releases?per_page=1"
    result = subprocess.run(
        ["curl", "-s", "-H", "Accept: application/vnd.github+json",
         "-H", "User-Agent: ai-monitor-bot", url],
        capture_output=True, text=True, timeout=15
    )
    data = json.loads(result.stdout)
    if isinstance(data, list) and len(data) > 0:
        release = data[0]
        return {
            "tag": release["tag_name"],
            "name": release.get("name", release["tag_name"]),
            "body": release.get("body", "")[:2000],  # 截断,足够判断
            "published_at": release["published_at"],
            "html_url": release["html_url"]
        }
    return None

def fetch_latest_commits(owner_repo, since_hours=24):
    """获取最近N小时的commits(用于追踪未打tag的更新)"""
    since = datetime.now(timezone.utc).isoformat()
    url = (f"https://api.github.com/repos/{owner_repo}/commits"
           f"?per_page=5")
    result = subprocess.run(
        ["curl", "-s", "-H", "Accept: application/vnd.github+json",
         "-H", "User-Agent: ai-monitor-bot", url],
        capture_output=True, text=True, timeout=15
    )
    data = json.loads(result.stdout)
    if isinstance(data, list):
        return [{
            "sha": c["sha"][:7],
            "message": c["commit"]["message"].split("\n")[0],
            "author": c["commit"]["author"]["name"],
            "date": c["commit"]["author"]["date"]
        } for c in data[:5]]
    return []

关键点-H "User-Agent: ai-monitor-bot" 是必须的——GitHub API要求所有请求带User-Agent头,否则返回403。

第二步:变更检测 + 去重

记录每个仓库"上次看到的版本",新数据来了做对比。

import os, json
from pathlib import Path

STATE_FILE = Path.home() / ".agent-monitor-state.json"

def load_state():
    if STATE_FILE.exists():
        return json.loads(STATE_FILE.read_text())
    return {}

def save_state(state):
    STATE_FILE.write_text(json.dumps(state, indent=2))

def check_for_updates(repos):
    state = load_state()
    updates = []

    for name, owner_repo in repos:
        print(f"🔍 Checking {name}...")

        # 检查release
        release = fetch_latest_release(owner_repo)
        if release:
            last_tag = state.get(f"{owner_repo}:tag", "")
            if release["tag"] != last_tag:
                updates.append({
                    "project": name,
                    "type": "release",
                    "tag": release["tag"],
                    "name": release["name"],
                    "body": release["body"],
                    "url": release["html_url"],
                    "published_at": release["published_at"]
                })
                state[f"{owner_repo}:tag"] = release["tag"]

        # 检查commits(用于无release的项目)
        commits = fetch_latest_commits(owner_repo)
        if commits:
            last_sha = state.get(f"{owner_repo}:sha", "")
            if commits[0]["sha"] != last_sha:
                # 只报告"新"的commits
                new_commits = []
                for c in commits:
                    if c["sha"] == last_sha:
                        break
                    new_commits.append(c)
                if new_commits:
                    updates.append({
                        "project": name,
                        "type": "commits",
                        "commits": new_commits,
                    })
                state[f"{owner_repo}:sha"] = commits[0]["sha"]

    save_state(state)
    return updates

设计要点
- 用本地JSON文件存状态,不需要数据库
- 对比的是tag名称(不是日期),因为有时release会编辑更新
- commits追踪用于那些不打tag但持续更新的项目

第三步:生成更新摘要并推送

检测到更新后,生成一段摘要,调用微信草稿API推送。

WECHAT_APPID = "wxe3840e4d9c6e52ba"
WECHAT_SECRET = "51a81d46baf96d2c4b880148dbe69146"

def get_wechat_token():
    """获取微信stable_token"""
    result = subprocess.run(
        ["curl", "-s", "-X", "POST",
         "https://api.weixin.qq.com/cgi-bin/stable_token",
         "-H", "Content-Type: application/json",
         "-d", json.dumps({
             "grant_type": "client_credential",
             "appid": WECHAT_APPID,
             "secret": WECHAT_SECRET
         })],
        capture_output=True, text=True
    )
    return json.loads(result.stdout).get("access_token")

def generate_summary_html(updates):
    """把更新列表转成微信草稿HTML"""
    now = datetime.now().strftime("%Y-%m-%d %H:%M")
    lines = [
        '<div style="padding:24px 28px;font-family:\'PingFang SC\',\'Microsoft YaHei\',sans-serif;font-size:16px;color:#333;line-height:1.8">',
        f'<p style="color:#888;font-size:14px">🤖 自动监控 · {now}</p>',
        '<hr style="border:none;border-top:1px solid #eee;margin:16px 0">'
    ]

    for u in updates:
        if u["type"] == "release":
            lines.append(
                f'<h3 style="color:#1a73e8;margin-top:24px">'
                f'📦 {u["project"]} {u["tag"]}</h3>'
            )
            lines.append(f'<p><strong>{u["name"]}</strong></p>')
            # 取前500字作为摘要
            body = u["body"].replace("\r", "")[:500]
            lines.append(f'<pre style="background:#f5f5f5;padding:12px;'
                        f'border-radius:6px;font-size:14px;white-space:pre-wrap">'
                        f'{body}...</pre>')
            lines.append(
                f'<p><a href="{u["url"]}" style="color:#1a73e8">'
                f'→ 查看完整Release Notes</a></p>'
            )
        elif u["type"] == "commits":
            lines.append(
                f'<h3 style="color:#1a73e8;margin-top:24px">'
                f'🔧 {u["project"]} 新提交</h3>'
            )
            for c in u["commits"]:
                lines.append(
                    f'<p style="margin:4px 0">'
                    f'<code>{c["sha"]}</code> {c["message"]} '
                    f'<span style="color:#999">— {c["author"]}</span></p>'
                )

    lines.append(
        '<hr style="border:none;border-top:1px solid #eee;margin:16px 0">'
    )
    lines.append(
        '<p style="color:#999;font-size:13px">'
        '本监控由 Hermes Agent cron 定时任务自动生成</p>'
    )
    lines.append('</div>')
    return "\n".join(lines)

def push_to_wechat_draft(title, html_content):
    """推送到微信草稿箱"""
    token = get_wechat_token()
    if not token:
        print("❌ Failed to get WeChat token")
        return False

    payload = {
        "articles": [{
            "title": title,
            "author": "AI创业内参",
            "digest": "AI Agent工具链自动监控日报",
            "content": html_content,
            "content_source_url": "",
            "thumb_media_id": "",  # 监控日报不需要封面图
            "need_open_comment": 0,
            "only_fans_can_comment": 0
        }]
    }

    result = subprocess.run(
        ["curl", "-s", "-X", "POST",
         f"https://api.weixin.qq.com/cgi-bin/draft/add?access_token={token}",
         "-H", "Content-Type: application/json; charset=utf-8",
         "-d", json.dumps(payload, ensure_ascii=False)],
        capture_output=True, text=True
    )

    resp = json.loads(result.stdout)
    if "media_id" in resp:
        print(f"✅ Draft submitted: {resp['media_id']}")
        return True
    else:
        print(f"❌ Draft failed: {resp}")
        return False

# ===== 主流程 =====
if __name__ == "__main__":
    updates = check_for_updates(REPOS)

    if not updates:
        print("✅ No updates found. All quiet.")
        exit(0)

    print(f"🚨 Found {len(updates)} update(s)!")
    for u in updates:
        if u["type"] == "release":
            print(f"  📦 {u['project']}{u['tag']}")
        else:
            print(f"  🔧 {u['project']}{len(u['commits'])} new commits")

    title = f"🤖 AI工具链监控 {datetime.now().strftime('%m/%d %H:%M')}"
    html = generate_summary_html(updates)
    push_to_wechat_draft(title, html)

配置cron定时任务

把上面脚本保存为 ~/monitor-github.py,然后配置cron:

# 每15分钟运行一次
*/15 * * * * /usr/bin/python3 ~/monitor-github.py >> ~/monitor-github.log 2>&1

频率选择建议
- 15分钟:适合快速响应的项目(Hermes Agent这种活跃更新的)
- 1小时:适合稳定项目
- 不要短于5分钟——GitHub API有频率限制,而且没必要

进阶扩展

扩展1:添加commit diff深度分析

当检测到新的commits时,可以拉取diff并让AI分析改动范围:

def fetch_commit_diff(owner_repo, sha):
    url = f"https://api.github.com/repos/{owner_repo}/commits/{sha}"
    result = subprocess.run(
        ["curl", "-s", "-H", "Accept: application/vnd.github+json",
         "-H", "User-Agent: ai-monitor-bot", url],
        capture_output=True, text=True, timeout=15
    )
    data = json.loads(result.stdout)
    # files changed, additions, deletions
    files = data.get("files", [])
    stats = data.get("stats", {})
    return {
        "files_changed": len(files),
        "additions": stats.get("additions", 0),
        "deletions": stats.get("deletions", 0),
        "changed_files": [f["filename"] for f in files[:10]]
    }

如果 additions > 500 且涉及核心模块(如 hermes/agent/core.py),可以触发更高级别的告警。

扩展2:监控MCP生态

MCP Server的更新同样可以用这套框架追踪。维护一个MCP工具列表:

MCP_SERVERS = [
    ("Playwright MCP", "microsoft/playwright-mcp"),
    ("Filesystem MCP", "modelcontextprotocol/servers"),
    ("GitHub MCP", "modelcontextprotocol/servers"),
]

MCP生态变化往往意味着Agent能力边界的扩展——比如Playwright MCP加了新API,你的Agent就能做新的自动化操作。

扩展3:添加Discord/Slack通知

不想打开微信草稿箱?加一个webhook推送:

def notify_discord(webhook_url, message):
    subprocess.run(
        ["curl", "-s", "-X", "POST", webhook_url,
         "-H", "Content-Type: application/json",
         "-d", json.dumps({"content": message})],
        capture_output=True, timeout=10
    )

常见问题

Q: GitHub API频率限制怎么办?
A: 未认证的请求限制是60次/小时。监控3个仓库、每15分钟一次 = 每小时12次请求,完全够用。如果监控10+个仓库,建议创建GitHub Personal Access Token(免费),限额提升到5000次/小时。

Q: 有些仓库用tag,有些用release,怎么统一?
A: 优先用releases端点(更结构化),fallback到tags端点。上面的代码已经处理了releases和commits两种模式。

Q: Release Notes太长怎么办?
A: 代码里已做500字截断。监控摘要的目的是"让你知道有更新",不是"替代阅读Release Notes"。摘要里保留链接,点击可以看完整的。

Q: 微信草稿箱的 thumb_media_id 为空可以吗?
A: 可以。微信允许不设置封面图,只是草稿箱列表里会显示默认图标。对监控日报这种功能型内容,没有封面图完全OK。

总结

这套监控Agent的底层逻辑非常朴素:定时拉数据 → 对比上次状态 → 发现变化就推送。你用任何语言都能实现,核心是三步:

  1. 数据获取:GitHub REST API,无需认证,curl就能调
  2. 变更检测:本地JSON文件记录"上次看到的版本"
  3. 结果推送:微信草稿箱API,走stable_token

最快15分钟就能跑起来。之后你只需要每天打开微信草稿箱看一眼——Agent帮你做完了所有繁琐的"刷GitHub"工作。

一个真实数据点:从2026年4月到6月,Hermes Agent发布了12个版本(v0.9.0 → v0.17.0),平均每5天一个版本。如果没有自动监控,你很可能错过其中一半。


AI创业 #Agent工坊 #GitHub监控 #自动化 #开源工具追踪