03
数据准备

考勤数据准备

attendance-data-prep
🗂️
数据准备 skills/attendance/
把某客户某月的原始考勤源数据,整理成「按员工分文件夹」的结构化目录,并产出标准化员工清单 reference_roster.xlsx,供 attendance-remarks 等下游直接消费。本 skill 只回答「数据从哪来、有哪些人、文件归谁」—— 只做归档与员工清单,不做备注计算。

TRIGGERS

触发场景
拿到一个月原始考勤源目录,.msg / Excel / PDF / 图片混杂待整理
需要先抽附件、AI 视觉预处理、确定员工范围、按人归档
下游 attendance-remarks 需要 reference_roster.xlsx 作为输入
触发词:准备考勤数据 / 考勤归档 / 整理考勤 / 考勤分文件夹

SESSION GATE

会话入口与前置门禁
  1. G1
    用户必须先提供目录名GATE
    目录名指 data/raw/ 下的子目录名(如 202604_客户A),不是脚本自动猜出的路径。用户未提供时暂停询问,不要自动选「最新目录」或看似最匹配的目录
  2. G2
    SOURCE_DIR 不存在则停下重确认GATE
    由用户提供的 DATASET_NAME 派生 SOURCE_DIR=data/raw/${DATASET_NAME}OUTPUT_DIR=outputs/${DATASET_NAME}。若 SOURCE_DIR 不存在,先列出可选目录请用户重新确认,不要继续运行。

INPUT & OUTPUT

输入 / 产出
↘ INPUT · 输入
  • data/raw/<目录名> 月度源目录(用户指定)
  • 可选底表:账单 / 工资 / 人员清单 / 汇总 Excel
  • .msg 邮件及其附件、邮件正文
  • 考勤 Excel、扫描 PDF、审批截图、签到明细图片
↗ OUTPUT · 产出
  • 给人看:reference_roster.xlsx 等 5 个 Excel
  • 给脚本/LLM:_prep_summary.json 等 JSON / JSONL
  • employees/<姓名>/ 按人归档 + _sources.json
  • _email_texts.jsonl 邮件正文(供 remarks 消费)
📤

ARTIFACTS

产物清单 · 人类件 vs 机器件给人看 Excel · 给脚本看 JSON
reference_roster.xlsx · 人类件
标准化员工清单:姓名、身份证号、来源、文件夹、底表行号。下游核心输入。
数据收集汇总.xlsx · 人类件
员工覆盖、问题概览、视觉概览的人类可读汇总。
人工复核清单.xlsx · 人类件
待处理问题与视觉待识别清单。非「附件列表」式交付,是当前未闭环项的索引。
姓名候选复核.xlsx · 人类件
无底表模式专用:列为 候选姓名 / 复核结论 / 来源 / 复核理由,结论填 确认纳入 / 剔除 / 待确认。人工确认入口。
视觉提取结果.xlsx · 人类件
vision_extracted_records.jsonl 的人类可读审阅版,列用中文状态(高/中/低、是/否)。
_prep_summary.json · 机器件 / 门禁
员工数、文件数、视觉待处理数等机器汇总,数字须自洽。
_prep_questions.json · 机器件 / 门禁
待追问:missing_attendance_data / extra_attendance_employee / unmatched_file / uncertain_employee_candidate。对应人工复核清单.xlsx。
_vision_manifest.json · 机器件 / 门禁
需 LLM 视觉处理的图片与 PDF 逐页 PNG 清单;含 status: pending/processedneeds_pdf_preprocess
_pdf_preprocess.json · 机器件
PDF 逐页预渲染结果索引;预渲染失败时先看其 errors
vision_extracted_records.jsonl · 机器件
AI / 人工视觉中间结果,下游脚本消费。每条须有 source_filepage_or_image
vision_extracted_records.reviewed.jsonl · 机器件
人工在视觉复核清单填「人工结论」后,apply_vision_review.py 回填生成的下游输入;确认/剔除写回 confidence / needs_review / record_status
vision_review_apply_report.xlsx · 人类件
回填汇总/明细;「是否可继续下游」须为「是」(无未匹配结论、无待追问)才把 reviewed JSONL 作为 --vision-records 传回。
_email_texts.jsonl · 机器件
邮件正文(按员工归属),remarks 的 --email-texts 消费。
employees/<姓名>/_sources.json · 机器件
每个员工目录归档依据:original_path / match_reason / match_confidence
config_draft.json · 机器件
配置草稿:attendance_start/end、候选法定假日等。
pdf_pages/<pdf标识>/page-NNN.png · 中间件
PDF 逐页 PNG,步骤 2 生成,供视觉逐页识别。
🪜

