每天记几行任务,周末让 AI 帮你写成周报。全程本地跑,不传数据,不花钱,格式还能自定义。
你有没有这种经历:周五下午快下班了,领导突然说"周报发一下"。
你打开笔记软件,翻了好几天的记录,发现有的写了、有的没写、有的只记了关键词。硬着头皮凑了三百字发出去,心里清楚这周报写得很水。
下周还这样。
今天做一个本地周报生成器。每天随手记几行任务,周末跑一个脚本,AI 自动帮你写成结构清晰的周报。数据完全在本地,不用上传任何内容。
项目背景
周报是打工人的常规动作,但写好一份不容易:
- 每天的事零零碎碎,汇总时理不清重点
- 写得太细像流水账,写得太略又像没干活
- 用在线 AI 写周报,工作内容传到了别人服务器上
- 每周重复写同样的结构,浪费时间
核心痛点就一个:记录太散,汇总太累。
这个项目的思路是:把"记录"和"写周报"两件事分开。每天花 30 秒记几行,周末让 AI 帮你汇总、提炼、排版。
技术选型
| 组件 | 选择 | 理由 |
|---|---|---|
| 任务记录 | JSON 文件 | 结构清晰,Python 直接读 |
| 大模型 | Ollama + qwen2.5:7b | 本地运行,中文周报质量好 |
| 任务解析 | Python 内置 json 模块 | 零依赖 |
| 周报输出 | Markdown | 可读性好,方便转其他格式 |
不需要数据库,不需要 Web 服务。数据就是一个 JSON 文件,脚本跑在本地,AI 也跑在本地。
Ollama 安装:
curl -fsSL https://ollama.com/install.sh | sh
ollama pull qwen2.5:7b
实现步骤
第一步:项目结构
mkdir -p weekly_report
cd weekly_report
touch daily_log.json generate_report.py
daily_log.json 用来存你每天的任务记录:
[
{
"date": "2026-08-18",
"items": [
{"task": "修复用户登录页面的超时问题", "type": "fix", "hours": 2},
{"task": "完成支付模块单元测试", "type": "feat", "hours": 3},
{"task": "参加产品评审会", "type": "meeting", "hours": 1}
]
},
{
"date": "2026-08-19",
"items": [
{"task": "优化搜索接口性能,响应时间从 800ms 降到 200ms", "type": "perf", "hours": 3},
{"task": "写接口文档", "type": "docs", "hours": 1.5},
{"task": "代码 review,修复 3 个 bug", "type": "fix", "hours": 1.5}
]
}
]
type 字段用来分类任务类型,可选:feat(新功能)、fix(修复)、perf(性能)、docs(文档)、meeting(会议)、other(其他)。
第二步:写周报生成脚本
创建 generate_report.py:
#!/usr/bin/env python3
"""本地周报生成器 — 从每日任务记录自动生成周报"""
import json
import sys
import ollama
from pathlib import Path
from datetime import datetime, timedelta
# ============ 配置区 ============
LOG_FILE = Path.home() / "weekly_report" / "daily_log.json"
MODEL = "qwen2.5:7b"
# 任务类型中文名映射
TYPE_NAMES = {
"feat": "新功能",
"fix": "Bug修复",
"perf": "性能优化",
"docs": "文档",
"meeting": "会议",
"other": "其他",
}
# 周报生成 prompt
REPORT_PROMPT = """你是一个高效的工作助手。请根据用户提供的本周任务记录,生成一份结构清晰的周报。
## 周报要求
1. **本周工作总结**(200字以内):概括本周主要工作内容和成果
2. **任务分类汇总**:按类型分组展示,每组列出具体任务
3. **本周亮点**:挑选 2-3 个最有价值的工作成果,简要说明
4. **遇到的问题**:如有阻塞或困难,列出并说明当前状态
5. **下周计划**:根据本周进度,推断下周可能的工作重点(3-5条)
6. **工时统计**:各类任务总工时
## 输出格式
使用 Markdown,结构如下:
周报 — YYYY-MM DD 至 YYYY-MM DD
📋 本周总结
(一段话概括)
📊 任务明细
新功能(feat)
- 任务1
- 任务2
Bug修复(fix)
- 任务1
(其他类型同理)
⭐ 本周亮点
- …
- …
- …
⚠️ 遇到的问题
- 问题描述(状态:进行中/已解决/阻塞)
📅 下周计划
- …
- …
⏱️ 工时统计
- 新功能:X小时
- Bug修复:X小时
- …
- 总计:X小时
只输出周报内容,不要解释其他内容。用中文写。
## 本周任务记录
{weekly_data}
"""
def load_daily_log(filepath: Path) -> list:
"""读取每日任务日志"""
if not filepath.exists():
print(f"❌ 找不到日志文件: {filepath}")
print("请先用日记功能记录本周任务")
sys.exit(1)
with open(filepath, "r", encoding="utf-8") as f:
return json.load(f)
def get_current_week_range():
"""获取本周一到周日的日期范围"""
today = datetime.now()
monday = today - timedelta(days=today.weekday())
sunday = monday + timedelta(days=6)
return monday, sunday
def filter_week_data(data: list, week_start: datetime, week_end: datetime) -> list:
"""筛选本周数据"""
week_data = []
for day in data:
try:
day_date = datetime.strptime(day["date"], "%Y-%m-%d")
if week_start <= day_date <= week_end:
week_data.append(day)
except (ValueError, KeyError):
continue
return week_data
def format_weekly_data(week_data: list) -> str:
"""将本周数据格式化为 prompt 可读的文本"""
if not week_data:
return "本周暂无任务记录,请开始记录你的每日工作。"
lines = []
total_hours = 0
for day in week_data:
date_str = day["date"]
items = day.get("items", [])
day_total = sum(item.get("hours", 0) for item in items)
total_hours += day_total
lines.append(f"\n📅 {date_str}({day_total}h)")
for item in items:
type_name = TYPE_NAMES.get(item.get("type", "other"), "其他")
lines.append(f" - [{type_name}] {item['task']}({item.get('hours', 0)}h)")
lines.append(f"\n💰 本周总工时: {total_hours}h")
return "\n".join(lines)
def generate_report(weekly_data_str: str) -> str:
"""调用 Ollama 生成周报"""
prompt = REPORT_PROMPT.format(weekly_data=weekly_data_str)
response = ollama.chat(
model=MODEL,
messages=[{"role": "user", "content": prompt}],
options={"temperature": 0.3, "num_predict": 1500}
)
return response["message"]["content"]
def main():
# 获取本周日期范围
week_start, week_end = get_current_week_range()
week_label = f"{week_start.strftime('%m/%d')}-{week_end.strftime('%m/%d')}"
print(f"📅 周报周期: {week_label}")
# 加载数据
data = load_daily_log(LOG_FILE)
week_data = filter_week_data(data, week_start, week_end)
if not week_data:
print("⚠️ 本周暂无任务记录,请先生成上周周报或补充数据")
print("用法: python generate_report.py --last # 生成上周周报")
print("用法: python generate_report.py --help # 查看帮助")
sys.exit(0)
# 格式化数据
weekly_data_str = format_weekly_data(week_data)
print(f"✅ 已加载 {len(week_data)} 天的任务记录")
print("🤖 正在生成周报...")
# 生成周报
try:
report = generate_report(weekly_data_str)
except Exception as e:
print(f"❌ 生成失败: {e}")
print("请确认 Ollama 正在运行: ollama list")
sys.exit(1)
# 保存周报
today_str = datetime.now().strftime("%Y-%m-%d")
output_file = Path.home() / "weekly_report" / f"report_{today_str}.md"
output_file.parent.mkdir(parents=True, exist_ok=True)
# 在周报开头加上标题行
header = f"# 周报 — {week_label}\n\n生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M')}\n\n---\n\n"
full_report = header + report
with open(output_file, "w", encoding="utf-8") as f:
f.write(full_report)
print(f"✅ 周报已保存到: {output_file}")
print("\n" + "=" * 50)
print("📋 周报预览:")
print("=" * 50)
print(full_report)
if __name__ == "__main__":
# 支持 --last 参数生成上周周报
if len(sys.argv) > 1 and sys.argv[1] == "--last":
today = datetime.now()
last_monday = today - timedelta(days=today.weekday() + 7)
last_sunday = last_monday + timedelta(days=6)
print(f"📅 上周周报: {last_monday.strftime('%m/%d')}-{last_sunday.strftime('%m/%d')}")
data = load_daily_log(LOG_FILE)
week_data = filter_week_data(data, last_monday, last_sunday)
if not week_data:
print("⚠️ 上周也无任务记录")
sys.exit(0)
weekly_data_str = format_weekly_data(week_data)
print(f"✅ 已加载 {len(week_data)} 天的任务记录")
print("🤖 正在生成上周周报...")
try:
report = generate_report(weekly_data_str)
except Exception as e:
print(f"❌ 生成失败: {e}")
sys.exit(1)
today_str = datetime.now().strftime("%Y-%m-%d")
output_file = Path.home() / "weekly_report" / f"report_last_week_{today_str}.md"
header = f"# 周报 — {last_monday.strftime('%m/%d')}-{last_sunday.strftime('%m/%d')}\n\n生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M')}\n\n---\n\n"
full_report = header + report
with open(output_file, "w", encoding="utf-8") as f:
f.write(full_report)
print(f"✅ 上周周报已保存到: {output_file}")
print("\n" + full_report)
else:
main()
第三步:写每日任务记录脚本
为了方便每天记录,再写一个轻量级的日记脚本:
#!/usr/bin/env python3
"""每日任务记录工具"""
import json
import sys
from pathlib import Path
from datetime import datetime
LOG_FILE = Path.home() / "weekly_report" / "daily_log.json"
def load_log():
if LOG_FILE.exists():
with open(LOG_FILE, "r", encoding="utf-8") as f:
return json.load(f)
return []
def save_log(data):
LOG_FILE.parent.mkdir(parents=True, exist_ok=True)
with open(LOG_FILE, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
def add_task(date_str, task, task_type="other", hours=1.0):
"""添加一条任务记录"""
data = load_log()
# 查找或创建当天的记录
day_record = None
for record in data:
if record.get("date") == date_str:
day_record = record
break
if day_record is None:
day_record = {"date": date_str, "items": []}
data.append(day_record)
else:
# 更新已存在的记录
data = [r for r in data if r.get("date") != date_str]
data.append(day_record)
day_record["items"].append({
"task": task,
"type": task_type,
"hours": hours
})
save_log(data)
type_name = {"feat": "新功能", "fix": "Bug修复", "perf": "性能优化",
"docs": "文档", "meeting": "会议", "other": "其他"}.get(task_type, "其他")
print(f"✅ 已记录 [{type_name}] {task}({hours}h)— {date_str}")
def list_tasks(date_str=None):
"""列出任务记录"""
data = load_log()
if not data:
print("📭 暂无任务记录")
return
if date_str:
# 只看指定日期
for record in data:
if record.get("date") == date_str:
print(f"\n📅 {record['date']}")
for item in record.get("items", []):
type_name = TYPE_NAMES.get(item.get("type", "other"), "其他")
print(f" - [{type_name}] {item['task']}({item.get('hours', 0)}h)")
break
return
# 显示所有记录(最近7天)
print("📋 最近的任务记录:\n")
for record in sorted(data, key=lambda x: x.get("date", ""), reverse=True)[:7]:
date_str = record.get("date", "未知")
items = record.get("items", [])
total = sum(i.get("hours", 0) for i in items)
print(f"📅 {date_str}({total}h)")
for item in items:
type_name = TYPE_NAMES.get(item.get("type", "other"), "其他")
print(f" - [{type_name}] {item['task']}")
print()
# 类型映射(和生成器共用)
TYPE_NAMES = {
"feat": "新功能", "fix": "Bug修复", "perf": "性能优化",
"docs": "文档", "meeting": "会议", "other": "其他"
}
def main():
if len(sys.argv) < 2:
print("用法:")
print(' python daily_log.py add "2026-08-22" "完成登录功能" --type feat --hours 3')
print(' python daily_log.py add "今天" "修复bug" --type fix')
print(' python daily_log.py list # 列出最近7天记录')
print(' python daily_log.py list 2026-08-22 # 查看指定日期')
print()
print("类型选项: feat(新功能) fix(Bug修复) perf(性能) docs(文档) meeting(会议) other(其他)")
sys.exit(0)
cmd = sys.argv[1]
if cmd == "add":
# 解析参数
date_str = sys.argv[2] if len(sys.argv) > 2 else datetime.now().strftime("%Y-%m-%d")
# 处理"今天"
if date_str == "今天":
date_str = datetime.now().strftime("%Y-%m-%d")
# 找任务内容("--task" 后面的内容)
task = ""
task_type = "other"
hours = 1.0
i = 3
args = sys.argv[3:]
while i < len(sys.argv):
if sys.argv[i] == "--task" and i + 1 < len(sys.argv):
task = sys.argv[i + 1]
i += 2
elif sys.argv[i] == "--type" and i + 1 < len(sys.argv):
task_type = sys.argv[i + 1]
i += 2
elif sys.argv[i] == "--hours" and i + 1 < len(sys.argv):
try:
hours = float(sys.argv[i + 1])
except ValueError:
hours = 1.0
i += 2
else:
# 没有 --task 标记时,把剩余内容当任务
if not task:
task = " ".join(sys.argv[i:])
i += 1
if not task:
print("❌ 请提供任务内容")
sys.exit(1)
add_task(date_str, task, task_type, hours)
elif cmd == "list":
date = sys.argv[2] if len(sys.argv) > 2 else None
list_tasks(date)
else:
print(f"❌ 未知命令: {cmd}")
print("运行 python daily_log.py 查看帮助")
if __name__ == "__main__":
main()
第四步:配置 shell 别名
把这两个脚本用起来,加个别名最方便:
# 添加到 ~/.bashrc 或 ~/.zshrc
alias wd='python3 ~/weekly_report/daily_log.py'
alias wg='python3 ~/weekly_report/generate_report.py'
然后生效:
source ~/.bashrc
第五步:日常使用流程
每天下班前,花 30 秒记任务:
# 记今天的任务
wd add "今天" "修复登录超时bug" --type fix --hours 2
wd add "今天" "完成支付模块测试" --type feat --hours 3
wd add "今天" "写接口文档" --type docs --hours 1.5
# 查看所有记录
wd list
周五下午,一键生成周报:
wg
想看上周的周报(如果这周没记):
wg --last
运行效果
# 记录任务
$ wd add "今天" "优化搜索接口性能,响应时间从800ms降到200ms" --type perf --hours 3
✅ 已记录 [性能优化] 优化搜索接口性能,响应时间从800ms降到200ms(3.0h)— 2026-08-22
$ wd add "今天" "完成支付模块单元测试" --type feat --hours 2
✅ 已记录 [新功能] 完成支付模块单元测试(2.0h)— 2026-08-22
$ wd add "今天" "参加产品评审会" --type meeting --hours 1
✅ 已记录 [会议] 参加产品评审会(1.0h)— 2026-08-22
# 生成周报
$ wg
📅 周报周期: 08-18-08-24
✅ 已加载 3 天的任务记录
🤖 正在生成周报...
✅ 周报已保存到: /home/user/weekly_report/report_2026-08-22.md
📋 周报预览:
==================================================
# 周报 — 08-18-08-24
生成时间: 2026-08-22 17:30
---
# 周报 — 08-18-08-24
## 📋 本周总结
本周主要围绕支付模块开发和搜索性能优化展开。完成了支付模块的核心功能开发和单元测试,同时对搜索接口进行了深度优化,将响应时间从800ms降低到200ms,提升了约75%的性能。此外参与了产品评审会议,推进了产品方向的确认。
## 📊 任务明细
### 新功能(feat)
- 完成支付模块单元测试
### Bug修复(fix)
- 修复用户登录页面的超时问题
### 性能优化(perf)
- 优化搜索接口性能,响应时间从800ms降到200ms
### 会议(meeting)
- 参加产品评审会
### 文档(docs)
- 写接口文档
## ⭐ 本周亮点
1. 搜索接口性能优化显著,响应时间降低75%,用户体验大幅提升
2. 支付模块单元测试全覆盖,为后续功能开发打下坚实基础
## ⚠️ 遇到的问题
- 登录超时问题已修复,需观察线上稳定性(状态:进行中)
## 📅 下周计划
1. 推进支付模块上线部署
2. 继续优化其他核心接口性能
3. 完善搜索功能的用户体验
4. 跟进产品评审会确定的需求
## ⏱️ 工时统计
- 新功能:2小时
- Bug修复:2小时
- 性能优化:3小时
- 文档:1.5小时
- 会议:1小时
- 总计:9.5小时
优化方向
脚本已经能用了,想继续打磨可以加这些:
- 自动备份:把
daily_log.json同步到 Git,历史随时可查 - 微信推送:周报生成后自动推送到微信/钉钉,不用手动发
- 工时统计图:用 matplotlib 生成周工时分布图,汇报时更有说服力
- 智能分类:不用手动标记类型,AI 自动判断任务是 feat/fix/perf 哪个
- 月度汇总:把每周周报自动汇总成月报,季度总结不再头疼
- 语音记录:用 Whisper 把语音转文字,说完任务直接记录,零打字
最简单的一步就是今天开始记。哪怕每天只写一行,周末的周报也不会再是临时抱佛脚了。
你的周报一般写多久?如果能把这时间省下来早点下班,值不值?
这篇文章对你有帮助吗?