跳到主内容
快讯直播
AI智模界
教程

用 AI 自动汇总进展,写周报月报

这篇能做出什么

做完之后,你会有一个能在本地跑的脚本:周五下班前跑一次,它自动读取你这周的 Git 提交记录和你每天随手写的流水账,交给大模型整理成一份结构化的中文周报,输出成 Markdown 文件。内容包含"本周进展 / 遇到的问题 / 下周计划"三段,进展按项目分组,每条以动词开头。月报则在周报基础上再汇总一次,不用你重新回忆一个月干了什么。

整个方案只有一个脚本文件加一个提示词,依赖极少,换成任何提供 OpenAI 兼容接口的模型服务都能用。

前置条件清单

在动手之前,确认这几件事:

1. Python 环境。Python 3 即可,安装方式和当前版本以 Python 官方文档为准。不需要装第三方库,全部用标准库实现。

2. 一个模型 API。任何提供 chat/completions 风格接口的服务都可以,需要拿到 API Key、接口地址(Base URL)和模型名称。具体型号和计费方式以服务方官方页面为准。

3. 工作记录的两类来源。一类是能自动抓的,比如 Git 仓库;另一类是手写的,比如每天的待办笔记。只有前者的话,模型只能看到"改了哪个文件",写不出"为什么改",所以手写记录这一步不建议省。

4. 基本的命令行能力。会 cd、会执行 python xxx.py 就够了。

整体思路

流程拆成三段,每段职责单一,出问题好定位:

  • 采集:把分散的来源(Git、笔记)拼成一份 raw.md 原始素材。
  • 生成:把 raw.md 塞进提示词模板,调用模型,得到周报正文。
  • 落地:输出到文件,必要时再人工过一遍。

分开的好处是,当周报质量不理想时,你能立刻判断是"素材不够"还是"提示词不好",而不是面对一团黑箱。

第一步:约定一个手写记录的格式

自动化能覆盖的部分有限,日常的沟通、评审、临时支援这些事,Git 里看不到。建议每天花一分钟写一行流水账,放在一个目录里,文件名用日期,内容用列表:

```markdown

2025-01-06

  • 把登录接口的超时改成可配置,线上偶发的 504 明显少了
  • 和产品确认导出功能的字段范围,先做 5 列
  • 帮运维排查了测试环境的磁盘告警,是日志没轮转

2025-01-07

  • 导出功能接口写完,本地自测通过
  • 上午开会讨论下季度排期,我们组先做数据看板

```

文件名 2025-01-06.md,放在 notes/ 目录下。格式越稳定,后面越好处理。不需要写得多好看,"做了什么 + 结果如何"就够。

如果所在环境不方便用 Markdown,用纯文本、每行一条也行,脚本里改一下读取逻辑即可。

第二步:写采集脚本

新建 collect.py

```python

#!/usr/bin/env python3

"""采集最近 N 天的工作素材,输出 raw.md"""

import subprocess

import datetime

import pathlib

REPO = pathlib.Path(".") # 改成你的 Git 仓库路径

NOTES = pathlib.Path("notes") # 流水账目录

DAYS = 7 # 采集窗口,周报用 7,月报用 31

def git_log(days):

since = (datetime.date.today() - datetime.timedelta(days=days)).isoformat()

cmd = [

"git", "log",

"--since=" + since,

"--no-merges",

"--date=short",

"--pretty=format:%ad %s",

]

try:

r = subprocess.run(cmd, cwd=REPO, capture_output=True, text=True, timeout=60)

except Exception as exc:

return "(读取 git 记录失败:%s)" % exc

if r.returncode != 0:

return "(git 命令返回错误:%s)" % r.stderr.strip()

return r.stdout.strip() or "(窗口内没有提交记录)"

def notes_text(days):

cutoff = datetime.date.today() - datetime.timedelta(days=days)

blocks = []

for p in sorted(NOTES.glob("*.md")):

try:

day = datetime.date.fromisoformat(p.stem)

except ValueError:

continue # 文件名不是日期的文件直接跳过

if day >= cutoff:

body = p.read_text(encoding="utf-8").strip()

blocks.append("### " + p.stem + "\n" + body)

return "\n\n".join(blocks) or "(没有找到手写记录)"

def main():

text = "\n\n".join([

"# 原始素材(最近 %d 天)" % DAYS,

"## Git 提交",

git_log(DAYS),

"## 手写工作记录",

notes_text(DAYS),

])

pathlib.Path("raw.md").write_text(text, encoding="utf-8")

print(text)

if __name__ == "__main__":

main()

```

