| name | law-firm-worklog |
| description | 律所工时月报生成(通用版,多数据源)。从用户已配置的任务管理工具(滴答清单 / Notion / Microsoft To Do / 飞书任务模块,任选其一)拉取指定日期范围内已完成的"业务工作"任务,按用户首次配置的工时模板(列名、固定值、姓名)和项目案号注册表(项目名、案号、关键词)自动归类并生成律所工时系统可导入的 Excel(默认 .xlsx)或 CSV。首次运行会引导用户完成一次性配置(数据源选择 + 工作单元白名单、姓名/律所、列模板、项目案号清单),后续可随时增删项目。当用户提到"工时""工作日志""月度工时""填工时""导出工时""律所工时""X月工时"等时触发,不论是否提及具体律所或工具。 |
律所工时月报生成 Skill(通用版,多数据源)
把"已完成任务"转成"律所工时系统 Excel / CSV"的工作流,适用于任何律所、使用以下任一任务工具的律师:
| 数据源 | 适配器文件 |
|---|
| 滴答清单 / TickTick | references/sources/dida365.md |
| Notion | references/sources/notion.md |
| Microsoft To Do | references/sources/todo.md |
| 飞书 / Lark | references/sources/feishu.md |
主流程(本文件)数据源无关:所有源在 B3 出口都归一化成 {date, title, project_key, is_all_day} 四元组,下游 B4/B5 统一处理。源专属细节(MCP 工具名、字段映射、时区处理、首次配置追问脚本)都封装在对应 references/sources/<source>.md 中。
总览
本 skill 把工作流拆成两类调用:
- 首次配置(user-config 目录为空):引导用户选数据源、录入律所工时表头、姓名、固定值、工作单元白名单、项目案号注册表。
- 生成工时(已配置过):读取配置,按 source 字段加载适配器,拉任务,归类,生成 Excel(默认)或 CSV。
另外随时支持:
- 新增/删减项目 — 用户说"新签了 X 项目,案号 …"或"删掉某项目"时,原地编辑
user-config/projects.csv。
- 修改个人信息 / 工作单元白名单 — 用户说"换律所了""改姓名""新增工作 list/database/project"时,编辑
user-config/profile.md,源专属字段格式见对应 references/sources/<source>.md。
起手判断
进入 skill 后按以下决策树走,不跳步:
user-config/profile.md 存在且完整?
├─ 否 → 流程 A(首次配置);其中第二组确定 source。
└─ 是 → 读取 profile.md 的 `source` 字段(缺省 = dida365,向后兼容老配置)
→ 加载 references/sources/<source>.md
→ 按其"MCP 工具集"小节验证工具可见性
├─ 工具不可见 → 输出该 adapter 的"工具不可见时的提示文案",停
├─ 工具可见但鉴权失败(401/403)→ 输出该 adapter 的鉴权失败提示,停
└─ 工具就绪 → 流程 B(生成工时)
绝不在此阶段假设默认姓名、默认列名、默认 source——没有用户认可的配置就不能生成任何输出文件。
流程 A:首次配置
详细脚本见 references/setup.md。要点:
-
一次性向用户收齐以下信息(一组一组问,不要一次甩一长串)。能用规则推断的字段不要主动问用户,只在用户 review 时纠正——这条仅适用于工时模板的列分类启发式,不适用于数据源 schema(见下方约束)。
- 第一组:工时模板 — 列名 + 启发式分类(保留不变;启发式分类详见
references/setup.md 的步骤 1-B 表格)。
- 第二组:数据源 + 工作单元白名单 — 先问用户用什么任务工具(
dida365 / notion / todo / feishu),然后加载 references/sources/<source>.md 中的"首次配置脚本"小节走源专属问答。每个源的"工作单元"概念不同:dida 是 project,notion 是 database,todo 是 task list,feishu 是任务容器(任务列表或全部)。
- 第三组:项目案号注册表 — 项目名 + 案号(关键词默认 = 项目名本身),数据源无关。
-
把收集到的信息写入 user-config/ 下三个文件:
profile.md — 包含 source: 字段、工作单元白名单(按 source 不同格式不同)、列分类表(启发式分类结果 + A 类各列固定值,含律师姓名)
template.csv 或 template.xlsx — 用户律所工时系统的表头(用户给什么格式就存什么格式)
projects.csv — 项目案号注册表,列固定为:项目名,案号,客户,关键词
-
写完后输出"配置已保存到 …,下次直接说'生成 X 月工时'即可"。
关键约束:
- 工时模板列名启发式分类允许(用户 review 时纠正)。
- 数据源 schema 不允许启发式(Notion / 飞书的 database / 多维表格 schema 用户自定义,必须显式问清"哪个字段是状态、哪个是完成时间、状态等于什么值算完成",绝不靠列名猜)。
- 项目案号绝不虚构。关键词允许默认(项目名本身),不允许凭空编造其他词。
流程 B:生成工时
B1. 确认日期范围
向用户确认起止日(如 2026-05-01 到 2026-05-24)。如果用户只说"5月工时",按当月 1 日到今天(或当月末,看上下文)处理,并复述给用户确认。
B1.5 检查输出文件是否已存在
生成前检查默认输出路径(YYYY-MM-工作日志.xlsx):
- 文件不存在 → 继续 B2,正常生成
- 文件已存在 → 提示用户:"已存在
YYYY-MM-工作日志.xlsx(上次生成于 <文件修改时间>)。是否覆盖?还是生成带时间戳的新文件(如 YYYY-MM-工作日志-v2.xlsx)?"
不要静默覆盖——用户可能已经在旧文件里手填了小时数。
B2. 拉指定日期范围内已完成任务
按 profile.md 的 source 字段加载 references/sources/<source>.md,执行其中的 "B2 数据拉取" 小节。
跨源共同约束(每个 adapter 都必须遵守):
- 绝不全量拉取再筛选。必须在 API 层用工作单元 ID(projectIds / database_id / list_id / table_id)过滤。理由:用户的"个人/学习/还款"任务混在同一账户里,全量拉会把婚礼、还款日、课程都拖进工时;大库还会触发分页/限速,慢且容易漏。
- 分页:返回结果如带
hasMore / has_more / nextCursor / next_cursor / @odata.nextLink 等标记,必须补拉至全部,再进入 B3。不要静默丢弃超出首页的任务。
如果用户说"我新建了一个工作单元(项目/list/database/table)"或拉回来的数据明显少了,先按 adapter 的"流程 C"小节复核白名单,必要时让用户确认后更新 profile.md。
B3. 字段归一化
按 profile.md 的 source 字段加载 references/sources/<source>.md,执行其中的 "B3 字段归一化" 小节。
每条任务必须归一化成以下统一四元组(下游 B4/B5 只读这层,不再触及源专属字段):
{
"date": "YYYY-MM-DD",
"title": "任务标题",
"project_key": "<source 内的工作单元 ID 或子分类值>",
"is_all_day": true | false
}
各源对时区、全天事件的处理差异详见对应 adapter 文件,本文件不重复。注意滴答清单的全天任务 UTC 日期 +1 天的坑由 dida365.md 处理——主流程不需要知道。
设计原理:完整的架构决策说明见末尾"关于为什么这样设计"段落。
B4. 按项目案号归类
读取 user-config/projects.csv,按以下顺序为每条任务分配项目和案号:
-
关键词匹配 — 任务标题/描述中包含某项目的任一关键词 → 命中该项目。多个项目同时命中时按以下优先级链裁决:
- 完全匹配优先:关键词完整出现在标题中(如标题含"新能源项目",关键词也是"新能源项目")> 仅部分匹配(如关键词是"新能源",仅命中标题中的子串)
- 匹配到的关键词字符数多者优先(减少单字/短词误命中)
- 仍冲突 → 问用户"以下任务同时命中 [项目A] 和 [项目B] 的关键词,请确认归属"
例:任务"新能源项目 — 审阅EPC合同"同时命中项目 A(关键词"新能源项目",5字)和项目 B(关键词"新能源",3字)→ 项目 A 胜出。
-
匹配不上 — 不要瞎猜,列出来让用户裁定("以下 N 条无法自动归类,请告知归属项目")。
兜底处理:
- 如果用户在合理时间内未回应:将该任务标记为"待确认",列在输出摘要末尾,并在文件名中加
(含待确认) 后缀。
- 如果用户明确说"随便归到 X":按用户说的填,但在输出摘要中注明"[已手动指定]"。
- 如果用户说"这条不算":跳过该任务,不出现在输出文件中。
B5. 生成输出文件
设计原理:完整的架构决策说明见末尾"关于为什么这样设计"段落。
默认生成 Excel(.xlsx),用户也可以说"导出 CSV"来要 CSV。
输出路径
文件名默认 YYYY-MM-工作日志.xlsx(或 .csv),保存到当前工作目录。
输出格式选择
- 用户说"生成 X 月工时"不加后缀 → 输出
.xlsx
- 用户说"导出 CSV"或"生成 CSV" → 输出
.csv
user-config/ 下只有 template.csv(没有 .xlsx)且用户没指定格式 → 输出 .csv(尊重历史配置)
表头来源不受模板格式限制:无论 user-config/ 存的是 template.xlsx 还是 template.csv,都能从中提取列名数组。所以用户给 Excel 模板、要求导出 CSV(或反过来)完全没问题——列名从模板读,输出格式按用户指令选。
Excel 生成(.xlsx)
调用 skill 自带的 scripts/generate_worklog.py,不要手写 openpyxl:
python <skill-dir>/scripts/generate_worklog.py \
--input /tmp/worklog_data.json \
--output "YYYY-MM-工作日志.xlsx" \
--template "<user-config/template.xlsx 的路径(如有)>"
其中 /tmp/worklog_data.json 的结构为:
{
"headers": ["列1", "列2", ...],
"rows": [["值1", "值2", ...], ...],
"column_widths": {"日期": 12, "描述": 50}
}
headers:按模板列顺序的列名数组
rows:按通用取值规则填充的数据行(二维数组)
column_widths:可选,指定列宽(key 为列名,value 为宽度数值);不传则脚本用内置默认值(日期 ~12、描述 ~50、其他 ~15)
脚本会自动处理:加粗灰底表头、细线边框、冻结首行、自动筛选。如果提供了 --template,列宽优先从模板继承。依赖 openpyxl,首次运行前提示用户 pip install openpyxl。
CSV 生成(.csv)
按 user-config/template.csv 的表头顺序写每行。编码 UTF-8 with BOM,Excel/WPS 双击不乱码。
通用取值规则(Excel 和 CSV 共用)
按首次配置时的列分类取值:
- A 类(固定值列):直接填 profile.md 里配置好的固定值
- B 类(任务衍生列):
- 日期列 → B3 归一化后的
date(北京时间,格式 YYYY-MM-DD)
- 描述/标题列 → B3 归一化后的
title
- C 类(项目映射衍生列):用 B4 归类结果(项目名 / 案号 / 客户名)填入
- D 类(留空待手填列):保持空白。理由:用户最了解每条任务实际花了多少时间或其他主观字段,自动估计反而误导;让用户在 Excel 里手填更准。
B6. 输出摘要
写完后给用户:
- 文件绝对路径及格式(
.xlsx / .csv)
- 总行数(不含表头)
- 项目分布表(项目 / 案号 / 条数)
- 需用户确认的边界条目清单:未匹配上的任务、关键词命中多个项目的任务
流程 C:增删项目 / 修改个人信息 / 增删工作单元
用户说"新签 X 项目,案号 …,关键词 …" → 追加一行到 user-config/projects.csv,让用户确认后保存。
用户说"删掉 X 项目" → 删行后保存。
用户说"改姓名/换律所" → 修改 profile.md 的列分类表中对应 A 类列的固定值。
用户说"新增/删除一个工作单元(项目 / list / database / table)" → 加载 references/sources/<source>.md 的"流程 C"小节,按源专属格式修改 profile.md 的工作单元白名单。
每次修改后回显"已更新,当前共 N 个项目(或 N 个工作单元)"。
重要约束
- 绝不全量拉再筛选 —— 所有数据源都必须在 API 层用工作单元 ID 过滤。理由跨源通用:用户的"个人/学习/还款"任务混在同一账户/workspace 里,全量拉会污染工时。
- 绝不虚构案号 —— 不在
user-config/projects.csv 中的项目,先问用户要新案号再写入注册表,再生成输出文件。
- 绝不在首次配置完成前生成输出文件 —— 没有用户认可的列模板/姓名/案号/source,任何 Excel/CSV 都不能直接用。
- 绝不对数据源 schema 启发式推断 —— Notion 的 status property、飞书多维表格的状态列、MS To Do 的 list 含义都必须由用户在首次配置时显式给出。工时模板的列名启发式分类是允许的(行业惯例统一),但数据源 schema 完全自定义,不能猜。
- 不强行预估"自报小时"(若列模板中有此列),保留空白让用户手填。
维护
- 用户新签项目/收到新案号 → 追加到
user-config/projects.csv
- 用户新增工作单元(任意 source)→ 按对应 adapter 的"流程 C"小节修改
user-config/profile.md
参考文件
references/setup.md — 首次配置详细脚本(一问一答模板、示例)
references/sources/<source>.md — 各数据源的专属配置脚本与数据拉取细节
scripts/generate_worklog.py — Excel 生成脚本(openpyxl),已打包,B5 直接调用,不要手写 openpyxl
常见问题排查
| 症状 | 可能原因 | 处理 |
|---|
| 任务拉不到 / 数量明显偏少 | 工作单元白名单过时(新建了 project/list/database/table 但 profile.md 没更新) | 按当前 source 的 adapter 列出可选工作单元,让用户确认后更新白名单 |
| 拉不到 / 报鉴权错 | MCP token / OAuth 过期 | 按当前 source 的 adapter 输出鉴权失败提示文案 |
| 多条任务无法自动归类 | projects.csv 缺少该项目或关键词覆盖不足 | 展示未匹配任务列表,问用户归属;确认后追加到 projects.csv |
| Excel 打开乱码 | CSV 忘了加 BOM | 确认写入时用了 utf-8-sig 编码(或直接改用 .xlsx 输出,无编码问题) |
openpyxl 报 ModuleNotFoundError | 环境没装 | 提示用户 pip install openpyxl |
| 任务日期不对(差一天) | 全天任务时区没正确处理 | 看当前 source 的 adapter 中 B3 节是怎么算 all-day 的(典型如 dida365.md 的 UTC+1 天规则) |
关于"为什么这样设计"
- 配置和工作流分离:列模板、姓名、案号都是律所/律师强相关的"私有信息",写在 user-config 里而非 SKILL.md 里,是为了让一个 skill 文件能服务任何律所,配置由用户自己管理。
- 数据源适配器分离:每个 source 的 MCP 工具名、字段语义、时区/全天事件处理差异巨大,但工作流的骨架(列分类、关键词匹配、Excel 生成)完全数据源无关。把源耦合点抽到
references/sources/<source>.md,主流程保持薄一层,新增源 = 加一个 reference 文件。
- 不预设默认值:律所工时表头差异极大(有的 5 列、有的 8 列、有的有"客户"列没有"项目"列),任何"默认表头"都会让用户拿到的文件导不进系统。宁可第一次多问几个问题,也不要默认值导致后续返工。同理:用户的 Notion database / 飞书多维表格 schema 也千差万别,绝不替用户猜状态/完成时间字段。
- 首跑严格走完配置:相比"边用边补",一次性把配置走完,后续每月只需一句"生成 5 月工时"就能完成,体验更稳。