STEPS

处理流程 · 6 步
  1. 1
    抽取 .msg 附件
    extract_msg_attachments.py 把邮件附件解到 attachments/,并写 _msg_meta.json(含 subject)。
  2. 2
    PDF 逐页渲染 PNG
    preprocess_pdfs_for_vision.py 把所有 PDF 逐页渲染成 PNG。考勤不能依赖 PDF 文本层,即使看似文字型 PDF 也交给视觉逐页识别。加密加 --password,字小调 --dpi(180–220)。
  3. 3
    AI 视觉结构化预处理
    逐图、逐页 PNG 读取,产出 vision_extracted_records.jsonl + 视觉提取结果.xlsx。保留来源定位,不猜身份证 / 小时数 / 日期 / 姓名;看不清写空并 needs_review=true。无视觉能力时走 PDF 文本层 fallback。
  4. 4
    确定底表GATE
    扫描源目录 Excel 识别底表候选(文件名含 底表/账单/工资/人员/汇总,表头含 姓名/身份证号/考勤备注/计薪周期)。单候选可继续但须写明依据;多候选必须暂停问用户;无候选进无底表模式。
  5. 5
    确定目标员工范围GATE
    有底表以底表人员为准;无底表先视觉 + 文件名/主题候选,再由 LLM 语义复核,人工确认前不生成 roster、不归档(详见下两节)。
  6. 6
    按员工归档
    prepare_attendance_data.py 按 5 级优先级把文件归入 employees/<姓名>/,无法归属进 _unmatched/,并产出 roster 与全部汇总文件。

ROSTER BRANCH

底表分支与姓名候选复核
有底表
以底表人员为目标范围,姓名 / 身份证号优先取自底表,考勤附件仅作交叉校验。底表有人但无考勤 → 标 missing_attendance_data;考勤有人但底表无 → 标 extra_attendance_employee,不可静默丢弃。
无底表
先用结构化考勤 Excel 建清单(须先做完视觉预处理避免漏人),再从文件名 / 邮件主题抽候选 —— 但仅作候选,不能凭正则直接确认。
  1. LLM 语义复核(仅候选集内)
    只在代码抽出的候选集合内做 确认 / 剔除 / 存疑,不从邮件正文泛抽姓名(防止「中控」「主题」「个小时」被误识别)。公司名 / 表单名标 rejected,如 英伟达科锐国际科之锐考勤表
  2. 人工确认门禁GATE
    先输出 姓名候选复核.xlsx 并暂停等待人工确认。仅 confirmed 进 roster;rejected 剔除;uncertain 不写 roster、写 _prep_questions.jsonuncertain_employee_candidate)。确认前不得生成 roster 或归档。
👁

VISION RULES

视觉抽取规则与字段
字段
source_file · page_or_image · record_typeattendance_detail / approval / summary / special_note / unknown)· confidence · needs_review · review_reason 及姓名/身份证/休假/加班/计薪天等。
不猜身份证号 / 小时数 / 日期 / 姓名;看不清、遮挡、手写不确定 → 写空值并 needs_review=true
一图多员工拆成多条;多页 PDF 逐页处理并保留页码来源;一页含多类(年假 + 法定加班 + 周末加班)拆成多条。
钉钉 / 打卡 / 签到明细记为 attendance_detail,不要误当成已审批加班;审批只有「加班到几点」无起算口径,不自行算小时数,写 待追问
小 logo / 二维码 / 签名图等无信息图片也写入并标 unknown,从待识别清单闭环移除。
PDF 文本层 fallback:无批量视觉 / OCR 时不能只停在渲染 + pending,须用 PyMuPDF 按页提文本层兜底(confidence=highreview_reason=pdf_text_layer_fallback)。
复核清单不是终点:可处理附件须被打开 / 读取 / 提取并回写 视觉提取结果.xlsx + vision_extracted_records.jsonl,再带 --vision-records 重跑刷新状态。
人工结论回填:视觉复核清单.xlsx 填「人工结论」(确认 / 剔除 / 需追问)后,用 apply_vision_review.py 回填为 vision_extracted_records.reviewed.jsonl;逐页结论按「来源文件 + 页码」精确匹配,不串到同文件其它页。
🗃

FILING

文件归档规则 · 5 级优先级
  1. 1
    结构化 Excel 内部员工姓名
    打开 .xlsx,在表头附近读 员工姓名 字段并归档到对应员工文件夹。
  2. 2
    文件名包含员工姓名
    遍历目标员工清单,文件名命中某员工则归档。
  3. 3
    邮件主题包含员工姓名
    _msg_meta.jsonsubject 匹配。
  4. 4
    AI 视觉识别结果
    vision_extracted_records.jsonlemployee_name 匹配。
  5. 5
    无法归属 → _unmatched/
    放入 _unmatched/,并在 _prep_questions.json 写对应 unmatched_file
  6. 多员工共享文件
    一个文件关联多员工(多人扫描件)时复制到每个员工目录,并在 _sources.jsonshared_with

