Bocai 未出勤异常预警
使用本 skill 为门店生成未出勤异常日报:先锁定唯一门店,再查询观远未出勤统计和明细,生成店长摘要、店长全量 HTML/PDF、每位教练独立 HTML/PDF 明细页和本地明细文档。默认只生成和展示数据,不发送企业微信;只有用户明确说“发送/推送企业微信”时才加 --send 并分别发送文本消息和实际附件文件消息。实际附件优先使用 PDF;脚本优先使用 Playwright 批量渲染 HTML 到 PDF,Playwright 不可用或启动失败时退回 Chrome/Edge 命令行打印 PDF;如果 PDF 生成仍超时或失败,脚本会保留 HTML 并把实际附件降级为 HTML。所有 HTML/PDF 文件都要按统计日期归档,但如果输出目录已经是统计日期目录,不要再嵌套一层日期。生成文件统一命名为 会员未出勤预警_<统计日期>_<门店ID>_<业务短标签>_<对象>,例如 会员未出勤预警_2026-06-05_1054_私教_店长.pdf 和 会员未出勤预警_2026-06-05_1054_私教_余11.pdf。
如需在新电脑启用 Playwright 主路径,先安装 Python 包和浏览器二进制:
python -m pip install playwright
python -m playwright install chromium
如需让脚本在缺少 Playwright 时自动安装,必须显式启用,不默认联网安装:
python bocai/absence-warning/scripts/generate_absence_warning.py --center-id <center-id> --threshold 10 --auto-install-playwright
$env:BOCAI_AUTO_INSTALL_PLAYWRIGHT = "1"
自动安装失败不会阻断日报;脚本会继续退回 Chrome/Edge 命令行打印 PDF,再失败则按既有规则发送 HTML 降级附件。
企业微信文本模板已经固化在 scripts/generate_absence_warning.py。不要让 agent 手写、改写或二次润色店长/教练企业微信正文;发送时直接使用结构化结果里的 manager_text 和脚本生成的教练正文,最终回复用户时也只转述 format_absence_result.py 的结论。
业务类型门禁
center list 返回的每个门店对象必须包含严格布尔 studio:true 表示工作室,false 表示健身房。不得根据门店名称猜测类型;字段缺失、非布尔、重复 ID 的 studio 冲突或 center-id/center-name 冲突时立即停止。
未出勤统计和明细都必须使用同一个 --type:
| 类型 | 业务 | 门店 | 报告结构 |
|---:|---|---|---|
| 1 | 工作室 | studio=true | 总数-工作室阶段分布-教练分布 |
| 2 | 健身房私教课 | studio=false | 总数-私教阶段分布-教练分布 |
| 3 | 健身房培训课 | studio=false | 总数-培训阶段分布-教练分布 |
工作室省略 --type 时由生成器自动选择 1;健身房必须显式指定 2 或 3。一次运行只生成一个业务类型,私教和培训必须分别运行、分别发送和分别保存结果。生成器自身只接受类型 1–3,不依赖当前 CLI wheel help 中仍存在的 1–5 范围。
授权和门店门禁
- 所有 SaaS 查询和企业微信发送都必须通过
scripts/bocai_with_auth.py或scripts/bocai-with-auth.ps1/scripts/bocai-with-auth.sh执行。 - 不要直接运行
bocai-cli auth start、bocai-cli auth check-status,不要手写授权轮询,也不要调用Start-Process、macOSopen、浏览器 MCP 或任何 GUI 打开动作处理授权。 - 执行任何
guanyuan查询、qywx upload或qywx send-message前,先通过 wrapper 运行center list获取可用门店,并解析唯一center-id。 - 如果用户输入、文件名或轻量样本不能唯一命中门店,且
center list返回多个门店,立即停止并列出可用门店名称和 ID,不要继续查询未出勤数据。 - 如果用户只是“查看/生成会员预警数据”,不要加
--send,不要调用center manager-phone、qywx upload或qywx send-message。 - 实时接口不可用时,直接提示接口失败原因;不要查找本地缓存,不要用原始 JSON 缓存替代实时接口结果。
- 将后端连接、数据库、Hibernate/JPA、连接池/资源池、接口 5xx、
Connection refused、Could not create JPA EntityManager、Unknown service requested、ConnectionProvider、Could not get a resource from the pool判定为实时后端不可用,不要判定为 token 或授权问题。 - 如果 wrapper 已经刷新凭据或完成过一次授权,但目标命令仍返回上述后端错误,立即停止授权路径,最多按同一 wrapper 链路短重试一次;仍失败就报告“未发送”,不要再次打开授权页。
- 如果错误文本同时包含
token和后端栈、数据库、资源池、连接失败信息,优先按实时后端故障处理,并在回复中原样保留关键错误片段。
推荐命令:
python bocai/absence-warning/scripts/bocai_with_auth.py -- center list
python bocai/absence-warning/scripts/bocai_with_auth.py -- guanyuan absence-statistics --center-id <center-id> --type <1|2|3>
python bocai/absence-warning/scripts/bocai_with_auth.py -- guanyuan absence-detail --center-id <center-id> --type <1|2|3>
python bocai/absence-warning/scripts/generate_absence_warning.py --center-id <center-id> --type <2|3> --threshold 10 --result-json H:\newskill\absence-warning-result.json
python bocai/absence-warning/scripts/generate_absence_warning.py --center-id <center-id> --type <2|3> --threshold 10 --send --result-json H:\newskill\absence-warning-result.json
python bocai/absence-warning/scripts/generate_absence_warning.py --center-id <center-id> --type <2|3> --threshold 10 --send --test-phone <phone> --result-json H:\newskill\absence-warning-result.json
# 中文注释:生成器会把上面的路径自动隔离为 result_<center-id>_<业务短标签>.json;以下以 1054 私教结果为例。
python bocai/absence-warning/scripts/resend_absence_warning_pdf.py --result-json H:\newskill\absence-warning-result_1054_私教.json --target all
python bocai/absence-warning/scripts/resend_absence_warning_pdf.py --result-json H:\newskill\absence-warning-result_1054_私教.json --target all --test-phone <phone>
python bocai/absence-warning/scripts/format_absence_result.py --result-json H:\newskill\absence-warning-result_1054_私教.json
python bocai/absence-warning/scripts/diagnose_absence_mismatch.py --statistics-json H:\newskill\Bocai_Absence_Warning_Raw_1054_statistics.json --detail-json H:\newskill\Bocai_Absence_Warning_Raw_1054_detail.json --coach-name 余11 --format text
generate_absence_warning.py 也兼容 --pdf 参数;脚本默认已经尝试生成 PDF,失败时自动降级 HTML,所以 --pdf 只用于接收旧命令或弱模型显式传参,不改变输出策略。
工作流
- 判断用户意图:查看/生成会员预警数据、创建日报任务、立即发送日报、追问是否已发送、诊断统计/明细差异。
- 创建日报任务时,必须确认接收对象、每日发送时间、会员沉默率标准值、企业微信授权状态;缺少任一项时先追问。
- 实时生成:工作室运行
generate_absence_warning.py --center-id <center-id> --threshold <阈值> --result-json <result.json>(自动使用类型 1);健身房必须加--type 2或--type 3,一次只生成一种业务。发送模式才额外加--send。 - 测试发送固定手机号:运行
generate_absence_warning.py --center-id <center-id> --type <2|3> --threshold <阈值> --send --test-phone <手机号> --result-json <result.json>,让脚本统一覆盖店长和当前业务教练收件人;不要写内联脚本,不要手工调用deliver_report。 - 实时失败:主生成器会输出
status=failed的结构化结果;随后运行format_absence_result.py --result-json <result.json>,直接提示失败原因。不要运行缓存查找,不要改用本地 JSON。 - 失败回复必须明确三件事:失败原因、是否已发送、是否使用缓存;不要凭旧 HTML/PDF 或旧 result JSON 声称“本次已发送”。
- 生成后总是运行
format_absence_result.py --result-json <result.json>,直接把 formatter 输出作为主要回复,不要手工改写“是否发送”“接口失败原因”“风险提示”等关键结论。 - 用户问“是否已发送企业微信消息”:优先运行
format_absence_result.py --result-json <result.json>;没有 result 文件时,重新生成一次结果或说明缺少结构化结果,不要凭 HTML 路径判断已发送。 - 用户问“统计和明细哪里不一致”:运行
diagnose_absence_mismatch.py --statistics-json <statistics.json> --detail-json <detail.json> [--coach-name <教练>] --format text,直接转述输出。 - 读取接口 JSON、计算阶段、渲染 HTML、生成企微 payload、企业微信正文模板和发送结果判断都交给脚本,不要让 agent 手工解析 JSON 或拼消息。
创建任务追问规则
- 未说明发送给谁:追问接收对象,例如店长、本门店全部教练、指定教练。
- 未说明几点发送:追问每天具体发送时间。
- 时间表达模糊:例如“早上”“下班前”,必须追问具体时刻。
- 未说明会员沉默率标准值:先解释口径,再追问阈值。
- 未确认企业微信授权:提示后续发送会通过 wrapper 自动处理授权,不要让 agent 自行打开授权页。
会员沉默率解释口径:
教练会员沉默率 = 教练 15-60 天未出勤会员数 / 教练有效会员池人数。
7-14 天未出勤只做早期提醒,不参与会员沉默率。
接口和消息协议
当前已确认的 Bocai CLI 接口:
center listcenter manager-phone --center-id <center-id>guanyuan absence-statistics --center-id <center-id> --type <1|2|3>guanyuan absence-detail --center-id <center-id> --type <1|2|3>qywx upload --center-id <center-id> --file <path>qywx send-message --center-id <center-id> --payload <json>
文本消息 payload 最低结构:
{
"phones": ["13811111111"],
"type": "text",
"content": "消息正文"
}
文件消息 payload 最低结构:
{
"phones": ["13811111111"],
"type": "file",
"media_id": "media-xxx"
}
发送命令示例:
python bocai/absence-warning/scripts/bocai_with_auth.py -- qywx upload --center-id <center-id> --file H:\path\detail.pdf
python bocai/absence-warning/scripts/bocai_with_auth.py -- qywx send-message --center-id <center-id> --payload "{\"phones\":[\"13811111111\"],\"type\":\"text\",\"content\":\"教练消息\"}"
python bocai/absence-warning/scripts/bocai_with_auth.py -- qywx send-message --center-id <center-id> --payload "{\"phones\":[\"13811111111\"],\"type\":\"file\",\"media_id\":\"media-xxx\"}"
当前按已确认链路处理:先把实际附件上传成企微素材,拿到 media_id 后分别发送文本摘要和文件消息。不要虚构公开链接;店长收到门店全量附件,教练收到本人附件;实际附件字段为 files.manager_attachment 和 files.trainer_attachment[...],后缀可能是 .pdf 或 PDF 失败后的 .html。发送模式优先使用结构化结果里的实际附件字段,不要从旧发送记录反推路径;旧 result JSON 没有实际附件字段时才兼容使用 files.manager_pdf 和 files.trainer_pdf[...]。补发/重发附件时运行 scripts/resend_absence_warning_pdf.py --result-json <result.json>;测试重发固定手机号时额外加 --test-phone <手机号>,不要手工拼 qywx upload --file。
新 result JSON 还包含 studio、business_type、business_label、business_short_label 和实际 result_json 路径。文件及结果路径带门店 ID和业务短标签,私教、培训、工作室不会互相覆盖。同一教练同时有私教和培训异常时,必须分别发送两套正文和附件。旧 result JSON 缺少全部业务字段时允许补发,但必须提示“历史结果、业务类型未知”;业务字段部分缺失或冲突时拒绝处理。
企业微信正文必须遵循 references/absence-warning-rules.md 的消息模板;其中“明细承接页”当前由上传后的实际附件文件消息承接,不代表可以虚构网页 URL。
辅助脚本
scripts/format_absence_result.py:读取主生成器结构化结果,输出可直接回复用户的中文文本。scripts/resend_absence_warning_pdf.py:按 result JSON 里的实际附件字段重发文件消息,允许 PDF 或降级 HTML 路径。scripts/diagnose_absence_mismatch.py:按教练和档位对比统计口径与明细口径,并列出明细会员。
弱模型或跨模型执行时,优先调用这些脚本;不要把它们的逻辑重新用自然语言推理一遍。
参考资料
- 未出勤阶段、沉默率、有效会员池和推送文案:
references/absence-warning-rules.md - 店长汇总、教练汇总、会员明细和本地文档结构:
references/output-schema.md - 店长 HTML 模板:
assets/manager-detail-template.html - 教练 HTML 模板:
assets/trainer-detail-template.html
验证
- 确认
center-id未锁定前,不会查询guanyuan absence-*,也不会发送企业微信。 - 确认
center list每个门店包含严格布尔studio,重复 ID 冲突和 ID/名称冲突会安全失败。 - 确认工作室自动使用类型 1,健身房不指定类型失败,健身房类型 1 和工作室类型 2/3 均在请求前失败。
- 确认统计和明细接口始终显式收到同一个
--type,响应存在type、businessType或业务类型时会校验冲突。 - 确认私教、培训和工作室的 result JSON、Markdown、HTML/PDF、正文和发送记录带门店 ID及业务短标签,不互相覆盖。
- 确认合法空响应生成店长空报告和 result JSON,不生成教练文件;空报告显式发送时只发送店长。
- 确认实时接口失败时输出失败原因,且不会查找或使用本地缓存。
- 确认后端连接、数据库、Hibernate/JPA、资源池和 5xx 类错误不会触发反复授权,只会按实时接口失败处理。
- 确认错误文本同时出现 token 和后端栈/资源池信息时,优先报告后端故障,并明确未发送。
- 确认
format_absence_result.py能稳定回答数据来源、是否发送、文件路径和风险提示。 - 确认
diagnose_absence_mismatch.py能按教练和档位列出统计/明细差异及会员名单。 - 确认所有 Bocai CLI 调用都通过本 skill 的
scripts/bocai_with_auth.py或.ps1/.shwrapper。 - 确认 7-14 天未出勤不参与会员沉默率,15-30 天和 31-60 天参与会员沉默率,60 天以上不进入一期每日推送。
- 确认店长摘要包含门店有效会员总数、三档人数、门店会员沉默率、教练分布和高于阈值的教练标记。
- 确认店长/教练企业微信正文包含方案模板里的 emoji 标题、早安问候、三档 emoji 行和“更多信息请点击明细查看:明细承接页”。
- 确认教练姓名已带“教练”时不会重复追加“教练”,且教练正文不展示方案外的会员沉默率。
- 确认执行日报时会先直接输出店长日报正文,再补充本地明细文档路径或后续动作。
- 确认店长会生成一份门店全量 HTML/PDF;PDF 失败时企业微信给店长发送 HTML 降级附件。
- 确认教练摘要只包含该教练本人名下会员,不向教练推送无归属会员。
- 确认每位教练都会生成独立 HTML/PDF 明细页,且 HTML 至少包含教练姓名、统计日期、阶段汇总和会员明细表。
- 确认所有 HTML/PDF 文件都会先按统计日期创建目录,再统一落到对应日期目录下。
- 确认生成文件名使用
会员未出勤预警_<统计日期>_<门店ID>_<业务短标签>_<对象>,让企业微信附件列表能直接看出日期、门店、业务和店长/教练对象。 - 确认
--output-dir已经是统计日期目录时,不会生成重复日期目录。 - 确认无异常时生成轻量提示,而不是误报系统失败。
- 确认发送时会先上传实际附件文件拿到
media_id,再分别发送文本消息和文件消息。 - 确认测试发送固定手机号时只调用
generate_absence_warning.py --send --test-phone <手机号>,不写内联脚本,不手工调用deliver_report。 - 确认发送模式优先使用
files.manager_attachment/files.trainer_attachment[...],PDF 失败时允许上传或发送 HTML 文件。 - 确认补发/重发只调用
resend_absence_warning_pdf.py,并由脚本从 result JSON 的实际附件字段取附件;测试重发固定手机号时加--test-phone <手机号>。 - 确认未加
--send时不会发送企业微信,正文提示本地明细文件而不是后续文件消息。 - 确认 formatter 和补发脚本显示业务标签、实际 result JSON 路径,并兼容提示无类型的历史结果。
- 确认静态 help、单元/mock、真实接口、企业微信和浏览器渲染证据分层记录;mock 成功不冒充真实接口或发送成功。
Scan to join WeChat group