Hermes 实战 :多渠道接入——日报推送到飞书/Telegram

技能跑起来了,结果需要推送到你手上:早上 6 点整,日报不用你打开任何网页,已经躺在飞书群里------标题、要点、链接,一条 text 消息。网页要你打开,推送才送到手上。

本文做两件事:给日报推送做一次选型,把凭据安全这条底线讲清楚。前几篇日报都结束在 report.html,一个网页。网页的宿命是你得记得打开它;推送的宿命是它自己跑到你面前。

先把这轮要加的东西放进全景图:

collect → curate → render → report.html,是系列第 3 篇《第一个真实任务》就立起来的骨架。这轮新加有:digest.md 和 items.json 分叉出一条 push.py → 飞书群。看着轻巧,选型、凭据、安全全在这里。

版本口径照旧:截至 2026-08-28,本机实测 Hermes v0.19.0 + deepseek-v4-flash。写稿时我核实过,上游已迭代到 v0.20 线(v0.20.0 代号 Herald,2026-08-03 发布)。本篇要讲的推送与凭据机制------webhook、gateway、hermes send------不是某个版本临时加的功能,是 Hermes 从设计上就有的骨架,不悬在版本上。

两条路:飞书 webhook 还是 Hermes gateway

推送渠道,从零搭到「能用」,差别大得离谱。差距不在代码量,在你要不要替对方平台「收消息」。

我把选型压成一句话:只发不收,一条 webhook URL 就够;要收要发、要多渠道,才轮到 gateway。

维度 轻量路线:飞书 webhook 完整路线:Hermes gateway
凭据 一条 webhook URL bot token / 账号(.env + config.yaml)
常驻进程 不需要 需要(gateway 常驻)
收消息 只发不收 可收可发,双向对话
适用 团队群日报 个人全渠道助手

轻量路线的主角是飞书自定义机器人:在群里加一个「自定义机器人」,飞书给你一条 webhook 地址。往这条 URL POST 一段 JSON,消息就出现在群里。一条 URL,不需要常驻进程,不需要账号体系。

完整路线的主角是 Hermes gateway:一个常驻的消息网关进程,把 Telegram、Discord、WhatsApp、微信这些平台和 Hermes 本体接起来。你要给每个平台准备 bot token 或账号,配进 Hermes 的配置。

两条路我先各写各的------第 3 节走轻量路,第 4 节拆完整路。选型的判断标准,我留到第 8 节统一给。

轻量路线:push.py,一个纯标准库脚本

先写代码。日报推送器 push.py,放在 daily-report/ 里,只依赖标准库。它干三件事:读 digest.md 的前 500 字符当正文、读 items.json 的前几条标题当要点、把这两样拼成飞书 text 消息的 payload,POST 到 webhook。

三个函数就是全部。读数据:

python 复制代码
def load_digest() -> str:
    """读 digest.md,截前 500 字符作推送正文。"""
    try:
        with open("digest.md", encoding="utf-8") as f:
            text = f.read().strip()
    except FileNotFoundError:
        return "(本期未生成汇总,digest.md 不存在)"
    return text[:500]


def load_headlines(head: int) -> list:
    """从 items.json 取前 head 条标题,供推送消息使用。"""
    try:
        with open("items.json", encoding="utf-8") as f:
            data = json.load(f)
    except FileNotFoundError:
        return []
    return [it["title"] for it in data.get("items", [])[:head]]

拼 payload、发请求:

python 复制代码
def build_payload(head: int) -> dict:
    """构造飞书 text 消息 payload。"""
    lines = ["【各家 AI 动态日报】", ""]
    lines.append(load_digest())
    titles = load_headlines(head)
    if titles:
        lines.append("")
        lines.append("今日要点:")
        lines.extend(f"{i}. {t}" for i, t in enumerate(titles, 1))
    return {
        "msg_type": "text",
        "content": {"text": "\n".join(lines)},
    }


def send(payload: dict, webhook: str) -> str:
    """POST 到飞书 webhook,返回响应体。"""
    data = json.dumps(payload).encode("utf-8")
    req = urllib.request.Request(
        webhook, data=data,
        headers={"Content-Type": "application/json"},
    )
    with urllib.request.urlopen(req, timeout=15) as resp:
        return resp.read().decode("utf-8")

payload 的格式就一句话:这是飞书自定义机器人的标准文本消息体------msg_typetextcontent.text 是拼好的一整段正文。我在飞书开放平台文档里核过,格式对得上(写作日核实)。

webhook URL 从哪来?不写死、不打印,从环境变量读:

python 复制代码
ENV_KEY = "FEISHU_WEBHOOK"
MAX_ITEMS = 8   # 推送消息里最多带几条标题,控制长度