PIPELINE

与其他 skill 衔接
归档结果与 reference_roster.xlsx 喂给 attendance-remarks 按配置计算备注、输出审核报告。若源数据已按员工归档好,可跳过本 skill 直接用 remarks。
底层 PDF 逐页渲染复用 pdf-llm-preprocess skill 的 PyMuPDF 能力,考勤流程用 --all-pages-image 等价全页出图策略。
人类件 vs 机器件边界:给人看 Excel(中文列名、可读状态),给脚本 / LLM 看 JSON / JSONL,默认引用 Excel 说明结果。

RUN

运行命令 · 三脚本
RUN · 先派生路径,再依次三步
DATASET_NAME="<用户提供的目录名>"        # 如 202604_客户A
SOURCE_DIR="data/raw/${DATASET_NAME}"
OUTPUT_DIR="outputs/${DATASET_NAME}"
ATTACHMENTS_DIR="${OUTPUT_DIR}/attachments"

# 1. 抽取 .msg 附件
uv run python skills/attendance/attendance-data-prep/scripts/extract_msg_attachments.py \
  "$SOURCE_DIR" "$ATTACHMENTS_DIR"

# 2. PDF 逐页渲染 PNG(加密加 --password,字小调 --dpi 180-220)
uv run python skills/attendance/attendance-data-prep/scripts/preprocess_pdfs_for_vision.py \
  "$OUTPUT_DIR" --source-dir "$SOURCE_DIR" --source-dir "$ATTACHMENTS_DIR"

# 3. 按员工归档
uv run python skills/attendance/attendance-data-prep/scripts/prepare_attendance_data.py \
  "$SOURCE_DIR" "$OUTPUT_DIR" \
  --attachments-dir "$ATTACHMENTS_DIR" \
  --base-table "$SOURCE_DIR/<底表文件名>" \        # 有底表时
  --vision-records "${OUTPUT_DIR}/vision_extracted_records.jsonl" \  # 有视觉结果时
  --name-candidate-review "${OUTPUT_DIR}/姓名候选复核.xlsx"          # 无底表且用了候选复核时必传

# 可选:人工在视觉复核清单填「人工结论」后回填为 reviewed JSONL,再带 --vision-records 重跑
uv run python skills/attendance/attendance-data-prep/scripts/apply_vision_review.py \
  --records "${OUTPUT_DIR}/vision_extracted_records.jsonl" \
  --review-workbook "${OUTPUT_DIR}/视觉复核清单.xlsx" \
  --output-records "${OUTPUT_DIR}/vision_extracted_records.reviewed.jsonl" \
  --report "${OUTPUT_DIR}/vision_review_apply_report.xlsx"

ACCEPTANCE

闭环验收 · 6 条门禁

ANTI-PATTERNS

反例检查
文件名 / 邮件主题里的客户名、公司名、部门名、月份标签被误识别成人名。
_vision_manifest.json 仍有 needs_pdf_preprocess=true,却继续把 PDF 当已处理。
视觉记录缺 source_file / page_or_image,下游无法追溯。
复核清单里还有可打开的附件 / 页面,却没尝试视觉读取或写入 视觉提取结果.xlsx
把签到明细 / 打卡截图直接当成已审批加班,未区分 attendance_detailapproval
加班类型 / 结算去向不清楚却强行归类为法定 / 周末 / 工作日加班,未标 needs_review=true
_unmatched/ 有文件但 _prep_questions.json 无对应 unmatched_file
同名员工只靠姓名归档,未用身份证号或待追问区分。

RETRY & LESSONS

失败重试与复盘沉淀
必须暂停询问的条件:SOURCE_DIR 不存在、底表多候选、无底表且姓名候选未人工确认。
PDF 预渲染失败时先看 _pdf_preprocess.jsonerrors,加 --password / 调高 --dpi / 补文件后重跑步骤 2。视觉不可用先做 PDF 文本层 fallback。
限制:只做归档与员工清单,不做备注计算;同名员工需身份证号区分,无法区分进 _prep_questions.json
复盘沉淀:新误识别 / 漏归档先写 LESSONS.md,重复后再补 SKILL.md 反例 / 校验 / pytest。已沉淀(复宏汉霖-202605):小 logo / 二维码过滤 + .msg 反向归属员工。