周期性执行与 TaoToken 统一 Key 通道)
1. 从 Nanobot 的 CronService 说起周期性执行到底解决了什么问题周期性执行cron/定时任务在 AI Agent 里不是新鲜概念但真正把它做进 Agent 架构、让 LLM 能自主注册和管理定时任务的项目并不多。Nanobot 作为 HKUDS 开源的超轻量级个人 AI 助手框架用 CronService CronTool 两个组件把这件事做成了一个完整的闭环。我最近在 OpenClaw 场景下拆这套源码发现它的设计思路对理解 Agent 的时间驱动能力很有帮助。简单说Nanobot 的周期性执行能力解决的是这样一个问题传统 Agent 只能在用户发起明确指令后执行操作属于被动响应模式。但很多真实需求是时间驱动的——比如每天早上九点检查一次项目仓库的 star 数、每 20 分钟提醒一次休息、工作日下班前汇总当天数据。这些需求如果靠用户手动触发就失去了自动化的意义。Nanobot 的做法是在 Agent 体系里嵌入一套轻量调度引擎。CronTool 作为面向 LLM 的接口层把自然语言指令转换成结构化的调度参数CronService 作为底层执行者负责任务的持久化、定时器管理和实际触发。两者通过依赖注入协作形成一个完整的定时任务管理方案。这套方案覆盖三种调度模式提醒Reminder、周期性任务Task、一次性任务One-time。时间配置上同时支持简易参数every_seconds和标准 cron 表达式兼顾普通用户和专业用户。任务生命周期管理包括添加、查询、删除、启用/禁用、手动触发还有连续错误自动禁用机制。对于正在学习 Agent 架构的开发者来说Nanobot 的这套实现是一个很好的参考样本——代码量不大但设计思路完整把时间驱动这个能力从需求到落地讲得很清楚。下面我会从源码结构、核心组件、配置方式到实际验证一步步拆开来看。2. TaoToken 统一 Key 通道的前置准备让周期性任务真正跑起来拆源码是一回事让周期性任务真正跑起来是另一回事。Nanobot 的 CronService 在触发任务时最终会调用 AgentLoop 去执行一次 LLM 请求。这意味着你需要一个稳定可用的模型 API 通道。我在测试时用的是 TaoToken 的统一 Key 通道它把多个模型的调用收敛到一个 Base URL 和一把 Key 上配置起来比较省事。TaoToken 的定位是统一 API 通道官网在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 入口是 https://taotoken.net/api 。它的核心价值在于你不需要为每个模型单独维护一套 Key 和 Base URL只需要在配置里写一次就能在 Nanobot 的 CronService 触发任务时稳定调用。具体来说Nanobot 的 CronService 在执行_execute_job时会通过on_job回调调用AgentLoop.process_direct()而 AgentLoop 内部会走一次完整的 LLM 请求。这个请求的 Base URL 和 API Key 就来自你的配置文件。如果你用的是 TaoToken 的统一通道配置里只需要写Base URL:https://taotoken.net/apiAPI Key: 从 TaoToken 控制台获取的 KeyModel ID: 你选择的模型标识这里有个细节值得注意Nanobot 的 CronService 本身不关心你用哪个模型它只负责在正确的时间触发on_job回调。真正决定用哪个模型的是 AgentLoop 的配置。所以你在配置 TaoToken 通道时实际上是在配置 AgentLoop 的 LLM 客户端而不是 CronService。我试过在 Nanobot 的配置文件里把 Base URL 指向 TaoToken 的 API 入口然后在 CronTool 里注册一个每 600 秒执行一次的任务任务是检查 HKUDS/nanobot GitHub stars 并报告。任务触发后AgentLoop 会通过 TaoToken 通道调用模型模型返回结果后再由 CronService 记录状态。整个过程跑通后你可以在日志里看到任务的执行记录和返回结果。如果你还没有 TaoToken 的 Key可以先到控制台创建一个https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 。创建后在 API Keys 页面复制 Keyhttps://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。这两个页面是后续配置的基础。3. 可复制的配置片段Nanobot CronService TaoToken 通道这一节给出可以直接复制使用的配置片段。Nanobot 的配置通常放在项目根目录的配置文件中具体路径取决于你的部署方式。下面是一个完整的配置示例包含 TaoToken 通道和 CronService 相关设置。首先是 LLM 通道配置这部分决定 CronService 触发任务时用哪个模型{ llm: { base_url: https://taotoken.net/api, api_key: sk-your-taotoken-key-here, model: claude-sonnet-4-20250514, timeout: 120, max_retries: 3 }, cron: { store_path: ./data/cron/jobs.json, enabled: true, tick_interval_ms: 1000, max_consecutive_errors: 5 } }如果你用的是 TOML 格式的配置等价写法如下[llm] base_url https://taotoken.net/api api_key sk-your-taotoken-key-here model claude-sonnet-4-20250514 timeout 120 max_retries 3 [cron] store_path ./data/cron/jobs.json enabled true tick_interval_ms 1000 max_consecutive_errors 5这里的关键点有三个。第一base_url必须指向 TaoToken 的 API 入口https://taotoken.net/api不要带 UTM 参数否则可能导致请求异常。第二api_key从 TaoToken 控制台获取格式通常是sk-开头。第三model字段填你实际要用的模型 ID这个 ID 需要和 TaoToken 支持的模型列表一致。CronService 的配置里store_path是任务持久化文件的位置Nanobot 会把所有 CronJob 序列化到这个 JSON 文件里。tick_interval_ms是定时器的轮询间隔默认 1000 毫秒。max_consecutive_errors是连续错误阈值达到后任务会自动禁用这是 Nanobot 的一个保护机制。配置写好后你需要确保 Nanobot 启动时能加载这个配置。通常是在启动脚本里指定配置路径或者在代码里通过环境变量注入。如果你用的是 Claude Code 类的工具来管理配置可以参考接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。配置完成后CronService 在启动时会执行_load_store()加载已有任务然后_recompute_next_runs()重新计算所有任务的下次执行时间最后_arm_timer()设置定时器。这个过程在日志里会有记录你可以通过日志确认配置是否生效。4. 验证请求与成功结果从注册任务到日志核对配置写好后下一步是验证整个链路是否跑通。我建议按这个顺序操作先注册一个简单的周期性任务然后观察日志最后核对返回结果。第一步通过 CronTool 注册一个测试任务。如果你是在交互式环境里可以直接对 Agent 说帮我添加一个定时任务每 120 秒提醒我一次检查 TaoToken 通道是否正常Agent 会调用 CronTool 的add动作参数大致是{ action: add, message: 检查 TaoToken 通道是否正常, every_seconds: 120 }CronTool 内部会把这个请求转换成 CronSchedule 对象然后调用 CronService.add_job()。add_job 会生成一个 8 位短 UUID 作为任务 ID计算下次执行时间把任务追加到 store.jobs 列表保存到 JSON 文件最后重新设置定时器。第二步观察日志。CronService 在启动和任务执行时都会打日志。你应该能看到类似这样的输出Cron service started with 1 jobs Cron: added job 检查 TaoToken 通道是否正常 (a1b2c3d4) Cron: executing job 检查 TaoToken 通道是否正常 (a1b2c3d4) Cron: job 检查 TaoToken 通道是否正常 completed如果看到executing和completed说明任务被正确触发并且执行成功。如果看到failed说明执行过程中出错了需要看last_error字段。第三步核对返回结果。CronService 在执行任务后会把状态写回 JSON 文件。你可以打开./data/cron/jobs.json查看{ version: 1, jobs: [ { id: a1b2c3d4, name: 检查 TaoToken 通道是否正常, enabled: true, schedule: { kind: every, everyMs: 120000 }, payload: { kind: agent_turn, message: 检查 TaoToken 通道是否正常, deliver: true, channel: telegram, to: your-chat-id }, state: { nextRunAtMs: 1735689600000, lastRunAtMs: 1735689480000, lastStatus: ok, lastError: null } } ] }重点看lastStatus字段。如果是ok说明任务执行成功如果是errorlastError里会有具体错误信息。nextRunAtMs是下次执行时间戳lastRunAtMs是上次执行时间戳。如果你想手动触发一次任务来验证可以用 CronTool 的run动作或者直接调用 CronService.run_job()。手动触发的好处是不用等定时器可以快速验证链路。验证模型返回是否正常可以到模型对话页面直接测试同一个模型https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentchatutm_campaignrewrite 。如果对话页面能正常返回说明 Key 和通道没问题问题可能出在 Nanobot 的配置或代码逻辑上。5. 本篇常见错误排查401、local proxy failed、reading choices、OAuth在配置 Nanobot CronService TaoToken 通道的过程中我遇到过几类典型错误。这一节按错误信息分类给出排查思路。401 Unauthorized这是最常见的错误通常出现在 CronService 触发任务、AgentLoop 调用 LLM 时。原因一般是 API Key 配置错误。排查步骤检查配置文件里的api_key是否和 TaoToken 控制台里的一致确认 Key 没有多余的空格或换行确认 Key 没有过期或被禁用确认base_url是https://taotoken.net/api不是其他地址如果 Key 是从环境变量读取的还要确认环境变量名和代码里读取的一致。Nanobot 的配置加载逻辑可能会优先读环境变量再读配置文件这个顺序要搞清楚。local proxy failed这个错误通常和网络环境有关。如果你在本地跑 Nanobot而本地有代理设置可能会导致请求失败。排查步骤检查系统代理设置确认没有冲突检查 Nanobot 的 HTTP 客户端配置确认没有硬编码代理如果用了容器部署检查容器网络是否能访问外部 API这个错误的本质是请求没有正确到达 TaoToken 的 API 入口。你可以先用 curl 测试一下curl -X POST https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer sk-your-key \ -H Content-Type: application/json \ -d {model:claude-sonnet-4-20250514,messages:[{role:user,content:test}]}如果 curl 能通说明网络没问题问题在 Nanobot 的配置或代码。reading choices 相关错误这个错误通常出现在解析 LLM 返回结果时。Nanobot 的 AgentLoop 会解析模型返回的 JSON如果返回格式不符合预期就会报这个错。排查步骤确认模型 ID 正确不同模型的返回格式可能不同确认 TaoToken 通道返回的是标准 OpenAI 兼容格式检查 Nanobot 的解析逻辑是否和返回格式匹配如果模型返回的是流式响应而 Nanobot 按非流式解析也会出问题。确认配置里的stream参数和代码逻辑一致。OAuth 相关错误如果你用的是 Claude Code 类的工具可能会遇到 OAuth 认证问题。这类工具通常有自己的认证流程和 API Key 认证不同。排查步骤确认你用的是 API Key 认证不是 OAuth如果工具强制走 OAuth检查是否有配置项可以切换到 API Key参考接入文档确认正确的认证方式对于 Claude Code 场景可以参考专门的接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。如果你需要长期跑编码类 Agent可以考虑 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。排查时有一个通用原则先确认单次请求能通再确认周期性任务能通。单次请求可以用 curl 或模型对话页面验证周期性任务则要看 CronService 的日志和 JSON 状态文件。两者分开排查能快速定位问题在哪一层。6. 把周期性执行接入你的 Agent 工作流Nanobot 的 CronService 设计里有一个值得借鉴的点它把何时执行和执行什么解耦了。CronService 只负责在正确的时间触发on_job回调具体执行逻辑由外部定义。这种解耦让调度器可以复用在不同的 Agent 场景里。在实际使用中你可以把周期性执行能力接入到这些场景定时巡检服务状态、周期性同步数据、定时生成报告、工作日提醒等。每个场景的核心都是同一个模式注册任务、等待触发、执行回调、记录状态。如果你想让 Agent 长期跑这些周期性任务建议用 Coding Plan 来管理通道和额度https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。它比单次 API 调用更适合长期运行的 Agent 场景。最后给一个实用技巧在注册周期性任务时先用较短的间隔比如 60 秒测试确认链路跑通后再改成实际需要的间隔。这样能快速发现问题避免等很久才看到错误。任务跑通后记得检查 JSON 状态文件里的lastStatus和nextRunAtMs确认任务在按预期执行。