这延续了系列第 2 篇《安装部署实战》立下的 key 管理原则------DeepSeek 的 key 走 key_env: DS-KEY 从环境变量读,不写进 config.yaml。凭据进环境变量,不进代码。

真正发消息前,脚本先检查有没有凭据。没凭据就拒绝发送,这就是凭据守卫:

python 复制代码
    webhook = os.environ.get(ENV_KEY, "")
    if not webhook:
        print(f"错误:未设置环境变量 {ENV_KEY},无法真实推送。", file=sys.stderr)
        print("先 `export FEISHU_WEBHOOK=https://open.feishu.cn/open-apis/bot/v2/hook/<你的key>`,或先跑 --dry-run 预览。", file=sys.stderr)
        return 1

完整路线:Hermes gateway,让 agent 住进聊天软件

轻量路只解决「推出去」。你要在群里跟 agent 对话------问它「今天有什么重点」、让它把某条展开------就得走 gateway。

Hermes gateway 是一个常驻消息网关进程。gateway --help 第一行就说清它是干什么的:

text 复制代码
Manage the messaging gateway (Telegram, Discord, WhatsApp, Weixin, and more)

支持的平台远不止这几个。官方文档列了 20 多个:Telegram、Discord、Slack、WhatsApp、Signal、Matrix、钉钉、飞书、企业微信、微信、QQ 机器人......(写作日 WebSearch 核实)。命令面:

text 复制代码
run/start/stop/restart/status/install/uninstall/list/setup/enroll

本机没跑。gateway status 逐字:

text 复制代码
✗ Gateway is not running

To start:
  hermes gateway run      # Run in foreground
  hermes gateway install  # Install as Windows Scheduled Task (auto-start on login)

gateway list 逐字:✗ default (current) --- not running

常驻的代价,在这两句里就看出来了------要么前台跑着别关,要么装成开机自启的服务。gateway 是「常驻」这条路线的核心词。要接 Telegram/Discord/WhatsApp/微信,你得先有 bot token 或账号:Telegram 找 @BotFather 建 bot 拿 token,Discord 去开发者后台建应用,WhatsApp 走官方云 API 或桥接。这些我本机一个都没配,标「待核实」。

到这儿,你多半已经默认:推送 = 要常驻一个机器人。我第 5 节再拆一层------不一定。

中间件:hermes send,不常驻的推送通道

gateway 把消息「收」进来,也负责把消息「发」出去。但如果你只需要「发」,有一个更薄的入口:hermes send。它的帮助文件把定位写得很清楚:

text 复制代码
Pipe text from any shell script to any messaging platform Hermes is already configured for. Reuses the gateway's platform credentials (~/.hermes/.env + ~/.hermes/config.yaml) --- no LLM, no agent loop, no running gateway required for bot-token platforms like Telegram/Discord/Slack/Signal.

翻译成大白话:把任意 shell 脚本的输出,管到任何 Hermes 已配置好的消息平台。复用 gateway 配好的凭据;没有 LLM、没有 agent loop;对 Telegram/Discord/Slack/Signal 这类 bot-token 平台,不需要常驻 gateway。

参数就几个:-t 指定平台或频道、-f 从文件读正文、-s 加一行标题、-q 安静模式。典型用法就是把日报文本喂给它:

text 复制代码
cat digest.md | hermes send --to telegram

一个细节先记住:帮助里的 ~/.hermes 是 Unix 路径。Windows 上 Hermes home 是 %LOCALAPPDATA%\hermes,系列第 4 篇《记忆系统拆解》讲过,凭据就放在那里的 .envconfig.yaml

bot-token 平台,hermes send 直连对方平台的 REST 接口,不需要 gateway 常驻。gateway 常驻的价值在「收」------守着群聊等消息进来;只在「发」的场景,脚本和定时任务配好凭据后,直接 hermes send 就推走了。这就是后面 H7 调度要用的推送通道。

凭据安全:webhook URL 和 bot token,都不进代码

这一节最容易被跳过,但它是底线。我直接给判断:凡是能替你把消息发出去的字符串------webhook URL、bot token------都按密码对待。

三个「不」:

  • 不进代码------不写死在 push.py 里;
  • 不进提交------.env 进 .gitignore;
  • 不进文章------本篇不会出现任何真实凭据。

凭据从哪来、进到哪去,画成一张流向图:

流向是单向的:凭据只存在于「运行环境」,进程从环境变量或本地配置文件读它,用完即弃,不打印、不落日志。push.pyos.environ.get(ENV_KEY, "") 读,没读到就拒绝发送------上一节那个守卫的完整报错,逐字是:

text 复制代码
错误:未设置环境变量 FEISHU_WEBHOOK,无法真实推送。
先 `export FEISHU_WEBHOOK=https://open.feishu.cn/open-apis/bot/v2/hook/<你的key>`,或先跑 --dry-run 预览。

