从零搭建智能周报系统
钉钉、金山文档、IMA笔记全链路数据采集与AI总结实践
7种数据源自动采集、AI生成结构化工作总结
一个每周都在用的效率工具
你有没有这样的经历——周五下午,打开钉钉,翻遍日程、会议、日志、审批、聊天记录,打开金山wps看看本周处理了哪些文档?IMA笔记本周记录了哪些工作内容?试图回忆这周到底干了什么,然后憋出一篇"流水账"式的周报?
这个痛点很多年了。
直到2026年初,AI agent技术发展,我终于决定不再忍了。与其每周手动翻找,不如让机器去干这件事。于是有了这个项目——一个基于钉钉CLI、IMA知识库、金山文档和AI的智能周报系统。
这篇文章不讲大道理,只分享一个真实的项目建设过程:为什么做、做了什么、踩了什么坑、怎么填的坑。如果你也在做类似的数据采集或自动化工具,希望能给你一些启发。
一、项目建设背景
1.1 痛点:周报是"信息考古"
作为一个钉钉重度用户,我的工作数据分散在多个"孤岛"里:
- 日程上有会议安排
- AI听记里有会议纪要
- 工作日志里有每日产出
- 审批里有流程记录
- 聊天里有沟通记录
- 金山wps文档里有协作内容
- 知识库(IMA)里有学习笔记
每个数据源单独看都没问题,但要把它们"拼图"成一份完整的周报,每周需要花费30-60分钟做信息考古。更糟糕的是,这种重复劳动毫无技术含量,纯粹是体力活。
1.2 目标:让机器自动完成
我想要一个系统,能做到以下几点:
- 自动采集:每周自动从7个数据源拉取数据
- 智能总结:用AI把零散数据组织成结构化工作总结
- 自动归档:总结自动发送到邮箱,同时归档到知识库
- 稳定可靠:失败自动重试,重试还失败就发告警通知我
听起来很简单?实际做起来,光是"获取数据"这一步就踩了无数坑——尤其是IMA知识库。
二、项目价值
系统上线后,最直观的变化是:每周写周报的时间从30分钟降到了0。打开邮箱,AI已经帮你写好了工作总结,你只需要看一遍、微调,然后提交。
| 维度 | 之前 | 之后 | 提升 |
|---|---|---|---|
| 周报耗时 | 30-60分钟 | 0-3分钟(仅审核) | ≈95% |
| 数据覆盖 | 依赖记忆,经常遗漏 | 7个数据源全覆盖 | 完整可追溯 |
| 总结质量 | 流水账式罗列 | AI结构化分类+数据驱动 | 决策可用 |
| 知识沉淀 | 周报发完就丢 | 自动归档到知识库 | 可检索 |
但对我来说,更大的价值在于可扩展性——这套架构不是"只能做周报",它的数据采集管道、AI总结引擎、告警机制都是通用的,稍加改造就能用在其他自动化场景中。
三、系统功能全景
整个系统由8个核心模块组成,分三层:采集层、处理层、输出层。
3.1 采集层:7种数据源
采集层通过钉钉CLI(dws命令行工具)和IMA OpenAPI,自动拉取以下数据:
| 数据源 | 采集方式 | 获取内容 |
|---|---|---|
| 钉钉日历 | dws calendar event list | 日程事件(标题、时间、参与人) |
| AI听记 | dws minutes list/get | 会议记录、摘要、文字稿 |
| 工作日志 | dws report outbox/inbox | 收/发日志及详情内容 |
| 聊天记录 | dws chat message list-all | 指定日期范围的会话消息 |
| 审批数据 | dws oa approval list | 已审批/待审批列表 |
| 钉钉文档 | dws doc search | 近期访问的文档列表 |
| 金山文档 | kdocs-cli search-files | 近期修改的文档列表 |
3.2 处理层:AI总结引擎
采集到的原始数据通过Markdown渲染后,交由阿里云百炼的DeepSeek模型生成结构化工作总结。输出包含:
- 工作总结:按类别分类(如需求分析、开发、测试、沟通协调),数据驱动,占篇幅85%以上
- 下期协调事项:表单格式,列出待办事项、负责人、优先级
3.3 输出层:三通道交付
- 邮件:智能总结在前、原始数据在后,一封邮件搞定
- 本地归档:Markdown + JSON 元数据,写入 kb_archive 目录
- 知识库:双写至 Obsidian Vault,支持全文检索
3.4 可靠性保障
- 自动重试:每个任务最多重试3次,间隔10秒
- 双通道告警:3次全部失败后,钉钉消息 + 邮件同时通知
- 登录自动化:钉钉CLI登录过期自动检测 + 自动重新登录
- 版本追踪:每次任务执行结果记录到SQLite变更日志
四、系统架构与数据流
下图展示了系统的完整数据流转。从CLI参数解析到最终归档,总共6个步骤,全部自动化串联。
🚀 start_server.py
CLI入口 → 📋 simple_web_api.py
调度层
B → 🔐 统一登录检查
钉钉 / IMA / 金山文档
C → 📁 文件已存在?
D → 是
D → 否
F → 1️⃣ 采集数据
DataCollector × 7
G1 → 2️⃣ 采集知识库
IMA API
G2 → 3️⃣ 生成Markdown
8个章节渲染
G3 → 4️⃣ AI智能总结
DeepSeek
G4 → 5️⃣ 发送邮件
SMTP QQ邮箱
G5 → 6️⃣ 归档知识库
本地 + Obsidian
G6 → 📝 版本记录
SQLite changelog.db
采集层是整个系统的核心,也是坑最多的地方。下面这张图展示了7个采集器的并行工作方式:
DataCollectorManager
顺序执行 → C1
M → C2
M → C3
M → C4
M → C5
M → C6
M → C7
C1 → raw_data
7个数据源合并
C2 → R
C3 → R
C4 → R
C5 → R
C6 → R
C7 → R
R → generate_raw_markdown()
8个章节渲染
MD → AI DeepSeek
生成工作总结
五、踩坑记:IMA知识库的"修改日期之谜"
接下来这部分,是我最想分享的——一个看似简单、却让我折腾了两天的"小问题"。
5.1 问题:API 没有返回修改时间
项目中需要从IMA知识库中采集"本周修改过的知识条目",这样周报才能体现最近的学习动态。我很快找到了IMA的搜索API:
POST /openapi/wiki/v1/search_knowledge
{
"query": "关键词",
"knowledge_base_id": "xxx",
"limit": 10
}然而,API返回的数据只有三个字段:
{
"media_id": "xxx",
"title": "知识条目标题",
"media_type": 11
}没有 modify_time、没有 update_time、没有 create_time——没有任何时间戳。
一开始我以为是自己没找对接口,反复查阅文档,尝试了知识库的浏览接口、搜索接口,结果都一样。IMA的笔记接口(list_note)是有 modify_time 的,但知识库的搜索接口就是没有。
5.2 第一版方案:日期关键词搜索
既然API不提供时间字段,我就换个思路——利用搜索功能,把目标日期范围内的每一天作为关键词去搜索:
# 生成日期关键词:250721, 250722, 250723 ...
queries = []
cursor = start_date
while cursor <= end_date:
queries.append(cursor.strftime('%y%m%d'))
cursor += timedelta(days=1)
# 并发搜索(最多5个线程)
with ThreadPoolExecutor(max_workers=5) as pool:
futures = {pool.submit(search, q): q for q in queries}
for future in as_completed(futures):
items = future.result()
# 按 media_id 去重合并这个方案能工作,但有两个硬伤:
- 依赖内容包含日期文本:如果知识条目的标题和正文里都不含日期字符串,就搜不到
- 可能漏掉:新创建的笔记如果内容里没有日期,就不会出现在搜索结果中
- 无法精确排序:搜索结果按相关性排序,不是按时间排序
当时我想:这大概就是IMA API的设计局限了,先凑合用吧。但每次看到周报里缺失的知识库条目,心里总是不太舒服。
5.3 第二版方案:AI Agent统一命名 + 文件名识别
转机出现在我观察到一个规律:我习惯在IMA中创建笔记时,标题都包含日期信息,比如"250721_项目评审纪要"。但并非所有笔记都遵守这个格式——有些笔记是IMA自动生成的,标题是"会议记录_20260721";有些是"未命名笔记"。
于是我想到了另一个思路:
YYMMDD_描述性标题 格式。然后,直接从文件名中解析出修改日期。
具体做法分三步:
第一步:AI Agent批量重命名
在IMA的AI Agent中创建一个"智能命名助手"Agent,给它一个系统指令:
你是一个知识库命名助手。请根据知识条目的内容,
为每个条目生成一个标准化的标题:
格式:YYMMDD_描述性标题(不超过20字)
- 日期从条目内容中提取(如果有)
- 如果内容中没有明确日期,则标注为"000000"
- 描述性标题概括条目核心内容然后逐条执行,让AI Agent读取每条知识库条目的内容,生成标准化的标题。
第二步:代码中从文件名解析日期
在数据采集代码中,增加一个文件名解析函数:
import re
from datetime import datetime
def parse_date_from_title(title: str):
"""从标准化的标题中解析日期"""
match = re.match(r'(\d{6})[_\s]?(.*)', title)
if not match:
return None, title
date_str = match.group(1)
description = match.group(2)
# 跳过"000000"(无日期)
if date_str == '000000':
return None, description
try:
dt = datetime.strptime(date_str, '%y%m%d')
return dt, description
except ValueError:
return None, title第三步:按日期过滤
采集时,先获取所有知识库条目,然后从标题中解析日期,再按日期范围过滤:
def get_modified_this_week(items, start_date, end_date):
result = []
for item in items:
mod_date, desc = parse_date_from_title(item['title'])
if mod_date and start_date <= mod_date <= end_date:
item['parsed_date'] = mod_date
item['clean_title'] = desc
result.append(item)
return result5.4 效果对比
| 方案 | 精确度 | 覆盖率 | 维护成本 |
|---|---|---|---|
| 日期关键词搜索 | 中(依赖内容含日期) | 约60-70% | 低 |
| AI Agent + 文件名识别 | 高(显式字段) | 约95%+ | 中(需一次性命名) |
这个方案的好处是:一次命名,永久受益。AI Agent把所有历史笔记都统一命名后,后续新创建的笔记也按这个规范来,采集代码只需要做简单的字符串解析,无需依赖任何API返回的时间字段。
六、项目背后的工程理念
这个项目虽然不大,但在开发过程中,我有意识地遵循了一些工程原则,分享出来供参考:
6.1 文档先行
每次架构变更,先更新文档,再写代码。项目根目录的 docs/harness/ 下维护了完整的架构图、生命周期、配置说明、容错机制等文档。这让我在两个月后回头看代码时,还能快速理解当初的设计意图。
6.2 契约优先
所有数据接口先定义好数据结构(TypedDict/Schema),再实现业务逻辑。比如采集器的输出格式、AI总结的输入格式、归档文件的元数据格式,都是先定好契约再写代码。
6.3 容错设计
系统与6个外部系统(钉钉CLI、IMA、金山文档、阿里云百炼、QQ邮箱、Obsidian)交互,每个都可能出问题。因此:
- 每个外部调用都有超时和重试
- 单个采集器失败不影响其他采集器
- 钉钉CLI登录过期自动检测 + 自动重新登录
- 3次重试失败后双通道告警,不遗漏任何异常
6.4 版本追踪
每次任务执行结果都记录到SQLite变更日志中,包括版本号、执行时间、状态、错误信息。这让我能回溯每一次运行情况,定位问题非常方便。
七、聊几句
说说你的自动化故事
你工作中最想"自动化"的重复劳动是什么?
——或者,你也遇到过API不返回关键字段的坑吗?
👆 在评论区告诉我你的选择,我会在后续文章里分享更多对应场景的自动化方案
这篇文章分享了一个真实项目的建设过程。从"手动翻找数据"到"全自动采集+AI总结",中间有太多没想到的细节——光是钉钉CLI的登录过期问题就让我折腾了好几个晚上。
但正是这些"没想到"的坑,让项目变得有趣。每次解决一个问题,系统就变得更可靠一点。到现在,这个系统已经稳定运行了几个月,每周自动给我发送工作总结,我几乎忘了"手动写周报"是什么感觉。
如果你也在做类似的事情,或者对某个技术细节感兴趣,欢迎留言交流。后续我计划分享:
- 钉钉CLI的踩坑与避坑指南
- AI Agent在数据治理中的实践
- 多数据源自动采集的架构设计模式
如果这篇文章对你有帮助,点个"在看"或分享给需要的朋友吧
雄哥Harness | 独立开发者 | 效率工具爱好者
专注于工程效率、自动化工具、知识管理
GitHub · 掘金 · 知乎
原创内容,转载请注明出处
夜雨聆风