写在前面:为什么做这个?
每个周一早上,我都要花 20-30 分钟 做同一件事——打开 Git 仓库,翻提交记录,回忆上周干了啥,然后拼凑成周报发给领导。
不是周报多难写,是这件事太机械、太重复、太浪费脑力了。
作为一个 8 年经验的程序员,我决定用自己最擅长的方式解决它:写一个 Agent,让它自动帮我搞定。
这不是什么高大上的项目,它就是一个 Go 程序,不到 400 行代码。但它是我的第一个真正"跑起来"的 AI Agent 工作流——从数据采集到 AI 生成再到自动推送,全程无人值守。
今天我把整个过程开源出来,包括完整的代码和部署步骤。如果你也想用 AI 自动化一些重复劳动,这篇文章就是一份可以照抄的作业。
一、整体架构:三步走
先看全局,不迷路:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ CNB API │ ──→ │ DeepSeek │ ──→ │ 企业微信 │
│ 采集提交记录 │ │ AI 生成周报 │ │ 推送消息 │
└─────────────┘ └─────────────┘ └─────────────┘
Step 1 Step 2 Step 3
三步,就这么简单:
| 步骤 | 做什么 | 技术 |
|---|---|---|
| Step 1 | 从代码仓库拉取本周所有 commit 记录 | CNB(类似 GitHub)REST API |
| Step 2 | 把原始提交记录丢给 AI,让它整理成专业周报 | DeepSeek Chat API |
| Step 3 | 把生成的周报推送到企业微信群 | 企业微信 Webhook |
整个流程跑下来大概 3 秒。比我自己写快了 10 倍,而且不会漏。
二、技术选型:为什么是 Go?
说实话,这个项目用 Python 也行,用 Node.js 也行。但我选 Go 的原因很务实:
- 单文件部署:
go build出来一个二进制,扔服务器上就能跑,不用装 Python 环境、不用管依赖 - 定时任务内置:Go 的
cron库一行代码搞定定时执行,不用额外配 crontab 或 Windows 计划任务 - 并发友好:以后要扩展多仓库、多团队,goroutine 天然支持
核心依赖就两个:
import (
"github.com/robfig/cron/v3" // 定时任务
)
对,你没看错,HTTP 请求用的标准库 net/http,JSON 处理用的标准库 encoding/json。零第三方依赖(除了 cron)。
三、Step 1:数据采集——从仓库拉取 Commit
3.1 配置设计
程序通过环境变量配置,.env 文件长这样:
# DeepSeek API(AI 生成周报用)
DEEPSEEK_API_KEY=sk-xxxxx
DEEPSEEK_MODEL=deepseek-chat
# CNB Token(代码托管平台 API 认证)
CNB_TOKEN=your-token
# 要监控的仓库列表(支持多个)
CNB_REPOS=org/repo1,org/repo2
# 企业微信机器人 Webhook
WECHATWORK_WEBHOOK=https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx
# 定时任务:每周一早上 7:00 执行
CRON_SPEC=0 7 * * 1
加载配置的代码很简单:
type Config struct {
DeepSeekAPIKey string
DeepSeekModel string
CNBToken string
CNBRepos []string
WeChatWorkWebhook string
CronSpec string
}
func loadConfig() *Config {
return &Config{
DeepSeekAPIKey: getEnv("DEEPSEEK_API_KEY", ""),
// ... 其他字段同理
}
}
3.2 调用 CNB 获取提交记录
核心逻辑:遍历配置的每个仓库,按日期范围逐天调用 API 拉取 PushEvent:
func fetchEventsFromCNB(cfg *Config, repo string, date string) ([]Commit, error) {
url := fmt.Sprintf("https://api.cnb.cool/events/%s/-/%s", repo, date)
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("Authorization", "Bearer "+cfg.CNBToken)
resp, err := http.DefaultClient.Do(req)
// ... 错误处理
// CNB 返回 JSONL 格式(每行一个 JSON 对象)
scanner := bufio.NewScanner(resp.Body)
for scanner.Scan() {
var evt CNBEvent
json.Unmarshal([]byte(scanner.Text()), &evt)
// 只处理 PushEvent(包含代码提交)
if evt.Type != "PushEvent" { continue }
for _, c := range evt.Payload.Commits {
commits = append(commits, Commit{
SHA: c.SHA,
Author: c.Author.Name,
Message: c.Message,
})
}
}
return commits, nil
}
几个关键点:
- 日期范围计算:默认取"上周一到上周五"(工作日)。周一早上跑,刚好覆盖上一周的完整工作日。
- 只取 PushEvent:CNB 的 Events API 会返回各种事件类型(创建分支、合并 PR 等),我们只关心 PushEvent,因为那才是真正的代码提交。
- 404 不算错:某一天没有提交时,CNB 返回 404 而非空数组。这是正常行为,跳过即可。
3.3 日期范围的坑
这里有个容易踩的坑——本周一的计算:
now := time.Now()
weekday := int(now.Weekday())
if weekday == 0 { weekday = 7 } // Go 里周日是 0,我们要转成 7
thisMonday := now.AddDate(0, 0, -(weekday-1)).Truncate(24 * time.Hour)
lastMonday := thisMonday.AddDate(0, 0, -7)
lastSunday := thisMonday.AddDate(0, 0, -1)
Go 的 time.Weekday() 返回周日=0、周一=1...周六=6。但我们的业务逻辑里一周应该从周一开始,所以需要做个转换。这个坑我调试了两次才对,记下来免得你踩。
四、Step 2:AI 生成周报——Prompt 是核心
原始的 commit 数据是这样的:
- init. (a1b2c3d)
- 总纲.md (e4f5g6h)
- 01.md (i7j8k9l)
- W1-个人故事开场.md (m0n1o2p)
- Update cover-w1-juejin.png (q3r4s5t)
直接发给领导肯定不行。我们需要 AI 做几件事:
- 合并同类项:同一个功能的多次提交合并成一条
- 去噪音:"init."、"fix"、"Update xxx" 这种无意义提交直接忽略
- 归类整理:按项目/模块分组
- 专业语气:像人写的周报,不像机器吐的日志
Prompt 设计
systemPrompt := `你是一个周报整理助手。把原始代码提交记录整理成简洁、专业的工作周报。
【核心原则】
1. 只基于原始数据归纳,绝对不能编造、推测、猜测
2. 合并同类项:同一功能的多轮调试提交,合并为一条
3. 去噪音:"1"、"fix"、"Update xxx.vue" 直接忽略
4. 删掉所有 Merge 相关的信息
【格式要求】
📊 周报(日期范围)
### 姓名
**项目名**(简要描述)
- 工作项 1
- 工作项 2
💡 建议
- 建议 1(针对本周数据的建设性意见)`
Prompt 设计的几点经验:
- 强调"不能编造":LLM 很容易"发挥",明明没有的功能它能给你编出来。必须明确约束
- 给示例:在 Prompt 里给出输入→输出的示例,比纯文字描述有效得多
- 加建议部分:让 AI 不仅整理周报,还给出改进建议(比如 commit message 太模糊),这样领导看了觉得"还有附加值"
调用 DeepSeek API
func generateReport(cfg *Config, rawData string) (string, error) {
reqBody := ChatRequest{
Model: cfg.DeepSeekModel,
Messages: []ChatMessage{
{Role: "system", Content: systemPrompt},
{Role: "user", Content: "以下是本周的代码仓库动态:\n" + rawData},
},
}
jsonBody, _ := json.Marshal(reqBody)
req, _ := http.NewRequest("POST",
"https://api.deepseek.com/v1/chat/completions",
bytes.NewReader(jsonBody))
req.Header.Set("Authorization", "Bearer "+cfg.DeepSeekAPIKey)
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
// ... 解析返回结果
return chatResp.Choices[0].Message.Content, nil
}
为什么选 DeepSeek?便宜。同样的任务,DeepSeek 的价格大概是 GPT-4 的 1/10。对于这种每天/每周跑一次的任务,成本几乎可以忽略不计。我这边的实际花费:生成一次周报约 0.01 元。
五、Step 3:推送到企业微信
企业微信的群机器人支持 Webhook 方式推送消息,非常简单:
func sendWeChatWorkMarkdown(webhook, markdown string) error {
msg := WeChatWorkMarkdownMsg{
MsgType: "markdown",
}
msg.Markdown.Content = markdown
jsonBody, _ := json.Marshal(msg)
resp, _ := http.Post(webhook, "application/json", bytes.NewReader(jsonBody))
// ... 检查返回的 errcode
return nil
}
企业微信支持 Markdown 格式,所以 AI 生成的周报可以直接渲染成带格式的消息:标题加粗、列表缩进、emoji 显示。
💡 如果你用飞书或钉钉,逻辑完全一样,只是 Webhook URL 和消息格式稍有不同。
六、定时执行:每周一自动跑
用 robfig/cron 库实现定时任务:
func main() {
loadDotEnv()
cfg := loadConfig()
// 支持立即执行模式:daily-report-agent now
if len(os.Args) > 1 && os.Args[1] == "now" {
runReport(cfg)
return
}
c := cron.New()
c.AddFunc(cfg.CronSpec, func() {
runReport(cfg)
})
c.Start()
log.Printf("🌱 周报 Agent 已启动,定时: %s", cfg.CronSpec)
select {} // 阻塞主 goroutine,保持程序运行
}
CRON_SPEC=0 7 * * 1 表示每周一早上 7:00 执行。上班路上,周报已经自己跑到群里了。
💡 如果你习惯周五写周报,可以改成
0 18 * * 5(每周五下午 6:00),但需要注意:当前代码的日期范围是固定取"上周一上周五",周五跑的话只到周四的数据。想覆盖到周五,需要改一下日期计算逻辑,取"本周一本周五"。这是一个简单的改造,欢迎 PR。
另外加了 now 参数支持手动触发,方便调试:
./daily-report-agent now # 立即执行一次
./daily-report-agent # 定时模式(阻塞运行)
七、实际效果
说了这么多,看看真实效果。这是企业微信群里收到的周报:
可以看到:
- ✅ 日期范围正确
- ✅ 按人名归类
- ✅ 提交记录经过 AI 整理,不再是原始的 commit message
- ✅ 末尾有💡建议部分,指出 commit message 可以更规范
从杂乱的 commit 列表 → 可读的专业周报,全靠中间这层 AI。
八、部署指南:5 分钟上手
前置准备
- 一个代码托管平台的账号和 API Token(CNB / GitHub / GitLab 都行)
- 一个 DeepSeek API Key(注册就送额度)
- 一个企业微信群机器人(群里添加机器人,复制 Webhook URL)
部署步骤
# 1. 克隆项目
git clone https://github.com/lobster-bujiaban/daily-report-agent.git
cd daily-report-agent
# 2. 配置 .env 文件
cp .env.example .env
# 编辑 .env,填入你的 API Key 和 Webhook
# 3. 编译(需要 Go 1.21+)
go build -o daily-report-agent .
测试运行
./daily-report-agent now
看到企业微信群里收到周报,说明一切正常。
长期运行
根据你的服务器系统选择部署方式:
Linux(推荐)
方式一:systemd 服务(最稳定)
创建 /etc/systemd/system/daily-report-agent.service:
[Unit]
Description=Daily Report Agent
After=network.target
[Service]
Type=simple
WorkingDirectory=/opt/daily-report-agent
ExecStart=/opt/daily-report-agent/daily-report-agent
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
启动并设置开机自启:
sudo systemctl daemon-reload
sudo systemctl enable daily-report-agent
sudo systemctl start daily-report-agent
# 查看运行状态
sudo systemctl status daily-report-agent
# 查看日志
sudo journalctl -u daily-report-agent -f
方式二:nohup(快速但不推荐生产环境)
nohup ./daily-report-agent > report.log 2>&1 &
方式三:crontab(如果不想用程序内置定时)
不启动程序的 cron 模式,直接用系统 crontab 调用 now 模式:
# 编辑 crontab
crontab -e
# 每周五下午 6 点执行
0 18 * * 5 cd /opt/daily-report-agent && ./daily-report-agent now >> /var/log/report-agent.log 2>&1
Windows
方式一:NSSM 注册为系统服务
# 下载 NSSM: https://nssm.cc/download
nssm install DailyReportAgent "C:\path\to\daily-report-agent.exe"
nssm set DailyReportAgent AppDirectory "C:\path\to"
nssm start DailyReportAgent
方式二:Windows 计划任务
schtasks /create /tn "DailyReportAgent" /tr "C:\path\to\daily-report-agent.exe now" /sc weekly /d FRI /st 18:00
Docker(跨平台通用)
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY . .
RUN go build -o daily-report-agent .
FROM alpine:latest
WORKDIR /app
COPY --from=builder /app/daily-report-agent .
COPY .env .
CMD ["./daily-report-agent"]
docker build -t daily-report-agent .
docker run -d --name report-agent --restart=always daily-report-agent
改造成你自己的
这个项目的架构是通用的,你可以轻松改造:
| 你想做的 | 改哪里 |
|---|---|
| 换代码平台 | 改 fetchEventsFromCNB 函数,适配 GitHub/GitLab API |
| 换 AI 模型 | 改 generateReport 的 API 地址和 Key,支持 OpenAI 兼容接口 |
| 换推送渠道 | 改 sendWeChatWorkMarkdown,对接飞书/钉钉/Slack |
| 加更多仓库 | .env 里 CNB_REPOS 逗号分隔添加即可 |
九、踩过的坑
写的过程中遇到几个问题,分享给你:
坑 1:CNB API 的 404 问题
某天没有提交记录时,CNB 返回 HTTP 404 而不是空数组。一开始我当错误处理了,导致日志里一堆 WARN。后来发现这是正常行为,改成静默跳过就好。
坑 2:Commit Message 的噪音
真实的 commit message 并不都像教科书一样规范。你会看到 "1"、"fix"、"12"、"update" 这种毫无意义的 message。如果不过滤,AI 生成的周报会很丑。所以在 Prompt 里加了明确的去噪规则。
坑 3:AI 编造内容
第一次跑的时候,AI 居然给我"编"了一个我没做的功能进去。后来在 Prompt 开头加了铁律:「绝对不能编造、推测、猜测」,并要求只基于传入的原始数据。之后就没再出过这个问题。
坑 4:时间计算的边界情况
当前代码默认取"上周一上周五",适合周一早上跑。但如果你改成周五跑,想取"本周一本周五"的数据,日期范围的计算逻辑就需要调整。这是不同执行时间带来的边界差异,改起来不难,但要意识到这个坑。
十、总结与源码
这个项目从想法到跑通,花了大约 一个周末。代码量不大,但它是我副业产线上的第一个"产品"。
它的意义不在于周报本身,而在于验证了一个流程:
数据采集 → AI 处理 → 自动推送
这个流程可以复用到很多场景:监控告警、日报汇总、竞品追踪……周报只是第一个。
📦 源码地址
GitHub - lobster-bujiaban/daily-report-agent
欢迎 Star、提 Issue、Fork 改造。如果你做了有趣的改造,欢迎 PR 或者给我发邮件交流。
写在最后
我是龙虾,一个 8 年程序员,正在用 AI Agent 搭建副业产线。
这不是一篇"教你赚大钱"的文章,这是一个普通程序员从 0 到 1 的实践记录。如果你觉得有用,点个「在看」就是最大的支持。
下周我会写一篇踩坑实录,详细记录做这个 Agent 过程中遇到的 5 个坑和解决方案。关注不迷路 👇
🦞 一只用 AI Agent 搭副业产线的程序员
公众号:虾哥不加班 | B站:龙虾不加班 | 掘金:龙虾不加班