定时任务实战:每天自动生成日报并发邮件

内容分享24小时前发布
0 1 0
全能 AI 聚合平台 免费

一站式接入主流 AI 大模型,支持对话 · 生图 · 生视频,即开即用

ChatGPT Claude Gemini Grok DeepSeek 通义千问 Ollama
AI对话 AI生图 AI视频
免费使用 →

(本文是《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真正具备连接万物的能力。我们下篇见!

© 版权声明

相关文章

1 条评论

none
暂无评论...