你看,没凭据,脚本根本不给你发。这是「凭据不进代码」最直接的好处:脚本本身是安全的,泄不泄密取决于运行环境,不取决于代码仓库。

再交代一个诚实的细节:飞书官方建议给自定义机器人开启签名校验------加一个 secret,发消息时按 HMAC-SHA256 生成 sign、带 timestamp(写作日核实)。我演示用的是最朴素的裸 webhook,团队群内部够用;生产环境建议按官方文档把签名补上。这层实现我没做,算半个「待核实」。

载体项目落地:推送链路成型

镜头拉回载体项目。整条链路现在是:collect.py 抓源 → curate.py 调 Hermes 汇总 → render.py 出网页,末尾多一档 push.py 推送。就是引言那张全景图。

真跑一遍 dry-run,验证推送内容长什么样------PYTHONIOENCODING=utf-8 python push.py --dry-run --head 3--head 3 指标题只带前 3 条。输出逐字取自本机:

text 复制代码
=== DRY RUN:将推送给飞书的消息 payload(不发) ===
【各家 AI 动态日报】

今日无新动态:24 条与昨日已报道重复,已剔除。日报沿用昨日内容。

今日要点:
1. Supporting Thailand's next generation of AI startups
2. Better answers, broader thinking: What students gain from ChatGPT and critical-thinking training
3. Expanding OpenAI's presence in Brazil

payload 共 251 字符

这条输出里藏着两个信号。一个是 digest------「今日无新动态:24 条与昨日已报道重复」,这是系列第 4 篇《记忆系统拆解》那个跨天去重 count=0 守卫的产物:昨天报过的今天重复,curate.py 不白调 Hermes,沿用昨日内容。另一个是标题------三条全是 OpenAI,items.json 按抓取顺序排,OpenAI 的 RSS 在前。推送和网页看到的是同一份数据,只是换了送达方式。

真实推送我没发:缺 FEISHU_WEBHOOK 凭据,标「待核实」。这正是脚本设计的本意------凭据不进代码,脚本没有凭据就发不出去,只能 dry-run 预览。Telegram 同理,缺 bot token,标「待核实」。

什么时候用 webhook,什么时候上 gateway

结论先放:团队群日报,webhook 够用;个人全渠道助手,才轮到 gateway。差别不在「渠道数量」这一个维度上。

我压成三个判断标准:推送频率、渠道数量、要不要收消息。

  • 推送频率------一天一次,webhook 一条 URL 就够;每分钟都要发,才需要正经管道。
  • 渠道数量------只推一个群,webhook;要同时到 Telegram、Discord、微信,gateway 或 hermes send 一次配好。
  • 要不要收消息------这条最锋利。日报只发不收,webhook 和 hermes send 都够;你要在群里跟 agent 对话、让它回你「今天有什么重点」,那就必须 gateway------双向通信需要一个常驻的接收端。

反过来读更扎心:很多人以为「推送 = 要个常驻机器人」,其实只发不收的话,一条 URL 就够了。gateway 的价值在「收」,不在「发」。

结论:推送把日报送到手上,还差「到点自动跑」

回到开头那个画面。日报现在能推了------push.py dry-run 跑通,凭据配好就能发。但还差最后一块拼图:6 点整那一下,谁来触发?

现在全靠人肉。每天 6 点手动跑一遍 push.py,那不叫自动化,叫「每天记得按一下」。这也正好接上系列第 6 篇《技能系统实战》的缺口------技能会跑,但没到点自动跑。

下一篇揭秘:让日报每天 6:00 无人值守地跑完 collect → curate → render → push 整条链。cron 就是那个「到点自动跑」的答案。关注别走丢,下一篇拆调度。

相关推荐
clorinda1 小时前
从零理解 PyTorch:用神经网络完成 MNIST 手写数字识别
人工智能·pytorch·神经网络
GoGeekBaird1 小时前
Agent 时代,你的生产环境,真的敢让它裸奔吗
后端·agent
吴佳浩1 小时前
大模型是怎么来的:从数据到 Foundation Model
人工智能·llm·agent
IT_陈寒1 小时前
Redis Pipeline用错竟比不用还慢,这个坑我帮你踩过了
前端·人工智能·后端
Henry-SAP1 小时前
AI新闻精选:聚焦前沿科技
人工智能·erp
庖丁AI1 小时前
PDF 转 Markdown 工具怎么选?AI 知识库和 RAG 场景要注意什么
人工智能·pdf·文档解析
AI推荐率1 小时前
为每种主体关系建立标准句:企业归属关系实操清单
人工智能
leeyi1 小时前
DDD 六条铁律:让 CI 替你骂人——go-arch-lint 门禁实战(第104篇)
ci/cd·agent·领域驱动设计
小小程序猴11 小时前
企业AI培训怎么选?一个四层加权评估模型的实现:需求定位 + 深潜解析
人工智能