几个细节说明:

  • --since 按提交日期过滤,配合 --no-merges 可以少掉合并提交带来的噪音。
  • %s 是提交标题,也可以换成 %s%n%b 把正文一并带出来,但素材会变长。
  • 如果只统计自己的提交,加一个 --author=你的名字或邮箱
  • 日期解析失败的文件名会被静默跳过,避免混进无关文件。

先手动跑一次,看 raw.md 内容对不对,再往下走。

第三步:写生成脚本

新建 generate.py,用标准库发请求,不装额外依赖:

```python

#!/usr/bin/env python3

"""读取 raw.md,调用模型生成周报"""

import os

import sys

import json

import time

import pathlib

import urllib.request

import urllib.error

def load_env(path=".env"):

"""从 .env 加载配置,适合 cron 这种拿不到 shell 变量的场景"""

p = pathlib.Path(path)

if not p.exists():

return

for line in p.read_text(encoding="utf-8").splitlines():

line = line.strip()

if not line or line.startswith("#") or "=" not in line:

continue

k, v = line.split("=", 1)

os.environ.setdefault(k.strip(), v.strip().strip('"').strip("'"))

load_env()

API_BASE = os.environ.get("LLM_BASE_URL", "").rstrip("/")

API_KEY = os.environ.get("LLM_API_KEY", "")

MODEL = os.environ.get("LLM_MODEL", "")

if not (API_BASE and API_KEY and MODEL):

sys.exit("请先在 .env 里配置 LLM_BASE_URL / LLM_API_KEY / LLM_MODEL")

SYSTEM = "你是一名严谨的中文工作助理,只根据给定素材写周报,不补充素材之外的事实。"

PROMPT = """下面是我最近一段时间的工作素材,包含 Git 提交和手写记录。

请整理成一份中文周报,要求:

1. 分为三段:本周进展、遇到的问题、下周计划。

2. "本周进展"按项目或模块分组,每条以动词开头,写清"做了什么、带来什么结果"。

3. 只使用素材中出现的事实,不要编造数字、人名、客户名、日期。

4. 素材里信息不足以判断结果的条目,在该条末尾标注"(待补充)"。

5. 语言平实,不用"赋能""闭环""抓手"这类空话。

6. 直接输出 Markdown 正文,不要写"以下是为您整理的周报"之类的开场白。

素材如下:

---

%s

---

"""

def call_llm(prompt, retries=4):

body = json.dumps({

"model": MODEL,

"messages": [

{"role": "system", "content": SYSTEM},

{"role": "user", "content": prompt},

],

"temperature": 0.3,

}).encode("utf-8")

req = urllib.request.Request(

API_BASE + "/chat/completions",

data=body,

headers={

"Content-Type": "application/json",

"Authorization": "Bearer " + API_KEY,

},

)

for i in range(retries):

try:

with urllib.request.urlopen(req, timeout=180) as resp:

data = json.loads(resp.read().decode("utf-8"))

return data["choices"][0]["message"]["content"].strip()

except urllib.error.HTTPError as exc:

detail = exc.read().decode("utf-8", "ignore")[:500]

if exc.code in (429, 500, 502, 503) and i < retries - 1:

time.sleep(2 ** i * 2) # 简单的指数退避

continue

raise SystemExit("接口返回 %s:%s" % (exc.code, detail))

except urllib.error.URLError as exc:

if i < retries - 1:

time.sleep(2 ** i * 2)

continue

raise SystemExit("网络错误:%s" % exc)

def main():

raw = pathlib.Path("raw.md").read_text(encoding="utf-8")

report = call_llm(PROMPT % raw)

pathlib.Path("weekly.md").write_text(report, encoding="utf-8")

print(report)

if __name__ == "__main__":

main()

```

同目录新建 .env(记得加进 .gitignore,不要提交):

```ini

LLM_BASE_URL=https://你的服务地址/v1

LLM_API_KEY=你的密钥

LLM_MODEL=你的模型名

```

然后依次执行:

```bash

python collect.py

python generate.py

```

打开 weekly.md 看结果。第一版通常会有几个常见问题,见后面的排错部分。

