
如果你在用 DeepSeek API 做批量任务大概率已经体会过一种微妙的心态白天跑任务费用肉眼可见地涨夜里或者某些优惠时段跑同样的任务成本曲线会平缓很多。于是“什么时候跑任务”这件事慢慢从一个调度问题变成了一个成本问题。最近社区里有个挺有意思的梗有人把 DeepSeek 创始人梁文峰的名字和“谷价”玩在一起戏称周末低价时段为“梁文谷”。梗归梗它背后是一个真实需求很多开发者希望系统能自动感知 DeepSeek 的降价时段并且在周末这类“谷价窗口”出现时第一时间通知自己而不是每天手动刷新价格页面、在群里追问“今天降价了吗”。这篇文章就围绕这个需求写一个轻量、可上手的自动化方案Python 定时任务 邮件通知。核心目标是帮你建立一套“时段感知 自动触达”的机制脚本每天定时检查当天是否为周末、是否处于降价时段如果条件满足就自动发送邮件通知。读完你可以直接部署一个最小可用版本也可以把它扩展成价格历史记录、企业微信通知、甚至自动触发批量任务的小系统。先说我的判断这类工具的价值不在于省下多少钱而在于把“盯价格”这种低价值的人力操作彻底自动化同时帮你建立一套可复用的 API 成本监控思路。DeepSeek 官方确实会调整 API 价格具体的周末是否降价、降多少以开放平台页面和公告为准本文不编造任何官方数字只把项目需求假设讲清楚。1. 为什么要做“周末降价邮件通知”先看一个典型场景。你维护着一个数据清洗服务每天晚上通过 DeepSeek API 处理几万条文本比如给评论打标签、做实体抽取、生成摘要。这种任务对实时性要求不高但对成本很敏感。API 的计费方式通常是按 token 计算同样一批数据在高峰期跑和错峰跑最终账单可能差出不少。问题来了你怎么知道什么时候是“谷价时段”大多数开发者目前的做法是每天手动打开 DeepSeek 开放平台看一下价格页有没有变化关注官方公告和社群消息靠别人转发才知道价格调整凭经验挑某个时间段跑任务但并不知道那个时间是否真的是优惠窗口。这些做法有一个共同问题不可持续。你不可能每天定时去刷网页也不可能永远靠群消息获取信息。尤其当你的任务调度窗口是凌晨或者周末时人工盯梢的成本已经超过了省下来的 API 费用。邮件通知脚本解决的就是这个问题。它把“检查价格 → 判断时段 → 发出通知”的完整链路自动化。脚本每天定时执行周末降价时段一到你的邮箱里就会收到一封明确的通知今天属于降价时段适合把批量任务安排进来。什么人最需要这个方案使用 DeepSeek API 做批量处理的开发者对 token 费用敏感维护自动化任务调度系统的工程师希望把耗时任务放到成本更低的时段想学习 Python 定时任务、SMTP 邮件发送、环境变量管理的后端初学者。这篇文章不做复杂的分布式系统设计目标是用一个周末即可跑通的最小项目覆盖从环境准备到部署运维的完整链路。2. DeepSeek API 价格与成本监控基本概念2.1 DeepSeek API 怎么计费DeepSeek 是深度求索公司推出的大语言模型开发者可以通过官方 API 调用模型能力。API 计费通常按 token 数量计算也就是模型处理的文本单元数。一次完整的请求会包含两部分输入 token你发送给模型的提示词、上下文、历史消息输出 token模型生成的结果内容。价格一般会区分输入价格和输出价格部分模型还会对命中了上下文缓存的请求给出更低的优惠价格。具体数值会因为模型版本、计费策略调整而变化所以不要在任何代码里写死价格数字应该做成可配置项。2.2 价格变动与“谷价时段”从行业惯例来看模型 API 的价格并不是永远不变的。厂商会根据运营成本、算力资源空闲情况、市场竞争等因素调整价格。有些厂商会推出闲时优惠鼓励开发者在低峰时段跑任务有些则是直接调整基础定价。DeepSeek 的价格调整同样以官方公告为准。本文提到的“周末全天降价”属于项目需求假设也就是说我们假设存在一个降价时段希望脚本在这个假设成立时自动发送通知。真正的判断条件可以是“当天是周六或周日”也可以是“官方公告中明确说明的优惠时段”。代码层面只需要把判断逻辑抽象出来后面官方政策怎么变你改一个函数就行。2.3 为什么需要自动化监控人工监控的最大问题是延迟和遗忘。价格变化不会等你盯着页面才发生而批量任务通常是在固定时间点跑的。如果没有自动化监控你可能直到月底看账单时才意识到原来上周末是降价时段而你把任务全排在了工作日的价格高位。自动化监控的价值体现在三处及时性脚本定时执行条件一旦满足就触发通知可追溯每次检查都留下日志方便回头看价格变化趋势可执行收到邮件后你可以立刻调整下一步的批量任务计划。2.4 项目需求拆解把“周末降价邮件通知”拆开核心功能只有三个判断当天是否为周末周六或周日判断当前是否处于降价时段可以结合价格数据或固定时间窗口如果条件满足发送邮件通知到指定邮箱。三者组装起来就是一个最小可用的价格监控提醒系统。下面用一个表格对比手动方案和脚本方案对比维度手动方案脚本方案检查频率靠人工记忆不稳定定时任务精确到分钟时效性信息滞后容易错过条件触发立即通知成本人力成本高服务器成本可忽略可扩展性无法积累历史数据可以记录日志统计价格变化出错率容易漏看、忘看逻辑固定前提是配置正确3. 项目设计与技术选型3.1 整体模块划分项目按职责划分为四个模块配置模块管理邮箱账号、收件人、API Key、模型价格等参数价格获取模块尝试从官网或本地配置获取当前价格作为降价判断的依据通知模块通过 SMTP 协议发送邮件调度模块控制脚本的执行频率既支持 cron 外部调度也支持进程内定时器。模块之间通过函数调用连接不引入复杂框架。整个项目只有 4 到 5 个 Python 文件方便阅读和修改。3.2 为什么选 PythonPython 是这类脚本任务最合适的选择原因有三smtplib、email、datetime 都是标准库不需要额外安装第三方库生态成熟apscheduler、requests、python-dotenv 等工具开箱即用代码可读性好后续扩展为企业微信通知、钉钉机器人、数据库写入都很方便。3.3 为什么不用复杂的调度框架项目初期不需要 Celery、Airflow 这类重量级调度框架。原因很简单我们的任务只有一个文件、一个函数cron 或者 APScheduler 已经足够。等哪天需要管理几十个任务、依赖关系、失败重试、分布式执行时再考虑升级到更专业的调度系统也不迟。过早引入复杂框架只会让一个 200 行就能解决的问题膨胀到几千行。4. 环境准备与前置条件4.1 运行环境本项目使用 Python 3.8 及以上版本操作系统不限Linux、macOS、Windows 都可以。如果你打算用 cron 做定时调度推荐使用 Linux 服务器如果只是本地测试Windows 和 macOS 都能跑通。先确认 Python 版本python3 --version建议使用虚拟环境避免污染系统 Pythonpython3 -m venv venv source venv/bin/activateWindows 下激活命令为venv\Scripts\activate4.2 安装依赖创建requirements.txtrequests2.32.3 APScheduler3.10.4 python-dotenv1.0.1安装方式pip install -r requirements.txt如果只是本地测试甚至可以只用标准库。本文为了演示真实项目的目录结构和依赖管理保留 requests 和 APScheduler但核心逻辑并不强依赖它们。4.3 邮箱 SMTP 准备邮件通知通过 SMTP 发送。以 QQ 邮箱为例登录 QQ 邮箱网页版进入“设置 → 账号”找到“POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV 服务”开启 SMTP 服务并按提示生成授权码。注意这里生成的是授权码不是你的 QQ 密码。授权码是客户端专用密码即使泄露也不会让你丢失邮箱账号但依然需要妥善保管。163 邮箱、Gmail、Outlook 的配置方式类似区别在于 SMTP 服务器地址和端口。如果不确定可以搜索对应邮箱的官方 SMTP 配置说明。4.4 DeepSeek API Key可选如果后续要接入真实价格接口你需要在 DeepSeek 开放平台注册并创建 API Key。本项目的价格获取模块做了配置化兜底即使没有 API Key也能读取本地配置文件中的价格数据完成演示。请把 API Key 放在环境变量或者.env文件中不要硬编码到源码里。5. 核心代码实现与定时调度5.1 项目目录结构deepseek-price-watcher/ ├── .env.example # 环境变量模板 ├── requirements.txt # 依赖清单 ├── config.py # 配置读取 ├── price_fetcher.py # 价格获取 ├── notifier.py # 邮件通知 ├── scheduler.py # 主流程与调度 └── README.md # 项目说明5.2 环境变量模板创建.env.example# SMTP 配置 SMTP_HOSTsmtp.qq.com SMTP_PORT465 SMTP_USERyour_emailqq.com SMTP_AUTH_CODEyour_auth_code MAIL_TOtargetexample.com # DeepSeek 配置可选 DEEPSEEK_API_KEY DEEPSEEK_PRICE_API_URL # 通知开关 NOTIFY_ON_WEEKENDtrue实际使用时复制一份为.env填入真实配置。5.3 配置读取模块创建config.pyimport os from dotenv import load_dotenv load_dotenv() class Config: SMTP_HOST os.getenv(SMTP_HOST, smtp.qq.com) SMTP_PORT int(os.getenv(SMTP_PORT, 465)) SMTP_USER os.getenv(SMTP_USER, ) SMTP_AUTH_CODE os.getenv(SMTP_AUTH_CODE, ) MAIL_TO os.getenv(MAIL_TO, ) DEEPSEEK_API_KEY os.getenv(DEEPSEEK_API_KEY, ) DEEPSEEK_PRICE_API_URL os.getenv(DEEPSEEK_PRICE_API_URL, ) NOTIFY_ON_WEEKEND os.getenv(NOTIFY_ON_WEEKEND, true).lower() true这个模块的作用是统一读取环境变量避免在业务代码里到处散落配置项。以后新增配置只需要在这个类里加一个字段。5.4 价格获取模块创建price_fetcher.pyimport json from typing import Dict, Optional def fetch_price_from_api() - Optional[Dict]: 从 DeepSeek 开放平台获取价格。 注意官方价格接口可能变动请以官方文档为准。 如果调用失败返回 None让上层回退到本地配置。 # 这里省略真实 HTTP 请求逻辑因为接口地址需要按官方文档实现。 # 建议优先从官方开放平台文档中查找价格查询接口。 return None def fetch_price_from_config() - Dict: 从本地配置读取价格作为兜底方案。 return { model: deepseek-chat, input_price_per_million: 1.0, # 示例值单位元/百万 tokens output_price_per_million: 2.0, # 示例值单位元/百万 tokens note: 价格以 DeepSeek 开放平台公告为准, } def get_current_price() - Dict: 统一入口优先请求官方接口失败后回退本地配置。 price fetch_price_from_api() if price: return price return fetch_price_from_config()为什么价格不写死因为 DeepSeek 的价格可能调整。示例代码里的 1.0 和 2.0 是占位演示值不是官方真实价格部署时你需要改成自己的配置。如果后续官方提供了稳定的价格查询 API只需要实现fetch_price_from_api()函数其他代码不用动。5.5 邮件通知模块创建notifier.pyimport smtplib from email.mime.text import MIMEText from email.header import Header def send_email(config, subject: str, body: str) - bool: 通过 SMTP 发送邮件发送成功返回 True。 msg MIMEText(body, plain, utf-8) msg[Subject] Header(subject, utf-8) msg[From] config.SMTP_USER msg[To] config.MAIL_TO try: if config.SMTP_PORT 465: server smtplib.SMTP_SSL(config.SMTP_HOST, config.SMTP_PORT, timeout10) else: server smtplib.SMTP(config.SMTP_HOST, config.SMTP_PORT, timeout10) server.starttls() server.login(config.SMTP_USER, config.SMTP_AUTH_CODE) server.sendmail(config.SMTP_USER, [config.MAIL_TO], msg.as_string()) server.quit() return True except Exception as e: print(f邮件发送失败: {e}) return False这段代码兼容了 465 端口SSL和 587 端口STARTTLS两种常见方式。如果你的邮箱服务商要求其他端口修改SMTP_PORT环境变量即可。5.6 主流程与周末判断创建scheduler.pyimport datetime from config import Config from price_fetcher import get_current_price from notifier import send_email def is_weekend(dt: datetime.datetime) - bool: 判断是否为周末。datetime.weekday()周一0周六5周日6。 return dt.weekday() 5 def build_notification_body(price: dict, dt: datetime.datetime) - str: 构造邮件正文。 return ( f检测时间{dt.strftime(%Y-%m-%d %H:%M:%S)}\n f今天是{周末 if is_weekend(dt) else 工作日}\n f当前模型{price.get(model, unknown)}\n f输入价格{price.get(input_price_per_million)} 元/百万 tokens\n f输出价格{price.get(output_price_per_million)} 元/百万 tokens\n f备注{price.get(note, )}\n ) def check_and_notify() - None: 核心逻辑判断是否在降价时段如果是就发邮件。 config Config() now datetime.datetime.now() # 将“是否周末”作为可配置的降价判定条件。 # 如果官方政策从“周末降价”变成“闲时降价” # 只需要修改这里的判断函数。 if config.NOTIFY_ON_WEEKEND and not is_weekend(now): print(f[{now}] 今天是工作日不满足通知条件。) return price get_current_price() body build_notification_body(price, now) subject 【DeepSeek 周末降价提醒】现在是谷价时段适合安排批量任务 success send_email(config, subject, body) if success: print(f[{now}] 通知邮件发送成功收件人{config.MAIL_TO}) else: print(f[{now}] 通知邮件发送失败请检查 SMTP 配置和日志。)这个文件是整个项目的核心入口。它先判断当前时间是否满足条件再拉取价格信息并发送邮件。5.7 使用 APScheduler 定时执行check_and_notify()默认只是一次性运行。如果希望脚本长期运行并在每天固定时间自动触发可以在scheduler.py末尾加上 APScheduler 配置from apscheduler.schedulers.blocking import BlockingScheduler def start_scheduler() - None: scheduler BlockingScheduler(timezoneAsia/Shanghai) # 每天早上 8 点检查一次 scheduler.add_job(check_and_notify, cron, hour8, minute0) # 如果担心错过可以在晚上 20 点再检查一次 scheduler.add_job(check_and_notify, cron, hour20, minute0) scheduler.start() if __name__ __main__: # 直接运行一次便于验证 check_and_notify()如果你只是想部署一个系统级定时任务也可以不启动 APScheduler而是用下面的 cron 方案。两种方式选一种即可。6. 定时任务部署方式6.1 方式一Linux cron如果你的脚本只是每天早上跑一次用 cron 是最简单的方式。先给脚本添加可执行权限chmod x scheduler.py然后编辑 crontabcrontab -e添加一行0 8 * * * cd /path/to/deepseek-price-watcher /usr/bin/python3 scheduler.py logs/price_watcher.log 21这行配置的含义是每天早上 8 点进入项目目录执行scheduler.py并把标准输出和错误输出追加到日志文件。注意两点/usr/bin/python3是 Python 的绝对路径建议用which python3查看cron 使用的时区是系统时区。如果服务器时区不是北京时间判断“周末”时可能出现偏差。建议在脚本里用Asia/Shanghai显式处理时区。在项目根目录创建日志目录mkdir -p logs6.2 方式二GitHub Actions 定时运行如果你不想维护服务器可以用 GitHub Actions 的定时任务。创建一个工作流文件.github/workflows/price-watcher.ymlname: DeepSeek Price Watcher on: schedule: # UTC 时间 00:00 对应北京时间 08:00 - cron: 0 0 * * * workflow_dispatch: jobs: check-price: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkoutv4 - name: Setup Python uses: actions/setup-pythonv5 with: python-version: 3.11 - name: Install dependencies run: | pip install -r requirements.txt - name: Run price watcher env: SMTP_HOST: ${{ secrets.SMTP_HOST }} SMTP_PORT: ${{ secrets.SMTP_PORT }} SMTP_USER: ${{ secrets.SMTP_USER }} SMTP_AUTH_CODE: ${{ secrets.SMTP_AUTH_CODE }} MAIL_TO: ${{ secrets.MAIL_TO }} run: | python scheduler.py使用 GitHub Actions 时你需要把邮箱 SMTP 配置添加到仓库的 Secrets 中而不是提交到代码仓库。具体路径是仓库 Settings → Secrets and variables → Actions → New repository secret。GitHub Actions 的 schedule 使用 UTC 时区北京时间 8 点对应 UTC 0 点所以 cron 写成0 0 * * *。如果想在多个时间点执行就写多条 cron 表达式。6.3 方式三服务器长期运行如果你想一直保持脚本进程可以用 APScheduler 的 blocking 模式。需要配合 systemd 或 nohup 保证进程在退出登录后继续运行。简单做法nohup python scheduler.py logs/price_watcher_apscheduler.log 21 但 nohup 方式不够规范进程重启后不会自动拉起。生产环境建议使用 systemd service 管理这里不展开属于运维范畴。6.4 时区问题定时邮件通知最隐蔽的坑就是时区。服务器默认时区可能是 UTC也可能是其他地区。如果你使用datetime.datetime.now()获取时间得到的是服务器本地时间而不是你期望的北京时间。在 Linux 服务器上可以先查看当前时区date如果时区不对可以在启动命令前指定环境变量TZAsia/Shanghai python scheduler.py或者在 Python 代码里统一使用zoneinfofrom zoneinfo import ZoneInfo from datetime import datetime now datetime.now(ZoneInfo(Asia/Shanghai))Python 3.9 及以上版本支持zoneinfo。这样无论服务器在哪个地区判断出来的“周末”都是北京时间。7. 运行结果与效果验证7.1 直接运行一次在项目目录下执行python scheduler.py如果当天是工作日输出类似[2025-02-24 08:00:00] 今天是工作日不满足通知条件。如果当天是周末输出类似[2025-02-23 08:00:00] 今天是周末。 [2025-02-23 08:00:00] 通知邮件发送成功收件人targetexample.com这里需要注意如果你在非周末做测试又不想等周末可以临时修改.env中的NOTIFY_ON_WEEKENDfalse或者写一个单测强制把日期改成周六。7.2 预期邮件内容收到的邮件正文大致如下检测时间2025-02-23 08:00:00 今天是周末 当前模型deepseek-chat 输入价格1.0 元/百万 tokens 输出价格2.0 元/百万 tokens 备注价格以 DeepSeek 开放平台公告为准邮件标题【DeepSeek 周末降价提醒】现在是谷价时段适合安排批量任务7.3 判断成功的标准脚本运行成功后可以通过三方面确认日志中有通知邮件发送成功收件邮箱确实收到了邮件邮件内容和当前日期、价格配置一致。如果发送失败优先检查 SMTP 配置。最常见的错误是授权码填写错误或者 SMTP 端口和加密方式不匹配。8. 常见问题与排查思路问题现象可能原因排查方式解决方案SMTP 认证失败授权码错误或未开启 SMTP 服务查看错误日志中的认证报错重新生成授权码确认 SMTP 服务已开启邮件发送成功但收不到被邮箱服务商拦截或进入垃圾箱检查垃圾箱尝试更换邮件标题将发件邮箱加入联系人白名单周末判断不准服务器时区不是北京时间执行date查看系统时间用zoneinfo指定Asia/Shanghaicron 任务不触发cron 表达式错误或路径不对查看/var/log/cron日志用绝对路径在 crontab 中重定向日志GitHub Actions 不触发cron 使用 UTC 时区检查 Actions 的 workflow 运行历史将北京时间减去 8 小时换算成 UTC重复收到邮件脚本被重复部署或被 cron 和 APScheduler 同时调度检查是否有多个进程在运行统一调度方式增加状态文件去重API Key 泄露密钥被硬编码并提交到 Git 仓库检查 Git 历史撤销旧 Key改用环境变量和 secrets9. 最佳实践与工程建议9.1 密钥管理与安全SMTP 授权码和 DeepSeek API Key 都属于敏感信息必须遵循最小权限原则使用.env文件保存本地开发配置并在.gitignore中忽略它部署到 GitHub Actions 时使用仓库 Secrets不要在代码、日志、评论区贴出任何真实 Key如果怀疑 Key 泄露立即在开放平台撤销并重新生成。9.2 日志与可观测性邮件通知脚本虽然小但生产环境一定要有日志。至少输出以下信息检查时间是否满足通知条件价格数据来源邮件发送结果。建议使用标准logging模块把日志同时输出到控制台和文件import logging logging.basicConfig( levellogging.INFO, format%(asctime)s %(levelname)s %(message)s, handlers[ logging.StreamHandler(), logging.FileHandler(logs/price_watcher.log, encodingutf-8), ], )9.3 失败重试与退避SMTP 发送邮件偶尔会失败比如网络波动或邮箱服务商限流。简单做法是失败后重试两次重试间隔 5 到 10 秒。不要无限重试也不要高频重试否则可能被服务商判定为异常行为。9.4 通知去重如果脚本每天检查两次而周末持续两整天你可能会收到多封内容相似的邮件。为了避免打扰可以增加一个状态文件LAST_NOTIFY_DATE_FILE last_notify_date.txt def should_notify_today(today: str) - bool: try: with open(LAST_NOTIFY_DATE_FILE, r) as f: return f.read().strip() ! today except FileNotFoundError: return True每天只发一封避免同一时段重复通知。9.5 价格数据源验证如果后续接入了官方价格接口一定要在代码里对返回数据做校验防止接口返回异常数据结构导致脚本崩溃。校验字段存在性、数值范围并设置超时时间。9.6 成本控制与预算提醒邮件通知只是帮你感知降价时段。真正要控制成本还需要结合任务量做预算评估。建议在通知正文中附上“按当前价格跑 100 万 tokens 的预估费用”这样收到邮件时就能快速估算本周末可以排多少任务。10. 总结与后续扩展方向这个项目本身很小但它把几个常用的技术点串在了一起Python 环境变量管理、SMTP 邮件发送、定时任务调度、时区处理、GitHub Actions Secrets。跑通一遍之后你会对“监控某个外部状态 → 触发通知”的通用模式有比较清晰的理解。建议先在本机用一个测试账号跑通一次确认邮件能收到再部署到服务器或 GitHub Actions。等到通知链路稳定了再考虑下一步扩展接入企业微信机器人、钉钉、Slack Webhook把通知从邮件扩展到即时通讯把每次检查的价格和日期写入 SQLite 或文件积累历史数据用图表展示价格变化趋势帮助判断哪些时段性价比最高把“通知”升级为“执行”收到提醒后自动触发预先定义好的批量任务真正实现错峰自动执行。但这里有一个重要的提醒不要一上来就做“自动执行”。先保证人的通知链路足够可靠再去考虑机器的执行链路。否则你会在某天早上看到一封“任务已自动执行”的邮件而完全不知道它为什么触发、执行得对不对。先把“知道什么时候该做什么”这件事做扎实自动化才有意义。