1. 项目概述为Hermes Agent注入“主动关怀”能力如果你正在使用Hermes Agent可能会发现一个普遍现象它非常强大能处理复杂的对话、调用工具、管理任务但它本质上是一个“被动响应”的智能体。只有当用户发起对话时它才会思考和行动。这就像一位知识渊博但性格内向的朋友你不找他他绝不会主动找你聊天。hermes-active-message这个项目就是为了打破这种被动性而生的。它的核心目标非常明确——在不修改Hermes核心代码的前提下为Hermes Agent增加一个“主动发送消息”的能力让它能从“应答机”升级为“关怀者”。想象一下这样的场景你的Hermes Agent在凌晨2点监测到服务器负载异常但它只能默默记录直到你第二天早上打开聊天窗口询问时它才会告诉你昨晚发生了什么。而有了hermes-active-message它可以在异常发生的几分钟内就通过Telegram等网关主动向你发送一条预警消息“嘿服务器CPU飙到95%了需要我帮你检查一下吗” 这种从“事后汇报”到“实时介入”的转变正是主动式AI智能体的价值所在。这个项目巧妙地利用了Hermes自身提供的扩展点——定时任务cron、插件钩子hooks和脚本能力以“覆盖层”overlay的方式无缝集成实现了功能的非侵入式增强。对于任何希望自己的AI助手能更“贴心”、更具前瞻性的开发者或高级用户来说这都是一项值得深入研究的特性。2. 核心设计思路与架构解析2.1 非侵入式覆盖层设计哲学hermes-active-message最精妙的设计在于其“非侵入性”。它没有采用Fork Hermes主仓库并修改源代码这种笨重且难以维护的方式而是将自己定位为一个“覆盖层”Overlay。你可以把它理解为一套“主题皮肤”或“功能插件包”直接放置在Hermes的标准目录结构之上通过官方预留的接口与主程序交互。这种设计带来了几个显著优势升级无忧Hermes主程序可以自由升级只要其扩展API保持稳定hermes-active-message的功能就不会受到影响。你无需在每次Hermes更新后费力地合并冲突或重新打补丁。部署简单项目提供了一个install.sh脚本其工作仅仅是文件复制。它将.hermes/目录下的特定文件如插件、脚本、配置文件拷贝到你的Hermes主目录~/.hermes中。这个过程不会触碰你的核心数据库state.db、认证文件auth.json或环境配置.env安全且干净。功能聚焦项目严格遵守“单一职责原则”只做“主动消息”这一件事。它通过cron定时触发通过脚本判断是否发送消息通过插件钩子处理消息上下文每个模块边界清晰易于理解和调试。2.2 运行时模型与数据流要理解这个项目如何工作我们需要梳理其核心的运行时数据流。整个过程可以看作一个由定时器驱动的决策与执行管道[定时器 Cron] 每隔N分钟触发 ↓ [执行脚本] active-message-build-context.py ↓ [构建上下文] 脚本读取Hermes的SQLite会话数据库、近期记忆等构建一个供LLM判断的“决策上下文” ↓ [LLM决策] 将上下文提交给配置的LLM如OpenAI GPT-4询问“基于当前上下文是否需要主动向用户发送消息如果需要消息内容是什么是否静默发送” ↓ [执行动作] 根据LLM返回的JSON结果 - 如果决定发送则通过Hermes网关如Telegram将消息送达指定聊天。 - 如果标记为 [SILENT]则可能仅记录日志而不实际发送取决于配置。 ↓ [上下文恢复] 通过 pre_llm_call 插件钩子将最近发送的主动消息“贴回”到用户的下一轮真实对话的上下文中保证对话连贯性。这个模型的关键在于决策权完全交给了LLM。项目本身不硬编码任何“在什么情况下发送什么消息”的逻辑。它只是定期地把当前系统状态最近对话、记忆、可能的外部数据整理好然后问LLM“你觉得现在该说话吗” 这使得主动消息的内容和时机具备了极高的灵活性和智能性可以适应从日常关怀“今天天气转凉记得加衣”到运维警报“数据库连接池即将耗尽”等各种复杂场景。2.3 安全与隐私考量项目的安全设计也值得称道。仓库中刻意排除了所有敏感信息不包含运行时数据如state.db会话数据库、auth.json认证令牌。不包含环境配置如.env文件中的API密钥。不包含用户信息如真实的聊天IDChat ID、用户记忆。不包含任务历史如cron/output/下的任务输出。所有需要这些信息的操作都在目标Hermes安装环境的运行时中完成。这意味着你克隆的只是一个“引擎”而“燃料”数据始终留在你的本地环境中。这种分离确保了即使代码仓库公开也不会泄露你的个人隐私或业务数据。在部署时你只需要通过修改本地的config.yaml来配置行为所有敏感操作都发生在你的可控环境内。3. 详细部署与配置指南3.1 环境准备与前置检查在运行安装脚本之前请确保你的Hermes基础环境已经就绪。这不仅仅是能运行那么简单而是要确保hermes-active-message所依赖的几个核心子系统工作正常。确认Hermes核心服务状态# 检查Hermes核心服务是否在运行 hermes status # 如果未运行请先启动 hermes start确保你使用的是较新版本的Hermes其插件和cron系统已完善。验证网关配置 主动消息需要一个出口。最常用的是Telegram网关。# 检查Telegram网关配置 cat ~/.hermes/config.yaml | grep -A5 -B5 “telegram”你需要确认gateways.telegram部分已正确配置了bot_token和allowed_chat_ids。记下你想要接收主动消息的chat_id后续创建cron任务时会用到。检查数据库与目录# 确保状态数据库存在通常在首次运行Hermes后自动创建 ls -la ~/.hermes/state.db # 确保cron相关目录存在 ls -la ~/.hermes/cron/如果state.db不存在可以先通过Hermes与网关进行一次简单对话来触发其创建。关键配置项调整 在Hermes的主配置文件~/.hermes/config.yaml中有一个与cron响应相关的设置需要特别注意# 确保此项设置为 false否则cron任务的输出会被包装可能影响脚本处理 cron: wrap_response: false这个设置告诉Hermes不要对cron脚本的输出进行额外封装保持原始输出这对于active-message-build-context.py脚本解析LLM返回的JSON至关重要。3.2 执行安装与文件结构剖析完成前置检查后就可以开始安装了。过程非常简单# 1. 克隆仓库建议放在用户代码目录下 git clone https://github.com/AllenReder/hermes-active-message.git ~/code/hermes-active-message cd ~/code/hermes-active-message # 2. 运行安装脚本 ./install.sh现在让我们深入看看安装脚本究竟在你的~/.hermes目录下部署了什么。理解这个结构有助于后续的调试和自定义。~/.hermes/ ├── active-message/ # 核心配置与状态目录 │ ├── config.yaml # 主配置文件由example复制而来需你编辑 │ ├── config.yaml.example # 配置示例文件 │ ├── cron_prompt.txt # 发送给LLM的决策提示词模板 │ └── state.json # 运行时状态文件安装时不包含运行后生成 ├── plugins/ │ └── active-message/ # 插件目录 │ ├── __init__.py │ └── hooks.py # 包含 pre_llm_call 钩子实现 └── scripts/ └── active-message-build-context.py # 核心决策脚本注意安装脚本是“增量”且“保守”的。它不会覆盖你已有的config.yaml。如果~/.hermes/active-message/config.yaml不存在它会从config.yaml.example复制一份作为初始模板。这意味着你可以安全地多次运行安装脚本而不用担心个人配置被覆盖。3.3 配置文件深度解读与定制安装完成后最重要的步骤就是配置~/.hermes/active-message/config.yaml。这个文件控制着主动消息行为的方方面面。我们打开示例文件逐项分析其含义和配置建议。# ~/.hermes/active-message/config.yaml 示例与解读 llm: # 使用哪个LLM提供商来做决策。必须与Hermes主配置中定义的llm配置块名称一致。 # 通常可能是 openai, claude, azure-openai 等。 provider: openai # 用于决策的模型名称。建议使用能力较强的模型如gpt-4系列以确保判断准确性。 model: gpt-4-turbo-preview # 决策时的温度参数。设置为较低值如0.1可以使LLM的决策更稳定、可预测。 temperature: 0.1 context: # 构建决策上下文时回溯多少条最近的“用户-助手”对话轮次。 # 这帮助LLM了解最近的互动背景。不宜过多否则上下文太长也不宜过少否则缺乏背景。 # 建议值在 5 到 20 之间取决于你的对话频率。 recent_turns: 10 # 是否包含“记忆”memories。Hermes的记忆系统可能记录了用户偏好、事实等。 # 开启后LLM能做出更个性化的主动消息例如“记得你明天有会议今天早点休息”。 include_memories: true # 从记忆中检索多少条相关条目。需要与 recent_turns 平衡总上下文长度。 memory_limit: 5 behavior: # 当LLM决定不发送任何消息时脚本的默认行为。 # log_only仅记录日志不执行任何发送操作。这是最安全、推荐的选择。 # send_silent仍然发送一个标记为[SILENT]的消息如果网关支持静默模式。 default_on_no_decision: log_only # 主动消息发送失败后的重试策略。 retry: # 是否启用重试 enabled: true # 最大重试次数 max_attempts: 2 # 重试间隔秒 delay_seconds: 30 # 日志配置用于调试和监控 logging: level: INFO # 可选 DEBUG, INFO, WARNING, ERROR file: ~/.hermes/active-message/active_message.log配置心得与避坑指南llm.provider匹配这里填写的必须是Hermes主配置~/.hermes/config.yaml中llms部分已定义好的配置块名称。例如如果你的主配置里是llms.openai-gpt4: ...那么这里就应该填openai-gpt4。填错会导致脚本无法找到LLM配置而失败。模型选择对于决策任务模型的推理能力比创意能力更重要。gpt-4系列比gpt-3.5-turbo更可靠。如果使用Claudeclaude-3-opus是更好的选择。切勿使用纯补全模型如davinci必须使用对话模型。上下文长度权衡recent_turns和memory_limit共同决定了每次调用LLM的上下文长度也即成本。你需要根据对话频率调整。如果每小时触发一次recent_turns: 10可能涵盖了过去几天的对话如果每分钟触发则可能只涵盖最近几分钟。建议从较小的值开始观察LLM决策质量再逐步调整。静默模式测试在正式启用前可以将default_on_no_decision设置为log_only并调高日志级别为DEBUG运行几次cron任务。查看日志文件观察LLM做出的决策和生成的消息内容是否符合预期而不会真的发送出去。3.4 创建与管理Cron任务配置完成后需要创建一个Cron任务来定期驱动整个流程。这是通过Hermes自身的hermes cron create命令完成的。# 这是一个完整的命令示例请替换 YOUR_CHAT_ID 为实际的Telegram聊天ID hermes cron create every 30m $( ~/.hermes/active-message/cron_prompt.txt) \ --name active-message \ --script active-message-build-context.py \ --deliver telegram:YOUR_CHAT_ID让我们拆解这个命令的每个部分“every 30m”这是调度表达式。表示每30分钟执行一次。你可以根据需要修改例如“every 1h”每小时、“every day at 09:00”每天上午9点。Hermes的cron语法非常灵活。“$( ~/.hermes/active-message/cron_prompt.txt)”这里使用了命令替换将cron_prompt.txt文件的内容读取出来作为“用户输入”传递给后面的--script。这个文件里的提示词是给LLM的“任务指令”告诉它现在需要执行主动消息决策。--name “active-message”给这个cron任务起一个名字便于后续管理如查看日志、删除任务。--script active-message-build-context.py指定要执行的脚本。Hermes会在~/.hermes/scripts/目录下寻找这个脚本。--deliver telegram:YOUR_CHAT_ID指定脚本输出的交付方式。这里是将脚本的输出即LLM生成的决策JSON交付给Telegram网关并发送到指定的聊天ID。这是消息最终被送出的关键参数。创建任务后的管理操作# 查看所有cron任务 hermes cron list # 查看特定任务的最近输出用于调试 hermes cron output active-message # 删除任务如果需要修改调度或重建 hermes cron delete active-message # 立即手动运行一次任务用于测试 hermes cron run active-message重要提示--deliver参数不仅用于发送主动消息也用于发送脚本执行失败的错误信息。确保YOUR_CHAT_ID是正确的并且你的Telegram Bot有权限向该聊天发送消息。一个常见的错误是使用了数字格式的Chat ID但某些情况下可能需要字符串格式请以Hermes网关的实际要求为准。4. 核心脚本与插件机制剖析4.1 决策脚本active-message-build-context.py工作流这个Python脚本是整个系统的“大脑”。它不是一个简单的消息转发器而是一个复杂的上下文构建与决策引擎。我们来逐步分析它的内部工作流程。初始化与配置加载脚本启动后首先加载~/.hermes/active-message/config.yaml中的配置并连接到Hermes的SQLite数据库~/.hermes/state.db。构建决策上下文这是最核心的一步。脚本会根据配置从数据库中提取多种数据拼接成一个结构化的“上下文快照”供LLM分析。最近对话获取最近N轮由context.recent_turns控制的用户与助手对话记录。这能让LLM知道“我们刚才聊了什么”。相关记忆如果context.include_memories为真脚本会调用Hermes的记忆检索接口获取与当前会话或用户相关的记忆片段。这为个性化消息提供了素材。系统状态可选扩展脚本预留了接口。你可以修改它从外部系统如服务器监控API、日历接口、天气API拉取数据并注入到上下文中。例如加入“当前服务器CPU使用率80%”LLM就可能据此决定发送警报。格式化与调用LLM将构建好的上下文连同cron_prompt.txt中的指令按照特定格式通常是ChatML或类似格式组织成LLM的请求消息。然后调用在配置中指定的LLM提供商和模型。解析与执行决策LLM的回复预期是一个格式严格的JSON对象例如{ “should_send”: true, “message”: “注意到您最近在询问项目部署进度。自动化部署脚本已于今天凌晨成功运行所有服务状态正常。需要我提供更详细的报告吗”, “silent”: false }脚本会解析这个JSON。如果should_send为true则准备发送message。silent标志可能用于控制消息的发送方式例如不触发通知。如果should_send为false则根据behavior.default_on_no_decision配置决定是记录日志还是发送静默消息。消息发送与状态持久化如果需要发送脚本会通过Hermes的内部接口或直接调用网关API来发送消息。同时它会将本次发送的消息内容、时间、决策依据等信息记录到~/.hermes/active-message/state.json中。这个状态文件有两个用途一是防止在极短时间内重复发送相似消息可通过脚本逻辑实现二是为后续的“上下文恢复”提供数据。自定义脚本的高级技巧 如果你想让主动消息更“智能”可以修改这个脚本。例如在构建上下文时增加对特定关键词的监控# 伪代码示例在上下文中加入外部健康检查结果 def fetch_system_health(): try: response requests.get(‘http://localhost:8080/health‘, timeout5) return {“status”: “healthy”} if response.ok else {“status”: “unhealthy”, “detail”: response.text} except Exception as e: return {“status”: “error”, “detail”: str(e)} system_health fetch_system_health() context[“system_health_check”] system_health然后在cron_prompt.txt中指示LLM“请关注system_health_check字段如果状态不是 ‘healthy’请务必发送一条告警消息。” 这样你就实现了一个基于外部状态的智能告警系统。4.2 插件钩子pre_llm_call与上下文恢复主动消息功能有一个潜在的“副作用”如果助手主动说了一句话而用户接下来的回复是基于这句主动消息的那么正常的对话历史中就会缺失助手主动发言的那一条记录导致上下文不连贯。hermes-active-message通过一个精巧的插件钩子解决了这个问题。在~/.hermes/plugins/active-message/hooks.py中实现了一个pre_llm_call钩子。这个钩子会在Hermes主程序每次调用LLM处理用户输入之前被触发。它的工作逻辑如下检查当用户发起新一轮对话时钩子函数被调用。它首先检查是否存在最近比如过去5分钟内由active-message-build-context.py脚本发送的主动消息记录通常从state.json或一个专门的缓存中读取。注入如果存在这样的记录钩子会修改即将发送给LLM的“对话历史”列表在历史记录的最末尾即紧挨着用户当前输入之前插入那条主动消息并将其角色role标记为“assistant”。效果这样LLM在响应用户时“看到”的对话历史就变成了“...更早的历史- [助手主动发言] - [用户最新提问]”。LLM就能完全理解上下文给出连贯的回复。这个机制至关重要。没有它就会出现以下尴尬局面助手主动说“服务器好像有点慢我在检查。”用户回复“查到原因了吗”由于用户的回复历史中没有助手的那句主动发言LLM看到的只是用户没头没尾的一句“查到原因了吗”它很可能回复“什么原因请提供更多背景信息。”通过pre_llm_call钩子的修复对话就变得自然流畅了。这是一个非常体现设计深度的细节它保证了主动消息功能不是孤立的“通知”而是能无缝融入持续对话的“交流”。5. 实战场景、问题排查与优化建议5.1 典型应用场景构想理解了原理和部署后我们可以构思一些具体的应用场景这能帮助你更好地定制自己的主动消息助手。个人健康与生活助理上下文集成健康API如Withings、Apple Health、日历、天气。主动消息示例“根据你的睡眠数据昨晚深度睡眠时间不足。今天下午你有一个空档建议安排20分钟的小憩。” 或 “今天下午有雨出门记得带伞。你预约的健身房课程一小时后开始。”开发与运维监控助手上下文集成Grafana/Prometheus警报、GitHub Pull Request状态、CI/CD流水线状态。主动消息示例“生产环境‘订单服务’的P99延迟在过去10分钟上升了200%已自动触发根因分析脚本初步怀疑是数据库连接池瓶颈。详细报告链接[...]” 或 “你关注的PR #123 已合并到main分支对应的CI部署流水线已启动。”学习与知识管理伙伴上下文集成RSS订阅、Readwise阅读高亮、Anki记忆卡片。主动消息示例“你三天前保存了一篇关于‘RAG模型优化’的文章根据艾宾浩斯遗忘曲线现在是复习的好时机。要我为你总结一下文章的核心要点吗” 或 “你订阅的‘AI工程周刊’刚刚更新头条是《Evaluating LLM Agents: Beyond Benchmark》。”客户支持与社交监听上下文集成社交媒体监听如品牌提及、客服工单系统、用户反馈论坛。主动消息示例“在Twitter上发现5条关于我们新产品‘搜索功能慢’的讨论情感倾向为负面。需要我生成一份摘要并起草一份内部警报吗”实现关键这些场景的核心在于扩展active-message-build-context.py脚本使其能从更多外部数据源获取信息并将这些信息结构化地放入提供给LLM的上下文中。同时需要精心设计cron_prompt.txt中的指令引导LLM如何解读和利用这些新增的上下文信息。5.2 常见问题排查手册在部署和使用过程中你可能会遇到一些问题。下面是一个快速排查指南。问题现象可能原因排查步骤与解决方案Cron任务创建失败1. Hermes cron服务未运行。2. 调度表达式语法错误。3.--deliver参数格式错误。1. 运行hermes status确认cron服务状态。2. 使用hermes cron create “every 1m” “test” --name test测试最简单的表达式。3. 检查hermes gateway list确认网关名称确保telegram:YOUR_CHAT_ID格式正确。任务运行但无消息发送1. LLM决策为“不发送”。2. 脚本执行出错。3. 网关发送失败。1. 检查任务输出hermes cron output active-message。查看LLM返回的JSON确认should_send是否为false。2. 查看脚本日志tail -f ~/.hermes/active-message/active_message.log。3. 测试网关手动发送消息hermes send telegram:YOUR_CHAT_ID “测试消息”。LLM返回格式错误1.cron_prompt.txt中的指令未明确要求JSON格式。2. 温度temperature参数过高导致输出不稳定。1. 确保提示词中包含类似“你必须以JSON格式回复”的强约束。2. 在config.yaml中将llm.temperature调低至0.1或0.2。3. 在提示词中提供更精确的JSON Schema示例。主动消息未融入后续对话1. 插件未正确加载。2.pre_llm_call钩子逻辑有误或状态未保存。1. 检查Hermes日志确认插件加载成功。2. 检查state.json文件看主动消息发送后是否有记录。3. 在插件代码中添加调试日志打印出它读取到的状态和修改前的历史。消息发送重复或过于频繁1. Cron调度间隔太短。2. 脚本逻辑中缺少防重检查。1. 调整Cron表达式如从“every 10m”改为“every 1h”。2. 修改脚本在state.json中记录上次发送时间和内容本次决策前先检查例如“同一主题消息1小时内不重复发送”。一个高级调试技巧直接模拟脚本运行。你可以手动执行决策脚本并观察其输出和日志这能隔离Cron系统带来的复杂性。cd ~/.hermes/scripts python active-message-build-context.py “这是一个手动测试的提示词”观察终端输出和日志文件这能最直接地看到上下文构建是否成功、LLM调用是否正常、决策逻辑是否正确。5.3 性能优化与成本控制建议主动消息功能会定期调用LLM这可能产生API费用并消耗Token。以下是一些优化建议优化调度策略不要盲目使用很短的间隔如每分钟。根据场景合理设置运维警报可以设置“every 5m”。日常关怀“every 6h”或“every day at 09:00,18:00”可能更合适。使用智能调度可以编写一个更高级的“调度器脚本”在特定条件如系统负载高、用户活跃时段下才触发真正的主动消息检查任务。精简上下文这是控制成本最有效的方法。调整recent_turns在满足LLM理解背景的前提下尽可能减少回溯的对话轮数。优化memory_limit限制检索的记忆条数或只检索高相关度的记忆。过滤无关对话修改脚本在构建上下文时跳过系统消息、过长消息或与决策无关的对话。使用性价比更高的模型对于决策任务不一定非要使用最顶级的模型。可以进行A/B测试用gpt-4和gpt-3.5-turbo在相同上下文下运行一段时间对比决策质量是否该发消息、消息内容是否合理。如果gpt-3.5-turbo在大多数情况下表现足够好可以将其作为默认决策模型仅在关键场景由脚本判断下切换到gpt-4。实现决策缓存如果上下文在短时间内没有显著变化LLM的决策很可能是一样的。可以在脚本中实现一个简单的缓存机制将当前上下文的哈希值如MD5和LLM的决策结果缓存起来缓存5-10分钟。如果下次运行时上下文哈希未变则直接使用缓存决策无需再次调用LLM API。设置预算与告警在LLM提供商的后台设置每月预算和用量告警。同时可以在active-message-build-context.py脚本中集成一个简单的计数器记录本月已使用的Token数或请求次数当接近阈值时自动停止发送非关键的主动消息或切换到一个本地轻量级模型如通过Hermes集成的本地LLM来做决策。通过以上这些部署、配置、剖析和优化步骤你应该能够将一个完全被动的Hermes Agent成功改造为一个善解人意、能在恰当时机提供价值的主动式AI伙伴。这个项目的价值不仅在于其功能本身更在于它展示了一种优雅的、非侵入式的AI智能体扩展范式值得所有AI应用开发者借鉴。