第四步:做成一条命令

把两步串起来,写个 run.sh

```bash

#!/usr/bin/env bash

set -e

cd "$(dirname "$0")"

python3 collect.py > /dev/null

python3 generate.py

```

在 Linux 或 macOS 上,用 cron 每周五下午六点自动跑:

```cron

0 18 * * 5 /bin/bash /home/me/weekly/run.sh >> /home/me/weekly/cron.log 2>&1

```

Windows 上可以用任务计划程序,或者命令行注册:

```bat

schtasks /create /tn "WeeklyReport" /tr "python C:\weekly\generate.py" /sc weekly /d FRI /st 18:00

```

macOS 也可以考虑 launchd,配置方式以 Apple 官方文档为准。

第五步:做月报

月报不需要另写一套采集逻辑,把窗口拉长、素材换成四份周报即可。最省事的做法是把本月生成的 weekly.md 按月归档到一个目录,月报脚本读这个目录,提示词改成"把这些周报归纳成月报,按主题合并同类项,突出阶段性成果与遗留问题"。主题合并是月报相对周报的主要增量:周报里分散在三周的"登录优化""超时改造""告警清理",在月报里应该合成一条"服务稳定性改善"。

常见坑与排错

接口返回 401 或 403。 多半是密钥没读到。脚本从 .env 读取就是为这个场景准备的;如果坚持用环境变量,注意 cron 不会继承你登录 shell 里的变量,需要在 crontab 里显式声明,或者干脆写进 .env。另外确认 Base URL 结尾有没有多余的斜杠,脚本里已经做了 rstrip("/"),但路径前缀要看服务方的接口文档。

接口返回 429。 触发了频率限制。脚本里已经有指数退避重试,如果仍然频繁遇到,把采集窗口调小、素材压缩,或者把任务挪到低峰时段。

周报内容空洞。 通常是素材问题,不是提示词问题。打开 raw.md 看一眼,如果 Git 提交全是"fix""update""修改",手写记录又是空白,模型只能照抄这些词。解决办法是提高素材质量:提交信息用"动作 + 对象 + 结果"的写法,手写记录里补上会议结论和沟通结果。

出现了素材里没有的数字或人名。 这是模型自由发挥。除了提示词里明确禁止,更可靠的做法是在生成后人工核对一遍所有数字和专有名词。周报是要发给同事和上级看的,事实错误比语言平淡严重得多。

时间窗口错位。 --since 基于提交时间,如果你做过 rebase 或者 cherry-pick,旧提交可能以新时间出现,导致上周的事重复进本周周报。遇到这种情况,用 --author-date-order 或者改用手写记录作为主素材,Git 记录作为参考。

素材太长超出上下文。 一个月几百条提交拼接后可能很长。可以在 collect.py 里按目录或按项目分组,每组只保留前若干条;或者先用一次模型调用对素材做压缩,再做正式生成。

输出带了开场白。 比如"好的,以下是您的周报"。提示词里已经要求直接输出正文,如果模型仍不听话,加两行代码:找到第一个 ## 的位置,截取之后的内容。

敏感信息外泄。 提交信息里可能带内网地址、客户名、密钥片段。生成前加一层替换,把关键词对照表里的词替换成代号;或者在 .env 里配置一个黑名单,脚本读到就整行剔除。对外发送前自己通读一遍,这一步不要省。

下一步建议

加一个"上周计划"的闭环。 把上一份周报里"下周计划"那一段自动提取出来,作为本周素材的一部分喂进去,让模型对照检查哪些做完了、哪些没动。这样周报就从"流水账"变成了"有交代的进展汇报"。

沉淀自己的提示词。 连续跑几周,记录每次生成后你手动改了多少。改得多的部分,说明提示词里缺少对应的约束,把那条约束补进去。这比一次性写好提示词有效。

接上分发渠道。 生成后自动发到团队群或者邮件。这一步涉及各平台的接口权限,按对应平台的官方文档来配置。

处理更敏感的内容时换本地模型。 如果素材涉及不方便外发的信息,可以在本机跑一个开源模型,把 LLM_BASE_URL 指向本地服务,脚本其余部分不用改。

把月报做成季度总结的上游。 同理,三个月的月报可以喂进去生成季度总结,链条越长,单次投入越省。

AI 生成本文由 AI 基于公开信息自动生成,仅供参考。