(本文是《Hermes Agent实战:从零搭建你的AI智能体》系列第5篇,入门篇)
每天下午六点,闹钟准时响起。这不是下班的铃声,而是我过去几年最焦虑的时刻——该写日报了。
打开公司内部系统,切回Jira看板,再点开Gitlab的提交记录。我要在三个浏览器标签页之间来回横跳,努力回想今天到底干了啥。那个“修复了登录Bug”到底是上午修的还是下午修的?那个“优化数据库查询”提测了吗?半小时后,我终于拼凑出几百字的流水账,点击发送。结果第二天早上,组长在群里@我:“小张,昨天的日报里那个缓存优化的问题,具体QPS提升了多少?下次记得写清楚。”
那一刻我就在想:这些数据系统里都有,为什么要靠人肉搬运?如果有个AI助手能帮我自动抓取Jira状态、汇总Git提交记录,甚至根据这些数据生成一份有模有样的总结,并在每天下班前自动发到组长邮箱,那该多好。
这不仅仅是偷懒,更是为了把时间留给真正有价值的思考。我们在前几篇教程中已经让Hermes Agent学会了思考和行动,今天,我们将赋予它最关键的一项能力——时间感。通过Hermes Agent的定时任务机制,我们可以让AI不再是被动等待指令的聊天机器人,而是变成一个主动工作的数字员工。
核心概念:让AI“守时”的秘密
在Hermes Agent的架构设计中,定时任务并不是一个简单的`while True`循环,而是一套完整的事件驱动机制。对于初学者来说,理解下面三个核心概念,是掌握定时任务的关键。
1. Cron:时间的表达语言
如果说Hermes Agent是执行者,那Cron表达式就是它的“作息表”。Cron是一种在Unix/Linux系统中广泛使用的时间表达式格式,它用五个或六个字段来定义任务执行的时间周期。
在Hermes Agent中,我们使用标准的五段式Cron表达式:
`分 时 日 月 周`
列如,我们想每天下午6点执行任务,表达式就是 `0 18 * * *`。这看起来有点像天书,但逻辑超级清晰:
`0`:第0分钟
`18`:第18小时(下午6点)
`*`:每一天
`*`:每一月
`*`:每一周的第几天
掌握了Cron,你就掌握了控制AI时间的遥控器。
2. Scheduler:任务调度中心
Hermes Agent内置了一个轻量级的`Scheduler`模块。它的作用类似于一个交通指挥中心。当你定义好一个Cron任务后,`Scheduler`会将其加入调度队列。它不负责执行具体的业务逻辑(列如写日报),而是负责在时间到达时,向Agent的核心引擎发送一个“触发信号”。
这种设计的好处是解耦。你的Agent主进程可以专注于处理当前的对话,而调度器则在后台默默监听时间。当时间一到,它会自动唤醒Agent,注入预设的Prompt(提示词),让Agent进入工作状态。
3. Context Injection(上下文注入)
定时任务最难的不是“定时”,而是“定内容”。当Agent在下午6点被唤醒时,它需要知道“我是谁”、“我要干什么”、“目前的日期是多少”。
Hermes Agent通过上下文注入机制,在任务触发时自动填充环境变量。例如,我们配置任务时可以预设一个Prompt模板:“目前是${current_time},请帮我汇总今天的日报…”。触发时,框架会自动把`${current_time}`替换为真实的日期时间,确保Agent不会由于时空错乱而发疯。
实战步骤:搭建自动日报系统
光说不练假把式。接下来,我们通过Hermes Agent搭建一个“自动日报生成器”。这个Agent会在每天下午6点,自动调用模拟的API获取任务数据,生成日报摘要,并调用邮件接口发送。
步骤一:环境准备与依赖安装
假设你已经按照前几篇教程搭建好了基础环境。我们需要引入`apscheduler`库作为底层的调度引擎(Hermes Agent底层对此进行了封装),并确保Hermes核心库是最新版。
在你的项目目录下,新建一个`daily_report_agent.py`文件,并安装必要依赖:
“`bash
pip install hermes-agent apscheduler
“`
步骤二:定义日报生成的工具
Hermes Agent的强劲之处在于Tool(工具)的扩展。我们定义两个工具:一个用于获取今日完成的任务,一个用于发送邮件。为了让演示更直观,这里我们使用模拟数据。
“`python
from hermes import Tool
import datetime
# 模拟获取任务数据的工具
@Tool
def get_finished_tasks() -> str:
“””
从Jira或项目管理系统中获取当前用户今天完成的任务列表。
返回格式为JSON字符串。
“””
print(“>>> 正在抓取项目管理数据…”)
# 这里模拟返回的数据
mock_data = [
{“id”: “PROJ-101”, “summary”: “修复用户登录页面的验证码失效Bug”, “status”: “Done”},
{“id”: “PROJ-102”, “summary”: “优化首页轮播图加载速度,延迟降低200ms”, “status”: “Done”},
{“id”: “PROJ-103”, “summary”: “编写用户模块的单元测试用例”, “status”: “In Progress”}
]
# 简单的过滤逻辑(实际场景中会调用真实API)
finished = [t for t in mock_data if t['status'] == 'Done']
return str(finished)
# 模拟发送邮件的工具
@Tool
def send_email_report(subject: str, content: str) -> str:
“””
将生成的日报内容发送给指定负责人。
“””
print(f”>>> 正在发送邮件…”)
print(f”主题: {subject}”)
print(f”内容:
{content}”)
# 模拟发送成功
return “邮件发送成功!收件人:team_leader@company.com”
print(“工具定义完成:get_finished_tasks, send_email_report”)
“`
这段代码中,我们使用了`@Tool`装饰器,这是Hermes Agent定义工具的标准方式。Agent在运行时会自动解析函数的Docstring,理解每个参数的含义。
步骤三:配置Cron定时任务
这是最核心的一步。我们需要实例化Hermes Agent,并将上述工具绑定给它,然后配置调度器。
“`python
from hermes import Agent, Scheduler
# 1. 初始化Agent
# 我们给Agent设定一个角色设定,让它知道自己是日报助手
agent = Agent(
name=”DailyReporter”,
system_prompt=”””
你是一个高效的日报助手。
你的工作流程是:
1. 调用 get_finished_tasks 获取今日完成的任务。
2. 将任务列表整理成简洁、专业的日报格式。
3. 日报需包含:今日完成、明日计划(留空待填)、风险与困难(若无则不写)。
4. 调用 send_email_report 将日报发送出去。
请严格按照此流程执行,不要遗漏。
“””,
tools=[get_finished_tasks, send_email_report],
llm_config={“model”: “hermes-2-pro”} # 指定Hermes模型
)
# 2. 初始化调度器
scheduler = Scheduler(agent=agent)
# 3. 添加定时任务
# Cron表达式: “0 18 * * *” 代表每天下午18:00执行
# trigger_prompt: 任务触发时发给Agent的指令
scheduler.add_job(
job_id=”daily_report_job”,
cron_expr=”0 18 * * *”,
trigger_prompt=”请开始生成并发送今天的日报。”
)
print(“定时任务已添加:每天18:00自动执行日报生成。”)
“`
在这段代码里,`Scheduler`接管了时间控制权。`trigger_prompt`超级关键,它是唤醒Agent的“闹钟铃声”。当时间到达18:00,调度器会自动将这句Prompt喂给Agent,Agent随即开始思考并执行`system_prompt`中定义的工作流。
步骤四:启动与运行
最后,我们需要启动调度器,让程序保持运行状态。
“`python
if __name__ == “__main__”:
print(“Hermes Agent 日报服务已启动,等待调度…”)
print(“按 Ctrl+C 退出程序”)
try:
# start() 会阻塞主线程,持续监听时间
scheduler.start()
except (KeyboardInterrupt, SystemExit):
scheduler.shutdown()
print(“服务已停止。”)
“`
运行效果演示:
当你启动程序后,控制台会静静等待。一旦时间到达设定点(为了测试方便,你可以把Cron改为下一分钟),你会看到如下输出:
“`text
[2023-10-27 18:00:00] Triggering job: daily_report_job
>>> Agent正在思考…
>>> Action: get_finished_tasks
>>> 正在抓取项目管理数据…
>>> Observation: [{'id': 'PROJ-101', 'summary': '修复用户登录页面的验证码失效Bug', 'status': 'Done'}, …]
>>> Agent正在思考…
>>> Action: send_email_report
>>> 正在发送邮件…
主题: 【日报】2023-10-27 工作汇报
内容:
【今日完成】
1. [PROJ-101] 修复用户登录页面的验证码失效Bug
2. [PROJ-102] 优化首页轮播图加载速度,延迟降低200ms
【明日计划】
(待补充)
【风险与困难】
无
>>> Finish.
“`
看,不需要你手动操作,Hermes Agent自动完成了数据抓取、内容润色和邮件发送的全过程。这就是自动化的魅力。
进阶技巧:避坑指南
虽然代码跑通了,但在生产环境中落地定时任务,还有几个必须要知道的“坑”。
1. 时区陷阱
默认情况下,Cron表达式使用的是服务器的系统时间。如果你的服务器在海外(列如AWS美区),而你在国内,下午6点就会变成凌晨6点。
最佳实践:在初始化`Scheduler`时显式指定时区。
“`python
from apscheduler.schedulers.background import BackgroundScheduler
# Hermes Scheduler支持传入外部scheduler实例
from pytz import timezone
sh_tz = timezone('Asia/Shanghai')
# 在Hermes配置中传入时区设置
scheduler = Scheduler(agent=agent, timezone=sh_tz)
“`
2. 任务重叠与并发
假设生成日报的逻辑很复杂,耗时超过了1分钟(Cron粒度是分钟级),或者任务卡住了,下一分钟的触发信号又来了,这时候就会发生任务重叠。
解决方案:使用任务锁(Lock)。Hermes Agent的`add_job`方法支持`max_instances`参数,设置为1即可防止重叠执行。
“`python
scheduler.add_job(
…,
max_instances=1, # 同一时刻只允许运行一个实例
coalesce=True # 错过的任务是否合并执行
)
“`
3. 错过触发
如果服务器在18:00重启了,导致任务错过触发窗口怎么办?默认情况下,错过就错过了。对于日报这种时效性强的任务,我们一般希望服务器重启后能立即补跑一次,或者记录日志告警。
Hermes Agent提供了`misfire_grace_time`配置,允许任务在错过触发时间的必定秒数内依旧执行。但对于日报这种场景,提议配合监控告警系统,确保Agent服务的高可用。
4. Prompt的动态性
我们在实战中使用了静态的`trigger_prompt`。但在真实场景中,你可能希望Prompt包含动态内容,列如“今天是周五,请生成周报”。
这时候,你可以不直接传字符串,而是传一个回调函数给调度器,让调度器在触发时动态生成Prompt。
“`python
def dynamic_prompt_generator():
today = datetime.datetime.now()
if today.weekday() == 4: # 周五
return “今天是周五,请生成本周工作周报并发送。”
return “请生成今日日报并发送。”
scheduler.add_job(…, prompt_generator=dynamic_prompt_generator)
“`
总结预告
今天我们通过Hermes Agent的定时任务功能,成功把那个让人头疼的“写日报”环节自动化了。从理解Cron表达式到配置Scheduler,再到绑定具体的工具函数,你会发现Hermes Agent的设计超级符合直觉:时间负责触发,Agent负责思考,Tool负责执行。
掌握了定时任务,你的AI智能体就拥有了“主动性”,它可以成为你的私人秘书、运维巡检员或者是数据分析师。但目前的工具(如`get_finished_tasks`)还是我们在代码里写死的模拟函数。
在真实的开发环境中,我们需要对接各种复杂的API,处理各种奇怪的数据格式,这就需要我们编写更加定制化的Skill。下一篇教程,我们将进入《写一个属于自己的Skill:从需求到实现》,手把手教你如何封装一个通用的HTTP请求Skill,让你的Agent真正具备连接万物的能力。我们下篇见!


